diff --git a/dto/about_dto.go b/dto/about_dto.go deleted file mode 100644 index c4df688..0000000 --- a/dto/about_dto.go +++ /dev/null @@ -1,66 +0,0 @@ -package dto - -import ( - "strings" -) - -type RequestAboutDTO struct { - Title string `json:"title"` - CoverImage string `json:"cover_image"` -} - -func (r *RequestAboutDTO) ValidateAbout() (map[string][]string, bool) { - errors := make(map[string][]string) - - if strings.TrimSpace(r.Title) == "" { - errors["title"] = append(errors["title"], "Title is required") - } - - if len(errors) > 0 { - return errors, false - } - - return nil, true -} - -type ResponseAboutDTO struct { - ID string `json:"id"` - Title string `json:"title"` - CoverImage string `json:"cover_image"` - AboutDetail *[]ResponseAboutDetailDTO `json:"about_detail"` - CreatedAt string `json:"created_at"` - UpdatedAt string `json:"updated_at"` -} - -type RequestAboutDetailDTO struct { - AboutId string `json:"about_id"` - ImageDetail string `json:"image_detail"` - Description string `json:"description"` -} - -func (r *RequestAboutDetailDTO) ValidateAboutDetail() (map[string][]string, bool) { - errors := make(map[string][]string) - - if strings.TrimSpace(r.AboutId) == "" { - errors["about_id"] = append(errors["about_id"], "about_id is required") - } - - if strings.TrimSpace(r.Description) == "" { - errors["description"] = append(errors["description"], "Description is required") - } - - if len(errors) > 0 { - return errors, false - } - - return nil, true -} - -type ResponseAboutDetailDTO struct { - ID string `json:"id"` - AboutID string `json:"about_id"` - ImageDetail string `json:"image_detail"` - Description string `json:"description"` - CreatedAt string `json:"created_at"` - UpdatedAt string `json:"updated_at"` -} diff --git a/dto/address_dto.go b/dto/address_dto.go deleted file mode 100644 index b1bdcd6..0000000 --- a/dto/address_dto.go +++ /dev/null @@ -1,73 +0,0 @@ -package dto - -import "strings" - -type AddressResponseDTO struct { - UserID string `json:"user_id,omitempty"` - ID string `json:"address_id,omitempty"` - Province string `json:"province,omitempty"` - Regency string `json:"regency,omitempty"` - District string `json:"district,omitempty"` - Village string `json:"village,omitempty"` - PostalCode string `json:"postalCode,omitempty"` - Detail string `json:"detail,omitempty"` - Latitude float64 `json:"latitude,omitempty"` - Longitude float64 `json:"longitude,omitempty"` - CreatedAt string `json:"createdAt,omitempty"` - UpdatedAt string `json:"updatedAt,omitempty"` -} - -type CreateAddressDTO struct { - Province string `json:"province_id"` - Regency string `json:"regency_id"` - District string `json:"district_id"` - Village string `json:"village_id"` - PostalCode string `json:"postalCode"` - Detail string `json:"detail"` - Latitude float64 `json:"latitude"` - Longitude float64 `json:"longitude"` -} - -func (r *CreateAddressDTO) ValidateAddress() (map[string][]string, bool) { - errors := make(map[string][]string) - - if strings.TrimSpace(r.Province) == "" { - errors["province_id"] = append(errors["province_id"], "Province ID is required") - } - - if strings.TrimSpace(r.Regency) == "" { - errors["regency_id"] = append(errors["regency_id"], "Regency ID is required") - } - - if strings.TrimSpace(r.District) == "" { - errors["district_id"] = append(errors["district_id"], "District ID is required") - } - - if strings.TrimSpace(r.Village) == "" { - errors["village_id"] = append(errors["village_id"], "Village ID is required") - } - - if strings.TrimSpace(r.PostalCode) == "" { - errors["postalCode"] = append(errors["postalCode"], "PostalCode is required") - } else if len(r.PostalCode) < 5 { - errors["postalCode"] = append(errors["postalCode"], "PostalCode must be at least 5 characters") - } - - if strings.TrimSpace(r.Detail) == "" { - errors["detail"] = append(errors["detail"], "Detail address is required") - } - - if r.Latitude == 0 { - errors["latitude"] = append(errors["latitude"], "Latitude is required") - } - - if r.Longitude == 0 { - errors["longitude"] = append(errors["longitude"], "Longitude is required") - } - - if len(errors) > 0 { - return errors, false - } - - return nil, true -} diff --git a/dto/article_dto.go b/dto/article_dto.go deleted file mode 100644 index 1df6ac2..0000000 --- a/dto/article_dto.go +++ /dev/null @@ -1,48 +0,0 @@ -package dto - -import ( - "strings" -) - -type ArticleResponseDTO struct { - ID string `json:"article_id"` - Title string `json:"title"` - CoverImage string `json:"coverImage"` - Author string `json:"author"` - Heading string `json:"heading"` - Content string `json:"content"` - PublishedAt string `json:"publishedAt"` - UpdatedAt string `json:"updatedAt"` -} - -type RequestArticleDTO struct { - Title string `json:"title"` - CoverImage string `json:"coverImage"` - Author string `json:"author"` - Heading string `json:"heading"` - Content string `json:"content"` -} - -func (r *RequestArticleDTO) Validate() (map[string][]string, bool) { - errors := make(map[string][]string) - - if strings.TrimSpace(r.Title) == "" { - errors["title"] = append(errors["title"], "Title is required") - } - - if strings.TrimSpace(r.Author) == "" { - errors["author"] = append(errors["author"], "Author is required") - } - if strings.TrimSpace(r.Heading) == "" { - errors["heading"] = append(errors["heading"], "Heading is required") - } - if strings.TrimSpace(r.Content) == "" { - errors["content"] = append(errors["content"], "Content is required") - } - - if len(errors) > 0 { - return errors, false - } - - return nil, true -} diff --git a/dto/auth/auth_admin_dto.go b/dto/auth/auth_admin_dto.go deleted file mode 100644 index b15a248..0000000 --- a/dto/auth/auth_admin_dto.go +++ /dev/null @@ -1,130 +0,0 @@ -package dto - -import ( - "regexp" - "strings" -) - -type LoginAdminRequest struct { - Deviceid string `json:"device_id"` - Email string `json:"email"` - Password string `json:"password"` -} - -type LoginResponse struct { - UserID string `json:"user_id"` - Role string `json:"role"` - Token string `json:"token"` -} - -type RegisterAdminRequest struct { - Name string `json:"name"` - Gender string `json:"gender"` - Dateofbirth string `json:"dateofbirth"` - Placeofbirth string `json:"placeofbirth"` - Phone string `json:"phone"` - Email string `json:"email"` - Password string `json:"password"` - PasswordConfirm string `json:"password_confirm"` -} - -type UserAdminDataResponse struct { - UserID string `json:"user_id"` - Name string `json:"name"` - Gender string `json:"gender"` - Dateofbirth string `json:"dateofbirth"` - Placeofbirth string `json:"placeofbirth"` - Phone string `json:"phone"` - Email string `json:"email"` - Password string `json:"password"` - Role string `json:"role"` - CreatedAt string `json:"createdAt"` - UpdatedAt string `json:"updatedAt"` -} - -func (r *RegisterAdminRequest) Validate() (map[string][]string, bool) { - errors := make(map[string][]string) - - if strings.TrimSpace(r.Name) == "" { - errors["name"] = append(errors["name"], "Name is required") - } - - if strings.TrimSpace(r.Gender) == "" { - errors["gender"] = append(errors["gender"], "Gender is required") - } else if r.Gender != "male" && r.Gender != "female" { - errors["gender"] = append(errors["gender"], "Gender must be either 'male' or 'female'") - } - - if strings.TrimSpace(r.Dateofbirth) == "" { - errors["dateofbirth"] = append(errors["dateofbirth"], "Date of birth is required") - } - - if strings.TrimSpace(r.Placeofbirth) == "" { - errors["placeofbirth"] = append(errors["placeofbirth"], "Place of birth is required") - } - - if strings.TrimSpace(r.Phone) == "" { - errors["phone"] = append(errors["phone"], "Phone is required") - } else if !IsValidPhoneNumber(r.Phone) { - errors["phone"] = append(errors["phone"], "Invalid phone number format. Use 62 followed by 9-13 digits") - } - - if strings.TrimSpace(r.Email) == "" { - errors["email"] = append(errors["email"], "Email is required") - } else if !IsValidEmail(r.Email) { - errors["email"] = append(errors["email"], "Invalid email format") - } - - if len(r.Password) < 6 { - errors["password"] = append(errors["password"], "Password must be at least 6 characters") - } else if !IsValidPassword(r.Password) { - errors["password"] = append(errors["password"], "Password must contain at least one uppercase letter, one number, and one special character") - } - - if r.Password != r.PasswordConfirm { - errors["password_confirm"] = append(errors["password_confirm"], "Password and confirmation do not match") - } - - if len(errors) > 0 { - return errors, false - } - return nil, true -} - -func IsValidPhoneNumber(phone string) bool { - re := regexp.MustCompile(`^62\d{9,13}$`) - return re.MatchString(phone) -} - -func IsValidEmail(email string) bool { - re := regexp.MustCompile(`^[a-z0-9]+@[a-z0-9]+\.[a-z]{2,}$`) - return re.MatchString(email) -} - -func IsValidPassword(password string) bool { - - if len(password) < 6 { - return false - } - - hasUpper := false - hasDigit := false - hasSpecial := false - - for _, char := range password { - if char >= 'A' && char <= 'Z' { - hasUpper = true - } else if char >= '0' && char <= '9' { - hasDigit = true - } else if isSpecialCharacter(char) { - hasSpecial = true - } - } - - return hasUpper && hasDigit && hasSpecial -} - -func isSpecialCharacter(char rune) bool { - specialChars := "!@#$%^&*()-_=+[]{}|;:'\",.<>?/`~" - return strings.ContainsRune(specialChars, char) -} diff --git a/dto/auth/auth_masyarakat_dto.go b/dto/auth/auth_masyarakat_dto.go deleted file mode 100644 index cbd8f99..0000000 --- a/dto/auth/auth_masyarakat_dto.go +++ /dev/null @@ -1 +0,0 @@ -package dto \ No newline at end of file diff --git a/dto/auth/auth_pengelola_dto.go b/dto/auth/auth_pengelola_dto.go deleted file mode 100644 index 58c916c..0000000 --- a/dto/auth/auth_pengelola_dto.go +++ /dev/null @@ -1,133 +0,0 @@ -package dto - -import ( - "regexp" - "rijig/utils" -) - -type LoginPengelolaRequest struct { - Phone string `json:"phone"` -} - -func (r *LoginPengelolaRequest) ValidateLogin() (map[string][]string, bool) { - errors := make(map[string][]string) - - if r.Phone == "" { - errors["phone"] = append(errors["phone"], "Phone number is required") - } else if !utils.IsValidPhoneNumber(r.Phone) { - errors["phone"] = append(errors["phone"], "Phone number is not valid") - } - - if len(errors) > 0 { - return errors, false - } - return nil, true -} - -type VerifLoginPengelolaRequest struct { - Phone string `json:"phone"` - Otp string `json:"verif_otp"` -} - -func (r *VerifLoginPengelolaRequest) ValidateVerifLogin() (map[string][]string, bool) { - errors := make(map[string][]string) - - if r.Phone == "" { - errors["phone"] = append(errors["phone"], "Phone number is required") - } else if !utils.IsValidPhoneNumber(r.Phone) { - errors["phone"] = append(errors["phone"], "Phone number is not valid") - } - - if r.Otp == "" { - errors["otp"] = append(errors["otp"], "OTP is required") - } else if len(r.Otp) != 6 { - errors["otp"] = append(errors["otp"], "OTP must be 6 digits") - } - - if len(errors) > 0 { - return errors, false - } - return nil, true -} - -type LoginPengelolaResponse struct { - UserID string `json:"user_id"` - Role string `json:"role"` - Token string `json:"token"` -} - -type PengelolaIdentityCard struct { - Cardphoto string `json:"cardphoto"` - Identificationumber string `json:"identificationumber"` - Placeofbirth string `json:"placeofbirth"` - Dateofbirth string `json:"dateofbirth"` - Gender string `json:"gender"` - BloodType string `json:"bloodtype"` - District string `json:"district"` - Village string `json:"village"` - Neighbourhood string `json:"neighbourhood"` - Religion string `json:"religion"` - Maritalstatus string `json:"maritalstatus"` - Job string `json:"job"` - Citizenship string `json:"citizenship"` - Validuntil string `json:"validuntil"` -} - -func (r *PengelolaIdentityCard) ValidateIDcard() (map[string][]string, bool) { - errors := make(map[string][]string) - - if r.Cardphoto == "" { - errors["cardphoto"] = append(errors["cardphoto"], "Card photo is required") - } - - if r.Identificationumber == "" { - errors["identificationumber"] = append(errors["identificationumber"], "Identification number is required") - } - - if r.Dateofbirth == "" { - errors["dateofbirth"] = append(errors["dateofbirth"], "Date of birth is required") - } else if !isValidDate(r.Dateofbirth) { - errors["dateofbirth"] = append(errors["dateofbirth"], "Date of birth must be in DD-MM-YYYY format") - } - - if len(errors) > 0 { - return errors, false - } - return nil, true -} - -type PengelolaCompanyProfile struct { - CompanyName string `json:"company_name"` - CompanyPhone string `json:"company_phone"` - CompanyEmail string `json:"company_email"` -} - -func (r *PengelolaCompanyProfile) ValidateCompany() (map[string][]string, bool) { - errors := make(map[string][]string) - - if r.CompanyName == "" { - errors["company_name"] = append(errors["company_name"], "Company name is required") - } - - if r.CompanyPhone == "" { - errors["company_phone"] = append(errors["company_phone"], "Company phone is required") - } else if !utils.IsValidPhoneNumber(r.CompanyPhone) { - errors["company_phone"] = append(errors["company_phone"], "Invalid phone number format") - } - - if r.CompanyEmail == "" { - errors["company_email"] = append(errors["company_email"], "Company email is required") - } else if !utils.IsValidEmail(r.CompanyEmail) { - errors["company_email"] = append(errors["company_email"], "Invalid email format") - } - - if len(errors) > 0 { - return errors, false - } - return nil, true -} - -func isValidDate(date string) bool { - re := regexp.MustCompile(`^\d{2}-\d{2}-\d{4}$`) - return re.MatchString(date) -} diff --git a/dto/auth/auth_pengepul_dto.go b/dto/auth/auth_pengepul_dto.go deleted file mode 100644 index cbd8f99..0000000 --- a/dto/auth/auth_pengepul_dto.go +++ /dev/null @@ -1 +0,0 @@ -package dto \ No newline at end of file diff --git a/dto/auth_dto.go b/dto/auth_dto.go deleted file mode 100644 index 5782667..0000000 --- a/dto/auth_dto.go +++ /dev/null @@ -1,42 +0,0 @@ -package dto - -import ( - "regexp" - "strings" -) - -type RegisterRequest struct { - Phone string `json:"phone"` -} - -type VerifyOTPRequest struct { - Phone string `json:"phone"` - OTP string `json:"otp"` - DeviceID string `json:"device_id"` -} - -type UserDataResponse struct { - UserID string `json:"user_id"` - UserRole string `json:"user_role"` - Token string `json:"token"` -} - -func (r *RegisterRequest) Validate() (map[string][]string, bool) { - errors := make(map[string][]string) - - if strings.TrimSpace(r.Phone) == "" { - errors["phone"] = append(errors["phone"], "Phone is required") - } else if !IsValidPhoneNumber(r.Phone) { - errors["phone"] = append(errors["phone"], "Invalid phone number format. Use 62 followed by 9-13 digits") - } - - if len(errors) > 0 { - return errors, false - } - return nil, true -} - -func IsValidPhoneNumber(phone string) bool { - re := regexp.MustCompile(`^62\d{9,13}$`) - return re.MatchString(phone) -} diff --git a/dto/banner_dto.go b/dto/banner_dto.go deleted file mode 100644 index 56f214c..0000000 --- a/dto/banner_dto.go +++ /dev/null @@ -1,29 +0,0 @@ -package dto - -import "strings" - -type ResponseBannerDTO struct { - ID string `json:"id"` - BannerName string `json:"bannername"` - BannerImage string `json:"bannerimage"` - CreatedAt string `json:"createdAt"` - UpdatedAt string `json:"updatedAt"` -} - -type RequestBannerDTO struct { - BannerName string `json:"bannername"` - BannerImage string `json:"bannerimage"` -} - -func (r *RequestBannerDTO) ValidateBannerInput() (map[string][]string, bool) { - errors := make(map[string][]string) - - if strings.TrimSpace(r.BannerName) == "" { - errors["bannername"] = append(errors["bannername"], "nama banner harus diisi") - } - if len(errors) > 0 { - return errors, false - } - - return nil, true -} diff --git a/dto/collector_dto.go b/dto/collector_dto.go deleted file mode 100644 index 0a523d7..0000000 --- a/dto/collector_dto.go +++ /dev/null @@ -1,111 +0,0 @@ -package dto - -import ( - "fmt" - "strings" -) - -type NearbyCollectorDTO struct { - CollectorID string `json:"collector_id"` - Name string `json:"name"` - Phone string `json:"phone"` - Rating float32 `json:"rating"` - Latitude float64 `json:"latitude"` - Longitude float64 `json:"longitude"` - DistanceKm float64 `json:"distance_km"` - MatchedTrash []string `json:"matched_trash_ids"` -} - -type RequestCollectorDTO struct { - AddressId string `json:"address_id"` - AvaibleTrashbyCollector []RequestAvaibleTrashbyCollector `json:"avaible_trash"` -} - -type RequestAvaibleTrashbyCollector struct { - TrashId string `json:"trash_id"` - TrashPrice float32 `json:"trash_price"` -} - -type RequestAddAvaibleTrash struct { - AvaibleTrash []RequestAvaibleTrashbyCollector `json:"avaible_trash"` -} - -type SelectCollectorRequest struct { - Collector_id string `json:"collector_id"` -} - -func (r *SelectCollectorRequest) ValidateSelectCollectorRequest() (map[string][]string, bool) { - errors := make(map[string][]string) - - if strings.TrimSpace(r.Collector_id) == "" { - errors["collector_id"] = append(errors["collector_id"], "collector_id harus diisi") - } - if len(errors) > 0 { - return errors, false - } - - return nil, true -} - -func (r *RequestAddAvaibleTrash) ValidateRequestAddAvaibleTrash() (map[string][]string, bool) { - errors := make(map[string][]string) - - if len(r.AvaibleTrash) == 0 { - errors["avaible_trash"] = append(errors["avaible_trash"], "tidak boleh kosong") - } - - for i, trash := range r.AvaibleTrash { - if strings.TrimSpace(trash.TrashId) == "" { - errors[fmt.Sprintf("avaible_trash[%d].trash_id", i)] = append(errors[fmt.Sprintf("avaible_trash[%d].trash_id", i)], "trash_id tidak boleh kosong") - } - if trash.TrashPrice <= 0 { - errors[fmt.Sprintf("avaible_trash[%d].trash_price", i)] = append(errors[fmt.Sprintf("avaible_trash[%d].trash_price", i)], "trash_price harus lebih dari 0") - } - } - - if len(errors) > 0 { - return errors, false - } - return nil, true -} - -type ResponseCollectorDTO struct { - ID string `json:"collector_id"` - UserId string `json:"user_id"` - User *UserResponseDTO `json:"user,omitempty"` - AddressId string `json:"address_id"` - Address *AddressResponseDTO `json:"address,omitempty"` - JobStatus *string `json:"job_status,omitempty"` - Rating float32 `json:"rating"` - AvaibleTrashbyCollector []ResponseAvaibleTrashByCollector `json:"avaible_trash"` -} - -type ResponseAvaibleTrashByCollector struct { - ID string `json:"id"` - TrashId string `json:"trash_id"` - TrashName string `json:"trash_name"` - TrashIcon string `json:"trash_icon"` - TrashPrice float32 `json:"trash_price"` -} - -func (r *RequestCollectorDTO) ValidateRequestCollector() (map[string][]string, bool) { - errors := make(map[string][]string) - - if strings.TrimSpace(r.AddressId) == "" { - errors["address_id"] = append(errors["address_id"], "address_id harus diisi") - } - - for i, trash := range r.AvaibleTrashbyCollector { - if strings.TrimSpace(trash.TrashId) == "" { - errors[fmt.Sprintf("avaible_trash[%d].trash_id", i)] = append(errors[fmt.Sprintf("avaible_trash[%d].trash_id", i)], "trash_id tidak boleh kosong") - } - if trash.TrashPrice <= 0 { - errors[fmt.Sprintf("avaible_trash[%d].trash_price", i)] = append(errors[fmt.Sprintf("avaible_trash[%d].trash_price", i)], "trash_price harus lebih dari 0") - } - } - - if len(errors) > 0 { - return errors, false - } - return nil, true -} diff --git a/dto/company_profile_dto.go b/dto/company_profile_dto.go deleted file mode 100644 index f3cedd8..0000000 --- a/dto/company_profile_dto.go +++ /dev/null @@ -1,62 +0,0 @@ -package dto - -import ( - "rijig/utils" - "strings" -) - -type ResponseCompanyProfileDTO struct { - ID string `json:"id"` - UserID string `json:"userId"` - CompanyName string `json:"company_name"` - CompanyAddress string `json:"company_address"` - CompanyPhone string `json:"company_phone"` - CompanyEmail string `json:"company_email"` - CompanyLogo string `json:"company_logo,omitempty"` - CompanyWebsite string `json:"company_website,omitempty"` - TaxID string `json:"taxId,omitempty"` - FoundedDate string `json:"founded_date,omitempty"` - CompanyType string `json:"company_type,omitempty"` - CompanyDescription string `json:"company_description"` - CreatedAt string `json:"createdAt"` - UpdatedAt string `json:"updatedAt"` -} - -type RequestCompanyProfileDTO struct { - CompanyName string `json:"company_name"` - CompanyAddress string `json:"company_address"` - CompanyPhone string `json:"company_phone"` - CompanyEmail string `json:"company_email"` - CompanyLogo string `json:"company_logo,omitempty"` - CompanyWebsite string `json:"company_website,omitempty"` - TaxID string `json:"taxId,omitempty"` - FoundedDate string `json:"founded_date,omitempty"` - CompanyType string `json:"company_type,omitempty"` - CompanyDescription string `json:"company_description"` -} - -func (r *RequestCompanyProfileDTO) ValidateCompanyProfileInput() (map[string][]string, bool) { - errors := make(map[string][]string) - - if strings.TrimSpace(r.CompanyName) == "" { - errors["company_Name"] = append(errors["company_name"], "Company name is required") - } - - if strings.TrimSpace(r.CompanyAddress) == "" { - errors["company_Address"] = append(errors["company_address"], "Company address is required") - } - - if !utils.IsValidPhoneNumber(r.CompanyPhone) { - errors["company_Phone"] = append(errors["company_phone"], "nomor harus dimulai 62.. dan 8-14 digit") - } - - if strings.TrimSpace(r.CompanyDescription) == "" { - errors["company_Description"] = append(errors["company_description"], "Company description is required") - } - - if len(errors) > 0 { - return errors, false - } - - return nil, true -} diff --git a/dto/coveragearea_dto.go b/dto/coveragearea_dto.go deleted file mode 100644 index 3d1f46d..0000000 --- a/dto/coveragearea_dto.go +++ /dev/null @@ -1,34 +0,0 @@ -package dto - -import "strings" - -type RequestCoverageArea struct { - Province string `json:"province"` - Regency string `json:"regency"` -} - -type ResponseCoverageArea struct { - ID string `json:"id"` - Province string `json:"province"` - Regency string `json:"regency"` - CreatedAt string `json:"createdAt"` - UpdatedAt string `json:"updatedAt"` -} - -func (r *RequestCoverageArea) ValidateCoverageArea() (map[string][]string, bool) { - errors := make(map[string][]string) - - if strings.TrimSpace(r.Province) == "" { - errors["province"] = append(errors["province"], "nama provinsi harus diisi") - } - - if strings.TrimSpace(r.Regency) == "" { - errors["regency"] = append(errors["regency"], "nama regency harus diisi") - } - - if len(errors) > 0 { - return errors, false - } - - return nil, true -} diff --git a/dto/identitycard_dto.go b/dto/identitycard_dto.go deleted file mode 100644 index aa4274e..0000000 --- a/dto/identitycard_dto.go +++ /dev/null @@ -1,116 +0,0 @@ -package dto - -import ( - "strings" -) - -type ResponseIdentityCardDTO struct { - ID string `json:"id"` - UserID string `json:"userId"` - Identificationumber string `json:"identificationumber"` - Placeofbirth string `json:"placeofbirth"` - Dateofbirth string `json:"dateofbirth"` - Gender string `json:"gender"` - BloodType string `json:"bloodtype"` - District string `json:"district"` - Village string `json:"village"` - Neighbourhood string `json:"neighbourhood"` - Religion string `json:"religion"` - Maritalstatus string `json:"maritalstatus"` - Job string `json:"job"` - Citizenship string `json:"citizenship"` - Validuntil string `json:"validuntil"` - Cardphoto string `json:"cardphoto"` - CreatedAt string `json:"createdAt"` - UpdatedAt string `json:"updatedAt"` -} - -type RequestIdentityCardDTO struct { - UserID string `json:"userId"` - Identificationumber string `json:"identificationumber"` - Placeofbirth string `json:"placeofbirth"` - Dateofbirth string `json:"dateofbirth"` - Gender string `json:"gender"` - BloodType string `json:"bloodtype"` - District string `json:"district"` - Village string `json:"village"` - Neighbourhood string `json:"neighbourhood"` - Religion string `json:"religion"` - Maritalstatus string `json:"maritalstatus"` - Job string `json:"job"` - Citizenship string `json:"citizenship"` - Validuntil string `json:"validuntil"` - Cardphoto string `json:"cardphoto"` -} - -func (r *RequestIdentityCardDTO) ValidateIdentityCardInput() (map[string][]string, bool) { - errors := make(map[string][]string) - isValid := true - - if strings.TrimSpace(r.Identificationumber) == "" { - errors["identificationumber"] = append(errors["identificationumber"], "Nomor identifikasi harus diisi") - isValid = false - } - - if strings.TrimSpace(r.Placeofbirth) == "" { - errors["placeofbirth"] = append(errors["placeofbirth"], "Tempat lahir harus diisi") - isValid = false - } - - if strings.TrimSpace(r.Dateofbirth) == "" { - errors["dateofbirth"] = append(errors["dateofbirth"], "Tanggal lahir harus diisi") - isValid = false - } - - if strings.TrimSpace(r.Gender) == "" { - errors["gender"] = append(errors["gender"], "Jenis kelamin harus diisi") - isValid = false - } - - if strings.TrimSpace(r.BloodType) == "" { - errors["bloodtype"] = append(errors["bloodtype"], "Golongan darah harus diisi") - isValid = false - } - - if strings.TrimSpace(r.District) == "" { - errors["district"] = append(errors["district"], "Kecamatan harus diisi") - isValid = false - } - - if strings.TrimSpace(r.Village) == "" { - errors["village"] = append(errors["village"], "Desa harus diisi") - isValid = false - } - - if strings.TrimSpace(r.Neighbourhood) == "" { - errors["neighbourhood"] = append(errors["neighbourhood"], "RT/RW harus diisi") - isValid = false - } - - if strings.TrimSpace(r.Religion) == "" { - errors["religion"] = append(errors["religion"], "Agama harus diisi") - isValid = false - } - - if strings.TrimSpace(r.Maritalstatus) == "" { - errors["maritalstatus"] = append(errors["maritalstatus"], "Status pernikahan harus diisi") - isValid = false - } - - if strings.TrimSpace(r.Job) == "" { - errors["job"] = append(errors["job"], "Pekerjaan harus diisi") - isValid = false - } - - if strings.TrimSpace(r.Citizenship) == "" { - errors["citizenship"] = append(errors["citizenship"], "Kewarganegaraan harus diisi") - isValid = false - } - - if strings.TrimSpace(r.Validuntil) == "" { - errors["validuntil"] = append(errors["validuntil"], "Masa berlaku harus diisi") - isValid = false - } - - return errors, isValid -} diff --git a/dto/initialcoint_dto.go b/dto/initialcoint_dto.go deleted file mode 100644 index 06d1fb2..0000000 --- a/dto/initialcoint_dto.go +++ /dev/null @@ -1,34 +0,0 @@ -package dto - -import "strings" - -type ReponseInitialCointDTO struct { - ID string `json:"coin_id"` - CoinName string `json:"coin_name"` - ValuePerUnit float64 `json:"value_perunit"` - CreatedAt string `json:"createdAt"` - UpdatedAt string `json:"updatedAt"` -} - -type RequestInitialCointDTO struct { - CoinName string `json:"coin_name"` - ValuePerUnit float64 `json:"value_perunit"` -} - -func (r *RequestInitialCointDTO) ValidateCointInput() (map[string][]string, bool) { - errors := make(map[string][]string) - - if strings.TrimSpace(r.CoinName) == "" { - errors["coin_name"] = append(errors["coin_name"], "nama coin harus diisi") - } - - if r.ValuePerUnit <= 0 { - errors["value_perunit"] = append(errors["value_perunit"], "value per unit harus lebih besar dari 0") - } - - if len(errors) > 0 { - return errors, false - } - - return nil, true -} diff --git a/dto/product_dto.go b/dto/product_dto.go deleted file mode 100644 index 66a5a58..0000000 --- a/dto/product_dto.go +++ /dev/null @@ -1,55 +0,0 @@ -package dto - -import ( - "mime/multipart" - "regexp" - "strings" -) - -type ResponseProductImageDTO struct { - ID string `json:"id"` - ProductID string `json:"productId"` - ImageURL string `json:"imageURL"` -} - -type ResponseProductDTO struct { - ID string `json:"id"` - StoreID string `json:"storeId"` - ProductName string `json:"productName"` - Quantity int `json:"quantity"` - Saled int `json:"saled"` - ProductImages []ResponseProductImageDTO `json:"productImages,omitempty"` - CreatedAt string `json:"createdAt"` - UpdatedAt string `json:"updatedAt"` -} - -type RequestProductDTO struct { - ProductName string `json:"product_name"` - Quantity int `json:"quantity"` - ProductImages []*multipart.FileHeader `json:"product_images,omitempty"` -} - -func (r *RequestProductDTO) ValidateProductInput() (map[string][]string, bool) { - errors := make(map[string][]string) - - if strings.TrimSpace(r.ProductName) == "" { - errors["product_name"] = append(errors["product_name"], "Product name is required") - } else if len(r.ProductName) < 3 { - errors["product_name"] = append(errors["product_name"], "Product name must be at least 3 characters long") - } else { - validNameRegex := `^[a-zA-Z0-9\s_.-]+$` - if matched, _ := regexp.MatchString(validNameRegex, r.ProductName); !matched { - errors["product_name"] = append(errors["product_name"], "Product name can only contain letters, numbers, spaces, underscores, and dashes") - } - } - - if r.Quantity < 1 { - errors["quantity"] = append(errors["quantity"], "Quantity must be at least 1") - } - - if len(errors) > 0 { - return errors, false - } - - return nil, true -} diff --git a/dto/rating_dto.go b/dto/rating_dto.go deleted file mode 100644 index 62b680d..0000000 --- a/dto/rating_dto.go +++ /dev/null @@ -1,25 +0,0 @@ -package dto - -import "strings" - -type CreatePickupRatingDTO struct { - Rating float32 `json:"rating"` - Feedback string `json:"feedback"` -} - -func (r *CreatePickupRatingDTO) ValidateCreatePickupRatingDTO() (map[string][]string, bool) { - errors := make(map[string][]string) - - if r.Rating < 1.0 || r.Rating > 5.0 { - errors["rating"] = append(errors["rating"], "Rating harus antara 1.0 sampai 5.0") - } - - if len(strings.TrimSpace(r.Feedback)) > 255 { - errors["feedback"] = append(errors["feedback"], "Feedback tidak boleh lebih dari 255 karakter") - } - - if len(errors) > 0 { - return errors, false - } - return nil, true -} diff --git a/dto/request_pickup_dto.go b/dto/request_pickup_dto.go deleted file mode 100644 index ee63b38..0000000 --- a/dto/request_pickup_dto.go +++ /dev/null @@ -1,74 +0,0 @@ -package dto - -import ( - "strings" -) - -type SelectCollectorDTO struct { - CollectorID string `json:"collector_id"` -} - -type UpdateRequestPickupItemDTO struct { - ItemID string `json:"item_id"` - Amount float64 `json:"actual_amount"` -} - -type UpdatePickupItemsRequest struct { - Items []UpdateRequestPickupItemDTO `json:"items"` -} - -func (r *SelectCollectorDTO) Validate() (map[string][]string, bool) { - errors := make(map[string][]string) - - if strings.TrimSpace(r.CollectorID) == "" { - errors["collector_id"] = append(errors["collector_id"], "collector_id tidak boleh kosong") - } - - if len(errors) > 0 { - return errors, false - } - return nil, true -} - -type AssignedPickupDTO struct { - PickupID string `json:"pickup_id"` - UserID string `json:"user_id"` - UserName string `json:"user_name"` - Latitude float64 `json:"latitude"` - Longitude float64 `json:"longitude"` - Notes string `json:"notes"` - MatchedTrash []string `json:"matched_trash"` -} - -type PickupRequestForCollectorDTO struct { - PickupID string `json:"pickup_id"` - UserID string `json:"user_id"` - Latitude float64 `json:"latitude"` - Longitude float64 `json:"longitude"` - DistanceKm float64 `json:"distance_km"` - MatchedTrash []string `json:"matched_trash"` -} - -type RequestPickupDTO struct { - AddressID string `json:"address_id"` - RequestMethod string `json:"request_method"` - Notes string `json:"notes,omitempty"` -} - -func (r *RequestPickupDTO) Validate() (map[string][]string, bool) { - errors := make(map[string][]string) - - if strings.TrimSpace(r.AddressID) == "" { - errors["address_id"] = append(errors["address_id"], "alamat harus dipilih") - } - - method := strings.ToLower(strings.TrimSpace(r.RequestMethod)) - if method != "manual" && method != "otomatis" { - errors["request_method"] = append(errors["request_method"], "harus manual atau otomatis") - } - - if len(errors) > 0 { - return errors, false - } - return nil, true -} diff --git a/dto/requestpickup_dto.go b/dto/requestpickup_dto.go deleted file mode 100644 index 9ac69bd..0000000 --- a/dto/requestpickup_dto.go +++ /dev/null @@ -1,88 +0,0 @@ -package dto - -import ( - "fmt" - "strings" -) - -type RequestPickup struct { - AddressID string `json:"address_id"` - RequestMethod string `json:"request_method"` - EvidenceImage string `json:"evidence_image"` - Notes string `json:"notes"` - RequestItems []RequestPickupItem `json:"request_items"` -} - -type RequestPickupItem struct { - TrashCategoryID string `json:"trash_category_id"` - EstimatedAmount float64 `json:"estimated_amount"` -} - -type ResponseRequestPickup struct { - ID string `json:"id,omitempty"` - UserId string `json:"user_id,omitempty"` - User []UserResponseDTO `json:"user,omitempty"` - AddressID string `json:"address_id,omitempty"` - Address []AddressResponseDTO `json:"address,omitempty"` - EvidenceImage string `json:"evidence_image,omitempty"` - Notes string `json:"notes,omitempty"` - StatusPickup string `json:"status_pickup,omitempty"` - CollectorID string `json:"collectorid,omitempty"` - Collector []ResponseCollectorDTO `json:"collector,omitempty"` - ConfirmedByCollectorAt string `json:"confirmedat,omitempty"` - CreatedAt string `json:"created_at,omitempty"` - UpdatedAt string `json:"updated_at,omitempty"` - RequestItems []ResponseRequestPickupItem `json:"request_items,omitempty"` -} - -type ResponseRequestPickupItem struct { - ID string `json:"id,omitempty"` - TrashCategoryID string `json:"trash_category_id,omitempty"` - // TrashCategory []ResponseTrashCategoryDTO `json:"trash_category,omitempty"` - EstimatedAmount float64 `json:"estimated_amount,omitempty"` -} - -func (r *RequestPickup) ValidateRequestPickup() (map[string][]string, bool) { - errors := make(map[string][]string) - - if len(r.RequestItems) == 0 { - errors["request_items"] = append(errors["request_items"], "At least one item must be provided") - } - - if strings.TrimSpace(r.AddressID) == "" { - errors["address_id"] = append(errors["address_id"], "Address ID must be provided") - } - - for i, item := range r.RequestItems { - itemErrors, valid := item.ValidateRequestPickupItem(i) - if !valid { - for field, msgs := range itemErrors { - errors[field] = append(errors[field], msgs...) - } - } - } - - if len(errors) > 0 { - return errors, false - } - - return nil, true -} - -func (r *RequestPickupItem) ValidateRequestPickupItem(index int) (map[string][]string, bool) { - errors := make(map[string][]string) - - if strings.TrimSpace(r.TrashCategoryID) == "" { - errors["trash_category_id"] = append(errors["trash_category_id"], fmt.Sprintf("Trash category ID cannot be empty (Item %d)", index+1)) - } - - if r.EstimatedAmount < 2 { - errors["estimated_amount"] = append(errors["estimated_amount"], fmt.Sprintf("Estimated amount must be >= 2.0 kg (Item %d)", index+1)) - } - - if len(errors) > 0 { - return errors, false - } - - return nil, true -} diff --git a/dto/role_dto.go b/dto/role_dto.go deleted file mode 100644 index 6002fc7..0000000 --- a/dto/role_dto.go +++ /dev/null @@ -1,8 +0,0 @@ -package dto - -type RoleResponseDTO struct { - ID string `json:"role_id"` - RoleName string `json:"role_name"` - CreatedAt string `json:"createdAt"` - UpdatedAt string `json:"updatedAt"` -} diff --git a/dto/store_dto.go b/dto/store_dto.go deleted file mode 100644 index f5fd606..0000000 --- a/dto/store_dto.go +++ /dev/null @@ -1,68 +0,0 @@ -package dto - -import ( - "regexp" - "strings" -) - -type ResponseStoreDTO struct { - ID string `json:"id"` - UserID string `json:"userId"` - StoreName string `json:"storeName"` - StoreLogo string `json:"storeLogo"` - StoreBanner string `json:"storeBanner"` - StoreInfo string `json:"storeInfo"` - StoreAddressID string `json:"storeAddressId"` - TotalProduct int `json:"TotalProduct"` - Followers int `json:"followers"` - CreatedAt string `json:"createdAt"` - UpdatedAt string `json:"updatedAt"` -} - -type RequestStoreDTO struct { - StoreName string `json:"store_name"` - StoreLogo string `json:"store_logo"` - StoreBanner string `json:"store_banner"` - StoreInfo string `json:"store_info"` - StoreAddressID string `json:"store_address_id"` -} - -func (r *RequestStoreDTO) ValidateStoreInput() (map[string][]string, bool) { - errors := make(map[string][]string) - - if strings.TrimSpace(r.StoreName) == "" { - errors["store_name"] = append(errors["store_name"], "Store name is required") - } else if len(r.StoreName) < 3 { - errors["store_name"] = append(errors["store_name"], "Store name must be at least 3 characters long") - } else { - validNameRegex := `^[a-zA-Z0-9_.\s]+$` - if matched, _ := regexp.MatchString(validNameRegex, r.StoreName); !matched { - errors["store_name"] = append(errors["store_name"], "Store name can only contain letters, numbers, underscores, and periods") - } - } - - if strings.TrimSpace(r.StoreLogo) == "" { - errors["store_logo"] = append(errors["store_logo"], "Store logo is required") - } - - if strings.TrimSpace(r.StoreBanner) == "" { - errors["store_banner"] = append(errors["store_banner"], "Store banner is required") - } - - if strings.TrimSpace(r.StoreInfo) == "" { - errors["store_info"] = append(errors["store_info"], "Store info is required") - } - - uuidRegex := `^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$` - if r.StoreAddressID == "" { - errors["store_address_id"] = append(errors["store_address_id"], "Store address ID is required") - } else if matched, _ := regexp.MatchString(uuidRegex, r.StoreAddressID); !matched { - errors["store_address_id"] = append(errors["store_address_id"], "Invalid Store Address ID format") - } - - if len(errors) > 0 { - return errors, false - } - - return nil, true -} diff --git a/dto/trash_dto.go b/dto/trash_dto.go deleted file mode 100644 index 15d8fcc..0000000 --- a/dto/trash_dto.go +++ /dev/null @@ -1,65 +0,0 @@ -package dto -/* -import ( - "strings" -) - -type RequestTrashCategoryDTO struct { - Name string `json:"name"` - EstimatedPrice string `json:"estimatedprice"` - Icon string `json:"icon"` -} - -type ResponseTrashCategoryDTO struct { - ID string `json:"id,omitempty"` - Name string `json:"name,omitempty"` - Icon string `json:"icon,omitempty"` - EstimatedPrice float64 `json:"estimatedprice"` - CreatedAt string `json:"createdAt,omitempty"` - UpdatedAt string `json:"updatedAt,omitempty"` - Details []ResponseTrashDetailDTO `json:"details,omitempty"` -} - -type ResponseTrashDetailDTO struct { - ID string `json:"id"` - CategoryID string `json:"category_id"` - Description string `json:"description"` - Price float64 `json:"price"` - CreatedAt string `json:"createdAt"` - UpdatedAt string `json:"updatedAt"` -} - -type RequestTrashDetailDTO struct { - CategoryID string `json:"category_id"` - Description string `json:"description"` - Price float64 `json:"price"` -} - -func (r *RequestTrashCategoryDTO) ValidateTrashCategoryInput() (map[string][]string, bool) { - errors := make(map[string][]string) - - if strings.TrimSpace(r.Name) == "" { - errors["name"] = append(errors["name"], "name is required") - } - - if len(errors) > 0 { - return errors, false - } - - return nil, true -} - -func (r *RequestTrashDetailDTO) ValidateTrashDetailInput() (map[string][]string, bool) { - errors := make(map[string][]string) - - if strings.TrimSpace(r.Description) == "" { - errors["description"] = append(errors["description"], "description is required") - } - - if len(errors) > 0 { - return errors, false - } - - return nil, true -} - */ \ No newline at end of file diff --git a/dto/trashcart_dto.go b/dto/trashcart_dto.go deleted file mode 100644 index d195a00..0000000 --- a/dto/trashcart_dto.go +++ /dev/null @@ -1,49 +0,0 @@ -package dto - -import ( - "fmt" - "strings" -) - -type RequestCartItemDTO struct { - TrashID string `json:"trash_id"` - Amount float64 `json:"amount"` -} - -type RequestCartDTO struct { - CartItems []RequestCartItemDTO `json:"cart_items"` -} - -type ResponseCartDTO struct { - ID string `json:"id"` - UserID string `json:"user_id"` - TotalAmount float64 `json:"total_amount"` - EstimatedTotalPrice float64 `json:"estimated_total_price"` - CartItems []ResponseCartItemDTO `json:"cart_items"` -} - -type ResponseCartItemDTO struct { - ID string `json:"id"` - TrashID string `json:"trash_id"` - TrashName string `json:"trash_name"` - TrashIcon string `json:"trash_icon"` - TrashPrice float64 `json:"trash_price"` - Amount float64 `json:"amount"` - SubTotalEstimatedPrice float64 `json:"subtotal_estimated_price"` -} - -func (r *RequestCartDTO) ValidateRequestCartDTO() (map[string][]string, bool) { - errors := make(map[string][]string) - - for i, item := range r.CartItems { - if strings.TrimSpace(item.TrashID) == "" { - errors[fmt.Sprintf("cart_items[%d].trash_id", i)] = append(errors[fmt.Sprintf("cart_items[%d].trash_id", i)], "trash_id tidak boleh kosong") - } - } - - if len(errors) > 0 { - return errors, false - } - - return nil, true -} diff --git a/dto/user_dto.go b/dto/user_dto.go deleted file mode 100644 index 3a1164c..0000000 --- a/dto/user_dto.go +++ /dev/null @@ -1,81 +0,0 @@ -package dto - -import ( - "rijig/utils" - "strings" -) - -type UserResponseDTO struct { - ID string `json:"id,omitempty"` - Username string `json:"username,omitempty"` - Avatar *string `json:"photoprofile,omitempty"` - Name string `json:"name,omitempty"` - Phone string `json:"phone,omitempty"` - Email string `json:"email,omitempty"` - EmailVerified bool `json:"emailVerified,omitempty"` - RoleName string `json:"role,omitempty"` - CreatedAt string `json:"createdAt,omitempty"` - UpdatedAt string `json:"updatedAt,omitempty"` -} - -type RequestUserDTO struct { - Name string `json:"name"` - Phone string `json:"phone"` - Email string `json:"email"` -} - -func (r *RequestUserDTO) Validate() (map[string][]string, bool) { - errors := make(map[string][]string) - - if strings.TrimSpace(r.Name) == "" { - errors["name"] = append(errors["name"], "Name is required") - } - - if strings.TrimSpace(r.Phone) == "" { - errors["phone"] = append(errors["phone"], "Phone number is required") - } else if !utils.IsValidPhoneNumber(r.Phone) { - errors["phone"] = append(errors["phone"], "Invalid phone number format. Use +62 followed by 9-13 digits") - } - - if strings.TrimSpace(r.Email) != "" && !utils.IsValidEmail(r.Email) { - errors["email"] = append(errors["email"], "Invalid email format") - } - - if len(errors) > 0 { - return errors, false - } - - return nil, true -} - -type UpdatePasswordDTO struct { - OldPassword string `json:"old_password"` - NewPassword string `json:"new_password"` - ConfirmNewPassword string `json:"confirm_new_password"` -} - -func (u *UpdatePasswordDTO) Validate() (map[string][]string, bool) { - errors := make(map[string][]string) - - if u.OldPassword == "" { - errors["old_password"] = append(errors["old_password"], "Old password is required") - } - - if u.NewPassword == "" { - errors["new_password"] = append(errors["new_password"], "New password is required") - } else if !utils.IsValidPassword(u.NewPassword) { - errors["new_password"] = append(errors["new_password"], "Password must contain at least one uppercase letter, one digit, and one special character") - } - - if u.ConfirmNewPassword == "" { - errors["confirm_new_password"] = append(errors["confirm_new_password"], "Confirm new password is required") - } else if u.NewPassword != u.ConfirmNewPassword { - errors["confirm_new_password"] = append(errors["confirm_new_password"], "Passwords do not match") - } - - if len(errors) > 0 { - return errors, false - } - - return nil, true -} diff --git a/dto/userpin_dto.go b/dto/userpin_dto.go deleted file mode 100644 index 146cc96..0000000 --- a/dto/userpin_dto.go +++ /dev/null @@ -1,66 +0,0 @@ -package dto - -import ( - "fmt" - "regexp" - "strings" -) - -type RequestUserPinDTO struct { - Pin string `json:"userpin"` -} - -func (r *RequestUserPinDTO) Validate() (map[string][]string, bool) { - errors := make(map[string][]string) - - if strings.TrimSpace(r.Pin) == "" { - errors["pin"] = append(errors["pin"], "Pin is required") - } - - if err := validatePin(r.Pin); err != nil { - errors["pin"] = append(errors["pin"], err.Error()) - } - - if len(errors) > 0 { - return errors, false - } - - return nil, true -} - -type UpdateUserPinDTO struct { - OldPin string `json:"old_pin"` - NewPin string `json:"new_pin"` -} - -func (u *UpdateUserPinDTO) Validate() (map[string][]string, bool) { - errors := make(map[string][]string) - - if strings.TrimSpace(u.OldPin) == "" { - errors["old_pin"] = append(errors["old_pin"], "Old pin is required") - } - - if err := validatePin(u.NewPin); err != nil { - errors["new_pin"] = append(errors["new_pin"], err.Error()) - } - - if len(errors) > 0 { - return errors, false - } - - return nil, true -} - -func isNumeric(s string) bool { - re := regexp.MustCompile(`^[0-9]+$`) - return re.MatchString(s) -} - -func validatePin(pin string) error { - if len(pin) != 6 { - return fmt.Errorf("pin harus terdiri dari 6 digit") - } else if !isNumeric(pin) { - return fmt.Errorf("pin harus berupa angka") - } - return nil -} diff --git a/dto/wiayah_indonesia_dto.go b/dto/wiayah_indonesia_dto.go deleted file mode 100644 index b247b59..0000000 --- a/dto/wiayah_indonesia_dto.go +++ /dev/null @@ -1,27 +0,0 @@ -package dto - -type ProvinceResponseDTO struct { - ID string `json:"id"` - Name string `json:"name"` - Regencies []RegencyResponseDTO `json:"regencies,omitempty"` -} - -type RegencyResponseDTO struct { - ID string `json:"id"` - ProvinceID string `json:"province_id"` - Name string `json:"name"` - Districts []DistrictResponseDTO `json:"districts,omitempty"` -} - -type DistrictResponseDTO struct { - ID string `json:"id"` - RegencyID string `json:"regency_id"` - Name string `json:"name"` - Villages []VillageResponseDTO `json:"villages,omitempty"` -} - -type VillageResponseDTO struct { - ID string `json:"id"` - DistrictID string `json:"district_id"` - Name string `json:"name"` -} diff --git a/internal/about/about_handler.go b/internal/about/about_handler.go index ed2ed65..a5f1134 100644 --- a/internal/about/about_handler.go +++ b/internal/about/about_handler.go @@ -3,7 +3,6 @@ package about import ( "fmt" "log" - "rijig/dto" "rijig/utils" "github.com/gofiber/fiber/v2" @@ -20,7 +19,7 @@ func NewAboutHandler(aboutService AboutService) *AboutHandler { } func (h *AboutHandler) CreateAbout(c *fiber.Ctx) error { - var request dto.RequestAboutDTO + var request RequestAboutDTO if err := c.BodyParser(&request); err != nil { return utils.ResponseErrorData(c, fiber.StatusBadRequest, "Invalid request body", map[string][]string{"body": {"Invalid body"}}) } @@ -47,7 +46,7 @@ func (h *AboutHandler) CreateAbout(c *fiber.Ctx) error { func (h *AboutHandler) UpdateAbout(c *fiber.Ctx) error { id := c.Params("id") - var request dto.RequestAboutDTO + var request RequestAboutDTO if err := c.BodyParser(&request); err != nil { log.Printf("Error parsing request body: %v", err) return utils.BadRequest(c, "Invalid input data") @@ -114,7 +113,7 @@ func (h *AboutHandler) DeleteAbout(c *fiber.Ctx) error { } func (h *AboutHandler) CreateAboutDetail(c *fiber.Ctx) error { - var request dto.RequestAboutDetailDTO + var request RequestAboutDetailDTO if err := c.BodyParser(&request); err != nil { log.Printf("Error parsing request body: %v", err) return utils.BadRequest(c, "Invalid input data") @@ -143,7 +142,7 @@ func (h *AboutHandler) CreateAboutDetail(c *fiber.Ctx) error { func (h *AboutHandler) UpdateAboutDetail(c *fiber.Ctx) error { id := c.Params("id") - var request dto.RequestAboutDetailDTO + var request RequestAboutDetailDTO if err := c.BodyParser(&request); err != nil { log.Printf("Error parsing request body: %v", err) return utils.BadRequest(c, "Invalid input data") diff --git a/internal/about/about_service.go b/internal/about/about_service.go index 4cec901..c6653e7 100644 --- a/internal/about/about_service.go +++ b/internal/about/about_service.go @@ -7,7 +7,6 @@ import ( "mime/multipart" "os" "path/filepath" - "rijig/dto" "rijig/model" "rijig/utils" "time" @@ -24,15 +23,15 @@ const ( ) type AboutService interface { - CreateAbout(ctx context.Context, request dto.RequestAboutDTO, coverImageAbout *multipart.FileHeader) (*dto.ResponseAboutDTO, error) - UpdateAbout(ctx context.Context, id string, request dto.RequestAboutDTO, coverImageAbout *multipart.FileHeader) (*dto.ResponseAboutDTO, error) - GetAllAbout(ctx context.Context) ([]dto.ResponseAboutDTO, error) - GetAboutByID(ctx context.Context, id string) (*dto.ResponseAboutDTO, error) - GetAboutDetailById(ctx context.Context, id string) (*dto.ResponseAboutDetailDTO, error) + CreateAbout(ctx context.Context, request RequestAboutDTO, coverImageAbout *multipart.FileHeader) (*ResponseAboutDTO, error) + UpdateAbout(ctx context.Context, id string, request RequestAboutDTO, coverImageAbout *multipart.FileHeader) (*ResponseAboutDTO, error) + GetAllAbout(ctx context.Context) ([]ResponseAboutDTO, error) + GetAboutByID(ctx context.Context, id string) (*ResponseAboutDTO, error) + GetAboutDetailById(ctx context.Context, id string) (*ResponseAboutDetailDTO, error) DeleteAbout(ctx context.Context, id string) error - CreateAboutDetail(ctx context.Context, request dto.RequestAboutDetailDTO, coverImageAboutDetail *multipart.FileHeader) (*dto.ResponseAboutDetailDTO, error) - UpdateAboutDetail(ctx context.Context, id string, request dto.RequestAboutDetailDTO, imageDetail *multipart.FileHeader) (*dto.ResponseAboutDetailDTO, error) + CreateAboutDetail(ctx context.Context, request RequestAboutDetailDTO, coverImageAboutDetail *multipart.FileHeader) (*ResponseAboutDetailDTO, error) + UpdateAboutDetail(ctx context.Context, id string, request RequestAboutDetailDTO, imageDetail *multipart.FileHeader) (*ResponseAboutDetailDTO, error) DeleteAboutDetail(ctx context.Context, id string) error } @@ -66,11 +65,11 @@ func (s *aboutService) invalidateAboutDetailCaches(aboutDetailID, aboutID string s.invalidateAboutCaches(aboutID) } -func formatResponseAboutDetailDTO(about *model.AboutDetail) (*dto.ResponseAboutDetailDTO, error) { +func formatResponseAboutDetailDTO(about *model.AboutDetail) (*ResponseAboutDetailDTO, error) { createdAt, _ := utils.FormatDateToIndonesianFormat(about.CreatedAt) updatedAt, _ := utils.FormatDateToIndonesianFormat(about.UpdatedAt) - response := &dto.ResponseAboutDetailDTO{ + response := &ResponseAboutDetailDTO{ ID: about.ID, AboutID: about.AboutID, ImageDetail: about.ImageDetail, @@ -82,11 +81,11 @@ func formatResponseAboutDetailDTO(about *model.AboutDetail) (*dto.ResponseAboutD return response, nil } -func formatResponseAboutDTO(about *model.About) (*dto.ResponseAboutDTO, error) { +func formatResponseAboutDTO(about *model.About) (*ResponseAboutDTO, error) { createdAt, _ := utils.FormatDateToIndonesianFormat(about.CreatedAt) updatedAt, _ := utils.FormatDateToIndonesianFormat(about.UpdatedAt) - response := &dto.ResponseAboutDTO{ + response := &ResponseAboutDTO{ ID: about.ID, Title: about.Title, CoverImage: about.CoverImage, @@ -194,7 +193,7 @@ func deleteCoverImageAbout(coverimageAboutPath string) error { return nil } -func (s *aboutService) CreateAbout(ctx context.Context, request dto.RequestAboutDTO, coverImageAbout *multipart.FileHeader) (*dto.ResponseAboutDTO, error) { +func (s *aboutService) CreateAbout(ctx context.Context, request RequestAboutDTO, coverImageAbout *multipart.FileHeader) (*ResponseAboutDTO, error) { errors, valid := request.ValidateAbout() if !valid { return nil, fmt.Errorf("validation error: %v", errors) @@ -224,7 +223,7 @@ func (s *aboutService) CreateAbout(ctx context.Context, request dto.RequestAbout return response, nil } -func (s *aboutService) UpdateAbout(ctx context.Context, id string, request dto.RequestAboutDTO, coverImageAbout *multipart.FileHeader) (*dto.ResponseAboutDTO, error) { +func (s *aboutService) UpdateAbout(ctx context.Context, id string, request RequestAboutDTO, coverImageAbout *multipart.FileHeader) (*ResponseAboutDTO, error) { errors, valid := request.ValidateAbout() if !valid { return nil, fmt.Errorf("validation error: %v", errors) @@ -271,9 +270,9 @@ func (s *aboutService) UpdateAbout(ctx context.Context, id string, request dto.R return response, nil } -func (s *aboutService) GetAllAbout(ctx context.Context) ([]dto.ResponseAboutDTO, error) { +func (s *aboutService) GetAllAbout(ctx context.Context) ([]ResponseAboutDTO, error) { - var cachedAbouts []dto.ResponseAboutDTO + var cachedAbouts []ResponseAboutDTO if err := utils.GetCache(cacheKeyAllAbout, &cachedAbouts); err == nil { return cachedAbouts, nil } @@ -283,7 +282,7 @@ func (s *aboutService) GetAllAbout(ctx context.Context) ([]dto.ResponseAboutDTO, return nil, fmt.Errorf("failed to get About list: %v", err) } - var aboutDTOList []dto.ResponseAboutDTO + var aboutDTOList []ResponseAboutDTO for _, about := range aboutList { response, err := formatResponseAboutDTO(&about) if err != nil { @@ -300,10 +299,10 @@ func (s *aboutService) GetAllAbout(ctx context.Context) ([]dto.ResponseAboutDTO, return aboutDTOList, nil } -func (s *aboutService) GetAboutByID(ctx context.Context, id string) (*dto.ResponseAboutDTO, error) { +func (s *aboutService) GetAboutByID(ctx context.Context, id string) (*ResponseAboutDTO, error) { cacheKey := fmt.Sprintf(cacheKeyAboutByID, id) - var cachedAbout dto.ResponseAboutDTO + var cachedAbout ResponseAboutDTO if err := utils.GetCache(cacheKey, &cachedAbout); err == nil { return &cachedAbout, nil } @@ -318,7 +317,7 @@ func (s *aboutService) GetAboutByID(ctx context.Context, id string) (*dto.Respon return nil, fmt.Errorf("error formatting About response: %v", err) } - var responseDetails []dto.ResponseAboutDetailDTO + var responseDetails []ResponseAboutDetailDTO for _, detail := range about.AboutDetail { formattedDetail, err := formatResponseAboutDetailDTO(&detail) if err != nil { @@ -336,10 +335,10 @@ func (s *aboutService) GetAboutByID(ctx context.Context, id string) (*dto.Respon return response, nil } -func (s *aboutService) GetAboutDetailById(ctx context.Context, id string) (*dto.ResponseAboutDetailDTO, error) { +func (s *aboutService) GetAboutDetailById(ctx context.Context, id string) (*ResponseAboutDetailDTO, error) { cacheKey := fmt.Sprintf(cacheKeyAboutDetail, id) - var cachedDetail dto.ResponseAboutDetailDTO + var cachedDetail ResponseAboutDetailDTO if err := utils.GetCache(cacheKey, &cachedDetail); err == nil { return &cachedDetail, nil } @@ -390,7 +389,7 @@ func (s *aboutService) DeleteAbout(ctx context.Context, id string) error { return nil } -func (s *aboutService) CreateAboutDetail(ctx context.Context, request dto.RequestAboutDetailDTO, coverImageAboutDetail *multipart.FileHeader) (*dto.ResponseAboutDetailDTO, error) { +func (s *aboutService) CreateAboutDetail(ctx context.Context, request RequestAboutDetailDTO, coverImageAboutDetail *multipart.FileHeader) (*ResponseAboutDetailDTO, error) { errors, valid := request.ValidateAboutDetail() if !valid { return nil, fmt.Errorf("validation error: %v", errors) @@ -426,7 +425,7 @@ func (s *aboutService) CreateAboutDetail(ctx context.Context, request dto.Reques return response, nil } -func (s *aboutService) UpdateAboutDetail(ctx context.Context, id string, request dto.RequestAboutDetailDTO, imageDetail *multipart.FileHeader) (*dto.ResponseAboutDetailDTO, error) { +func (s *aboutService) UpdateAboutDetail(ctx context.Context, id string, request RequestAboutDetailDTO, imageDetail *multipart.FileHeader) (*ResponseAboutDetailDTO, error) { errors, valid := request.ValidateAboutDetail() if !valid { return nil, fmt.Errorf("validation error: %v", errors) diff --git a/internal/address/address_handler.go b/internal/address/address_handler.go index 1c4eb44..cdc4514 100644 --- a/internal/address/address_handler.go +++ b/internal/address/address_handler.go @@ -1,7 +1,6 @@ package address import ( - "rijig/dto" "rijig/middleware" "rijig/utils" @@ -17,7 +16,7 @@ func NewAddressHandler(addressService AddressService) *AddressHandler { } func (h *AddressHandler) CreateAddress(c *fiber.Ctx) error { - var request dto.CreateAddressDTO + var request CreateAddressDTO claims, err := middleware.GetUserFromContext(c) if err != nil { return err @@ -74,7 +73,7 @@ func (h *AddressHandler) UpdateAddress(c *fiber.Ctx) error { addressID := c.Params("address_id") - var request dto.CreateAddressDTO + var request CreateAddressDTO claims, err := middleware.GetUserFromContext(c) if err != nil { return err diff --git a/internal/address/address_service.go b/internal/address/address_service.go index 12b9625..5189173 100644 --- a/internal/address/address_service.go +++ b/internal/address/address_service.go @@ -6,7 +6,6 @@ import ( "fmt" "time" - "rijig/dto" "rijig/internal/wilayahindo" "rijig/model" "rijig/utils" @@ -20,10 +19,10 @@ const ( ) type AddressService interface { - CreateAddress(ctx context.Context, userID string, request dto.CreateAddressDTO) (*dto.AddressResponseDTO, error) - GetAddressByUserID(ctx context.Context, userID string) ([]dto.AddressResponseDTO, error) - GetAddressByID(ctx context.Context, userID, id string) (*dto.AddressResponseDTO, error) - UpdateAddress(ctx context.Context, userID, id string, addressDTO dto.CreateAddressDTO) (*dto.AddressResponseDTO, error) + CreateAddress(ctx context.Context, userID string, request CreateAddressDTO) (*AddressResponseDTO, error) + GetAddressByUserID(ctx context.Context, userID string) ([]AddressResponseDTO, error) + GetAddressByID(ctx context.Context, userID, id string) (*AddressResponseDTO, error) + UpdateAddress(ctx context.Context, userID, id string, addressDTO CreateAddressDTO) (*AddressResponseDTO, error) DeleteAddress(ctx context.Context, userID, id string) error } @@ -39,7 +38,7 @@ func NewAddressService(addressRepo AddressRepository, wilayahRepo wilayahindo.Wi } } -func (s *addressService) validateWilayahIDs(ctx context.Context, addressDTO dto.CreateAddressDTO) (string, string, string, string, error) { +func (s *addressService) validateWilayahIDs(ctx context.Context, addressDTO CreateAddressDTO) (string, string, string, string, error) { province, _, err := s.wilayahRepo.FindProvinceByID(ctx, addressDTO.Province, 0, 0) if err != nil { @@ -64,11 +63,11 @@ func (s *addressService) validateWilayahIDs(ctx context.Context, addressDTO dto. return province.Name, regency.Name, district.Name, village.Name, nil } -func (s *addressService) mapToResponseDTO(address *model.Address) *dto.AddressResponseDTO { +func (s *addressService) mapToResponseDTO(address *model.Address) *AddressResponseDTO { createdAt, _ := utils.FormatDateToIndonesianFormat(address.CreatedAt) updatedAt, _ := utils.FormatDateToIndonesianFormat(address.UpdatedAt) - return &dto.AddressResponseDTO{ + return &AddressResponseDTO{ UserID: address.UserID, ID: address.ID, Province: address.Province, @@ -98,20 +97,20 @@ func (s *addressService) invalidateAddressCaches(userID, addressID string) { } } -func (s *addressService) cacheAddress(addressDTO *dto.AddressResponseDTO) { +func (s *addressService) cacheAddress(addressDTO *AddressResponseDTO) { cacheKey := fmt.Sprintf(addressCacheKeyPattern, addressDTO.ID) if err := utils.SetCache(cacheKey, addressDTO, cacheTTL); err != nil { fmt.Printf("Error caching address to Redis: %v\n", err) } } -func (s *addressService) cacheUserAddresses(ctx context.Context, userID string) ([]dto.AddressResponseDTO, error) { +func (s *addressService) cacheUserAddresses(ctx context.Context, userID string) ([]AddressResponseDTO, error) { addresses, err := s.addressRepo.FindAddressByUserID(ctx, userID) if err != nil { return nil, fmt.Errorf("failed to fetch addresses: %w", err) } - var addressDTOs []dto.AddressResponseDTO + var addressDTOs []AddressResponseDTO for _, address := range addresses { addressDTOs = append(addressDTOs, *s.mapToResponseDTO(&address)) } @@ -137,7 +136,7 @@ func (s *addressService) checkAddressOwnership(ctx context.Context, userID, addr return address, nil } -func (s *addressService) CreateAddress(ctx context.Context, userID string, addressDTO dto.CreateAddressDTO) (*dto.AddressResponseDTO, error) { +func (s *addressService) CreateAddress(ctx context.Context, userID string, addressDTO CreateAddressDTO) (*AddressResponseDTO, error) { provinceName, regencyName, districtName, villageName, err := s.validateWilayahIDs(ctx, addressDTO) if err != nil { @@ -168,10 +167,10 @@ func (s *addressService) CreateAddress(ctx context.Context, userID string, addre return responseDTO, nil } -func (s *addressService) GetAddressByUserID(ctx context.Context, userID string) ([]dto.AddressResponseDTO, error) { +func (s *addressService) GetAddressByUserID(ctx context.Context, userID string) ([]AddressResponseDTO, error) { cacheKey := fmt.Sprintf(userAddressesCacheKeyPattern, userID) - var cachedAddresses []dto.AddressResponseDTO + var cachedAddresses []AddressResponseDTO if err := utils.GetCache(cacheKey, &cachedAddresses); err == nil { return cachedAddresses, nil @@ -180,7 +179,7 @@ func (s *addressService) GetAddressByUserID(ctx context.Context, userID string) return s.cacheUserAddresses(ctx, userID) } -func (s *addressService) GetAddressByID(ctx context.Context, userID, id string) (*dto.AddressResponseDTO, error) { +func (s *addressService) GetAddressByID(ctx context.Context, userID, id string) (*AddressResponseDTO, error) { address, err := s.checkAddressOwnership(ctx, userID, id) if err != nil { @@ -188,7 +187,7 @@ func (s *addressService) GetAddressByID(ctx context.Context, userID, id string) } cacheKey := fmt.Sprintf(addressCacheKeyPattern, id) - var cachedAddress dto.AddressResponseDTO + var cachedAddress AddressResponseDTO if err := utils.GetCache(cacheKey, &cachedAddress); err == nil { return &cachedAddress, nil @@ -200,7 +199,7 @@ func (s *addressService) GetAddressByID(ctx context.Context, userID, id string) return responseDTO, nil } -func (s *addressService) UpdateAddress(ctx context.Context, userID, id string, addressDTO dto.CreateAddressDTO) (*dto.AddressResponseDTO, error) { +func (s *addressService) UpdateAddress(ctx context.Context, userID, id string, addressDTO CreateAddressDTO) (*AddressResponseDTO, error) { address, err := s.checkAddressOwnership(ctx, userID, id) if err != nil { diff --git a/internal/authentication/authentication_repository.go b/internal/authentication/authentication_repository.go index 4993150..23d367e 100644 --- a/internal/authentication/authentication_repository.go +++ b/internal/authentication/authentication_repository.go @@ -2,6 +2,8 @@ package authentication import ( "context" + "fmt" + "log" "rijig/model" "gorm.io/gorm" @@ -15,6 +17,9 @@ type AuthenticationRepository interface { CreateUser(ctx context.Context, user *model.User) error UpdateUser(ctx context.Context, user *model.User) error PatchUser(ctx context.Context, userID string, updates map[string]interface{}) error + + GetIdentityCardsByUserRegStatus(ctx context.Context, userRegStatus string) ([]model.IdentityCard, error) + GetCompanyProfilesByUserRegStatus(ctx context.Context, userRegStatus string) ([]model.CompanyProfile, error) } type authenticationRepository struct { @@ -84,3 +89,37 @@ func (r *authenticationRepository) PatchUser(ctx context.Context, userID string, Where("id = ?", userID). Updates(updates).Error } + +func (r *authenticationRepository) GetIdentityCardsByUserRegStatus(ctx context.Context, userRegStatus string) ([]model.IdentityCard, error) { + var identityCards []model.IdentityCard + + if err := r.db.WithContext(ctx). + Joins("JOIN users ON identity_cards.user_id = users.id"). + Where("users.registration_status = ?", userRegStatus). + Preload("User"). + Preload("User.Role"). + Find(&identityCards).Error; err != nil { + log.Printf("Error fetching identity cards by user registration status: %v", err) + return nil, fmt.Errorf("error fetching identity cards by user registration status: %w", err) + } + + log.Printf("Found %d identity cards with registration status: %s", len(identityCards), userRegStatus) + return identityCards, nil +} + +func (r *authenticationRepository) GetCompanyProfilesByUserRegStatus(ctx context.Context, userRegStatus string) ([]model.CompanyProfile, error) { + var companyProfiles []model.CompanyProfile + + if err := r.db.WithContext(ctx). + Joins("JOIN users ON company_profiles.user_id = users.id"). + Where("users.registration_status = ?", userRegStatus). + Preload("User"). + Preload("User.Role"). + Find(&companyProfiles).Error; err != nil { + log.Printf("Error fetching company profiles by user registration status: %v", err) + return nil, fmt.Errorf("error fetching company profiles by user registration status: %w", err) + } + + log.Printf("Found %d company profiles with registration status: %s", len(companyProfiles), userRegStatus) + return companyProfiles, nil +} diff --git a/internal/authentication/authentication_service.go b/internal/authentication/authentication_service.go index 3956216..f2be14f 100644 --- a/internal/authentication/authentication_service.go +++ b/internal/authentication/authentication_service.go @@ -315,7 +315,9 @@ func (s *authenticationService) VerifyLoginOTP(ctx context.Context, req *VerifyO return nil, fmt.Errorf("kode OTP salah") } - user, err := s.authRepo.FindUserByID(ctx, otpData.UserID) + normalizedRole := strings.ToLower(req.RoleName) + + user, err := s.authRepo.FindUserByPhoneAndRole(ctx, req.Phone, normalizedRole) if err != nil { return nil, fmt.Errorf("user tidak ditemukan") } @@ -324,24 +326,29 @@ func (s *authenticationService) VerifyLoginOTP(ctx context.Context, req *VerifyO tokenResponse, err := utils.GenerateTokenPair( user.ID, - user.Role.RoleName, + normalizedRole, req.DeviceID, - "pin_verification_required", + user.RegistrationStatus, int(user.RegistrationProgress), ) if err != nil { return nil, fmt.Errorf("gagal generate token: %v", err) } + nextStep := utils.GetNextRegistrationStep( + normalizedRole, + int(user.RegistrationProgress), + user.RegistrationStatus, + ) + return &AuthResponse{ - Message: "OTP berhasil diverifikasi, silakan masukkan PIN", + Message: "otp berhasil diverifikasi", AccessToken: tokenResponse.AccessToken, RefreshToken: tokenResponse.RefreshToken, TokenType: string(tokenResponse.TokenType), ExpiresIn: tokenResponse.ExpiresIn, - User: convertUserToResponse(user), RegistrationStatus: user.RegistrationStatus, - NextStep: "Masukkan PIN", + NextStep: nextStep, SessionID: tokenResponse.SessionID, }, nil } diff --git a/internal/company/company_route.go b/internal/company/company_route.go index 78012aa..4e8b5ae 100644 --- a/internal/company/company_route.go +++ b/internal/company/company_route.go @@ -2,6 +2,7 @@ package company import ( "rijig/config" + "rijig/internal/authentication" "rijig/middleware" "github.com/gofiber/fiber/v2" @@ -9,7 +10,8 @@ import ( func CompanyRouter(api fiber.Router) { companyProfileRepo := NewCompanyProfileRepository(config.DB) - companyProfileService := NewCompanyProfileService(companyProfileRepo) + authRepo := authentication.NewAuthenticationRepository(config.DB) + companyProfileService := NewCompanyProfileService(companyProfileRepo, authRepo) companyProfileHandler := NewCompanyProfileHandler(companyProfileService) companyProfileAPI := api.Group("/companyprofile") diff --git a/internal/company/company_service.go b/internal/company/company_service.go index ebef795..291086a 100644 --- a/internal/company/company_service.go +++ b/internal/company/company_service.go @@ -3,8 +3,13 @@ package company import ( "context" "fmt" + "log" + "rijig/internal/authentication" + "rijig/internal/role" + "rijig/internal/userprofile" "rijig/model" "rijig/utils" + "time" ) type CompanyProfileService interface { @@ -13,15 +18,19 @@ type CompanyProfileService interface { GetCompanyProfilesByUserID(ctx context.Context, userID string) ([]ResponseCompanyProfileDTO, error) UpdateCompanyProfile(ctx context.Context, userID string, request *RequestCompanyProfileDTO) (*ResponseCompanyProfileDTO, error) DeleteCompanyProfile(ctx context.Context, userID string) error + + GetAllCompanyProfilesByRegStatus(ctx context.Context, userRegStatus string) ([]ResponseCompanyProfileDTO, error) + UpdateUserRegistrationStatusByCompany(ctx context.Context, companyUserID string, newStatus string) error } type companyProfileService struct { companyRepo CompanyProfileRepository + authRepo authentication.AuthenticationRepository } -func NewCompanyProfileService(companyRepo CompanyProfileRepository) CompanyProfileService { +func NewCompanyProfileService(companyRepo CompanyProfileRepository, authRepo authentication.AuthenticationRepository) CompanyProfileService { return &companyProfileService{ - companyRepo: companyRepo, + companyRepo, authRepo, } } @@ -48,9 +57,9 @@ func FormatResponseCompanyProfile(companyProfile *model.CompanyProfile) (*Respon } func (s *companyProfileService) CreateCompanyProfile(ctx context.Context, userID string, request *RequestCompanyProfileDTO) (*ResponseCompanyProfileDTO, error) { - if errors, valid := request.ValidateCompanyProfileInput(); !valid { - return nil, fmt.Errorf("validation failed: %v", errors) - } + // if errors, valid := request.ValidateCompanyProfileInput(); !valid { + // return nil, fmt.Errorf("validation failed: %v", errors) + // } companyProfile := &model.CompanyProfile{ UserID: userID, @@ -134,3 +143,100 @@ func (s *companyProfileService) UpdateCompanyProfile(ctx context.Context, userID func (s *companyProfileService) DeleteCompanyProfile(ctx context.Context, userID string) error { return s.companyRepo.DeleteCompanyProfileByUserID(ctx, userID) } + +func (s *companyProfileService) GetAllCompanyProfilesByRegStatus(ctx context.Context, userRegStatus string) ([]ResponseCompanyProfileDTO, error) { + companyProfiles, err := s.authRepo.GetCompanyProfilesByUserRegStatus(ctx, userRegStatus) + if err != nil { + log.Printf("Error getting company profiles by registration status: %v", err) + return nil, fmt.Errorf("failed to get company profiles: %w", err) + } + + var response []ResponseCompanyProfileDTO + for _, profile := range companyProfiles { + dto := ResponseCompanyProfileDTO{ + ID: profile.ID, + UserID: profile.UserID, + CompanyName: profile.CompanyName, + CompanyAddress: profile.CompanyAddress, + CompanyPhone: profile.CompanyPhone, + CompanyEmail: profile.CompanyEmail, + CompanyLogo: profile.CompanyLogo, + CompanyWebsite: profile.CompanyWebsite, + TaxID: profile.TaxID, + FoundedDate: profile.FoundedDate, + CompanyType: profile.CompanyType, + CompanyDescription: profile.CompanyDescription, + CreatedAt: profile.CreatedAt.Format("2006-01-02T15:04:05Z07:00"), + UpdatedAt: profile.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"), + } + response = append(response, dto) + } + + return response, nil +} + +func (s *companyProfileService) UpdateUserRegistrationStatusByCompany(ctx context.Context, companyUserID string, newStatus string) error { + + user, err := s.authRepo.FindUserByID(ctx, companyUserID) + if err != nil { + log.Printf("Error finding user by ID %s: %v", companyUserID, err) + return fmt.Errorf("user not found: %w", err) + } + + updates := map[string]interface{}{ + "registration_status": newStatus, + "updated_at": time.Now(), + } + + switch newStatus { + case utils.RegStatusConfirmed: + updates["registration_progress"] = utils.ProgressDataSubmitted + case utils.RegStatusRejected: + updates["registration_progress"] = utils.ProgressOTPVerified + } + + err = s.authRepo.PatchUser(ctx, user.ID, updates) + if err != nil { + log.Printf("Error updating user registration status for user ID %s: %v", user.ID, err) + return fmt.Errorf("failed to update user registration status: %w", err) + } + + log.Printf("Successfully updated registration status for user ID %s to %s", user.ID, newStatus) + return nil +} + +func (s *companyProfileService) GetUserProfile(ctx context.Context, userID string) (*userprofile.UserProfileResponseDTO, error) { + user, err := s.authRepo.FindUserByID(ctx, userID) + if err != nil { + log.Printf("Error getting user profile for ID %s: %v", userID, err) + return nil, fmt.Errorf("failed to get user profile: %w", err) + } + + response := &userprofile.UserProfileResponseDTO{ + ID: user.ID, + Name: user.Name, + Gender: user.Gender, + Dateofbirth: user.Dateofbirth, + Placeofbirth: user.Placeofbirth, + Phone: user.Phone, + Email: user.Email, + PhoneVerified: user.PhoneVerified, + CreatedAt: user.CreatedAt.Format("2006-01-02T15:04:05Z07:00"), + UpdatedAt: user.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"), + } + + if user.Avatar != nil { + response.Avatar = *user.Avatar + } + + if user.Role != nil { + response.Role = role.RoleResponseDTO{ + ID: user.Role.ID, + RoleName: user.Role.RoleName, + CreatedAt: user.Role.CreatedAt.Format("2006-01-02T15:04:05Z07:00"), + UpdatedAt: user.Role.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"), + } + } + + return response, nil +} diff --git a/internal/identitycart/identitycart_dto.go b/internal/identitycart/identitycart_dto.go index 78dc475..ea7a4d7 100644 --- a/internal/identitycart/identitycart_dto.go +++ b/internal/identitycart/identitycart_dto.go @@ -9,6 +9,7 @@ type ResponseIdentityCardDTO struct { ID string `json:"id"` UserID string `json:"userId"` Identificationumber string `json:"identificationumber"` + Fullname string `json:"fullname"` Placeofbirth string `json:"placeofbirth"` Dateofbirth string `json:"dateofbirth"` Gender string `json:"gender"` @@ -31,9 +32,10 @@ type ResponseIdentityCardDTO struct { } type RequestIdentityCardDTO struct { - DeviceID string `json:"device_id"` + // DeviceID string `json:"device_id"` UserID string `json:"userId"` Identificationumber string `json:"identificationumber"` + Fullname string `json:"fullname"` Placeofbirth string `json:"placeofbirth"` Dateofbirth string `json:"dateofbirth"` Gender string `json:"gender"` diff --git a/internal/identitycart/identitycart_handler.go b/internal/identitycart/identitycart_handler.go index 049b9b1..325ca37 100644 --- a/internal/identitycart/identitycart_handler.go +++ b/internal/identitycart/identitycart_handler.go @@ -1,8 +1,10 @@ package identitycart import ( + "log" "rijig/middleware" "rijig/utils" + "strings" "github.com/gofiber/fiber/v2" ) @@ -18,27 +20,33 @@ func NewIdentityCardHandler(service IdentityCardService) *IdentityCardHandler { func (h *IdentityCardHandler) CreateIdentityCardHandler(c *fiber.Ctx) error { claims, err := middleware.GetUserFromContext(c) if err != nil { - return err - } - - cardPhoto, err := c.FormFile("cardphoto") - if err != nil { - return utils.BadRequest(c, "KTP photo is required") + log.Printf("Error getting user from context: %v", err) + return utils.Unauthorized(c, "unauthorized access") } var input RequestIdentityCardDTO if err := c.BodyParser(&input); err != nil { + log.Printf("Error parsing body: %v", err) return utils.BadRequest(c, "Invalid input format") } - if errs, valid := input.ValidateIdentityCardInput(); !valid { return utils.ResponseErrorData(c, fiber.StatusBadRequest, "Input validation failed", errs) } - response, err := h.service.CreateIdentityCard(c.Context(), claims.UserID, &input, cardPhoto) + cardPhoto, err := c.FormFile("cardphoto") if err != nil { - return utils.InternalServerError(c, err.Error()) + log.Printf("Error getting card photo: %v", err) + return utils.BadRequest(c, "KTP photo is required") + } + + response, err := h.service.CreateIdentityCard(c.Context(), claims.UserID, claims.DeviceID, &input, cardPhoto) + if err != nil { + log.Printf("Error creating identity card: %v", err) + if strings.Contains(err.Error(), "invalid file type") { + return utils.BadRequest(c, err.Error()) + } + return utils.InternalServerError(c, "Failed to create identity card") } return utils.SuccessWithData(c, "KTP successfully submitted", response) @@ -47,27 +55,158 @@ func (h *IdentityCardHandler) CreateIdentityCardHandler(c *fiber.Ctx) error { func (h *IdentityCardHandler) GetIdentityByID(c *fiber.Ctx) error { id := c.Params("id") if id == "" { - return utils.BadRequest(c, "id is required") + return utils.BadRequest(c, "ID is required") } result, err := h.service.GetIdentityCardByID(c.Context(), id) if err != nil { - return utils.NotFound(c, "data not found") + log.Printf("Error getting identity card by ID %s: %v", id, err) + return utils.NotFound(c, "Identity card not found") } - return utils.SuccessWithData(c, "success retrieve identity card", result) + return utils.SuccessWithData(c, "Successfully retrieved identity card", result) } func (h *IdentityCardHandler) GetIdentityByUserId(c *fiber.Ctx) error { claims, err := middleware.GetUserFromContext(c) if err != nil { - return err + log.Printf("Error getting user from context: %v", err) + return utils.Unauthorized(c, "Unauthorized access") } result, err := h.service.GetIdentityCardsByUserID(c.Context(), claims.UserID) if err != nil { - return utils.InternalServerError(c, "failed to fetch your identity card data") + log.Printf("Error getting identity cards for user %s: %v", claims.UserID, err) + return utils.InternalServerError(c, "Failed to fetch your identity card data") } - return utils.SuccessWithData(c, "success retrieve your identity card", result) + return utils.SuccessWithData(c, "Successfully retrieved your identity cards", result) +} + +func (h *IdentityCardHandler) UpdateIdentityCardHandler(c *fiber.Ctx) error { + claims, err := middleware.GetUserFromContext(c) + if err != nil { + log.Printf("Error getting user from context: %v", err) + return utils.Unauthorized(c, "Unauthorized access") + } + + id := c.Params("id") + if id == "" { + return utils.BadRequest(c, "Identity card ID is required") + } + + var input RequestIdentityCardDTO + if err := c.BodyParser(&input); err != nil { + log.Printf("Error parsing body: %v", err) + return utils.BadRequest(c, "Invalid input format") + } + + if errs, valid := input.ValidateIdentityCardInput(); !valid { + return utils.ResponseErrorData(c, fiber.StatusBadRequest, "Input validation failed", errs) + } + + cardPhoto, err := c.FormFile("cardphoto") + if err != nil && err.Error() != "there is no uploaded file associated with the given key" { + log.Printf("Error getting card photo: %v", err) + return utils.BadRequest(c, "Invalid card photo") + } + + if cardPhoto != nil && cardPhoto.Size > 5*1024*1024 { + return utils.BadRequest(c, "File size must be less than 5MB") + } + + response, err := h.service.UpdateIdentityCard(c.Context(), claims.UserID, id, &input, cardPhoto) + if err != nil { + log.Printf("Error updating identity card: %v", err) + if strings.Contains(err.Error(), "not found") { + return utils.NotFound(c, "Identity card not found") + } + if strings.Contains(err.Error(), "invalid file type") { + return utils.BadRequest(c, err.Error()) + } + return utils.InternalServerError(c, "Failed to update identity card") + } + + return utils.SuccessWithData(c, "Identity card successfully updated", response) +} + +func (h *IdentityCardHandler) GetAllIdentityCardsByRegStatus(c *fiber.Ctx) error { + _, err := middleware.GetUserFromContext(c) + if err != nil { + log.Printf("Error getting user from context: %v", err) + return utils.Unauthorized(c, "Unauthorized access") + } + + // if claims.Role != "admin" { + // return utils.Forbidden(c, "Access denied: admin role required") + // } + + status := c.Query("status", utils.RegStatusPending) + + validStatuses := map[string]bool{ + utils.RegStatusPending: true, + "confirmed": true, + "rejected": true, + } + + if !validStatuses[status] { + return utils.BadRequest(c, "Invalid status. Valid values: pending, confirmed, rejected") + } + + result, err := h.service.GetAllIdentityCardsByRegStatus(c.Context(), status) + if err != nil { + log.Printf("Error getting identity cards by status %s: %v", status, err) + return utils.InternalServerError(c, "Failed to fetch identity cards") + } + + return utils.SuccessWithData(c, "Successfully retrieved identity cards", result) +} + +func (h *IdentityCardHandler) UpdateUserRegistrationStatusByIdentityCard(c *fiber.Ctx) error { + _, err := middleware.GetUserFromContext(c) + if err != nil { + log.Printf("Error getting user from context: %v", err) + return utils.Unauthorized(c, "Unauthorized access") + } + + userID := c.Params("userId") + if userID == "" { + return utils.BadRequest(c, "User ID is required") + } + + type StatusUpdateRequest struct { + Status string `json:"status" validate:"required,oneof=confirmed rejected"` + } + + var input StatusUpdateRequest + if err := c.BodyParser(&input); err != nil { + log.Printf("Error parsing body: %v", err) + return utils.BadRequest(c, "Invalid input format") + } + + if input.Status != "confirmed" && input.Status != "rejected" { + return utils.BadRequest(c, "Invalid status. Valid values: confirmed, rejected") + } + + err = h.service.UpdateUserRegistrationStatusByIdentityCard(c.Context(), userID, input.Status) + if err != nil { + log.Printf("Error updating user registration status: %v", err) + if strings.Contains(err.Error(), "not found") { + return utils.NotFound(c, "User not found") + } + return utils.InternalServerError(c, "Failed to update registration status") + } + + message := "User registration status successfully updated to " + input.Status + return utils.Success(c, message) +} + +func (h *IdentityCardHandler) DeleteIdentityCardHandler(c *fiber.Ctx) error { + + id := c.Params("id") + if id == "" { + return utils.BadRequest(c, "Identity card ID is required") + } + + return utils.Success(c, "Identity card successfully deleted") } diff --git a/internal/identitycart/identitycart_route.go b/internal/identitycart/identitycart_route.go index f9f0673..51fd8ca 100644 --- a/internal/identitycart/identitycart_route.go +++ b/internal/identitycart/identitycart_route.go @@ -3,7 +3,9 @@ package identitycart import ( "rijig/config" "rijig/internal/authentication" + "rijig/internal/userprofile" "rijig/middleware" + "rijig/utils" "github.com/gofiber/fiber/v2" ) @@ -11,25 +13,34 @@ import ( func UserIdentityCardRoute(api fiber.Router) { identityRepo := NewIdentityCardRepository(config.DB) authRepo := authentication.NewAuthenticationRepository(config.DB) - identityService := NewIdentityCardService(identityRepo, authRepo) + userRepo := userprofile.NewUserProfileRepository(config.DB) + identityService := NewIdentityCardService(identityRepo, authRepo, userRepo) identityHandler := NewIdentityCardHandler(identityService) identity := api.Group("/identity") identity.Post("/create", middleware.AuthMiddleware(), - middleware.RequireRoles("pengelola", "pengepul"), + middleware.RequireRoles(utils.RolePengepul), identityHandler.CreateIdentityCardHandler, ) identity.Get("/:id", middleware.AuthMiddleware(), - middleware.RequireRoles("pengelola", "pengepul"), identityHandler.GetIdentityByID, ) + identity.Get("/s", + middleware.AuthMiddleware(), + identityHandler.GetIdentityByUserId, + ) identity.Get("/", middleware.AuthMiddleware(), - middleware.RequireRoles("pengelola", "pengepul"), - identityHandler.GetIdentityByUserId, + middleware.RequireRoles(utils.RoleAdministrator), + identityHandler.GetAllIdentityCardsByRegStatus, + ) + identity.Patch("/:userId/status", + middleware.AuthMiddleware(), + middleware.RequireRoles(utils.RoleAdministrator), + identityHandler.UpdateUserRegistrationStatusByIdentityCard, ) } diff --git a/internal/identitycart/identitycart_service.go b/internal/identitycart/identitycart_service.go index e72e219..0c557f9 100644 --- a/internal/identitycart/identitycart_service.go +++ b/internal/identitycart/identitycart_service.go @@ -2,36 +2,49 @@ package identitycart import ( "context" + "errors" "fmt" + "io" "log" "mime/multipart" "os" "path/filepath" "rijig/internal/authentication" + "rijig/internal/role" + "rijig/internal/userprofile" "rijig/model" "rijig/utils" - "strings" + "time" ) type IdentityCardService interface { - CreateIdentityCard(ctx context.Context, userID string, request *RequestIdentityCardDTO, cardPhoto *multipart.FileHeader) (*authentication.AuthResponse, error) + CreateIdentityCard(ctx context.Context, userID, deviceID string, request *RequestIdentityCardDTO, cardPhoto *multipart.FileHeader) (*authentication.AuthResponse, error) GetIdentityCardByID(ctx context.Context, id string) (*ResponseIdentityCardDTO, error) GetIdentityCardsByUserID(ctx context.Context, userID string) ([]ResponseIdentityCardDTO, error) UpdateIdentityCard(ctx context.Context, userID string, id string, request *RequestIdentityCardDTO, cardPhoto *multipart.FileHeader) (*ResponseIdentityCardDTO, error) + + GetAllIdentityCardsByRegStatus(ctx context.Context, userRegStatus string) ([]ResponseIdentityCardDTO, error) + UpdateUserRegistrationStatusByIdentityCard(ctx context.Context, identityCardUserID string, newStatus string) error } type identityCardService struct { identityRepo IdentityCardRepository authRepo authentication.AuthenticationRepository + userRepo userprofile.UserProfileRepository } -func NewIdentityCardService(identityRepo IdentityCardRepository, authRepo authentication.AuthenticationRepository) IdentityCardService { +func NewIdentityCardService(identityRepo IdentityCardRepository, authRepo authentication.AuthenticationRepository, userRepo userprofile.UserProfileRepository) IdentityCardService { return &identityCardService{ - identityRepo: identityRepo, - authRepo: authRepo, + identityRepo, + authRepo, userRepo, } } +type IdentityCardWithUserDTO struct { + IdentityCard ResponseIdentityCardDTO `json:"identity_card"` + User userprofile.UserProfileResponseDTO `json:"user"` +} + func FormatResponseIdentityCard(identityCard *model.IdentityCard) (*ResponseIdentityCardDTO, error) { createdAt, _ := utils.FormatDateToIndonesianFormat(identityCard.CreatedAt) updatedAt, _ := utils.FormatDateToIndonesianFormat(identityCard.UpdatedAt) @@ -93,7 +106,7 @@ func (s *identityCardService) saveIdentityCardImage(userID string, cardPhoto *mu } defer dst.Close() - if _, err := dst.ReadFrom(src); err != nil { + if _, err := io.Copy(dst, src); err != nil { return "", fmt.Errorf("failed to save card photo: %v", err) } @@ -123,16 +136,7 @@ func deleteIdentityCardImage(imagePath string) error { return nil } -func (s *identityCardService) CreateIdentityCard(ctx context.Context, userID string, request *RequestIdentityCardDTO, cardPhoto *multipart.FileHeader) (*authentication.AuthResponse, error) { - - // Validate essential parameters - if userID == "" { - return nil, fmt.Errorf("userID cannot be empty") - } - - if request.DeviceID == "" { - return nil, fmt.Errorf("deviceID cannot be empty") - } +func (s *identityCardService) CreateIdentityCard(ctx context.Context, userID, deviceID string, request *RequestIdentityCardDTO, cardPhoto *multipart.FileHeader) (*authentication.AuthResponse, error) { cardPhotoPath, err := s.saveIdentityCardImage(userID, cardPhoto) if err != nil { @@ -172,33 +176,13 @@ func (s *identityCardService) CreateIdentityCard(ctx context.Context, userID str return nil, fmt.Errorf("failed to find user: %v", err) } - // Validate user data if user.Role.RoleName == "" { return nil, fmt.Errorf("user role not found") } - roleName := strings.ToLower(user.Role.RoleName) - - // Determine new registration status and progress - var newRegistrationStatus string - var newRegistrationProgress int - - switch roleName { - case "pengepul": - newRegistrationProgress = 2 - newRegistrationStatus = utils.RegStatusPending - case "pengelola": - newRegistrationProgress = 2 - newRegistrationStatus = user.RegistrationStatus - default: - newRegistrationProgress = int(user.RegistrationProgress) - newRegistrationStatus = user.RegistrationStatus - } - - // Update user registration progress and status updates := map[string]interface{}{ - "registration_progress": newRegistrationProgress, - "registration_status": newRegistrationStatus, + "registration_progress": utils.ProgressDataSubmitted, + "registration_status": utils.RegStatusPending, } err = s.authRepo.PatchUser(ctx, userID, updates) @@ -206,34 +190,46 @@ func (s *identityCardService) CreateIdentityCard(ctx context.Context, userID str return nil, fmt.Errorf("failed to update user: %v", err) } - // Debug logging before token generation + updated, err := s.userRepo.GetByID(ctx, userID) + if err != nil { + if errors.Is(err, userprofile.ErrUserNotFound) { + return nil, fmt.Errorf("user not found") + } + return nil, fmt.Errorf("failed to get updated user: %w", err) + } + log.Printf("Token Generation Parameters:") log.Printf("- UserID: '%s'", user.ID) log.Printf("- Role: '%s'", user.Role.RoleName) - log.Printf("- DeviceID: '%s'", request.DeviceID) - log.Printf("- Registration Status: '%s'", newRegistrationStatus) + log.Printf("- DeviceID: '%s'", deviceID) + log.Printf("- Registration Status: '%s'", utils.RegStatusPending) - // Generate token pair with updated status tokenResponse, err := utils.GenerateTokenPair( - user.ID, - user.Role.RoleName, - request.DeviceID, - newRegistrationStatus, - newRegistrationProgress, + updated.ID, + updated.Role.RoleName, + deviceID, + updated.RegistrationStatus, + int(updated.RegistrationProgress), ) if err != nil { log.Printf("GenerateTokenPair error: %v", err) return nil, fmt.Errorf("failed to generate token: %v", err) } + nextStep := utils.GetNextRegistrationStep( + updated.Role.RoleName, + int(updated.RegistrationProgress), + updated.RegistrationStatus, + ) + return &authentication.AuthResponse{ Message: "identity card berhasil diunggah, silakan tunggu konfirmasi dari admin dalam 1x24 jam", AccessToken: tokenResponse.AccessToken, RefreshToken: tokenResponse.RefreshToken, TokenType: string(tokenResponse.TokenType), ExpiresIn: tokenResponse.ExpiresIn, - RegistrationStatus: newRegistrationStatus, - NextStep: tokenResponse.NextStep, + RegistrationStatus: updated.RegistrationStatus, + NextStep: nextStep, SessionID: tokenResponse.SessionID, }, nil } @@ -319,3 +315,164 @@ func (s *identityCardService) UpdateIdentityCard(ctx context.Context, userID str return idcardResponseDTO, nil } + +func (s *identityCardService) GetAllIdentityCardsByRegStatus(ctx context.Context, userRegStatus string) ([]ResponseIdentityCardDTO, error) { + identityCards, err := s.authRepo.GetIdentityCardsByUserRegStatus(ctx, userRegStatus) + if err != nil { + log.Printf("Error getting identity cards by registration status: %v", err) + return nil, fmt.Errorf("failed to get identity cards: %w", err) + } + + var response []ResponseIdentityCardDTO + for _, card := range identityCards { + createdAt, _ := utils.FormatDateToIndonesianFormat(card.CreatedAt) + updatedAt, _ := utils.FormatDateToIndonesianFormat(card.UpdatedAt) + dto := ResponseIdentityCardDTO{ + ID: card.ID, + UserID: card.UserID, + Identificationumber: card.Identificationumber, + Placeofbirth: card.Placeofbirth, + Dateofbirth: card.Dateofbirth, + Gender: card.Gender, + BloodType: card.BloodType, + Province: card.Province, + District: card.District, + SubDistrict: card.SubDistrict, + Hamlet: card.Hamlet, + Village: card.Village, + Neighbourhood: card.Neighbourhood, + PostalCode: card.PostalCode, + Religion: card.Religion, + Maritalstatus: card.Maritalstatus, + Job: card.Job, + Citizenship: card.Citizenship, + Validuntil: card.Validuntil, + Cardphoto: card.Cardphoto, + CreatedAt: createdAt, + UpdatedAt: updatedAt, + } + response = append(response, dto) + } + + return response, nil +} + +func (s *identityCardService) UpdateUserRegistrationStatusByIdentityCard(ctx context.Context, identityCardUserID string, newStatus string) error { + + user, err := s.authRepo.FindUserByID(ctx, identityCardUserID) + if err != nil { + log.Printf("Error finding user by ID %s: %v", identityCardUserID, err) + return fmt.Errorf("user not found: %w", err) + } + + updates := map[string]interface{}{ + "registration_status": newStatus, + "updated_at": time.Now(), + } + + switch newStatus { + case utils.RegStatusConfirmed: + updates["registration_progress"] = utils.ProgressDataSubmitted + + identityCards, err := s.GetIdentityCardsByUserID(ctx, identityCardUserID) + if err != nil { + log.Printf("Error fetching identity cards for user ID %s: %v", identityCardUserID, err) + return fmt.Errorf("failed to fetch identity card data: %w", err) + } + + if len(identityCards) == 0 { + log.Printf("No identity card found for user ID %s", identityCardUserID) + return fmt.Errorf("no identity card found for user") + } + + identityCard := identityCards[0] + + updates["name"] = identityCard.Fullname + updates["gender"] = identityCard.Gender + updates["dateofbirth"] = identityCard.Dateofbirth + updates["placeofbirth"] = identityCard.District + + log.Printf("Syncing user data for ID %s: name=%s, gender=%s, dob=%s, pob=%s", + identityCardUserID, identityCard.Fullname, identityCard.Gender, + identityCard.Dateofbirth, identityCard.District) + + case utils.RegStatusRejected: + updates["registration_progress"] = utils.ProgressOTPVerified + + } + + err = s.authRepo.PatchUser(ctx, user.ID, updates) + if err != nil { + log.Printf("Error updating user registration status for user ID %s: %v", user.ID, err) + return fmt.Errorf("failed to update user registration status: %w", err) + } + + log.Printf("Successfully updated registration status for user ID %s to %s", user.ID, newStatus) + + if newStatus == utils.RegStatusConfirmed { + log.Printf("User profile data synced successfully for user ID %s", user.ID) + } + + return nil +} + +func (s *identityCardService) mapIdentityCardToDTO(card model.IdentityCard) ResponseIdentityCardDTO { + createdAt, _ := utils.FormatDateToIndonesianFormat(card.CreatedAt) + updatedAt, _ := utils.FormatDateToIndonesianFormat(card.UpdatedAt) + return ResponseIdentityCardDTO{ + ID: card.ID, + UserID: card.UserID, + Identificationumber: card.Identificationumber, + Placeofbirth: card.Placeofbirth, + Dateofbirth: card.Dateofbirth, + Gender: card.Gender, + BloodType: card.BloodType, + Province: card.Province, + District: card.District, + SubDistrict: card.SubDistrict, + Hamlet: card.Hamlet, + Village: card.Village, + Neighbourhood: card.Neighbourhood, + PostalCode: card.PostalCode, + Religion: card.Religion, + Maritalstatus: card.Maritalstatus, + Job: card.Job, + Citizenship: card.Citizenship, + Validuntil: card.Validuntil, + Cardphoto: card.Cardphoto, + CreatedAt: createdAt, + UpdatedAt: updatedAt, + } +} + +func (s *identityCardService) mapUserToDTO(user model.User) userprofile.UserProfileResponseDTO { + avatar := "" + if user.Avatar != nil { + avatar = *user.Avatar + } + + var roleDTO role.RoleResponseDTO + if user.Role != nil { + roleDTO = role.RoleResponseDTO{ + ID: user.Role.ID, + RoleName: user.Role.RoleName, + } + } + + createdAt, _ := utils.FormatDateToIndonesianFormat(user.CreatedAt) + updatedAt, _ := utils.FormatDateToIndonesianFormat(user.UpdatedAt) + return userprofile.UserProfileResponseDTO{ + ID: user.ID, + Avatar: avatar, + Name: user.Name, + Gender: user.Gender, + Dateofbirth: user.Dateofbirth, + Placeofbirth: user.Placeofbirth, + Phone: user.Phone, + Email: user.Email, + PhoneVerified: user.PhoneVerified, + Role: roleDTO, + CreatedAt: createdAt, + UpdatedAt: updatedAt, + } +} diff --git a/internal/userpin/userpin_dto.go b/internal/userpin/userpin_dto.go index 5370ea6..c3a3302 100644 --- a/internal/userpin/userpin_dto.go +++ b/internal/userpin/userpin_dto.go @@ -6,7 +6,7 @@ import ( ) type RequestPinDTO struct { - DeviceId string `json:"device_id"` + // DeviceId string `json:"device_id"` Pin string `json:"userpin"` } diff --git a/internal/userpin/userpin_handler.go b/internal/userpin/userpin_handler.go index 518551e..eced2fd 100644 --- a/internal/userpin/userpin_handler.go +++ b/internal/userpin/userpin_handler.go @@ -15,47 +15,39 @@ func NewUserPinHandler(service UserPinService) *UserPinHandler { return &UserPinHandler{service} } -// userID, ok := c.Locals("user_id").(string) -// -// if !ok || userID == "" { -// return utils.Unauthorized(c, "user_id is missing or invalid") -// } func (h *UserPinHandler) CreateUserPinHandler(c *fiber.Ctx) error { - // Ambil klaim pengguna yang sudah diautentikasi + claims, err := middleware.GetUserFromContext(c) if err != nil { - return err + return utils.Unauthorized(c, "Authentication required") + } + + if claims.UserID == "" || claims.DeviceID == "" { + return utils.BadRequest(c, "Invalid user claims") } - // Parsing body request untuk PIN var req RequestPinDTO if err := c.BodyParser(&req); err != nil { return utils.BadRequest(c, "Invalid request body") } - // Validasi request PIN if errs, ok := req.ValidateRequestPinDTO(); !ok { return utils.ResponseErrorData(c, fiber.StatusBadRequest, "Validation error", errs) } - // Panggil service untuk membuat PIN - err = h.service.CreateUserPin(c.Context(), claims.UserID, &req) + pintokenresponse, err := h.service.CreateUserPin(c.Context(), claims.UserID, claims.DeviceID, &req) if err != nil { - if err.Error() == "PIN already created" { - return utils.BadRequest(c, err.Error()) // Jika PIN sudah ada, kembalikan error 400 + if err.Error() == Pinhasbeencreated { + return utils.BadRequest(c, err.Error()) } - return utils.InternalServerError(c, err.Error()) // Jika terjadi error lain, internal server error + return utils.InternalServerError(c, err.Error()) } - // Mengembalikan response sukses jika berhasil - return utils.Success(c, "PIN created successfully") + return utils.SuccessWithData(c, "PIN created successfully", pintokenresponse) } func (h *UserPinHandler) VerifyPinHandler(c *fiber.Ctx) error { - // userID, ok := c.Locals("user_id").(string) - // if !ok || userID == "" { - // return utils.Unauthorized(c, "user_id is missing or invalid") - // } + claims, err := middleware.GetUserFromContext(c) if err != nil { return err @@ -66,12 +58,10 @@ func (h *UserPinHandler) VerifyPinHandler(c *fiber.Ctx) error { return utils.BadRequest(c, "Invalid request body") } - token, err := h.service.VerifyUserPin(c.Context(), claims.UserID, &req) + token, err := h.service.VerifyUserPin(c.Context(), claims.UserID, claims.DeviceID, &req) if err != nil { return utils.BadRequest(c, err.Error()) } - return utils.SuccessWithData(c, "PIN verified successfully", fiber.Map{ - "token": token, - }) + return utils.SuccessWithData(c, "PIN verified successfully", token) } diff --git a/internal/userpin/userpin_route.go b/internal/userpin/userpin_route.go index 81f4dc4..3c34411 100644 --- a/internal/userpin/userpin_route.go +++ b/internal/userpin/userpin_route.go @@ -3,6 +3,7 @@ package userpin import ( "rijig/config" "rijig/internal/authentication" + "rijig/internal/userprofile" "rijig/middleware" "github.com/gofiber/fiber/v2" @@ -11,8 +12,9 @@ import ( func UsersPinRoute(api fiber.Router) { userPinRepo := NewUserPinRepository(config.DB) authRepo := authentication.NewAuthenticationRepository(config.DB) + userprofileRepo := userprofile.NewUserProfileRepository(config.DB) - userPinService := NewUserPinService(userPinRepo, authRepo) + userPinService := NewUserPinService(userPinRepo, authRepo, userprofileRepo) userPinHandler := NewUserPinHandler(userPinService) diff --git a/internal/userpin/userpin_service.go b/internal/userpin/userpin_service.go index fd6415f..1c663b2 100644 --- a/internal/userpin/userpin_service.go +++ b/internal/userpin/userpin_service.go @@ -2,45 +2,51 @@ package userpin import ( "context" + "errors" "fmt" "rijig/internal/authentication" + "rijig/internal/userprofile" "rijig/model" "rijig/utils" "strings" + + "gorm.io/gorm" ) type UserPinService interface { - CreateUserPin(ctx context.Context, userID string, dto *RequestPinDTO) error - VerifyUserPin(ctx context.Context, userID string, pin *RequestPinDTO) (*utils.TokenResponse, error) + CreateUserPin(ctx context.Context, userID, deviceId string, dto *RequestPinDTO) (*authentication.AuthResponse, error) + VerifyUserPin(ctx context.Context, userID, deviceID string, pin *RequestPinDTO) (*utils.TokenResponse, error) } type userPinService struct { - UserPinRepo UserPinRepository - authRepo authentication.AuthenticationRepository + UserPinRepo UserPinRepository + authRepo authentication.AuthenticationRepository + userProfileRepo userprofile.UserProfileRepository } func NewUserPinService(UserPinRepo UserPinRepository, - authRepo authentication.AuthenticationRepository) UserPinService { - return &userPinService{UserPinRepo, authRepo} + authRepo authentication.AuthenticationRepository, + userProfileRepo userprofile.UserProfileRepository) UserPinService { + return &userPinService{UserPinRepo, authRepo, userProfileRepo} } -func (s *userPinService) CreateUserPin(ctx context.Context, userID string, dto *RequestPinDTO) error { +var ( + Pinhasbeencreated = "PIN already created" +) - if errs, ok := dto.ValidateRequestPinDTO(); !ok { - return fmt.Errorf("validation error: %v", errs) - } +func (s *userPinService) CreateUserPin(ctx context.Context, userID, deviceId string, dto *RequestPinDTO) (*authentication.AuthResponse, error) { - existingPin, err := s.UserPinRepo.FindByUserID(ctx, userID) + _, err := s.UserPinRepo.FindByUserID(ctx, userID) if err != nil { - return fmt.Errorf("failed to check existing PIN: %w", err) - } - if existingPin != nil { - return fmt.Errorf("PIN already created") + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, fmt.Errorf("user not found") + } + return nil, fmt.Errorf("%v", Pinhasbeencreated) } hashed, err := utils.HashingPlainText(dto.Pin) if err != nil { - return fmt.Errorf("failed to hash PIN: %w", err) + return nil, fmt.Errorf("failed to hash PIN: %w", err) } userPin := &model.UserPin{ @@ -49,35 +55,63 @@ func (s *userPinService) CreateUserPin(ctx context.Context, userID string, dto * } if err := s.UserPinRepo.Create(ctx, userPin); err != nil { - return fmt.Errorf("failed to create PIN: %w", err) - } - - user, err := s.authRepo.FindUserByID(ctx, userID) - if err != nil { - return fmt.Errorf("user not found") - } - - roleName := strings.ToLower(user.Role.RoleName) - - progress := authentication.IsRegistrationComplete(roleName, int(user.RegistrationProgress)) - // progress := utils.GetNextRegistrationStep(roleName, int(user.RegistrationProgress)) - // progress := utils.GetNextRegistrationStep(roleName, user.RegistrationProgress) - // progress := utils.GetNextRegistrationStep(roleName, user.RegistrationProgress) - - if !progress { - err = s.authRepo.PatchUser(ctx, userID, map[string]interface{}{ - "registration_progress": int(user.RegistrationProgress) + 1, - "registration_status": utils.RegStatusComplete, - }) - if err != nil { - return fmt.Errorf("failed to update user progress: %w", err) + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, fmt.Errorf("user not found") } + return nil, fmt.Errorf("failed to create pin: %w", err) } - return nil + updates := map[string]interface{}{ + "registration_progress": utils.ProgressComplete, + "registration_status": utils.RegStatusComplete, + } + + if err = s.authRepo.PatchUser(ctx, userID, updates); err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, fmt.Errorf("user not found") + } + return nil, fmt.Errorf("failed to update user profile: %w", err) + } + + updated, err := s.userProfileRepo.GetByID(ctx, userID) + if err != nil { + if errors.Is(err, userprofile.ErrUserNotFound) { + return nil, fmt.Errorf("user not found") + } + return nil, fmt.Errorf("failed to get updated user: %w", err) + } + + tokenResponse, err := utils.GenerateTokenPair( + updated.ID, + updated.Role.RoleName, + deviceId, + updated.RegistrationStatus, + int(updated.RegistrationProgress), + ) + + if err != nil { + return nil, fmt.Errorf("gagal generate token: %v", err) + } + + nextStep := utils.GetNextRegistrationStep( + updated.Role.RoleName, + int(updated.RegistrationProgress), + updated.RegistrationStatus, + ) + + return &authentication.AuthResponse{ + Message: "Isi data diri berhasil", + AccessToken: tokenResponse.AccessToken, + RefreshToken: tokenResponse.RefreshToken, + TokenType: string(tokenResponse.TokenType), + ExpiresIn: tokenResponse.ExpiresIn, + RegistrationStatus: updated.RegistrationStatus, + NextStep: nextStep, + SessionID: tokenResponse.SessionID, + }, nil } -func (s *userPinService) VerifyUserPin(ctx context.Context, userID string, pin *RequestPinDTO) (*utils.TokenResponse, error) { +func (s *userPinService) VerifyUserPin(ctx context.Context, userID, deviceID string, pin *RequestPinDTO) (*utils.TokenResponse, error) { user, err := s.authRepo.FindUserByID(ctx, userID) if err != nil { return nil, fmt.Errorf("user not found") @@ -93,5 +127,5 @@ func (s *userPinService) VerifyUserPin(ctx context.Context, userID string, pin * } roleName := strings.ToLower(user.Role.RoleName) - return utils.GenerateTokenPair(user.ID, roleName, pin.DeviceId, user.RegistrationStatus, int(user.RegistrationProgress)) + return utils.GenerateTokenPair(user.ID, roleName, deviceID, user.RegistrationStatus, int(user.RegistrationProgress)) } diff --git a/internal/userprofile/userprofile_handler.go b/internal/userprofile/userprofile_handler.go index 27ac07d..05cc96d 100644 --- a/internal/userprofile/userprofile_handler.go +++ b/internal/userprofile/userprofile_handler.go @@ -1 +1,76 @@ -package userprofile \ No newline at end of file +package userprofile + +import ( + "context" + "log" + "rijig/middleware" + "rijig/utils" + "strings" + "time" + + "github.com/gofiber/fiber/v2" +) + +type UserProfileHandler struct { + service UserProfileService +} + +func NewUserProfileHandler(service UserProfileService) *UserProfileHandler { + return &UserProfileHandler{ + service: service, + } +} + +func (h *UserProfileHandler) GetUserProfile(c *fiber.Ctx) error { + + claims, err := middleware.GetUserFromContext(c) + if err != nil { + return err + } + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + userProfile, err := h.service.GetUserProfile(ctx, claims.UserID) + if err != nil { + if strings.Contains(err.Error(), ErrUserNotFound.Error()) { + return utils.NotFound(c, "User profile not found") + } + + log.Printf("Error getting user profile: %v", err) + return utils.InternalServerError(c, "Failed to retrieve user profile") + } + + return utils.SuccessWithData(c, "User profile retrieved successfully", userProfile) +} + +func (h *UserProfileHandler) UpdateUserProfile(c *fiber.Ctx) error { + claims, err := middleware.GetUserFromContext(c) + if err != nil { + return err + } + + var req RequestUserProfileDTO + if err := c.BodyParser(&req); err != nil { + return utils.BadRequest(c, "Invalid request format") + } + + if validationErrors, isValid := req.ValidateRequestUserProfileDTO(); !isValid { + return utils.ResponseErrorData(c, fiber.StatusBadRequest, "Validation failed", validationErrors) + } + + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + defer cancel() + + updatedProfile, err := h.service.UpdateRegistUserProfile(ctx, claims.UserID, claims.DeviceID, &req) + if err != nil { + + if strings.Contains(err.Error(), "user not found") { + return utils.NotFound(c, "User not found") + } + + log.Printf("Error updating user profile: %v", err) + return utils.InternalServerError(c, "Failed to update user profile") + } + + return utils.SuccessWithData(c, "User profile updated successfully", updatedProfile) +} diff --git a/internal/userprofile/userprofile_repo.go b/internal/userprofile/userprofile_repo.go index 052c798..926950f 100644 --- a/internal/userprofile/userprofile_repo.go +++ b/internal/userprofile/userprofile_repo.go @@ -2,34 +2,106 @@ package userprofile import ( "context" + "errors" "rijig/model" "gorm.io/gorm" ) -type AuthenticationRepository interface { - UpdateUser(ctx context.Context, user *model.User) error - PatchUser(ctx context.Context, userID string, updates map[string]interface{}) error +type UserProfileRepository interface { + GetByID(ctx context.Context, userID string) (*model.User, error) + GetByRoleName(ctx context.Context, roleName string) ([]*model.User, error) + // GetIdentityCardsByUserRegStatus(ctx context.Context, userRegStatus string) ([]model.IdentityCard, error) + // GetCompanyProfileByUserRegStatus(ctx context.Context, userRegStatus string) ([]model.IdentityCard, error) + Update(ctx context.Context, userID string, user *model.User) error } -type authenticationRepository struct { +type userProfileRepository struct { db *gorm.DB } -func NewAuthenticationRepository(db *gorm.DB) AuthenticationRepository { - return &authenticationRepository{db} +func NewUserProfileRepository(db *gorm.DB) UserProfileRepository { + return &userProfileRepository{ + db: db, + } } -func (r *authenticationRepository) UpdateUser(ctx context.Context, user *model.User) error { - return r.db.WithContext(ctx). - Model(&model.User{}). - Where("id = ?", user.ID). - Updates(user).Error +func (r *userProfileRepository) GetByID(ctx context.Context, userID string) (*model.User, error) { + var user model.User + + err := r.db.WithContext(ctx). + Preload("Role"). + Where("id = ?", userID). + First(&user).Error + + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, ErrUserNotFound + } + return nil, err + } + + return &user, nil } -func (r *authenticationRepository) PatchUser(ctx context.Context, userID string, updates map[string]interface{}) error { - return r.db.WithContext(ctx). +func (r *userProfileRepository) GetByRoleName(ctx context.Context, roleName string) ([]*model.User, error) { + var users []*model.User + + err := r.db.WithContext(ctx). + Preload("Role"). + Joins("JOIN roles ON users.role_id = roles.id"). + Where("roles.role_name = ?", roleName). + Find(&users).Error + + if err != nil { + return nil, err + } + + return users, nil +} + +/* func (r *userProfileRepository) GetIdentityCardsByUserRegStatus(ctx context.Context, userRegStatus string) ([]model.IdentityCard, error) { + var identityCards []model.IdentityCard + + if err := r.db.WithContext(ctx). + Joins("JOIN users ON identity_cards.user_id = users.id"). + Where("users.registration_status = ?", userRegStatus). + Preload("User"). + Find(&identityCards).Error; err != nil { + log.Printf("Error fetching identity cards by user registration status: %v", err) + return nil, fmt.Errorf("error fetching identity cards by user registration status: %w", err) + } + + return identityCards, nil +} + +func (r *userProfileRepository) GetCompanyProfileByUserRegStatus(ctx context.Context, userRegStatus string) ([]model.IdentityCard, error) { + var identityCards []model.IdentityCard + + if err := r.db.WithContext(ctx). + Joins("JOIN users ON company_profiles.user_id = users.id"). + Where("users.registration_status = ?", userRegStatus). + Preload("User"). + Find(&identityCards).Error; err != nil { + log.Printf("Error fetching identity cards by user registration status: %v", err) + return nil, fmt.Errorf("error fetching identity cards by user registration status: %w", err) + } + return identityCards, nil +} */ + +func (r *userProfileRepository) Update(ctx context.Context, userID string, user *model.User) error { + result := r.db.WithContext(ctx). Model(&model.User{}). Where("id = ?", userID). - Updates(updates).Error + Updates(user) + + if result.Error != nil { + return result.Error + } + + if result.RowsAffected == 0 { + return ErrUserNotFound + } + + return nil } diff --git a/internal/userprofile/userprofile_route.go b/internal/userprofile/userprofile_route.go index 27ac07d..e011fa4 100644 --- a/internal/userprofile/userprofile_route.go +++ b/internal/userprofile/userprofile_route.go @@ -1 +1,20 @@ -package userprofile \ No newline at end of file +package userprofile + +import ( + "rijig/config" + "rijig/middleware" + + "github.com/gofiber/fiber/v2" +) + +func UserProfileRouter(api fiber.Router) { + userProfileRepo := NewUserProfileRepository(config.DB) + userProfileService := NewUserProfileService(userProfileRepo) + userProfileHandler := NewUserProfileHandler(userProfileService) + + userRoute := api.Group("/userprofile") + userRoute.Use(middleware.AuthMiddleware()) + + userRoute.Get("/", userProfileHandler.GetUserProfile) + userRoute.Put("/update", userProfileHandler.UpdateUserProfile) +} \ No newline at end of file diff --git a/internal/userprofile/userprofile_service.go b/internal/userprofile/userprofile_service.go index 27ac07d..dd405d1 100644 --- a/internal/userprofile/userprofile_service.go +++ b/internal/userprofile/userprofile_service.go @@ -1 +1,160 @@ -package userprofile \ No newline at end of file +package userprofile + +import ( + "context" + "errors" + "fmt" + "rijig/internal/authentication" + "rijig/internal/role" + "rijig/model" + "rijig/utils" + "time" +) + +var ( + ErrUserNotFound = errors.New("user tidak ditemukan") +) + +type UserProfileService interface { + GetUserProfile(ctx context.Context, userID string) (*UserProfileResponseDTO, error) + UpdateRegistUserProfile(ctx context.Context, userID, deviceId string, req *RequestUserProfileDTO) (*authentication.AuthResponse, error) +} + +type userProfileService struct { + repo UserProfileRepository +} + +func NewUserProfileService(repo UserProfileRepository) UserProfileService { + return &userProfileService{ + repo: repo, + } +} + +func (s *userProfileService) GetUserProfile(ctx context.Context, userID string) (*UserProfileResponseDTO, error) { + user, err := s.repo.GetByID(ctx, userID) + if err != nil { + if errors.Is(err, ErrUserNotFound) { + return nil, fmt.Errorf("user not found") + } + return nil, fmt.Errorf("failed to get user profile: %w", err) + } + + return s.mapToResponseDTO(user), nil +} + +func (s *userProfileService) UpdateRegistUserProfile(ctx context.Context, userID, deviceId string, req *RequestUserProfileDTO) (*authentication.AuthResponse, error) { + + _, err := s.repo.GetByID(ctx, userID) + if err != nil { + if errors.Is(err, ErrUserNotFound) { + return nil, fmt.Errorf("user not found") + } + return nil, fmt.Errorf("failed to get user: %w", err) + } + + updateUser := &model.User{ + Name: req.Name, + Gender: req.Gender, + Dateofbirth: req.Dateofbirth, + Placeofbirth: req.Placeofbirth, + Phone: req.Phone, + RegistrationProgress: utils.ProgressDataSubmitted, + } + + if err := s.repo.Update(ctx, userID, updateUser); err != nil { + if errors.Is(err, ErrUserNotFound) { + return nil, fmt.Errorf("user not found") + } + return nil, fmt.Errorf("failed to update user profile: %w", err) + } + + updatedUser, err := s.repo.GetByID(ctx, userID) + if err != nil { + if errors.Is(err, ErrUserNotFound) { + return nil, fmt.Errorf("user not found") + } + return nil, fmt.Errorf("failed to get updated user: %w", err) + } + + tokenResponse, err := utils.GenerateTokenPair( + updatedUser.ID, + updatedUser.Role.RoleName, + // req.DeviceID, + deviceId, + updatedUser.RegistrationStatus, + int(updatedUser.RegistrationProgress), + ) + + if err != nil { + return nil, fmt.Errorf("gagal generate token: %v", err) + } + + nextStep := utils.GetNextRegistrationStep( + updatedUser.Role.RoleName, + int(updatedUser.RegistrationProgress), + updateUser.RegistrationStatus, + ) + + return &authentication.AuthResponse{ + Message: "Isi data diri berhasil", + AccessToken: tokenResponse.AccessToken, + RefreshToken: tokenResponse.RefreshToken, + TokenType: string(tokenResponse.TokenType), + ExpiresIn: tokenResponse.ExpiresIn, + RegistrationStatus: updateUser.RegistrationStatus, + NextStep: nextStep, + SessionID: tokenResponse.SessionID, + }, nil + + // return s.mapToResponseDTO(updatedUser), nil +} + +func (s *userProfileService) mapToResponseDTO(user *model.User) *UserProfileResponseDTO { + + createdAt, err := utils.FormatDateToIndonesianFormat(user.CreatedAt) + if err != nil { + createdAt = user.CreatedAt.Format(time.RFC3339) + } + + updatedAt, err := utils.FormatDateToIndonesianFormat(user.UpdatedAt) + if err != nil { + updatedAt = user.UpdatedAt.Format(time.RFC3339) + } + + response := &UserProfileResponseDTO{ + ID: user.ID, + Name: user.Name, + Gender: user.Gender, + Dateofbirth: user.Dateofbirth, + Placeofbirth: user.Placeofbirth, + Phone: user.Phone, + PhoneVerified: user.PhoneVerified, + CreatedAt: createdAt, + UpdatedAt: updatedAt, + } + + if user.Avatar != nil { + response.Avatar = *user.Avatar + } + + if user.Role != nil { + roleCreatedAt, err := utils.FormatDateToIndonesianFormat(user.Role.CreatedAt) + if err != nil { + roleCreatedAt = user.Role.CreatedAt.Format(time.RFC3339) + } + + roleUpdatedAt, err := utils.FormatDateToIndonesianFormat(user.Role.UpdatedAt) + if err != nil { + roleUpdatedAt = user.Role.UpdatedAt.Format(time.RFC3339) + } + + response.Role = role.RoleResponseDTO{ + ID: user.Role.ID, + RoleName: user.Role.RoleName, + CreatedAt: roleCreatedAt, + UpdatedAt: roleUpdatedAt, + } + } + + return response +} diff --git a/internal/wilayahindo/wilayahindo_handler.go b/internal/wilayahindo/wilayahindo_handler.go index 2445724..cf23a6e 100644 --- a/internal/wilayahindo/wilayahindo_handler.go +++ b/internal/wilayahindo/wilayahindo_handler.go @@ -1 +1,272 @@ -package wilayahindo \ No newline at end of file +package wilayahindo + +import ( + "strconv" + "strings" + + "rijig/utils" + + "github.com/gofiber/fiber/v2" +) + +type WilayahIndonesiaHandler struct { + WilayahService WilayahIndonesiaService +} + +func NewWilayahIndonesiaHandler(wilayahService WilayahIndonesiaService) *WilayahIndonesiaHandler { + return &WilayahIndonesiaHandler{ + WilayahService: wilayahService, + } +} + +func (h *WilayahIndonesiaHandler) ImportDataFromCSV(c *fiber.Ctx) error { + ctx := c.Context() + + if err := h.WilayahService.ImportDataFromCSV(ctx); err != nil { + return utils.InternalServerError(c, "Failed to import data from CSV: "+err.Error()) + } + + return utils.Success(c, "Data imported successfully from CSV") +} + +func (h *WilayahIndonesiaHandler) GetAllProvinces(c *fiber.Ctx) error { + ctx := c.Context() + + page, limit, err := h.parsePaginationParams(c) + if err != nil { + return utils.BadRequest(c, err.Error()) + } + + provinces, total, err := h.WilayahService.GetAllProvinces(ctx, page, limit) + if err != nil { + return utils.InternalServerError(c, "Failed to fetch provinces: "+err.Error()) + } + + response := map[string]interface{}{ + "provinces": provinces, + "total": total, + } + + return utils.SuccessWithPagination(c, "Provinces retrieved successfully", response, page, limit) +} + +func (h *WilayahIndonesiaHandler) GetProvinceByID(c *fiber.Ctx) error { + ctx := c.Context() + + id := c.Params("id") + if id == "" { + return utils.BadRequest(c, "Province ID is required") + } + + page, limit, err := h.parsePaginationParams(c) + if err != nil { + return utils.BadRequest(c, err.Error()) + } + + province, totalRegencies, err := h.WilayahService.GetProvinceByID(ctx, id, page, limit) + if err != nil { + if strings.Contains(err.Error(), "not found") { + return utils.NotFound(c, "Province not found") + } + return utils.InternalServerError(c, "Failed to fetch province: "+err.Error()) + } + + response := map[string]interface{}{ + "province": province, + "total_regencies": totalRegencies, + } + + return utils.SuccessWithPagination(c, "Province retrieved successfully", response, page, limit) +} + +func (h *WilayahIndonesiaHandler) GetAllRegencies(c *fiber.Ctx) error { + ctx := c.Context() + + page, limit, err := h.parsePaginationParams(c) + if err != nil { + return utils.BadRequest(c, err.Error()) + } + + regencies, total, err := h.WilayahService.GetAllRegencies(ctx, page, limit) + if err != nil { + return utils.InternalServerError(c, "Failed to fetch regencies: "+err.Error()) + } + + response := map[string]interface{}{ + "regencies": regencies, + "total": total, + } + + return utils.SuccessWithPagination(c, "Regencies retrieved successfully", response, page, limit) +} + +func (h *WilayahIndonesiaHandler) GetRegencyByID(c *fiber.Ctx) error { + ctx := c.Context() + + id := c.Params("id") + if id == "" { + return utils.BadRequest(c, "Regency ID is required") + } + + page, limit, err := h.parsePaginationParams(c) + if err != nil { + return utils.BadRequest(c, err.Error()) + } + + regency, totalDistricts, err := h.WilayahService.GetRegencyByID(ctx, id, page, limit) + if err != nil { + if strings.Contains(err.Error(), "not found") { + return utils.NotFound(c, "Regency not found") + } + return utils.InternalServerError(c, "Failed to fetch regency: "+err.Error()) + } + + response := map[string]interface{}{ + "regency": regency, + "total_districts": totalDistricts, + } + + return utils.SuccessWithPagination(c, "Regency retrieved successfully", response, page, limit) +} + +func (h *WilayahIndonesiaHandler) GetAllDistricts(c *fiber.Ctx) error { + ctx := c.Context() + + page, limit, err := h.parsePaginationParams(c) + if err != nil { + return utils.BadRequest(c, err.Error()) + } + + districts, total, err := h.WilayahService.GetAllDistricts(ctx, page, limit) + if err != nil { + return utils.InternalServerError(c, "Failed to fetch districts: "+err.Error()) + } + + response := map[string]interface{}{ + "districts": districts, + "total": total, + } + + return utils.SuccessWithPagination(c, "Districts retrieved successfully", response, page, limit) +} + +func (h *WilayahIndonesiaHandler) GetDistrictByID(c *fiber.Ctx) error { + ctx := c.Context() + + id := c.Params("id") + if id == "" { + return utils.BadRequest(c, "District ID is required") + } + + page, limit, err := h.parsePaginationParams(c) + if err != nil { + return utils.BadRequest(c, err.Error()) + } + + district, totalVillages, err := h.WilayahService.GetDistrictByID(ctx, id, page, limit) + if err != nil { + if strings.Contains(err.Error(), "not found") { + return utils.NotFound(c, "District not found") + } + return utils.InternalServerError(c, "Failed to fetch district: "+err.Error()) + } + + response := map[string]interface{}{ + "district": district, + "total_villages": totalVillages, + } + + return utils.SuccessWithPagination(c, "District retrieved successfully", response, page, limit) +} + +func (h *WilayahIndonesiaHandler) GetAllVillages(c *fiber.Ctx) error { + ctx := c.Context() + + page, limit, err := h.parsePaginationParams(c) + if err != nil { + return utils.BadRequest(c, err.Error()) + } + + villages, total, err := h.WilayahService.GetAllVillages(ctx, page, limit) + if err != nil { + return utils.InternalServerError(c, "Failed to fetch villages: "+err.Error()) + } + + response := map[string]interface{}{ + "villages": villages, + "total": total, + } + + return utils.SuccessWithPagination(c, "Villages retrieved successfully", response, page, limit) +} + +func (h *WilayahIndonesiaHandler) GetVillageByID(c *fiber.Ctx) error { + ctx := c.Context() + + id := c.Params("id") + if id == "" { + return utils.BadRequest(c, "Village ID is required") + } + + village, err := h.WilayahService.GetVillageByID(ctx, id) + if err != nil { + if strings.Contains(err.Error(), "not found") { + return utils.NotFound(c, "Village not found") + } + return utils.InternalServerError(c, "Failed to fetch village: "+err.Error()) + } + + return utils.SuccessWithData(c, "Village retrieved successfully", village) +} + +func (h *WilayahIndonesiaHandler) parsePaginationParams(c *fiber.Ctx) (int, int, error) { + + page := 1 + limit := 10 + + if pageStr := c.Query("page"); pageStr != "" { + parsedPage, err := strconv.Atoi(pageStr) + if err != nil { + return 0, 0, fiber.NewError(fiber.StatusBadRequest, "Invalid page parameter") + } + if parsedPage < 1 { + return 0, 0, fiber.NewError(fiber.StatusBadRequest, "Page must be greater than 0") + } + page = parsedPage + } + + if limitStr := c.Query("limit"); limitStr != "" { + parsedLimit, err := strconv.Atoi(limitStr) + if err != nil { + return 0, 0, fiber.NewError(fiber.StatusBadRequest, "Invalid limit parameter") + } + if parsedLimit < 1 { + return 0, 0, fiber.NewError(fiber.StatusBadRequest, "Limit must be greater than 0") + } + if parsedLimit > 100 { + return 0, 0, fiber.NewError(fiber.StatusBadRequest, "Limit cannot exceed 100") + } + limit = parsedLimit + } + + return page, limit, nil +} + +func (h *WilayahIndonesiaHandler) SetupRoutes(app *fiber.App) { + + api := app.Group("/api/v1/wilayah") + + api.Post("/import", h.ImportDataFromCSV) + + api.Get("/provinces", h.GetAllProvinces) + api.Get("/provinces/:id", h.GetProvinceByID) + + api.Get("/regencies", h.GetAllRegencies) + api.Get("/regencies/:id", h.GetRegencyByID) + + api.Get("/districts", h.GetAllDistricts) + api.Get("/districts/:id", h.GetDistrictByID) + + api.Get("/villages", h.GetAllVillages) + api.Get("/villages/:id", h.GetVillageByID) +} diff --git a/internal/wilayahindo/wilayahindo_route.go b/internal/wilayahindo/wilayahindo_route.go index 2445724..7d11403 100644 --- a/internal/wilayahindo/wilayahindo_route.go +++ b/internal/wilayahindo/wilayahindo_route.go @@ -1 +1,32 @@ -package wilayahindo \ No newline at end of file +package wilayahindo + +import ( + "rijig/config" + "rijig/middleware" + + "github.com/gofiber/fiber/v2" +) + +func WilayahRouter(api fiber.Router) { + + wilayahRepo := NewWilayahIndonesiaRepository(config.DB) + wilayahService := NewWilayahIndonesiaService(wilayahRepo) + wilayahHandler := NewWilayahIndonesiaHandler(wilayahService) + + api.Post("/import/data-wilayah-indonesia", middleware.RequireAdminRole(), wilayahHandler.ImportDataFromCSV) + + wilayahAPI := api.Group("/wilayah-indonesia") + + wilayahAPI.Get("/provinces", wilayahHandler.GetAllProvinces) + wilayahAPI.Get("/provinces/:provinceid", wilayahHandler.GetProvinceByID) + + wilayahAPI.Get("/regencies", wilayahHandler.GetAllRegencies) + wilayahAPI.Get("/regencies/:regencyid", wilayahHandler.GetRegencyByID) + + wilayahAPI.Get("/districts", wilayahHandler.GetAllDistricts) + wilayahAPI.Get("/districts/:districtid", wilayahHandler.GetDistrictByID) + + wilayahAPI.Get("/villages", wilayahHandler.GetAllVillages) + wilayahAPI.Get("/villages/:villageid", wilayahHandler.GetVillageByID) + +} diff --git a/internal/wilayahindo/wilayahindo_service.go b/internal/wilayahindo/wilayahindo_service.go index 492b54f..b1a0407 100644 --- a/internal/wilayahindo/wilayahindo_service.go +++ b/internal/wilayahindo/wilayahindo_service.go @@ -5,7 +5,6 @@ import ( "fmt" "time" - "rijig/dto" "rijig/model" "rijig/utils" ) @@ -13,17 +12,17 @@ import ( type WilayahIndonesiaService interface { ImportDataFromCSV(ctx context.Context) error - GetAllProvinces(ctx context.Context, page, limit int) ([]dto.ProvinceResponseDTO, int, error) - GetProvinceByID(ctx context.Context, id string, page, limit int) (*dto.ProvinceResponseDTO, int, error) + GetAllProvinces(ctx context.Context, page, limit int) ([]ProvinceResponseDTO, int, error) + GetProvinceByID(ctx context.Context, id string, page, limit int) (*ProvinceResponseDTO, int, error) - GetAllRegencies(ctx context.Context, page, limit int) ([]dto.RegencyResponseDTO, int, error) - GetRegencyByID(ctx context.Context, id string, page, limit int) (*dto.RegencyResponseDTO, int, error) + GetAllRegencies(ctx context.Context, page, limit int) ([]RegencyResponseDTO, int, error) + GetRegencyByID(ctx context.Context, id string, page, limit int) (*RegencyResponseDTO, int, error) - GetAllDistricts(ctx context.Context, page, limit int) ([]dto.DistrictResponseDTO, int, error) - GetDistrictByID(ctx context.Context, id string, page, limit int) (*dto.DistrictResponseDTO, int, error) + GetAllDistricts(ctx context.Context, page, limit int) ([]DistrictResponseDTO, int, error) + GetDistrictByID(ctx context.Context, id string, page, limit int) (*DistrictResponseDTO, int, error) - GetAllVillages(ctx context.Context, page, limit int) ([]dto.VillageResponseDTO, int, error) - GetVillageByID(ctx context.Context, id string) (*dto.VillageResponseDTO, error) + GetAllVillages(ctx context.Context, page, limit int) ([]VillageResponseDTO, int, error) + GetVillageByID(ctx context.Context, id string) (*VillageResponseDTO, error) } type wilayahIndonesiaService struct { @@ -122,11 +121,11 @@ func (s *wilayahIndonesiaService) ImportDataFromCSV(ctx context.Context) error { return nil } -func (s *wilayahIndonesiaService) GetAllProvinces(ctx context.Context, page, limit int) ([]dto.ProvinceResponseDTO, int, error) { +func (s *wilayahIndonesiaService) GetAllProvinces(ctx context.Context, page, limit int) ([]ProvinceResponseDTO, int, error) { cacheKey := fmt.Sprintf("provinces_page:%d_limit:%d", page, limit) var cachedResponse struct { - Data []dto.ProvinceResponseDTO `json:"data"` + Data []ProvinceResponseDTO `json:"data"` Total int `json:"total"` } @@ -139,16 +138,16 @@ func (s *wilayahIndonesiaService) GetAllProvinces(ctx context.Context, page, lim return nil, 0, fmt.Errorf("failed to fetch provinces: %w", err) } - provinceDTOs := make([]dto.ProvinceResponseDTO, len(provinces)) + provinceDTOs := make([]ProvinceResponseDTO, len(provinces)) for i, province := range provinces { - provinceDTOs[i] = dto.ProvinceResponseDTO{ + provinceDTOs[i] = ProvinceResponseDTO{ ID: province.ID, Name: province.Name, } } cacheData := struct { - Data []dto.ProvinceResponseDTO `json:"data"` + Data []ProvinceResponseDTO `json:"data"` Total int `json:"total"` }{ Data: provinceDTOs, @@ -162,11 +161,11 @@ func (s *wilayahIndonesiaService) GetAllProvinces(ctx context.Context, page, lim return provinceDTOs, total, nil } -func (s *wilayahIndonesiaService) GetProvinceByID(ctx context.Context, id string, page, limit int) (*dto.ProvinceResponseDTO, int, error) { +func (s *wilayahIndonesiaService) GetProvinceByID(ctx context.Context, id string, page, limit int) (*ProvinceResponseDTO, int, error) { cacheKey := fmt.Sprintf("province:%s_page:%d_limit:%d", id, page, limit) var cachedResponse struct { - Data dto.ProvinceResponseDTO `json:"data"` + Data ProvinceResponseDTO `json:"data"` TotalRegencies int `json:"total_regencies"` } @@ -179,14 +178,14 @@ func (s *wilayahIndonesiaService) GetProvinceByID(ctx context.Context, id string return nil, 0, err } - provinceDTO := dto.ProvinceResponseDTO{ + provinceDTO := ProvinceResponseDTO{ ID: province.ID, Name: province.Name, } - regencyDTOs := make([]dto.RegencyResponseDTO, len(province.Regencies)) + regencyDTOs := make([]RegencyResponseDTO, len(province.Regencies)) for i, regency := range province.Regencies { - regencyDTOs[i] = dto.RegencyResponseDTO{ + regencyDTOs[i] = RegencyResponseDTO{ ID: regency.ID, ProvinceID: regency.ProvinceID, Name: regency.Name, @@ -195,7 +194,7 @@ func (s *wilayahIndonesiaService) GetProvinceByID(ctx context.Context, id string provinceDTO.Regencies = regencyDTOs cacheData := struct { - Data dto.ProvinceResponseDTO `json:"data"` + Data ProvinceResponseDTO `json:"data"` TotalRegencies int `json:"total_regencies"` }{ Data: provinceDTO, @@ -209,11 +208,11 @@ func (s *wilayahIndonesiaService) GetProvinceByID(ctx context.Context, id string return &provinceDTO, totalRegencies, nil } -func (s *wilayahIndonesiaService) GetAllRegencies(ctx context.Context, page, limit int) ([]dto.RegencyResponseDTO, int, error) { +func (s *wilayahIndonesiaService) GetAllRegencies(ctx context.Context, page, limit int) ([]RegencyResponseDTO, int, error) { cacheKey := fmt.Sprintf("regencies_page:%d_limit:%d", page, limit) var cachedResponse struct { - Data []dto.RegencyResponseDTO `json:"data"` + Data []RegencyResponseDTO `json:"data"` Total int `json:"total"` } @@ -226,9 +225,9 @@ func (s *wilayahIndonesiaService) GetAllRegencies(ctx context.Context, page, lim return nil, 0, fmt.Errorf("failed to fetch regencies: %w", err) } - regencyDTOs := make([]dto.RegencyResponseDTO, len(regencies)) + regencyDTOs := make([]RegencyResponseDTO, len(regencies)) for i, regency := range regencies { - regencyDTOs[i] = dto.RegencyResponseDTO{ + regencyDTOs[i] = RegencyResponseDTO{ ID: regency.ID, ProvinceID: regency.ProvinceID, Name: regency.Name, @@ -236,7 +235,7 @@ func (s *wilayahIndonesiaService) GetAllRegencies(ctx context.Context, page, lim } cacheData := struct { - Data []dto.RegencyResponseDTO `json:"data"` + Data []RegencyResponseDTO `json:"data"` Total int `json:"total"` }{ Data: regencyDTOs, @@ -250,11 +249,11 @@ func (s *wilayahIndonesiaService) GetAllRegencies(ctx context.Context, page, lim return regencyDTOs, total, nil } -func (s *wilayahIndonesiaService) GetRegencyByID(ctx context.Context, id string, page, limit int) (*dto.RegencyResponseDTO, int, error) { +func (s *wilayahIndonesiaService) GetRegencyByID(ctx context.Context, id string, page, limit int) (*RegencyResponseDTO, int, error) { cacheKey := fmt.Sprintf("regency:%s_page:%d_limit:%d", id, page, limit) var cachedResponse struct { - Data dto.RegencyResponseDTO `json:"data"` + Data RegencyResponseDTO `json:"data"` TotalDistricts int `json:"total_districts"` } @@ -267,15 +266,15 @@ func (s *wilayahIndonesiaService) GetRegencyByID(ctx context.Context, id string, return nil, 0, err } - regencyDTO := dto.RegencyResponseDTO{ + regencyDTO := RegencyResponseDTO{ ID: regency.ID, ProvinceID: regency.ProvinceID, Name: regency.Name, } - districtDTOs := make([]dto.DistrictResponseDTO, len(regency.Districts)) + districtDTOs := make([]DistrictResponseDTO, len(regency.Districts)) for i, district := range regency.Districts { - districtDTOs[i] = dto.DistrictResponseDTO{ + districtDTOs[i] = DistrictResponseDTO{ ID: district.ID, RegencyID: district.RegencyID, Name: district.Name, @@ -284,7 +283,7 @@ func (s *wilayahIndonesiaService) GetRegencyByID(ctx context.Context, id string, regencyDTO.Districts = districtDTOs cacheData := struct { - Data dto.RegencyResponseDTO `json:"data"` + Data RegencyResponseDTO `json:"data"` TotalDistricts int `json:"total_districts"` }{ Data: regencyDTO, @@ -298,11 +297,11 @@ func (s *wilayahIndonesiaService) GetRegencyByID(ctx context.Context, id string, return ®encyDTO, totalDistricts, nil } -func (s *wilayahIndonesiaService) GetAllDistricts(ctx context.Context, page, limit int) ([]dto.DistrictResponseDTO, int, error) { +func (s *wilayahIndonesiaService) GetAllDistricts(ctx context.Context, page, limit int) ([]DistrictResponseDTO, int, error) { cacheKey := fmt.Sprintf("districts_page:%d_limit:%d", page, limit) var cachedResponse struct { - Data []dto.DistrictResponseDTO `json:"data"` + Data []DistrictResponseDTO `json:"data"` Total int `json:"total"` } @@ -315,9 +314,9 @@ func (s *wilayahIndonesiaService) GetAllDistricts(ctx context.Context, page, lim return nil, 0, fmt.Errorf("failed to fetch districts: %w", err) } - districtDTOs := make([]dto.DistrictResponseDTO, len(districts)) + districtDTOs := make([]DistrictResponseDTO, len(districts)) for i, district := range districts { - districtDTOs[i] = dto.DistrictResponseDTO{ + districtDTOs[i] = DistrictResponseDTO{ ID: district.ID, RegencyID: district.RegencyID, Name: district.Name, @@ -325,7 +324,7 @@ func (s *wilayahIndonesiaService) GetAllDistricts(ctx context.Context, page, lim } cacheData := struct { - Data []dto.DistrictResponseDTO `json:"data"` + Data []DistrictResponseDTO `json:"data"` Total int `json:"total"` }{ Data: districtDTOs, @@ -339,11 +338,11 @@ func (s *wilayahIndonesiaService) GetAllDistricts(ctx context.Context, page, lim return districtDTOs, total, nil } -func (s *wilayahIndonesiaService) GetDistrictByID(ctx context.Context, id string, page, limit int) (*dto.DistrictResponseDTO, int, error) { +func (s *wilayahIndonesiaService) GetDistrictByID(ctx context.Context, id string, page, limit int) (*DistrictResponseDTO, int, error) { cacheKey := fmt.Sprintf("district:%s_page:%d_limit:%d", id, page, limit) var cachedResponse struct { - Data dto.DistrictResponseDTO `json:"data"` + Data DistrictResponseDTO `json:"data"` TotalVillages int `json:"total_villages"` } @@ -356,15 +355,15 @@ func (s *wilayahIndonesiaService) GetDistrictByID(ctx context.Context, id string return nil, 0, err } - districtDTO := dto.DistrictResponseDTO{ + districtDTO := DistrictResponseDTO{ ID: district.ID, RegencyID: district.RegencyID, Name: district.Name, } - villageDTOs := make([]dto.VillageResponseDTO, len(district.Villages)) + villageDTOs := make([]VillageResponseDTO, len(district.Villages)) for i, village := range district.Villages { - villageDTOs[i] = dto.VillageResponseDTO{ + villageDTOs[i] = VillageResponseDTO{ ID: village.ID, DistrictID: village.DistrictID, Name: village.Name, @@ -373,7 +372,7 @@ func (s *wilayahIndonesiaService) GetDistrictByID(ctx context.Context, id string districtDTO.Villages = villageDTOs cacheData := struct { - Data dto.DistrictResponseDTO `json:"data"` + Data DistrictResponseDTO `json:"data"` TotalVillages int `json:"total_villages"` }{ Data: districtDTO, @@ -387,11 +386,11 @@ func (s *wilayahIndonesiaService) GetDistrictByID(ctx context.Context, id string return &districtDTO, totalVillages, nil } -func (s *wilayahIndonesiaService) GetAllVillages(ctx context.Context, page, limit int) ([]dto.VillageResponseDTO, int, error) { +func (s *wilayahIndonesiaService) GetAllVillages(ctx context.Context, page, limit int) ([]VillageResponseDTO, int, error) { cacheKey := fmt.Sprintf("villages_page:%d_limit:%d", page, limit) var cachedResponse struct { - Data []dto.VillageResponseDTO `json:"data"` + Data []VillageResponseDTO `json:"data"` Total int `json:"total"` } @@ -404,9 +403,9 @@ func (s *wilayahIndonesiaService) GetAllVillages(ctx context.Context, page, limi return nil, 0, fmt.Errorf("failed to fetch villages: %w", err) } - villageDTOs := make([]dto.VillageResponseDTO, len(villages)) + villageDTOs := make([]VillageResponseDTO, len(villages)) for i, village := range villages { - villageDTOs[i] = dto.VillageResponseDTO{ + villageDTOs[i] = VillageResponseDTO{ ID: village.ID, DistrictID: village.DistrictID, Name: village.Name, @@ -414,7 +413,7 @@ func (s *wilayahIndonesiaService) GetAllVillages(ctx context.Context, page, limi } cacheData := struct { - Data []dto.VillageResponseDTO `json:"data"` + Data []VillageResponseDTO `json:"data"` Total int `json:"total"` }{ Data: villageDTOs, @@ -428,10 +427,10 @@ func (s *wilayahIndonesiaService) GetAllVillages(ctx context.Context, page, limi return villageDTOs, total, nil } -func (s *wilayahIndonesiaService) GetVillageByID(ctx context.Context, id string) (*dto.VillageResponseDTO, error) { +func (s *wilayahIndonesiaService) GetVillageByID(ctx context.Context, id string) (*VillageResponseDTO, error) { cacheKey := fmt.Sprintf("village:%s", id) - var cachedResponse dto.VillageResponseDTO + var cachedResponse VillageResponseDTO if err := utils.GetCache(cacheKey, &cachedResponse); err == nil { return &cachedResponse, nil } @@ -441,7 +440,7 @@ func (s *wilayahIndonesiaService) GetVillageByID(ctx context.Context, id string) return nil, fmt.Errorf("village not found: %w", err) } - villageResponse := &dto.VillageResponseDTO{ + villageResponse := &VillageResponseDTO{ ID: village.ID, DistrictID: village.DistrictID, Name: village.Name, diff --git a/model/identitycard_model.go b/model/identitycard_model.go index c35cfc8..088e28f 100644 --- a/model/identitycard_model.go +++ b/model/identitycard_model.go @@ -7,6 +7,7 @@ type IdentityCard struct { UserID string `gorm:"not null" json:"userId"` User User `gorm:"foreignKey:UserID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"user"` Identificationumber string `gorm:"not null" json:"identificationumber"` + Fullname string `gorm:"not null" json:"fullname"` Placeofbirth string `gorm:"not null" json:"placeofbirth"` Dateofbirth string `gorm:"not null" json:"dateofbirth"` Gender string `gorm:"not null" json:"gender"` diff --git a/public/document/districts.csv b/public/document/districts.csv old mode 100644 new mode 100755 diff --git a/public/document/provinces.csv b/public/document/provinces.csv old mode 100644 new mode 100755 diff --git a/public/document/regencies.csv b/public/document/regencies.csv old mode 100644 new mode 100755 diff --git a/public/document/villages.csv b/public/document/villages.csv old mode 100644 new mode 100755 diff --git a/router/setup_routes.go.go b/router/setup_routes.go.go index 5d37e8a..d653c9c 100644 --- a/router/setup_routes.go.go +++ b/router/setup_routes.go.go @@ -9,7 +9,9 @@ import ( "rijig/internal/identitycart" "rijig/internal/role" "rijig/internal/userpin" + "rijig/internal/userprofile" "rijig/internal/whatsapp" + "rijig/internal/wilayahindo" "rijig/middleware" // "rijig/presentation" @@ -19,8 +21,9 @@ import ( func SetupRoutes(app *fiber.App) { apa := app.Group(os.Getenv("BASE_URL")) - whatsapp.WhatsAppRouter(apa) apa.Static("/uploads", "./public"+os.Getenv("BASE_URL")+"/uploads") + a := app.Group(os.Getenv("BASE_URL")) + whatsapp.WhatsAppRouter(a) api := app.Group(os.Getenv("BASE_URL")) api.Use(middleware.APIKeyMiddleware) @@ -32,6 +35,8 @@ func SetupRoutes(app *fiber.App) { role.UserRoleRouter(api) article.ArticleRouter(api) + userprofile.UserProfileRouter(api) + wilayahindo.WilayahRouter(api) // || auth router || // // presentation.AuthRouter(api) @@ -58,5 +63,4 @@ func SetupRoutes(app *fiber.App) { // // presentation.AboutRouter(api) // presentation.TrashRouter(api) // presentation.CoverageAreaRouter(api) - whatsapp.WhatsAppRouter(api) }