lifedebugger commited on
Commit
f3f5b2d
·
1 Parent(s): 1cea019

Deploy files from GitHub repository

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. main.go +0 -6
  2. models/database_orm_model.go +8 -6
  3. models/request_model.go +8 -6
  4. router/router.go +0 -1
  5. router/server.go +0 -4
  6. services/partner_criteria_service.go +4 -2
  7. space/pkg/validation/validation.go +16 -155
  8. space/response/validation.go +19 -2
  9. space/services/marriage_readiness_profile_service.go +4 -3
  10. space/services/partner_criteria_service.go +4 -3
  11. space/space/controller/partner_criteria/partner_criteria_controller.go +66 -0
  12. space/space/main.go +9 -3
  13. space/space/models/database_orm_model.go +54 -20
  14. space/space/models/request_model.go +112 -77
  15. space/space/pkg/validation/custom_rules.go +236 -116
  16. space/space/repositories/partner_criteria_repository.go +38 -0
  17. space/space/router/partner_criteria_route.go +11 -0
  18. space/space/router/router.go +1 -0
  19. space/space/router/server.go +4 -0
  20. space/space/services/cv_service.go +16 -13
  21. space/space/services/partner_criteria_service.go +77 -0
  22. space/space/space/config/database_connection_config.go +13 -2
  23. space/space/space/models/sequence.go +25 -0
  24. space/space/space/repositories/account_repository.go +18 -0
  25. space/space/space/repositories/cv_repository.go +162 -146
  26. space/space/space/services/cv_service.go +33 -7
  27. space/space/space/services/user_profile_service.go +36 -8
  28. space/space/space/space/models/database_orm_model.go +50 -50
  29. space/space/space/space/models/field_counter.go +4 -19
  30. space/space/space/space/space/config/database_connection_config.go +1 -0
  31. space/space/space/space/space/controller/marriage_readiness_profile/marriage_readiness_profile_controller.go +66 -0
  32. space/space/space/space/space/main.go +12 -5
  33. space/space/space/space/space/repositories/marriage_readiness_profile_repository.go +38 -0
  34. space/space/space/space/space/router/marriage_readiness_profile_route.go +11 -0
  35. space/space/space/space/space/services/marriage_readiness_profile_service.go +90 -0
  36. space/space/space/space/space/space/controller/cv/cv_controller.go +171 -47
  37. space/space/space/space/space/space/models/database_orm_model.go +95 -43
  38. space/space/space/space/space/space/models/request_model.go +199 -45
  39. space/space/space/space/space/space/pkg/validation/validation.go +10 -1
  40. space/space/space/space/space/space/pkg/worker/task_send_forgot_password_email.go +8 -6
  41. space/space/space/space/space/space/pkg/worker/task_send_verify_email.go +8 -5
  42. space/space/space/space/space/space/router/cv_route.go +1 -1
  43. space/space/space/space/space/space/router/router.go +1 -0
  44. space/space/space/space/space/space/router/server.go +11 -7
  45. space/space/space/space/space/space/services/cv_service.go +188 -181
  46. space/space/space/space/space/space/space/services/academy_quiz_answer_service.go +3 -0
  47. space/space/space/space/space/space/space/services/academy_quiz_question_service.go +6 -0
  48. space/space/space/space/space/space/space/space/controller/quiz/result_quiz_controller.go +24 -0
  49. space/space/space/space/space/space/space/space/controller/quiz/submit_quiz_controller.go +4 -3
  50. space/space/space/space/space/space/space/space/models/database_orm_model.go +4 -3
main.go CHANGED
@@ -7,7 +7,6 @@ import (
7
 
8
  "api.qobiltu.id/config"
9
  cv_controller "api.qobiltu.id/controller/cv"
10
- health_check_controller "api.qobiltu.id/controller/health_check"
11
  marriage_readiness_profile_controller "api.qobiltu.id/controller/marriage_readiness_profile"
12
  partner_criteria_controller "api.qobiltu.id/controller/partner_criteria"
13
  "api.qobiltu.id/mail"
@@ -55,10 +54,6 @@ func main() {
55
  worker.AsyncTaskDistributor = taskDistributor
56
 
57
  // setup repo, service, and controller
58
- healthCheckRepository := repositories.NewHealthCheckRepository(config.DB)
59
- healthCheckService := services.NewHealthCheckService(healthCheckRepository)
60
- healthCheckController := health_check_controller.NewHealthCheckController(healthCheckService)
61
-
62
  cvRepository := repositories.NewCVRepository(config.DB)
63
  cvService := services.NewCVService(cvRepository, localStorage, validator)
64
  cvController := cv_controller.NewCVController(cvService)
@@ -78,7 +73,6 @@ func main() {
78
 
79
  // create server
80
  s, err := router.NewServer(
81
- healthCheckController,
82
  cvController,
83
  marriageReadinessProfileController,
84
  partnerCriteriaController,
 
7
 
8
  "api.qobiltu.id/config"
9
  cv_controller "api.qobiltu.id/controller/cv"
 
10
  marriage_readiness_profile_controller "api.qobiltu.id/controller/marriage_readiness_profile"
11
  partner_criteria_controller "api.qobiltu.id/controller/partner_criteria"
12
  "api.qobiltu.id/mail"
 
54
  worker.AsyncTaskDistributor = taskDistributor
55
 
56
  // setup repo, service, and controller
 
 
 
 
57
  cvRepository := repositories.NewCVRepository(config.DB)
58
  cvService := services.NewCVService(cvRepository, localStorage, validator)
59
  cvController := cv_controller.NewCVController(cvService)
 
73
 
74
  // create server
75
  s, err := router.NewServer(
 
76
  cvController,
77
  marriageReadinessProfileController,
78
  partnerCriteriaController,
models/database_orm_model.go CHANGED
@@ -396,7 +396,8 @@ type (
396
  Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"`
397
 
398
  // Kriteria Umum
399
- ExpectedAgeLimit *int `gorm:"column:expected_age_limit" json:"expected_age_limit"` // batas usia pasangan yang diharapkan
 
400
  ExpectedDomicile *string `gorm:"column:expected_domicile" json:"expected_domicile"` // domisili pasangan yang diharapkan
401
  AcceptedMaritalStatus *pq.StringArray `gorm:"column:accepted_marital_status;type:varchar(255)[]" json:"accepted_marital_status"` // status pernikahan yang diterima
402
  PartnerFamilyReligion *string `gorm:"column:partner_family_religion" json:"partner_family_religion"` // agama yang dianut keluarga pasangan
@@ -405,11 +406,12 @@ type (
405
  PartnerCanCook *string `gorm:"column:partner_can_cook" json:"partner_can_cook"` // apakah pasangan diharapkan bisa memasak
406
 
407
  // Kriteria Fisik
408
- ExpectedBodyShapes *pq.StringArray `gorm:"column:expected_body_shapes;type:varchar(255)[]" json:"expected_body_shapes"` // bentuk tubuh yang diharapkan
409
- ExpectedSkinColors *pq.StringArray `gorm:"column:expected_skin_colors;type:varchar(255)[]" json:"expected_skin_colors"` // warna kulit yang diharapkan
410
- ExpectedHairTypes *pq.StringArray `gorm:"column:expected_hair_types;type:varchar(255)[]" json:"expected_hair_types"` // tipe rambut yang diharapkan
411
- ExpectedHairThickness *pq.StringArray `gorm:"column:expected_hair_thickness;type:varchar(255)[]" json:"expected_hair_thickness"` // jenis rambut yang diharapkan
412
- ExpectedHeight *int `gorm:"column:expected_height" json:"expected_height"` // tinggi badan yang diharapkan
 
413
 
414
  // Pendidikan & Pekerjaan
415
  ExpectedPartnerIncome *string `gorm:"column:expected_partner_income" json:"expected_partner_income"` // penghasilan pasangan per bulan yang diharapkan
 
396
  Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"`
397
 
398
  // Kriteria Umum
399
+ ExpecteMinAgeLimit *int `gorm:"column:expected_min_age_limit" json:"expected_min_age_limit"` // batas usia pasangan yang diharapkan
400
+ ExpecteMaxAgeLimit *int `gorm:"column:expected_max_age_limit" json:"expected_max_age_limit"` // batas usia pasangan yang diharapkan
401
  ExpectedDomicile *string `gorm:"column:expected_domicile" json:"expected_domicile"` // domisili pasangan yang diharapkan
402
  AcceptedMaritalStatus *pq.StringArray `gorm:"column:accepted_marital_status;type:varchar(255)[]" json:"accepted_marital_status"` // status pernikahan yang diterima
403
  PartnerFamilyReligion *string `gorm:"column:partner_family_religion" json:"partner_family_religion"` // agama yang dianut keluarga pasangan
 
406
  PartnerCanCook *string `gorm:"column:partner_can_cook" json:"partner_can_cook"` // apakah pasangan diharapkan bisa memasak
407
 
408
  // Kriteria Fisik
409
+ ExpectedBodyShapes *pq.StringArray `gorm:"column:expected_body_shapes;type:varchar(255)[]" json:"expected_body_shapes"` // bentuk tubuh yang diharapkan
410
+ ExpectedSkinColors *pq.StringArray `gorm:"column:expected_skin_colors;type:varchar(255)[]" json:"expected_skin_colors"` // warna kulit yang diharapkan
411
+ ExpectedHairTypes *pq.StringArray `gorm:"column:expected_hair_types;type:varchar(255)[]" json:"expected_hair_types"` // tipe rambut yang diharapkan
412
+ ExpectedHairThickness *pq.StringArray `gorm:"column:expected_hair_thickness;type:varchar(255)[]" json:"expected_hair_thickness"` // jenis rambut yang diharapkan
413
+ ExpectedMinHeightLimit *int `gorm:"column:expected_min_height_limit" json:"expected_min_height_limit"` // tinggi badan yang diharapkan
414
+ ExpectedMaxHeightLimit *int `gorm:"column:expected_max_height_limit" json:"expected_max_height_limit"` // tinggi badan yang diharapkan
415
 
416
  // Pendidikan & Pekerjaan
417
  ExpectedPartnerIncome *string `gorm:"column:expected_partner_income" json:"expected_partner_income"` // penghasilan pasangan per bulan yang diharapkan
models/request_model.go CHANGED
@@ -336,7 +336,8 @@ type (
336
  SavePartnerCriteriaRequest struct {
337
  AccountID int64 `json:"account_id"`
338
 
339
- ExpectedAgeLimit *int `gorm:"column:expected_age_limit" json:"expected_age_limit"` // batas usia pasangan yang diharapkan
 
340
  ExpectedDomicile *string `gorm:"column:expected_domicile" json:"expected_domicile"` // domisili pasangan yang diharapkan
341
  AcceptedMaritalStatus *pq.StringArray `gorm:"column:accepted_marital_status;type:varchar(255)[]" json:"accepted_marital_status"` // status pernikahan yang diterima
342
  PartnerFamilyReligion *string `gorm:"column:partner_family_religion" json:"partner_family_religion"` // agama yang dianut keluarga pasangan
@@ -345,11 +346,12 @@ type (
345
  PartnerCanCook *string `gorm:"column:partner_can_cook" json:"partner_can_cook"` // apakah pasangan diharapkan bisa memasak
346
 
347
  // Kriteria Fisik
348
- ExpectedBodyShapes *pq.StringArray `gorm:"column:expected_body_shapes;type:varchar(255)[]" json:"expected_body_shapes"` // bentuk tubuh yang diharapkan
349
- ExpectedSkinColors *pq.StringArray `gorm:"column:expected_skin_colors;type:varchar(255)[]" json:"expected_skin_colors"` // warna kulit yang diharapkan
350
- ExpectedHairTypes *pq.StringArray `gorm:"column:expected_hair_types;type:varchar(255)[]" json:"expected_hair_types"` // tipe rambut yang diharapkan
351
- ExpectedHairThickness *pq.StringArray `gorm:"column:expected_hair_thickness;type:varchar(255)[]" json:"expected_hair_thickness"` // jenis rambut yang diharapkan
352
- ExpectedHeight *int `gorm:"column:expected_height" json:"expected_height"` // tinggi badan yang diharapkan
 
353
 
354
  // Pendidikan & Pekerjaan
355
  ExpectedPartnerIncome *string `gorm:"column:expected_partner_income" json:"expected_partner_income"` // penghasilan pasangan per bulan yang diharapkan
 
336
  SavePartnerCriteriaRequest struct {
337
  AccountID int64 `json:"account_id"`
338
 
339
+ ExpecteMinAgeLimit *int `gorm:"column:expected_min_age_limit" json:"expected_min_age_limit"` // batas usia pasangan yang diharapkan
340
+ ExpecteMaxAgeLimit *int `gorm:"column:expected_max_age_limit" json:"expected_max_age_limit"` // batas usia pasangan yang diharapkan
341
  ExpectedDomicile *string `gorm:"column:expected_domicile" json:"expected_domicile"` // domisili pasangan yang diharapkan
342
  AcceptedMaritalStatus *pq.StringArray `gorm:"column:accepted_marital_status;type:varchar(255)[]" json:"accepted_marital_status"` // status pernikahan yang diterima
343
  PartnerFamilyReligion *string `gorm:"column:partner_family_religion" json:"partner_family_religion"` // agama yang dianut keluarga pasangan
 
346
  PartnerCanCook *string `gorm:"column:partner_can_cook" json:"partner_can_cook"` // apakah pasangan diharapkan bisa memasak
347
 
348
  // Kriteria Fisik
349
+ ExpectedBodyShapes *pq.StringArray `gorm:"column:expected_body_shapes;type:varchar(255)[]" json:"expected_body_shapes"` // bentuk tubuh yang diharapkan
350
+ ExpectedSkinColors *pq.StringArray `gorm:"column:expected_skin_colors;type:varchar(255)[]" json:"expected_skin_colors"` // warna kulit yang diharapkan
351
+ ExpectedHairTypes *pq.StringArray `gorm:"column:expected_hair_types;type:varchar(255)[]" json:"expected_hair_types"` // tipe rambut yang diharapkan
352
+ ExpectedHairThickness *pq.StringArray `gorm:"column:expected_hair_thickness;type:varchar(255)[]" json:"expected_hair_thickness"` // jenis rambut yang diharapkan
353
+ ExpectedMinHeightLimit *int `gorm:"column:expected_min_height_limit" json:"expected_min_height_limit"` // tinggi badan yang diharapkan
354
+ ExpectedMaxHeightLimit *int `gorm:"column:expected_max_height_limit" json:"expected_max_height_limit"` // tinggi badan yang diharapkan
355
 
356
  // Pendidikan & Pekerjaan
357
  ExpectedPartnerIncome *string `gorm:"column:expected_partner_income" json:"expected_partner_income"` // penghasilan pasangan per bulan yang diharapkan
router/router.go CHANGED
@@ -16,7 +16,6 @@ func (s *Server) setupRoutes() {
16
  QuizRoute(s.router)
17
 
18
  // another way to register routes
19
- s.HealthCheckRoute()
20
  s.CVRoute()
21
  s.MarriageReadinessProfileRoute()
22
  s.PartnerCriteriaRoute()
 
16
  QuizRoute(s.router)
17
 
18
  // another way to register routes
 
19
  s.CVRoute()
20
  s.MarriageReadinessProfileRoute()
21
  s.PartnerCriteriaRoute()
router/server.go CHANGED
@@ -2,7 +2,6 @@ package router
2
 
3
  import (
4
  cv_controller "api.qobiltu.id/controller/cv"
5
- health_check_controller "api.qobiltu.id/controller/health_check"
6
  marriage_readiness_profile_controller "api.qobiltu.id/controller/marriage_readiness_profile"
7
  partner_criteria_controller "api.qobiltu.id/controller/partner_criteria"
8
  "github.com/gin-gonic/gin"
@@ -10,14 +9,12 @@ import (
10
 
11
  type Server struct {
12
  router *gin.Engine
13
- healthCheckController health_check_controller.HealthCheckController
14
  cvController cv_controller.CVController
15
  marriageReadinessProfileController marriage_readiness_profile_controller.MarriageReadinessProfileController
16
  partnerCriteriaController partner_criteria_controller.PartnerCriteriaController
17
  }
18
 
19
  func NewServer(
20
- healthCheckController health_check_controller.HealthCheckController,
21
  cvController cv_controller.CVController,
22
  marriageReadinessProfileController marriage_readiness_profile_controller.MarriageReadinessProfileController,
23
  partnerCriteriaController partner_criteria_controller.PartnerCriteriaController,
@@ -27,7 +24,6 @@ func NewServer(
27
  router.Use(gin.Recovery())
28
 
29
  server := &Server{
30
- healthCheckController: healthCheckController,
31
  cvController: cvController,
32
  marriageReadinessProfileController: marriageReadinessProfileController,
33
  partnerCriteriaController: partnerCriteriaController,
 
2
 
3
  import (
4
  cv_controller "api.qobiltu.id/controller/cv"
 
5
  marriage_readiness_profile_controller "api.qobiltu.id/controller/marriage_readiness_profile"
6
  partner_criteria_controller "api.qobiltu.id/controller/partner_criteria"
7
  "github.com/gin-gonic/gin"
 
9
 
10
  type Server struct {
11
  router *gin.Engine
 
12
  cvController cv_controller.CVController
13
  marriageReadinessProfileController marriage_readiness_profile_controller.MarriageReadinessProfileController
14
  partnerCriteriaController partner_criteria_controller.PartnerCriteriaController
15
  }
16
 
17
  func NewServer(
 
18
  cvController cv_controller.CVController,
19
  marriageReadinessProfileController marriage_readiness_profile_controller.MarriageReadinessProfileController,
20
  partnerCriteriaController partner_criteria_controller.PartnerCriteriaController,
 
24
  router.Use(gin.Recovery())
25
 
26
  server := &Server{
 
27
  cvController: cvController,
28
  marriageReadinessProfileController: marriageReadinessProfileController,
29
  partnerCriteriaController: partnerCriteriaController,
services/partner_criteria_service.go CHANGED
@@ -42,7 +42,8 @@ func (s *partnerCriteriaService) SavePartnerCriteria(ctx context.Context, req *m
42
 
43
  partnerCriteria.AccountID = req.AccountID
44
 
45
- utils.AssignIfNotNil(&partnerCriteria.ExpectedAgeLimit, req.ExpectedAgeLimit)
 
46
  utils.AssignIfNotNil(&partnerCriteria.ExpectedDomicile, req.ExpectedDomicile)
47
  utils.AssignIfNotNil(&partnerCriteria.AcceptedMaritalStatus, req.AcceptedMaritalStatus)
48
  utils.AssignIfNotNil(&partnerCriteria.PartnerFamilyReligion, req.PartnerFamilyReligion)
@@ -54,7 +55,8 @@ func (s *partnerCriteriaService) SavePartnerCriteria(ctx context.Context, req *m
54
  utils.AssignIfNotNil(&partnerCriteria.ExpectedSkinColors, req.ExpectedSkinColors)
55
  utils.AssignIfNotNil(&partnerCriteria.ExpectedHairTypes, req.ExpectedHairTypes)
56
  utils.AssignIfNotNil(&partnerCriteria.ExpectedHairThickness, req.ExpectedHairThickness)
57
- utils.AssignIfNotNil(&partnerCriteria.ExpectedHeight, req.ExpectedHeight)
 
58
 
59
  utils.AssignIfNotNil(&partnerCriteria.ExpectedPartnerIncome, req.ExpectedPartnerIncome)
60
  utils.AssignIfNotNil(&partnerCriteria.ExpectedIncomeSources, req.ExpectedIncomeSources)
 
42
 
43
  partnerCriteria.AccountID = req.AccountID
44
 
45
+ utils.AssignIfNotNil(&partnerCriteria.ExpecteMinAgeLimit, req.ExpecteMinAgeLimit)
46
+ utils.AssignIfNotNil(&partnerCriteria.ExpecteMaxAgeLimit, req.ExpecteMaxAgeLimit)
47
  utils.AssignIfNotNil(&partnerCriteria.ExpectedDomicile, req.ExpectedDomicile)
48
  utils.AssignIfNotNil(&partnerCriteria.AcceptedMaritalStatus, req.AcceptedMaritalStatus)
49
  utils.AssignIfNotNil(&partnerCriteria.PartnerFamilyReligion, req.PartnerFamilyReligion)
 
55
  utils.AssignIfNotNil(&partnerCriteria.ExpectedSkinColors, req.ExpectedSkinColors)
56
  utils.AssignIfNotNil(&partnerCriteria.ExpectedHairTypes, req.ExpectedHairTypes)
57
  utils.AssignIfNotNil(&partnerCriteria.ExpectedHairThickness, req.ExpectedHairThickness)
58
+ utils.AssignIfNotNil(&partnerCriteria.ExpectedMinHeightLimit, req.ExpectedMinHeightLimit)
59
+ utils.AssignIfNotNil(&partnerCriteria.ExpectedMaxHeightLimit, req.ExpectedMaxHeightLimit)
60
 
61
  utils.AssignIfNotNil(&partnerCriteria.ExpectedPartnerIncome, req.ExpectedPartnerIncome)
62
  utils.AssignIfNotNil(&partnerCriteria.ExpectedIncomeSources, req.ExpectedIncomeSources)
space/pkg/validation/validation.go CHANGED
@@ -1,174 +1,35 @@
1
  package validation
2
 
3
  import (
4
- "errors"
5
- "fmt"
6
- "reflect"
7
- "strings"
8
 
9
- "github.com/go-playground/locales/en"
10
- "github.com/go-playground/locales/id"
11
- ut "github.com/go-playground/universal-translator"
12
- v10 "github.com/go-playground/validator/v10"
13
- entranslations "github.com/go-playground/validator/v10/translations/en"
14
- idtranslations "github.com/go-playground/validator/v10/translations/id"
15
  )
16
 
17
- // Constants for supported locales
18
- const (
19
- LocaleID = "id"
20
- LocaleEN = "en"
21
- )
22
-
23
- // ErrorMessage represents a validation error message
24
  type ErrorMessage struct {
25
  Field string `json:"field"`
26
  Message string `json:"-"`
27
  }
28
 
29
- type validator struct {
30
- validate *v10.Validate
31
- translator ut.Translator
32
- }
33
-
34
- // validatorInstance adalah instance global dari validator.
35
- var validatorInstance *validator
36
-
37
- // New creates a new validation instance with the specified locale
38
- // dan menginisialisasi instance global validatorInstance.
39
- func New(locale string) error {
40
- v := &validator{}
41
- parsedLocale := parseLocale(locale)
42
-
43
- uni := ut.New(en.New(), id.New(), en.New())
44
- translator, found := uni.GetTranslator(parsedLocale)
45
- if !found {
46
- return fmt.Errorf("translator not found for locale: %s", parsedLocale)
47
- }
48
-
49
- validate := v10.New()
50
-
51
- if err := setupValidations(validate); err != nil {
52
- return fmt.Errorf("failed to setup validations: %w", err)
53
- }
54
-
55
- if err := setupTranslations(validate, translator, parsedLocale); err != nil {
56
- return fmt.Errorf("failed to setup translations for locale %s: %w", parsedLocale, err)
57
- }
58
-
59
- v.validate = validate
60
- v.translator = translator
61
-
62
- validatorInstance = v // Inisialisasi instance global
63
- return nil
64
- }
65
-
66
- func parseLocale(locale string) string {
67
- switch strings.ToLower(locale) {
68
- case "id":
69
- return LocaleID
70
- case "en":
71
- return LocaleEN
72
- default:
73
- return LocaleID // Default to Indonesian
74
- }
75
- }
76
-
77
- // setupValidations configures custom validation rules.
78
- func setupValidations(validate *v10.Validate) error {
79
- rules := NewValidatorRules(&InMemoryOptionSource{})
80
- if err := rules.RegisterAllCustomRules(validate); err != nil {
81
- return err
82
- }
83
-
84
- return nil
85
- }
86
 
87
- // setupTranslations configures translations for validation messages.
88
- func setupTranslations(validate *v10.Validate, translator ut.Translator, locale string) error {
89
- // Register default translations based on locale
90
- if err := registerDefaultTranslations(validate, translator, locale); err != nil {
91
- return fmt.Errorf("failed to register default translations for locale %s: %w", locale, err)
92
- }
93
-
94
- // Register custom password validation translation
95
- err := validate.RegisterTranslation("password", translator,
96
- func(ut ut.Translator) error {
97
- return ut.Add("password", "harus mengandung minimal 8 karakter, huruf besar, huruf kecil, dan angka.", true)
98
- },
99
- func(ut ut.Translator, fe v10.FieldError) string {
100
- translated, err := ut.T(fe.Tag(), fe.Field())
101
- if err != nil {
102
- return fe.Field() + " is invalid"
103
- }
104
- return translated
105
- },
106
- )
107
  if err != nil {
108
- return fmt.Errorf("failed to register password translation: %w", err)
109
- }
110
-
111
- return nil
112
- }
113
-
114
- // registerDefaultTranslations sets up default translations for the specified locale.
115
- func registerDefaultTranslations(validate *v10.Validate, translator ut.Translator, locale string) error {
116
- switch locale {
117
- case LocaleID:
118
- return idtranslations.RegisterDefaultTranslations(validate, translator)
119
- case LocaleEN:
120
- return entranslations.RegisterDefaultTranslations(validate, translator)
121
- default:
122
- // Fallback to English if the locale is not supported
123
- return entranslations.RegisterDefaultTranslations(validate, translator)
124
  }
125
- }
126
-
127
- // Validate validates a struct using the global validator instance
128
- // and returns a slice of ErrorMessage.
129
- func Validate(s any) []ErrorMessage {
130
- if validatorInstance == nil {
131
- return []ErrorMessage{{Field: "", Message: "Validator belum diinisialisasi. Panggil validation.New() terlebih dahulu."}}
132
- }
133
-
134
- err := validatorInstance.validate.Struct(s)
135
- if err != nil {
136
- return TranslateError(err)
137
- }
138
-
139
- return nil
140
- }
141
-
142
- // TranslateError takes a validation error and translates it using the global translator.
143
- func TranslateError(err error) []ErrorMessage {
144
- if validatorInstance == nil {
145
- return nil
146
- }
147
-
148
- var validationErrors v10.ValidationErrors
149
- if !errors.As(err, &validationErrors) {
150
- return nil
151
- }
152
-
153
- var errorMessages []ErrorMessage
154
-
155
- for _, e := range validationErrors {
156
- fieldLabel := e.Field()
157
-
158
- if e.Kind() == reflect.Ptr {
159
- continue
160
- }
161
 
162
- msg, err := validatorInstance.translator.T(e.Tag(), fieldLabel)
163
- if err != nil {
164
- msg = fieldLabel + " is Invalid"
165
- }
166
 
167
- errorMessages = append(errorMessages, ErrorMessage{
168
- Field: e.Tag(),
169
- Message: msg,
170
- })
171
  }
172
 
173
- return errorMessages
174
  }
 
1
  package validation
2
 
3
  import (
4
+ "context"
5
+ "time"
 
 
6
 
7
+ "gorm.io/gorm"
 
 
 
 
 
8
  )
9
 
 
 
 
 
 
 
 
10
  type ErrorMessage struct {
11
  Field string `json:"field"`
12
  Message string `json:"-"`
13
  }
14
 
15
+ func New(db *gorm.DB) (*Validator, error) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
 
17
+ // Create source with 5 minute cache expiry
18
+ expiry := 5 * time.Minute
19
+ dbSource, err := NewDBOptionSource(db, expiry)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  if err != nil {
21
+ return nil, err
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
+ // Start background refresh every 10 minutes
25
+ ctx := context.Background()
26
+ dbSource.StartAutoRefresh(ctx, 10*time.Minute)
 
27
 
28
+ // create validator
29
+ validator := NewValidator(dbSource)
30
+ if err := validator.RegisterAllCustomRules(); err != nil {
31
+ return nil, err
32
  }
33
 
34
+ return validator, nil
35
  }
space/response/validation.go CHANGED
@@ -3,12 +3,29 @@ package response
3
  import (
4
  "api.qobiltu.id/models"
5
  "api.qobiltu.id/pkg/validation"
 
6
  )
7
 
8
- func HandleValidationError(validationErrors []validation.ErrorMessage) error {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  return models.Exception{
10
  ValidationError: true,
11
  Message: "Validation failed",
12
- ValidationErrorFields: validationErrors,
13
  }
14
  }
 
3
  import (
4
  "api.qobiltu.id/models"
5
  "api.qobiltu.id/pkg/validation"
6
+ "github.com/go-playground/validator/v10"
7
  )
8
 
9
+ func HandleValidationError(err error) error {
10
+ validationErrors, ok := err.(validator.ValidationErrors)
11
+ if !ok {
12
+ return models.Exception{
13
+ ValidationError: true,
14
+ Message: "Validation failed",
15
+ }
16
+ }
17
+
18
+ validationErrorMessages := make([]validation.ErrorMessage, len(validationErrors))
19
+ for i, err := range validationErrors {
20
+ validationErrorMessages[i] = validation.ErrorMessage{
21
+ Field: err.Field(),
22
+ Message: err.Error(),
23
+ }
24
+ }
25
+
26
  return models.Exception{
27
  ValidationError: true,
28
  Message: "Validation failed",
29
+ ValidationErrorFields: validationErrorMessages,
30
  }
31
  }
space/services/marriage_readiness_profile_service.go CHANGED
@@ -19,14 +19,15 @@ type MarriageReadinessProfileService interface {
19
 
20
  type marriageReadinessProfileService struct {
21
  marriageReadinessProfileRepository repositories.MarriageReadinessProfileRepository
 
22
  }
23
 
24
- func NewMarriageReadinessProfileService(marriageReadinessProfileRepository repositories.MarriageReadinessProfileRepository) MarriageReadinessProfileService {
25
- return &marriageReadinessProfileService{marriageReadinessProfileRepository: marriageReadinessProfileRepository}
26
  }
27
 
28
  func (s *marriageReadinessProfileService) SaveMarriageReadinessProfile(ctx context.Context, req *models.SaveMarriageReadinessProfileRequest) (*models.MarriageReadinessProfile, error) {
29
- if err := validation.Validate(req); err != nil {
30
  return nil, response.HandleValidationError(err)
31
  }
32
 
 
19
 
20
  type marriageReadinessProfileService struct {
21
  marriageReadinessProfileRepository repositories.MarriageReadinessProfileRepository
22
+ validator *validation.Validator
23
  }
24
 
25
+ func NewMarriageReadinessProfileService(marriageReadinessProfileRepository repositories.MarriageReadinessProfileRepository, validator *validation.Validator) MarriageReadinessProfileService {
26
+ return &marriageReadinessProfileService{marriageReadinessProfileRepository: marriageReadinessProfileRepository, validator: validator}
27
  }
28
 
29
  func (s *marriageReadinessProfileService) SaveMarriageReadinessProfile(ctx context.Context, req *models.SaveMarriageReadinessProfileRequest) (*models.MarriageReadinessProfile, error) {
30
+ if err := s.validator.Validate(req); err != nil {
31
  return nil, response.HandleValidationError(err)
32
  }
33
 
space/services/partner_criteria_service.go CHANGED
@@ -19,14 +19,15 @@ type PartnerCriteriaService interface {
19
 
20
  type partnerCriteriaService struct {
21
  partnerCriteriaRepository repositories.PartnerCriteriaRepository
 
22
  }
23
 
24
- func NewPartnerCriteriaService(partnerCriteriaRepository repositories.PartnerCriteriaRepository) PartnerCriteriaService {
25
- return &partnerCriteriaService{partnerCriteriaRepository: partnerCriteriaRepository}
26
  }
27
 
28
  func (s *partnerCriteriaService) SavePartnerCriteria(ctx context.Context, req *models.SavePartnerCriteriaRequest) (*models.PartnerCriteria, error) {
29
- if err := validation.Validate(req); err != nil {
30
  return nil, response.HandleValidationError(err)
31
  }
32
 
 
19
 
20
  type partnerCriteriaService struct {
21
  partnerCriteriaRepository repositories.PartnerCriteriaRepository
22
+ validator *validation.Validator
23
  }
24
 
25
+ func NewPartnerCriteriaService(partnerCriteriaRepository repositories.PartnerCriteriaRepository, validator *validation.Validator) PartnerCriteriaService {
26
+ return &partnerCriteriaService{partnerCriteriaRepository: partnerCriteriaRepository, validator: validator}
27
  }
28
 
29
  func (s *partnerCriteriaService) SavePartnerCriteria(ctx context.Context, req *models.SavePartnerCriteriaRequest) (*models.PartnerCriteria, error) {
30
+ if err := s.validator.Validate(req); err != nil {
31
  return nil, response.HandleValidationError(err)
32
  }
33
 
space/space/controller/partner_criteria/partner_criteria_controller.go ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package partner_criteria_controller
2
+
3
+ import (
4
+ "net/http"
5
+
6
+ "api.qobiltu.id/middleware"
7
+ "api.qobiltu.id/models"
8
+ "api.qobiltu.id/response"
9
+ "api.qobiltu.id/services"
10
+ "github.com/gin-gonic/gin"
11
+ )
12
+
13
+ type PartnerCriteriaController interface {
14
+ SavePartnerCriteria(ctx *gin.Context)
15
+ GetPartnerCriteria(ctx *gin.Context)
16
+ }
17
+
18
+ type partnerCriteriaController struct {
19
+ partnerCriteriaService services.PartnerCriteriaService
20
+ }
21
+
22
+ func NewPartnerCriteriaController(partnerCriteriaService services.PartnerCriteriaService) PartnerCriteriaController {
23
+ return &partnerCriteriaController{
24
+ partnerCriteriaService: partnerCriteriaService,
25
+ }
26
+ }
27
+
28
+ func (c *partnerCriteriaController) SavePartnerCriteria(ctx *gin.Context) {
29
+ var req models.SavePartnerCriteriaRequest
30
+ if err := ctx.ShouldBindJSON(&req); err != nil {
31
+ response.HandleError(ctx, models.Exception{
32
+ Message: "Invalid body request",
33
+ BadRequest: true,
34
+ Err: err,
35
+ })
36
+ return
37
+ }
38
+
39
+ accountData := middleware.GetAccountData(ctx)
40
+ req.AccountID = int64(accountData.UserID)
41
+
42
+ res, err := c.partnerCriteriaService.SavePartnerCriteria(ctx, &req)
43
+ if err != nil {
44
+ response.HandleError(ctx, err)
45
+ return
46
+ }
47
+
48
+ response.HandleSuccess(ctx, http.StatusOK, "Partner criteria saved", res, nil)
49
+ }
50
+
51
+ func (c *partnerCriteriaController) GetPartnerCriteria(ctx *gin.Context) {
52
+ accountData := middleware.GetAccountData(ctx)
53
+ accountID := int64(accountData.UserID)
54
+
55
+ req := models.GetPartnerCriteriaRequest{
56
+ AccountID: accountID,
57
+ }
58
+
59
+ res, err := c.partnerCriteriaService.GetPartnerCriteria(ctx, &req)
60
+ if err != nil {
61
+ response.HandleError(ctx, err)
62
+ return
63
+ }
64
+
65
+ response.HandleSuccess(ctx, http.StatusOK, "Get partner criteria success", res, nil)
66
+ }
space/space/main.go CHANGED
@@ -9,6 +9,7 @@ import (
9
  cv_controller "api.qobiltu.id/controller/cv"
10
  health_check_controller "api.qobiltu.id/controller/health_check"
11
  marriage_readiness_profile_controller "api.qobiltu.id/controller/marriage_readiness_profile"
 
12
  "api.qobiltu.id/mail"
13
  "api.qobiltu.id/pkg/storage"
14
  "api.qobiltu.id/pkg/validation"
@@ -23,7 +24,7 @@ import (
23
  func main() {
24
 
25
  // setup validation
26
- err := validation.New(validation.LocaleID)
27
  utils.FatalIfErr("failed to setup validator", err)
28
 
29
  // setup storage
@@ -59,13 +60,17 @@ func main() {
59
  healthCheckController := health_check_controller.NewHealthCheckController(healthCheckService)
60
 
61
  cvRepository := repositories.NewCVRepository(config.DB)
62
- cvService := services.NewCVService(cvRepository, localStorage)
63
  cvController := cv_controller.NewCVController(cvService)
64
 
65
  marriageReadinessProfileRepository := repositories.NewMarriageReadinessProfileRepository(config.DB)
66
- marriageReadinessProfileService := services.NewMarriageReadinessProfileService(marriageReadinessProfileRepository)
67
  marriageReadinessProfileController := marriage_readiness_profile_controller.NewMarriageReadinessProfileController(marriageReadinessProfileService)
68
 
 
 
 
 
69
  // start task processor
70
  err = taskProcessor.Start()
71
  utils.FatalIfErr("failed to start task processor", err)
@@ -76,6 +81,7 @@ func main() {
76
  healthCheckController,
77
  cvController,
78
  marriageReadinessProfileController,
 
79
  )
80
  utils.FatalIfErr("failed to create server", err)
81
 
 
9
  cv_controller "api.qobiltu.id/controller/cv"
10
  health_check_controller "api.qobiltu.id/controller/health_check"
11
  marriage_readiness_profile_controller "api.qobiltu.id/controller/marriage_readiness_profile"
12
+ partner_criteria_controller "api.qobiltu.id/controller/partner_criteria"
13
  "api.qobiltu.id/mail"
14
  "api.qobiltu.id/pkg/storage"
15
  "api.qobiltu.id/pkg/validation"
 
24
  func main() {
25
 
26
  // setup validation
27
+ validator, err := validation.New(config.DB)
28
  utils.FatalIfErr("failed to setup validator", err)
29
 
30
  // setup storage
 
60
  healthCheckController := health_check_controller.NewHealthCheckController(healthCheckService)
61
 
62
  cvRepository := repositories.NewCVRepository(config.DB)
63
+ cvService := services.NewCVService(cvRepository, localStorage, validator)
64
  cvController := cv_controller.NewCVController(cvService)
65
 
66
  marriageReadinessProfileRepository := repositories.NewMarriageReadinessProfileRepository(config.DB)
67
+ marriageReadinessProfileService := services.NewMarriageReadinessProfileService(marriageReadinessProfileRepository, validator)
68
  marriageReadinessProfileController := marriage_readiness_profile_controller.NewMarriageReadinessProfileController(marriageReadinessProfileService)
69
 
70
+ partnerCriteriaRepository := repositories.NewPartnerCriteriaRepository(config.DB)
71
+ partnerCriteriaService := services.NewPartnerCriteriaService(partnerCriteriaRepository, validator)
72
+ partnerCriteriaController := partner_criteria_controller.NewPartnerCriteriaController(partnerCriteriaService)
73
+
74
  // start task processor
75
  err = taskProcessor.Start()
76
  utils.FatalIfErr("failed to start task processor", err)
 
81
  healthCheckController,
82
  cvController,
83
  marriageReadinessProfileController,
84
+ partnerCriteriaController,
85
  )
86
  utils.FatalIfErr("failed to create server", err)
87
 
space/space/models/database_orm_model.go CHANGED
@@ -248,26 +248,27 @@ type (
248
  }
249
 
250
  WorshipAndReligiousUnderstandingCV struct {
251
- ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id" counter:"skip"`
252
- AccountID int64 `gorm:"column:account_id;not null;unique" json:"account_id" counter:"skip"`
253
- Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty" counter:"skip"`
254
- ObligatoryPrayer *string `gorm:"column:obligatory_prayer" json:"obligatory_prayer"` // sholat_wajib_5_waktu
255
- CongregationalPrayer *string `gorm:"column:congregational_prayer" json:"congregational_prayer"` // sholat_berjamaah_di_masjid
256
- TahajjudPrayer *string `gorm:"column:tahajjud_prayer" json:"tahajjud_prayer"` // sholat_tahajud
257
- DhuhaPrayer *string `gorm:"column:dhuha_prayer" json:"dhuha_prayer"` // sholat_dhuha
258
- QuranMemorization *string `gorm:"column:quran_memorization" json:"quran_memorization"` // hafalan_alquran
259
- QuranReadingAbility *string `gorm:"column:quran_reading_ability" json:"quran_reading_ability"` // kemampuan_baca_alquran
260
- DaudFasting *string `gorm:"column:daud_fasting" json:"daud_fasting"` // puasa_daud
261
- AyyamulBidhFasting *string `gorm:"column:ayyamul_bidh_fasting" json:"ayyamul_bidh_fasting"` // puasa_ayyamul_bidh
262
- HajjOrUmrah *pq.StringArray `gorm:"column:hajj_or_umrah;type:varchar(255)[]" json:"hajj_or_umrah"` // ibadah_haji_umroh
263
- ListeningToMusic *string `gorm:"column:listening_to_music" json:"listening_to_music"` // mendengarkan_musik
264
- OpinionOnIkhtilat *string `gorm:"column:opinion_on_ikhtilat" json:"opinion_on_ikhtilat"` // pendapat_ikhtilat
265
- OpinionOnTouchingNonMahram *string `gorm:"column:opinion_on_touching_non_mahram" json:"opinion_on_touching_non_mahram"` // pendapat_menyentuh_non_mahram
266
- OpinionOnVeil *string `gorm:"column:opinion_on_veil" json:"opinion_on_veil"` // pendapat_tentang_cadar
267
- WeeklyReligiousStudies *string `gorm:"column:weekly_religious_studies" json:"weekly_religious_studies"` // kajian_yang_diikuti_dalam_sepekan
268
- FollowedUstadz *string `gorm:"column:followed_ustadz" json:"followed_ustadz"` // ustadz_yang_diikuti
269
- CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at" counter:"skip"`
270
- UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at" counter:"skip"`
 
271
  FieldCounter
272
  }
273
 
@@ -388,6 +389,39 @@ type (
388
  }
389
  )
390
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
391
  // Gorm table name settings
392
  func (Account) TableName() string { return "account" }
393
  func (AccountDetails) TableName() string { return "account_details" }
 
248
  }
249
 
250
  WorshipAndReligiousUnderstandingCV struct {
251
+ ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id" counter:"skip"`
252
+ AccountID int64 `gorm:"column:account_id;not null;unique" json:"account_id" counter:"skip"`
253
+ Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty" counter:"skip"`
254
+ ObligatoryPrayer *string `gorm:"column:obligatory_prayer" json:"obligatory_prayer"` // sholat_wajib_5_waktu
255
+ CongregationalPrayer *string `gorm:"column:congregational_prayer" json:"congregational_prayer"` // sholat_berjamaah_di_masjid
256
+ TahajjudPrayer *string `gorm:"column:tahajjud_prayer" json:"tahajjud_prayer"` // sholat_tahajud
257
+ DhuhaPrayer *string `gorm:"column:dhuha_prayer" json:"dhuha_prayer"` // sholat_dhuha
258
+ QuranMemorization *string `gorm:"column:quran_memorization" json:"quran_memorization"` // hafalan_alquran
259
+ QuranReadingAbility *string `gorm:"column:quran_reading_ability" json:"quran_reading_ability"` // kemampuan_baca_alquran
260
+ WeeklyReligiousStudyFrequency *string `gorm:"column:weekly_religious_study_frequency" json:"weekly_religious_study_frequency"` // kajian_yang_diikuti_dalam_sepekan
261
+ DaudFasting *string `gorm:"column:daud_fasting" json:"daud_fasting"` // puasa_daud
262
+ AyyamulBidhFasting *string `gorm:"column:ayyamul_bidh_fasting" json:"ayyamul_bidh_fasting"` // puasa_ayyamul_bidh
263
+ HajjOrUmrah *pq.StringArray `gorm:"column:hajj_or_umrah;type:varchar(255)[]" json:"hajj_or_umrah"` // ibadah_haji_umroh
264
+ ListeningToMusic *string `gorm:"column:listening_to_music" json:"listening_to_music"` // mendengarkan_musik
265
+ OpinionOnIkhtilat *string `gorm:"column:opinion_on_ikhtilat" json:"opinion_on_ikhtilat"` // pendapat_ikhtilat
266
+ OpinionOnTouchingNonMahram *string `gorm:"column:opinion_on_touching_non_mahram" json:"opinion_on_touching_non_mahram"` // pendapat_menyentuh_non_mahram
267
+ OpinionOnVeil *string `gorm:"column:opinion_on_veil" json:"opinion_on_veil"` // pendapat_tentang_cadar
268
+ WeeklyReligiousStudies *string `gorm:"column:weekly_religious_studies" json:"weekly_religious_studies"` // kajian_yang_diikuti_dalam_sepekan
269
+ FollowedUstadz *string `gorm:"column:followed_ustadz" json:"followed_ustadz"` // ustadz_yang_diikuti
270
+ CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at" counter:"skip"`
271
+ UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at" counter:"skip"`
272
  FieldCounter
273
  }
274
 
 
389
  }
390
  )
391
 
392
+ type (
393
+ PartnerCriteria struct {
394
+ ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
395
+ AccountID int64 `gorm:"column:account_id;not null;unique" json:"account_id"`
396
+ Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"`
397
+
398
+ // Kriteria Umum
399
+ ExpectedAgeLimit *int `gorm:"column:expected_age_limit" json:"expected_age_limit"` // batas usia pasangan yang diharapkan
400
+ ExpectedDomicile *string `gorm:"column:expected_domicile" json:"expected_domicile"` // domisili pasangan yang diharapkan
401
+ AcceptedMaritalStatus *pq.StringArray `gorm:"column:accepted_marital_status;type:varchar(255)[]" json:"accepted_marital_status"` // status pernikahan yang diterima
402
+ PartnerFamilyReligion *string `gorm:"column:partner_family_religion" json:"partner_family_religion"` // agama yang dianut keluarga pasangan
403
+ PartnerWeeklyReligiousStudyFrequency *string `gorm:"column:partner_weekly_religious_study_frequency" json:"partner_weekly_religious_study_frequency"` // rata-rata jumlah kajian yang diikuti pasangan per pekan
404
+ PartnerMonthlySpendingEstimate *string `gorm:"column:partner_monthly_spending_estimate" json:"partner_monthly_spending_estimate"` // estimasi pengeluaran pasangan per bulan
405
+ PartnerCanCook *string `gorm:"column:partner_can_cook" json:"partner_can_cook"` // apakah pasangan diharapkan bisa memasak
406
+
407
+ // Kriteria Fisik
408
+ ExpectedBodyShapes *pq.StringArray `gorm:"column:expected_body_shapes;type:varchar(255)[]" json:"expected_body_shapes"` // bentuk tubuh yang diharapkan
409
+ ExpectedSkinColors *pq.StringArray `gorm:"column:expected_skin_colors;type:varchar(255)[]" json:"expected_skin_colors"` // warna kulit yang diharapkan
410
+ ExpectedHairTypes *pq.StringArray `gorm:"column:expected_hair_types;type:varchar(255)[]" json:"expected_hair_types"` // tipe rambut yang diharapkan
411
+ ExpectedHairThickness *pq.StringArray `gorm:"column:expected_hair_thickness;type:varchar(255)[]" json:"expected_hair_thickness"` // jenis rambut yang diharapkan
412
+ ExpectedHeight *int `gorm:"column:expected_height" json:"expected_height"` // tinggi badan yang diharapkan
413
+
414
+ // Pendidikan & Pekerjaan
415
+ ExpectedPartnerIncome *string `gorm:"column:expected_partner_income" json:"expected_partner_income"` // penghasilan pasangan per bulan yang diharapkan
416
+ ExpectedIncomeSources *pq.StringArray `gorm:"column:expected_income_sources;type:varchar(255)[]" json:"expected_income_sources"` // sumber penghasilan pasangan
417
+ ExpectedLastEducation *pq.StringArray `gorm:"column:expected_last_education;type:varchar(255)[]" json:"expected_last_education"` // pendidikan terakhir yang diharapkan
418
+ ExpectedJobType *string `gorm:"column:expected_job_type" json:"expected_job_type"` // jenis pekerjaan pasangan yang diharapkan
419
+
420
+ CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"`
421
+ UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"`
422
+ }
423
+ )
424
+
425
  // Gorm table name settings
426
  func (Account) TableName() string { return "account" }
427
  func (AccountDetails) TableName() string { return "account_details" }
space/space/models/request_model.go CHANGED
@@ -74,7 +74,7 @@ type (
74
  FavoriteFoodAndDrinks *string `json:"favorite_food_and_drinks"` // makanan dan minuman favorit
75
  CanCook *bool `json:"can_cook"` // bisa memasak
76
  TypesOfDishesCooked *string `json:"types_of_dishes_cooked"` // jenis masakan yang bisa dimasak
77
- MonthlyExpenses *string `json:"monthly_expenses" validate:"monthly_expenses"` // pengeluaran per bulan
78
  }
79
 
80
  GetPersonalityAndPreferenceRequest struct {
@@ -84,11 +84,11 @@ type (
84
  CreateFamilyMemberRequest struct {
85
  AccountID int64 `json:"-"`
86
 
87
- Role *string `json:"role" validate:"family_role"` // Peran dalam keluarga
88
- Status *string `json:"status" validate:"life_status"` // Status (Hidup, Wafat)
89
  Religion *string `json:"religion" validate:"religion"` // Agama
90
  Job *string `json:"job"` // Pekerjaan
91
- LastEducation *string `json:"last_education" validate:"last_education"` // Pendidikan terakhir
92
  Age *int `json:"age"` // Usia
93
  }
94
 
@@ -96,11 +96,11 @@ type (
96
  ID int64 `json:"-"`
97
  AccountID int64 `json:"-"`
98
 
99
- Role *string `json:"role" validate:"family_role"` // Peran dalam keluarga
100
- Status *string `json:"status" validate:"life_status"` // Status (Hidup, Wafat)
101
  Religion *string `json:"religion" validate:"religion"` // Agama
102
  Job *string `json:"job"` // Pekerjaan
103
- LastEducation *string `json:"last_education" validate:"last_education"` // Pendidikan terakhir
104
  Age *int `json:"age"` // Usia
105
  }
106
 
@@ -120,9 +120,9 @@ type (
120
  AccountID int64 `json:"-"`
121
  HeightInCm *int `json:"height_cm"` // Tinggi badan dalam satuan sentimeter
122
  WeightInKg *int `json:"weight_kg"` // Berat badan dalam satuan kilogram
123
- BodyShape *string `json:"body_shape" validate:"body_shape"` // Bentuk tubuh
124
- SkinColor *string `json:"skin_color" validate:"skin_color"` // Warna kulit
125
- HairType *string `json:"hair_type" validate:"hair_type"` // Tipe rambut
126
  MedicalHistory *pq.StringArray `json:"medical_history"` // Riwayat penyakit
127
  PhysicalDisorder *string `json:"physical_disorder"` // Cacat fisik
128
  PhysicalTraits *string `json:"physical_traits"` // Ciri khas fisik
@@ -139,10 +139,10 @@ type (
139
  DateOfBirth *time.Time `json:"date_of_birth"`
140
  PlaceOfBirth *string `json:"place_of_birth"`
141
  Domicile *string `json:"domicile"`
142
- MaritalStatus *string `json:"marital_status" validate:"marital_status"`
143
- LastEducation *string `json:"last_education" validate:"last_education"`
144
  LastJob *string `json:"last_job"`
145
- PhoneNumber *string `json:"phone_number" validate:"phone_number"`
146
  }
147
 
148
  GetAccountDetailsRequest struct {
@@ -150,22 +150,23 @@ type (
150
  }
151
 
152
  SaveWorshipAndReligiousUnderstandingRequest struct {
153
- AccountID int64 `json:"-"`
154
- ObligatoryPrayer *string `json:"obligatory_prayer"` // sholat_wajib_5_waktu
155
- CongregationalPrayer *string `json:"congregational_prayer"` // sholat_berjamaah_di_masjid
156
- TahajjudPrayer *string `json:"tahajjud_prayer"` // sholat_tahajud
157
- DhuhaPrayer *string `json:"dhuha_prayer"` // sholat_dhuha
158
- QuranMemorization *string `json:"quran_memorization"` // hafalan_alquran
159
- QuranReadingAbility *string `json:"quran_reading_ability" validate:"quran_reading_ability"` // kemampuan_baca_alquran
160
- DaudFasting *string `json:"daud_fasting"` // puasa_daud
161
- AyyamulBidhFasting *string `json:"ayyamul_bidh_fasting"` // puasa_ayyamul_bidh
162
- HajjOrUmrah *pq.StringArray `json:"hajj_or_umrah"` // ibadah_haji_umroh
163
- ListeningToMusic *string `json:"listening_to_music"` // mendengarkan_musik
164
- OpinionOnIkhtilat *string `json:"opinion_on_ikhtilat"` // pendapat_ikhtilat
165
- OpinionOnTouchingNonMahram *string `json:"opinion_on_touching_non_mahram"` // pendapat_menyentuh_non_mahram
166
- OpinionOnVeil *string `json:"opinion_on_veil"` // pendapat_tentang_cadar
167
- WeeklyReligiousStudies *string `json:"weekly_religious_studies"` // kajian_yang_diikuti_dalam_sepekan
168
- FollowedUstadz *string `json:"followed_ustadz"` // ustadz_yang_diikuti
 
169
  }
170
 
171
  GetWorshipAndReligiousUnderstandingRequest struct {
@@ -174,7 +175,7 @@ type (
174
 
175
  CreateEducationRequest struct {
176
  AccountID int64 `json:"-"`
177
- LastEducation *string `json:"last_education" validate:"last_education"` // pendidikan terakhir
178
  EducationInstitute *string `json:"education_institute"` // institusi pendidikan
179
  EducationMajor *string `json:"education_major"` // jurusan pendidikan
180
  YearStart *int `json:"year_start"` // tahun masuk
@@ -184,7 +185,7 @@ type (
184
  UpdateEducationRequest struct {
185
  ID int64 `json:"-"`
186
  AccountID int64 `json:"-"`
187
- LastEducation *string `json:"last_education" validate:"last_education"` // pendidikan terakhir
188
  EducationInstitute *string `json:"education_institute"` // institusi pendidikan
189
  EducationMajor *string `json:"education_major"` // jurusan pendidikan
190
  YearStart *int `json:"year_start"` // tahun masuk
@@ -208,7 +209,7 @@ type (
208
  InstitutionName *string `json:"institution_name"` // nama instansi
209
  CurrentJob *string `json:"current_job"` // pekerjaan saat ini
210
  YearStartedWorking *int `json:"year_started_working"` // tahun mulai bekerja
211
- MonthlyIncome *string `json:"monthly_income" validate:"monthly_income"` // penghasilan per bulan
212
  IncomeSources *pq.StringArray `json:"income_sources"` // sumber penghasilan
213
  }
214
 
@@ -218,7 +219,7 @@ type (
218
  InstitutionName *string `json:"institution_name"` // nama instansi
219
  CurrentJob *string `json:"current_job"` // pekerjaan saat ini
220
  YearStartedWorking *int `json:"year_started_working"` // tahun mulai bekerja
221
- MonthlyIncome *string `json:"monthly_income" validate:"monthly_income"` // penghasilan per bulan
222
  IncomeSources *pq.StringArray `json:"income_sources"` // sumber penghasilan
223
  }
224
 
@@ -283,48 +284,82 @@ type (
283
  }
284
  )
285
 
286
- type SaveMarriageReadinessProfileRequest struct {
287
- AccountID int64 `json:"account_id"`
288
-
289
- // Visi Misi Rumah Tangga
290
- MarriageVision *string `gorm:"column:marriage_vision" json:"marriage_vision"` // Apa visi dan tujuan utama kamu dalam membangun rumah tangga?
291
- LivingPlan *string `gorm:"column:living_plan" json:"living_plan"` // Setelah menikah, kamu berencana tinggal di mana?
292
- SpouseRoles *string `gorm:"column:spouse_roles" json:"spouse_roles"` // Menurutmu, apa peran utama suami dan istri dalam rumah tangga?
293
- SpouseRights *string `gorm:"column:spouse_rights" json:"spouse_rights"` // Apa saja hak suami dan istri menurutmu?
294
- ConflictResolution *string `gorm:"column:conflict_resolution" json:"conflict_resolution"` // Jika terjadi konflik, bagaimana kamu menyikapinya?
295
- ParentingStyle *string `gorm:"column:parenting_style" json:"parenting_style"` // Setelah punya anak, pola pengasuhan seperti apa yang kamu inginkan?
296
-
297
- // Konsep Acara Pernikahan
298
- ReadyToMarryDuration *string `gorm:"column:ready_to_marry_duration" json:"ready_to_marry_duration"` // Setelah taaruf dimulai, berapa lama kamu butuh untuk siap menikah?
299
- WeddingConcept *string `gorm:"column:wedding_concept" json:"wedding_concept"` // Seperti apa konsep pernikahan yang kamu harapkan?
300
- WeddingFunding *string `gorm:"column:wedding_funding" json:"wedding_funding"` // Apakah kamu sudah mempersiapkan dana pernikahan? Dari mana sumbernya?
301
-
302
- // Karir Kedepannya
303
- CareerAfterMarriage *string `gorm:"column:career_after_marriage" json:"career_after_marriage"` // Apakah kamu ingin tetap bekerja setelah menikah?
304
- TimeManagement *string `gorm:"column:time_management" json:"time_management"` // Bagaimana kamu membagi waktu antara keluarga, ibadah, dan pekerjaan?
305
- CareerGoals *string `gorm:"column:career_goals" json:"career_goals"` // Apa impian karier atau cita-cita kamu dalam 5 sampai 10 tahun ke depan?
306
- SelfDevelopment *string `gorm:"column:self_development" json:"self_development"` // Apa usaha kamu untuk terus mengembangkan diri dan skill?
307
-
308
- // Pendidikan Keluarga
309
- DelayChildren *string `gorm:"column:delay_children" json:"delay_children"` // Apakah kamu berencana menunda memiliki anak?
310
- ChildEducationPlan *string `gorm:"column:child_education_plan" json:"child_education_plan"` // Apakah kamu sudah punya rencana pendidikan anak?
311
- ReligiousEmotionalBond *string `gorm:"column:religious_emotional_bond" json:"religious_emotional_bond"` // Bagaimana cara menjaga nilai agama & kedekatan emosional dengan anak?
312
- ParentingChallenges *string `gorm:"column:parenting_challenges" json:"parenting_challenges"` // Saat menghadapi tantangan pengasuhan, bagaimana kamu menyikapinya?
313
-
314
- // Finansial Keluarga
315
- MonthlyFinance *string `gorm:"column:monthly_finance" json:"monthly_finance"` // Bagaimana kamu mengelola keuangan bulanan?
316
- FamilyResponsibility *string `gorm:"column:family_responsibility" json:"family_responsibility"` // Apakah kamu masih punya tanggungan keluarga setelah menikah?
317
- DebtStatus *string `gorm:"column:debt_status" json:"debt_status"` // Apakah kamu punya cicilan/utang setelah menikah?
318
- FinanceSharing *string `gorm:"column:finance_sharing" json:"finance_sharing"` // Setelah menikah, bagaimana pembagian tanggung jawab keuangan?
319
- IncomeGapView *string `gorm:"column:income_gap_view" json:"income_gap_view"` // Pandangan kamu jika istri berpenghasilan lebih besar dari suami?
320
-
321
- // Keputusan dan Komunikasi
322
- DecisionMaking *string `gorm:"column:decision_making" json:"decision_making"` // Dalam mengambil keputusan, kamu lebih mempertimbangkan pasangan atau sendiri?
323
- GrowthTogether *string `gorm:"column:growth_together" json:"growth_together"` // Apakah kamu siap berjuang bersama pasangan? Apa saja yang ingin diperjuangkan?
324
- ParentIntervention *string `gorm:"column:parent_intervention" json:"parent_intervention"` // Sejauh mana orang tua/mertua boleh ikut campur dalam rumah tangga?
325
- HabitResponse *string `gorm:"column:habit_response" json:"habit_response"` // Bagaimana kamu menyikapi kebiasaan pasangan yang kurang kamu sukai?
326
- }
327
 
328
- type GetMarriageReadinessProfileRequest struct {
329
- AccountID int64 `json:"-"`
330
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  FavoriteFoodAndDrinks *string `json:"favorite_food_and_drinks"` // makanan dan minuman favorit
75
  CanCook *bool `json:"can_cook"` // bisa memasak
76
  TypesOfDishesCooked *string `json:"types_of_dishes_cooked"` // jenis masakan yang bisa dimasak
77
+ MonthlyExpenses *string `json:"monthly_expenses" validate:"monthly-expenses"` // pengeluaran per bulan
78
  }
79
 
80
  GetPersonalityAndPreferenceRequest struct {
 
84
  CreateFamilyMemberRequest struct {
85
  AccountID int64 `json:"-"`
86
 
87
+ Role *string `json:"role" validate:"family-role"` // Peran dalam keluarga
88
+ Status *string `json:"status" validate:"life-status"` // Status (Hidup, Wafat)
89
  Religion *string `json:"religion" validate:"religion"` // Agama
90
  Job *string `json:"job"` // Pekerjaan
91
+ LastEducation *string `json:"last_education" validate:"last-education"` // Pendidikan terakhir
92
  Age *int `json:"age"` // Usia
93
  }
94
 
 
96
  ID int64 `json:"-"`
97
  AccountID int64 `json:"-"`
98
 
99
+ Role *string `json:"role" validate:"family-role"` // Peran dalam keluarga
100
+ Status *string `json:"status" validate:"life-status"` // Status (Hidup, Wafat)
101
  Religion *string `json:"religion" validate:"religion"` // Agama
102
  Job *string `json:"job"` // Pekerjaan
103
+ LastEducation *string `json:"last_education" validate:"last-education"` // Pendidikan terakhir
104
  Age *int `json:"age"` // Usia
105
  }
106
 
 
120
  AccountID int64 `json:"-"`
121
  HeightInCm *int `json:"height_cm"` // Tinggi badan dalam satuan sentimeter
122
  WeightInKg *int `json:"weight_kg"` // Berat badan dalam satuan kilogram
123
+ BodyShape *string `json:"body_shape" validate:"body-shape"` // Bentuk tubuh
124
+ SkinColor *string `json:"skin_color" validate:"skin-color"` // Warna kulit
125
+ HairType *string `json:"hair_type" validate:"hair-type"` // Tipe rambut
126
  MedicalHistory *pq.StringArray `json:"medical_history"` // Riwayat penyakit
127
  PhysicalDisorder *string `json:"physical_disorder"` // Cacat fisik
128
  PhysicalTraits *string `json:"physical_traits"` // Ciri khas fisik
 
139
  DateOfBirth *time.Time `json:"date_of_birth"`
140
  PlaceOfBirth *string `json:"place_of_birth"`
141
  Domicile *string `json:"domicile"`
142
+ MaritalStatus *string `json:"marital_status" validate:"marital-status"`
143
+ LastEducation *string `json:"last_education" validate:"last-education"`
144
  LastJob *string `json:"last_job"`
145
+ PhoneNumber *string `json:"phone_number" validate:"phone-number"`
146
  }
147
 
148
  GetAccountDetailsRequest struct {
 
150
  }
151
 
152
  SaveWorshipAndReligiousUnderstandingRequest struct {
153
+ AccountID int64 `json:"-"`
154
+ ObligatoryPrayer *string `json:"obligatory_prayer"` // sholat_wajib_5_waktu
155
+ CongregationalPrayer *string `json:"congregational_prayer"` // sholat_berjamaah_di_masjid
156
+ TahajjudPrayer *string `json:"tahajjud_prayer"` // sholat_tahajud
157
+ DhuhaPrayer *string `json:"dhuha_prayer"` // sholat_dhuha
158
+ QuranMemorization *string `json:"quran_memorization"` // hafalan_alquran
159
+ QuranReadingAbility *string `json:"quran_reading_ability" validate:"quran-reading-ability"` // kemampuan_baca_alquran
160
+ WeeklyReligiousStudyFrequency *string `json:"weekly_religious_study_frequency" validate:"weekly-religious-study-frequency"` // kajian_yang_diikuti_dalam_sepekan
161
+ DaudFasting *string `json:"daud_fasting"` // puasa_daud
162
+ AyyamulBidhFasting *string `json:"ayyamul_bidh_fasting"` // puasa_ayyamul_bidh
163
+ HajjOrUmrah *pq.StringArray `json:"hajj_or_umrah"` // ibadah_haji_umroh
164
+ ListeningToMusic *string `json:"listening_to_music"` // mendengarkan_musik
165
+ OpinionOnIkhtilat *string `json:"opinion_on_ikhtilat"` // pendapat_ikhtilat
166
+ OpinionOnTouchingNonMahram *string `json:"opinion_on_touching_non_mahram"` // pendapat_menyentuh_non_mahram
167
+ OpinionOnVeil *string `json:"opinion_on_veil"` // pendapat_tentang_cadar
168
+ WeeklyReligiousStudies *string `json:"weekly_religious_studies"` // kajian_yang_diikuti_dalam_sepekan
169
+ FollowedUstadz *string `json:"followed_ustadz"` // ustadz_yang_diikuti
170
  }
171
 
172
  GetWorshipAndReligiousUnderstandingRequest struct {
 
175
 
176
  CreateEducationRequest struct {
177
  AccountID int64 `json:"-"`
178
+ LastEducation *string `json:"last_education" validate:"last-education"` // pendidikan terakhir
179
  EducationInstitute *string `json:"education_institute"` // institusi pendidikan
180
  EducationMajor *string `json:"education_major"` // jurusan pendidikan
181
  YearStart *int `json:"year_start"` // tahun masuk
 
185
  UpdateEducationRequest struct {
186
  ID int64 `json:"-"`
187
  AccountID int64 `json:"-"`
188
+ LastEducation *string `json:"last_education" validate:"last-education"` // pendidikan terakhir
189
  EducationInstitute *string `json:"education_institute"` // institusi pendidikan
190
  EducationMajor *string `json:"education_major"` // jurusan pendidikan
191
  YearStart *int `json:"year_start"` // tahun masuk
 
209
  InstitutionName *string `json:"institution_name"` // nama instansi
210
  CurrentJob *string `json:"current_job"` // pekerjaan saat ini
211
  YearStartedWorking *int `json:"year_started_working"` // tahun mulai bekerja
212
+ MonthlyIncome *string `json:"monthly_income" validate:"monthly-income"` // penghasilan per bulan
213
  IncomeSources *pq.StringArray `json:"income_sources"` // sumber penghasilan
214
  }
215
 
 
219
  InstitutionName *string `json:"institution_name"` // nama instansi
220
  CurrentJob *string `json:"current_job"` // pekerjaan saat ini
221
  YearStartedWorking *int `json:"year_started_working"` // tahun mulai bekerja
222
+ MonthlyIncome *string `json:"monthly_income" validate:"monthly-income"` // penghasilan per bulan
223
  IncomeSources *pq.StringArray `json:"income_sources"` // sumber penghasilan
224
  }
225
 
 
284
  }
285
  )
286
 
287
+ type (
288
+ SaveMarriageReadinessProfileRequest struct {
289
+ AccountID int64 `json:"account_id"`
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
290
 
291
+ // Visi Misi Rumah Tangga
292
+ MarriageVision *string `gorm:"column:marriage_vision" json:"marriage_vision"` // Apa visi dan tujuan utama kamu dalam membangun rumah tangga?
293
+ LivingPlan *string `gorm:"column:living_plan" json:"living_plan"` // Setelah menikah, kamu berencana tinggal di mana?
294
+ SpouseRoles *string `gorm:"column:spouse_roles" json:"spouse_roles"` // Menurutmu, apa peran utama suami dan istri dalam rumah tangga?
295
+ SpouseRights *string `gorm:"column:spouse_rights" json:"spouse_rights"` // Apa saja hak suami dan istri menurutmu?
296
+ ConflictResolution *string `gorm:"column:conflict_resolution" json:"conflict_resolution"` // Jika terjadi konflik, bagaimana kamu menyikapinya?
297
+ ParentingStyle *string `gorm:"column:parenting_style" json:"parenting_style"` // Setelah punya anak, pola pengasuhan seperti apa yang kamu inginkan?
298
+
299
+ // Konsep Acara Pernikahan
300
+ ReadyToMarryDuration *string `gorm:"column:ready_to_marry_duration" json:"ready_to_marry_duration"` // Setelah taaruf dimulai, berapa lama kamu butuh untuk siap menikah?
301
+ WeddingConcept *string `gorm:"column:wedding_concept" json:"wedding_concept"` // Seperti apa konsep pernikahan yang kamu harapkan?
302
+ WeddingFunding *string `gorm:"column:wedding_funding" json:"wedding_funding"` // Apakah kamu sudah mempersiapkan dana pernikahan? Dari mana sumbernya?
303
+
304
+ // Karir Kedepannya
305
+ CareerAfterMarriage *string `gorm:"column:career_after_marriage" json:"career_after_marriage"` // Apakah kamu ingin tetap bekerja setelah menikah?
306
+ TimeManagement *string `gorm:"column:time_management" json:"time_management"` // Bagaimana kamu membagi waktu antara keluarga, ibadah, dan pekerjaan?
307
+ CareerGoals *string `gorm:"column:career_goals" json:"career_goals"` // Apa impian karier atau cita-cita kamu dalam 5 sampai 10 tahun ke depan?
308
+ SelfDevelopment *string `gorm:"column:self_development" json:"self_development"` // Apa usaha kamu untuk terus mengembangkan diri dan skill?
309
+
310
+ // Pendidikan Keluarga
311
+ DelayChildren *string `gorm:"column:delay_children" json:"delay_children"` // Apakah kamu berencana menunda memiliki anak?
312
+ ChildEducationPlan *string `gorm:"column:child_education_plan" json:"child_education_plan"` // Apakah kamu sudah punya rencana pendidikan anak?
313
+ ReligiousEmotionalBond *string `gorm:"column:religious_emotional_bond" json:"religious_emotional_bond"` // Bagaimana cara menjaga nilai agama & kedekatan emosional dengan anak?
314
+ ParentingChallenges *string `gorm:"column:parenting_challenges" json:"parenting_challenges"` // Saat menghadapi tantangan pengasuhan, bagaimana kamu menyikapinya?
315
+
316
+ // Finansial Keluarga
317
+ MonthlyFinance *string `gorm:"column:monthly_finance" json:"monthly_finance"` // Bagaimana kamu mengelola keuangan bulanan?
318
+ FamilyResponsibility *string `gorm:"column:family_responsibility" json:"family_responsibility"` // Apakah kamu masih punya tanggungan keluarga setelah menikah?
319
+ DebtStatus *string `gorm:"column:debt_status" json:"debt_status"` // Apakah kamu punya cicilan/utang setelah menikah?
320
+ FinanceSharing *string `gorm:"column:finance_sharing" json:"finance_sharing"` // Setelah menikah, bagaimana pembagian tanggung jawab keuangan?
321
+ IncomeGapView *string `gorm:"column:income_gap_view" json:"income_gap_view"` // Pandangan kamu jika istri berpenghasilan lebih besar dari suami?
322
+
323
+ // Keputusan dan Komunikasi
324
+ DecisionMaking *string `gorm:"column:decision_making" json:"decision_making"` // Dalam mengambil keputusan, kamu lebih mempertimbangkan pasangan atau sendiri?
325
+ GrowthTogether *string `gorm:"column:growth_together" json:"growth_together"` // Apakah kamu siap berjuang bersama pasangan? Apa saja yang ingin diperjuangkan?
326
+ ParentIntervention *string `gorm:"column:parent_intervention" json:"parent_intervention"` // Sejauh mana orang tua/mertua boleh ikut campur dalam rumah tangga?
327
+ HabitResponse *string `gorm:"column:habit_response" json:"habit_response"` // Bagaimana kamu menyikapi kebiasaan pasangan yang kurang kamu sukai?
328
+ }
329
+
330
+ GetMarriageReadinessProfileRequest struct {
331
+ AccountID int64 `json:"-"`
332
+ }
333
+ )
334
+
335
+ type (
336
+ SavePartnerCriteriaRequest struct {
337
+ AccountID int64 `json:"account_id"`
338
+
339
+ ExpectedAgeLimit *int `gorm:"column:expected_age_limit" json:"expected_age_limit"` // batas usia pasangan yang diharapkan
340
+ ExpectedDomicile *string `gorm:"column:expected_domicile" json:"expected_domicile"` // domisili pasangan yang diharapkan
341
+ AcceptedMaritalStatus *pq.StringArray `gorm:"column:accepted_marital_status;type:varchar(255)[]" json:"accepted_marital_status"` // status pernikahan yang diterima
342
+ PartnerFamilyReligion *string `gorm:"column:partner_family_religion" json:"partner_family_religion"` // agama yang dianut keluarga pasangan
343
+ PartnerWeeklyReligiousStudyFrequency *string `gorm:"column:partner_weekly_religious_study_frequency" json:"partner_weekly_religious_study_frequency"` // rata-rata jumlah kajian yang diikuti pasangan per pekan
344
+ PartnerMonthlySpendingEstimate *string `gorm:"column:partner_monthly_spending_estimate" json:"partner_monthly_spending_estimate"` // estimasi pengeluaran pasangan per bulan
345
+ PartnerCanCook *string `gorm:"column:partner_can_cook" json:"partner_can_cook"` // apakah pasangan diharapkan bisa memasak
346
+
347
+ // Kriteria Fisik
348
+ ExpectedBodyShapes *pq.StringArray `gorm:"column:expected_body_shapes;type:varchar(255)[]" json:"expected_body_shapes"` // bentuk tubuh yang diharapkan
349
+ ExpectedSkinColors *pq.StringArray `gorm:"column:expected_skin_colors;type:varchar(255)[]" json:"expected_skin_colors"` // warna kulit yang diharapkan
350
+ ExpectedHairTypes *pq.StringArray `gorm:"column:expected_hair_types;type:varchar(255)[]" json:"expected_hair_types"` // tipe rambut yang diharapkan
351
+ ExpectedHairThickness *pq.StringArray `gorm:"column:expected_hair_thickness;type:varchar(255)[]" json:"expected_hair_thickness"` // jenis rambut yang diharapkan
352
+ ExpectedHeight *int `gorm:"column:expected_height" json:"expected_height"` // tinggi badan yang diharapkan
353
+
354
+ // Pendidikan & Pekerjaan
355
+ ExpectedPartnerIncome *string `gorm:"column:expected_partner_income" json:"expected_partner_income"` // penghasilan pasangan per bulan yang diharapkan
356
+ ExpectedIncomeSources *pq.StringArray `gorm:"column:expected_income_sources;type:varchar(255)[]" json:"expected_income_sources"` // sumber penghasilan pasangan
357
+ ExpectedLastEducation *pq.StringArray `gorm:"column:expected_last_education;type:varchar(255)[]" json:"expected_last_education"` // pendidikan terakhir yang diharapkan
358
+ ExpectedJobType *string `gorm:"column:expected_job_type" json:"expected_job_type"` // jenis pekerjaan pasangan yang diharapkan
359
+
360
+ }
361
+
362
+ GetPartnerCriteriaRequest struct {
363
+ AccountID int64 `json:"-"`
364
+ }
365
+ )
space/space/pkg/validation/custom_rules.go CHANGED
@@ -1,105 +1,154 @@
1
  package validation
2
 
3
  import (
 
 
 
4
  "regexp"
5
  "strings"
6
  "sync"
 
7
 
8
  v10 "github.com/go-playground/validator/v10"
9
  "gorm.io/gorm"
10
  )
11
 
12
- type ValidOptionSource interface {
13
  GetValidOptions(key string) ([]string, error)
14
  GetValidKeys() []string
 
 
 
15
  }
16
 
17
  // --------------------
18
- // InMemoryOptionSource
19
  // --------------------
20
 
21
- type InMemoryOptionSource struct{}
22
-
23
- var inMemoryOptions = map[string][]string{
24
- "last_education": {"SD", "SMP", "SMA", "D1", "D2", "D3", "D4", "D5", "S1", "S2", "S3"},
25
- "marital_status": {"Belum Menikah", "Duda", "Janda"},
26
- "gender": {"Laki-laki", "Perempuan"},
27
- "monthly_expenses": {"< 2 Juta", "2-5 Juta", "5-20 Juta", "> 10 Juta"},
28
- "monthly_income": {"< 3 Juta", "3-5 Juta", "5-10 Juta", "> 10 Juta"},
29
- "religion": {"Islam", "Non-Islam"},
30
- "family_role": {"Ayah", "Ibu", "Kakak", "Adik", "Anak"},
31
- "life_status": {"Hidup", "Wafat"},
32
- "body_shape": {"Ideal", "Kurus", "Berisi", "Gemuk"},
33
- "skin_color": {"Putih", "Kuning Langsat", "Sawo Matang"},
34
- "hair_type": {"Lurus", "Bergelombang", "Keriting"},
35
- "frequently": {"Selalu", "Sering", "Kadang", "Jarang", "Tidak Pernah"},
36
- "quran_reading_ability": {"Lancar", "Menengah", "Perlu Bimbingan"},
37
- }
38
-
39
- func (s *InMemoryOptionSource) GetValidOptions(key string) ([]string, error) {
40
- return inMemoryOptions[key], nil
41
  }
42
 
43
- func (s *InMemoryOptionSource) GetValidKeys() []string {
44
- keys := make([]string, 0, len(inMemoryOptions))
45
- for k := range inMemoryOptions {
46
- keys = append(keys, k)
 
47
  }
48
- return keys
 
 
 
49
  }
50
 
51
- // --------------------
52
- // DBOptionSource
53
- // --------------------
54
 
55
- type DBOptionSource struct {
56
- options map[string][]string
57
- mu sync.RWMutex
58
- }
59
 
60
- type (
61
- OptionCategory struct {
62
- ID int64 `gorm:"primaryKey" json:"id"`
63
- OptionName string `json:"option_name"`
64
- OptionSlug string `json:"option_slug" gorm:"uniqueIndex"`
65
  }
66
 
67
- OptionValues struct {
68
- ID int64 `gorm:"primaryKey" json:"id"`
69
- OptionCategoryID int64 `json:"option_category_id"`
70
- OptionValue string `json:"option_value"`
 
 
 
 
 
 
 
71
  }
72
- )
73
 
74
- func NewDBOptionSource(db *gorm.DB) (*DBOptionSource, error) {
75
- var categories []OptionCategory
76
- if err := db.Find(&categories).Error; err != nil {
77
- return nil, err
78
  }
79
 
80
- options := make(map[string][]string)
81
- for _, cat := range categories {
82
- var values []OptionValues
83
- if err := db.Where("option_category_id = ?", cat.ID).Find(&values).Error; err != nil {
84
- return nil, err
85
- }
86
- for _, val := range values {
87
- options[cat.OptionSlug] = append(options[cat.OptionSlug], val.OptionValue)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  }
89
- }
 
 
 
 
 
90
 
91
- return &DBOptionSource{options: options}, nil
 
 
 
 
92
  }
93
 
94
  func (s *DBOptionSource) GetValidOptions(key string) ([]string, error) {
 
 
 
 
 
 
 
 
 
 
95
  s.mu.RLock()
96
  defer s.mu.RUnlock()
97
- return s.options[key], nil
 
 
 
 
 
 
 
 
98
  }
99
 
100
  func (s *DBOptionSource) GetValidKeys() []string {
101
  s.mu.RLock()
102
  defer s.mu.RUnlock()
 
103
  keys := make([]string, 0, len(s.options))
104
  for k := range s.options {
105
  keys = append(keys, k)
@@ -108,58 +157,144 @@ func (s *DBOptionSource) GetValidKeys() []string {
108
  }
109
 
110
  // --------------------
111
- // Validator
112
  // --------------------
113
 
114
  type Validator struct {
115
- source ValidOptionSource
 
116
  }
117
 
118
- func NewValidatorRules(source ValidOptionSource) *Validator {
119
- return &Validator{source: source}
 
 
 
 
120
  }
121
 
122
- func (v *Validator) GenericOptionRule(key string) func(fl v10.FieldLevel) bool {
123
- return func(fl v10.FieldLevel) bool {
124
- value := fl.Field().String()
125
- if value == "" {
126
- return true
 
 
 
 
 
127
  }
128
- validOptions, err := v.source.GetValidOptions(key)
129
- if err != nil {
130
- return false
 
 
 
131
  }
132
- for _, opt := range validOptions {
133
- if opt == value {
134
- return true
135
- }
136
  }
137
- return false
138
  }
 
 
139
  }
 
 
 
 
140
 
141
- func (v *Validator) RegisterAllCustomRules(validate *v10.Validate) error {
142
- for _, key := range v.source.GetValidKeys() {
143
- err := validate.RegisterValidation(key, v.GenericOptionRule(key))
144
- if err != nil {
145
- return err
146
  }
 
147
  }
148
 
149
- err := validate.RegisterValidation("password", v.PasswordRule)
150
- if err != nil {
151
- return err
152
  }
153
 
154
- err = validate.RegisterValidation("phone_number", v.PhoneNumberRule)
155
- if err != nil {
156
- return err
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  }
158
 
159
- return nil
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
  }
161
 
162
- func (v *Validator) PasswordRule(fl v10.FieldLevel) bool {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
  password := fl.Field().String()
164
  return len(password) >= 8 &&
165
  strings.ContainsAny(password, "abcdefghijklmnopqrstuvwxyz") &&
@@ -167,38 +302,23 @@ func (v *Validator) PasswordRule(fl v10.FieldLevel) bool {
167
  strings.ContainsAny(password, "0123456789")
168
  }
169
 
170
- func (v *Validator) PhoneNumberRule(fl v10.FieldLevel) bool {
171
- phone := SanitizePhoneNumber(fl.Field().String())
172
  return strings.HasPrefix(phone, "+62")
173
  }
174
 
175
- func SanitizePhoneNumber(input string) string {
176
- // Hilangkan semua spasi dan strip
177
- input = strings.ReplaceAll(input, " ", "")
178
- input = strings.ReplaceAll(input, "-", "")
179
- input = strings.ReplaceAll(input, "(", "")
180
- input = strings.ReplaceAll(input, ")", "")
181
-
182
- // Hilangkan semua karakter non-digit kecuali +
183
  re := regexp.MustCompile(`[^0-9\+]`)
184
  input = re.ReplaceAllString(input, "")
185
 
186
- // Handle nomor diawali 0 (contoh: 0812...) menjadi +62812...
187
- if strings.HasPrefix(input, "0") {
188
- input = "+62" + input[1:]
 
 
 
 
 
 
189
  }
190
-
191
- // Handle jika diawali dengan 62 tanpa + (contoh: 62812...)
192
- if strings.HasPrefix(input, "62") && !strings.HasPrefix(input, "+62") {
193
- input = "+" + input
194
- }
195
-
196
- // Handle jika tidak ada awalan +62 sama sekali (contoh: 8123456789)
197
- if !strings.HasPrefix(input, "+62") {
198
- if strings.HasPrefix(input, "8") {
199
- input = "+62" + input
200
- }
201
- }
202
-
203
- return input
204
  }
 
1
  package validation
2
 
3
  import (
4
+ "context"
5
+ "fmt"
6
+ "reflect"
7
  "regexp"
8
  "strings"
9
  "sync"
10
+ "time"
11
 
12
  v10 "github.com/go-playground/validator/v10"
13
  "gorm.io/gorm"
14
  )
15
 
16
+ type ValidatorOptionSource interface {
17
  GetValidOptions(key string) ([]string, error)
18
  GetValidKeys() []string
19
+ Refresh() error
20
+ StartAutoRefresh(ctx context.Context, interval time.Duration)
21
+ HasKey(key string) bool
22
  }
23
 
24
  // --------------------
25
+ // DBOptionSource with Safe Handling
26
  // --------------------
27
 
28
+ type DBOptionSource struct {
29
+ db *gorm.DB
30
+ options map[string][]string
31
+ mu sync.RWMutex
32
+ lastUpdate time.Time
33
+ expiry time.Duration
34
+ stopChan chan struct{}
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  }
36
 
37
+ func NewDBOptionSource(db *gorm.DB, expiry time.Duration) (*DBOptionSource, error) {
38
+ source := &DBOptionSource{
39
+ db: db,
40
+ expiry: expiry,
41
+ stopChan: make(chan struct{}),
42
  }
43
+ if err := source.Refresh(); err != nil {
44
+ return nil, fmt.Errorf("failed to initialize DB option source: %w", err)
45
+ }
46
+ return source, nil
47
  }
48
 
49
+ func (s *DBOptionSource) Refresh() error {
50
+ s.mu.Lock()
51
+ defer s.mu.Unlock()
52
 
53
+ // Buat session baru tanpa transaction
54
+ tx := s.db.Session(&gorm.Session{SkipDefaultTransaction: true})
 
 
55
 
56
+ var results []struct {
57
+ Slug string `gorm:"column:slug"`
58
+ Value string `gorm:"column:value"`
 
 
59
  }
60
 
61
+ err := tx.Raw(`
62
+ SELECT
63
+ c.option_slug AS slug,
64
+ v.option_value AS value
65
+ FROM option_categories c
66
+ JOIN option_values v ON c.id = v.option_category_id
67
+ ORDER BY c.id, v.id
68
+ `).Scan(&results).Error
69
+
70
+ if err != nil {
71
+ return fmt.Errorf("failed to refresh options: %w", err)
72
  }
 
73
 
74
+ newOptions := make(map[string][]string)
75
+ for _, r := range results {
76
+ newOptions[r.Slug] = append(newOptions[r.Slug], r.Value)
 
77
  }
78
 
79
+ s.options = newOptions
80
+ s.lastUpdate = time.Now()
81
+
82
+ fmt.Println("options refreshed")
83
+
84
+ return nil
85
+ }
86
+
87
+ func (s *DBOptionSource) StartAutoRefresh(ctx context.Context, interval time.Duration) {
88
+ ticker := time.NewTicker(interval)
89
+ go func() {
90
+ for {
91
+ select {
92
+ case <-ticker.C:
93
+ s.mu.Lock()
94
+ needsRefresh := time.Since(s.lastUpdate) > s.expiry
95
+ s.mu.Unlock()
96
+
97
+ if needsRefresh {
98
+ if err := s.Refresh(); err != nil {
99
+ fmt.Printf("failed to auto-refresh options: %v\n", err)
100
+ }
101
+ }
102
+ case <-ctx.Done():
103
+ ticker.Stop()
104
+ return
105
+ case <-s.stopChan:
106
+ ticker.Stop()
107
+ return
108
+ }
109
  }
110
+ }()
111
+ }
112
+
113
+ func (s *DBOptionSource) StopAutoRefresh() {
114
+ close(s.stopChan)
115
+ }
116
 
117
+ func (s *DBOptionSource) HasKey(key string) bool {
118
+ s.mu.RLock()
119
+ defer s.mu.RUnlock()
120
+ _, exists := s.options[key]
121
+ return exists
122
  }
123
 
124
  func (s *DBOptionSource) GetValidOptions(key string) ([]string, error) {
125
+ s.mu.RLock()
126
+ needsRefresh := time.Since(s.lastUpdate) > s.expiry
127
+ s.mu.RUnlock()
128
+
129
+ if needsRefresh {
130
+ if err := s.Refresh(); err != nil {
131
+ return nil, fmt.Errorf("failed to refresh options: %w", err)
132
+ }
133
+ }
134
+
135
  s.mu.RLock()
136
  defer s.mu.RUnlock()
137
+
138
+ options, exists := s.options[key]
139
+ if !exists {
140
+ return nil, nil // Return nil instead of error for missing keys
141
+ }
142
+
143
+ copied := make([]string, len(options))
144
+ copy(copied, options)
145
+ return copied, nil
146
  }
147
 
148
  func (s *DBOptionSource) GetValidKeys() []string {
149
  s.mu.RLock()
150
  defer s.mu.RUnlock()
151
+
152
  keys := make([]string, 0, len(s.options))
153
  for k := range s.options {
154
  keys = append(keys, k)
 
157
  }
158
 
159
  // --------------------
160
+ // Validator with Safe Rule Handling
161
  // --------------------
162
 
163
  type Validator struct {
164
+ source ValidatorOptionSource
165
+ validate *v10.Validate
166
  }
167
 
168
+ func NewValidator(source ValidatorOptionSource) *Validator {
169
+ validate := v10.New()
170
+ return &Validator{
171
+ source: source,
172
+ validate: validate,
173
+ }
174
  }
175
 
176
+ func (v *Validator) RegisterAllCustomRules() error {
177
+ // First register static validation rules
178
+ staticRules := map[string]func(v10.FieldLevel) bool{
179
+ "password": v.validatePassword,
180
+ "phone-number": v.validatePhoneNumber,
181
+ }
182
+
183
+ for name, fn := range staticRules {
184
+ if err := v.validate.RegisterValidation(name, fn); err != nil {
185
+ return fmt.Errorf("failed to register %s validation: %w", name, err)
186
  }
187
+ }
188
+
189
+ // Then register dynamic option rules
190
+ for _, key := range v.source.GetValidKeys() {
191
+ if !v.source.HasKey(key) {
192
+ continue // Skip if key doesn't exist
193
  }
194
+
195
+ if err := v.validate.RegisterValidation(key, v.createOptionRule(key)); err != nil {
196
+ return fmt.Errorf("failed to register validation for %s: %w", key, err)
 
197
  }
 
198
  }
199
+
200
+ return nil
201
  }
202
+ func (v *Validator) Validate(input interface{}) error {
203
+ if input == nil {
204
+ return nil
205
+ }
206
 
207
+ val := reflect.ValueOf(input)
208
+ if val.Kind() == reflect.Ptr {
209
+ if val.IsNil() {
210
+ return nil
 
211
  }
212
+ val = val.Elem() // Dereference the pointer
213
  }
214
 
215
+ err := v.validate.Struct(input)
216
+ if err == nil {
217
+ return nil
218
  }
219
 
220
+ // Filter out errors for nil/empty fields
221
+ if ve, ok := err.(v10.ValidationErrors); ok {
222
+ var filteredErrors v10.ValidationErrors
223
+ for _, fe := range ve {
224
+ fieldValue := val.FieldByName(fe.StructField())
225
+ if !fieldValue.IsValid() {
226
+ continue
227
+ }
228
+
229
+ if !isEmpty(fieldValue) {
230
+ filteredErrors = append(filteredErrors, fe)
231
+ } else {
232
+ fmt.Printf("Ignoring validation error for empty field: %s\n", fe.Field())
233
+ }
234
+ }
235
+
236
+ if len(filteredErrors) > 0 {
237
+ return filteredErrors
238
+ }
239
+ return nil
240
  }
241
 
242
+ return err
243
+ }
244
+
245
+ // isEmpty checks if a value is nil or empty
246
+ func isEmpty(v reflect.Value) bool {
247
+ switch v.Kind() {
248
+ case reflect.String:
249
+ return v.Len() == 0
250
+ case reflect.Ptr, reflect.Interface:
251
+ return v.IsNil()
252
+ case reflect.Slice, reflect.Map, reflect.Array:
253
+ return v.Len() == 0
254
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
255
+ return v.Int() == 0
256
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
257
+ return v.Uint() == 0
258
+ case reflect.Float32, reflect.Float64:
259
+ return v.Float() == 0
260
+ case reflect.Bool:
261
+ return !v.Bool()
262
+ case reflect.Struct:
263
+ if t, ok := v.Interface().(time.Time); ok {
264
+ return t.IsZero()
265
+ }
266
+ // Consider non-time structs as non-empty
267
+ return false
268
+ default:
269
+ return false
270
+ }
271
  }
272
 
273
+ // createOptionRule remains the same as previous version
274
+ func (v *Validator) createOptionRule(key string) func(v10.FieldLevel) bool {
275
+ return func(fl v10.FieldLevel) bool {
276
+ field := fl.Field()
277
+ if isEmpty(field) {
278
+ return true
279
+ }
280
+
281
+ value := field.String()
282
+ validOptions, err := v.source.GetValidOptions(key)
283
+ if err != nil || validOptions == nil {
284
+ return true
285
+ }
286
+
287
+ for _, opt := range validOptions {
288
+ if opt == value {
289
+ return true
290
+ }
291
+ }
292
+
293
+ return false
294
+ }
295
+ }
296
+
297
+ func (v *Validator) validatePassword(fl v10.FieldLevel) bool {
298
  password := fl.Field().String()
299
  return len(password) >= 8 &&
300
  strings.ContainsAny(password, "abcdefghijklmnopqrstuvwxyz") &&
 
302
  strings.ContainsAny(password, "0123456789")
303
  }
304
 
305
+ func (v *Validator) validatePhoneNumber(fl v10.FieldLevel) bool {
306
+ phone := NormalizePhoneNumber(fl.Field().String())
307
  return strings.HasPrefix(phone, "+62")
308
  }
309
 
310
+ func NormalizePhoneNumber(input string) string {
 
 
 
 
 
 
 
311
  re := regexp.MustCompile(`[^0-9\+]`)
312
  input = re.ReplaceAllString(input, "")
313
 
314
+ switch {
315
+ case strings.HasPrefix(input, "0"):
316
+ return "+62" + input[1:]
317
+ case strings.HasPrefix(input, "62") && !strings.HasPrefix(input, "+62"):
318
+ return "+" + input
319
+ case strings.HasPrefix(input, "8"):
320
+ return "+62" + input
321
+ default:
322
+ return input
323
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
324
  }
space/space/repositories/partner_criteria_repository.go ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package repositories
2
+
3
+ import (
4
+ "context"
5
+
6
+ "api.qobiltu.id/models"
7
+ "gorm.io/gorm"
8
+ )
9
+
10
+ type PartnerCriteriaRepository interface {
11
+ SavePartnerCriteria(ctx context.Context, req *models.PartnerCriteria) (*models.PartnerCriteria, error)
12
+ GetPartnerCriteria(ctx context.Context, accountID int64) (*models.PartnerCriteria, error)
13
+ }
14
+
15
+ type partnerCriteriaRepository struct {
16
+ db *gorm.DB
17
+ }
18
+
19
+ func NewPartnerCriteriaRepository(db *gorm.DB) PartnerCriteriaRepository {
20
+ return &partnerCriteriaRepository{
21
+ db: db,
22
+ }
23
+ }
24
+
25
+ func (r *partnerCriteriaRepository) SavePartnerCriteria(ctx context.Context, req *models.PartnerCriteria) (*models.PartnerCriteria, error) {
26
+ if err := r.db.WithContext(ctx).Save(req).Error; err != nil {
27
+ return req, err
28
+ }
29
+ return req, nil
30
+ }
31
+
32
+ func (r *partnerCriteriaRepository) GetPartnerCriteria(ctx context.Context, accountID int64) (*models.PartnerCriteria, error) {
33
+ var partnerCriteria models.PartnerCriteria
34
+ if err := r.db.WithContext(ctx).Where("account_id = ?", accountID).First(&partnerCriteria).Error; err != nil {
35
+ return nil, err
36
+ }
37
+ return &partnerCriteria, nil
38
+ }
space/space/router/partner_criteria_route.go ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package router
2
+
3
+ import "api.qobiltu.id/middleware"
4
+
5
+ func (s *Server) PartnerCriteriaRoute() {
6
+ routerGroup := s.router.Group("/api/v1/partner-criteria").Use(middleware.AuthUser)
7
+ {
8
+ routerGroup.POST("", s.partnerCriteriaController.SavePartnerCriteria)
9
+ routerGroup.GET("", s.partnerCriteriaController.GetPartnerCriteria)
10
+ }
11
+ }
space/space/router/router.go CHANGED
@@ -19,5 +19,6 @@ func (s *Server) setupRoutes() {
19
  s.HealthCheckRoute()
20
  s.CVRoute()
21
  s.MarriageReadinessProfileRoute()
 
22
  s.StorageRoute()
23
  }
 
19
  s.HealthCheckRoute()
20
  s.CVRoute()
21
  s.MarriageReadinessProfileRoute()
22
+ s.PartnerCriteriaRoute()
23
  s.StorageRoute()
24
  }
space/space/router/server.go CHANGED
@@ -4,6 +4,7 @@ import (
4
  cv_controller "api.qobiltu.id/controller/cv"
5
  health_check_controller "api.qobiltu.id/controller/health_check"
6
  marriage_readiness_profile_controller "api.qobiltu.id/controller/marriage_readiness_profile"
 
7
  "github.com/gin-gonic/gin"
8
  )
9
 
@@ -12,12 +13,14 @@ type Server struct {
12
  healthCheckController health_check_controller.HealthCheckController
13
  cvController cv_controller.CVController
14
  marriageReadinessProfileController marriage_readiness_profile_controller.MarriageReadinessProfileController
 
15
  }
16
 
17
  func NewServer(
18
  healthCheckController health_check_controller.HealthCheckController,
19
  cvController cv_controller.CVController,
20
  marriageReadinessProfileController marriage_readiness_profile_controller.MarriageReadinessProfileController,
 
21
  ) (*Server, error) {
22
 
23
  router := gin.Default()
@@ -27,6 +30,7 @@ func NewServer(
27
  healthCheckController: healthCheckController,
28
  cvController: cvController,
29
  marriageReadinessProfileController: marriageReadinessProfileController,
 
30
  router: router,
31
  }
32
 
 
4
  cv_controller "api.qobiltu.id/controller/cv"
5
  health_check_controller "api.qobiltu.id/controller/health_check"
6
  marriage_readiness_profile_controller "api.qobiltu.id/controller/marriage_readiness_profile"
7
+ partner_criteria_controller "api.qobiltu.id/controller/partner_criteria"
8
  "github.com/gin-gonic/gin"
9
  )
10
 
 
13
  healthCheckController health_check_controller.HealthCheckController
14
  cvController cv_controller.CVController
15
  marriageReadinessProfileController marriage_readiness_profile_controller.MarriageReadinessProfileController
16
+ partnerCriteriaController partner_criteria_controller.PartnerCriteriaController
17
  }
18
 
19
  func NewServer(
20
  healthCheckController health_check_controller.HealthCheckController,
21
  cvController cv_controller.CVController,
22
  marriageReadinessProfileController marriage_readiness_profile_controller.MarriageReadinessProfileController,
23
+ partnerCriteriaController partner_criteria_controller.PartnerCriteriaController,
24
  ) (*Server, error) {
25
 
26
  router := gin.Default()
 
30
  healthCheckController: healthCheckController,
31
  cvController: cvController,
32
  marriageReadinessProfileController: marriageReadinessProfileController,
33
+ partnerCriteriaController: partnerCriteriaController,
34
  router: router,
35
  }
36
 
space/space/services/cv_service.go CHANGED
@@ -60,17 +60,19 @@ type CVService interface {
60
  type cvService struct {
61
  cvRepository repositories.CVRepository
62
  storage storage.Storage
 
63
  }
64
 
65
- func NewCVService(cvRepository repositories.CVRepository, storage storage.Storage) CVService {
66
  return &cvService{
67
  cvRepository: cvRepository,
68
  storage: storage,
 
69
  }
70
  }
71
 
72
  func (s *cvService) SaveAccountDetails(ctx context.Context, req *models.SaveAccountDetailsRequest) (*models.AccountDetails, error) {
73
- if err := validation.Validate(req); err != nil {
74
  return nil, response.HandleValidationError(err)
75
  }
76
 
@@ -124,8 +126,8 @@ func (s *cvService) SaveAccountDetails(ctx context.Context, req *models.SaveAcco
124
  utils.AssignIfNotNil(&accountDetails.LastJob, req.LastJob)
125
 
126
  if req.PhoneNumber != nil {
127
- sanitizedPhone := validation.SanitizePhoneNumber(*req.PhoneNumber)
128
- accountDetails.PhoneNumber = &sanitizedPhone
129
  }
130
 
131
  // Simpan ke database
@@ -146,7 +148,7 @@ func (s *cvService) GetAccountDetails(ctx context.Context, req *models.GetAccoun
146
  }
147
 
148
  func (s *cvService) SavePersonalityAndPreference(ctx context.Context, req *models.SavePersonalityAndPreferenceRequest) (*models.PersonalityAndPreferenceCV, error) {
149
- if err := validation.Validate(req); err != nil {
150
  return nil, response.HandleValidationError(err)
151
  }
152
 
@@ -196,7 +198,7 @@ func (s *cvService) GetPersonalityAndPreference(ctx context.Context, req *models
196
  }
197
 
198
  func (s *cvService) CreateFamilyMember(ctx context.Context, req *models.CreateFamilyMemberRequest) (*models.FamilyMemberCV, error) {
199
- if err := validation.Validate(req); err != nil {
200
  return nil, response.HandleValidationError(err)
201
  }
202
 
@@ -245,7 +247,7 @@ func (s *cvService) DeleteFamilyMember(ctx context.Context, req *models.DeleteFa
245
  }
246
 
247
  func (s *cvService) UpdateFamilyMember(ctx context.Context, req *models.UpdateFamilyMemberRequest) (*models.FamilyMemberCV, error) {
248
- if err := validation.Validate(req); err != nil {
249
  return nil, response.HandleValidationError(err)
250
  }
251
 
@@ -270,7 +272,7 @@ func (s *cvService) UpdateFamilyMember(ctx context.Context, req *models.UpdateFa
270
  }
271
 
272
  func (s *cvService) SavePhysicalAndHealth(ctx context.Context, req *models.SavePhysicalAndHealthRequest) (*models.PhysicalAndHealthCV, error) {
273
- if err := validation.Validate(req); err != nil {
274
  return nil, response.HandleValidationError(err)
275
  }
276
 
@@ -315,7 +317,7 @@ func (s *cvService) GetPhysicalAndHealth(ctx context.Context, req *models.GetPhy
315
  }
316
 
317
  func (s *cvService) SaveWorshipAndReligiousUnderstanding(ctx context.Context, req *models.SaveWorshipAndReligiousUnderstandingRequest) (*models.WorshipAndReligiousUnderstandingCV, error) {
318
- if err := validation.Validate(req); err != nil {
319
  return nil, response.HandleValidationError(err)
320
  }
321
 
@@ -339,6 +341,7 @@ func (s *cvService) SaveWorshipAndReligiousUnderstanding(ctx context.Context, re
339
  utils.AssignIfNotNil(&worshipAndReligiousUnderstanding.DhuhaPrayer, req.DhuhaPrayer)
340
  utils.AssignIfNotNil(&worshipAndReligiousUnderstanding.QuranMemorization, req.QuranMemorization)
341
  utils.AssignIfNotNil(&worshipAndReligiousUnderstanding.QuranReadingAbility, req.QuranReadingAbility)
 
342
  utils.AssignIfNotNil(&worshipAndReligiousUnderstanding.DaudFasting, req.DaudFasting)
343
  utils.AssignIfNotNil(&worshipAndReligiousUnderstanding.AyyamulBidhFasting, req.AyyamulBidhFasting)
344
  utils.AssignIfNotNil(&worshipAndReligiousUnderstanding.HajjOrUmrah, req.HajjOrUmrah)
@@ -367,7 +370,7 @@ func (s *cvService) GetWorshipAndReligiousUnderstanding(ctx context.Context, req
367
  }
368
 
369
  func (s *cvService) CreateEducation(ctx context.Context, req *models.CreateEducationRequest) (*models.EducationCV, error) {
370
- if err := validation.Validate(req); err != nil {
371
  return nil, response.HandleValidationError(err)
372
  }
373
 
@@ -389,7 +392,7 @@ func (s *cvService) CreateEducation(ctx context.Context, req *models.CreateEduca
389
  }
390
 
391
  func (s *cvService) UpdateEducation(ctx context.Context, req *models.UpdateEducationRequest) (*models.EducationCV, error) {
392
- if err := validation.Validate(req); err != nil {
393
  return nil, response.HandleValidationError(err)
394
  }
395
 
@@ -428,7 +431,7 @@ func (s *cvService) DeleteEducation(ctx context.Context, req *models.DeleteEduca
428
  }
429
 
430
  func (s *cvService) CreateJob(ctx context.Context, req *models.CreateJobRequest) (*models.JobCV, error) {
431
- if err := validation.Validate(req); err != nil {
432
  return nil, response.HandleValidationError(err)
433
  }
434
 
@@ -448,7 +451,7 @@ func (s *cvService) CreateJob(ctx context.Context, req *models.CreateJobRequest)
448
  }
449
 
450
  func (s *cvService) UpdateJob(ctx context.Context, req *models.UpdateJobRequest) (*models.JobCV, error) {
451
- if err := validation.Validate(req); err != nil {
452
  return nil, response.HandleValidationError(err)
453
  }
454
 
 
60
  type cvService struct {
61
  cvRepository repositories.CVRepository
62
  storage storage.Storage
63
+ validator *validation.Validator
64
  }
65
 
66
+ func NewCVService(cvRepository repositories.CVRepository, storage storage.Storage, validator *validation.Validator) CVService {
67
  return &cvService{
68
  cvRepository: cvRepository,
69
  storage: storage,
70
+ validator: validator,
71
  }
72
  }
73
 
74
  func (s *cvService) SaveAccountDetails(ctx context.Context, req *models.SaveAccountDetailsRequest) (*models.AccountDetails, error) {
75
+ if err := s.validator.Validate(req); err != nil {
76
  return nil, response.HandleValidationError(err)
77
  }
78
 
 
126
  utils.AssignIfNotNil(&accountDetails.LastJob, req.LastJob)
127
 
128
  if req.PhoneNumber != nil {
129
+ normalizedPhoneNumber := validation.NormalizePhoneNumber(*req.PhoneNumber)
130
+ accountDetails.PhoneNumber = &normalizedPhoneNumber
131
  }
132
 
133
  // Simpan ke database
 
148
  }
149
 
150
  func (s *cvService) SavePersonalityAndPreference(ctx context.Context, req *models.SavePersonalityAndPreferenceRequest) (*models.PersonalityAndPreferenceCV, error) {
151
+ if err := s.validator.Validate(req); err != nil {
152
  return nil, response.HandleValidationError(err)
153
  }
154
 
 
198
  }
199
 
200
  func (s *cvService) CreateFamilyMember(ctx context.Context, req *models.CreateFamilyMemberRequest) (*models.FamilyMemberCV, error) {
201
+ if err := s.validator.Validate(req); err != nil {
202
  return nil, response.HandleValidationError(err)
203
  }
204
 
 
247
  }
248
 
249
  func (s *cvService) UpdateFamilyMember(ctx context.Context, req *models.UpdateFamilyMemberRequest) (*models.FamilyMemberCV, error) {
250
+ if err := s.validator.Validate(req); err != nil {
251
  return nil, response.HandleValidationError(err)
252
  }
253
 
 
272
  }
273
 
274
  func (s *cvService) SavePhysicalAndHealth(ctx context.Context, req *models.SavePhysicalAndHealthRequest) (*models.PhysicalAndHealthCV, error) {
275
+ if err := s.validator.Validate(req); err != nil {
276
  return nil, response.HandleValidationError(err)
277
  }
278
 
 
317
  }
318
 
319
  func (s *cvService) SaveWorshipAndReligiousUnderstanding(ctx context.Context, req *models.SaveWorshipAndReligiousUnderstandingRequest) (*models.WorshipAndReligiousUnderstandingCV, error) {
320
+ if err := s.validator.Validate(req); err != nil {
321
  return nil, response.HandleValidationError(err)
322
  }
323
 
 
341
  utils.AssignIfNotNil(&worshipAndReligiousUnderstanding.DhuhaPrayer, req.DhuhaPrayer)
342
  utils.AssignIfNotNil(&worshipAndReligiousUnderstanding.QuranMemorization, req.QuranMemorization)
343
  utils.AssignIfNotNil(&worshipAndReligiousUnderstanding.QuranReadingAbility, req.QuranReadingAbility)
344
+ utils.AssignIfNotNil(&worshipAndReligiousUnderstanding.WeeklyReligiousStudyFrequency, req.WeeklyReligiousStudyFrequency)
345
  utils.AssignIfNotNil(&worshipAndReligiousUnderstanding.DaudFasting, req.DaudFasting)
346
  utils.AssignIfNotNil(&worshipAndReligiousUnderstanding.AyyamulBidhFasting, req.AyyamulBidhFasting)
347
  utils.AssignIfNotNil(&worshipAndReligiousUnderstanding.HajjOrUmrah, req.HajjOrUmrah)
 
370
  }
371
 
372
  func (s *cvService) CreateEducation(ctx context.Context, req *models.CreateEducationRequest) (*models.EducationCV, error) {
373
+ if err := s.validator.Validate(req); err != nil {
374
  return nil, response.HandleValidationError(err)
375
  }
376
 
 
392
  }
393
 
394
  func (s *cvService) UpdateEducation(ctx context.Context, req *models.UpdateEducationRequest) (*models.EducationCV, error) {
395
+ if err := s.validator.Validate(req); err != nil {
396
  return nil, response.HandleValidationError(err)
397
  }
398
 
 
431
  }
432
 
433
  func (s *cvService) CreateJob(ctx context.Context, req *models.CreateJobRequest) (*models.JobCV, error) {
434
+ if err := s.validator.Validate(req); err != nil {
435
  return nil, response.HandleValidationError(err)
436
  }
437
 
 
451
  }
452
 
453
  func (s *cvService) UpdateJob(ctx context.Context, req *models.UpdateJobRequest) (*models.JobCV, error) {
454
+ if err := s.validator.Validate(req); err != nil {
455
  return nil, response.HandleValidationError(err)
456
  }
457
 
space/space/services/partner_criteria_service.go ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package services
2
+
3
+ import (
4
+ "context"
5
+ "errors"
6
+
7
+ "api.qobiltu.id/models"
8
+ "api.qobiltu.id/pkg/validation"
9
+ "api.qobiltu.id/repositories"
10
+ "api.qobiltu.id/response"
11
+ "api.qobiltu.id/utils"
12
+ "gorm.io/gorm"
13
+ )
14
+
15
+ type PartnerCriteriaService interface {
16
+ SavePartnerCriteria(ctx context.Context, req *models.SavePartnerCriteriaRequest) (*models.PartnerCriteria, error)
17
+ GetPartnerCriteria(ctx context.Context, req *models.GetPartnerCriteriaRequest) (*models.PartnerCriteria, error)
18
+ }
19
+
20
+ type partnerCriteriaService struct {
21
+ partnerCriteriaRepository repositories.PartnerCriteriaRepository
22
+ }
23
+
24
+ func NewPartnerCriteriaService(partnerCriteriaRepository repositories.PartnerCriteriaRepository) PartnerCriteriaService {
25
+ return &partnerCriteriaService{partnerCriteriaRepository: partnerCriteriaRepository}
26
+ }
27
+
28
+ func (s *partnerCriteriaService) SavePartnerCriteria(ctx context.Context, req *models.SavePartnerCriteriaRequest) (*models.PartnerCriteria, error) {
29
+ if err := validation.Validate(req); err != nil {
30
+ return nil, response.HandleValidationError(err)
31
+ }
32
+
33
+ partnerCriteria, err := s.partnerCriteriaRepository.GetPartnerCriteria(ctx, req.AccountID)
34
+ if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
35
+ return nil, response.HandleGormError(err, "Internal Server Error")
36
+ }
37
+
38
+ if partnerCriteria == nil {
39
+ partnerCriteria = &models.PartnerCriteria{}
40
+ }
41
+
42
+ partnerCriteria.AccountID = req.AccountID
43
+
44
+ utils.AssignIfNotNil(&partnerCriteria.ExpectedAgeLimit, req.ExpectedAgeLimit)
45
+ utils.AssignIfNotNil(&partnerCriteria.ExpectedDomicile, req.ExpectedDomicile)
46
+ utils.AssignIfNotNil(&partnerCriteria.AcceptedMaritalStatus, req.AcceptedMaritalStatus)
47
+ utils.AssignIfNotNil(&partnerCriteria.PartnerFamilyReligion, req.PartnerFamilyReligion)
48
+ utils.AssignIfNotNil(&partnerCriteria.PartnerWeeklyReligiousStudyFrequency, req.PartnerWeeklyReligiousStudyFrequency)
49
+ utils.AssignIfNotNil(&partnerCriteria.PartnerMonthlySpendingEstimate, req.PartnerMonthlySpendingEstimate)
50
+ utils.AssignIfNotNil(&partnerCriteria.PartnerCanCook, req.PartnerCanCook)
51
+
52
+ utils.AssignIfNotNil(&partnerCriteria.ExpectedBodyShapes, req.ExpectedBodyShapes)
53
+ utils.AssignIfNotNil(&partnerCriteria.ExpectedSkinColors, req.ExpectedSkinColors)
54
+ utils.AssignIfNotNil(&partnerCriteria.ExpectedHairTypes, req.ExpectedHairTypes)
55
+ utils.AssignIfNotNil(&partnerCriteria.ExpectedHairThickness, req.ExpectedHairThickness)
56
+ utils.AssignIfNotNil(&partnerCriteria.ExpectedHeight, req.ExpectedHeight)
57
+
58
+ utils.AssignIfNotNil(&partnerCriteria.ExpectedPartnerIncome, req.ExpectedPartnerIncome)
59
+ utils.AssignIfNotNil(&partnerCriteria.ExpectedIncomeSources, req.ExpectedIncomeSources)
60
+ utils.AssignIfNotNil(&partnerCriteria.ExpectedLastEducation, req.ExpectedLastEducation)
61
+ utils.AssignIfNotNil(&partnerCriteria.ExpectedJobType, req.ExpectedJobType)
62
+
63
+ res, err := s.partnerCriteriaRepository.SavePartnerCriteria(ctx, partnerCriteria)
64
+ if err != nil {
65
+ return nil, response.HandleGormError(err, "Internal Server Error")
66
+ }
67
+
68
+ return res, nil
69
+ }
70
+
71
+ func (s *partnerCriteriaService) GetPartnerCriteria(ctx context.Context, req *models.GetPartnerCriteriaRequest) (*models.PartnerCriteria, error) {
72
+ res, err := s.partnerCriteriaRepository.GetPartnerCriteria(ctx, req.AccountID)
73
+ if err != nil {
74
+ return nil, response.HandleGormError(err, "Internal Server Error")
75
+ }
76
+ return res, nil
77
+ }
space/space/space/config/database_connection_config.go CHANGED
@@ -3,7 +3,6 @@ package config
3
  import (
4
  "fmt"
5
  "log"
6
- "log/slog"
7
  "os"
8
 
9
  "gorm.io/driver/postgres"
@@ -78,11 +77,23 @@ func AutoMigrateAll(db *gorm.DB) {
78
  &models.JobCV{},
79
  &models.AchievementCV{},
80
  &models.MarriageReadinessProfile{},
 
81
  )
82
 
83
  if err != nil {
84
  log.Fatal(err)
85
  }
86
 
87
- slog.Info("Auto-migration completed successfully")
 
 
 
 
 
 
 
 
 
 
 
88
  }
 
3
  import (
4
  "fmt"
5
  "log"
 
6
  "os"
7
 
8
  "gorm.io/driver/postgres"
 
77
  &models.JobCV{},
78
  &models.AchievementCV{},
79
  &models.MarriageReadinessProfile{},
80
+ &models.PartnerCriteria{},
81
  )
82
 
83
  if err != nil {
84
  log.Fatal(err)
85
  }
86
 
87
+ sequences := []string{
88
+ `CREATE SEQUENCE IF NOT EXISTS seq_ikh_counter START 1 INCREMENT 1 MINVALUE 1;`,
89
+ `CREATE SEQUENCE IF NOT EXISTS seq_akh_counter START 1 INCREMENT 1 MINVALUE 1;`,
90
+ }
91
+
92
+ for _, seq := range sequences {
93
+ if err := db.Exec(seq).Error; err != nil {
94
+ fmt.Printf("Gagal membuat sequence: %v", err)
95
+ }
96
+ }
97
+
98
+ fmt.Println("Auto-migration sequence check completed successfully")
99
  }
space/space/space/models/sequence.go ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package models
2
+
3
+ import (
4
+ "fmt"
5
+ "strings"
6
+ )
7
+
8
+ const (
9
+ SeqIkhCounter = "seq_ikh_counter"
10
+ SeqAkhCounter = "seq_akh_counter"
11
+ )
12
+
13
+ func GetSequenceName(gender string) string {
14
+ if strings.ToLower(gender) == "laki-laki" {
15
+ return SeqIkhCounter
16
+ }
17
+ return SeqAkhCounter
18
+ }
19
+
20
+ func BuildInitialName(gender string, number int64) string {
21
+ if strings.ToLower(gender) == "laki-laki" {
22
+ return fmt.Sprintf("IKH_%d", number)
23
+ }
24
+ return fmt.Sprintf("AKH_%d", number)
25
+ }
space/space/space/repositories/account_repository.go CHANGED
@@ -85,3 +85,21 @@ func UpdateAccountDetails(accountDetails models.AccountDetails) Repository[model
85
  repo.Result = accountDetails
86
  return *repo
87
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  repo.Result = accountDetails
86
  return *repo
87
  }
88
+
89
+ func GetNextInitialNameNumber(gender string) Repository[string, int] {
90
+ repo := Construct[string, int](gender)
91
+ sequenceName := models.GetSequenceName(gender)
92
+ repo.Transaction.Raw("SELECT nextval('" + sequenceName + "')").Scan(&repo.Result)
93
+ return *repo
94
+ }
95
+
96
+ func GetAccountDetailsByAccountID(accountID uint) Repository[models.AccountDetails, models.AccountDetails] {
97
+ repo := Construct[models.AccountDetails, models.AccountDetails](
98
+ models.AccountDetails{AccountID: accountID},
99
+ )
100
+ repo.Transactions(
101
+ WhereGivenConstructor[models.AccountDetails, models.AccountDetails],
102
+ Find[models.AccountDetails, models.AccountDetails],
103
+ )
104
+ return *repo
105
+ }
space/space/space/repositories/cv_repository.go CHANGED
@@ -1,243 +1,259 @@
1
  package repositories
2
 
3
  import (
4
- "api.qobiltu.id/models"
5
- "context"
6
- "gorm.io/gorm"
 
 
7
  )
8
 
9
  type CVRepository interface {
10
- SaveAccountDetails(ctx context.Context, req *models.AccountDetails) (*models.AccountDetails, error)
11
- GetAccountDetailsByAccountID(ctx context.Context, accountID int64) (*models.AccountDetails, error)
12
-
13
- SavePersonalityAndPreference(ctx context.Context, req *models.PersonalityAndPreferenceCV) (*models.PersonalityAndPreferenceCV, error)
14
- GetPersonalityAndPreferenceByAccountID(ctx context.Context, accountID int64) (*models.PersonalityAndPreferenceCV, error)
15
-
16
- SaveFamilyMember(ctx context.Context, req *models.FamilyMemberCV) (*models.FamilyMemberCV, error)
17
- ListFamilyMember(ctx context.Context, accountID int64) ([]models.FamilyMemberCV, error)
18
- GetFamilyMember(ctx context.Context, id int64) (*models.FamilyMemberCV, error)
19
- DeleteFamilyMember(ctx context.Context, id int64) error
20
-
21
- SavePhysicalAndHealth(ctx context.Context, req *models.PhysicalAndHealthCV) (*models.PhysicalAndHealthCV, error)
22
- GetPhysicalAndHealthByAccountID(ctx context.Context, accountID int64) (*models.PhysicalAndHealthCV, error)
23
-
24
- SaveWorshipAndReligiousUnderstanding(ctx context.Context, req *models.WorshipAndReligiousUnderstandingCV) (*models.WorshipAndReligiousUnderstandingCV, error)
25
- GetWorshipAndReligiousUnderstandingByAccountID(ctx context.Context, accountID int64) (*models.WorshipAndReligiousUnderstandingCV, error)
26
-
27
- SaveEducation(ctx context.Context, req *models.EducationCV) (*models.EducationCV, error)
28
- ListEducation(ctx context.Context, accountID int64) ([]models.EducationCV, error)
29
- GetEducation(ctx context.Context, id int64) (*models.EducationCV, error)
30
- DeleteEducation(ctx context.Context, id int64) error
31
-
32
- SaveJob(ctx context.Context, req *models.JobCV) (*models.JobCV, error)
33
- ListJob(ctx context.Context, accountID int64) ([]models.JobCV, error)
34
- GetJob(ctx context.Context, id int64) (*models.JobCV, error)
35
- DeleteJob(ctx context.Context, id int64) error
36
-
37
- SaveAchievement(ctx context.Context, req *models.AchievementCV) (*models.AchievementCV, error)
38
- ListAchievement(ctx context.Context, accountID int64) ([]models.AchievementCV, error)
39
- GetAchievement(ctx context.Context, id int64) (*models.AchievementCV, error)
40
- DeleteAchievement(ctx context.Context, id int64) error
 
41
  }
42
 
43
  type cvRepository struct {
44
- db *gorm.DB
45
  }
46
 
47
  func NewCVRepository(db *gorm.DB) CVRepository {
48
- return &cvRepository{
49
- db: db,
50
- }
51
  }
52
 
53
  func (r *cvRepository) SaveAccountDetails(ctx context.Context, req *models.AccountDetails) (*models.AccountDetails, error) {
54
- if err := r.db.WithContext(ctx).Save(req).Error; err != nil {
55
- return req, err
56
- }
57
- return req, nil
58
  }
59
 
60
  func (r *cvRepository) GetAccountDetailsByAccountID(ctx context.Context, accountID int64) (*models.AccountDetails, error) {
61
- var accountDetails models.AccountDetails
62
- if err := r.db.WithContext(ctx).Where("account_id = ?", accountID).First(&accountDetails).Error; err != nil {
63
- return nil, err
64
- }
65
- return &accountDetails, nil
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  }
67
 
68
  func (r *cvRepository) SavePersonalityAndPreference(ctx context.Context, req *models.PersonalityAndPreferenceCV) (*models.PersonalityAndPreferenceCV, error) {
69
- if err := r.db.WithContext(ctx).Save(req).Error; err != nil {
70
- return req, err
71
- }
72
- return req, nil
73
  }
74
 
75
  func (r *cvRepository) GetPersonalityAndPreferenceByAccountID(ctx context.Context, accountID int64) (*models.PersonalityAndPreferenceCV, error) {
76
- var personalityAndPreference models.PersonalityAndPreferenceCV
77
- if err := r.db.WithContext(ctx).Where("account_id = ?", accountID).First(&personalityAndPreference).Error; err != nil {
78
- return nil, err
79
- }
80
- return &personalityAndPreference, nil
81
  }
82
 
83
  func (r *cvRepository) SaveFamilyMember(ctx context.Context, req *models.FamilyMemberCV) (*models.FamilyMemberCV, error) {
84
- if err := r.db.WithContext(ctx).Save(req).Error; err != nil {
85
- return req, err
86
- }
87
- return req, nil
88
  }
89
 
90
  func (r *cvRepository) GetFamilyMember(ctx context.Context, id int64) (*models.FamilyMemberCV, error) {
91
- var familyMember models.FamilyMemberCV
92
- if err := r.db.WithContext(ctx).Where("id = ?", id).First(&familyMember).Error; err != nil {
93
- return nil, err
94
- }
95
- return &familyMember, nil
96
  }
97
 
98
  func (r *cvRepository) DeleteFamilyMember(ctx context.Context, id int64) error {
99
- if err := r.db.WithContext(ctx).Where("id = ?", id).Delete(&models.FamilyMemberCV{}).Error; err != nil {
100
- return err
101
- }
102
- return nil
103
  }
104
 
105
  func (r *cvRepository) ListFamilyMember(ctx context.Context, accountID int64) ([]models.FamilyMemberCV, error) {
106
- familyMembers := make([]models.FamilyMemberCV, 0)
107
- if err := r.db.WithContext(ctx).Where("account_id = ?", accountID).Find(&familyMembers).Error; err != nil {
108
- return nil, err
109
- }
110
- return familyMembers, nil
111
  }
112
 
113
  func (r *cvRepository) SavePhysicalAndHealth(ctx context.Context, req *models.PhysicalAndHealthCV) (*models.PhysicalAndHealthCV, error) {
114
- if err := r.db.WithContext(ctx).Save(req).Error; err != nil {
115
- return req, err
116
- }
117
- return req, nil
118
  }
119
 
120
  func (r *cvRepository) GetPhysicalAndHealthByAccountID(ctx context.Context, accountID int64) (*models.PhysicalAndHealthCV, error) {
121
- var physicalAndHealth models.PhysicalAndHealthCV
122
- if err := r.db.WithContext(ctx).Where("account_id = ?", accountID).First(&physicalAndHealth).Error; err != nil {
123
- return nil, err
124
- }
125
- return &physicalAndHealth, nil
126
  }
127
 
128
  func (r *cvRepository) SaveWorshipAndReligiousUnderstanding(ctx context.Context, req *models.WorshipAndReligiousUnderstandingCV) (*models.WorshipAndReligiousUnderstandingCV, error) {
129
- if err := r.db.WithContext(ctx).Save(req).Error; err != nil {
130
- return req, err
131
- }
132
- return req, nil
133
  }
134
 
135
  func (r *cvRepository) GetWorshipAndReligiousUnderstandingByAccountID(ctx context.Context, accountID int64) (*models.WorshipAndReligiousUnderstandingCV, error) {
136
- var worshipAndReligiousUnderstanding models.WorshipAndReligiousUnderstandingCV
137
- if err := r.db.WithContext(ctx).Where("account_id = ?", accountID).First(&worshipAndReligiousUnderstanding).Error; err != nil {
138
- return nil, err
139
- }
140
- return &worshipAndReligiousUnderstanding, nil
141
  }
142
 
143
  // SaveEducation menyimpan atau memperbarui data pendidikan ke database
144
  func (r *cvRepository) SaveEducation(ctx context.Context, req *models.EducationCV) (*models.EducationCV, error) {
145
- if err := r.db.WithContext(ctx).Save(req).Error; err != nil {
146
- return req, err
147
- }
148
- return req, nil
149
  }
150
 
151
  // ListEducation mengambil daftar data pendidikan berdasarkan account_id
152
  func (r *cvRepository) ListEducation(ctx context.Context, accountID int64) ([]models.EducationCV, error) {
153
- var educations []models.EducationCV
154
- if err := r.db.WithContext(ctx).Where("account_id = ?", accountID).Find(&educations).Error; err != nil {
155
- return nil, err
156
- }
157
- return educations, nil
158
  }
159
 
160
  // GetEducation mengambil satu data pendidikan berdasarkan id
161
  func (r *cvRepository) GetEducation(ctx context.Context, id int64) (*models.EducationCV, error) {
162
- var education models.EducationCV
163
- if err := r.db.WithContext(ctx).Where("id = ?", id).First(&education).Error; err != nil {
164
- return nil, err
165
- }
166
- return &education, nil
167
  }
168
 
169
  // DeleteEducation menghapus data pendidikan berdasarkan id
170
  func (r *cvRepository) DeleteEducation(ctx context.Context, id int64) error {
171
- if err := r.db.WithContext(ctx).Where("id = ?", id).Delete(&models.EducationCV{}).Error; err != nil {
172
- return err
173
- }
174
- return nil
175
  }
176
 
177
  // SaveJob menyimpan atau memperbarui data pekerjaan ke database
178
  func (r *cvRepository) SaveJob(ctx context.Context, req *models.JobCV) (*models.JobCV, error) {
179
- if err := r.db.WithContext(ctx).Save(req).Error; err != nil {
180
- return req, err
181
- }
182
- return req, nil
183
  }
184
 
185
  // ListJob mengambil daftar data pekerjaan berdasarkan account_id
186
  func (r *cvRepository) ListJob(ctx context.Context, accountID int64) ([]models.JobCV, error) {
187
- var jobs []models.JobCV
188
- if err := r.db.WithContext(ctx).Where("account_id = ?", accountID).Find(&jobs).Error; err != nil {
189
- return nil, err
190
- }
191
- return jobs, nil
192
  }
193
 
194
  // GetJob mengambil satu data pekerjaan berdasarkan id
195
  func (r *cvRepository) GetJob(ctx context.Context, id int64) (*models.JobCV, error) {
196
- var job models.JobCV
197
- if err := r.db.WithContext(ctx).Where("id = ?", id).First(&job).Error; err != nil {
198
- return nil, err
199
- }
200
- return &job, nil
201
  }
202
 
203
  // DeleteJob menghapus data pekerjaan berdasarkan id
204
  func (r *cvRepository) DeleteJob(ctx context.Context, id int64) error {
205
- if err := r.db.WithContext(ctx).Where("id = ?", id).Delete(&models.JobCV{}).Error; err != nil {
206
- return err
207
- }
208
- return nil
209
  }
210
 
211
  // SaveAchievement menyimpan atau memperbarui data prestasi ke database
212
  func (r *cvRepository) SaveAchievement(ctx context.Context, req *models.AchievementCV) (*models.AchievementCV, error) {
213
- if err := r.db.WithContext(ctx).Save(req).Error; err != nil {
214
- return req, err
215
- }
216
- return req, nil
217
  }
218
 
219
  // ListAchievement mengambil daftar data prestasi berdasarkan account_id
220
  func (r *cvRepository) ListAchievement(ctx context.Context, accountID int64) ([]models.AchievementCV, error) {
221
- var achievements []models.AchievementCV
222
- if err := r.db.WithContext(ctx).Where("account_id = ?", accountID).Find(&achievements).Error; err != nil {
223
- return nil, err
224
- }
225
- return achievements, nil
226
  }
227
 
228
  // GetAchievement mengambil satu data prestasi berdasarkan id
229
  func (r *cvRepository) GetAchievement(ctx context.Context, id int64) (*models.AchievementCV, error) {
230
- var achievement models.AchievementCV
231
- if err := r.db.WithContext(ctx).Where("id = ?", id).First(&achievement).Error; err != nil {
232
- return nil, err
233
- }
234
- return &achievement, nil
235
  }
236
 
237
  // DeleteAchievement menghapus data prestasi berdasarkan id
238
  func (r *cvRepository) DeleteAchievement(ctx context.Context, id int64) error {
239
- if err := r.db.WithContext(ctx).Where("id = ?", id).Delete(&models.AchievementCV{}).Error; err != nil {
240
- return err
241
- }
242
- return nil
243
  }
 
1
  package repositories
2
 
3
  import (
4
+ "context"
5
+ "fmt"
6
+
7
+ "api.qobiltu.id/models"
8
+ "gorm.io/gorm"
9
  )
10
 
11
  type CVRepository interface {
12
+ SaveAccountDetails(ctx context.Context, req *models.AccountDetails) (*models.AccountDetails, error)
13
+ GetAccountDetailsByAccountID(ctx context.Context, accountID int64) (*models.AccountDetails, error)
14
+ GetNextInitialNameNumber(ctx context.Context, gender string) (int64, error)
15
+
16
+ SavePersonalityAndPreference(ctx context.Context, req *models.PersonalityAndPreferenceCV) (*models.PersonalityAndPreferenceCV, error)
17
+ GetPersonalityAndPreferenceByAccountID(ctx context.Context, accountID int64) (*models.PersonalityAndPreferenceCV, error)
18
+
19
+ SaveFamilyMember(ctx context.Context, req *models.FamilyMemberCV) (*models.FamilyMemberCV, error)
20
+ ListFamilyMember(ctx context.Context, accountID int64) ([]models.FamilyMemberCV, error)
21
+ GetFamilyMember(ctx context.Context, id int64) (*models.FamilyMemberCV, error)
22
+ DeleteFamilyMember(ctx context.Context, id int64) error
23
+
24
+ SavePhysicalAndHealth(ctx context.Context, req *models.PhysicalAndHealthCV) (*models.PhysicalAndHealthCV, error)
25
+ GetPhysicalAndHealthByAccountID(ctx context.Context, accountID int64) (*models.PhysicalAndHealthCV, error)
26
+
27
+ SaveWorshipAndReligiousUnderstanding(ctx context.Context, req *models.WorshipAndReligiousUnderstandingCV) (*models.WorshipAndReligiousUnderstandingCV, error)
28
+ GetWorshipAndReligiousUnderstandingByAccountID(ctx context.Context, accountID int64) (*models.WorshipAndReligiousUnderstandingCV, error)
29
+
30
+ SaveEducation(ctx context.Context, req *models.EducationCV) (*models.EducationCV, error)
31
+ ListEducation(ctx context.Context, accountID int64) ([]models.EducationCV, error)
32
+ GetEducation(ctx context.Context, id int64) (*models.EducationCV, error)
33
+ DeleteEducation(ctx context.Context, id int64) error
34
+
35
+ SaveJob(ctx context.Context, req *models.JobCV) (*models.JobCV, error)
36
+ ListJob(ctx context.Context, accountID int64) ([]models.JobCV, error)
37
+ GetJob(ctx context.Context, id int64) (*models.JobCV, error)
38
+ DeleteJob(ctx context.Context, id int64) error
39
+
40
+ SaveAchievement(ctx context.Context, req *models.AchievementCV) (*models.AchievementCV, error)
41
+ ListAchievement(ctx context.Context, accountID int64) ([]models.AchievementCV, error)
42
+ GetAchievement(ctx context.Context, id int64) (*models.AchievementCV, error)
43
+ DeleteAchievement(ctx context.Context, id int64) error
44
  }
45
 
46
  type cvRepository struct {
47
+ db *gorm.DB
48
  }
49
 
50
  func NewCVRepository(db *gorm.DB) CVRepository {
51
+ return &cvRepository{
52
+ db: db,
53
+ }
54
  }
55
 
56
  func (r *cvRepository) SaveAccountDetails(ctx context.Context, req *models.AccountDetails) (*models.AccountDetails, error) {
57
+ if err := r.db.WithContext(ctx).Save(req).Error; err != nil {
58
+ return req, err
59
+ }
60
+ return req, nil
61
  }
62
 
63
  func (r *cvRepository) GetAccountDetailsByAccountID(ctx context.Context, accountID int64) (*models.AccountDetails, error) {
64
+ var accountDetails models.AccountDetails
65
+ if err := r.db.WithContext(ctx).Where("account_id = ?", accountID).First(&accountDetails).Error; err != nil {
66
+ return nil, err
67
+ }
68
+ return &accountDetails, nil
69
+ }
70
+
71
+ func (r *cvRepository) GetNextInitialNameNumber(ctx context.Context, gender string) (int64, error) {
72
+ sequenceName := models.GetSequenceName(gender)
73
+
74
+ var number int64
75
+ query := fmt.Sprintf("SELECT nextval('%s')", sequenceName)
76
+ err := r.db.WithContext(ctx).Raw(query).Scan(&number).Error
77
+ if err != nil {
78
+ return 0, err
79
+ }
80
+
81
+ return number, nil
82
  }
83
 
84
  func (r *cvRepository) SavePersonalityAndPreference(ctx context.Context, req *models.PersonalityAndPreferenceCV) (*models.PersonalityAndPreferenceCV, error) {
85
+ if err := r.db.WithContext(ctx).Save(req).Error; err != nil {
86
+ return req, err
87
+ }
88
+ return req, nil
89
  }
90
 
91
  func (r *cvRepository) GetPersonalityAndPreferenceByAccountID(ctx context.Context, accountID int64) (*models.PersonalityAndPreferenceCV, error) {
92
+ var personalityAndPreference models.PersonalityAndPreferenceCV
93
+ if err := r.db.WithContext(ctx).Where("account_id = ?", accountID).First(&personalityAndPreference).Error; err != nil {
94
+ return nil, err
95
+ }
96
+ return &personalityAndPreference, nil
97
  }
98
 
99
  func (r *cvRepository) SaveFamilyMember(ctx context.Context, req *models.FamilyMemberCV) (*models.FamilyMemberCV, error) {
100
+ if err := r.db.WithContext(ctx).Save(req).Error; err != nil {
101
+ return req, err
102
+ }
103
+ return req, nil
104
  }
105
 
106
  func (r *cvRepository) GetFamilyMember(ctx context.Context, id int64) (*models.FamilyMemberCV, error) {
107
+ var familyMember models.FamilyMemberCV
108
+ if err := r.db.WithContext(ctx).Where("id = ?", id).First(&familyMember).Error; err != nil {
109
+ return nil, err
110
+ }
111
+ return &familyMember, nil
112
  }
113
 
114
  func (r *cvRepository) DeleteFamilyMember(ctx context.Context, id int64) error {
115
+ if err := r.db.WithContext(ctx).Where("id = ?", id).Delete(&models.FamilyMemberCV{}).Error; err != nil {
116
+ return err
117
+ }
118
+ return nil
119
  }
120
 
121
  func (r *cvRepository) ListFamilyMember(ctx context.Context, accountID int64) ([]models.FamilyMemberCV, error) {
122
+ familyMembers := make([]models.FamilyMemberCV, 0)
123
+ if err := r.db.WithContext(ctx).Where("account_id = ?", accountID).Find(&familyMembers).Error; err != nil {
124
+ return nil, err
125
+ }
126
+ return familyMembers, nil
127
  }
128
 
129
  func (r *cvRepository) SavePhysicalAndHealth(ctx context.Context, req *models.PhysicalAndHealthCV) (*models.PhysicalAndHealthCV, error) {
130
+ if err := r.db.WithContext(ctx).Save(req).Error; err != nil {
131
+ return req, err
132
+ }
133
+ return req, nil
134
  }
135
 
136
  func (r *cvRepository) GetPhysicalAndHealthByAccountID(ctx context.Context, accountID int64) (*models.PhysicalAndHealthCV, error) {
137
+ var physicalAndHealth models.PhysicalAndHealthCV
138
+ if err := r.db.WithContext(ctx).Where("account_id = ?", accountID).First(&physicalAndHealth).Error; err != nil {
139
+ return nil, err
140
+ }
141
+ return &physicalAndHealth, nil
142
  }
143
 
144
  func (r *cvRepository) SaveWorshipAndReligiousUnderstanding(ctx context.Context, req *models.WorshipAndReligiousUnderstandingCV) (*models.WorshipAndReligiousUnderstandingCV, error) {
145
+ if err := r.db.WithContext(ctx).Save(req).Error; err != nil {
146
+ return req, err
147
+ }
148
+ return req, nil
149
  }
150
 
151
  func (r *cvRepository) GetWorshipAndReligiousUnderstandingByAccountID(ctx context.Context, accountID int64) (*models.WorshipAndReligiousUnderstandingCV, error) {
152
+ var worshipAndReligiousUnderstanding models.WorshipAndReligiousUnderstandingCV
153
+ if err := r.db.WithContext(ctx).Where("account_id = ?", accountID).First(&worshipAndReligiousUnderstanding).Error; err != nil {
154
+ return nil, err
155
+ }
156
+ return &worshipAndReligiousUnderstanding, nil
157
  }
158
 
159
  // SaveEducation menyimpan atau memperbarui data pendidikan ke database
160
  func (r *cvRepository) SaveEducation(ctx context.Context, req *models.EducationCV) (*models.EducationCV, error) {
161
+ if err := r.db.WithContext(ctx).Save(req).Error; err != nil {
162
+ return req, err
163
+ }
164
+ return req, nil
165
  }
166
 
167
  // ListEducation mengambil daftar data pendidikan berdasarkan account_id
168
  func (r *cvRepository) ListEducation(ctx context.Context, accountID int64) ([]models.EducationCV, error) {
169
+ var educations []models.EducationCV
170
+ if err := r.db.WithContext(ctx).Where("account_id = ?", accountID).Find(&educations).Error; err != nil {
171
+ return nil, err
172
+ }
173
+ return educations, nil
174
  }
175
 
176
  // GetEducation mengambil satu data pendidikan berdasarkan id
177
  func (r *cvRepository) GetEducation(ctx context.Context, id int64) (*models.EducationCV, error) {
178
+ var education models.EducationCV
179
+ if err := r.db.WithContext(ctx).Where("id = ?", id).First(&education).Error; err != nil {
180
+ return nil, err
181
+ }
182
+ return &education, nil
183
  }
184
 
185
  // DeleteEducation menghapus data pendidikan berdasarkan id
186
  func (r *cvRepository) DeleteEducation(ctx context.Context, id int64) error {
187
+ if err := r.db.WithContext(ctx).Where("id = ?", id).Delete(&models.EducationCV{}).Error; err != nil {
188
+ return err
189
+ }
190
+ return nil
191
  }
192
 
193
  // SaveJob menyimpan atau memperbarui data pekerjaan ke database
194
  func (r *cvRepository) SaveJob(ctx context.Context, req *models.JobCV) (*models.JobCV, error) {
195
+ if err := r.db.WithContext(ctx).Save(req).Error; err != nil {
196
+ return req, err
197
+ }
198
+ return req, nil
199
  }
200
 
201
  // ListJob mengambil daftar data pekerjaan berdasarkan account_id
202
  func (r *cvRepository) ListJob(ctx context.Context, accountID int64) ([]models.JobCV, error) {
203
+ var jobs []models.JobCV
204
+ if err := r.db.WithContext(ctx).Where("account_id = ?", accountID).Find(&jobs).Error; err != nil {
205
+ return nil, err
206
+ }
207
+ return jobs, nil
208
  }
209
 
210
  // GetJob mengambil satu data pekerjaan berdasarkan id
211
  func (r *cvRepository) GetJob(ctx context.Context, id int64) (*models.JobCV, error) {
212
+ var job models.JobCV
213
+ if err := r.db.WithContext(ctx).Where("id = ?", id).First(&job).Error; err != nil {
214
+ return nil, err
215
+ }
216
+ return &job, nil
217
  }
218
 
219
  // DeleteJob menghapus data pekerjaan berdasarkan id
220
  func (r *cvRepository) DeleteJob(ctx context.Context, id int64) error {
221
+ if err := r.db.WithContext(ctx).Where("id = ?", id).Delete(&models.JobCV{}).Error; err != nil {
222
+ return err
223
+ }
224
+ return nil
225
  }
226
 
227
  // SaveAchievement menyimpan atau memperbarui data prestasi ke database
228
  func (r *cvRepository) SaveAchievement(ctx context.Context, req *models.AchievementCV) (*models.AchievementCV, error) {
229
+ if err := r.db.WithContext(ctx).Save(req).Error; err != nil {
230
+ return req, err
231
+ }
232
+ return req, nil
233
  }
234
 
235
  // ListAchievement mengambil daftar data prestasi berdasarkan account_id
236
  func (r *cvRepository) ListAchievement(ctx context.Context, accountID int64) ([]models.AchievementCV, error) {
237
+ var achievements []models.AchievementCV
238
+ if err := r.db.WithContext(ctx).Where("account_id = ?", accountID).Find(&achievements).Error; err != nil {
239
+ return nil, err
240
+ }
241
+ return achievements, nil
242
  }
243
 
244
  // GetAchievement mengambil satu data prestasi berdasarkan id
245
  func (r *cvRepository) GetAchievement(ctx context.Context, id int64) (*models.AchievementCV, error) {
246
+ var achievement models.AchievementCV
247
+ if err := r.db.WithContext(ctx).Where("id = ?", id).First(&achievement).Error; err != nil {
248
+ return nil, err
249
+ }
250
+ return &achievement, nil
251
  }
252
 
253
  // DeleteAchievement menghapus data prestasi berdasarkan id
254
  func (r *cvRepository) DeleteAchievement(ctx context.Context, id int64) error {
255
+ if err := r.db.WithContext(ctx).Where("id = ?", id).Delete(&models.AchievementCV{}).Error; err != nil {
256
+ return err
257
+ }
258
+ return nil
259
  }
space/space/space/services/cv_service.go CHANGED
@@ -5,6 +5,7 @@ import (
5
  "errors"
6
  "math"
7
  "strconv"
 
8
 
9
  "api.qobiltu.id/models"
10
  "api.qobiltu.id/pkg/storage"
@@ -69,8 +70,6 @@ func NewCVService(cvRepository repositories.CVRepository, storage storage.Storag
69
  }
70
 
71
  func (s *cvService) SaveAccountDetails(ctx context.Context, req *models.SaveAccountDetailsRequest) (*models.AccountDetails, error) {
72
- // notes
73
- // jika ingin mengubah value wajib kirimkan field beserta value nya
74
  if err := validation.Validate(req); err != nil {
75
  return nil, response.HandleValidationError(err)
76
  }
@@ -81,15 +80,42 @@ func (s *cvService) SaveAccountDetails(ctx context.Context, req *models.SaveAcco
81
  return nil, response.HandleGormError(err, "Internal Server Error")
82
  }
83
 
84
- // Apply perubahan
85
- if accountDetails == nil {
 
86
  accountDetails = &models.AccountDetails{}
 
 
 
 
 
 
 
87
  }
88
 
89
- accountDetails.AccountID = uint(req.AccountID)
 
 
 
 
 
 
 
 
90
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  utils.AssignIfNotNil(&accountDetails.FullName, req.FullName)
92
- utils.AssignIfNotNil(&accountDetails.Gender, req.Gender)
93
  utils.AssignIfNotNil(&accountDetails.DateOfBirth, req.DateOfBirth)
94
  utils.AssignIfNotNil(&accountDetails.PlaceOfBirth, req.PlaceOfBirth)
95
  utils.AssignIfNotNil(&accountDetails.Domicile, req.Domicile)
@@ -102,7 +128,7 @@ func (s *cvService) SaveAccountDetails(ctx context.Context, req *models.SaveAcco
102
  accountDetails.PhoneNumber = &sanitizedPhone
103
  }
104
 
105
- // Simpan data
106
  res, err := s.cvRepository.SaveAccountDetails(ctx, accountDetails)
107
  if err != nil {
108
  return nil, response.HandleGormError(err, "Internal Server Error")
 
5
  "errors"
6
  "math"
7
  "strconv"
8
+ "strings"
9
 
10
  "api.qobiltu.id/models"
11
  "api.qobiltu.id/pkg/storage"
 
70
  }
71
 
72
  func (s *cvService) SaveAccountDetails(ctx context.Context, req *models.SaveAccountDetailsRequest) (*models.AccountDetails, error) {
 
 
73
  if err := validation.Validate(req); err != nil {
74
  return nil, response.HandleValidationError(err)
75
  }
 
80
  return nil, response.HandleGormError(err, "Internal Server Error")
81
  }
82
 
83
+ // Cek apakah accountDetails baru atau existing
84
+ isNew := accountDetails == nil
85
+ if isNew {
86
  accountDetails = &models.AccountDetails{}
87
+ accountDetails.AccountID = uint(req.AccountID)
88
+ }
89
+
90
+ // Simpan gender lama jika ada
91
+ oldGender := ""
92
+ if accountDetails.Gender != nil {
93
+ oldGender = strings.ToLower(*accountDetails.Gender)
94
  }
95
 
96
+ // Update gender jika dikirim dari request
97
+ var genderChanged bool
98
+ if req.Gender != nil {
99
+ newGender := strings.ToLower(*req.Gender)
100
+ if oldGender != newGender {
101
+ genderChanged = true
102
+ accountDetails.Gender = req.Gender
103
+ }
104
+ }
105
 
106
+ // Jika gender baru atau gender berubah, generate InitialName baru via sequence
107
+ if isNew || genderChanged {
108
+ if accountDetails.Gender != nil {
109
+ number, err := s.cvRepository.GetNextInitialNameNumber(ctx, *accountDetails.Gender)
110
+ if err != nil {
111
+ return nil, response.HandleGormError(err, "Gagal generate initial name")
112
+ }
113
+ accountDetails.InitialName = models.BuildInitialName(*accountDetails.Gender, number)
114
+ }
115
+ }
116
+
117
+ // Apply perubahan field lainnya
118
  utils.AssignIfNotNil(&accountDetails.FullName, req.FullName)
 
119
  utils.AssignIfNotNil(&accountDetails.DateOfBirth, req.DateOfBirth)
120
  utils.AssignIfNotNil(&accountDetails.PlaceOfBirth, req.PlaceOfBirth)
121
  utils.AssignIfNotNil(&accountDetails.Domicile, req.Domicile)
 
128
  accountDetails.PhoneNumber = &sanitizedPhone
129
  }
130
 
131
+ // Simpan ke database
132
  res, err := s.cvRepository.SaveAccountDetails(ctx, accountDetails)
133
  if err != nil {
134
  return nil, response.HandleGormError(err, "Internal Server Error")
space/space/space/services/user_profile_service.go CHANGED
@@ -2,7 +2,6 @@ package services
2
 
3
  import (
4
  "regexp"
5
- "strconv"
6
  "strings"
7
 
8
  "api.qobiltu.id/models"
@@ -73,22 +72,47 @@ func (s *UserProfileService) Retrieve() {
73
  }
74
 
75
  func (s *UserProfileService) Update() {
 
76
  if s.Constructor.PhoneNumber != nil {
77
  phoneNumber := *s.Constructor.PhoneNumber
78
  *s.Constructor.PhoneNumber = SanitizePhoneNumber(phoneNumber)
79
  }
80
- usersCount := repositories.GetAllAccount().RowsCount
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  var initialName string
 
 
82
  if s.Constructor.Gender != nil {
83
- if strings.ToLower(*s.Constructor.Gender) == "laki-laki" {
84
- initialName = "IKH_"
85
- } else {
86
- initialName = "AKH_"
 
 
 
 
 
 
87
  }
88
  }
89
 
90
- initialName += strconv.Itoa(usersCount)
91
- s.Constructor.InitialName = initialName
 
 
 
92
  userProfile := repositories.UpdateAccountDetails(s.Constructor)
93
  s.Error = userProfile.RowsError
94
  if userProfile.NoRecord {
@@ -96,6 +120,8 @@ func (s *UserProfileService) Update() {
96
  s.Exception.Message = "There is no account with given credentials!"
97
  return
98
  }
 
 
99
  account := repositories.GetAccountById(uint(s.Constructor.AccountID))
100
  account.Result.IsDetailCompleted = (userProfile.Result.InitialName != "" &&
101
  userProfile.Result.FullName != nil &&
@@ -107,9 +133,11 @@ func (s *UserProfileService) Update() {
107
  userProfile.Result.LastEducation != nil &&
108
  userProfile.Result.MaritalStatus != nil)
109
  repositories.UpdateAccount(account.Result)
 
110
  s.Result = models.UserProfileResponse{
111
  Account: account.Result,
112
  Details: userProfile.Result,
113
  }
 
114
  s.Result.Account.Password = "SECRET"
115
  }
 
2
 
3
  import (
4
  "regexp"
 
5
  "strings"
6
 
7
  "api.qobiltu.id/models"
 
72
  }
73
 
74
  func (s *UserProfileService) Update() {
75
+ // Sanitize phone number
76
  if s.Constructor.PhoneNumber != nil {
77
  phoneNumber := *s.Constructor.PhoneNumber
78
  *s.Constructor.PhoneNumber = SanitizePhoneNumber(phoneNumber)
79
  }
80
+
81
+ // Ambil data account details dari DB
82
+ existingDetails := repositories.GetAccountDetailsByAccountID(uint(s.Constructor.AccountID))
83
+ if existingDetails.RowsError != nil {
84
+ s.Error = existingDetails.RowsError
85
+ return
86
+ }
87
+ if existingDetails.NoRecord {
88
+ s.Exception.DataNotFound = true
89
+ s.Exception.Message = "Account details not found"
90
+ return
91
+ }
92
+
93
+ // Update initial name jika gender berubah
94
  var initialName string
95
+ shouldUpdateInitialName := false
96
+
97
  if s.Constructor.Gender != nil {
98
+ newGender := strings.ToLower(*s.Constructor.Gender)
99
+ oldGender := ""
100
+ if existingDetails.Result.Gender != nil {
101
+ oldGender = strings.ToLower(*existingDetails.Result.Gender)
102
+ }
103
+ if newGender != oldGender {
104
+ initialNameNumber := repositories.GetNextInitialNameNumber(*s.Constructor.Gender)
105
+ initialName = models.BuildInitialName(*s.Constructor.Gender, int64(initialNameNumber.Result))
106
+ s.Constructor.InitialName = initialName
107
+ shouldUpdateInitialName = true
108
  }
109
  }
110
 
111
+ // Update ke database
112
+ if !shouldUpdateInitialName {
113
+ s.Constructor.InitialName = existingDetails.Result.InitialName
114
+ }
115
+
116
  userProfile := repositories.UpdateAccountDetails(s.Constructor)
117
  s.Error = userProfile.RowsError
118
  if userProfile.NoRecord {
 
120
  s.Exception.Message = "There is no account with given credentials!"
121
  return
122
  }
123
+
124
+ // Update flag isDetailCompleted di account
125
  account := repositories.GetAccountById(uint(s.Constructor.AccountID))
126
  account.Result.IsDetailCompleted = (userProfile.Result.InitialName != "" &&
127
  userProfile.Result.FullName != nil &&
 
133
  userProfile.Result.LastEducation != nil &&
134
  userProfile.Result.MaritalStatus != nil)
135
  repositories.UpdateAccount(account.Result)
136
+
137
  s.Result = models.UserProfileResponse{
138
  Account: account.Result,
139
  Details: userProfile.Result,
140
  }
141
+
142
  s.Result.Account.Password = "SECRET"
143
  }
space/space/space/space/models/database_orm_model.go CHANGED
@@ -19,8 +19,8 @@ type Account struct {
19
  }
20
 
21
  type AccountDetails struct {
22
- ID uint64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
23
- AccountID uint `gorm:"column:account_id;not null;unique" json:"account_id"`
24
  InitialName string `gorm:"column:initial_name;not null" json:"initial_name"`
25
  FullName *string `gorm:"column:full_name" json:"full_name"`
26
  DateOfBirth *time.Time `gorm:"column:date_of_birth" json:"date_of_birth"`
@@ -32,8 +32,8 @@ type AccountDetails struct {
32
  MaritalStatus *string `gorm:"column:marital_status" json:"marital_status"`
33
  Avatar *string `gorm:"column:avatar" json:"avatar"`
34
  PhoneNumber *string `gorm:"column:phone_number" json:"phone_number"`
35
- CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"`
36
- UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"`
37
  FieldCounter
38
  }
39
 
@@ -193,9 +193,9 @@ type QuizResult struct {
193
 
194
  type (
195
  PersonalityAndPreferenceCV struct {
196
- ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
197
- AccountID int64 `gorm:"column:account_id;not null;unique" json:"account_id"`
198
- Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"`
199
  PositiveTraits *string `gorm:"column:positive_traits" json:"positive_traits"` // sifat positif
200
  NegativeTraits *string `gorm:"column:negative_traits" json:"negative_traits"` // sifat negatif
201
  Hobbies *string `gorm:"column:hobbies" json:"hobbies"` // hobi
@@ -210,30 +210,30 @@ type (
210
  CanCook *bool `gorm:"column:can_cook" json:"can_cook"` // bisa memasak
211
  TypesOfDishesCooked *string `gorm:"column:types_of_dishes_cooked" json:"types_of_dishes_cooked"` // jenis masakan yang bisa dimasak
212
  MonthlyExpenses *string `gorm:"column:monthly_expenses" json:"monthly_expenses"` // pengeluaran per bulan
213
- CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"`
214
- UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"`
215
  FieldCounter
216
  }
217
 
218
  FamilyMemberCV struct {
219
- ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
220
- AccountID int64 `gorm:"column:account_id;not null" json:"account_id"`
221
- Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"`
222
  Role *string `gorm:"column:role" json:"role"` // Peran dalam keluarga
223
  Status *string `gorm:"column:status" json:"status"` // Status (Hidup, Wafat)
224
  Religion *string `gorm:"column:religion" json:"religion"` // Agama
225
  Job *string `gorm:"column:job" json:"job"` // Pekerjaan
226
  LastEducation *string `gorm:"column:last_education" json:"last_education"` // Pendidikan terakhir
227
  Age *int `gorm:"column:age" json:"age"` // Usia
228
- CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"`
229
- UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"`
230
  FieldCounter
231
  }
232
 
233
  PhysicalAndHealthCV struct {
234
- ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
235
- AccountID int64 `gorm:"column:account_id;not null;unique" json:"account_id"`
236
- Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"`
237
  HeightInCm *int `gorm:"column:height_cm" json:"height_cm"` // Tinggi badan dalam satuan sentimeter
238
  WeightInKg *int `gorm:"column:weight_kg" json:"weight_kg"` // Berat badan dalam satuan kilogram
239
  BodyShape *string `gorm:"column:body_shape" json:"body_shape"` // Bentuk tubuh
@@ -242,15 +242,15 @@ type (
242
  MedicalHistory *pq.StringArray `gorm:"column:medical_history;type:varchar(255)[]" json:"medical_history"` // Riwayat penyakit
243
  PhysicalDisorder *string `gorm:"column:physical_disorder" json:"physical_disorder"` // Cacat fisik
244
  PhysicalTraits *string `gorm:"column:physical_traits" json:"physical_traits"` // Ciri khas fisik
245
- CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"` // Waktu data dibuat
246
- UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"` // Waktu data terakhir diperbarui
247
  FieldCounter
248
  }
249
 
250
  WorshipAndReligiousUnderstandingCV struct {
251
- ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
252
- AccountID int64 `gorm:"column:account_id;not null;unique" json:"account_id"`
253
- Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"`
254
  ObligatoryPrayer *string `gorm:"column:obligatory_prayer" json:"obligatory_prayer"` // sholat_wajib_5_waktu
255
  CongregationalPrayer *string `gorm:"column:congregational_prayer" json:"congregational_prayer"` // sholat_berjamaah_di_masjid
256
  TahajjudPrayer *string `gorm:"column:tahajjud_prayer" json:"tahajjud_prayer"` // sholat_tahajud
@@ -266,44 +266,44 @@ type (
266
  OpinionOnVeil *string `gorm:"column:opinion_on_veil" json:"opinion_on_veil"` // pendapat_tentang_cadar
267
  WeeklyReligiousStudies *string `gorm:"column:weekly_religious_studies" json:"weekly_religious_studies"` // kajian_yang_diikuti_dalam_sepekan
268
  FollowedUstadz *string `gorm:"column:followed_ustadz" json:"followed_ustadz"` // ustadz_yang_diikuti
269
- CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"`
270
- UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"`
271
  FieldCounter
272
  }
273
 
274
  EducationCV struct {
275
- ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // id
276
- AccountID int64 `gorm:"column:account_id;not null" json:"account_id"` // id akun
277
- Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"` // relasi ke akun
278
- LastEducation *string `gorm:"column:last_education" json:"last_education"` // pendidikan terakhir
279
- EducationInstitute *string `gorm:"column:education_institute" json:"education_institute"` // institusi pendidikan
280
- EducationMajor *string `gorm:"column:education_major" json:"education_major"` // jurusan pendidikan
281
- YearStart *int `gorm:"column:year_start" json:"year_start"` // tahun masuk
282
- YearGraduate *int `gorm:"column:year_graduate" json:"year_graduate"` // tahun lulus
283
- CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"` // tanggal dibuat
284
- UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"` // tanggal diperbarui
285
  }
286
 
287
  JobCV struct {
288
- ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // id
289
- AccountID int64 `gorm:"column:account_id;not null" json:"account_id"` // id akun
290
- Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"` // relasi ke akun
291
- InstitutionName *string `gorm:"column:institution_name" json:"institution_name"` // nama instansi
292
- CurrentJob *string `gorm:"column:current_job" json:"current_job"` // pekerjaan saat ini
293
- YearStartedWorking *int `gorm:"column:year_started_working" json:"year_started_working"` // tahun mulai bekerja
294
- MonthlyIncome *string `gorm:"column:monthly_income" json:"monthly_income"` // penghasilan per bulan
295
- IncomeSources *pq.StringArray `gorm:"column:income_sources;type:varchar(255)[]" json:"income_sources"` // sumber penghasilan
296
- CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"` // tanggal dibuat
297
- UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"` // tanggal diperbarui
298
  }
299
 
300
  AchievementCV struct {
301
- ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // id
302
- AccountID int64 `gorm:"column:account_id;not null" json:"account_id"` // id akun
303
- Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"` // relasi ke akun
304
- AchievementOrAward *string `gorm:"column:achievement_or_award" json:"achievement_or_award"` // prestasi atau penghargaan
305
- CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"` // tanggal dibuat
306
- UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"` // tanggal diperbarui
307
  }
308
  )
309
 
 
19
  }
20
 
21
  type AccountDetails struct {
22
+ ID uint64 `gorm:"column:id;primaryKey;autoIncrement" json:"id" counter:"skip"`
23
+ AccountID uint `gorm:"column:account_id;not null;unique" json:"account_id" counter:"skip"`
24
  InitialName string `gorm:"column:initial_name;not null" json:"initial_name"`
25
  FullName *string `gorm:"column:full_name" json:"full_name"`
26
  DateOfBirth *time.Time `gorm:"column:date_of_birth" json:"date_of_birth"`
 
32
  MaritalStatus *string `gorm:"column:marital_status" json:"marital_status"`
33
  Avatar *string `gorm:"column:avatar" json:"avatar"`
34
  PhoneNumber *string `gorm:"column:phone_number" json:"phone_number"`
35
+ CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at" counter:"skip"`
36
+ UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at" counter:"skip"`
37
  FieldCounter
38
  }
39
 
 
193
 
194
  type (
195
  PersonalityAndPreferenceCV struct {
196
+ ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id" counter:"skip"`
197
+ AccountID int64 `gorm:"column:account_id;not null;unique" json:"account_id" counter:"skip"`
198
+ Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty" counter:"skip"`
199
  PositiveTraits *string `gorm:"column:positive_traits" json:"positive_traits"` // sifat positif
200
  NegativeTraits *string `gorm:"column:negative_traits" json:"negative_traits"` // sifat negatif
201
  Hobbies *string `gorm:"column:hobbies" json:"hobbies"` // hobi
 
210
  CanCook *bool `gorm:"column:can_cook" json:"can_cook"` // bisa memasak
211
  TypesOfDishesCooked *string `gorm:"column:types_of_dishes_cooked" json:"types_of_dishes_cooked"` // jenis masakan yang bisa dimasak
212
  MonthlyExpenses *string `gorm:"column:monthly_expenses" json:"monthly_expenses"` // pengeluaran per bulan
213
+ CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at" counter:"skip"`
214
+ UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at" counter:"skip"`
215
  FieldCounter
216
  }
217
 
218
  FamilyMemberCV struct {
219
+ ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id" counter:"skip"`
220
+ AccountID int64 `gorm:"column:account_id;not null" json:"account_id" counter:"skip"`
221
+ Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty" counter:"skip"`
222
  Role *string `gorm:"column:role" json:"role"` // Peran dalam keluarga
223
  Status *string `gorm:"column:status" json:"status"` // Status (Hidup, Wafat)
224
  Religion *string `gorm:"column:religion" json:"religion"` // Agama
225
  Job *string `gorm:"column:job" json:"job"` // Pekerjaan
226
  LastEducation *string `gorm:"column:last_education" json:"last_education"` // Pendidikan terakhir
227
  Age *int `gorm:"column:age" json:"age"` // Usia
228
+ CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at" counter:"skip"`
229
+ UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at" counter:"skip"`
230
  FieldCounter
231
  }
232
 
233
  PhysicalAndHealthCV struct {
234
+ ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id" counter:"skip"`
235
+ AccountID int64 `gorm:"column:account_id;not null;unique" json:"account_id" counter:"skip"`
236
+ Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty" counter:"skip"`
237
  HeightInCm *int `gorm:"column:height_cm" json:"height_cm"` // Tinggi badan dalam satuan sentimeter
238
  WeightInKg *int `gorm:"column:weight_kg" json:"weight_kg"` // Berat badan dalam satuan kilogram
239
  BodyShape *string `gorm:"column:body_shape" json:"body_shape"` // Bentuk tubuh
 
242
  MedicalHistory *pq.StringArray `gorm:"column:medical_history;type:varchar(255)[]" json:"medical_history"` // Riwayat penyakit
243
  PhysicalDisorder *string `gorm:"column:physical_disorder" json:"physical_disorder"` // Cacat fisik
244
  PhysicalTraits *string `gorm:"column:physical_traits" json:"physical_traits"` // Ciri khas fisik
245
+ CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at" counter:"skip"` // Waktu data dibuat
246
+ UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at" counter:"skip"` // Waktu data terakhir diperbarui
247
  FieldCounter
248
  }
249
 
250
  WorshipAndReligiousUnderstandingCV struct {
251
+ ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id" counter:"skip"`
252
+ AccountID int64 `gorm:"column:account_id;not null;unique" json:"account_id" counter:"skip"`
253
+ Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty" counter:"skip"`
254
  ObligatoryPrayer *string `gorm:"column:obligatory_prayer" json:"obligatory_prayer"` // sholat_wajib_5_waktu
255
  CongregationalPrayer *string `gorm:"column:congregational_prayer" json:"congregational_prayer"` // sholat_berjamaah_di_masjid
256
  TahajjudPrayer *string `gorm:"column:tahajjud_prayer" json:"tahajjud_prayer"` // sholat_tahajud
 
266
  OpinionOnVeil *string `gorm:"column:opinion_on_veil" json:"opinion_on_veil"` // pendapat_tentang_cadar
267
  WeeklyReligiousStudies *string `gorm:"column:weekly_religious_studies" json:"weekly_religious_studies"` // kajian_yang_diikuti_dalam_sepekan
268
  FollowedUstadz *string `gorm:"column:followed_ustadz" json:"followed_ustadz"` // ustadz_yang_diikuti
269
+ CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at" counter:"skip"`
270
+ UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at" counter:"skip"`
271
  FieldCounter
272
  }
273
 
274
  EducationCV struct {
275
+ ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id" counter:"skip"` // id
276
+ AccountID int64 `gorm:"column:account_id;not null" json:"account_id" counter:"skip"` // id akun
277
+ Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty" counter:"skip"` // relasi ke akun
278
+ LastEducation *string `gorm:"column:last_education" json:"last_education"` // pendidikan terakhir
279
+ EducationInstitute *string `gorm:"column:education_institute" json:"education_institute"` // institusi pendidikan
280
+ EducationMajor *string `gorm:"column:education_major" json:"education_major"` // jurusan pendidikan
281
+ YearStart *int `gorm:"column:year_start" json:"year_start"` // tahun masuk
282
+ YearGraduate *int `gorm:"column:year_graduate" json:"year_graduate"` // tahun lulus
283
+ CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at" counter:"skip"` // tanggal dibuat
284
+ UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at" counter:"skip"` // tanggal diperbarui
285
  }
286
 
287
  JobCV struct {
288
+ ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id" counter:"skip"` // id
289
+ AccountID int64 `gorm:"column:account_id;not null" json:"account_id" counter:"skip"` // id akun
290
+ Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty" counter:"skip"` // relasi ke akun
291
+ InstitutionName *string `gorm:"column:institution_name" json:"institution_name"` // nama instansi
292
+ CurrentJob *string `gorm:"column:current_job" json:"current_job"` // pekerjaan saat ini
293
+ YearStartedWorking *int `gorm:"column:year_started_working" json:"year_started_working"` // tahun mulai bekerja
294
+ MonthlyIncome *string `gorm:"column:monthly_income" json:"monthly_income"` // penghasilan per bulan
295
+ IncomeSources *pq.StringArray `gorm:"column:income_sources;type:varchar(255)[]" json:"income_sources"` // sumber penghasilan
296
+ CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at" counter:"skip"` // tanggal dibuat
297
+ UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at" counter:"skip"` // tanggal diperbarui
298
  }
299
 
300
  AchievementCV struct {
301
+ ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id" counter:"skip"` // id
302
+ AccountID int64 `gorm:"column:account_id;not null" json:"account_id" counter:"skip"` // id akun
303
+ Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty" counter:"skip"` // relasi ke akun
304
+ AchievementOrAward *string `gorm:"column:achievement_or_award" json:"achievement_or_award"` // prestasi atau penghargaan
305
+ CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at" counter:"skip"` // tanggal dibuat
306
+ UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at" counter:"skip"` // tanggal diperbarui
307
  }
308
  )
309
 
space/space/space/space/models/field_counter.go CHANGED
@@ -1,6 +1,7 @@
1
  package models
2
 
3
  import (
 
4
  "reflect"
5
  "time"
6
  )
@@ -17,29 +18,13 @@ type FieldCounter struct{}
17
 
18
  // shouldSkipField menentukan apakah field harus dilewati dalam penghitungan
19
  func shouldSkipField(field reflect.StructField) bool {
20
- // Skip field dengan nama FieldCounter (embedded type)
21
  if field.Name == "FieldCounter" {
22
  return true
23
  }
24
 
25
- // Skip field yang berasal dari GORM atau standard fields
26
- // seperti ID, timestamps, dan foreign keys
27
- if field.Name == "ID" || field.Name == "CreatedAt" || field.Name == "UpdatedAt" || field.Name == "DeletedAt" ||
28
- field.Name == "AccountID" || field.Name == "Account" {
29
- return true
30
- }
31
-
32
- // Periksa tag gorm untuk auto fields
33
- gormTag := field.Tag.Get("gorm")
34
- if len(gormTag) > 0 {
35
- // Skip kolom yang auto-increment atau auto-timestamp
36
- if gormTag == "primaryKey" || gormTag == "autoIncrement" ||
37
- gormTag == "autoCreateTime" || gormTag == "autoUpdateTime" {
38
- return true
39
- }
40
- }
41
-
42
- return false
43
  }
44
 
45
  // TotalFields menghitung jumlah total field dalam struct
 
1
  package models
2
 
3
  import (
4
+ "fmt"
5
  "reflect"
6
  "time"
7
  )
 
18
 
19
  // shouldSkipField menentukan apakah field harus dilewati dalam penghitungan
20
  func shouldSkipField(field reflect.StructField) bool {
 
21
  if field.Name == "FieldCounter" {
22
  return true
23
  }
24
 
25
+ counterTag := field.Tag.Get("counter")
26
+ fmt.Println(field.Name, "=", counterTag)
27
+ return counterTag == "skip"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  }
29
 
30
  // TotalFields menghitung jumlah total field dalam struct
space/space/space/space/space/config/database_connection_config.go CHANGED
@@ -77,6 +77,7 @@ func AutoMigrateAll(db *gorm.DB) {
77
  &models.EducationCV{},
78
  &models.JobCV{},
79
  &models.AchievementCV{},
 
80
  )
81
 
82
  if err != nil {
 
77
  &models.EducationCV{},
78
  &models.JobCV{},
79
  &models.AchievementCV{},
80
+ &models.MarriageReadinessProfile{},
81
  )
82
 
83
  if err != nil {
space/space/space/space/space/controller/marriage_readiness_profile/marriage_readiness_profile_controller.go ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package cv_controller
2
+
3
+ import (
4
+ "net/http"
5
+
6
+ "api.qobiltu.id/middleware"
7
+ "api.qobiltu.id/models"
8
+ "api.qobiltu.id/response"
9
+ "api.qobiltu.id/services"
10
+ "github.com/gin-gonic/gin"
11
+ )
12
+
13
+ type MarriageReadinessProfileController interface {
14
+ SaveMarriageReadinessProfile(ctx *gin.Context)
15
+ GetMarriageReadinessProfile(ctx *gin.Context)
16
+ }
17
+
18
+ type marriageReadinessProfileController struct {
19
+ marriageReadinessProfileService services.MarriageReadinessProfileService
20
+ }
21
+
22
+ func NewMarriageReadinessProfileController(marriageReadinessProfileService services.MarriageReadinessProfileService) MarriageReadinessProfileController {
23
+ return &marriageReadinessProfileController{
24
+ marriageReadinessProfileService: marriageReadinessProfileService,
25
+ }
26
+ }
27
+
28
+ func (c *marriageReadinessProfileController) SaveMarriageReadinessProfile(ctx *gin.Context) {
29
+ var req models.SaveMarriageReadinessProfileRequest
30
+ if err := ctx.ShouldBindJSON(&req); err != nil {
31
+ response.HandleError(ctx, models.Exception{
32
+ Message: "Invalid body request",
33
+ BadRequest: true,
34
+ Err: err,
35
+ })
36
+ return
37
+ }
38
+
39
+ accountData := middleware.GetAccountData(ctx)
40
+ req.AccountID = int64(accountData.UserID)
41
+
42
+ res, err := c.marriageReadinessProfileService.SaveMarriageReadinessProfile(ctx, &req)
43
+ if err != nil {
44
+ response.HandleError(ctx, err)
45
+ return
46
+ }
47
+
48
+ response.HandleSuccess(ctx, http.StatusOK, "Marriage readiness profile saved", res, nil)
49
+ }
50
+
51
+ func (c *marriageReadinessProfileController) GetMarriageReadinessProfile(ctx *gin.Context) {
52
+ accountData := middleware.GetAccountData(ctx)
53
+ accountID := int64(accountData.UserID)
54
+
55
+ req := models.GetMarriageReadinessProfileRequest{
56
+ AccountID: accountID,
57
+ }
58
+
59
+ res, err := c.marriageReadinessProfileService.GetMarriageReadinessProfile(ctx, &req)
60
+ if err != nil {
61
+ response.HandleError(ctx, err)
62
+ return
63
+ }
64
+
65
+ response.HandleSuccess(ctx, http.StatusOK, "Get marriage readiness profile success", res, nil)
66
+ }
space/space/space/space/space/main.go CHANGED
@@ -1,9 +1,14 @@
1
  package main
2
 
3
  import (
 
 
 
 
4
  "api.qobiltu.id/config"
5
- "api.qobiltu.id/controller/cv"
6
- "api.qobiltu.id/controller/health_check"
 
7
  "api.qobiltu.id/mail"
8
  "api.qobiltu.id/pkg/storage"
9
  "api.qobiltu.id/pkg/validation"
@@ -13,9 +18,6 @@ import (
13
  "api.qobiltu.id/services"
14
  "api.qobiltu.id/utils"
15
  "github.com/hibiken/asynq"
16
- "log/slog"
17
- "net"
18
- "strconv"
19
  )
20
 
21
  func main() {
@@ -60,6 +62,10 @@ func main() {
60
  cvService := services.NewCVService(cvRepository, localStorage)
61
  cvController := cv_controller.NewCVController(cvService)
62
 
 
 
 
 
63
  // start task processor
64
  err = taskProcessor.Start()
65
  utils.FatalIfErr("failed to start task processor", err)
@@ -69,6 +75,7 @@ func main() {
69
  s, err := router.NewServer(
70
  healthCheckController,
71
  cvController,
 
72
  )
73
  utils.FatalIfErr("failed to create server", err)
74
 
 
1
  package main
2
 
3
  import (
4
+ "log/slog"
5
+ "net"
6
+ "strconv"
7
+
8
  "api.qobiltu.id/config"
9
+ cv_controller "api.qobiltu.id/controller/cv"
10
+ health_check_controller "api.qobiltu.id/controller/health_check"
11
+ marriage_readiness_profile_controller "api.qobiltu.id/controller/marriage_readiness_profile"
12
  "api.qobiltu.id/mail"
13
  "api.qobiltu.id/pkg/storage"
14
  "api.qobiltu.id/pkg/validation"
 
18
  "api.qobiltu.id/services"
19
  "api.qobiltu.id/utils"
20
  "github.com/hibiken/asynq"
 
 
 
21
  )
22
 
23
  func main() {
 
62
  cvService := services.NewCVService(cvRepository, localStorage)
63
  cvController := cv_controller.NewCVController(cvService)
64
 
65
+ marriageReadinessProfileRepository := repositories.NewMarriageReadinessProfileRepository(config.DB)
66
+ marriageReadinessProfileService := services.NewMarriageReadinessProfileService(marriageReadinessProfileRepository)
67
+ marriageReadinessProfileController := marriage_readiness_profile_controller.NewMarriageReadinessProfileController(marriageReadinessProfileService)
68
+
69
  // start task processor
70
  err = taskProcessor.Start()
71
  utils.FatalIfErr("failed to start task processor", err)
 
75
  s, err := router.NewServer(
76
  healthCheckController,
77
  cvController,
78
+ marriageReadinessProfileController,
79
  )
80
  utils.FatalIfErr("failed to create server", err)
81
 
space/space/space/space/space/repositories/marriage_readiness_profile_repository.go ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package repositories
2
+
3
+ import (
4
+ "context"
5
+
6
+ "api.qobiltu.id/models"
7
+ "gorm.io/gorm"
8
+ )
9
+
10
+ type MarriageReadinessProfileRepository interface {
11
+ SaveMarriageReadinessProfile(ctx context.Context, req *models.MarriageReadinessProfile) (*models.MarriageReadinessProfile, error)
12
+ GetMarriageReadinessProfile(ctx context.Context, accountID int64) (*models.MarriageReadinessProfile, error)
13
+ }
14
+
15
+ type marriageReadinessProfileRepository struct {
16
+ db *gorm.DB
17
+ }
18
+
19
+ func NewMarriageReadinessProfileRepository(db *gorm.DB) MarriageReadinessProfileRepository {
20
+ return &marriageReadinessProfileRepository{
21
+ db: db,
22
+ }
23
+ }
24
+
25
+ func (r *marriageReadinessProfileRepository) SaveMarriageReadinessProfile(ctx context.Context, req *models.MarriageReadinessProfile) (*models.MarriageReadinessProfile, error) {
26
+ if err := r.db.WithContext(ctx).Save(req).Error; err != nil {
27
+ return req, err
28
+ }
29
+ return req, nil
30
+ }
31
+
32
+ func (r *marriageReadinessProfileRepository) GetMarriageReadinessProfile(ctx context.Context, accountID int64) (*models.MarriageReadinessProfile, error) {
33
+ var marriageReadinessProfile models.MarriageReadinessProfile
34
+ if err := r.db.WithContext(ctx).Where("account_id = ?", accountID).First(&marriageReadinessProfile).Error; err != nil {
35
+ return nil, err
36
+ }
37
+ return &marriageReadinessProfile, nil
38
+ }
space/space/space/space/space/router/marriage_readiness_profile_route.go ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package router
2
+
3
+ import "api.qobiltu.id/middleware"
4
+
5
+ func (s *Server) MarriageReadinessProfileRoute() {
6
+ routerGroup := s.router.Group("/api/v1/marriage-readiness-profile").Use(middleware.AuthUser)
7
+ {
8
+ routerGroup.POST("", s.marriageReadinessProfileController.SaveMarriageReadinessProfile)
9
+ routerGroup.GET("", s.marriageReadinessProfileController.GetMarriageReadinessProfile)
10
+ }
11
+ }
space/space/space/space/space/services/marriage_readiness_profile_service.go ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package services
2
+
3
+ import (
4
+ "context"
5
+ "errors"
6
+
7
+ "api.qobiltu.id/models"
8
+ "api.qobiltu.id/pkg/validation"
9
+ "api.qobiltu.id/repositories"
10
+ "api.qobiltu.id/response"
11
+ "api.qobiltu.id/utils"
12
+ "gorm.io/gorm"
13
+ )
14
+
15
+ type MarriageReadinessProfileService interface {
16
+ SaveMarriageReadinessProfile(ctx context.Context, req *models.SaveMarriageReadinessProfileRequest) (*models.MarriageReadinessProfile, error)
17
+ GetMarriageReadinessProfile(ctx context.Context, req *models.GetMarriageReadinessProfileRequest) (*models.MarriageReadinessProfile, error)
18
+ }
19
+
20
+ type marriageReadinessProfileService struct {
21
+ marriageReadinessProfileRepository repositories.MarriageReadinessProfileRepository
22
+ }
23
+
24
+ func NewMarriageReadinessProfileService(marriageReadinessProfileRepository repositories.MarriageReadinessProfileRepository) MarriageReadinessProfileService {
25
+ return &marriageReadinessProfileService{marriageReadinessProfileRepository: marriageReadinessProfileRepository}
26
+ }
27
+
28
+ func (s *marriageReadinessProfileService) SaveMarriageReadinessProfile(ctx context.Context, req *models.SaveMarriageReadinessProfileRequest) (*models.MarriageReadinessProfile, error) {
29
+ if err := validation.Validate(req); err != nil {
30
+ return nil, response.HandleValidationError(err)
31
+ }
32
+
33
+ marriageReadinessProfile, err := s.marriageReadinessProfileRepository.GetMarriageReadinessProfile(ctx, req.AccountID)
34
+ if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
35
+ return nil, response.HandleGormError(err, "Internal Server Error")
36
+ }
37
+
38
+ if marriageReadinessProfile == nil {
39
+ marriageReadinessProfile = &models.MarriageReadinessProfile{}
40
+ }
41
+
42
+ marriageReadinessProfile.AccountID = req.AccountID
43
+
44
+ utils.AssignIfNotNil(&marriageReadinessProfile.MarriageVision, req.MarriageVision)
45
+ utils.AssignIfNotNil(&marriageReadinessProfile.LivingPlan, req.LivingPlan)
46
+ utils.AssignIfNotNil(&marriageReadinessProfile.SpouseRoles, req.SpouseRoles)
47
+ utils.AssignIfNotNil(&marriageReadinessProfile.SpouseRights, req.SpouseRights)
48
+ utils.AssignIfNotNil(&marriageReadinessProfile.ConflictResolution, req.ConflictResolution)
49
+ utils.AssignIfNotNil(&marriageReadinessProfile.ParentingStyle, req.ParentingStyle)
50
+
51
+ utils.AssignIfNotNil(&marriageReadinessProfile.ReadyToMarryDuration, req.ReadyToMarryDuration)
52
+ utils.AssignIfNotNil(&marriageReadinessProfile.WeddingConcept, req.WeddingConcept)
53
+ utils.AssignIfNotNil(&marriageReadinessProfile.WeddingFunding, req.WeddingFunding)
54
+
55
+ utils.AssignIfNotNil(&marriageReadinessProfile.CareerAfterMarriage, req.CareerAfterMarriage)
56
+ utils.AssignIfNotNil(&marriageReadinessProfile.TimeManagement, req.TimeManagement)
57
+ utils.AssignIfNotNil(&marriageReadinessProfile.CareerGoals, req.CareerGoals)
58
+ utils.AssignIfNotNil(&marriageReadinessProfile.SelfDevelopment, req.SelfDevelopment)
59
+
60
+ utils.AssignIfNotNil(&marriageReadinessProfile.DelayChildren, req.DelayChildren)
61
+ utils.AssignIfNotNil(&marriageReadinessProfile.ChildEducationPlan, req.ChildEducationPlan)
62
+ utils.AssignIfNotNil(&marriageReadinessProfile.ReligiousEmotionalBond, req.ReligiousEmotionalBond)
63
+ utils.AssignIfNotNil(&marriageReadinessProfile.ParentingChallenges, req.ParentingChallenges)
64
+
65
+ utils.AssignIfNotNil(&marriageReadinessProfile.MonthlyFinance, req.MonthlyFinance)
66
+ utils.AssignIfNotNil(&marriageReadinessProfile.FamilyResponsibility, req.FamilyResponsibility)
67
+ utils.AssignIfNotNil(&marriageReadinessProfile.DebtStatus, req.DebtStatus)
68
+ utils.AssignIfNotNil(&marriageReadinessProfile.FinanceSharing, req.FinanceSharing)
69
+ utils.AssignIfNotNil(&marriageReadinessProfile.IncomeGapView, req.IncomeGapView)
70
+
71
+ utils.AssignIfNotNil(&marriageReadinessProfile.DecisionMaking, req.DecisionMaking)
72
+ utils.AssignIfNotNil(&marriageReadinessProfile.GrowthTogether, req.GrowthTogether)
73
+ utils.AssignIfNotNil(&marriageReadinessProfile.ParentIntervention, req.ParentIntervention)
74
+ utils.AssignIfNotNil(&marriageReadinessProfile.HabitResponse, req.HabitResponse)
75
+
76
+ res, err := s.marriageReadinessProfileRepository.SaveMarriageReadinessProfile(ctx, marriageReadinessProfile)
77
+ if err != nil {
78
+ return nil, response.HandleGormError(err, "Internal Server Error")
79
+ }
80
+
81
+ return res, nil
82
+ }
83
+
84
+ func (s *marriageReadinessProfileService) GetMarriageReadinessProfile(ctx context.Context, req *models.GetMarriageReadinessProfileRequest) (*models.MarriageReadinessProfile, error) {
85
+ res, err := s.marriageReadinessProfileRepository.GetMarriageReadinessProfile(ctx, req.AccountID)
86
+ if err != nil {
87
+ return nil, response.HandleGormError(err, "Internal Server Error")
88
+ }
89
+ return res, nil
90
+ }
space/space/space/space/space/space/controller/cv/cv_controller.go CHANGED
@@ -49,7 +49,7 @@ type CVController interface {
49
  DeleteAchievement(ctx *gin.Context)
50
 
51
  UploadProfileImage(ctx *gin.Context)
52
- GetProgress(ctx *gin.Context)
53
  }
54
 
55
  type cvController struct {
@@ -64,9 +64,13 @@ func NewCVController(cvService services.CVService) CVController {
64
 
65
  // --- Account Details ---
66
  func (c *cvController) SaveAccountDetails(ctx *gin.Context) {
67
- var req models.AccountDetailsRequest
68
  if err := ctx.ShouldBindJSON(&req); err != nil {
69
- response.HandleError(ctx, err)
 
 
 
 
70
  return
71
  }
72
 
@@ -86,7 +90,11 @@ func (c *cvController) GetAccountDetails(ctx *gin.Context) {
86
  accountData := middleware.GetAccountData(ctx)
87
  accountID := int64(accountData.UserID)
88
 
89
- res, err := c.cvService.GetAccountDetails(ctx, accountID)
 
 
 
 
90
  if err != nil {
91
  response.HandleError(ctx, err)
92
  return
@@ -98,9 +106,13 @@ func (c *cvController) GetAccountDetails(ctx *gin.Context) {
98
  // --- Personality & Preference ---
99
 
100
  func (c *cvController) SavePersonalityAndPreference(ctx *gin.Context) {
101
- var req models.PersonalityAndPreferenceCVRequest
102
  if err := ctx.ShouldBindJSON(&req); err != nil {
103
- response.HandleError(ctx, err)
 
 
 
 
104
  return
105
  }
106
 
@@ -120,7 +132,11 @@ func (c *cvController) GetPersonalityAndPreference(ctx *gin.Context) {
120
  accountData := middleware.GetAccountData(ctx)
121
  accountID := int64(accountData.UserID)
122
 
123
- res, err := c.cvService.GetPersonalityAndPreference(ctx, accountID)
 
 
 
 
124
  if err != nil {
125
  response.HandleError(ctx, err)
126
  return
@@ -132,9 +148,13 @@ func (c *cvController) GetPersonalityAndPreference(ctx *gin.Context) {
132
  // --- Family Member ---
133
 
134
  func (c *cvController) CreateFamilyMember(ctx *gin.Context) {
135
- var req models.FamilyMemberRequest
136
  if err := ctx.ShouldBindJSON(&req); err != nil {
137
- response.HandleError(ctx, err)
 
 
 
 
138
  return
139
  }
140
 
@@ -158,13 +178,19 @@ func (c *cvController) UpdateFamilyMember(ctx *gin.Context) {
158
  return
159
  }
160
 
161
- var req models.FamilyMemberRequest
162
  if err := ctx.ShouldBindJSON(&req); err != nil {
163
- response.HandleError(ctx, err)
 
 
 
 
164
  return
165
  }
166
 
167
- res, err := c.cvService.UpdateFamilyMember(ctx, id, &req)
 
 
168
  if err != nil {
169
  response.HandleError(ctx, err)
170
  return
@@ -177,7 +203,11 @@ func (c *cvController) ListFamilyMember(ctx *gin.Context) {
177
  accountData := middleware.GetAccountData(ctx)
178
  accountID := int64(accountData.UserID)
179
 
180
- list, err := c.cvService.ListFamilyMember(ctx, accountID)
 
 
 
 
181
  if err != nil {
182
  response.HandleError(ctx, err)
183
  return
@@ -194,7 +224,11 @@ func (c *cvController) GetFamilyMember(ctx *gin.Context) {
194
  return
195
  }
196
 
197
- res, err := c.cvService.GetFamilyMember(ctx, id)
 
 
 
 
198
  if err != nil {
199
  response.HandleError(ctx, err)
200
  return
@@ -211,7 +245,11 @@ func (c *cvController) DeleteFamilyMember(ctx *gin.Context) {
211
  return
212
  }
213
 
214
- err = c.cvService.DeleteFamilyMember(ctx, id)
 
 
 
 
215
  if err != nil {
216
  response.HandleError(ctx, err)
217
  return
@@ -223,9 +261,13 @@ func (c *cvController) DeleteFamilyMember(ctx *gin.Context) {
223
  // --- Physical and Health ---
224
 
225
  func (c *cvController) SavePhysicalAndHealth(ctx *gin.Context) {
226
- var req models.PhysicalAndHealthRequest
227
  if err := ctx.ShouldBindJSON(&req); err != nil {
228
- response.HandleError(ctx, err)
 
 
 
 
229
  return
230
  }
231
 
@@ -245,7 +287,11 @@ func (c *cvController) GetPhysicalAndHealth(ctx *gin.Context) {
245
  accountData := middleware.GetAccountData(ctx)
246
  accountID := int64(accountData.UserID)
247
 
248
- res, err := c.cvService.GetPhysicalAndHealth(ctx, accountID)
 
 
 
 
249
  if err != nil {
250
  response.HandleError(ctx, err)
251
  return
@@ -257,9 +303,13 @@ func (c *cvController) GetPhysicalAndHealth(ctx *gin.Context) {
257
  // --- Worship and Religious Understanding ---
258
 
259
  func (c *cvController) SaveWorshipAndReligiousUnderstanding(ctx *gin.Context) {
260
- var req models.WorshipAndReligiousUnderstandingRequest
261
  if err := ctx.ShouldBindJSON(&req); err != nil {
262
- response.HandleError(ctx, err)
 
 
 
 
263
  return
264
  }
265
 
@@ -279,7 +329,11 @@ func (c *cvController) GetWorshipAndReligiousUnderstanding(ctx *gin.Context) {
279
  accountData := middleware.GetAccountData(ctx)
280
  accountID := int64(accountData.UserID)
281
 
282
- res, err := c.cvService.GetWorshipAndReligiousUnderstanding(ctx, accountID)
 
 
 
 
283
  if err != nil {
284
  response.HandleError(ctx, err)
285
  return
@@ -290,9 +344,13 @@ func (c *cvController) GetWorshipAndReligiousUnderstanding(ctx *gin.Context) {
290
 
291
  // --- Education ---
292
  func (c *cvController) CreateEducation(ctx *gin.Context) {
293
- var req models.EducationRequest
294
  if err := ctx.ShouldBindJSON(&req); err != nil {
295
- response.HandleError(ctx, err)
 
 
 
 
296
  return
297
  }
298
 
@@ -316,13 +374,19 @@ func (c *cvController) UpdateEducation(ctx *gin.Context) {
316
  return
317
  }
318
 
319
- var req models.EducationRequest
320
  if err := ctx.ShouldBindJSON(&req); err != nil {
321
- response.HandleError(ctx, err)
 
 
 
 
322
  return
323
  }
324
 
325
- res, err := c.cvService.UpdateEducation(ctx, id, &req)
 
 
326
  if err != nil {
327
  response.HandleError(ctx, err)
328
  return
@@ -335,7 +399,11 @@ func (c *cvController) ListEducation(ctx *gin.Context) {
335
  accountData := middleware.GetAccountData(ctx)
336
  accountID := int64(accountData.UserID)
337
 
338
- res, err := c.cvService.ListEducation(ctx, accountID)
 
 
 
 
339
  if err != nil {
340
  response.HandleError(ctx, err)
341
  return
@@ -352,7 +420,11 @@ func (c *cvController) GetEducation(ctx *gin.Context) {
352
  return
353
  }
354
 
355
- res, err := c.cvService.GetEducation(ctx, id)
 
 
 
 
356
  if err != nil {
357
  response.HandleError(ctx, err)
358
  return
@@ -369,7 +441,11 @@ func (c *cvController) DeleteEducation(ctx *gin.Context) {
369
  return
370
  }
371
 
372
- err = c.cvService.DeleteEducation(ctx, id)
 
 
 
 
373
  if err != nil {
374
  response.HandleError(ctx, err)
375
  return
@@ -380,9 +456,13 @@ func (c *cvController) DeleteEducation(ctx *gin.Context) {
380
 
381
  // --- Job ---
382
  func (c *cvController) CreateJob(ctx *gin.Context) {
383
- var req models.JobRequest
384
  if err := ctx.ShouldBindJSON(&req); err != nil {
385
- response.HandleError(ctx, err)
 
 
 
 
386
  return
387
  }
388
 
@@ -406,13 +486,19 @@ func (c *cvController) UpdateJob(ctx *gin.Context) {
406
  return
407
  }
408
 
409
- var req models.JobRequest
410
  if err := ctx.ShouldBindJSON(&req); err != nil {
411
- response.HandleError(ctx, err)
 
 
 
 
412
  return
413
  }
414
 
415
- res, err := c.cvService.UpdateJob(ctx, id, &req)
 
 
416
  if err != nil {
417
  response.HandleError(ctx, err)
418
  return
@@ -425,7 +511,11 @@ func (c *cvController) ListJob(ctx *gin.Context) {
425
  accountData := middleware.GetAccountData(ctx)
426
  accountID := int64(accountData.UserID)
427
 
428
- res, err := c.cvService.ListJob(ctx, accountID)
 
 
 
 
429
  if err != nil {
430
  response.HandleError(ctx, err)
431
  return
@@ -442,7 +532,11 @@ func (c *cvController) GetJob(ctx *gin.Context) {
442
  return
443
  }
444
 
445
- res, err := c.cvService.GetJob(ctx, id)
 
 
 
 
446
  if err != nil {
447
  response.HandleError(ctx, err)
448
  return
@@ -459,7 +553,11 @@ func (c *cvController) DeleteJob(ctx *gin.Context) {
459
  return
460
  }
461
 
462
- err = c.cvService.DeleteJob(ctx, id)
 
 
 
 
463
  if err != nil {
464
  response.HandleError(ctx, err)
465
  return
@@ -470,9 +568,13 @@ func (c *cvController) DeleteJob(ctx *gin.Context) {
470
 
471
  // --- Achievement ---
472
  func (c *cvController) CreateAchievement(ctx *gin.Context) {
473
- var req models.AchievementRequest
474
  if err := ctx.ShouldBindJSON(&req); err != nil {
475
- response.HandleError(ctx, err)
 
 
 
 
476
  return
477
  }
478
 
@@ -496,13 +598,19 @@ func (c *cvController) UpdateAchievement(ctx *gin.Context) {
496
  return
497
  }
498
 
499
- var req models.AchievementRequest
500
  if err := ctx.ShouldBindJSON(&req); err != nil {
501
- response.HandleError(ctx, err)
 
 
 
 
502
  return
503
  }
504
 
505
- res, err := c.cvService.UpdateAchievement(ctx, id, &req)
 
 
506
  if err != nil {
507
  response.HandleError(ctx, err)
508
  return
@@ -515,7 +623,11 @@ func (c *cvController) ListAchievement(ctx *gin.Context) {
515
  accountData := middleware.GetAccountData(ctx)
516
  accountID := int64(accountData.UserID)
517
 
518
- res, err := c.cvService.ListAchievement(ctx, accountID)
 
 
 
 
519
  if err != nil {
520
  response.HandleError(ctx, err)
521
  return
@@ -532,7 +644,11 @@ func (c *cvController) GetAchievement(ctx *gin.Context) {
532
  return
533
  }
534
 
535
- res, err := c.cvService.GetAchievement(ctx, id)
 
 
 
 
536
  if err != nil {
537
  response.HandleError(ctx, err)
538
  return
@@ -549,7 +665,11 @@ func (c *cvController) DeleteAchievement(ctx *gin.Context) {
549
  return
550
  }
551
 
552
- err = c.cvService.DeleteAchievement(ctx, id)
 
 
 
 
553
  if err != nil {
554
  response.HandleError(ctx, err)
555
  return
@@ -576,11 +696,15 @@ func (c *cvController) UploadProfileImage(ctx *gin.Context) {
576
  response.HandleSuccess(ctx, http.StatusOK, "Profile image uploaded successfully", res, nil)
577
  }
578
 
579
- func (c *cvController) GetProgress(ctx *gin.Context) {
580
  accountData := middleware.GetAccountData(ctx)
581
  accountID := int64(accountData.UserID)
582
 
583
- res, err := c.cvService.GetProgress(ctx, accountID)
 
 
 
 
584
  if err != nil {
585
  response.HandleError(ctx, err)
586
  return
 
49
  DeleteAchievement(ctx *gin.Context)
50
 
51
  UploadProfileImage(ctx *gin.Context)
52
+ GetProgressCV(ctx *gin.Context)
53
  }
54
 
55
  type cvController struct {
 
64
 
65
  // --- Account Details ---
66
  func (c *cvController) SaveAccountDetails(ctx *gin.Context) {
67
+ var req models.SaveAccountDetailsRequest
68
  if err := ctx.ShouldBindJSON(&req); err != nil {
69
+ response.HandleError(ctx, models.Exception{
70
+ Message: "Invalid body request",
71
+ BadRequest: true,
72
+ Err: err,
73
+ })
74
  return
75
  }
76
 
 
90
  accountData := middleware.GetAccountData(ctx)
91
  accountID := int64(accountData.UserID)
92
 
93
+ req := models.GetAccountDetailsRequest{
94
+ AccountID: accountID,
95
+ }
96
+
97
+ res, err := c.cvService.GetAccountDetails(ctx, &req)
98
  if err != nil {
99
  response.HandleError(ctx, err)
100
  return
 
106
  // --- Personality & Preference ---
107
 
108
  func (c *cvController) SavePersonalityAndPreference(ctx *gin.Context) {
109
+ var req models.SavePersonalityAndPreferenceRequest
110
  if err := ctx.ShouldBindJSON(&req); err != nil {
111
+ response.HandleError(ctx, models.Exception{
112
+ Message: "Invalid body request",
113
+ BadRequest: true,
114
+ Err: err,
115
+ })
116
  return
117
  }
118
 
 
132
  accountData := middleware.GetAccountData(ctx)
133
  accountID := int64(accountData.UserID)
134
 
135
+ req := models.GetPersonalityAndPreferenceRequest{
136
+ AccountID: accountID,
137
+ }
138
+
139
+ res, err := c.cvService.GetPersonalityAndPreference(ctx, &req)
140
  if err != nil {
141
  response.HandleError(ctx, err)
142
  return
 
148
  // --- Family Member ---
149
 
150
  func (c *cvController) CreateFamilyMember(ctx *gin.Context) {
151
+ var req models.CreateFamilyMemberRequest
152
  if err := ctx.ShouldBindJSON(&req); err != nil {
153
+ response.HandleError(ctx, models.Exception{
154
+ Message: "Invalid body request",
155
+ BadRequest: true,
156
+ Err: err,
157
+ })
158
  return
159
  }
160
 
 
178
  return
179
  }
180
 
181
+ var req models.UpdateFamilyMemberRequest
182
  if err := ctx.ShouldBindJSON(&req); err != nil {
183
+ response.HandleError(ctx, models.Exception{
184
+ Message: "Invalid body request",
185
+ BadRequest: true,
186
+ Err: err,
187
+ })
188
  return
189
  }
190
 
191
+ req.ID = id
192
+
193
+ res, err := c.cvService.UpdateFamilyMember(ctx, &req)
194
  if err != nil {
195
  response.HandleError(ctx, err)
196
  return
 
203
  accountData := middleware.GetAccountData(ctx)
204
  accountID := int64(accountData.UserID)
205
 
206
+ req := models.ListFamilyMemberRequest{
207
+ AccountID: accountID,
208
+ }
209
+
210
+ list, err := c.cvService.ListFamilyMember(ctx, &req)
211
  if err != nil {
212
  response.HandleError(ctx, err)
213
  return
 
224
  return
225
  }
226
 
227
+ req := models.GetFamilyMemberRequest{
228
+ ID: id,
229
+ }
230
+
231
+ res, err := c.cvService.GetFamilyMember(ctx, &req)
232
  if err != nil {
233
  response.HandleError(ctx, err)
234
  return
 
245
  return
246
  }
247
 
248
+ req := models.DeleteFamilyMemberRequest{
249
+ ID: id,
250
+ }
251
+
252
+ err = c.cvService.DeleteFamilyMember(ctx, &req)
253
  if err != nil {
254
  response.HandleError(ctx, err)
255
  return
 
261
  // --- Physical and Health ---
262
 
263
  func (c *cvController) SavePhysicalAndHealth(ctx *gin.Context) {
264
+ var req models.SavePhysicalAndHealthRequest
265
  if err := ctx.ShouldBindJSON(&req); err != nil {
266
+ response.HandleError(ctx, models.Exception{
267
+ Message: "Invalid body request",
268
+ BadRequest: true,
269
+ Err: err,
270
+ })
271
  return
272
  }
273
 
 
287
  accountData := middleware.GetAccountData(ctx)
288
  accountID := int64(accountData.UserID)
289
 
290
+ req := models.GetPhysicalAndHealthRequest{
291
+ AccountID: accountID,
292
+ }
293
+
294
+ res, err := c.cvService.GetPhysicalAndHealth(ctx, &req)
295
  if err != nil {
296
  response.HandleError(ctx, err)
297
  return
 
303
  // --- Worship and Religious Understanding ---
304
 
305
  func (c *cvController) SaveWorshipAndReligiousUnderstanding(ctx *gin.Context) {
306
+ var req models.SaveWorshipAndReligiousUnderstandingRequest
307
  if err := ctx.ShouldBindJSON(&req); err != nil {
308
+ response.HandleError(ctx, models.Exception{
309
+ Message: "Invalid body request",
310
+ BadRequest: true,
311
+ Err: err,
312
+ })
313
  return
314
  }
315
 
 
329
  accountData := middleware.GetAccountData(ctx)
330
  accountID := int64(accountData.UserID)
331
 
332
+ req := models.GetWorshipAndReligiousUnderstandingRequest{
333
+ AccountID: accountID,
334
+ }
335
+
336
+ res, err := c.cvService.GetWorshipAndReligiousUnderstanding(ctx, &req)
337
  if err != nil {
338
  response.HandleError(ctx, err)
339
  return
 
344
 
345
  // --- Education ---
346
  func (c *cvController) CreateEducation(ctx *gin.Context) {
347
+ var req models.CreateEducationRequest
348
  if err := ctx.ShouldBindJSON(&req); err != nil {
349
+ response.HandleError(ctx, models.Exception{
350
+ Message: "Invalid body request",
351
+ BadRequest: true,
352
+ Err: err,
353
+ })
354
  return
355
  }
356
 
 
374
  return
375
  }
376
 
377
+ var req models.UpdateEducationRequest
378
  if err := ctx.ShouldBindJSON(&req); err != nil {
379
+ response.HandleError(ctx, models.Exception{
380
+ Message: "Invalid body request",
381
+ BadRequest: true,
382
+ Err: err,
383
+ })
384
  return
385
  }
386
 
387
+ req.ID = id
388
+
389
+ res, err := c.cvService.UpdateEducation(ctx, &req)
390
  if err != nil {
391
  response.HandleError(ctx, err)
392
  return
 
399
  accountData := middleware.GetAccountData(ctx)
400
  accountID := int64(accountData.UserID)
401
 
402
+ req := models.ListEducationRequest{
403
+ AccountID: accountID,
404
+ }
405
+
406
+ res, err := c.cvService.ListEducation(ctx, &req)
407
  if err != nil {
408
  response.HandleError(ctx, err)
409
  return
 
420
  return
421
  }
422
 
423
+ req := models.GetEducationRequest{
424
+ ID: id,
425
+ }
426
+
427
+ res, err := c.cvService.GetEducation(ctx, &req)
428
  if err != nil {
429
  response.HandleError(ctx, err)
430
  return
 
441
  return
442
  }
443
 
444
+ req := models.DeleteEducationRequest{
445
+ ID: id,
446
+ }
447
+
448
+ err = c.cvService.DeleteEducation(ctx, &req)
449
  if err != nil {
450
  response.HandleError(ctx, err)
451
  return
 
456
 
457
  // --- Job ---
458
  func (c *cvController) CreateJob(ctx *gin.Context) {
459
+ var req models.CreateJobRequest
460
  if err := ctx.ShouldBindJSON(&req); err != nil {
461
+ response.HandleError(ctx, models.Exception{
462
+ Message: "Invalid body request",
463
+ BadRequest: true,
464
+ Err: err,
465
+ })
466
  return
467
  }
468
 
 
486
  return
487
  }
488
 
489
+ var req models.UpdateJobRequest
490
  if err := ctx.ShouldBindJSON(&req); err != nil {
491
+ response.HandleError(ctx, models.Exception{
492
+ Message: "Invalid body request",
493
+ BadRequest: true,
494
+ Err: err,
495
+ })
496
  return
497
  }
498
 
499
+ req.ID = id
500
+
501
+ res, err := c.cvService.UpdateJob(ctx, &req)
502
  if err != nil {
503
  response.HandleError(ctx, err)
504
  return
 
511
  accountData := middleware.GetAccountData(ctx)
512
  accountID := int64(accountData.UserID)
513
 
514
+ req := models.ListJobRequest{
515
+ AccountID: accountID,
516
+ }
517
+
518
+ res, err := c.cvService.ListJob(ctx, &req)
519
  if err != nil {
520
  response.HandleError(ctx, err)
521
  return
 
532
  return
533
  }
534
 
535
+ req := models.GetJobRequest{
536
+ ID: id,
537
+ }
538
+
539
+ res, err := c.cvService.GetJob(ctx, &req)
540
  if err != nil {
541
  response.HandleError(ctx, err)
542
  return
 
553
  return
554
  }
555
 
556
+ req := models.DeleteJobRequest{
557
+ ID: id,
558
+ }
559
+
560
+ err = c.cvService.DeleteJob(ctx, &req)
561
  if err != nil {
562
  response.HandleError(ctx, err)
563
  return
 
568
 
569
  // --- Achievement ---
570
  func (c *cvController) CreateAchievement(ctx *gin.Context) {
571
+ var req models.CreateAchievementRequest
572
  if err := ctx.ShouldBindJSON(&req); err != nil {
573
+ response.HandleError(ctx, models.Exception{
574
+ Message: "Invalid body request",
575
+ BadRequest: true,
576
+ Err: err,
577
+ })
578
  return
579
  }
580
 
 
598
  return
599
  }
600
 
601
+ var req models.UpdateAchievementRequest
602
  if err := ctx.ShouldBindJSON(&req); err != nil {
603
+ response.HandleError(ctx, models.Exception{
604
+ Message: "Invalid body request",
605
+ BadRequest: true,
606
+ Err: err,
607
+ })
608
  return
609
  }
610
 
611
+ req.ID = id
612
+
613
+ res, err := c.cvService.UpdateAchievement(ctx, &req)
614
  if err != nil {
615
  response.HandleError(ctx, err)
616
  return
 
623
  accountData := middleware.GetAccountData(ctx)
624
  accountID := int64(accountData.UserID)
625
 
626
+ req := models.ListAchievementRequest{
627
+ AccountID: accountID,
628
+ }
629
+
630
+ res, err := c.cvService.ListAchievement(ctx, &req)
631
  if err != nil {
632
  response.HandleError(ctx, err)
633
  return
 
644
  return
645
  }
646
 
647
+ req := models.GetAchievementRequest{
648
+ ID: id,
649
+ }
650
+
651
+ res, err := c.cvService.GetAchievement(ctx, &req)
652
  if err != nil {
653
  response.HandleError(ctx, err)
654
  return
 
665
  return
666
  }
667
 
668
+ req := models.DeleteAchievementRequest{
669
+ ID: id,
670
+ }
671
+
672
+ err = c.cvService.DeleteAchievement(ctx, &req)
673
  if err != nil {
674
  response.HandleError(ctx, err)
675
  return
 
696
  response.HandleSuccess(ctx, http.StatusOK, "Profile image uploaded successfully", res, nil)
697
  }
698
 
699
+ func (c *cvController) GetProgressCV(ctx *gin.Context) {
700
  accountData := middleware.GetAccountData(ctx)
701
  accountID := int64(accountData.UserID)
702
 
703
+ req := models.GetProgressCVRequest{
704
+ AccountID: accountID,
705
+ }
706
+
707
+ res, err := c.cvService.GetProgressCV(ctx, &req)
708
  if err != nil {
709
  response.HandleError(ctx, err)
710
  return
space/space/space/space/space/space/models/database_orm_model.go CHANGED
@@ -231,43 +231,43 @@ type (
231
  }
232
 
233
  PhysicalAndHealthCV struct {
234
- ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
235
- AccountID int64 `gorm:"column:account_id;not null;unique" json:"account_id"`
236
- Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"`
237
- HeightInCm *int `gorm:"column:height_cm" json:"height_cm"` // Tinggi badan dalam satuan sentimeter
238
- WeightInKg *int `gorm:"column:weight_kg" json:"weight_kg"` // Berat badan dalam satuan kilogram
239
- BodyShape *string `gorm:"column:body_shape" json:"body_shape"` // Bentuk tubuh
240
- SkinColor *string `gorm:"column:skin_color" json:"skin_color"` // Warna kulit
241
- HairType *string `gorm:"column:hair_type" json:"hair_type"` // Tipe rambut
242
- MedicalHistory *string `gorm:"column:medical_history" json:"medical_history"` // Riwayat penyakit
243
- PhysicalDisorder *string `gorm:"column:physical_disorder" json:"physical_disorder"` // Cacat fisik
244
- PhysicalTraits *string `gorm:"column:physical_traits" json:"physical_traits"` // Ciri khas fisik
245
- CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"` // Waktu data dibuat
246
- UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"` // Waktu data terakhir diperbarui
247
  FieldCounter
248
  }
249
 
250
  WorshipAndReligiousUnderstandingCV struct {
251
- ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
252
- AccountID int64 `gorm:"column:account_id;not null;unique" json:"account_id"`
253
- Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"`
254
- ObligatoryPrayer *string `gorm:"column:obligatory_prayer" json:"obligatory_prayer"` // sholat_wajib_5_waktu
255
- CongregationalPrayer *string `gorm:"column:congregational_prayer" json:"congregational_prayer"` // sholat_berjamaah_di_masjid
256
- TahajjudPrayer *string `gorm:"column:tahajjud_prayer" json:"tahajjud_prayer"` // sholat_tahajud
257
- DhuhaPrayer *string `gorm:"column:dhuha_prayer" json:"dhuha_prayer"` // sholat_dhuha
258
- QuranMemorization *string `gorm:"column:quran_memorization" json:"quran_memorization"` // hafalan_alquran
259
- QuranReadingAbility *string `gorm:"column:quran_reading_ability" json:"quran_reading_ability"` // kemampuan_baca_alquran
260
- DaudFasting *string `gorm:"column:daud_fasting" json:"daud_fasting"` // puasa_daud
261
- AyyamulBidhFasting *string `gorm:"column:ayyamul_bidh_fasting" json:"ayyamul_bidh_fasting"` // puasa_ayyamul_bidh
262
- HajjOrUmrah pq.StringArray `gorm:"column:hajj_or_umrah;type:varchar(255)[]" json:"hajj_or_umrah"` // ibadah_haji_umroh
263
- ListeningToMusic *string `gorm:"column:listening_to_music" json:"listening_to_music"` // mendengarkan_musik
264
- OpinionOnIkhtilat *string `gorm:"column:opinion_on_ikhtilat" json:"opinion_on_ikhtilat"` // pendapat_ikhtilat
265
- OpinionOnTouchingNonMahram *string `gorm:"column:opinion_on_touching_non_mahram" json:"opinion_on_touching_non_mahram"` // pendapat_menyentuh_non_mahram
266
- OpinionOnVeil *string `gorm:"column:opinion_on_veil" json:"opinion_on_veil"` // pendapat_tentang_cadar
267
- WeeklyReligiousStudies *string `gorm:"column:weekly_religious_studies" json:"weekly_religious_studies"` // kajian_yang_diikuti_dalam_sepekan
268
- FollowedUstadz *string `gorm:"column:followed_ustadz" json:"followed_ustadz"` // ustadz_yang_diikuti
269
- CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"`
270
- UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"`
271
  FieldCounter
272
  }
273
 
@@ -285,16 +285,16 @@ type (
285
  }
286
 
287
  JobCV struct {
288
- ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // id
289
- AccountID int64 `gorm:"column:account_id;not null" json:"account_id"` // id akun
290
- Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"` // relasi ke akun
291
- InstitutionName *string `gorm:"column:institution_name" json:"institution_name"` // nama instansi
292
- CurrentJob *string `gorm:"column:current_job" json:"current_job"` // pekerjaan saat ini
293
- YearStartedWorking *int `gorm:"column:year_started_working" json:"year_started_working"` // tahun mulai bekerja
294
- MonthlyIncome *string `gorm:"column:monthly_income" json:"monthly_income"` // penghasilan per bulan
295
- IncomeSources pq.StringArray `gorm:"column:income_sources;type:varchar(255)[]" json:"income_sources"` // sumber penghasilan
296
- CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"` // tanggal dibuat
297
- UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"` // tanggal diperbarui
298
  }
299
 
300
  AchievementCV struct {
@@ -339,6 +339,55 @@ func (w WorshipAndReligiousUnderstandingCV) GetFilledFields() []string {
339
  return w.FieldCounter.GetFilledFields(w)
340
  }
341
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
342
  // Gorm table name settings
343
  func (Account) TableName() string { return "account" }
344
  func (AccountDetails) TableName() string { return "account_details" }
@@ -369,3 +418,6 @@ func (WorshipAndReligiousUnderstandingCV) TableName() string {
369
  func (EducationCV) TableName() string { return "education_cv" }
370
  func (JobCV) TableName() string { return "job_cv" }
371
  func (AchievementCV) TableName() string { return "achievement_cv" }
 
 
 
 
231
  }
232
 
233
  PhysicalAndHealthCV struct {
234
+ ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
235
+ AccountID int64 `gorm:"column:account_id;not null;unique" json:"account_id"`
236
+ Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"`
237
+ HeightInCm *int `gorm:"column:height_cm" json:"height_cm"` // Tinggi badan dalam satuan sentimeter
238
+ WeightInKg *int `gorm:"column:weight_kg" json:"weight_kg"` // Berat badan dalam satuan kilogram
239
+ BodyShape *string `gorm:"column:body_shape" json:"body_shape"` // Bentuk tubuh
240
+ SkinColor *string `gorm:"column:skin_color" json:"skin_color"` // Warna kulit
241
+ HairType *string `gorm:"column:hair_type" json:"hair_type"` // Tipe rambut
242
+ MedicalHistory *pq.StringArray `gorm:"column:medical_history;type:varchar(255)[]" json:"medical_history"` // Riwayat penyakit
243
+ PhysicalDisorder *string `gorm:"column:physical_disorder" json:"physical_disorder"` // Cacat fisik
244
+ PhysicalTraits *string `gorm:"column:physical_traits" json:"physical_traits"` // Ciri khas fisik
245
+ CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"` // Waktu data dibuat
246
+ UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"` // Waktu data terakhir diperbarui
247
  FieldCounter
248
  }
249
 
250
  WorshipAndReligiousUnderstandingCV struct {
251
+ ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
252
+ AccountID int64 `gorm:"column:account_id;not null;unique" json:"account_id"`
253
+ Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"`
254
+ ObligatoryPrayer *string `gorm:"column:obligatory_prayer" json:"obligatory_prayer"` // sholat_wajib_5_waktu
255
+ CongregationalPrayer *string `gorm:"column:congregational_prayer" json:"congregational_prayer"` // sholat_berjamaah_di_masjid
256
+ TahajjudPrayer *string `gorm:"column:tahajjud_prayer" json:"tahajjud_prayer"` // sholat_tahajud
257
+ DhuhaPrayer *string `gorm:"column:dhuha_prayer" json:"dhuha_prayer"` // sholat_dhuha
258
+ QuranMemorization *string `gorm:"column:quran_memorization" json:"quran_memorization"` // hafalan_alquran
259
+ QuranReadingAbility *string `gorm:"column:quran_reading_ability" json:"quran_reading_ability"` // kemampuan_baca_alquran
260
+ DaudFasting *string `gorm:"column:daud_fasting" json:"daud_fasting"` // puasa_daud
261
+ AyyamulBidhFasting *string `gorm:"column:ayyamul_bidh_fasting" json:"ayyamul_bidh_fasting"` // puasa_ayyamul_bidh
262
+ HajjOrUmrah *pq.StringArray `gorm:"column:hajj_or_umrah;type:varchar(255)[]" json:"hajj_or_umrah"` // ibadah_haji_umroh
263
+ ListeningToMusic *string `gorm:"column:listening_to_music" json:"listening_to_music"` // mendengarkan_musik
264
+ OpinionOnIkhtilat *string `gorm:"column:opinion_on_ikhtilat" json:"opinion_on_ikhtilat"` // pendapat_ikhtilat
265
+ OpinionOnTouchingNonMahram *string `gorm:"column:opinion_on_touching_non_mahram" json:"opinion_on_touching_non_mahram"` // pendapat_menyentuh_non_mahram
266
+ OpinionOnVeil *string `gorm:"column:opinion_on_veil" json:"opinion_on_veil"` // pendapat_tentang_cadar
267
+ WeeklyReligiousStudies *string `gorm:"column:weekly_religious_studies" json:"weekly_religious_studies"` // kajian_yang_diikuti_dalam_sepekan
268
+ FollowedUstadz *string `gorm:"column:followed_ustadz" json:"followed_ustadz"` // ustadz_yang_diikuti
269
+ CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"`
270
+ UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"`
271
  FieldCounter
272
  }
273
 
 
285
  }
286
 
287
  JobCV struct {
288
+ ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // id
289
+ AccountID int64 `gorm:"column:account_id;not null" json:"account_id"` // id akun
290
+ Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"` // relasi ke akun
291
+ InstitutionName *string `gorm:"column:institution_name" json:"institution_name"` // nama instansi
292
+ CurrentJob *string `gorm:"column:current_job" json:"current_job"` // pekerjaan saat ini
293
+ YearStartedWorking *int `gorm:"column:year_started_working" json:"year_started_working"` // tahun mulai bekerja
294
+ MonthlyIncome *string `gorm:"column:monthly_income" json:"monthly_income"` // penghasilan per bulan
295
+ IncomeSources *pq.StringArray `gorm:"column:income_sources;type:varchar(255)[]" json:"income_sources"` // sumber penghasilan
296
+ CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"` // tanggal dibuat
297
+ UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"` // tanggal diperbarui
298
  }
299
 
300
  AchievementCV struct {
 
339
  return w.FieldCounter.GetFilledFields(w)
340
  }
341
 
342
+ type (
343
+ MarriageReadinessProfile struct {
344
+ ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
345
+ AccountID int64 `gorm:"column:account_id;not null;unique" json:"account_id"`
346
+ Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"`
347
+
348
+ // Visi Misi Rumah Tangga
349
+ MarriageVision *string `gorm:"column:marriage_vision" json:"marriage_vision"` // Apa visi dan tujuan utama kamu dalam membangun rumah tangga?
350
+ LivingPlan *string `gorm:"column:living_plan" json:"living_plan"` // Setelah menikah, kamu berencana tinggal di mana?
351
+ SpouseRoles *string `gorm:"column:spouse_roles" json:"spouse_roles"` // Menurutmu, apa peran utama suami dan istri dalam rumah tangga?
352
+ SpouseRights *string `gorm:"column:spouse_rights" json:"spouse_rights"` // Apa saja hak suami dan istri menurutmu?
353
+ ConflictResolution *string `gorm:"column:conflict_resolution" json:"conflict_resolution"` // Jika terjadi konflik, bagaimana kamu menyikapinya?
354
+ ParentingStyle *string `gorm:"column:parenting_style" json:"parenting_style"` // Setelah punya anak, pola pengasuhan seperti apa yang kamu inginkan?
355
+
356
+ // Konsep Acara Pernikahan
357
+ ReadyToMarryDuration *string `gorm:"column:ready_to_marry_duration" json:"ready_to_marry_duration"` // Setelah taaruf dimulai, berapa lama kamu butuh untuk siap menikah?
358
+ WeddingConcept *string `gorm:"column:wedding_concept" json:"wedding_concept"` // Seperti apa konsep pernikahan yang kamu harapkan?
359
+ WeddingFunding *string `gorm:"column:wedding_funding" json:"wedding_funding"` // Apakah kamu sudah mempersiapkan dana pernikahan? Dari mana sumbernya?
360
+
361
+ // Karir Kedepannya
362
+ CareerAfterMarriage *string `gorm:"column:career_after_marriage" json:"career_after_marriage"` // Apakah kamu ingin tetap bekerja setelah menikah?
363
+ TimeManagement *string `gorm:"column:time_management" json:"time_management"` // Bagaimana kamu membagi waktu antara keluarga, ibadah, dan pekerjaan?
364
+ CareerGoals *string `gorm:"column:career_goals" json:"career_goals"` // Apa impian karier atau cita-cita kamu dalam 5 sampai 10 tahun ke depan?
365
+ SelfDevelopment *string `gorm:"column:self_development" json:"self_development"` // Apa usaha kamu untuk terus mengembangkan diri dan skill?
366
+
367
+ // Pendidikan Keluarga
368
+ DelayChildren *string `gorm:"column:delay_children" json:"delay_children"` // Apakah kamu berencana menunda memiliki anak?
369
+ ChildEducationPlan *string `gorm:"column:child_education_plan" json:"child_education_plan"` // Apakah kamu sudah punya rencana pendidikan anak?
370
+ ReligiousEmotionalBond *string `gorm:"column:religious_emotional_bond" json:"religious_emotional_bond"` // Bagaimana cara menjaga nilai agama & kedekatan emosional dengan anak?
371
+ ParentingChallenges *string `gorm:"column:parenting_challenges" json:"parenting_challenges"` // Saat menghadapi tantangan pengasuhan, bagaimana kamu menyikapinya?
372
+
373
+ // Finansial Keluarga
374
+ MonthlyFinance *string `gorm:"column:monthly_finance" json:"monthly_finance"` // Bagaimana kamu mengelola keuangan bulanan?
375
+ FamilyResponsibility *string `gorm:"column:family_responsibility" json:"family_responsibility"` // Apakah kamu masih punya tanggungan keluarga setelah menikah?
376
+ DebtStatus *string `gorm:"column:debt_status" json:"debt_status"` // Apakah kamu punya cicilan/utang setelah menikah?
377
+ FinanceSharing *string `gorm:"column:finance_sharing" json:"finance_sharing"` // Setelah menikah, bagaimana pembagian tanggung jawab keuangan?
378
+ IncomeGapView *string `gorm:"column:income_gap_view" json:"income_gap_view"` // Pandangan kamu jika istri berpenghasilan lebih besar dari suami?
379
+
380
+ // Keputusan dan Komunikasi
381
+ DecisionMaking *string `gorm:"column:decision_making" json:"decision_making"` // Dalam mengambil keputusan, kamu lebih mempertimbangkan pasangan atau sendiri?
382
+ GrowthTogether *string `gorm:"column:growth_together" json:"growth_together"` // Apakah kamu siap berjuang bersama pasangan? Apa saja yang ingin diperjuangkan?
383
+ ParentIntervention *string `gorm:"column:parent_intervention" json:"parent_intervention"` // Sejauh mana orang tua/mertua boleh ikut campur dalam rumah tangga?
384
+ HabitResponse *string `gorm:"column:habit_response" json:"habit_response"` // Bagaimana kamu menyikapi kebiasaan pasangan yang kurang kamu sukai?
385
+
386
+ CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"`
387
+ UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"`
388
+ }
389
+ )
390
+
391
  // Gorm table name settings
392
  func (Account) TableName() string { return "account" }
393
  func (AccountDetails) TableName() string { return "account_details" }
 
418
  func (EducationCV) TableName() string { return "education_cv" }
419
  func (JobCV) TableName() string { return "job_cv" }
420
  func (AchievementCV) TableName() string { return "achievement_cv" }
421
+ func (MarriageReadinessProfile) TableName() string {
422
+ return "marriage_readiness_profile"
423
+ }
space/space/space/space/space/space/models/request_model.go CHANGED
@@ -58,8 +58,9 @@ type AnswerQuizRequest struct {
58
  }
59
 
60
  type (
61
- PersonalityAndPreferenceCVRequest struct {
62
- AccountID int64 `json:"-"`
 
63
  PositiveTraits *string `json:"positive_traits"` // sifat positif
64
  NegativeTraits *string `json:"negative_traits"` // sifat negatif
65
  Hobbies *string `json:"hobbies"` // hobi
@@ -76,8 +77,25 @@ type (
76
  MonthlyExpenses *string `json:"monthly_expenses" validate:"monthly_expenses"` // pengeluaran per bulan
77
  }
78
 
79
- FamilyMemberRequest struct {
80
- AccountID int64 `json:"-"`
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  Role *string `json:"role" validate:"family_role"` // Peran dalam keluarga
82
  Status *string `json:"status" validate:"life_status"` // Status (Hidup, Wafat)
83
  Religion *string `json:"religion" validate:"religion"` // Agama
@@ -86,19 +104,35 @@ type (
86
  Age *int `json:"age"` // Usia
87
  }
88
 
89
- PhysicalAndHealthRequest struct {
90
- AccountID int64 `json:"-"`
91
- HeightInCm *int `json:"height_cm"` // Tinggi badan dalam satuan sentimeter
92
- WeightInKg *int `json:"weight_kg"` // Berat badan dalam satuan kilogram
93
- BodyShape *string `json:"body_shape" validate:"body_shape"` // Bentuk tubuh
94
- SkinColor *string `json:"skin_color" validate:"skin_color"` // Warna kulit
95
- HairType *string `json:"hair_type" validate:"hair_type"` // Tipe rambut
96
- MedicalHistory *string `json:"medical_history"` // Riwayat penyakit
97
- PhysicalDisorder *string `json:"physical_disorder"` // Cacat fisik
98
- PhysicalTraits *string `json:"physical_traits"` // Ciri khas fisik
99
  }
100
 
101
- AccountDetailsRequest struct {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  AccountID int64 `json:"-"`
103
  FullName *string `json:"full_name"`
104
  Gender *string `json:"gender" validate:"gender"`
@@ -111,27 +145,45 @@ type (
111
  PhoneNumber *string `json:"phone_number" validate:"phone_number"`
112
  }
113
 
114
- WorshipAndReligiousUnderstandingRequest struct {
115
- AccountID int64 `json:"-"`
116
- ObligatoryPrayer *string `json:"obligatory_prayer"` // sholat_wajib_5_waktu
117
- CongregationalPrayer *string `json:"congregational_prayer"` // sholat_berjamaah_di_masjid
118
- TahajjudPrayer *string `json:"tahajjud_prayer"` // sholat_tahajud
119
- DhuhaPrayer *string `json:"dhuha_prayer"` // sholat_dhuha
120
- QuranMemorization *string `json:"quran_memorization"` // hafalan_alquran
121
- QuranReadingAbility *string `json:"quran_reading_ability" validate:"quran_reading_ability"` // kemampuan_baca_alquran
122
- DaudFasting *string `json:"daud_fasting"` // puasa_daud
123
- AyyamulBidhFasting *string `json:"ayyamul_bidh_fasting"` // puasa_ayyamul_bidh
124
- HajjOrUmrah pq.StringArray `json:"hajj_or_umrah"` // ibadah_haji_umroh
125
- ListeningToMusic *string `json:"listening_to_music"` // mendengarkan_musik
126
- OpinionOnIkhtilat *string `json:"opinion_on_ikhtilat"` // pendapat_ikhtilat
127
- OpinionOnTouchingNonMahram *string `json:"opinion_on_touching_non_mahram"` // pendapat_menyentuh_non_mahram
128
- OpinionOnVeil *string `json:"opinion_on_veil"` // pendapat_tentang_cadar
129
- WeeklyReligiousStudies *string `json:"weekly_religious_studies"` // kajian_yang_diikuti_dalam_sepekan
130
- FollowedUstadz *string `json:"followed_ustadz"` // ustadz_yang_diikuti
131
- }
132
-
133
- EducationRequest struct {
134
- AccountID int64 `json:"account_id"`
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
  LastEducation *string `json:"last_education" validate:"last_education"` // pendidikan terakhir
136
  EducationInstitute *string `json:"education_institute"` // institusi pendidikan
137
  EducationMajor *string `json:"education_major"` // jurusan pendidikan
@@ -139,20 +191,72 @@ type (
139
  YearGraduate *int `json:"year_graduate"` // tahun lulus
140
  }
141
 
142
- JobRequest struct {
143
- AccountID int64 `json:"account_id"`
144
- InstitutionName *string `json:"institution_name"` // nama instansi
145
- CurrentJob *string `json:"current_job"` // pekerjaan saat ini
146
- YearStartedWorking *int `json:"year_started_working"` // tahun mulai bekerja
147
- MonthlyIncome *string `json:"monthly_income" validate:"monthly_income"` // penghasilan per bulan
148
- IncomeSources pq.StringArray `json:"income_sources"` // sumber penghasilan
149
  }
150
 
151
- AchievementRequest struct {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  AccountID int64 `json:"account_id"`
153
  AchievementOrAward *string `json:"achievement_or_award"` // prestasi atau penghargaan
154
  }
155
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
  UploadProfileImageRequest struct {
157
  AccountID int64
158
  File *multipart.FileHeader
@@ -162,7 +266,11 @@ type (
162
  URL string `json:"url"`
163
  }
164
 
165
- ProgressResponse struct {
 
 
 
 
166
  AccountDetailsProgress float64 `json:"account_details_progress"`
167
  PersonalityAndPreferenceCVProgress float64 `json:"personality_and_preference_cv_progress"`
168
  FamilyMemberCVProgress float64 `json:"family_member_cv_progress"`
@@ -174,3 +282,49 @@ type (
174
  TotalProgress float64 `json:"total_progress"`
175
  }
176
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  }
59
 
60
  type (
61
+ SavePersonalityAndPreferenceRequest struct {
62
+ AccountID int64 `json:"-"`
63
+
64
  PositiveTraits *string `json:"positive_traits"` // sifat positif
65
  NegativeTraits *string `json:"negative_traits"` // sifat negatif
66
  Hobbies *string `json:"hobbies"` // hobi
 
77
  MonthlyExpenses *string `json:"monthly_expenses" validate:"monthly_expenses"` // pengeluaran per bulan
78
  }
79
 
80
+ GetPersonalityAndPreferenceRequest struct {
81
+ AccountID int64 `json:"-"`
82
+ }
83
+
84
+ CreateFamilyMemberRequest struct {
85
+ AccountID int64 `json:"-"`
86
+
87
+ Role *string `json:"role" validate:"family_role"` // Peran dalam keluarga
88
+ Status *string `json:"status" validate:"life_status"` // Status (Hidup, Wafat)
89
+ Religion *string `json:"religion" validate:"religion"` // Agama
90
+ Job *string `json:"job"` // Pekerjaan
91
+ LastEducation *string `json:"last_education" validate:"last_education"` // Pendidikan terakhir
92
+ Age *int `json:"age"` // Usia
93
+ }
94
+
95
+ UpdateFamilyMemberRequest struct {
96
+ ID int64 `json:"-"`
97
+ AccountID int64 `json:"-"`
98
+
99
  Role *string `json:"role" validate:"family_role"` // Peran dalam keluarga
100
  Status *string `json:"status" validate:"life_status"` // Status (Hidup, Wafat)
101
  Religion *string `json:"religion" validate:"religion"` // Agama
 
104
  Age *int `json:"age"` // Usia
105
  }
106
 
107
+ ListFamilyMemberRequest struct {
108
+ AccountID int64 `json:"-"`
 
 
 
 
 
 
 
 
109
  }
110
 
111
+ GetFamilyMemberRequest struct {
112
+ ID int64 `json:"-"`
113
+ }
114
+
115
+ DeleteFamilyMemberRequest struct {
116
+ ID int64 `json:"-"`
117
+ }
118
+
119
+ SavePhysicalAndHealthRequest struct {
120
+ AccountID int64 `json:"-"`
121
+ HeightInCm *int `json:"height_cm"` // Tinggi badan dalam satuan sentimeter
122
+ WeightInKg *int `json:"weight_kg"` // Berat badan dalam satuan kilogram
123
+ BodyShape *string `json:"body_shape" validate:"body_shape"` // Bentuk tubuh
124
+ SkinColor *string `json:"skin_color" validate:"skin_color"` // Warna kulit
125
+ HairType *string `json:"hair_type" validate:"hair_type"` // Tipe rambut
126
+ MedicalHistory *pq.StringArray `json:"medical_history"` // Riwayat penyakit
127
+ PhysicalDisorder *string `json:"physical_disorder"` // Cacat fisik
128
+ PhysicalTraits *string `json:"physical_traits"` // Ciri khas fisik
129
+ }
130
+
131
+ GetPhysicalAndHealthRequest struct {
132
+ AccountID int64 `json:"-"`
133
+ }
134
+
135
+ SaveAccountDetailsRequest struct {
136
  AccountID int64 `json:"-"`
137
  FullName *string `json:"full_name"`
138
  Gender *string `json:"gender" validate:"gender"`
 
145
  PhoneNumber *string `json:"phone_number" validate:"phone_number"`
146
  }
147
 
148
+ GetAccountDetailsRequest struct {
149
+ AccountID int64 `json:"account_id"`
150
+ }
151
+
152
+ SaveWorshipAndReligiousUnderstandingRequest struct {
153
+ AccountID int64 `json:"-"`
154
+ ObligatoryPrayer *string `json:"obligatory_prayer"` // sholat_wajib_5_waktu
155
+ CongregationalPrayer *string `json:"congregational_prayer"` // sholat_berjamaah_di_masjid
156
+ TahajjudPrayer *string `json:"tahajjud_prayer"` // sholat_tahajud
157
+ DhuhaPrayer *string `json:"dhuha_prayer"` // sholat_dhuha
158
+ QuranMemorization *string `json:"quran_memorization"` // hafalan_alquran
159
+ QuranReadingAbility *string `json:"quran_reading_ability" validate:"quran_reading_ability"` // kemampuan_baca_alquran
160
+ DaudFasting *string `json:"daud_fasting"` // puasa_daud
161
+ AyyamulBidhFasting *string `json:"ayyamul_bidh_fasting"` // puasa_ayyamul_bidh
162
+ HajjOrUmrah *pq.StringArray `json:"hajj_or_umrah"` // ibadah_haji_umroh
163
+ ListeningToMusic *string `json:"listening_to_music"` // mendengarkan_musik
164
+ OpinionOnIkhtilat *string `json:"opinion_on_ikhtilat"` // pendapat_ikhtilat
165
+ OpinionOnTouchingNonMahram *string `json:"opinion_on_touching_non_mahram"` // pendapat_menyentuh_non_mahram
166
+ OpinionOnVeil *string `json:"opinion_on_veil"` // pendapat_tentang_cadar
167
+ WeeklyReligiousStudies *string `json:"weekly_religious_studies"` // kajian_yang_diikuti_dalam_sepekan
168
+ FollowedUstadz *string `json:"followed_ustadz"` // ustadz_yang_diikuti
169
+ }
170
+
171
+ GetWorshipAndReligiousUnderstandingRequest struct {
172
+ AccountID int64 `json:"-"`
173
+ }
174
+
175
+ CreateEducationRequest struct {
176
+ AccountID int64 `json:"-"`
177
+ LastEducation *string `json:"last_education" validate:"last_education"` // pendidikan terakhir
178
+ EducationInstitute *string `json:"education_institute"` // institusi pendidikan
179
+ EducationMajor *string `json:"education_major"` // jurusan pendidikan
180
+ YearStart *int `json:"year_start"` // tahun masuk
181
+ YearGraduate *int `json:"year_graduate"` // tahun lulus
182
+ }
183
+
184
+ UpdateEducationRequest struct {
185
+ ID int64 `json:"-"`
186
+ AccountID int64 `json:"-"`
187
  LastEducation *string `json:"last_education" validate:"last_education"` // pendidikan terakhir
188
  EducationInstitute *string `json:"education_institute"` // institusi pendidikan
189
  EducationMajor *string `json:"education_major"` // jurusan pendidikan
 
191
  YearGraduate *int `json:"year_graduate"` // tahun lulus
192
  }
193
 
194
+ ListEducationRequest struct {
195
+ AccountID int64 `json:"-"`
 
 
 
 
 
196
  }
197
 
198
+ GetEducationRequest struct {
199
+ ID int64 `json:"-"`
200
+ }
201
+
202
+ DeleteEducationRequest struct {
203
+ ID int64 `json:"-"`
204
+ }
205
+
206
+ CreateJobRequest struct {
207
+ AccountID int64 `json:"account_id"`
208
+ InstitutionName *string `json:"institution_name"` // nama instansi
209
+ CurrentJob *string `json:"current_job"` // pekerjaan saat ini
210
+ YearStartedWorking *int `json:"year_started_working"` // tahun mulai bekerja
211
+ MonthlyIncome *string `json:"monthly_income" validate:"monthly_income"` // penghasilan per bulan
212
+ IncomeSources *pq.StringArray `json:"income_sources"` // sumber penghasilan
213
+ }
214
+
215
+ UpdateJobRequest struct {
216
+ ID int64 `json:"-"`
217
+ AccountID int64 `json:"-"`
218
+ InstitutionName *string `json:"institution_name"` // nama instansi
219
+ CurrentJob *string `json:"current_job"` // pekerjaan saat ini
220
+ YearStartedWorking *int `json:"year_started_working"` // tahun mulai bekerja
221
+ MonthlyIncome *string `json:"monthly_income" validate:"monthly_income"` // penghasilan per bulan
222
+ IncomeSources *pq.StringArray `json:"income_sources"` // sumber penghasilan
223
+ }
224
+
225
+ ListJobRequest struct {
226
+ AccountID int64 `json:"-"`
227
+ }
228
+
229
+ GetJobRequest struct {
230
+ ID int64 `json:"-"`
231
+ }
232
+
233
+ DeleteJobRequest struct {
234
+ ID int64 `json:"-"`
235
+ }
236
+
237
+ CreateAchievementRequest struct {
238
  AccountID int64 `json:"account_id"`
239
  AchievementOrAward *string `json:"achievement_or_award"` // prestasi atau penghargaan
240
  }
241
 
242
+ UpdateAchievementRequest struct {
243
+ ID int64 `json:"-"`
244
+ AccountID int64 `json:"-"`
245
+ AchievementOrAward *string `json:"achievement_or_award"` // prestasi atau penghargaan
246
+ }
247
+
248
+ ListAchievementRequest struct {
249
+ AccountID int64 `json:"-"`
250
+ }
251
+
252
+ GetAchievementRequest struct {
253
+ ID int64 `json:"-"`
254
+ }
255
+
256
+ DeleteAchievementRequest struct {
257
+ ID int64 `json:"-"`
258
+ }
259
+
260
  UploadProfileImageRequest struct {
261
  AccountID int64
262
  File *multipart.FileHeader
 
266
  URL string `json:"url"`
267
  }
268
 
269
+ GetProgressCVRequest struct {
270
+ AccountID int64 `json:"-"`
271
+ }
272
+
273
+ GetProgressCVResponse struct {
274
  AccountDetailsProgress float64 `json:"account_details_progress"`
275
  PersonalityAndPreferenceCVProgress float64 `json:"personality_and_preference_cv_progress"`
276
  FamilyMemberCVProgress float64 `json:"family_member_cv_progress"`
 
282
  TotalProgress float64 `json:"total_progress"`
283
  }
284
  )
285
+
286
+ type SaveMarriageReadinessProfileRequest struct {
287
+ AccountID int64 `json:"account_id"`
288
+
289
+ // Visi Misi Rumah Tangga
290
+ MarriageVision *string `gorm:"column:marriage_vision" json:"marriage_vision"` // Apa visi dan tujuan utama kamu dalam membangun rumah tangga?
291
+ LivingPlan *string `gorm:"column:living_plan" json:"living_plan"` // Setelah menikah, kamu berencana tinggal di mana?
292
+ SpouseRoles *string `gorm:"column:spouse_roles" json:"spouse_roles"` // Menurutmu, apa peran utama suami dan istri dalam rumah tangga?
293
+ SpouseRights *string `gorm:"column:spouse_rights" json:"spouse_rights"` // Apa saja hak suami dan istri menurutmu?
294
+ ConflictResolution *string `gorm:"column:conflict_resolution" json:"conflict_resolution"` // Jika terjadi konflik, bagaimana kamu menyikapinya?
295
+ ParentingStyle *string `gorm:"column:parenting_style" json:"parenting_style"` // Setelah punya anak, pola pengasuhan seperti apa yang kamu inginkan?
296
+
297
+ // Konsep Acara Pernikahan
298
+ ReadyToMarryDuration *string `gorm:"column:ready_to_marry_duration" json:"ready_to_marry_duration"` // Setelah taaruf dimulai, berapa lama kamu butuh untuk siap menikah?
299
+ WeddingConcept *string `gorm:"column:wedding_concept" json:"wedding_concept"` // Seperti apa konsep pernikahan yang kamu harapkan?
300
+ WeddingFunding *string `gorm:"column:wedding_funding" json:"wedding_funding"` // Apakah kamu sudah mempersiapkan dana pernikahan? Dari mana sumbernya?
301
+
302
+ // Karir Kedepannya
303
+ CareerAfterMarriage *string `gorm:"column:career_after_marriage" json:"career_after_marriage"` // Apakah kamu ingin tetap bekerja setelah menikah?
304
+ TimeManagement *string `gorm:"column:time_management" json:"time_management"` // Bagaimana kamu membagi waktu antara keluarga, ibadah, dan pekerjaan?
305
+ CareerGoals *string `gorm:"column:career_goals" json:"career_goals"` // Apa impian karier atau cita-cita kamu dalam 5 sampai 10 tahun ke depan?
306
+ SelfDevelopment *string `gorm:"column:self_development" json:"self_development"` // Apa usaha kamu untuk terus mengembangkan diri dan skill?
307
+
308
+ // Pendidikan Keluarga
309
+ DelayChildren *string `gorm:"column:delay_children" json:"delay_children"` // Apakah kamu berencana menunda memiliki anak?
310
+ ChildEducationPlan *string `gorm:"column:child_education_plan" json:"child_education_plan"` // Apakah kamu sudah punya rencana pendidikan anak?
311
+ ReligiousEmotionalBond *string `gorm:"column:religious_emotional_bond" json:"religious_emotional_bond"` // Bagaimana cara menjaga nilai agama & kedekatan emosional dengan anak?
312
+ ParentingChallenges *string `gorm:"column:parenting_challenges" json:"parenting_challenges"` // Saat menghadapi tantangan pengasuhan, bagaimana kamu menyikapinya?
313
+
314
+ // Finansial Keluarga
315
+ MonthlyFinance *string `gorm:"column:monthly_finance" json:"monthly_finance"` // Bagaimana kamu mengelola keuangan bulanan?
316
+ FamilyResponsibility *string `gorm:"column:family_responsibility" json:"family_responsibility"` // Apakah kamu masih punya tanggungan keluarga setelah menikah?
317
+ DebtStatus *string `gorm:"column:debt_status" json:"debt_status"` // Apakah kamu punya cicilan/utang setelah menikah?
318
+ FinanceSharing *string `gorm:"column:finance_sharing" json:"finance_sharing"` // Setelah menikah, bagaimana pembagian tanggung jawab keuangan?
319
+ IncomeGapView *string `gorm:"column:income_gap_view" json:"income_gap_view"` // Pandangan kamu jika istri berpenghasilan lebih besar dari suami?
320
+
321
+ // Keputusan dan Komunikasi
322
+ DecisionMaking *string `gorm:"column:decision_making" json:"decision_making"` // Dalam mengambil keputusan, kamu lebih mempertimbangkan pasangan atau sendiri?
323
+ GrowthTogether *string `gorm:"column:growth_together" json:"growth_together"` // Apakah kamu siap berjuang bersama pasangan? Apa saja yang ingin diperjuangkan?
324
+ ParentIntervention *string `gorm:"column:parent_intervention" json:"parent_intervention"` // Sejauh mana orang tua/mertua boleh ikut campur dalam rumah tangga?
325
+ HabitResponse *string `gorm:"column:habit_response" json:"habit_response"` // Bagaimana kamu menyikapi kebiasaan pasangan yang kurang kamu sukai?
326
+ }
327
+
328
+ type GetMarriageReadinessProfileRequest struct {
329
+ AccountID int64 `json:"-"`
330
+ }
space/space/space/space/space/space/pkg/validation/validation.go CHANGED
@@ -3,6 +3,7 @@ package validation
3
  import (
4
  "errors"
5
  "fmt"
 
6
  "strings"
7
 
8
  "github.com/go-playground/locales/en"
@@ -129,10 +130,12 @@ func Validate(s any) []ErrorMessage {
129
  if validatorInstance == nil {
130
  return []ErrorMessage{{Field: "", Message: "Validator belum diinisialisasi. Panggil validation.New() terlebih dahulu."}}
131
  }
 
132
  err := validatorInstance.validate.Struct(s)
133
  if err != nil {
134
  return TranslateError(err)
135
  }
 
136
  return nil
137
  }
138
 
@@ -147,9 +150,15 @@ func TranslateError(err error) []ErrorMessage {
147
  return nil
148
  }
149
 
150
- errorMessages := make([]ErrorMessage, 0, len(validationErrors))
 
151
  for _, e := range validationErrors {
152
  fieldLabel := e.Field()
 
 
 
 
 
153
  msg, err := validatorInstance.translator.T(e.Tag(), fieldLabel)
154
  if err != nil {
155
  msg = fieldLabel + " is Invalid"
 
3
  import (
4
  "errors"
5
  "fmt"
6
+ "reflect"
7
  "strings"
8
 
9
  "github.com/go-playground/locales/en"
 
130
  if validatorInstance == nil {
131
  return []ErrorMessage{{Field: "", Message: "Validator belum diinisialisasi. Panggil validation.New() terlebih dahulu."}}
132
  }
133
+
134
  err := validatorInstance.validate.Struct(s)
135
  if err != nil {
136
  return TranslateError(err)
137
  }
138
+
139
  return nil
140
  }
141
 
 
150
  return nil
151
  }
152
 
153
+ var errorMessages []ErrorMessage
154
+
155
  for _, e := range validationErrors {
156
  fieldLabel := e.Field()
157
+
158
+ if e.Kind() == reflect.Ptr {
159
+ continue
160
+ }
161
+
162
  msg, err := validatorInstance.translator.T(e.Tag(), fieldLabel)
163
  if err != nil {
164
  msg = fieldLabel + " is Invalid"
space/space/space/space/space/space/pkg/worker/task_send_forgot_password_email.go CHANGED
@@ -1,14 +1,15 @@
1
  package worker
2
 
3
  import (
4
- "api.qobiltu.id/assets"
5
  "bytes"
6
  "context"
7
  "encoding/json"
8
  "fmt"
9
- "github.com/hibiken/asynq"
10
  "html/template"
11
- "log/slog"
 
 
 
12
  )
13
 
14
  const (
@@ -62,14 +63,15 @@ func (p *RedisTaskProcessor) ProcessTaskSendForgotPasswordEmail(ctx context.Cont
62
  }
63
  htmlContent := body.String()
64
 
65
- slog.Info("Sending forgot password email", slog.String("email", payload.EmailAddress))
 
66
 
67
  err = p.emailSender.Send(payload.EmailAddress, payload.Subject, htmlContent, payload)
68
  if err != nil {
69
  return fmt.Errorf("failed to send forgot password email: %w", err)
70
  }
71
 
72
- slog.Info("Forgot password email sent successfully", slog.String("email", payload.EmailAddress))
73
-
74
  return nil
75
  }
 
1
  package worker
2
 
3
  import (
 
4
  "bytes"
5
  "context"
6
  "encoding/json"
7
  "fmt"
 
8
  "html/template"
9
+ "time"
10
+
11
+ "api.qobiltu.id/assets"
12
+ "github.com/hibiken/asynq"
13
  )
14
 
15
  const (
 
63
  }
64
  htmlContent := body.String()
65
 
66
+ fmt.Println("Sending forgot password email", payload.EmailAddress)
67
+ start := time.Now()
68
 
69
  err = p.emailSender.Send(payload.EmailAddress, payload.Subject, htmlContent, payload)
70
  if err != nil {
71
  return fmt.Errorf("failed to send forgot password email: %w", err)
72
  }
73
 
74
+ fmt.Println("Forgot password email sent successfully", payload.EmailAddress)
75
+ fmt.Println("Time taken", time.Since(start))
76
  return nil
77
  }
space/space/space/space/space/space/pkg/worker/task_send_verify_email.go CHANGED
@@ -1,14 +1,15 @@
1
  package worker
2
 
3
  import (
4
- "api.qobiltu.id/assets"
5
  "bytes"
6
  "context"
7
  "encoding/json"
8
  "fmt"
9
- "github.com/hibiken/asynq"
10
  "html/template"
11
- "log/slog"
 
 
 
12
  )
13
 
14
  const (
@@ -62,14 +63,16 @@ func (p *RedisTaskProcessor) ProcessTaskSendVerifyEmail(ctx context.Context, tas
62
  }
63
  htmlContent := body.String()
64
 
65
- slog.Info("Sending verification email", slog.String("email", payload.EmailAddress))
 
66
 
67
  err = p.emailSender.Send(payload.EmailAddress, payload.Subject, htmlContent, payload)
68
  if err != nil {
69
  return fmt.Errorf("failed to send verify email: %w", err)
70
  }
71
 
72
- slog.Info("Verification email sent successfully", slog.String("email", payload.EmailAddress))
 
73
 
74
  return nil
75
  }
 
1
  package worker
2
 
3
  import (
 
4
  "bytes"
5
  "context"
6
  "encoding/json"
7
  "fmt"
 
8
  "html/template"
9
+ "time"
10
+
11
+ "api.qobiltu.id/assets"
12
+ "github.com/hibiken/asynq"
13
  )
14
 
15
  const (
 
63
  }
64
  htmlContent := body.String()
65
 
66
+ fmt.Println("Sending verification email", payload.EmailAddress)
67
+ start := time.Now()
68
 
69
  err = p.emailSender.Send(payload.EmailAddress, payload.Subject, htmlContent, payload)
70
  if err != nil {
71
  return fmt.Errorf("failed to send verify email: %w", err)
72
  }
73
 
74
+ fmt.Println("Verification email sent successfully", payload.EmailAddress)
75
+ fmt.Println("Time taken", time.Since(start))
76
 
77
  return nil
78
  }
space/space/space/space/space/space/router/cv_route.go CHANGED
@@ -42,6 +42,6 @@ func (s *Server) CVRoute() {
42
  routerGroup.PUT("/achievements/:id", s.cvController.UpdateAchievement)
43
  routerGroup.DELETE("/achievements/:id", s.cvController.DeleteAchievement)
44
 
45
- routerGroup.GET("/progress", s.cvController.GetProgress)
46
  }
47
  }
 
42
  routerGroup.PUT("/achievements/:id", s.cvController.UpdateAchievement)
43
  routerGroup.DELETE("/achievements/:id", s.cvController.DeleteAchievement)
44
 
45
+ routerGroup.GET("/progress", s.cvController.GetProgressCV)
46
  }
47
  }
space/space/space/space/space/space/router/router.go CHANGED
@@ -18,5 +18,6 @@ func (s *Server) setupRoutes() {
18
  // another way to register routes
19
  s.HealthCheckRoute()
20
  s.CVRoute()
 
21
  s.StorageRoute()
22
  }
 
18
  // another way to register routes
19
  s.HealthCheckRoute()
20
  s.CVRoute()
21
+ s.MarriageReadinessProfileRoute()
22
  s.StorageRoute()
23
  }
space/space/space/space/space/space/router/server.go CHANGED
@@ -2,28 +2,32 @@ package router
2
 
3
  import (
4
  cv_controller "api.qobiltu.id/controller/cv"
5
- "api.qobiltu.id/controller/health_check"
 
6
  "github.com/gin-gonic/gin"
7
  )
8
 
9
  type Server struct {
10
- router *gin.Engine
11
- healthCheckController health_check_controller.HealthCheckController
12
- cvController cv_controller.CVController
 
13
  }
14
 
15
  func NewServer(
16
  healthCheckController health_check_controller.HealthCheckController,
17
  cvController cv_controller.CVController,
 
18
  ) (*Server, error) {
19
 
20
  router := gin.Default()
21
  router.Use(gin.Recovery())
22
 
23
  server := &Server{
24
- healthCheckController: healthCheckController,
25
- cvController: cvController,
26
- router: router,
 
27
  }
28
 
29
  server.setupRoutes()
 
2
 
3
  import (
4
  cv_controller "api.qobiltu.id/controller/cv"
5
+ health_check_controller "api.qobiltu.id/controller/health_check"
6
+ marriage_readiness_profile_controller "api.qobiltu.id/controller/marriage_readiness_profile"
7
  "github.com/gin-gonic/gin"
8
  )
9
 
10
  type Server struct {
11
+ router *gin.Engine
12
+ healthCheckController health_check_controller.HealthCheckController
13
+ cvController cv_controller.CVController
14
+ marriageReadinessProfileController marriage_readiness_profile_controller.MarriageReadinessProfileController
15
  }
16
 
17
  func NewServer(
18
  healthCheckController health_check_controller.HealthCheckController,
19
  cvController cv_controller.CVController,
20
+ marriageReadinessProfileController marriage_readiness_profile_controller.MarriageReadinessProfileController,
21
  ) (*Server, error) {
22
 
23
  router := gin.Default()
24
  router.Use(gin.Recovery())
25
 
26
  server := &Server{
27
+ healthCheckController: healthCheckController,
28
+ cvController: cvController,
29
+ marriageReadinessProfileController: marriageReadinessProfileController,
30
+ router: router,
31
  }
32
 
33
  server.setupRoutes()
space/space/space/space/space/space/services/cv_service.go CHANGED
@@ -11,48 +11,49 @@ import (
11
  "api.qobiltu.id/pkg/validation"
12
  "api.qobiltu.id/repositories"
13
  "api.qobiltu.id/response"
 
14
  "gorm.io/gorm"
15
  )
16
 
17
  type CVService interface {
18
- SaveAccountDetails(ctx context.Context, req *models.AccountDetailsRequest) (*models.AccountDetails, error)
19
- GetAccountDetails(ctx context.Context, id int64) (*models.AccountDetails, error)
20
-
21
- SavePersonalityAndPreference(ctx context.Context, req *models.PersonalityAndPreferenceCVRequest) (*models.PersonalityAndPreferenceCV, error)
22
- GetPersonalityAndPreference(ctx context.Context, id int64) (*models.PersonalityAndPreferenceCV, error)
23
-
24
- CreateFamilyMember(ctx context.Context, req *models.FamilyMemberRequest) (*models.FamilyMemberCV, error)
25
- UpdateFamilyMember(ctx context.Context, id int64, req *models.FamilyMemberRequest) (*models.FamilyMemberCV, error)
26
- ListFamilyMember(ctx context.Context, accountID int64) ([]models.FamilyMemberCV, error)
27
- GetFamilyMember(ctx context.Context, id int64) (*models.FamilyMemberCV, error)
28
- DeleteFamilyMember(ctx context.Context, id int64) error
29
-
30
- SavePhysicalAndHealth(ctx context.Context, req *models.PhysicalAndHealthRequest) (*models.PhysicalAndHealthCV, error)
31
- GetPhysicalAndHealth(ctx context.Context, id int64) (*models.PhysicalAndHealthCV, error)
32
-
33
- SaveWorshipAndReligiousUnderstanding(ctx context.Context, req *models.WorshipAndReligiousUnderstandingRequest) (*models.WorshipAndReligiousUnderstandingCV, error)
34
- GetWorshipAndReligiousUnderstanding(ctx context.Context, id int64) (*models.WorshipAndReligiousUnderstandingCV, error)
35
-
36
- CreateEducation(ctx context.Context, req *models.EducationRequest) (*models.EducationCV, error)
37
- UpdateEducation(ctx context.Context, id int64, req *models.EducationRequest) (*models.EducationCV, error)
38
- ListEducation(ctx context.Context, accountID int64) ([]models.EducationCV, error)
39
- GetEducation(ctx context.Context, id int64) (*models.EducationCV, error)
40
- DeleteEducation(ctx context.Context, id int64) error
41
-
42
- CreateJob(ctx context.Context, req *models.JobRequest) (*models.JobCV, error)
43
- UpdateJob(ctx context.Context, id int64, req *models.JobRequest) (*models.JobCV, error)
44
- ListJob(ctx context.Context, accountID int64) ([]models.JobCV, error)
45
- GetJob(ctx context.Context, id int64) (*models.JobCV, error)
46
- DeleteJob(ctx context.Context, id int64) error
47
-
48
- CreateAchievement(ctx context.Context, req *models.AchievementRequest) (*models.AchievementCV, error)
49
- UpdateAchievement(ctx context.Context, id int64, req *models.AchievementRequest) (*models.AchievementCV, error)
50
- ListAchievement(ctx context.Context, accountID int64) ([]models.AchievementCV, error)
51
- GetAchievement(ctx context.Context, id int64) (*models.AchievementCV, error)
52
- DeleteAchievement(ctx context.Context, id int64) error
53
 
54
  UploadProfileImage(ctx context.Context, req *models.UploadProfileImageRequest) (*models.UploadProfileImageResponse, error)
55
- GetProgress(ctx context.Context, accountID int64) (*models.ProgressResponse, error)
56
  }
57
 
58
  type cvService struct {
@@ -67,7 +68,9 @@ func NewCVService(cvRepository repositories.CVRepository, storage storage.Storag
67
  }
68
  }
69
 
70
- func (s *cvService) SaveAccountDetails(ctx context.Context, req *models.AccountDetailsRequest) (*models.AccountDetails, error) {
 
 
71
  if err := validation.Validate(req); err != nil {
72
  return nil, response.HandleValidationError(err)
73
  }
@@ -84,14 +87,15 @@ func (s *cvService) SaveAccountDetails(ctx context.Context, req *models.AccountD
84
  }
85
 
86
  accountDetails.AccountID = uint(req.AccountID)
87
- accountDetails.FullName = req.FullName
88
- accountDetails.Gender = req.Gender
89
- accountDetails.DateOfBirth = req.DateOfBirth
90
- accountDetails.PlaceOfBirth = req.PlaceOfBirth
91
- accountDetails.Domicile = req.Domicile
92
- accountDetails.MaritalStatus = req.MaritalStatus
93
- accountDetails.LastEducation = req.LastEducation
94
- accountDetails.LastJob = req.LastJob
 
95
 
96
  if req.PhoneNumber != nil {
97
  sanitizedPhone := validation.SanitizePhoneNumber(*req.PhoneNumber)
@@ -107,15 +111,15 @@ func (s *cvService) SaveAccountDetails(ctx context.Context, req *models.AccountD
107
  return res, nil
108
  }
109
 
110
- func (s *cvService) GetAccountDetails(ctx context.Context, id int64) (*models.AccountDetails, error) {
111
- res, err := s.cvRepository.GetAccountDetailsByAccountID(ctx, id)
112
  if err != nil {
113
- return nil, response.HandleGormError(err, "Data diri tidak ditemukan")
114
  }
115
  return res, nil
116
  }
117
 
118
- func (s *cvService) SavePersonalityAndPreference(ctx context.Context, req *models.PersonalityAndPreferenceCVRequest) (*models.PersonalityAndPreferenceCV, error) {
119
  if err := validation.Validate(req); err != nil {
120
  return nil, response.HandleValidationError(err)
121
  }
@@ -132,20 +136,21 @@ func (s *cvService) SavePersonalityAndPreference(ctx context.Context, req *model
132
  }
133
 
134
  personalityAndPreference.AccountID = req.AccountID
135
- personalityAndPreference.PositiveTraits = req.PositiveTraits
136
- personalityAndPreference.NegativeTraits = req.NegativeTraits
137
- personalityAndPreference.Hobbies = req.Hobbies
138
- personalityAndPreference.LifeGoals = req.LifeGoals
139
- personalityAndPreference.DailyActivities = req.DailyActivities
140
- personalityAndPreference.LeisureActivities = req.LeisureActivities
141
- personalityAndPreference.Likes = req.Likes
142
- personalityAndPreference.Dislikes = req.Dislikes
143
- personalityAndPreference.StressHandling = req.StressHandling
144
- personalityAndPreference.AngerTriggers = req.AngerTriggers
145
- personalityAndPreference.FavoriteFoodAndDrinks = req.FavoriteFoodAndDrinks
146
- personalityAndPreference.CanCook = req.CanCook
147
- personalityAndPreference.TypesOfDishesCooked = req.TypesOfDishesCooked
148
- personalityAndPreference.MonthlyExpenses = req.MonthlyExpenses
 
149
 
150
  res, err := s.cvRepository.SavePersonalityAndPreference(ctx, personalityAndPreference)
151
  if err != nil {
@@ -155,8 +160,8 @@ func (s *cvService) SavePersonalityAndPreference(ctx context.Context, req *model
155
  return res, nil
156
  }
157
 
158
- func (s *cvService) GetPersonalityAndPreference(ctx context.Context, id int64) (*models.PersonalityAndPreferenceCV, error) {
159
- res, err := s.cvRepository.GetPersonalityAndPreferenceByAccountID(ctx, id)
160
  if err != nil {
161
  return nil, response.HandleGormError(err, "Internal Server Error")
162
  }
@@ -164,7 +169,7 @@ func (s *cvService) GetPersonalityAndPreference(ctx context.Context, id int64) (
164
  return res, nil
165
  }
166
 
167
- func (s *cvService) CreateFamilyMember(ctx context.Context, req *models.FamilyMemberRequest) (*models.FamilyMemberCV, error) {
168
  if err := validation.Validate(req); err != nil {
169
  return nil, response.HandleValidationError(err)
170
  }
@@ -183,62 +188,62 @@ func (s *cvService) CreateFamilyMember(ctx context.Context, req *models.FamilyMe
183
  // Simpan ke repository
184
  res, err := s.cvRepository.SaveFamilyMember(ctx, familyMember)
185
  if err != nil {
186
- return nil, response.HandleGormError(err, "Gagal menyimpan anggota keluarga")
187
  }
188
 
189
  return res, nil
190
  }
191
 
192
- func (s *cvService) ListFamilyMember(ctx context.Context, accountID int64) ([]models.FamilyMemberCV, error) {
193
- list, err := s.cvRepository.ListFamilyMember(ctx, accountID)
194
  if err != nil {
195
- return nil, response.HandleGormError(err, "Gagal mengambil daftar anggota keluarga")
196
  }
197
  return list, nil
198
  }
199
 
200
- func (s *cvService) GetFamilyMember(ctx context.Context, id int64) (*models.FamilyMemberCV, error) {
201
- res, err := s.cvRepository.GetFamilyMember(ctx, id)
202
  if err != nil {
203
- return nil, response.HandleGormError(err, "Data anggota keluarga tidak ditemukan")
204
  }
205
  return res, nil
206
  }
207
 
208
- func (s *cvService) DeleteFamilyMember(ctx context.Context, id int64) error {
209
- err := s.cvRepository.DeleteFamilyMember(ctx, id)
210
  if err != nil {
211
- return response.HandleGormError(err, "Gagal menghapus anggota keluarga")
212
  }
213
  return nil
214
  }
215
 
216
- func (s *cvService) UpdateFamilyMember(ctx context.Context, id int64, req *models.FamilyMemberRequest) (*models.FamilyMemberCV, error) {
217
  if err := validation.Validate(req); err != nil {
218
  return nil, response.HandleValidationError(err)
219
  }
220
 
221
- existing, err := s.cvRepository.GetFamilyMember(ctx, id)
222
  if err != nil {
223
- return nil, response.HandleGormError(err, "Data anggota keluarga tidak ditemukan")
224
  }
225
 
226
- existing.Role = req.Role
227
- existing.Status = req.Status
228
- existing.Religion = req.Religion
229
- existing.Job = req.Job
230
- existing.LastEducation = req.LastEducation
231
- existing.Age = req.Age
232
 
233
  updated, err := s.cvRepository.SaveFamilyMember(ctx, existing)
234
  if err != nil {
235
- return nil, response.HandleGormError(err, "Gagal memperbarui anggota keluarga")
236
  }
237
 
238
  return updated, nil
239
  }
240
 
241
- func (s *cvService) SavePhysicalAndHealth(ctx context.Context, req *models.PhysicalAndHealthRequest) (*models.PhysicalAndHealthCV, error) {
242
  if err := validation.Validate(req); err != nil {
243
  return nil, response.HandleValidationError(err)
244
  }
@@ -246,7 +251,7 @@ func (s *cvService) SavePhysicalAndHealth(ctx context.Context, req *models.Physi
246
  // Cek apakah data sudah ada berdasarkan account_id
247
  existing, err := s.cvRepository.GetPhysicalAndHealthByAccountID(ctx, req.AccountID)
248
  if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
249
- return nil, response.HandleGormError(err, "Terjadi kesalahan saat mengambil data fisik dan kesehatan")
250
  }
251
 
252
  // Jika belum ada, buat objek baru
@@ -256,33 +261,34 @@ func (s *cvService) SavePhysicalAndHealth(ctx context.Context, req *models.Physi
256
 
257
  // Mapping field dari request
258
  existing.AccountID = req.AccountID
259
- existing.HeightInCm = req.HeightInCm
260
- existing.WeightInKg = req.WeightInKg
261
- existing.BodyShape = req.BodyShape
262
- existing.SkinColor = req.SkinColor
263
- existing.HairType = req.HairType
264
- existing.MedicalHistory = req.MedicalHistory
265
- existing.PhysicalDisorder = req.PhysicalDisorder
266
- existing.PhysicalTraits = req.PhysicalTraits
 
267
 
268
  // Simpan data
269
  res, err := s.cvRepository.SavePhysicalAndHealth(ctx, existing)
270
  if err != nil {
271
- return nil, response.HandleGormError(err, "Gagal menyimpan data fisik dan kesehatan")
272
  }
273
 
274
  return res, nil
275
  }
276
 
277
- func (s *cvService) GetPhysicalAndHealth(ctx context.Context, id int64) (*models.PhysicalAndHealthCV, error) {
278
- res, err := s.cvRepository.GetPhysicalAndHealthByAccountID(ctx, id)
279
  if err != nil {
280
- return nil, response.HandleGormError(err, "Data fisik dan kesehatan tidak ditemukan")
281
  }
282
  return res, nil
283
  }
284
 
285
- func (s *cvService) SaveWorshipAndReligiousUnderstanding(ctx context.Context, req *models.WorshipAndReligiousUnderstandingRequest) (*models.WorshipAndReligiousUnderstandingCV, error) {
286
  if err := validation.Validate(req); err != nil {
287
  return nil, response.HandleValidationError(err)
288
  }
@@ -290,7 +296,7 @@ func (s *cvService) SaveWorshipAndReligiousUnderstanding(ctx context.Context, re
290
  // Cek apakah data sudah ada berdasarkan account_id
291
  worshipAndReligiousUnderstanding, err := s.cvRepository.GetWorshipAndReligiousUnderstandingByAccountID(ctx, req.AccountID)
292
  if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
293
- return nil, response.HandleGormError(err, "Terjadi kesalahan saat mengambil data agama dan pemahaman agama")
294
  }
295
 
296
  // Jika belum ada, buat objek baru
@@ -300,21 +306,22 @@ func (s *cvService) SaveWorshipAndReligiousUnderstanding(ctx context.Context, re
300
 
301
  // Mapping field dari request
302
  worshipAndReligiousUnderstanding.AccountID = req.AccountID
303
- worshipAndReligiousUnderstanding.ObligatoryPrayer = req.ObligatoryPrayer
304
- worshipAndReligiousUnderstanding.CongregationalPrayer = req.CongregationalPrayer
305
- worshipAndReligiousUnderstanding.TahajjudPrayer = req.TahajjudPrayer
306
- worshipAndReligiousUnderstanding.DhuhaPrayer = req.DhuhaPrayer
307
- worshipAndReligiousUnderstanding.QuranMemorization = req.QuranMemorization
308
- worshipAndReligiousUnderstanding.QuranReadingAbility = req.QuranReadingAbility
309
- worshipAndReligiousUnderstanding.DaudFasting = req.DaudFasting
310
- worshipAndReligiousUnderstanding.AyyamulBidhFasting = req.AyyamulBidhFasting
311
- worshipAndReligiousUnderstanding.HajjOrUmrah = req.HajjOrUmrah
312
- worshipAndReligiousUnderstanding.ListeningToMusic = req.ListeningToMusic
313
- worshipAndReligiousUnderstanding.OpinionOnIkhtilat = req.OpinionOnIkhtilat
314
- worshipAndReligiousUnderstanding.OpinionOnTouchingNonMahram = req.OpinionOnTouchingNonMahram
315
- worshipAndReligiousUnderstanding.OpinionOnVeil = req.OpinionOnVeil
316
- worshipAndReligiousUnderstanding.WeeklyReligiousStudies = req.WeeklyReligiousStudies
317
- worshipAndReligiousUnderstanding.FollowedUstadz = req.FollowedUstadz
 
318
 
319
  // Simpan data
320
  res, err := s.cvRepository.SaveWorshipAndReligiousUnderstanding(ctx, worshipAndReligiousUnderstanding)
@@ -325,15 +332,15 @@ func (s *cvService) SaveWorshipAndReligiousUnderstanding(ctx context.Context, re
325
  return res, nil
326
  }
327
 
328
- func (s *cvService) GetWorshipAndReligiousUnderstanding(ctx context.Context, id int64) (*models.WorshipAndReligiousUnderstandingCV, error) {
329
- res, err := s.cvRepository.GetWorshipAndReligiousUnderstandingByAccountID(ctx, id)
330
  if err != nil {
331
- return nil, response.HandleGormError(err, "Data agama dan pemahaman agama tidak ditemukan")
332
  }
333
  return res, nil
334
  }
335
 
336
- func (s *cvService) CreateEducation(ctx context.Context, req *models.EducationRequest) (*models.EducationCV, error) {
337
  if err := validation.Validate(req); err != nil {
338
  return nil, response.HandleValidationError(err)
339
  }
@@ -349,52 +356,52 @@ func (s *cvService) CreateEducation(ctx context.Context, req *models.EducationRe
349
 
350
  res, err := s.cvRepository.SaveEducation(ctx, edu)
351
  if err != nil {
352
- return nil, response.HandleGormError(err, "Gagal menambahkan data pendidikan")
353
  }
354
 
355
  return res, nil
356
  }
357
 
358
- func (s *cvService) UpdateEducation(ctx context.Context, id int64, req *models.EducationRequest) (*models.EducationCV, error) {
359
  if err := validation.Validate(req); err != nil {
360
  return nil, response.HandleValidationError(err)
361
  }
362
 
363
- edu, err := s.cvRepository.GetEducation(ctx, id)
364
  if err != nil {
365
- return nil, response.HandleGormError(err, "Data pendidikan tidak ditemukan")
366
  }
367
 
368
- edu.LastEducation = req.LastEducation
369
- edu.EducationInstitute = req.EducationInstitute
370
- edu.EducationMajor = req.EducationMajor
371
- edu.YearStart = req.YearStart
372
- edu.YearGraduate = req.YearGraduate
373
 
374
  res, err := s.cvRepository.SaveEducation(ctx, edu)
375
  if err != nil {
376
- return nil, response.HandleGormError(err, "Gagal memperbarui data pendidikan")
377
  }
378
  return res, nil
379
  }
380
 
381
- func (s *cvService) ListEducation(ctx context.Context, accountID int64) ([]models.EducationCV, error) {
382
- return s.cvRepository.ListEducation(ctx, accountID)
383
  }
384
 
385
- func (s *cvService) GetEducation(ctx context.Context, id int64) (*models.EducationCV, error) {
386
- edu, err := s.cvRepository.GetEducation(ctx, id)
387
  if err != nil {
388
- return nil, response.HandleGormError(err, "Data pendidikan tidak ditemukan")
389
  }
390
  return edu, nil
391
  }
392
 
393
- func (s *cvService) DeleteEducation(ctx context.Context, id int64) error {
394
- return s.cvRepository.DeleteEducation(ctx, id)
395
  }
396
 
397
- func (s *cvService) CreateJob(ctx context.Context, req *models.JobRequest) (*models.JobCV, error) {
398
  if err := validation.Validate(req); err != nil {
399
  return nil, response.HandleValidationError(err)
400
  }
@@ -409,93 +416,93 @@ func (s *cvService) CreateJob(ctx context.Context, req *models.JobRequest) (*mod
409
  }
410
  res, err := s.cvRepository.SaveJob(ctx, job)
411
  if err != nil {
412
- return nil, response.HandleGormError(err, "Gagal menambahkan data pekerjaan")
413
  }
414
  return res, nil
415
  }
416
 
417
- func (s *cvService) UpdateJob(ctx context.Context, id int64, req *models.JobRequest) (*models.JobCV, error) {
418
  if err := validation.Validate(req); err != nil {
419
  return nil, response.HandleValidationError(err)
420
  }
421
 
422
- job, err := s.cvRepository.GetJob(ctx, id)
423
  if err != nil {
424
- return nil, response.HandleGormError(err, "Data pekerjaan tidak ditemukan")
425
  }
426
 
427
- job.InstitutionName = req.InstitutionName
428
- job.CurrentJob = req.CurrentJob
429
- job.YearStartedWorking = req.YearStartedWorking
430
- job.MonthlyIncome = req.MonthlyIncome
431
- job.IncomeSources = req.IncomeSources
432
 
433
  res, err := s.cvRepository.SaveJob(ctx, job)
434
  if err != nil {
435
- return nil, response.HandleGormError(err, "Gagal memperbarui data pekerjaan")
436
  }
437
  return res, nil
438
  }
439
 
440
- func (s *cvService) ListJob(ctx context.Context, accountID int64) ([]models.JobCV, error) {
441
- return s.cvRepository.ListJob(ctx, accountID)
442
  }
443
 
444
- func (s *cvService) GetJob(ctx context.Context, id int64) (*models.JobCV, error) {
445
- job, err := s.cvRepository.GetJob(ctx, id)
446
  if err != nil {
447
- return nil, response.HandleGormError(err, "Data pekerjaan tidak ditemukan")
448
  }
449
  return job, nil
450
  }
451
 
452
- func (s *cvService) DeleteJob(ctx context.Context, id int64) error {
453
- return s.cvRepository.DeleteJob(ctx, id)
454
  }
455
 
456
- func (s *cvService) CreateAchievement(ctx context.Context, req *models.AchievementRequest) (*models.AchievementCV, error) {
457
  ach := &models.AchievementCV{
458
  AccountID: req.AccountID,
459
  AchievementOrAward: req.AchievementOrAward,
460
  }
461
  res, err := s.cvRepository.SaveAchievement(ctx, ach)
462
  if err != nil {
463
- return nil, response.HandleGormError(err, "Gagal menambahkan data prestasi")
464
  }
465
 
466
  return res, nil
467
  }
468
 
469
- func (s *cvService) UpdateAchievement(ctx context.Context, id int64, req *models.AchievementRequest) (*models.AchievementCV, error) {
470
- ach, err := s.cvRepository.GetAchievement(ctx, id)
471
  if err != nil {
472
- return nil, response.HandleGormError(err, "Data prestasi tidak ditemukan")
473
  }
474
 
475
  ach.AchievementOrAward = req.AchievementOrAward
476
 
477
  res, err := s.cvRepository.SaveAchievement(ctx, ach)
478
  if err != nil {
479
- return nil, response.HandleGormError(err, "Gagal memperbarui data prestasi")
480
  }
481
 
482
  return res, nil
483
  }
484
 
485
- func (s *cvService) ListAchievement(ctx context.Context, accountID int64) ([]models.AchievementCV, error) {
486
- return s.cvRepository.ListAchievement(ctx, accountID)
487
  }
488
 
489
- func (s *cvService) GetAchievement(ctx context.Context, id int64) (*models.AchievementCV, error) {
490
- ach, err := s.cvRepository.GetAchievement(ctx, id)
491
  if err != nil {
492
- return nil, response.HandleGormError(err, "Data prestasi tidak ditemukan")
493
  }
494
  return ach, nil
495
  }
496
 
497
- func (s *cvService) DeleteAchievement(ctx context.Context, id int64) error {
498
- return s.cvRepository.DeleteAchievement(ctx, id)
499
  }
500
 
501
  func (s *cvService) UploadProfileImage(ctx context.Context, req *models.UploadProfileImageRequest) (*models.UploadProfileImageResponse, error) {
@@ -569,8 +576,8 @@ func (s *cvService) UploadProfileImage(ctx context.Context, req *models.UploadPr
569
  }, nil
570
  }
571
 
572
- func (s *cvService) GetProgress(ctx context.Context, accountID int64) (*models.ProgressResponse, error) {
573
- accountDetails, err := s.cvRepository.GetAccountDetailsByAccountID(ctx, accountID)
574
  if err != nil {
575
  if !errors.Is(err, gorm.ErrRecordNotFound) {
576
  return nil, response.HandleGormError(err, "Internal Server Error")
@@ -578,7 +585,7 @@ func (s *cvService) GetProgress(ctx context.Context, accountID int64) (*models.P
578
  accountDetails = &models.AccountDetails{}
579
  }
580
 
581
- personalityAndPreferenceCV, err := s.cvRepository.GetPersonalityAndPreferenceByAccountID(ctx, accountID)
582
  if err != nil {
583
  if !errors.Is(err, gorm.ErrRecordNotFound) {
584
  return nil, response.HandleGormError(err, "Internal Server Error")
@@ -586,7 +593,7 @@ func (s *cvService) GetProgress(ctx context.Context, accountID int64) (*models.P
586
  personalityAndPreferenceCV = &models.PersonalityAndPreferenceCV{}
587
  }
588
 
589
- physicalAndHealthCV, err := s.cvRepository.GetPhysicalAndHealthByAccountID(ctx, accountID)
590
  if err != nil {
591
  if !errors.Is(err, gorm.ErrRecordNotFound) {
592
  return nil, response.HandleGormError(err, "Internal Server Error")
@@ -594,7 +601,7 @@ func (s *cvService) GetProgress(ctx context.Context, accountID int64) (*models.P
594
  physicalAndHealthCV = &models.PhysicalAndHealthCV{}
595
  }
596
 
597
- worshipAndReligiousUnderstandingCV, err := s.cvRepository.GetWorshipAndReligiousUnderstandingByAccountID(ctx, accountID)
598
  if err != nil {
599
  if !errors.Is(err, gorm.ErrRecordNotFound) {
600
  return nil, response.HandleGormError(err, "Internal Server Error")
@@ -602,22 +609,22 @@ func (s *cvService) GetProgress(ctx context.Context, accountID int64) (*models.P
602
  worshipAndReligiousUnderstandingCV = &models.WorshipAndReligiousUnderstandingCV{}
603
  }
604
 
605
- educationCV, err := s.cvRepository.ListEducation(ctx, accountID)
606
  if err != nil {
607
  return nil, response.HandleGormError(err, "Internal Server Error")
608
  }
609
 
610
- familyMemberCV, err := s.cvRepository.ListFamilyMember(ctx, accountID)
611
  if err != nil {
612
  return nil, response.HandleGormError(err, "Internal Server Error")
613
  }
614
 
615
- jobCV, err := s.cvRepository.ListJob(ctx, accountID)
616
  if err != nil {
617
  return nil, response.HandleGormError(err, "Internal Server Error")
618
  }
619
 
620
- achievementCV, err := s.cvRepository.ListAchievement(ctx, accountID)
621
  if err != nil {
622
  return nil, response.HandleGormError(err, "Internal Server Error")
623
  }
@@ -645,9 +652,9 @@ func calculateProgress(
645
  familyMemberCV int,
646
  jobCV int,
647
  achievementCV int,
648
- ) *models.ProgressResponse {
649
 
650
- // fullIfPresent returns 100 if the data is greater than 0, otherwise returns 0
651
  fullIfPresent := func(data int) float64 {
652
  if data > 0 {
653
  return 100
@@ -667,7 +674,7 @@ func calculateProgress(
667
 
668
  overallProgress := (accountDetailsPercentage + personalityAndPreferenceCVPercentage + physicalAndHealthCVPercentage + worshipAndReligiousUnderstandingCVPercentage + educationCVPercentage + familyMemberCVPercentage + jobCVPercentage + achievementCVPercentage) / 8
669
 
670
- return &models.ProgressResponse{
671
  AccountDetailsProgress: math.Round(accountDetailsPercentage*100) / 100,
672
  PersonalityAndPreferenceCVProgress: math.Round(personalityAndPreferenceCVPercentage*100) / 100,
673
  FamilyMemberCVProgress: math.Round(familyMemberCVPercentage*100) / 100,
 
11
  "api.qobiltu.id/pkg/validation"
12
  "api.qobiltu.id/repositories"
13
  "api.qobiltu.id/response"
14
+ "api.qobiltu.id/utils"
15
  "gorm.io/gorm"
16
  )
17
 
18
  type CVService interface {
19
+ SaveAccountDetails(ctx context.Context, req *models.SaveAccountDetailsRequest) (*models.AccountDetails, error)
20
+ GetAccountDetails(ctx context.Context, req *models.GetAccountDetailsRequest) (*models.AccountDetails, error)
21
+
22
+ SavePersonalityAndPreference(ctx context.Context, req *models.SavePersonalityAndPreferenceRequest) (*models.PersonalityAndPreferenceCV, error)
23
+ GetPersonalityAndPreference(ctx context.Context, req *models.GetPersonalityAndPreferenceRequest) (*models.PersonalityAndPreferenceCV, error)
24
+
25
+ CreateFamilyMember(ctx context.Context, req *models.CreateFamilyMemberRequest) (*models.FamilyMemberCV, error)
26
+ UpdateFamilyMember(ctx context.Context, req *models.UpdateFamilyMemberRequest) (*models.FamilyMemberCV, error)
27
+ ListFamilyMember(ctx context.Context, req *models.ListFamilyMemberRequest) ([]models.FamilyMemberCV, error)
28
+ GetFamilyMember(ctx context.Context, req *models.GetFamilyMemberRequest) (*models.FamilyMemberCV, error)
29
+ DeleteFamilyMember(ctx context.Context, req *models.DeleteFamilyMemberRequest) error
30
+
31
+ SavePhysicalAndHealth(ctx context.Context, req *models.SavePhysicalAndHealthRequest) (*models.PhysicalAndHealthCV, error)
32
+ GetPhysicalAndHealth(ctx context.Context, req *models.GetPhysicalAndHealthRequest) (*models.PhysicalAndHealthCV, error)
33
+
34
+ SaveWorshipAndReligiousUnderstanding(ctx context.Context, req *models.SaveWorshipAndReligiousUnderstandingRequest) (*models.WorshipAndReligiousUnderstandingCV, error)
35
+ GetWorshipAndReligiousUnderstanding(ctx context.Context, req *models.GetWorshipAndReligiousUnderstandingRequest) (*models.WorshipAndReligiousUnderstandingCV, error)
36
+
37
+ CreateEducation(ctx context.Context, req *models.CreateEducationRequest) (*models.EducationCV, error)
38
+ UpdateEducation(ctx context.Context, req *models.UpdateEducationRequest) (*models.EducationCV, error)
39
+ ListEducation(ctx context.Context, req *models.ListEducationRequest) ([]models.EducationCV, error)
40
+ GetEducation(ctx context.Context, req *models.GetEducationRequest) (*models.EducationCV, error)
41
+ DeleteEducation(ctx context.Context, req *models.DeleteEducationRequest) error
42
+
43
+ CreateJob(ctx context.Context, req *models.CreateJobRequest) (*models.JobCV, error)
44
+ UpdateJob(ctx context.Context, req *models.UpdateJobRequest) (*models.JobCV, error)
45
+ ListJob(ctx context.Context, req *models.ListJobRequest) ([]models.JobCV, error)
46
+ GetJob(ctx context.Context, req *models.GetJobRequest) (*models.JobCV, error)
47
+ DeleteJob(ctx context.Context, req *models.DeleteJobRequest) error
48
+
49
+ CreateAchievement(ctx context.Context, req *models.CreateAchievementRequest) (*models.AchievementCV, error)
50
+ UpdateAchievement(ctx context.Context, req *models.UpdateAchievementRequest) (*models.AchievementCV, error)
51
+ ListAchievement(ctx context.Context, req *models.ListAchievementRequest) ([]models.AchievementCV, error)
52
+ GetAchievement(ctx context.Context, req *models.GetAchievementRequest) (*models.AchievementCV, error)
53
+ DeleteAchievement(ctx context.Context, req *models.DeleteAchievementRequest) error
54
 
55
  UploadProfileImage(ctx context.Context, req *models.UploadProfileImageRequest) (*models.UploadProfileImageResponse, error)
56
+ GetProgressCV(ctx context.Context, req *models.GetProgressCVRequest) (*models.GetProgressCVResponse, error)
57
  }
58
 
59
  type cvService struct {
 
68
  }
69
  }
70
 
71
+ func (s *cvService) SaveAccountDetails(ctx context.Context, req *models.SaveAccountDetailsRequest) (*models.AccountDetails, error) {
72
+ // notes
73
+ // jika ingin mengubah value wajib kirimkan field beserta value nya
74
  if err := validation.Validate(req); err != nil {
75
  return nil, response.HandleValidationError(err)
76
  }
 
87
  }
88
 
89
  accountDetails.AccountID = uint(req.AccountID)
90
+
91
+ utils.AssignIfNotNil(&accountDetails.FullName, req.FullName)
92
+ utils.AssignIfNotNil(&accountDetails.Gender, req.Gender)
93
+ utils.AssignIfNotNil(&accountDetails.DateOfBirth, req.DateOfBirth)
94
+ utils.AssignIfNotNil(&accountDetails.PlaceOfBirth, req.PlaceOfBirth)
95
+ utils.AssignIfNotNil(&accountDetails.Domicile, req.Domicile)
96
+ utils.AssignIfNotNil(&accountDetails.MaritalStatus, req.MaritalStatus)
97
+ utils.AssignIfNotNil(&accountDetails.LastEducation, req.LastEducation)
98
+ utils.AssignIfNotNil(&accountDetails.LastJob, req.LastJob)
99
 
100
  if req.PhoneNumber != nil {
101
  sanitizedPhone := validation.SanitizePhoneNumber(*req.PhoneNumber)
 
111
  return res, nil
112
  }
113
 
114
+ func (s *cvService) GetAccountDetails(ctx context.Context, req *models.GetAccountDetailsRequest) (*models.AccountDetails, error) {
115
+ res, err := s.cvRepository.GetAccountDetailsByAccountID(ctx, req.AccountID)
116
  if err != nil {
117
+ return nil, response.HandleGormError(err, "Internal Server Error")
118
  }
119
  return res, nil
120
  }
121
 
122
+ func (s *cvService) SavePersonalityAndPreference(ctx context.Context, req *models.SavePersonalityAndPreferenceRequest) (*models.PersonalityAndPreferenceCV, error) {
123
  if err := validation.Validate(req); err != nil {
124
  return nil, response.HandleValidationError(err)
125
  }
 
136
  }
137
 
138
  personalityAndPreference.AccountID = req.AccountID
139
+
140
+ utils.AssignIfNotNil(&personalityAndPreference.PositiveTraits, req.PositiveTraits)
141
+ utils.AssignIfNotNil(&personalityAndPreference.NegativeTraits, req.NegativeTraits)
142
+ utils.AssignIfNotNil(&personalityAndPreference.Hobbies, req.Hobbies)
143
+ utils.AssignIfNotNil(&personalityAndPreference.LifeGoals, req.LifeGoals)
144
+ utils.AssignIfNotNil(&personalityAndPreference.DailyActivities, req.DailyActivities)
145
+ utils.AssignIfNotNil(&personalityAndPreference.LeisureActivities, req.LeisureActivities)
146
+ utils.AssignIfNotNil(&personalityAndPreference.Likes, req.Likes)
147
+ utils.AssignIfNotNil(&personalityAndPreference.Dislikes, req.Dislikes)
148
+ utils.AssignIfNotNil(&personalityAndPreference.StressHandling, req.StressHandling)
149
+ utils.AssignIfNotNil(&personalityAndPreference.AngerTriggers, req.AngerTriggers)
150
+ utils.AssignIfNotNil(&personalityAndPreference.FavoriteFoodAndDrinks, req.FavoriteFoodAndDrinks)
151
+ utils.AssignIfNotNil(&personalityAndPreference.CanCook, req.CanCook)
152
+ utils.AssignIfNotNil(&personalityAndPreference.TypesOfDishesCooked, req.TypesOfDishesCooked)
153
+ utils.AssignIfNotNil(&personalityAndPreference.MonthlyExpenses, req.MonthlyExpenses)
154
 
155
  res, err := s.cvRepository.SavePersonalityAndPreference(ctx, personalityAndPreference)
156
  if err != nil {
 
160
  return res, nil
161
  }
162
 
163
+ func (s *cvService) GetPersonalityAndPreference(ctx context.Context, req *models.GetPersonalityAndPreferenceRequest) (*models.PersonalityAndPreferenceCV, error) {
164
+ res, err := s.cvRepository.GetPersonalityAndPreferenceByAccountID(ctx, req.AccountID)
165
  if err != nil {
166
  return nil, response.HandleGormError(err, "Internal Server Error")
167
  }
 
169
  return res, nil
170
  }
171
 
172
+ func (s *cvService) CreateFamilyMember(ctx context.Context, req *models.CreateFamilyMemberRequest) (*models.FamilyMemberCV, error) {
173
  if err := validation.Validate(req); err != nil {
174
  return nil, response.HandleValidationError(err)
175
  }
 
188
  // Simpan ke repository
189
  res, err := s.cvRepository.SaveFamilyMember(ctx, familyMember)
190
  if err != nil {
191
+ return nil, response.HandleGormError(err, "Internal Server Error")
192
  }
193
 
194
  return res, nil
195
  }
196
 
197
+ func (s *cvService) ListFamilyMember(ctx context.Context, req *models.ListFamilyMemberRequest) ([]models.FamilyMemberCV, error) {
198
+ list, err := s.cvRepository.ListFamilyMember(ctx, req.AccountID)
199
  if err != nil {
200
+ return nil, response.HandleGormError(err, "Internal Server Error")
201
  }
202
  return list, nil
203
  }
204
 
205
+ func (s *cvService) GetFamilyMember(ctx context.Context, req *models.GetFamilyMemberRequest) (*models.FamilyMemberCV, error) {
206
+ res, err := s.cvRepository.GetFamilyMember(ctx, req.ID)
207
  if err != nil {
208
+ return nil, response.HandleGormError(err, "Internal Server Error")
209
  }
210
  return res, nil
211
  }
212
 
213
+ func (s *cvService) DeleteFamilyMember(ctx context.Context, req *models.DeleteFamilyMemberRequest) error {
214
+ err := s.cvRepository.DeleteFamilyMember(ctx, req.ID)
215
  if err != nil {
216
+ return response.HandleGormError(err, "Internal Server Error")
217
  }
218
  return nil
219
  }
220
 
221
+ func (s *cvService) UpdateFamilyMember(ctx context.Context, req *models.UpdateFamilyMemberRequest) (*models.FamilyMemberCV, error) {
222
  if err := validation.Validate(req); err != nil {
223
  return nil, response.HandleValidationError(err)
224
  }
225
 
226
+ existing, err := s.cvRepository.GetFamilyMember(ctx, req.ID)
227
  if err != nil {
228
+ return nil, response.HandleGormError(err, "Internal Server Error")
229
  }
230
 
231
+ utils.AssignIfNotNil(&existing.Role, req.Role)
232
+ utils.AssignIfNotNil(&existing.Status, req.Status)
233
+ utils.AssignIfNotNil(&existing.Religion, req.Religion)
234
+ utils.AssignIfNotNil(&existing.Job, req.Job)
235
+ utils.AssignIfNotNil(&existing.LastEducation, req.LastEducation)
236
+ utils.AssignIfNotNil(&existing.Age, req.Age)
237
 
238
  updated, err := s.cvRepository.SaveFamilyMember(ctx, existing)
239
  if err != nil {
240
+ return nil, response.HandleGormError(err, "Internal Server Error")
241
  }
242
 
243
  return updated, nil
244
  }
245
 
246
+ func (s *cvService) SavePhysicalAndHealth(ctx context.Context, req *models.SavePhysicalAndHealthRequest) (*models.PhysicalAndHealthCV, error) {
247
  if err := validation.Validate(req); err != nil {
248
  return nil, response.HandleValidationError(err)
249
  }
 
251
  // Cek apakah data sudah ada berdasarkan account_id
252
  existing, err := s.cvRepository.GetPhysicalAndHealthByAccountID(ctx, req.AccountID)
253
  if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
254
+ return nil, response.HandleGormError(err, "Internal Server Error")
255
  }
256
 
257
  // Jika belum ada, buat objek baru
 
261
 
262
  // Mapping field dari request
263
  existing.AccountID = req.AccountID
264
+
265
+ utils.AssignIfNotNil(&existing.HeightInCm, req.HeightInCm)
266
+ utils.AssignIfNotNil(&existing.WeightInKg, req.WeightInKg)
267
+ utils.AssignIfNotNil(&existing.BodyShape, req.BodyShape)
268
+ utils.AssignIfNotNil(&existing.SkinColor, req.SkinColor)
269
+ utils.AssignIfNotNil(&existing.HairType, req.HairType)
270
+ utils.AssignIfNotNil(&existing.MedicalHistory, req.MedicalHistory)
271
+ utils.AssignIfNotNil(&existing.PhysicalDisorder, req.PhysicalDisorder)
272
+ utils.AssignIfNotNil(&existing.PhysicalTraits, req.PhysicalTraits)
273
 
274
  // Simpan data
275
  res, err := s.cvRepository.SavePhysicalAndHealth(ctx, existing)
276
  if err != nil {
277
+ return nil, response.HandleGormError(err, "Internal Server Error")
278
  }
279
 
280
  return res, nil
281
  }
282
 
283
+ func (s *cvService) GetPhysicalAndHealth(ctx context.Context, req *models.GetPhysicalAndHealthRequest) (*models.PhysicalAndHealthCV, error) {
284
+ res, err := s.cvRepository.GetPhysicalAndHealthByAccountID(ctx, req.AccountID)
285
  if err != nil {
286
+ return nil, response.HandleGormError(err, "Internal Server Error")
287
  }
288
  return res, nil
289
  }
290
 
291
+ func (s *cvService) SaveWorshipAndReligiousUnderstanding(ctx context.Context, req *models.SaveWorshipAndReligiousUnderstandingRequest) (*models.WorshipAndReligiousUnderstandingCV, error) {
292
  if err := validation.Validate(req); err != nil {
293
  return nil, response.HandleValidationError(err)
294
  }
 
296
  // Cek apakah data sudah ada berdasarkan account_id
297
  worshipAndReligiousUnderstanding, err := s.cvRepository.GetWorshipAndReligiousUnderstandingByAccountID(ctx, req.AccountID)
298
  if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
299
+ return nil, response.HandleGormError(err, "Internal Server Error")
300
  }
301
 
302
  // Jika belum ada, buat objek baru
 
306
 
307
  // Mapping field dari request
308
  worshipAndReligiousUnderstanding.AccountID = req.AccountID
309
+
310
+ utils.AssignIfNotNil(&worshipAndReligiousUnderstanding.ObligatoryPrayer, req.ObligatoryPrayer)
311
+ utils.AssignIfNotNil(&worshipAndReligiousUnderstanding.CongregationalPrayer, req.CongregationalPrayer)
312
+ utils.AssignIfNotNil(&worshipAndReligiousUnderstanding.TahajjudPrayer, req.TahajjudPrayer)
313
+ utils.AssignIfNotNil(&worshipAndReligiousUnderstanding.DhuhaPrayer, req.DhuhaPrayer)
314
+ utils.AssignIfNotNil(&worshipAndReligiousUnderstanding.QuranMemorization, req.QuranMemorization)
315
+ utils.AssignIfNotNil(&worshipAndReligiousUnderstanding.QuranReadingAbility, req.QuranReadingAbility)
316
+ utils.AssignIfNotNil(&worshipAndReligiousUnderstanding.DaudFasting, req.DaudFasting)
317
+ utils.AssignIfNotNil(&worshipAndReligiousUnderstanding.AyyamulBidhFasting, req.AyyamulBidhFasting)
318
+ utils.AssignIfNotNil(&worshipAndReligiousUnderstanding.HajjOrUmrah, req.HajjOrUmrah)
319
+ utils.AssignIfNotNil(&worshipAndReligiousUnderstanding.ListeningToMusic, req.ListeningToMusic)
320
+ utils.AssignIfNotNil(&worshipAndReligiousUnderstanding.OpinionOnIkhtilat, req.OpinionOnIkhtilat)
321
+ utils.AssignIfNotNil(&worshipAndReligiousUnderstanding.OpinionOnTouchingNonMahram, req.OpinionOnTouchingNonMahram)
322
+ utils.AssignIfNotNil(&worshipAndReligiousUnderstanding.OpinionOnVeil, req.OpinionOnVeil)
323
+ utils.AssignIfNotNil(&worshipAndReligiousUnderstanding.WeeklyReligiousStudies, req.WeeklyReligiousStudies)
324
+ utils.AssignIfNotNil(&worshipAndReligiousUnderstanding.FollowedUstadz, req.FollowedUstadz)
325
 
326
  // Simpan data
327
  res, err := s.cvRepository.SaveWorshipAndReligiousUnderstanding(ctx, worshipAndReligiousUnderstanding)
 
332
  return res, nil
333
  }
334
 
335
+ func (s *cvService) GetWorshipAndReligiousUnderstanding(ctx context.Context, req *models.GetWorshipAndReligiousUnderstandingRequest) (*models.WorshipAndReligiousUnderstandingCV, error) {
336
+ res, err := s.cvRepository.GetWorshipAndReligiousUnderstandingByAccountID(ctx, req.AccountID)
337
  if err != nil {
338
+ return nil, response.HandleGormError(err, "Internal Server Error")
339
  }
340
  return res, nil
341
  }
342
 
343
+ func (s *cvService) CreateEducation(ctx context.Context, req *models.CreateEducationRequest) (*models.EducationCV, error) {
344
  if err := validation.Validate(req); err != nil {
345
  return nil, response.HandleValidationError(err)
346
  }
 
356
 
357
  res, err := s.cvRepository.SaveEducation(ctx, edu)
358
  if err != nil {
359
+ return nil, response.HandleGormError(err, "Internal Server Error")
360
  }
361
 
362
  return res, nil
363
  }
364
 
365
+ func (s *cvService) UpdateEducation(ctx context.Context, req *models.UpdateEducationRequest) (*models.EducationCV, error) {
366
  if err := validation.Validate(req); err != nil {
367
  return nil, response.HandleValidationError(err)
368
  }
369
 
370
+ edu, err := s.cvRepository.GetEducation(ctx, req.ID)
371
  if err != nil {
372
+ return nil, response.HandleGormError(err, "Internal Server Error")
373
  }
374
 
375
+ utils.AssignIfNotNil(&edu.LastEducation, req.LastEducation)
376
+ utils.AssignIfNotNil(&edu.EducationInstitute, req.EducationInstitute)
377
+ utils.AssignIfNotNil(&edu.EducationMajor, req.EducationMajor)
378
+ utils.AssignIfNotNil(&edu.YearStart, req.YearStart)
379
+ utils.AssignIfNotNil(&edu.YearGraduate, req.YearGraduate)
380
 
381
  res, err := s.cvRepository.SaveEducation(ctx, edu)
382
  if err != nil {
383
+ return nil, response.HandleGormError(err, "Internal Server Error")
384
  }
385
  return res, nil
386
  }
387
 
388
+ func (s *cvService) ListEducation(ctx context.Context, req *models.ListEducationRequest) ([]models.EducationCV, error) {
389
+ return s.cvRepository.ListEducation(ctx, req.AccountID)
390
  }
391
 
392
+ func (s *cvService) GetEducation(ctx context.Context, req *models.GetEducationRequest) (*models.EducationCV, error) {
393
+ edu, err := s.cvRepository.GetEducation(ctx, req.ID)
394
  if err != nil {
395
+ return nil, response.HandleGormError(err, "Internal Server Error")
396
  }
397
  return edu, nil
398
  }
399
 
400
+ func (s *cvService) DeleteEducation(ctx context.Context, req *models.DeleteEducationRequest) error {
401
+ return s.cvRepository.DeleteEducation(ctx, req.ID)
402
  }
403
 
404
+ func (s *cvService) CreateJob(ctx context.Context, req *models.CreateJobRequest) (*models.JobCV, error) {
405
  if err := validation.Validate(req); err != nil {
406
  return nil, response.HandleValidationError(err)
407
  }
 
416
  }
417
  res, err := s.cvRepository.SaveJob(ctx, job)
418
  if err != nil {
419
+ return nil, response.HandleGormError(err, "Internal Server Error")
420
  }
421
  return res, nil
422
  }
423
 
424
+ func (s *cvService) UpdateJob(ctx context.Context, req *models.UpdateJobRequest) (*models.JobCV, error) {
425
  if err := validation.Validate(req); err != nil {
426
  return nil, response.HandleValidationError(err)
427
  }
428
 
429
+ job, err := s.cvRepository.GetJob(ctx, req.ID)
430
  if err != nil {
431
+ return nil, response.HandleGormError(err, "Internal Server Error")
432
  }
433
 
434
+ utils.AssignIfNotNil(&job.InstitutionName, req.InstitutionName)
435
+ utils.AssignIfNotNil(&job.CurrentJob, req.CurrentJob)
436
+ utils.AssignIfNotNil(&job.YearStartedWorking, req.YearStartedWorking)
437
+ utils.AssignIfNotNil(&job.MonthlyIncome, req.MonthlyIncome)
438
+ utils.AssignIfNotNil(&job.IncomeSources, req.IncomeSources)
439
 
440
  res, err := s.cvRepository.SaveJob(ctx, job)
441
  if err != nil {
442
+ return nil, response.HandleGormError(err, "Internal Server Error")
443
  }
444
  return res, nil
445
  }
446
 
447
+ func (s *cvService) ListJob(ctx context.Context, req *models.ListJobRequest) ([]models.JobCV, error) {
448
+ return s.cvRepository.ListJob(ctx, req.AccountID)
449
  }
450
 
451
+ func (s *cvService) GetJob(ctx context.Context, req *models.GetJobRequest) (*models.JobCV, error) {
452
+ job, err := s.cvRepository.GetJob(ctx, req.ID)
453
  if err != nil {
454
+ return nil, response.HandleGormError(err, "Internal Server Error")
455
  }
456
  return job, nil
457
  }
458
 
459
+ func (s *cvService) DeleteJob(ctx context.Context, req *models.DeleteJobRequest) error {
460
+ return s.cvRepository.DeleteJob(ctx, req.ID)
461
  }
462
 
463
+ func (s *cvService) CreateAchievement(ctx context.Context, req *models.CreateAchievementRequest) (*models.AchievementCV, error) {
464
  ach := &models.AchievementCV{
465
  AccountID: req.AccountID,
466
  AchievementOrAward: req.AchievementOrAward,
467
  }
468
  res, err := s.cvRepository.SaveAchievement(ctx, ach)
469
  if err != nil {
470
+ return nil, response.HandleGormError(err, "Internal Server Error")
471
  }
472
 
473
  return res, nil
474
  }
475
 
476
+ func (s *cvService) UpdateAchievement(ctx context.Context, req *models.UpdateAchievementRequest) (*models.AchievementCV, error) {
477
+ ach, err := s.cvRepository.GetAchievement(ctx, req.ID)
478
  if err != nil {
479
+ return nil, response.HandleGormError(err, "Internal Server Error")
480
  }
481
 
482
  ach.AchievementOrAward = req.AchievementOrAward
483
 
484
  res, err := s.cvRepository.SaveAchievement(ctx, ach)
485
  if err != nil {
486
+ return nil, response.HandleGormError(err, "Internal Server Error")
487
  }
488
 
489
  return res, nil
490
  }
491
 
492
+ func (s *cvService) ListAchievement(ctx context.Context, req *models.ListAchievementRequest) ([]models.AchievementCV, error) {
493
+ return s.cvRepository.ListAchievement(ctx, req.AccountID)
494
  }
495
 
496
+ func (s *cvService) GetAchievement(ctx context.Context, req *models.GetAchievementRequest) (*models.AchievementCV, error) {
497
+ ach, err := s.cvRepository.GetAchievement(ctx, req.ID)
498
  if err != nil {
499
+ return nil, response.HandleGormError(err, "Internal Server Error")
500
  }
501
  return ach, nil
502
  }
503
 
504
+ func (s *cvService) DeleteAchievement(ctx context.Context, req *models.DeleteAchievementRequest) error {
505
+ return s.cvRepository.DeleteAchievement(ctx, req.ID)
506
  }
507
 
508
  func (s *cvService) UploadProfileImage(ctx context.Context, req *models.UploadProfileImageRequest) (*models.UploadProfileImageResponse, error) {
 
576
  }, nil
577
  }
578
 
579
+ func (s *cvService) GetProgressCV(ctx context.Context, req *models.GetProgressCVRequest) (*models.GetProgressCVResponse, error) {
580
+ accountDetails, err := s.cvRepository.GetAccountDetailsByAccountID(ctx, req.AccountID)
581
  if err != nil {
582
  if !errors.Is(err, gorm.ErrRecordNotFound) {
583
  return nil, response.HandleGormError(err, "Internal Server Error")
 
585
  accountDetails = &models.AccountDetails{}
586
  }
587
 
588
+ personalityAndPreferenceCV, err := s.cvRepository.GetPersonalityAndPreferenceByAccountID(ctx, req.AccountID)
589
  if err != nil {
590
  if !errors.Is(err, gorm.ErrRecordNotFound) {
591
  return nil, response.HandleGormError(err, "Internal Server Error")
 
593
  personalityAndPreferenceCV = &models.PersonalityAndPreferenceCV{}
594
  }
595
 
596
+ physicalAndHealthCV, err := s.cvRepository.GetPhysicalAndHealthByAccountID(ctx, req.AccountID)
597
  if err != nil {
598
  if !errors.Is(err, gorm.ErrRecordNotFound) {
599
  return nil, response.HandleGormError(err, "Internal Server Error")
 
601
  physicalAndHealthCV = &models.PhysicalAndHealthCV{}
602
  }
603
 
604
+ worshipAndReligiousUnderstandingCV, err := s.cvRepository.GetWorshipAndReligiousUnderstandingByAccountID(ctx, req.AccountID)
605
  if err != nil {
606
  if !errors.Is(err, gorm.ErrRecordNotFound) {
607
  return nil, response.HandleGormError(err, "Internal Server Error")
 
609
  worshipAndReligiousUnderstandingCV = &models.WorshipAndReligiousUnderstandingCV{}
610
  }
611
 
612
+ educationCV, err := s.cvRepository.ListEducation(ctx, req.AccountID)
613
  if err != nil {
614
  return nil, response.HandleGormError(err, "Internal Server Error")
615
  }
616
 
617
+ familyMemberCV, err := s.cvRepository.ListFamilyMember(ctx, req.AccountID)
618
  if err != nil {
619
  return nil, response.HandleGormError(err, "Internal Server Error")
620
  }
621
 
622
+ jobCV, err := s.cvRepository.ListJob(ctx, req.AccountID)
623
  if err != nil {
624
  return nil, response.HandleGormError(err, "Internal Server Error")
625
  }
626
 
627
+ achievementCV, err := s.cvRepository.ListAchievement(ctx, req.AccountID)
628
  if err != nil {
629
  return nil, response.HandleGormError(err, "Internal Server Error")
630
  }
 
652
  familyMemberCV int,
653
  jobCV int,
654
  achievementCV int,
655
+ ) *models.GetProgressCVResponse {
656
 
657
+ // fullIfPresent mengembalikan 100 jika data lebih dari 0, jika tidak maka mengembalikan 0
658
  fullIfPresent := func(data int) float64 {
659
  if data > 0 {
660
  return 100
 
674
 
675
  overallProgress := (accountDetailsPercentage + personalityAndPreferenceCVPercentage + physicalAndHealthCVPercentage + worshipAndReligiousUnderstandingCVPercentage + educationCVPercentage + familyMemberCVPercentage + jobCVPercentage + achievementCVPercentage) / 8
676
 
677
+ return &models.GetProgressCVResponse{
678
  AccountDetailsProgress: math.Round(accountDetailsPercentage*100) / 100,
679
  PersonalityAndPreferenceCVProgress: math.Round(personalityAndPreferenceCVPercentage*100) / 100,
680
  FamilyMemberCVProgress: math.Round(familyMemberCVPercentage*100) / 100,
space/space/space/space/space/space/space/services/academy_quiz_answer_service.go CHANGED
@@ -53,4 +53,7 @@ func (s *AnswerQuizService) Update(userID uint, questionNo int, answer int) {
53
  }
54
  return
55
  })
 
 
 
56
  }
 
53
  }
54
  return
55
  })
56
+ s.Exception = QuizAttemptService.Exception
57
+ s.Error = errors.Join(s.Error, QuizAttemptService.Error)
58
+ return
59
  }
space/space/space/space/space/space/space/services/academy_quiz_question_service.go CHANGED
@@ -1,6 +1,8 @@
1
  package services
2
 
3
  import (
 
 
4
  "api.qobiltu.id/models"
5
  "api.qobiltu.id/repositories"
6
  )
@@ -39,4 +41,8 @@ func (s *QuestionQuizService) Retrieve(userID uint, questionNo int) {
39
  }
40
  return
41
  })
 
 
 
 
42
  }
 
1
  package services
2
 
3
  import (
4
+ "errors"
5
+
6
  "api.qobiltu.id/models"
7
  "api.qobiltu.id/repositories"
8
  )
 
41
  }
42
  return
43
  })
44
+
45
+ s.Exception = QuizAttemptService.Exception
46
+ s.Error = errors.Join(s.Error, QuizAttemptService.Error)
47
+ return
48
  }
space/space/space/space/space/space/space/space/controller/quiz/result_quiz_controller.go ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package controller
2
+
3
+ import (
4
+ "strconv"
5
+
6
+ "api.qobiltu.id/controller"
7
+ "api.qobiltu.id/models"
8
+ "api.qobiltu.id/services"
9
+ "github.com/gin-gonic/gin"
10
+ )
11
+
12
+ func Result(c *gin.Context) {
13
+ quizResult := services.QuizResultService{}
14
+ quizResultController := controller.Controller[any, models.QuizAttempt, []models.QuizResultResponse]{
15
+ Service: &quizResult.Service,
16
+ }
17
+ quizResultController.HeaderParse(c, func() {
18
+ academyId, _ := strconv.Atoi(c.Param("attempt_id"))
19
+ quizResult.Constructor.AccountID = uint(quizResultController.AccountData.UserID)
20
+ quizResult.Constructor.ID = uint(academyId) | 0
21
+ quizResult.Retrieve()
22
+ quizResultController.Response(c)
23
+ })
24
+ }
space/space/space/space/space/space/space/space/controller/quiz/submit_quiz_controller.go CHANGED
@@ -15,9 +15,10 @@ func Submit(c *gin.Context) {
15
  Service: &submitQuiz.Service,
16
  }
17
  submitQuizController.HeaderParse(c, func() {
18
- quizId, _ := strconv.Atoi(c.Param("attempt_id"))
19
- submitQuizController.Service.Constructor.ID = uint(quizId)
20
- submitQuiz.Create(submitQuizController.AccountData.UserID)
 
21
  submitQuizController.Response(c)
22
  })
23
  }
 
15
  Service: &submitQuiz.Service,
16
  }
17
  submitQuizController.HeaderParse(c, func() {
18
+ attemptId, _ := strconv.Atoi(c.Param("attempt_id"))
19
+ submitQuizController.Service.Constructor.ID = uint(attemptId)
20
+ submitQuizController.Service.Constructor.AccountID = uint(submitQuizController.AccountData.UserID)
21
+ submitQuiz.Create()
22
  submitQuizController.Response(c)
23
  })
24
  }
space/space/space/space/space/space/space/space/models/database_orm_model.go CHANGED
@@ -152,7 +152,8 @@ type Question struct {
152
  QuizID uint `json:"quiz_id"`
153
  Content string `json:"content"`
154
  Order int `json:"order"`
155
- CorrectAnswer uint `json:"-"`
 
156
  }
157
  type Quiz struct {
158
  ID uint `gorm:"primaryKey" json:"id"`
@@ -160,7 +161,7 @@ type Quiz struct {
160
  Slug string `json:"slug" gorm:"uniqueIndex" `
161
  Title string `json:"title"`
162
  Description string `json:"description"`
163
- TotalQuestions string `json:"total_questions"`
164
  AttemptLimit int `json:"attempt_limit"`
165
  TimeLimit int `json:"time_limit"`
166
  MinScore int `json:"min_score"`
@@ -274,7 +275,7 @@ type (
274
  ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // id
275
  AccountID int64 `gorm:"column:account_id;not null" json:"account_id"` // id akun
276
  Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"` // relasi ke akun
277
- LastEducation *string `gorm:"column:last_education" json:"last_education" validate:""` // pendidikan terakhir
278
  EducationInstitute *string `gorm:"column:education_institute" json:"education_institute"` // institusi pendidikan
279
  EducationMajor *string `gorm:"column:education_major" json:"education_major"` // jurusan pendidikan
280
  YearStart *int `gorm:"column:year_start" json:"year_start"` // tahun masuk
 
152
  QuizID uint `json:"quiz_id"`
153
  Content string `json:"content"`
154
  Order int `json:"order"`
155
+ CorrectAnswer uint `json:"corrent_answer"`
156
+ Review string `json:"reviews"`
157
  }
158
  type Quiz struct {
159
  ID uint `gorm:"primaryKey" json:"id"`
 
161
  Slug string `json:"slug" gorm:"uniqueIndex" `
162
  Title string `json:"title"`
163
  Description string `json:"description"`
164
+ TotalQuestions int `json:"total_questions"`
165
  AttemptLimit int `json:"attempt_limit"`
166
  TimeLimit int `json:"time_limit"`
167
  MinScore int `json:"min_score"`
 
275
  ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // id
276
  AccountID int64 `gorm:"column:account_id;not null" json:"account_id"` // id akun
277
  Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"` // relasi ke akun
278
+ LastEducation *string `gorm:"column:last_education" json:"last_education"` // pendidikan terakhir
279
  EducationInstitute *string `gorm:"column:education_institute" json:"education_institute"` // institusi pendidikan
280
  EducationMajor *string `gorm:"column:education_major" json:"education_major"` // jurusan pendidikan
281
  YearStart *int `gorm:"column:year_start" json:"year_start"` // tahun masuk