fix : update clients slug

This commit is contained in:
hanif salafi 2025-10-12 17:55:35 +07:00
parent 88db076da6
commit 7220375a69
6 changed files with 64 additions and 7 deletions

View File

@ -9,6 +9,7 @@ import (
type Clients struct { type Clients struct {
ID uuid.UUID `json:"id" gorm:"primaryKey;type:UUID"` ID uuid.UUID `json:"id" gorm:"primaryKey;type:UUID"`
Name string `json:"name" gorm:"type:varchar"` Name string `json:"name" gorm:"type:varchar"`
Slug string `json:"slug" gorm:"type:varchar;uniqueIndex"`
Description *string `json:"description" gorm:"type:text"` Description *string `json:"description" gorm:"type:text"`
ClientType string `json:"client_type" gorm:"type:varchar;default:'sub_client'"` // 'parent_client', 'sub_client', 'standalone' ClientType string `json:"client_type" gorm:"type:varchar;default:'sub_client'"` // 'parent_client', 'sub_client', 'standalone'
ParentClientId *uuid.UUID `json:"parent_client_id" gorm:"type:UUID;index"` ParentClientId *uuid.UUID `json:"parent_client_id" gorm:"type:UUID;index"`

View File

@ -10,6 +10,7 @@ func ClientsResponseMapper(clientsReq *entity.Clients) (clientsRes *res.ClientsR
clientsRes = &res.ClientsResponse{ clientsRes = &res.ClientsResponse{
ID: clientsReq.ID, ID: clientsReq.ID,
Name: clientsReq.Name, Name: clientsReq.Name,
Slug: clientsReq.Slug,
Description: clientsReq.Description, Description: clientsReq.Description,
ClientType: clientsReq.ClientType, ClientType: clientsReq.ClientType,
ParentClientId: clientsReq.ParentClientId, ParentClientId: clientsReq.ParentClientId,

View File

@ -14,6 +14,7 @@ import (
type ClientsResponse struct { type ClientsResponse struct {
ID uuid.UUID `json:"id"` ID uuid.UUID `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Slug string `json:"slug"`
Description *string `json:"description"` Description *string `json:"description"`
ClientType string `json:"clientType"` ClientType string `json:"clientType"`
ParentClientId *uuid.UUID `json:"parentClientId"` ParentClientId *uuid.UUID `json:"parentClientId"`
@ -108,6 +109,7 @@ type UserInfo struct {
type ClientHierarchyResponse struct { type ClientHierarchyResponse struct {
ID uuid.UUID `json:"id"` ID uuid.UUID `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Slug string `json:"slug"`
Description *string `json:"description"` Description *string `json:"description"`
ClientType string `json:"clientType"` ClientType string `json:"clientType"`
Level int `json:"level"` // Depth in tree (0 = root) Level int `json:"level"` // Depth in tree (0 = root)

View File

@ -30,7 +30,7 @@ func NewClientLogoUploadService(minioStorage *config.MinioStorage, log zerolog.L
} }
// UploadLogo uploads client logo to MinIO and returns the image path // UploadLogo uploads client logo to MinIO and returns the image path
func (s *ClientLogoUploadService) UploadLogo(c *fiber.Ctx, clientId, clientName string) (string, string, error) { func (s *ClientLogoUploadService) UploadLogo(c *fiber.Ctx, clientId, clientSlug string) (string, string, error) {
s.Log.Info(). s.Log.Info().
Str("UploadLogo", clientId). Str("UploadLogo", clientId).
@ -71,7 +71,7 @@ func (s *ClientLogoUploadService) UploadLogo(c *fiber.Ctx, clientId, clientName
Str("Upload Logo bucketName", clientId).Interface("bucketName", bucketName).Msg("Client upload logo files") Str("Upload Logo bucketName", clientId).Interface("bucketName", bucketName).Msg("Client upload logo files")
// Generate unique filename using client name // Generate unique filename using client name
filename := s.generateUniqueFilename(file.Filename, clientName) filename := s.generateUniqueFilename(file.Filename, clientSlug)
objectName := fmt.Sprintf("clients/logos/%s/%s", clientId, filename) objectName := fmt.Sprintf("clients/logos/%s/%s", clientId, filename)
// Open file // Open file
@ -211,12 +211,9 @@ func (s *ClientLogoUploadService) isValidImageType(filename string) bool {
} }
// generateUniqueFilename generates a unique filename using client name slug + Unix timestamp // generateUniqueFilename generates a unique filename using client name slug + Unix timestamp
func (s *ClientLogoUploadService) generateUniqueFilename(originalFilename, clientName string) string { func (s *ClientLogoUploadService) generateUniqueFilename(originalFilename, clientSlug string) string {
ext := filepath.Ext(originalFilename) ext := filepath.Ext(originalFilename)
// Create slug from client name
clientSlug := s.createSlug(clientName)
// Get Unix timestamp // Get Unix timestamp
now := time.Now() now := time.Now()
timestamp := now.Unix() timestamp := now.Unix()

View File

@ -157,9 +157,13 @@ func (_i *clientsService) ShowWithAuth(authToken string) (clients *response.Clie
func (_i *clientsService) Save(req request.ClientsCreateRequest, authToken string) (clients *entity.Clients, err error) { func (_i *clientsService) Save(req request.ClientsCreateRequest, authToken string) (clients *entity.Clients, err error) {
_i.Log.Info().Interface("data", req).Msg("") _i.Log.Info().Interface("data", req).Msg("")
// Generate slug from client name
slug := utilSvc.MakeSlug(req.Name)
// Convert request to entity // Convert request to entity
newReq := &entity.Clients{ newReq := &entity.Clients{
Name: req.Name, Name: req.Name,
Slug: slug,
Description: req.Description, Description: req.Description,
ClientType: req.ClientType, ClientType: req.ClientType,
ParentClientId: req.ParentClientId, ParentClientId: req.ParentClientId,
@ -269,9 +273,13 @@ func (_i *clientsService) CreateSubClient(parentId uuid.UUID, req request.Client
req.ClientType = "sub_client" req.ClientType = "sub_client"
req.ParentClientId = &parentId req.ParentClientId = &parentId
// Generate slug from client name
slug := utilSvc.MakeSlug(req.Name)
// Convert to entity // Convert to entity
newReq := &entity.Clients{ newReq := &entity.Clients{
Name: req.Name, Name: req.Name,
Slug: slug,
Description: req.Description, Description: req.Description,
ClientType: req.ClientType, ClientType: req.ClientType,
ParentClientId: req.ParentClientId, ParentClientId: req.ParentClientId,
@ -326,6 +334,7 @@ func (_i *clientsService) buildHierarchyResponse(client *entity.Clients, level i
resp := &response.ClientHierarchyResponse{ resp := &response.ClientHierarchyResponse{
ID: client.ID, ID: client.ID,
Name: client.Name, Name: client.Name,
Slug: client.Slug,
Description: client.Description, Description: client.Description,
ClientType: client.ClientType, ClientType: client.ClientType,
Level: level, Level: level,
@ -428,8 +437,13 @@ func (_i *clientsService) CreateClientWithUser(req request.ClientWithUserCreateR
// Step 1: Create the client // Step 1: Create the client
clientReq := req.Client clientReq := req.Client
// Generate slug from client name
slug := utilSvc.MakeSlug(clientReq.Name)
newClient := &entity.Clients{ newClient := &entity.Clients{
Name: clientReq.Name, Name: clientReq.Name,
Slug: slug,
Description: clientReq.Description, Description: clientReq.Description,
ClientType: clientReq.ClientType, ClientType: clientReq.ClientType,
ParentClientId: clientReq.ParentClientId, ParentClientId: clientReq.ParentClientId,
@ -553,7 +567,7 @@ func (_i *clientsService) UploadLogo(authToken string, c *fiber.Ctx) (string, er
} }
// Upload logo using the upload service // Upload logo using the upload service
imagePath, newFilename, err := _i.ClientLogoUploadSvc.UploadLogo(c, clientId.String(), client.Name) imagePath, newFilename, err := _i.ClientLogoUploadSvc.UploadLogo(c, clientId.String(), client.Slug)
if err != nil { if err != nil {
_i.Log.Error().Err(err).Str("clientId", clientId.String()).Msg("Failed to upload client logo") _i.Log.Error().Err(err).Str("clientId", clientId.String()).Msg("Failed to upload client logo")
return "", err return "", err

View File

@ -0,0 +1,42 @@
-- Migration: Add slug field to clients table
-- Description: Adds a unique slug field to the clients table for URL-friendly client identification
ALTER TABLE clients
ADD COLUMN slug VARCHAR(255);
-- Add unique index on slug field
CREATE UNIQUE INDEX IF NOT EXISTS idx_clients_slug ON clients(slug);
-- Add comment
COMMENT ON COLUMN clients.slug IS 'URL-friendly slug generated from client name';
-- Update existing records with slugs based on their names
-- Note: This will generate slugs for existing clients
UPDATE clients
SET slug = LOWER(
REGEXP_REPLACE(
REGEXP_REPLACE(name, '[^a-zA-Z0-9\s-]', '', 'g'),
'\s+', '-', 'g'
)
)
WHERE slug IS NULL OR slug = '';
-- Handle potential duplicates by appending numbers
WITH numbered_slugs AS (
SELECT
id,
slug,
ROW_NUMBER() OVER (PARTITION BY slug ORDER BY created_at) as rn
FROM clients
WHERE slug IS NOT NULL
)
UPDATE clients
SET slug = CASE
WHEN ns.rn > 1 THEN ns.slug || '-' || ns.rn
ELSE ns.slug
END
FROM numbered_slugs ns
WHERE clients.id = ns.id AND ns.rn > 1;
-- Make slug field NOT NULL after populating existing data
ALTER TABLE clients ALTER COLUMN slug SET NOT NULL;