lifedebugger commited on
Commit
a020582
·
1 Parent(s): bf4c029

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. assets/efs.go +8 -0
  2. assets/seeds/city.json +4114 -0
  3. assets/seeds/province.json +192 -0
  4. controller/options/options_controller.go +72 -0
  5. controller/region/region_controller.go +90 -0
  6. main.go +13 -0
  7. models/database_orm_model.go +8 -5
  8. models/request_model.go +33 -4
  9. models/response_model.go +2 -0
  10. repositories/option_repository.go +82 -36
  11. repositories/region_repository.go +41 -31
  12. router/options_route.go +11 -15
  13. router/router.go +1 -1
  14. router/server.go +9 -2
  15. services/options_service.go +149 -0
  16. services/region_service.go +41 -63
  17. space/services/academy_quiz_navigation_service.go +1 -1
  18. space/space/space/controller/quiz/navigation_quiz_controller.go +24 -0
  19. space/space/space/models/database_orm_model.go +2 -1
  20. space/space/space/models/response_model.go +9 -0
  21. space/space/space/repositories/quiz_repository.go +9 -0
  22. space/space/space/router/quiz_route.go +1 -0
  23. space/space/space/services/academy_quiz_answer_service.go +6 -2
  24. space/space/space/services/academy_quiz_navigation_service.go +31 -0
  25. space/space/space/space/pkg/mail/sender.go +8 -0
  26. space/space/space/space/pkg/mail/smtp.go +71 -0
  27. space/space/space/space/space/pkg/worker/processor.go +3 -2
  28. space/space/space/space/space/space/controller/email/email_controller.go +64 -0
  29. space/space/space/space/space/space/main.go +7 -1
  30. space/space/space/space/space/space/models/database_orm_model.go +7 -5
  31. space/space/space/space/space/space/models/request_model.go +11 -0
  32. space/space/space/space/space/space/repositories/email_repository.go +65 -0
  33. space/space/space/space/space/space/router/email_route.go +4 -6
  34. space/space/space/space/space/space/router/router.go +1 -2
  35. space/space/space/space/space/space/router/server.go +4 -0
  36. space/space/space/space/space/space/services/email_service.go +123 -0
  37. space/space/space/space/space/space/space/controller/quiz/review_quiz_controller.go +26 -0
  38. space/space/space/space/space/space/space/router/quiz_route.go +1 -0
  39. space/space/space/space/space/space/space/services/academy_quiz_question_service.go +2 -0
  40. space/space/space/space/space/space/space/services/academy_quiz_review_service.go +56 -0
  41. space/space/space/space/space/space/space/services/academy_quiz_service.go +2 -5
  42. space/space/space/space/space/space/space/space/main.go +0 -6
  43. space/space/space/space/space/space/space/space/models/database_orm_model.go +12 -7
  44. space/space/space/space/space/space/space/space/models/request_model.go +8 -6
  45. space/space/space/space/space/space/space/space/router/router.go +0 -1
  46. space/space/space/space/space/space/space/space/router/server.go +0 -4
  47. space/space/space/space/space/space/space/space/services/partner_criteria_service.go +4 -2
  48. space/space/space/space/space/space/space/space/space/pkg/validation/validation.go +16 -155
  49. space/space/space/space/space/space/space/space/space/response/validation.go +19 -2
  50. space/space/space/space/space/space/space/space/space/services/marriage_readiness_profile_service.go +4 -3
assets/efs.go CHANGED
@@ -11,3 +11,11 @@ const (
11
  EmailConfirmationTemplatePath = "emails/email-confirmation.tmpl"
12
  EmailForgotPasswordTemplatePath = "emails/email-forgot-password.tmpl"
13
  )
 
 
 
 
 
 
 
 
 
11
  EmailConfirmationTemplatePath = "emails/email-confirmation.tmpl"
12
  EmailForgotPasswordTemplatePath = "emails/email-forgot-password.tmpl"
13
  )
14
+
15
+ var (
16
+ //go:embed "seeds/province.json"
17
+ EmbeddedProvinceJSON []byte
18
+
19
+ //go:embed "seeds/city.json"
20
+ EmbeddedCityJSON []byte
21
+ )
assets/seeds/city.json ADDED
@@ -0,0 +1,4114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "id": 1,
4
+ "type": "Kabupaten",
5
+ "name": "Aceh Barat",
6
+ "code": "05",
7
+ "full_code": "1105",
8
+ "province_id": 1
9
+ },
10
+ {
11
+ "id": 2,
12
+ "type": "Kabupaten",
13
+ "name": "Aceh Barat Daya",
14
+ "code": "12",
15
+ "full_code": "1112",
16
+ "province_id": 1
17
+ },
18
+ {
19
+ "id": 3,
20
+ "type": "Kabupaten",
21
+ "name": "Sabu Raijua",
22
+ "code": "20",
23
+ "full_code": "5320",
24
+ "province_id": 23
25
+ },
26
+ {
27
+ "id": 4,
28
+ "type": "Kota",
29
+ "name": "Salatiga",
30
+ "code": "73",
31
+ "full_code": "3373",
32
+ "province_id": 10
33
+ },
34
+ {
35
+ "id": 5,
36
+ "type": "Kabupaten",
37
+ "name": "Aceh Besar",
38
+ "code": "06",
39
+ "full_code": "1106",
40
+ "province_id": 1
41
+ },
42
+ {
43
+ "id": 6,
44
+ "type": "Kabupaten",
45
+ "name": "Aceh Jaya",
46
+ "code": "14",
47
+ "full_code": "1114",
48
+ "province_id": 1
49
+ },
50
+ {
51
+ "id": 7,
52
+ "type": "Kabupaten",
53
+ "name": "Aceh Selatan",
54
+ "code": "01",
55
+ "full_code": "1101",
56
+ "province_id": 1
57
+ },
58
+ {
59
+ "id": 8,
60
+ "type": "Kabupaten",
61
+ "name": "Aceh Singkil",
62
+ "code": "10",
63
+ "full_code": "1110",
64
+ "province_id": 1
65
+ },
66
+ {
67
+ "id": 9,
68
+ "type": "Kabupaten",
69
+ "name": "Aceh Tamiang",
70
+ "code": "16",
71
+ "full_code": "1116",
72
+ "province_id": 1
73
+ },
74
+ {
75
+ "id": 10,
76
+ "type": "Kabupaten",
77
+ "name": "Aceh Tengah",
78
+ "code": "04",
79
+ "full_code": "1104",
80
+ "province_id": 1
81
+ },
82
+ {
83
+ "id": 11,
84
+ "type": "Kabupaten",
85
+ "name": "Aceh Tenggara",
86
+ "code": "02",
87
+ "full_code": "1102",
88
+ "province_id": 1
89
+ },
90
+ {
91
+ "id": 12,
92
+ "type": "Kabupaten",
93
+ "name": "Aceh Timur",
94
+ "code": "03",
95
+ "full_code": "1103",
96
+ "province_id": 1
97
+ },
98
+ {
99
+ "id": 13,
100
+ "type": "Kabupaten",
101
+ "name": "Gorontalo Utara",
102
+ "code": "05",
103
+ "full_code": "7505",
104
+ "province_id": 7
105
+ },
106
+ {
107
+ "id": 14,
108
+ "type": "Kabupaten",
109
+ "name": "Aceh Utara",
110
+ "code": "08",
111
+ "full_code": "1108",
112
+ "province_id": 1
113
+ },
114
+ {
115
+ "id": 15,
116
+ "type": "Kabupaten",
117
+ "name": "Agam",
118
+ "code": "06",
119
+ "full_code": "1306",
120
+ "province_id": 36
121
+ },
122
+ {
123
+ "id": 16,
124
+ "type": "Kabupaten",
125
+ "name": "Alor",
126
+ "code": "05",
127
+ "full_code": "5305",
128
+ "province_id": 23
129
+ },
130
+ {
131
+ "id": 17,
132
+ "type": "Kota",
133
+ "name": "Ambon",
134
+ "code": "71",
135
+ "full_code": "8171",
136
+ "province_id": 20
137
+ },
138
+ {
139
+ "id": 18,
140
+ "type": "Kabupaten",
141
+ "name": "Gowa",
142
+ "code": "06",
143
+ "full_code": "7306",
144
+ "province_id": 32
145
+ },
146
+ {
147
+ "id": 19,
148
+ "type": "Kota",
149
+ "name": "Samarinda",
150
+ "code": "72",
151
+ "full_code": "6472",
152
+ "province_id": 15
153
+ },
154
+ {
155
+ "id": 20,
156
+ "type": "Kabupaten",
157
+ "name": "Asahan",
158
+ "code": "09",
159
+ "full_code": "1209",
160
+ "province_id": 38
161
+ },
162
+ {
163
+ "id": 21,
164
+ "type": "Kabupaten",
165
+ "name": "Sambas",
166
+ "code": "01",
167
+ "full_code": "6101",
168
+ "province_id": 12
169
+ },
170
+ {
171
+ "id": 22,
172
+ "type": "Kabupaten",
173
+ "name": "Asmat",
174
+ "code": "04",
175
+ "full_code": "9304",
176
+ "province_id": 28
177
+ },
178
+ {
179
+ "id": 23,
180
+ "type": "Kabupaten",
181
+ "name": "Samosir",
182
+ "code": "17",
183
+ "full_code": "1217",
184
+ "province_id": 38
185
+ },
186
+ {
187
+ "id": 24,
188
+ "type": "Kabupaten",
189
+ "name": "Badung",
190
+ "code": "03",
191
+ "full_code": "5103",
192
+ "province_id": 2
193
+ },
194
+ {
195
+ "id": 25,
196
+ "type": "Kabupaten",
197
+ "name": "Sampang",
198
+ "code": "27",
199
+ "full_code": "3527",
200
+ "province_id": 11
201
+ },
202
+ {
203
+ "id": 26,
204
+ "type": "Kabupaten",
205
+ "name": "Balangan",
206
+ "code": "11",
207
+ "full_code": "6311",
208
+ "province_id": 13
209
+ },
210
+ {
211
+ "id": 27,
212
+ "type": "Kabupaten",
213
+ "name": "Sanggau",
214
+ "code": "03",
215
+ "full_code": "6103",
216
+ "province_id": 12
217
+ },
218
+ {
219
+ "id": 28,
220
+ "type": "Kabupaten",
221
+ "name": "Sarmi",
222
+ "code": "10",
223
+ "full_code": "9110",
224
+ "province_id": 24
225
+ },
226
+ {
227
+ "id": 29,
228
+ "type": "Kabupaten",
229
+ "name": "Gresik",
230
+ "code": "25",
231
+ "full_code": "3525",
232
+ "province_id": 11
233
+ },
234
+ {
235
+ "id": 30,
236
+ "type": "Kabupaten",
237
+ "name": "Grobogan",
238
+ "code": "15",
239
+ "full_code": "3315",
240
+ "province_id": 10
241
+ },
242
+ {
243
+ "id": 31,
244
+ "type": "Kabupaten",
245
+ "name": "Gunung Mas",
246
+ "code": "10",
247
+ "full_code": "6210",
248
+ "province_id": 14
249
+ },
250
+ {
251
+ "id": 32,
252
+ "type": "Kabupaten",
253
+ "name": "Gunungkidul",
254
+ "code": "03",
255
+ "full_code": "3403",
256
+ "province_id": 5
257
+ },
258
+ {
259
+ "id": 33,
260
+ "type": "Kabupaten",
261
+ "name": "Sarolangun",
262
+ "code": "03",
263
+ "full_code": "1503",
264
+ "province_id": 8
265
+ },
266
+ {
267
+ "id": 34,
268
+ "type": "Kota",
269
+ "name": "Gunungsitoli",
270
+ "code": "78",
271
+ "full_code": "1278",
272
+ "province_id": 38
273
+ },
274
+ {
275
+ "id": 35,
276
+ "type": "Kota",
277
+ "name": "Sawahlunto",
278
+ "code": "73",
279
+ "full_code": "1373",
280
+ "province_id": 36
281
+ },
282
+ {
283
+ "id": 36,
284
+ "type": "Kabupaten",
285
+ "name": "Halmahera Barat",
286
+ "code": "01",
287
+ "full_code": "8201",
288
+ "province_id": 21
289
+ },
290
+ {
291
+ "id": 37,
292
+ "type": "Kabupaten",
293
+ "name": "Sekadau",
294
+ "code": "09",
295
+ "full_code": "6109",
296
+ "province_id": 12
297
+ },
298
+ {
299
+ "id": 38,
300
+ "type": "Kabupaten",
301
+ "name": "Halmahera Selatan",
302
+ "code": "04",
303
+ "full_code": "8204",
304
+ "province_id": 21
305
+ },
306
+ {
307
+ "id": 39,
308
+ "type": "Kabupaten",
309
+ "name": "Seluma",
310
+ "code": "05",
311
+ "full_code": "1705",
312
+ "province_id": 4
313
+ },
314
+ {
315
+ "id": 40,
316
+ "type": "Kabupaten",
317
+ "name": "Halmahera Tengah",
318
+ "code": "02",
319
+ "full_code": "8202",
320
+ "province_id": 21
321
+ },
322
+ {
323
+ "id": 41,
324
+ "type": "Kabupaten",
325
+ "name": "Semarang",
326
+ "code": "22",
327
+ "full_code": "3322",
328
+ "province_id": 10
329
+ },
330
+ {
331
+ "id": 42,
332
+ "type": "Kota",
333
+ "name": "Semarang",
334
+ "code": "74",
335
+ "full_code": "3374",
336
+ "province_id": 10
337
+ },
338
+ {
339
+ "id": 43,
340
+ "type": "Kabupaten",
341
+ "name": "Halmahera Timur",
342
+ "code": "06",
343
+ "full_code": "8206",
344
+ "province_id": 21
345
+ },
346
+ {
347
+ "id": 44,
348
+ "type": "Kabupaten",
349
+ "name": "Seram Bagian Barat",
350
+ "code": "06",
351
+ "full_code": "8106",
352
+ "province_id": 20
353
+ },
354
+ {
355
+ "id": 45,
356
+ "type": "Kabupaten",
357
+ "name": "Halmahera Utara",
358
+ "code": "03",
359
+ "full_code": "8203",
360
+ "province_id": 21
361
+ },
362
+ {
363
+ "id": 46,
364
+ "type": "Kabupaten",
365
+ "name": "Seram Bagian Timur",
366
+ "code": "05",
367
+ "full_code": "8105",
368
+ "province_id": 20
369
+ },
370
+ {
371
+ "id": 47,
372
+ "type": "Kabupaten",
373
+ "name": "Hulu Sungai Selatan",
374
+ "code": "06",
375
+ "full_code": "6306",
376
+ "province_id": 13
377
+ },
378
+ {
379
+ "id": 48,
380
+ "type": "Kabupaten",
381
+ "name": "Hulu Sungai Tengah",
382
+ "code": "07",
383
+ "full_code": "6307",
384
+ "province_id": 13
385
+ },
386
+ {
387
+ "id": 49,
388
+ "type": "Kabupaten",
389
+ "name": "Hulu Sungai Utara",
390
+ "code": "08",
391
+ "full_code": "6308",
392
+ "province_id": 13
393
+ },
394
+ {
395
+ "id": 50,
396
+ "type": "Kota",
397
+ "name": "Serang",
398
+ "code": "73",
399
+ "full_code": "3673",
400
+ "province_id": 3
401
+ },
402
+ {
403
+ "id": 51,
404
+ "type": "Kabupaten",
405
+ "name": "Serang",
406
+ "code": "04",
407
+ "full_code": "3604",
408
+ "province_id": 3
409
+ },
410
+ {
411
+ "id": 52,
412
+ "type": "Kabupaten",
413
+ "name": "Humbang Hasundutan",
414
+ "code": "16",
415
+ "full_code": "1216",
416
+ "province_id": 38
417
+ },
418
+ {
419
+ "id": 53,
420
+ "type": "Kota",
421
+ "name": "Balikpapan",
422
+ "code": "71",
423
+ "full_code": "6471",
424
+ "province_id": 15
425
+ },
426
+ {
427
+ "id": 54,
428
+ "type": "Kabupaten",
429
+ "name": "Indragiri Hilir",
430
+ "code": "04",
431
+ "full_code": "1404",
432
+ "province_id": 30
433
+ },
434
+ {
435
+ "id": 55,
436
+ "type": "Kabupaten",
437
+ "name": "Indragiri Hulu",
438
+ "code": "02",
439
+ "full_code": "1402",
440
+ "province_id": 30
441
+ },
442
+ {
443
+ "id": 56,
444
+ "type": "Kota",
445
+ "name": "Banda Aceh",
446
+ "code": "71",
447
+ "full_code": "1171",
448
+ "province_id": 1
449
+ },
450
+ {
451
+ "id": 57,
452
+ "type": "Kota",
453
+ "name": "Bandar Lampung",
454
+ "code": "71",
455
+ "full_code": "1871",
456
+ "province_id": 19
457
+ },
458
+ {
459
+ "id": 58,
460
+ "type": "Kota",
461
+ "name": "Bandung",
462
+ "code": "73",
463
+ "full_code": "3273",
464
+ "province_id": 9
465
+ },
466
+ {
467
+ "id": 59,
468
+ "type": "Kabupaten",
469
+ "name": "Bandung",
470
+ "code": "04",
471
+ "full_code": "3204",
472
+ "province_id": 9
473
+ },
474
+ {
475
+ "id": 60,
476
+ "type": "Kabupaten",
477
+ "name": "Bandung Barat",
478
+ "code": "17",
479
+ "full_code": "3217",
480
+ "province_id": 9
481
+ },
482
+ {
483
+ "id": 61,
484
+ "type": "Kabupaten",
485
+ "name": "Banggai",
486
+ "code": "01",
487
+ "full_code": "7201",
488
+ "province_id": 33
489
+ },
490
+ {
491
+ "id": 62,
492
+ "type": "Kabupaten",
493
+ "name": "Banggai Kepulauan",
494
+ "code": "07",
495
+ "full_code": "7207",
496
+ "province_id": 33
497
+ },
498
+ {
499
+ "id": 63,
500
+ "type": "Kabupaten",
501
+ "name": "Banggai Laut",
502
+ "code": "11",
503
+ "full_code": "7211",
504
+ "province_id": 33
505
+ },
506
+ {
507
+ "id": 64,
508
+ "type": "Kabupaten",
509
+ "name": "Bangka",
510
+ "code": "01",
511
+ "full_code": "1901",
512
+ "province_id": 17
513
+ },
514
+ {
515
+ "id": 65,
516
+ "type": "Kabupaten",
517
+ "name": "Bangka Barat",
518
+ "code": "05",
519
+ "full_code": "1905",
520
+ "province_id": 17
521
+ },
522
+ {
523
+ "id": 66,
524
+ "type": "Kabupaten",
525
+ "name": "Bangka Selatan",
526
+ "code": "03",
527
+ "full_code": "1903",
528
+ "province_id": 17
529
+ },
530
+ {
531
+ "id": 67,
532
+ "type": "Kabupaten",
533
+ "name": "Bangka Tengah",
534
+ "code": "04",
535
+ "full_code": "1904",
536
+ "province_id": 17
537
+ },
538
+ {
539
+ "id": 68,
540
+ "type": "Kabupaten",
541
+ "name": "Bangkalan",
542
+ "code": "26",
543
+ "full_code": "3526",
544
+ "province_id": 11
545
+ },
546
+ {
547
+ "id": 69,
548
+ "type": "Kabupaten",
549
+ "name": "Bangli",
550
+ "code": "06",
551
+ "full_code": "5106",
552
+ "province_id": 2
553
+ },
554
+ {
555
+ "id": 70,
556
+ "type": "Kota",
557
+ "name": "Banjar",
558
+ "code": "79",
559
+ "full_code": "3279",
560
+ "province_id": 9
561
+ },
562
+ {
563
+ "id": 71,
564
+ "type": "Kabupaten",
565
+ "name": "Banjar",
566
+ "code": "03",
567
+ "full_code": "6303",
568
+ "province_id": 13
569
+ },
570
+ {
571
+ "id": 72,
572
+ "type": "Kota",
573
+ "name": "Banjarbaru",
574
+ "code": "72",
575
+ "full_code": "6372",
576
+ "province_id": 13
577
+ },
578
+ {
579
+ "id": 73,
580
+ "type": "Kota",
581
+ "name": "Banjarmasin",
582
+ "code": "71",
583
+ "full_code": "6371",
584
+ "province_id": 13
585
+ },
586
+ {
587
+ "id": 74,
588
+ "type": "Kabupaten",
589
+ "name": "Banjarnegara",
590
+ "code": "04",
591
+ "full_code": "3304",
592
+ "province_id": 10
593
+ },
594
+ {
595
+ "id": 75,
596
+ "type": "Kabupaten",
597
+ "name": "Bantaeng",
598
+ "code": "03",
599
+ "full_code": "7303",
600
+ "province_id": 32
601
+ },
602
+ {
603
+ "id": 76,
604
+ "type": "Kabupaten",
605
+ "name": "Bantul",
606
+ "code": "02",
607
+ "full_code": "3402",
608
+ "province_id": 5
609
+ },
610
+ {
611
+ "id": 77,
612
+ "type": "Kabupaten",
613
+ "name": "Banyuasin",
614
+ "code": "07",
615
+ "full_code": "1607",
616
+ "province_id": 37
617
+ },
618
+ {
619
+ "id": 78,
620
+ "type": "Kabupaten",
621
+ "name": "Banyumas",
622
+ "code": "02",
623
+ "full_code": "3302",
624
+ "province_id": 10
625
+ },
626
+ {
627
+ "id": 79,
628
+ "type": "Kabupaten",
629
+ "name": "Banyuwangi",
630
+ "code": "10",
631
+ "full_code": "3510",
632
+ "province_id": 11
633
+ },
634
+ {
635
+ "id": 80,
636
+ "type": "Kabupaten",
637
+ "name": "Barito Kuala",
638
+ "code": "04",
639
+ "full_code": "6304",
640
+ "province_id": 13
641
+ },
642
+ {
643
+ "id": 81,
644
+ "type": "Kabupaten",
645
+ "name": "Barito Selatan",
646
+ "code": "04",
647
+ "full_code": "6204",
648
+ "province_id": 14
649
+ },
650
+ {
651
+ "id": 82,
652
+ "type": "Kabupaten",
653
+ "name": "Barito Timur",
654
+ "code": "13",
655
+ "full_code": "6213",
656
+ "province_id": 14
657
+ },
658
+ {
659
+ "id": 83,
660
+ "type": "Kabupaten",
661
+ "name": "Barito Utara",
662
+ "code": "05",
663
+ "full_code": "6205",
664
+ "province_id": 14
665
+ },
666
+ {
667
+ "id": 84,
668
+ "type": "Kabupaten",
669
+ "name": "Barru",
670
+ "code": "11",
671
+ "full_code": "7311",
672
+ "province_id": 32
673
+ },
674
+ {
675
+ "id": 85,
676
+ "type": "Kota",
677
+ "name": "Batam",
678
+ "code": "71",
679
+ "full_code": "2171",
680
+ "province_id": 18
681
+ },
682
+ {
683
+ "id": 86,
684
+ "type": "Kabupaten",
685
+ "name": "Batang",
686
+ "code": "25",
687
+ "full_code": "3325",
688
+ "province_id": 10
689
+ },
690
+ {
691
+ "id": 87,
692
+ "type": "Kabupaten",
693
+ "name": "Batanghari",
694
+ "code": "04",
695
+ "full_code": "1504",
696
+ "province_id": 8
697
+ },
698
+ {
699
+ "id": 88,
700
+ "type": "Kota",
701
+ "name": "Batu",
702
+ "code": "79",
703
+ "full_code": "3579",
704
+ "province_id": 11
705
+ },
706
+ {
707
+ "id": 89,
708
+ "type": "Kabupaten",
709
+ "name": "Batu Bara",
710
+ "code": "19",
711
+ "full_code": "1219",
712
+ "province_id": 38
713
+ },
714
+ {
715
+ "id": 90,
716
+ "type": "Kota",
717
+ "name": "Bau Bau",
718
+ "code": "72",
719
+ "full_code": "7472",
720
+ "province_id": 34
721
+ },
722
+ {
723
+ "id": 91,
724
+ "type": "Kota",
725
+ "name": "Bekasi",
726
+ "code": "75",
727
+ "full_code": "3275",
728
+ "province_id": 9
729
+ },
730
+ {
731
+ "id": 92,
732
+ "type": "Kabupaten",
733
+ "name": "Bekasi",
734
+ "code": "16",
735
+ "full_code": "3216",
736
+ "province_id": 9
737
+ },
738
+ {
739
+ "id": 93,
740
+ "type": "Kabupaten",
741
+ "name": "Belitung",
742
+ "code": "02",
743
+ "full_code": "1902",
744
+ "province_id": 17
745
+ },
746
+ {
747
+ "id": 94,
748
+ "type": "Kabupaten",
749
+ "name": "Belitung Timur",
750
+ "code": "06",
751
+ "full_code": "1906",
752
+ "province_id": 17
753
+ },
754
+ {
755
+ "id": 95,
756
+ "type": "Kabupaten",
757
+ "name": "Belu",
758
+ "code": "04",
759
+ "full_code": "5304",
760
+ "province_id": 23
761
+ },
762
+ {
763
+ "id": 96,
764
+ "type": "Kabupaten",
765
+ "name": "Bener Meriah",
766
+ "code": "17",
767
+ "full_code": "1117",
768
+ "province_id": 1
769
+ },
770
+ {
771
+ "id": 97,
772
+ "type": "Kabupaten",
773
+ "name": "Bengkalis",
774
+ "code": "03",
775
+ "full_code": "1403",
776
+ "province_id": 30
777
+ },
778
+ {
779
+ "id": 98,
780
+ "type": "Kabupaten",
781
+ "name": "Bengkayang",
782
+ "code": "07",
783
+ "full_code": "6107",
784
+ "province_id": 12
785
+ },
786
+ {
787
+ "id": 99,
788
+ "type": "Kabupaten",
789
+ "name": "Serdang Bedagai",
790
+ "code": "18",
791
+ "full_code": "1218",
792
+ "province_id": 38
793
+ },
794
+ {
795
+ "id": 100,
796
+ "type": "Kota",
797
+ "name": "Bengkulu",
798
+ "code": "71",
799
+ "full_code": "1771",
800
+ "province_id": 4
801
+ },
802
+ {
803
+ "id": 101,
804
+ "type": "Kabupaten",
805
+ "name": "Bengkulu Selatan",
806
+ "code": "01",
807
+ "full_code": "1701",
808
+ "province_id": 4
809
+ },
810
+ {
811
+ "id": 102,
812
+ "type": "Kabupaten",
813
+ "name": "Seruyan",
814
+ "code": "07",
815
+ "full_code": "6207",
816
+ "province_id": 14
817
+ },
818
+ {
819
+ "id": 103,
820
+ "type": "Kabupaten",
821
+ "name": "Indramayu",
822
+ "code": "12",
823
+ "full_code": "3212",
824
+ "province_id": 9
825
+ },
826
+ {
827
+ "id": 104,
828
+ "type": "Kabupaten",
829
+ "name": "Siak",
830
+ "code": "08",
831
+ "full_code": "1408",
832
+ "province_id": 30
833
+ },
834
+ {
835
+ "id": 105,
836
+ "type": "Kabupaten",
837
+ "name": "Intan Jaya",
838
+ "code": "07",
839
+ "full_code": "9407",
840
+ "province_id": 29
841
+ },
842
+ {
843
+ "id": 106,
844
+ "type": "Kota",
845
+ "name": "Jakarta Barat",
846
+ "code": "73",
847
+ "full_code": "3173",
848
+ "province_id": 6
849
+ },
850
+ {
851
+ "id": 107,
852
+ "type": "Kota",
853
+ "name": "Sibolga",
854
+ "code": "73",
855
+ "full_code": "1273",
856
+ "province_id": 38
857
+ },
858
+ {
859
+ "id": 108,
860
+ "type": "Kabupaten",
861
+ "name": "Bengkulu Tengah",
862
+ "code": "09",
863
+ "full_code": "1709",
864
+ "province_id": 4
865
+ },
866
+ {
867
+ "id": 109,
868
+ "type": "Kota",
869
+ "name": "Jakarta Pusat",
870
+ "code": "71",
871
+ "full_code": "3171",
872
+ "province_id": 6
873
+ },
874
+ {
875
+ "id": 110,
876
+ "type": "Kabupaten",
877
+ "name": "Sidenreng Rappang",
878
+ "code": "14",
879
+ "full_code": "7314",
880
+ "province_id": 32
881
+ },
882
+ {
883
+ "id": 111,
884
+ "type": "Kabupaten",
885
+ "name": "Bengkulu Utara",
886
+ "code": "03",
887
+ "full_code": "1703",
888
+ "province_id": 4
889
+ },
890
+ {
891
+ "id": 112,
892
+ "type": "Kota",
893
+ "name": "Jakarta Selatan",
894
+ "code": "74",
895
+ "full_code": "3174",
896
+ "province_id": 6
897
+ },
898
+ {
899
+ "id": 113,
900
+ "type": "Kabupaten",
901
+ "name": "Sidoarjo",
902
+ "code": "15",
903
+ "full_code": "3515",
904
+ "province_id": 11
905
+ },
906
+ {
907
+ "id": 114,
908
+ "type": "Kota",
909
+ "name": "Jakarta Timur",
910
+ "code": "75",
911
+ "full_code": "3175",
912
+ "province_id": 6
913
+ },
914
+ {
915
+ "id": 115,
916
+ "type": "Kabupaten",
917
+ "name": "Sigi",
918
+ "code": "10",
919
+ "full_code": "7210",
920
+ "province_id": 33
921
+ },
922
+ {
923
+ "id": 116,
924
+ "type": "Kabupaten",
925
+ "name": "Berau",
926
+ "code": "03",
927
+ "full_code": "6403",
928
+ "province_id": 15
929
+ },
930
+ {
931
+ "id": 117,
932
+ "type": "Kabupaten",
933
+ "name": "Biak Numfor",
934
+ "code": "06",
935
+ "full_code": "9106",
936
+ "province_id": 24
937
+ },
938
+ {
939
+ "id": 118,
940
+ "type": "Kabupaten",
941
+ "name": "Bima",
942
+ "code": "06",
943
+ "full_code": "5206",
944
+ "province_id": 22
945
+ },
946
+ {
947
+ "id": 119,
948
+ "type": "Kota",
949
+ "name": "Jakarta Utara",
950
+ "code": "72",
951
+ "full_code": "3172",
952
+ "province_id": 6
953
+ },
954
+ {
955
+ "id": 120,
956
+ "type": "Kota",
957
+ "name": "Bima",
958
+ "code": "72",
959
+ "full_code": "5272",
960
+ "province_id": 22
961
+ },
962
+ {
963
+ "id": 121,
964
+ "type": "Kota",
965
+ "name": "Jambi",
966
+ "code": "71",
967
+ "full_code": "1571",
968
+ "province_id": 8
969
+ },
970
+ {
971
+ "id": 122,
972
+ "type": "Kabupaten",
973
+ "name": "Sijunjung",
974
+ "code": "03",
975
+ "full_code": "1303",
976
+ "province_id": 36
977
+ },
978
+ {
979
+ "id": 123,
980
+ "type": "Kota",
981
+ "name": "Binjai",
982
+ "code": "75",
983
+ "full_code": "1275",
984
+ "province_id": 38
985
+ },
986
+ {
987
+ "id": 124,
988
+ "type": "Kabupaten",
989
+ "name": "Jayapura",
990
+ "code": "03",
991
+ "full_code": "9103",
992
+ "province_id": 24
993
+ },
994
+ {
995
+ "id": 125,
996
+ "type": "Kabupaten",
997
+ "name": "Bintan",
998
+ "code": "01",
999
+ "full_code": "2101",
1000
+ "province_id": 18
1001
+ },
1002
+ {
1003
+ "id": 126,
1004
+ "type": "Kabupaten",
1005
+ "name": "Sikka",
1006
+ "code": "07",
1007
+ "full_code": "5307",
1008
+ "province_id": 23
1009
+ },
1010
+ {
1011
+ "id": 127,
1012
+ "type": "Kabupaten",
1013
+ "name": "Bireuen",
1014
+ "code": "11",
1015
+ "full_code": "1111",
1016
+ "province_id": 1
1017
+ },
1018
+ {
1019
+ "id": 128,
1020
+ "type": "Kabupaten",
1021
+ "name": "Simalungun",
1022
+ "code": "08",
1023
+ "full_code": "1208",
1024
+ "province_id": 38
1025
+ },
1026
+ {
1027
+ "id": 129,
1028
+ "type": "Kota",
1029
+ "name": "Bitung",
1030
+ "code": "72",
1031
+ "full_code": "7172",
1032
+ "province_id": 35
1033
+ },
1034
+ {
1035
+ "id": 130,
1036
+ "type": "Kota",
1037
+ "name": "Jayapura",
1038
+ "code": "71",
1039
+ "full_code": "9171",
1040
+ "province_id": 24
1041
+ },
1042
+ {
1043
+ "id": 131,
1044
+ "type": "Kabupaten",
1045
+ "name": "Simeulue",
1046
+ "code": "09",
1047
+ "full_code": "1109",
1048
+ "province_id": 1
1049
+ },
1050
+ {
1051
+ "id": 132,
1052
+ "type": "Kabupaten",
1053
+ "name": "Jayawijaya",
1054
+ "code": "01",
1055
+ "full_code": "9501",
1056
+ "province_id": 27
1057
+ },
1058
+ {
1059
+ "id": 133,
1060
+ "type": "Kabupaten",
1061
+ "name": "Jember",
1062
+ "code": "09",
1063
+ "full_code": "3509",
1064
+ "province_id": 11
1065
+ },
1066
+ {
1067
+ "id": 134,
1068
+ "type": "Kota",
1069
+ "name": "Singkawang",
1070
+ "code": "72",
1071
+ "full_code": "6172",
1072
+ "province_id": 12
1073
+ },
1074
+ {
1075
+ "id": 135,
1076
+ "type": "Kabupaten",
1077
+ "name": "Blitar",
1078
+ "code": "05",
1079
+ "full_code": "3505",
1080
+ "province_id": 11
1081
+ },
1082
+ {
1083
+ "id": 136,
1084
+ "type": "Kabupaten",
1085
+ "name": "Jembrana",
1086
+ "code": "01",
1087
+ "full_code": "5101",
1088
+ "province_id": 2
1089
+ },
1090
+ {
1091
+ "id": 137,
1092
+ "type": "Kabupaten",
1093
+ "name": "Sinjai",
1094
+ "code": "07",
1095
+ "full_code": "7307",
1096
+ "province_id": 32
1097
+ },
1098
+ {
1099
+ "id": 138,
1100
+ "type": "Kota",
1101
+ "name": "Blitar",
1102
+ "code": "72",
1103
+ "full_code": "3572",
1104
+ "province_id": 11
1105
+ },
1106
+ {
1107
+ "id": 139,
1108
+ "type": "Kabupaten",
1109
+ "name": "Jeneponto",
1110
+ "code": "04",
1111
+ "full_code": "7304",
1112
+ "province_id": 32
1113
+ },
1114
+ {
1115
+ "id": 140,
1116
+ "type": "Kabupaten",
1117
+ "name": "Blora",
1118
+ "code": "16",
1119
+ "full_code": "3316",
1120
+ "province_id": 10
1121
+ },
1122
+ {
1123
+ "id": 141,
1124
+ "type": "Kabupaten",
1125
+ "name": "Jepara",
1126
+ "code": "20",
1127
+ "full_code": "3320",
1128
+ "province_id": 10
1129
+ },
1130
+ {
1131
+ "id": 142,
1132
+ "type": "Kabupaten",
1133
+ "name": "Sintang",
1134
+ "code": "05",
1135
+ "full_code": "6105",
1136
+ "province_id": 12
1137
+ },
1138
+ {
1139
+ "id": 143,
1140
+ "type": "Kabupaten",
1141
+ "name": "Boalemo",
1142
+ "code": "02",
1143
+ "full_code": "7502",
1144
+ "province_id": 7
1145
+ },
1146
+ {
1147
+ "id": 144,
1148
+ "type": "Kabupaten",
1149
+ "name": "Jombang",
1150
+ "code": "17",
1151
+ "full_code": "3517",
1152
+ "province_id": 11
1153
+ },
1154
+ {
1155
+ "id": 145,
1156
+ "type": "Kabupaten",
1157
+ "name": "Bogor",
1158
+ "code": "01",
1159
+ "full_code": "3201",
1160
+ "province_id": 9
1161
+ },
1162
+ {
1163
+ "id": 146,
1164
+ "type": "Kabupaten",
1165
+ "name": "Situbondo",
1166
+ "code": "12",
1167
+ "full_code": "3512",
1168
+ "province_id": 11
1169
+ },
1170
+ {
1171
+ "id": 147,
1172
+ "type": "Kabupaten",
1173
+ "name": "Kaimana",
1174
+ "code": "08",
1175
+ "full_code": "9208",
1176
+ "province_id": 25
1177
+ },
1178
+ {
1179
+ "id": 148,
1180
+ "type": "Kota",
1181
+ "name": "Bogor",
1182
+ "code": "71",
1183
+ "full_code": "3271",
1184
+ "province_id": 9
1185
+ },
1186
+ {
1187
+ "id": 149,
1188
+ "type": "Kabupaten",
1189
+ "name": "Sleman",
1190
+ "code": "04",
1191
+ "full_code": "3404",
1192
+ "province_id": 5
1193
+ },
1194
+ {
1195
+ "id": 150,
1196
+ "type": "Kabupaten",
1197
+ "name": "Kampar",
1198
+ "code": "01",
1199
+ "full_code": "1401",
1200
+ "province_id": 30
1201
+ },
1202
+ {
1203
+ "id": 151,
1204
+ "type": "Kabupaten",
1205
+ "name": "Bojonegoro",
1206
+ "code": "22",
1207
+ "full_code": "3522",
1208
+ "province_id": 11
1209
+ },
1210
+ {
1211
+ "id": 152,
1212
+ "type": "Kabupaten",
1213
+ "name": "Solok",
1214
+ "code": "02",
1215
+ "full_code": "1302",
1216
+ "province_id": 36
1217
+ },
1218
+ {
1219
+ "id": 153,
1220
+ "type": "Kabupaten",
1221
+ "name": "Bolaang Mongondow",
1222
+ "code": "01",
1223
+ "full_code": "7101",
1224
+ "province_id": 35
1225
+ },
1226
+ {
1227
+ "id": 154,
1228
+ "type": "Kabupaten",
1229
+ "name": "Bolaang Mongondow Selatan",
1230
+ "code": "11",
1231
+ "full_code": "7111",
1232
+ "province_id": 35
1233
+ },
1234
+ {
1235
+ "id": 155,
1236
+ "type": "Kota",
1237
+ "name": "Solok",
1238
+ "code": "72",
1239
+ "full_code": "1372",
1240
+ "province_id": 36
1241
+ },
1242
+ {
1243
+ "id": 156,
1244
+ "type": "Kabupaten",
1245
+ "name": "Kapuas",
1246
+ "code": "03",
1247
+ "full_code": "6203",
1248
+ "province_id": 14
1249
+ },
1250
+ {
1251
+ "id": 157,
1252
+ "type": "Kabupaten",
1253
+ "name": "Solok Selatan",
1254
+ "code": "11",
1255
+ "full_code": "1311",
1256
+ "province_id": 36
1257
+ },
1258
+ {
1259
+ "id": 158,
1260
+ "type": "Kabupaten",
1261
+ "name": "Bolaang Mongondow Timur",
1262
+ "code": "10",
1263
+ "full_code": "7110",
1264
+ "province_id": 35
1265
+ },
1266
+ {
1267
+ "id": 159,
1268
+ "type": "Kabupaten",
1269
+ "name": "Kapuas Hulu",
1270
+ "code": "06",
1271
+ "full_code": "6106",
1272
+ "province_id": 12
1273
+ },
1274
+ {
1275
+ "id": 160,
1276
+ "type": "Kabupaten",
1277
+ "name": "Karanganyar",
1278
+ "code": "13",
1279
+ "full_code": "3313",
1280
+ "province_id": 10
1281
+ },
1282
+ {
1283
+ "id": 161,
1284
+ "type": "Kabupaten",
1285
+ "name": "Karangasem",
1286
+ "code": "07",
1287
+ "full_code": "5107",
1288
+ "province_id": 2
1289
+ },
1290
+ {
1291
+ "id": 162,
1292
+ "type": "Kabupaten",
1293
+ "name": "Bolaang Mongondow Utara",
1294
+ "code": "08",
1295
+ "full_code": "7108",
1296
+ "province_id": 35
1297
+ },
1298
+ {
1299
+ "id": 163,
1300
+ "type": "Kabupaten",
1301
+ "name": "Karawang",
1302
+ "code": "15",
1303
+ "full_code": "3215",
1304
+ "province_id": 9
1305
+ },
1306
+ {
1307
+ "id": 164,
1308
+ "type": "Kabupaten",
1309
+ "name": "Bombana",
1310
+ "code": "06",
1311
+ "full_code": "7406",
1312
+ "province_id": 34
1313
+ },
1314
+ {
1315
+ "id": 165,
1316
+ "type": "Kabupaten",
1317
+ "name": "Karimun",
1318
+ "code": "02",
1319
+ "full_code": "2102",
1320
+ "province_id": 18
1321
+ },
1322
+ {
1323
+ "id": 166,
1324
+ "type": "Kabupaten",
1325
+ "name": "Bondowoso",
1326
+ "code": "11",
1327
+ "full_code": "3511",
1328
+ "province_id": 11
1329
+ },
1330
+ {
1331
+ "id": 167,
1332
+ "type": "Kabupaten",
1333
+ "name": "Karo",
1334
+ "code": "06",
1335
+ "full_code": "1206",
1336
+ "province_id": 38
1337
+ },
1338
+ {
1339
+ "id": 168,
1340
+ "type": "Kabupaten",
1341
+ "name": "Soppeng",
1342
+ "code": "12",
1343
+ "full_code": "7312",
1344
+ "province_id": 32
1345
+ },
1346
+ {
1347
+ "id": 169,
1348
+ "type": "Kabupaten",
1349
+ "name": "Katingan",
1350
+ "code": "06",
1351
+ "full_code": "6206",
1352
+ "province_id": 14
1353
+ },
1354
+ {
1355
+ "id": 170,
1356
+ "type": "Kabupaten",
1357
+ "name": "Kaur",
1358
+ "code": "04",
1359
+ "full_code": "1704",
1360
+ "province_id": 4
1361
+ },
1362
+ {
1363
+ "id": 171,
1364
+ "type": "Kabupaten",
1365
+ "name": "Sorong",
1366
+ "code": "01",
1367
+ "full_code": "9201",
1368
+ "province_id": 26
1369
+ },
1370
+ {
1371
+ "id": 172,
1372
+ "type": "Kabupaten",
1373
+ "name": "Kayong Utara",
1374
+ "code": "11",
1375
+ "full_code": "6111",
1376
+ "province_id": 12
1377
+ },
1378
+ {
1379
+ "id": 173,
1380
+ "type": "Kota",
1381
+ "name": "Sorong",
1382
+ "code": "71",
1383
+ "full_code": "9271",
1384
+ "province_id": 26
1385
+ },
1386
+ {
1387
+ "id": 174,
1388
+ "type": "Kabupaten",
1389
+ "name": "Sorong Selatan",
1390
+ "code": "04",
1391
+ "full_code": "9204",
1392
+ "province_id": 26
1393
+ },
1394
+ {
1395
+ "id": 175,
1396
+ "type": "Kabupaten",
1397
+ "name": "Kebumen",
1398
+ "code": "05",
1399
+ "full_code": "3305",
1400
+ "province_id": 10
1401
+ },
1402
+ {
1403
+ "id": 176,
1404
+ "type": "Kabupaten",
1405
+ "name": "Sragen",
1406
+ "code": "14",
1407
+ "full_code": "3314",
1408
+ "province_id": 10
1409
+ },
1410
+ {
1411
+ "id": 177,
1412
+ "type": "Kabupaten",
1413
+ "name": "Kediri",
1414
+ "code": "06",
1415
+ "full_code": "3506",
1416
+ "province_id": 11
1417
+ },
1418
+ {
1419
+ "id": 178,
1420
+ "type": "Kabupaten",
1421
+ "name": "Subang",
1422
+ "code": "13",
1423
+ "full_code": "3213",
1424
+ "province_id": 9
1425
+ },
1426
+ {
1427
+ "id": 179,
1428
+ "type": "Kota",
1429
+ "name": "Subulussalam",
1430
+ "code": "75",
1431
+ "full_code": "1175",
1432
+ "province_id": 1
1433
+ },
1434
+ {
1435
+ "id": 180,
1436
+ "type": "Kota",
1437
+ "name": "Sukabumi",
1438
+ "code": "72",
1439
+ "full_code": "3272",
1440
+ "province_id": 9
1441
+ },
1442
+ {
1443
+ "id": 181,
1444
+ "type": "Kota",
1445
+ "name": "Kediri",
1446
+ "code": "71",
1447
+ "full_code": "3571",
1448
+ "province_id": 11
1449
+ },
1450
+ {
1451
+ "id": 182,
1452
+ "type": "Kabupaten",
1453
+ "name": "Bone",
1454
+ "code": "08",
1455
+ "full_code": "7308",
1456
+ "province_id": 32
1457
+ },
1458
+ {
1459
+ "id": 183,
1460
+ "type": "Kabupaten",
1461
+ "name": "Keerom",
1462
+ "code": "11",
1463
+ "full_code": "9111",
1464
+ "province_id": 24
1465
+ },
1466
+ {
1467
+ "id": 184,
1468
+ "type": "Kabupaten",
1469
+ "name": "Sukabumi",
1470
+ "code": "02",
1471
+ "full_code": "3202",
1472
+ "province_id": 9
1473
+ },
1474
+ {
1475
+ "id": 185,
1476
+ "type": "Kabupaten",
1477
+ "name": "Bone Bolango",
1478
+ "code": "03",
1479
+ "full_code": "7503",
1480
+ "province_id": 7
1481
+ },
1482
+ {
1483
+ "id": 186,
1484
+ "type": "Kabupaten",
1485
+ "name": "Kendal",
1486
+ "code": "24",
1487
+ "full_code": "3324",
1488
+ "province_id": 10
1489
+ },
1490
+ {
1491
+ "id": 187,
1492
+ "type": "Kota",
1493
+ "name": "Bontang",
1494
+ "code": "74",
1495
+ "full_code": "6474",
1496
+ "province_id": 15
1497
+ },
1498
+ {
1499
+ "id": 188,
1500
+ "type": "Kota",
1501
+ "name": "Kendari",
1502
+ "code": "71",
1503
+ "full_code": "7471",
1504
+ "province_id": 34
1505
+ },
1506
+ {
1507
+ "id": 189,
1508
+ "type": "Kabupaten",
1509
+ "name": "Boven Digoel",
1510
+ "code": "02",
1511
+ "full_code": "9302",
1512
+ "province_id": 28
1513
+ },
1514
+ {
1515
+ "id": 190,
1516
+ "type": "Kabupaten",
1517
+ "name": "Kepahiang",
1518
+ "code": "08",
1519
+ "full_code": "1708",
1520
+ "province_id": 4
1521
+ },
1522
+ {
1523
+ "id": 191,
1524
+ "type": "Kabupaten",
1525
+ "name": "Boyolali",
1526
+ "code": "09",
1527
+ "full_code": "3309",
1528
+ "province_id": 10
1529
+ },
1530
+ {
1531
+ "id": 192,
1532
+ "type": "Kabupaten",
1533
+ "name": "Sukamara",
1534
+ "code": "08",
1535
+ "full_code": "6208",
1536
+ "province_id": 14
1537
+ },
1538
+ {
1539
+ "id": 193,
1540
+ "type": "Kabupaten",
1541
+ "name": "Kepulauan Anambas",
1542
+ "code": "05",
1543
+ "full_code": "2105",
1544
+ "province_id": 18
1545
+ },
1546
+ {
1547
+ "id": 194,
1548
+ "type": "Kabupaten",
1549
+ "name": "Sukoharjo",
1550
+ "code": "11",
1551
+ "full_code": "3311",
1552
+ "province_id": 10
1553
+ },
1554
+ {
1555
+ "id": 195,
1556
+ "type": "Kabupaten",
1557
+ "name": "Sumba Barat",
1558
+ "code": "12",
1559
+ "full_code": "5312",
1560
+ "province_id": 23
1561
+ },
1562
+ {
1563
+ "id": 196,
1564
+ "type": "Kabupaten",
1565
+ "name": "Brebes",
1566
+ "code": "29",
1567
+ "full_code": "3329",
1568
+ "province_id": 10
1569
+ },
1570
+ {
1571
+ "id": 197,
1572
+ "type": "Kota",
1573
+ "name": "Bukittinggi",
1574
+ "code": "75",
1575
+ "full_code": "1375",
1576
+ "province_id": 36
1577
+ },
1578
+ {
1579
+ "id": 198,
1580
+ "type": "Kabupaten",
1581
+ "name": "Buleleng",
1582
+ "code": "08",
1583
+ "full_code": "5108",
1584
+ "province_id": 2
1585
+ },
1586
+ {
1587
+ "id": 199,
1588
+ "type": "Kabupaten",
1589
+ "name": "Bulukumba",
1590
+ "code": "02",
1591
+ "full_code": "7302",
1592
+ "province_id": 32
1593
+ },
1594
+ {
1595
+ "id": 200,
1596
+ "type": "Kabupaten",
1597
+ "name": "Sumba Barat Daya",
1598
+ "code": "18",
1599
+ "full_code": "5318",
1600
+ "province_id": 23
1601
+ },
1602
+ {
1603
+ "id": 201,
1604
+ "type": "Kabupaten",
1605
+ "name": "Sumba Tengah",
1606
+ "code": "17",
1607
+ "full_code": "5317",
1608
+ "province_id": 23
1609
+ },
1610
+ {
1611
+ "id": 202,
1612
+ "type": "Kabupaten",
1613
+ "name": "Sumba Timur",
1614
+ "code": "11",
1615
+ "full_code": "5311",
1616
+ "province_id": 23
1617
+ },
1618
+ {
1619
+ "id": 203,
1620
+ "type": "Kabupaten",
1621
+ "name": "Bulungan",
1622
+ "code": "01",
1623
+ "full_code": "6501",
1624
+ "province_id": 16
1625
+ },
1626
+ {
1627
+ "id": 204,
1628
+ "type": "Kabupaten",
1629
+ "name": "Sumbawa",
1630
+ "code": "04",
1631
+ "full_code": "5204",
1632
+ "province_id": 22
1633
+ },
1634
+ {
1635
+ "id": 205,
1636
+ "type": "Kabupaten",
1637
+ "name": "Bungo",
1638
+ "code": "08",
1639
+ "full_code": "1508",
1640
+ "province_id": 8
1641
+ },
1642
+ {
1643
+ "id": 206,
1644
+ "type": "Kabupaten",
1645
+ "name": "Sumbawa Barat",
1646
+ "code": "07",
1647
+ "full_code": "5207",
1648
+ "province_id": 22
1649
+ },
1650
+ {
1651
+ "id": 207,
1652
+ "type": "Kabupaten",
1653
+ "name": "Buol",
1654
+ "code": "05",
1655
+ "full_code": "7205",
1656
+ "province_id": 33
1657
+ },
1658
+ {
1659
+ "id": 208,
1660
+ "type": "Kabupaten",
1661
+ "name": "Sumedang",
1662
+ "code": "11",
1663
+ "full_code": "3211",
1664
+ "province_id": 9
1665
+ },
1666
+ {
1667
+ "id": 209,
1668
+ "type": "Kabupaten",
1669
+ "name": "Buru",
1670
+ "code": "04",
1671
+ "full_code": "8104",
1672
+ "province_id": 20
1673
+ },
1674
+ {
1675
+ "id": 210,
1676
+ "type": "Kabupaten",
1677
+ "name": "Buru Selatan",
1678
+ "code": "09",
1679
+ "full_code": "8109",
1680
+ "province_id": 20
1681
+ },
1682
+ {
1683
+ "id": 211,
1684
+ "type": "Kabupaten",
1685
+ "name": "Kepulauan Aru",
1686
+ "code": "07",
1687
+ "full_code": "8107",
1688
+ "province_id": 20
1689
+ },
1690
+ {
1691
+ "id": 212,
1692
+ "type": "Kabupaten",
1693
+ "name": "Sumenep",
1694
+ "code": "29",
1695
+ "full_code": "3529",
1696
+ "province_id": 11
1697
+ },
1698
+ {
1699
+ "id": 213,
1700
+ "type": "Kabupaten",
1701
+ "name": "Buton",
1702
+ "code": "04",
1703
+ "full_code": "7404",
1704
+ "province_id": 34
1705
+ },
1706
+ {
1707
+ "id": 214,
1708
+ "type": "Kota",
1709
+ "name": "Sungai Penuh",
1710
+ "code": "72",
1711
+ "full_code": "1572",
1712
+ "province_id": 8
1713
+ },
1714
+ {
1715
+ "id": 215,
1716
+ "type": "Kabupaten",
1717
+ "name": "Buton Selatan",
1718
+ "code": "15",
1719
+ "full_code": "7415",
1720
+ "province_id": 34
1721
+ },
1722
+ {
1723
+ "id": 216,
1724
+ "type": "Kabupaten",
1725
+ "name": "Supiori",
1726
+ "code": "19",
1727
+ "full_code": "9119",
1728
+ "province_id": 24
1729
+ },
1730
+ {
1731
+ "id": 217,
1732
+ "type": "Kabupaten",
1733
+ "name": "Buton Tengah",
1734
+ "code": "14",
1735
+ "full_code": "7414",
1736
+ "province_id": 34
1737
+ },
1738
+ {
1739
+ "id": 218,
1740
+ "type": "Kabupaten",
1741
+ "name": "Buton Utara",
1742
+ "code": "10",
1743
+ "full_code": "7410",
1744
+ "province_id": 34
1745
+ },
1746
+ {
1747
+ "id": 219,
1748
+ "type": "Kota",
1749
+ "name": "Surabaya",
1750
+ "code": "78",
1751
+ "full_code": "3578",
1752
+ "province_id": 11
1753
+ },
1754
+ {
1755
+ "id": 220,
1756
+ "type": "Kabupaten",
1757
+ "name": "Kepulauan Mentawai",
1758
+ "code": "09",
1759
+ "full_code": "1309",
1760
+ "province_id": 36
1761
+ },
1762
+ {
1763
+ "id": 221,
1764
+ "type": "Kabupaten",
1765
+ "name": "Ciamis",
1766
+ "code": "07",
1767
+ "full_code": "3207",
1768
+ "province_id": 9
1769
+ },
1770
+ {
1771
+ "id": 222,
1772
+ "type": "Kota",
1773
+ "name": "Surakarta",
1774
+ "code": "72",
1775
+ "full_code": "3372",
1776
+ "province_id": 10
1777
+ },
1778
+ {
1779
+ "id": 223,
1780
+ "type": "Kabupaten",
1781
+ "name": "Kepulauan Meranti",
1782
+ "code": "10",
1783
+ "full_code": "1410",
1784
+ "province_id": 30
1785
+ },
1786
+ {
1787
+ "id": 224,
1788
+ "type": "Kabupaten",
1789
+ "name": "Cianjur",
1790
+ "code": "03",
1791
+ "full_code": "3203",
1792
+ "province_id": 9
1793
+ },
1794
+ {
1795
+ "id": 225,
1796
+ "type": "Kabupaten",
1797
+ "name": "Tabalong",
1798
+ "code": "09",
1799
+ "full_code": "6309",
1800
+ "province_id": 13
1801
+ },
1802
+ {
1803
+ "id": 226,
1804
+ "type": "Kabupaten",
1805
+ "name": "Kepulauan Sangihe",
1806
+ "code": "03",
1807
+ "full_code": "7103",
1808
+ "province_id": 35
1809
+ },
1810
+ {
1811
+ "id": 227,
1812
+ "type": "Kabupaten",
1813
+ "name": "Kepulauan Selayar",
1814
+ "code": "01",
1815
+ "full_code": "7301",
1816
+ "province_id": 32
1817
+ },
1818
+ {
1819
+ "id": 228,
1820
+ "type": "Kabupaten",
1821
+ "name": "Cilacap",
1822
+ "code": "01",
1823
+ "full_code": "3301",
1824
+ "province_id": 10
1825
+ },
1826
+ {
1827
+ "id": 229,
1828
+ "type": "Kota",
1829
+ "name": "Cilegon",
1830
+ "code": "72",
1831
+ "full_code": "3672",
1832
+ "province_id": 3
1833
+ },
1834
+ {
1835
+ "id": 230,
1836
+ "type": "Kabupaten",
1837
+ "name": "Tabanan",
1838
+ "code": "02",
1839
+ "full_code": "5102",
1840
+ "province_id": 2
1841
+ },
1842
+ {
1843
+ "id": 231,
1844
+ "type": "Kota",
1845
+ "name": "Cimahi",
1846
+ "code": "77",
1847
+ "full_code": "3277",
1848
+ "province_id": 9
1849
+ },
1850
+ {
1851
+ "id": 232,
1852
+ "type": "Kabupaten",
1853
+ "name": "Kepulauan Seribu",
1854
+ "code": "01",
1855
+ "full_code": "3101",
1856
+ "province_id": 6
1857
+ },
1858
+ {
1859
+ "id": 233,
1860
+ "type": "Kabupaten",
1861
+ "name": "Takalar",
1862
+ "code": "05",
1863
+ "full_code": "7305",
1864
+ "province_id": 32
1865
+ },
1866
+ {
1867
+ "id": 234,
1868
+ "type": "Kota",
1869
+ "name": "Cirebon",
1870
+ "code": "74",
1871
+ "full_code": "3274",
1872
+ "province_id": 9
1873
+ },
1874
+ {
1875
+ "id": 235,
1876
+ "type": "Kabupaten",
1877
+ "name": "Kepulauan Siau Tagulandang Biaro (Sitaro)",
1878
+ "code": "09",
1879
+ "full_code": "7109",
1880
+ "province_id": 35
1881
+ },
1882
+ {
1883
+ "id": 236,
1884
+ "type": "Kabupaten",
1885
+ "name": "Kepulauan Sula",
1886
+ "code": "05",
1887
+ "full_code": "8205",
1888
+ "province_id": 21
1889
+ },
1890
+ {
1891
+ "id": 237,
1892
+ "type": "Kabupaten",
1893
+ "name": "Tambrauw",
1894
+ "code": "09",
1895
+ "full_code": "9209",
1896
+ "province_id": 26
1897
+ },
1898
+ {
1899
+ "id": 238,
1900
+ "type": "Kabupaten",
1901
+ "name": "Kepulauan Talaud",
1902
+ "code": "04",
1903
+ "full_code": "7104",
1904
+ "province_id": 35
1905
+ },
1906
+ {
1907
+ "id": 239,
1908
+ "type": "Kabupaten",
1909
+ "name": "Kepulauan Tanimbar (Maluku Tenggara Barat)",
1910
+ "code": "03",
1911
+ "full_code": "8103",
1912
+ "province_id": 20
1913
+ },
1914
+ {
1915
+ "id": 240,
1916
+ "type": "Kabupaten",
1917
+ "name": "Cirebon",
1918
+ "code": "09",
1919
+ "full_code": "3209",
1920
+ "province_id": 9
1921
+ },
1922
+ {
1923
+ "id": 241,
1924
+ "type": "Kabupaten",
1925
+ "name": "Dairi",
1926
+ "code": "11",
1927
+ "full_code": "1211",
1928
+ "province_id": 38
1929
+ },
1930
+ {
1931
+ "id": 242,
1932
+ "type": "Kabupaten",
1933
+ "name": "Tana Tidung",
1934
+ "code": "04",
1935
+ "full_code": "6504",
1936
+ "province_id": 16
1937
+ },
1938
+ {
1939
+ "id": 243,
1940
+ "type": "Kabupaten",
1941
+ "name": "Deiyai",
1942
+ "code": "08",
1943
+ "full_code": "9408",
1944
+ "province_id": 29
1945
+ },
1946
+ {
1947
+ "id": 244,
1948
+ "type": "Kabupaten",
1949
+ "name": "Kepulauan Yapen",
1950
+ "code": "05",
1951
+ "full_code": "9105",
1952
+ "province_id": 24
1953
+ },
1954
+ {
1955
+ "id": 245,
1956
+ "type": "Kabupaten",
1957
+ "name": "Deli Serdang",
1958
+ "code": "07",
1959
+ "full_code": "1207",
1960
+ "province_id": 38
1961
+ },
1962
+ {
1963
+ "id": 246,
1964
+ "type": "Kabupaten",
1965
+ "name": "Tana Toraja",
1966
+ "code": "18",
1967
+ "full_code": "7318",
1968
+ "province_id": 32
1969
+ },
1970
+ {
1971
+ "id": 247,
1972
+ "type": "Kabupaten",
1973
+ "name": "Demak",
1974
+ "code": "21",
1975
+ "full_code": "3321",
1976
+ "province_id": 10
1977
+ },
1978
+ {
1979
+ "id": 248,
1980
+ "type": "Kabupaten",
1981
+ "name": "Kerinci",
1982
+ "code": "01",
1983
+ "full_code": "1501",
1984
+ "province_id": 8
1985
+ },
1986
+ {
1987
+ "id": 249,
1988
+ "type": "Kabupaten",
1989
+ "name": "Tanah Bumbu",
1990
+ "code": "10",
1991
+ "full_code": "6310",
1992
+ "province_id": 13
1993
+ },
1994
+ {
1995
+ "id": 250,
1996
+ "type": "Kota",
1997
+ "name": "Denpasar",
1998
+ "code": "71",
1999
+ "full_code": "5171",
2000
+ "province_id": 2
2001
+ },
2002
+ {
2003
+ "id": 251,
2004
+ "type": "Kabupaten",
2005
+ "name": "Ketapang",
2006
+ "code": "04",
2007
+ "full_code": "6104",
2008
+ "province_id": 12
2009
+ },
2010
+ {
2011
+ "id": 252,
2012
+ "type": "Kota",
2013
+ "name": "Depok",
2014
+ "code": "76",
2015
+ "full_code": "3276",
2016
+ "province_id": 9
2017
+ },
2018
+ {
2019
+ "id": 253,
2020
+ "type": "Kabupaten",
2021
+ "name": "Tanah Datar",
2022
+ "code": "04",
2023
+ "full_code": "1304",
2024
+ "province_id": 36
2025
+ },
2026
+ {
2027
+ "id": 254,
2028
+ "type": "Kabupaten",
2029
+ "name": "Klaten",
2030
+ "code": "10",
2031
+ "full_code": "3310",
2032
+ "province_id": 10
2033
+ },
2034
+ {
2035
+ "id": 255,
2036
+ "type": "Kabupaten",
2037
+ "name": "Dharmasraya",
2038
+ "code": "10",
2039
+ "full_code": "1310",
2040
+ "province_id": 36
2041
+ },
2042
+ {
2043
+ "id": 256,
2044
+ "type": "Kabupaten",
2045
+ "name": "Tanah Laut",
2046
+ "code": "01",
2047
+ "full_code": "6301",
2048
+ "province_id": 13
2049
+ },
2050
+ {
2051
+ "id": 257,
2052
+ "type": "Kabupaten",
2053
+ "name": "Dogiyai",
2054
+ "code": "06",
2055
+ "full_code": "9406",
2056
+ "province_id": 29
2057
+ },
2058
+ {
2059
+ "id": 258,
2060
+ "type": "Kabupaten",
2061
+ "name": "Klungkung",
2062
+ "code": "05",
2063
+ "full_code": "5105",
2064
+ "province_id": 2
2065
+ },
2066
+ {
2067
+ "id": 259,
2068
+ "type": "Kabupaten",
2069
+ "name": "Kolaka",
2070
+ "code": "01",
2071
+ "full_code": "7401",
2072
+ "province_id": 34
2073
+ },
2074
+ {
2075
+ "id": 260,
2076
+ "type": "Kota",
2077
+ "name": "Tangerang",
2078
+ "code": "71",
2079
+ "full_code": "3671",
2080
+ "province_id": 3
2081
+ },
2082
+ {
2083
+ "id": 261,
2084
+ "type": "Kabupaten",
2085
+ "name": "Kolaka Timur",
2086
+ "code": "11",
2087
+ "full_code": "7411",
2088
+ "province_id": 34
2089
+ },
2090
+ {
2091
+ "id": 262,
2092
+ "type": "Kabupaten",
2093
+ "name": "Tangerang",
2094
+ "code": "03",
2095
+ "full_code": "3603",
2096
+ "province_id": 3
2097
+ },
2098
+ {
2099
+ "id": 263,
2100
+ "type": "Kota",
2101
+ "name": "Tangerang Selatan",
2102
+ "code": "74",
2103
+ "full_code": "3674",
2104
+ "province_id": 3
2105
+ },
2106
+ {
2107
+ "id": 264,
2108
+ "type": "Kabupaten",
2109
+ "name": "Tanggamus",
2110
+ "code": "06",
2111
+ "full_code": "1806",
2112
+ "province_id": 19
2113
+ },
2114
+ {
2115
+ "id": 265,
2116
+ "type": "Kabupaten",
2117
+ "name": "Kolaka Utara",
2118
+ "code": "08",
2119
+ "full_code": "7408",
2120
+ "province_id": 34
2121
+ },
2122
+ {
2123
+ "id": 266,
2124
+ "type": "Kabupaten",
2125
+ "name": "Konawe",
2126
+ "code": "02",
2127
+ "full_code": "7402",
2128
+ "province_id": 34
2129
+ },
2130
+ {
2131
+ "id": 267,
2132
+ "type": "Kabupaten",
2133
+ "name": "Konawe Kepulauan",
2134
+ "code": "12",
2135
+ "full_code": "7412",
2136
+ "province_id": 34
2137
+ },
2138
+ {
2139
+ "id": 268,
2140
+ "type": "Kota",
2141
+ "name": "Tanjung Balai",
2142
+ "code": "74",
2143
+ "full_code": "1274",
2144
+ "province_id": 38
2145
+ },
2146
+ {
2147
+ "id": 269,
2148
+ "type": "Kabupaten",
2149
+ "name": "Konawe Selatan",
2150
+ "code": "05",
2151
+ "full_code": "7405",
2152
+ "province_id": 34
2153
+ },
2154
+ {
2155
+ "id": 270,
2156
+ "type": "Kabupaten",
2157
+ "name": "Konawe Utara",
2158
+ "code": "09",
2159
+ "full_code": "7409",
2160
+ "province_id": 34
2161
+ },
2162
+ {
2163
+ "id": 271,
2164
+ "type": "Kabupaten",
2165
+ "name": "Kotabaru",
2166
+ "code": "02",
2167
+ "full_code": "6302",
2168
+ "province_id": 13
2169
+ },
2170
+ {
2171
+ "id": 272,
2172
+ "type": "Kabupaten",
2173
+ "name": "Tanjung Jabung Barat",
2174
+ "code": "06",
2175
+ "full_code": "1506",
2176
+ "province_id": 8
2177
+ },
2178
+ {
2179
+ "id": 273,
2180
+ "type": "Kota",
2181
+ "name": "Kotamobagu",
2182
+ "code": "74",
2183
+ "full_code": "7174",
2184
+ "province_id": 35
2185
+ },
2186
+ {
2187
+ "id": 274,
2188
+ "type": "Kabupaten",
2189
+ "name": "Tanjung Jabung Timur",
2190
+ "code": "07",
2191
+ "full_code": "1507",
2192
+ "province_id": 8
2193
+ },
2194
+ {
2195
+ "id": 275,
2196
+ "type": "Kabupaten",
2197
+ "name": "Kotawaringin Barat",
2198
+ "code": "01",
2199
+ "full_code": "6201",
2200
+ "province_id": 14
2201
+ },
2202
+ {
2203
+ "id": 276,
2204
+ "type": "Kabupaten",
2205
+ "name": "Kotawaringin Timur",
2206
+ "code": "02",
2207
+ "full_code": "6202",
2208
+ "province_id": 14
2209
+ },
2210
+ {
2211
+ "id": 277,
2212
+ "type": "Kota",
2213
+ "name": "Tanjung Pinang",
2214
+ "code": "72",
2215
+ "full_code": "2172",
2216
+ "province_id": 18
2217
+ },
2218
+ {
2219
+ "id": 278,
2220
+ "type": "Kabupaten",
2221
+ "name": "Kuantan Singingi",
2222
+ "code": "09",
2223
+ "full_code": "1409",
2224
+ "province_id": 30
2225
+ },
2226
+ {
2227
+ "id": 279,
2228
+ "type": "Kabupaten",
2229
+ "name": "Tapanuli Selatan",
2230
+ "code": "03",
2231
+ "full_code": "1203",
2232
+ "province_id": 38
2233
+ },
2234
+ {
2235
+ "id": 280,
2236
+ "type": "Kabupaten",
2237
+ "name": "Kubu Raya",
2238
+ "code": "12",
2239
+ "full_code": "6112",
2240
+ "province_id": 12
2241
+ },
2242
+ {
2243
+ "id": 281,
2244
+ "type": "Kabupaten",
2245
+ "name": "Kudus",
2246
+ "code": "19",
2247
+ "full_code": "3319",
2248
+ "province_id": 10
2249
+ },
2250
+ {
2251
+ "id": 282,
2252
+ "type": "Kabupaten",
2253
+ "name": "Tapanuli Tengah",
2254
+ "code": "01",
2255
+ "full_code": "1201",
2256
+ "province_id": 38
2257
+ },
2258
+ {
2259
+ "id": 283,
2260
+ "type": "Kabupaten",
2261
+ "name": "Tapanuli Utara",
2262
+ "code": "02",
2263
+ "full_code": "1202",
2264
+ "province_id": 38
2265
+ },
2266
+ {
2267
+ "id": 284,
2268
+ "type": "Kabupaten",
2269
+ "name": "Tapin",
2270
+ "code": "05",
2271
+ "full_code": "6305",
2272
+ "province_id": 13
2273
+ },
2274
+ {
2275
+ "id": 285,
2276
+ "type": "Kota",
2277
+ "name": "Tarakan",
2278
+ "code": "71",
2279
+ "full_code": "6571",
2280
+ "province_id": 16
2281
+ },
2282
+ {
2283
+ "id": 286,
2284
+ "type": "Kota",
2285
+ "name": "Tasikmalaya",
2286
+ "code": "78",
2287
+ "full_code": "3278",
2288
+ "province_id": 9
2289
+ },
2290
+ {
2291
+ "id": 287,
2292
+ "type": "Kabupaten",
2293
+ "name": "Kulon Progo",
2294
+ "code": "01",
2295
+ "full_code": "3401",
2296
+ "province_id": 5
2297
+ },
2298
+ {
2299
+ "id": 288,
2300
+ "type": "Kabupaten",
2301
+ "name": "Kuningan",
2302
+ "code": "08",
2303
+ "full_code": "3208",
2304
+ "province_id": 9
2305
+ },
2306
+ {
2307
+ "id": 289,
2308
+ "type": "Kabupaten",
2309
+ "name": "Kupang",
2310
+ "code": "01",
2311
+ "full_code": "5301",
2312
+ "province_id": 23
2313
+ },
2314
+ {
2315
+ "id": 290,
2316
+ "type": "Kota",
2317
+ "name": "Kupang",
2318
+ "code": "71",
2319
+ "full_code": "5371",
2320
+ "province_id": 23
2321
+ },
2322
+ {
2323
+ "id": 291,
2324
+ "type": "Kabupaten",
2325
+ "name": "Tasikmalaya",
2326
+ "code": "06",
2327
+ "full_code": "3206",
2328
+ "province_id": 9
2329
+ },
2330
+ {
2331
+ "id": 292,
2332
+ "type": "Kabupaten",
2333
+ "name": "Kutai Barat",
2334
+ "code": "07",
2335
+ "full_code": "6407",
2336
+ "province_id": 15
2337
+ },
2338
+ {
2339
+ "id": 293,
2340
+ "type": "Kabupaten",
2341
+ "name": "Kutai Kartanegara",
2342
+ "code": "02",
2343
+ "full_code": "6402",
2344
+ "province_id": 15
2345
+ },
2346
+ {
2347
+ "id": 294,
2348
+ "type": "Kabupaten",
2349
+ "name": "Kutai Timur",
2350
+ "code": "08",
2351
+ "full_code": "6408",
2352
+ "province_id": 15
2353
+ },
2354
+ {
2355
+ "id": 295,
2356
+ "type": "Kabupaten",
2357
+ "name": "Labuhanbatu",
2358
+ "code": "10",
2359
+ "full_code": "1210",
2360
+ "province_id": 38
2361
+ },
2362
+ {
2363
+ "id": 296,
2364
+ "type": "Kota",
2365
+ "name": "Tebing Tinggi",
2366
+ "code": "76",
2367
+ "full_code": "1276",
2368
+ "province_id": 38
2369
+ },
2370
+ {
2371
+ "id": 297,
2372
+ "type": "Kabupaten",
2373
+ "name": "Labuhanbatu Selatan",
2374
+ "code": "22",
2375
+ "full_code": "1222",
2376
+ "province_id": 38
2377
+ },
2378
+ {
2379
+ "id": 298,
2380
+ "type": "Kabupaten",
2381
+ "name": "Tebo",
2382
+ "code": "09",
2383
+ "full_code": "1509",
2384
+ "province_id": 8
2385
+ },
2386
+ {
2387
+ "id": 299,
2388
+ "type": "Kabupaten",
2389
+ "name": "Tegal",
2390
+ "code": "28",
2391
+ "full_code": "3328",
2392
+ "province_id": 10
2393
+ },
2394
+ {
2395
+ "id": 300,
2396
+ "type": "Kota",
2397
+ "name": "Tegal",
2398
+ "code": "76",
2399
+ "full_code": "3376",
2400
+ "province_id": 10
2401
+ },
2402
+ {
2403
+ "id": 301,
2404
+ "type": "Kabupaten",
2405
+ "name": "Teluk Bintuni",
2406
+ "code": "06",
2407
+ "full_code": "9206",
2408
+ "province_id": 25
2409
+ },
2410
+ {
2411
+ "id": 302,
2412
+ "type": "Kabupaten",
2413
+ "name": "Teluk Wondama",
2414
+ "code": "07",
2415
+ "full_code": "9207",
2416
+ "province_id": 25
2417
+ },
2418
+ {
2419
+ "id": 303,
2420
+ "type": "Kabupaten",
2421
+ "name": "Labuhanbatu Utara",
2422
+ "code": "23",
2423
+ "full_code": "1223",
2424
+ "province_id": 38
2425
+ },
2426
+ {
2427
+ "id": 304,
2428
+ "type": "Kabupaten",
2429
+ "name": "Temanggung",
2430
+ "code": "23",
2431
+ "full_code": "3323",
2432
+ "province_id": 10
2433
+ },
2434
+ {
2435
+ "id": 305,
2436
+ "type": "Kabupaten",
2437
+ "name": "Lahat",
2438
+ "code": "04",
2439
+ "full_code": "1604",
2440
+ "province_id": 37
2441
+ },
2442
+ {
2443
+ "id": 306,
2444
+ "type": "Kota",
2445
+ "name": "Ternate",
2446
+ "code": "71",
2447
+ "full_code": "8271",
2448
+ "province_id": 21
2449
+ },
2450
+ {
2451
+ "id": 307,
2452
+ "type": "Kabupaten",
2453
+ "name": "Lamandau",
2454
+ "code": "09",
2455
+ "full_code": "6209",
2456
+ "province_id": 14
2457
+ },
2458
+ {
2459
+ "id": 308,
2460
+ "type": "Kabupaten",
2461
+ "name": "Dompu",
2462
+ "code": "05",
2463
+ "full_code": "5205",
2464
+ "province_id": 22
2465
+ },
2466
+ {
2467
+ "id": 309,
2468
+ "type": "Kabupaten",
2469
+ "name": "Donggala",
2470
+ "code": "03",
2471
+ "full_code": "7203",
2472
+ "province_id": 33
2473
+ },
2474
+ {
2475
+ "id": 310,
2476
+ "type": "Kota",
2477
+ "name": "Dumai",
2478
+ "code": "72",
2479
+ "full_code": "1472",
2480
+ "province_id": 30
2481
+ },
2482
+ {
2483
+ "id": 311,
2484
+ "type": "Kabupaten",
2485
+ "name": "Empat Lawang",
2486
+ "code": "11",
2487
+ "full_code": "1611",
2488
+ "province_id": 37
2489
+ },
2490
+ {
2491
+ "id": 312,
2492
+ "type": "Kabupaten",
2493
+ "name": "Lamongan",
2494
+ "code": "24",
2495
+ "full_code": "3524",
2496
+ "province_id": 11
2497
+ },
2498
+ {
2499
+ "id": 313,
2500
+ "type": "Kabupaten",
2501
+ "name": "Ende",
2502
+ "code": "08",
2503
+ "full_code": "5308",
2504
+ "province_id": 23
2505
+ },
2506
+ {
2507
+ "id": 314,
2508
+ "type": "Kabupaten",
2509
+ "name": "Lampung Barat",
2510
+ "code": "04",
2511
+ "full_code": "1804",
2512
+ "province_id": 19
2513
+ },
2514
+ {
2515
+ "id": 315,
2516
+ "type": "Kota",
2517
+ "name": "Tidore Kepulauan",
2518
+ "code": "72",
2519
+ "full_code": "8272",
2520
+ "province_id": 21
2521
+ },
2522
+ {
2523
+ "id": 316,
2524
+ "type": "Kabupaten",
2525
+ "name": "Enrekang",
2526
+ "code": "16",
2527
+ "full_code": "7316",
2528
+ "province_id": 32
2529
+ },
2530
+ {
2531
+ "id": 317,
2532
+ "type": "Kabupaten",
2533
+ "name": "Lampung Selatan",
2534
+ "code": "01",
2535
+ "full_code": "1801",
2536
+ "province_id": 19
2537
+ },
2538
+ {
2539
+ "id": 318,
2540
+ "type": "Kabupaten",
2541
+ "name": "Fak Fak",
2542
+ "code": "03",
2543
+ "full_code": "9203",
2544
+ "province_id": 25
2545
+ },
2546
+ {
2547
+ "id": 319,
2548
+ "type": "Kabupaten",
2549
+ "name": "Timor Tengah Selatan",
2550
+ "code": "02",
2551
+ "full_code": "5302",
2552
+ "province_id": 23
2553
+ },
2554
+ {
2555
+ "id": 320,
2556
+ "type": "Kabupaten",
2557
+ "name": "Lampung Tengah",
2558
+ "code": "02",
2559
+ "full_code": "1802",
2560
+ "province_id": 19
2561
+ },
2562
+ {
2563
+ "id": 321,
2564
+ "type": "Kabupaten",
2565
+ "name": "Flores Timur",
2566
+ "code": "06",
2567
+ "full_code": "5306",
2568
+ "province_id": 23
2569
+ },
2570
+ {
2571
+ "id": 322,
2572
+ "type": "Kabupaten",
2573
+ "name": "Lampung Timur",
2574
+ "code": "07",
2575
+ "full_code": "1807",
2576
+ "province_id": 19
2577
+ },
2578
+ {
2579
+ "id": 323,
2580
+ "type": "Kabupaten",
2581
+ "name": "Garut",
2582
+ "code": "05",
2583
+ "full_code": "3205",
2584
+ "province_id": 9
2585
+ },
2586
+ {
2587
+ "id": 324,
2588
+ "type": "Kabupaten",
2589
+ "name": "Timor Tengah Utara",
2590
+ "code": "03",
2591
+ "full_code": "5303",
2592
+ "province_id": 23
2593
+ },
2594
+ {
2595
+ "id": 325,
2596
+ "type": "Kabupaten",
2597
+ "name": "Lampung Utara",
2598
+ "code": "03",
2599
+ "full_code": "1803",
2600
+ "province_id": 19
2601
+ },
2602
+ {
2603
+ "id": 326,
2604
+ "type": "Kabupaten",
2605
+ "name": "Toba",
2606
+ "code": "12",
2607
+ "full_code": "1212",
2608
+ "province_id": 38
2609
+ },
2610
+ {
2611
+ "id": 327,
2612
+ "type": "Kabupaten",
2613
+ "name": "Tojo Una Una",
2614
+ "code": "09",
2615
+ "full_code": "7209",
2616
+ "province_id": 33
2617
+ },
2618
+ {
2619
+ "id": 328,
2620
+ "type": "Kabupaten",
2621
+ "name": "Gayo Lues",
2622
+ "code": "13",
2623
+ "full_code": "1113",
2624
+ "province_id": 1
2625
+ },
2626
+ {
2627
+ "id": 329,
2628
+ "type": "Kabupaten",
2629
+ "name": "Landak",
2630
+ "code": "08",
2631
+ "full_code": "6108",
2632
+ "province_id": 12
2633
+ },
2634
+ {
2635
+ "id": 330,
2636
+ "type": "Kabupaten",
2637
+ "name": "Gianyar",
2638
+ "code": "04",
2639
+ "full_code": "5104",
2640
+ "province_id": 2
2641
+ },
2642
+ {
2643
+ "id": 331,
2644
+ "type": "Kabupaten",
2645
+ "name": "Langkat",
2646
+ "code": "05",
2647
+ "full_code": "1205",
2648
+ "province_id": 38
2649
+ },
2650
+ {
2651
+ "id": 332,
2652
+ "type": "Kabupaten",
2653
+ "name": "Gorontalo",
2654
+ "code": "01",
2655
+ "full_code": "7501",
2656
+ "province_id": 7
2657
+ },
2658
+ {
2659
+ "id": 333,
2660
+ "type": "Kota",
2661
+ "name": "Langsa",
2662
+ "code": "74",
2663
+ "full_code": "1174",
2664
+ "province_id": 1
2665
+ },
2666
+ {
2667
+ "id": 334,
2668
+ "type": "Kabupaten",
2669
+ "name": "Toli Toli",
2670
+ "code": "04",
2671
+ "full_code": "7204",
2672
+ "province_id": 33
2673
+ },
2674
+ {
2675
+ "id": 335,
2676
+ "type": "Kabupaten",
2677
+ "name": "Lanny Jaya",
2678
+ "code": "07",
2679
+ "full_code": "9507",
2680
+ "province_id": 27
2681
+ },
2682
+ {
2683
+ "id": 336,
2684
+ "type": "Kabupaten",
2685
+ "name": "Lebak",
2686
+ "code": "02",
2687
+ "full_code": "3602",
2688
+ "province_id": 3
2689
+ },
2690
+ {
2691
+ "id": 337,
2692
+ "type": "Kota",
2693
+ "name": "Gorontalo",
2694
+ "code": "71",
2695
+ "full_code": "7571",
2696
+ "province_id": 7
2697
+ },
2698
+ {
2699
+ "id": 338,
2700
+ "type": "Kabupaten",
2701
+ "name": "Tolikara",
2702
+ "code": "04",
2703
+ "full_code": "9504",
2704
+ "province_id": 27
2705
+ },
2706
+ {
2707
+ "id": 339,
2708
+ "type": "Kota",
2709
+ "name": "Tomohon",
2710
+ "code": "73",
2711
+ "full_code": "7173",
2712
+ "province_id": 35
2713
+ },
2714
+ {
2715
+ "id": 340,
2716
+ "type": "Kabupaten",
2717
+ "name": "Lebong",
2718
+ "code": "07",
2719
+ "full_code": "1707",
2720
+ "province_id": 4
2721
+ },
2722
+ {
2723
+ "id": 341,
2724
+ "type": "Kabupaten",
2725
+ "name": "Toraja Utara",
2726
+ "code": "26",
2727
+ "full_code": "7326",
2728
+ "province_id": 32
2729
+ },
2730
+ {
2731
+ "id": 342,
2732
+ "type": "Kabupaten",
2733
+ "name": "Lembata",
2734
+ "code": "13",
2735
+ "full_code": "5313",
2736
+ "province_id": 23
2737
+ },
2738
+ {
2739
+ "id": 343,
2740
+ "type": "Kota",
2741
+ "name": "Lhokseumawe",
2742
+ "code": "73",
2743
+ "full_code": "1173",
2744
+ "province_id": 1
2745
+ },
2746
+ {
2747
+ "id": 344,
2748
+ "type": "Kabupaten",
2749
+ "name": "Lima Puluh Kota",
2750
+ "code": "07",
2751
+ "full_code": "1307",
2752
+ "province_id": 36
2753
+ },
2754
+ {
2755
+ "id": 345,
2756
+ "type": "Kabupaten",
2757
+ "name": "Lingga",
2758
+ "code": "04",
2759
+ "full_code": "2104",
2760
+ "province_id": 18
2761
+ },
2762
+ {
2763
+ "id": 346,
2764
+ "type": "Kabupaten",
2765
+ "name": "Trenggalek",
2766
+ "code": "03",
2767
+ "full_code": "3503",
2768
+ "province_id": 11
2769
+ },
2770
+ {
2771
+ "id": 347,
2772
+ "type": "Kota",
2773
+ "name": "Tual",
2774
+ "code": "72",
2775
+ "full_code": "8172",
2776
+ "province_id": 20
2777
+ },
2778
+ {
2779
+ "id": 348,
2780
+ "type": "Kabupaten",
2781
+ "name": "Tuban",
2782
+ "code": "23",
2783
+ "full_code": "3523",
2784
+ "province_id": 11
2785
+ },
2786
+ {
2787
+ "id": 349,
2788
+ "type": "Kabupaten",
2789
+ "name": "Lombok Barat",
2790
+ "code": "01",
2791
+ "full_code": "5201",
2792
+ "province_id": 22
2793
+ },
2794
+ {
2795
+ "id": 350,
2796
+ "type": "Kabupaten",
2797
+ "name": "Tulang Bawang",
2798
+ "code": "05",
2799
+ "full_code": "1805",
2800
+ "province_id": 19
2801
+ },
2802
+ {
2803
+ "id": 351,
2804
+ "type": "Kabupaten",
2805
+ "name": "Lombok Tengah",
2806
+ "code": "02",
2807
+ "full_code": "5202",
2808
+ "province_id": 22
2809
+ },
2810
+ {
2811
+ "id": 352,
2812
+ "type": "Kabupaten",
2813
+ "name": "Lombok Timur",
2814
+ "code": "03",
2815
+ "full_code": "5203",
2816
+ "province_id": 22
2817
+ },
2818
+ {
2819
+ "id": 353,
2820
+ "type": "Kabupaten",
2821
+ "name": "Tulang Bawang Barat",
2822
+ "code": "12",
2823
+ "full_code": "1812",
2824
+ "province_id": 19
2825
+ },
2826
+ {
2827
+ "id": 354,
2828
+ "type": "Kabupaten",
2829
+ "name": "Lombok Utara",
2830
+ "code": "08",
2831
+ "full_code": "5208",
2832
+ "province_id": 22
2833
+ },
2834
+ {
2835
+ "id": 355,
2836
+ "type": "Kota",
2837
+ "name": "Lubuk Linggau",
2838
+ "code": "73",
2839
+ "full_code": "1673",
2840
+ "province_id": 37
2841
+ },
2842
+ {
2843
+ "id": 356,
2844
+ "type": "Kabupaten",
2845
+ "name": "Tulungagung",
2846
+ "code": "04",
2847
+ "full_code": "3504",
2848
+ "province_id": 11
2849
+ },
2850
+ {
2851
+ "id": 357,
2852
+ "type": "Kabupaten",
2853
+ "name": "Lumajang",
2854
+ "code": "08",
2855
+ "full_code": "3508",
2856
+ "province_id": 11
2857
+ },
2858
+ {
2859
+ "id": 358,
2860
+ "type": "Kabupaten",
2861
+ "name": "Wajo",
2862
+ "code": "13",
2863
+ "full_code": "7313",
2864
+ "province_id": 32
2865
+ },
2866
+ {
2867
+ "id": 359,
2868
+ "type": "Kabupaten",
2869
+ "name": "Luwu",
2870
+ "code": "17",
2871
+ "full_code": "7317",
2872
+ "province_id": 32
2873
+ },
2874
+ {
2875
+ "id": 360,
2876
+ "type": "Kabupaten",
2877
+ "name": "Luwu Timur",
2878
+ "code": "24",
2879
+ "full_code": "7324",
2880
+ "province_id": 32
2881
+ },
2882
+ {
2883
+ "id": 361,
2884
+ "type": "Kabupaten",
2885
+ "name": "Wakatobi",
2886
+ "code": "07",
2887
+ "full_code": "7407",
2888
+ "province_id": 34
2889
+ },
2890
+ {
2891
+ "id": 362,
2892
+ "type": "Kabupaten",
2893
+ "name": "Waropen",
2894
+ "code": "15",
2895
+ "full_code": "9115",
2896
+ "province_id": 24
2897
+ },
2898
+ {
2899
+ "id": 363,
2900
+ "type": "Kabupaten",
2901
+ "name": "Way Kanan",
2902
+ "code": "08",
2903
+ "full_code": "1808",
2904
+ "province_id": 19
2905
+ },
2906
+ {
2907
+ "id": 364,
2908
+ "type": "Kabupaten",
2909
+ "name": "Wonogiri",
2910
+ "code": "12",
2911
+ "full_code": "3312",
2912
+ "province_id": 10
2913
+ },
2914
+ {
2915
+ "id": 365,
2916
+ "type": "Kabupaten",
2917
+ "name": "Wonosobo",
2918
+ "code": "07",
2919
+ "full_code": "3307",
2920
+ "province_id": 10
2921
+ },
2922
+ {
2923
+ "id": 366,
2924
+ "type": "Kabupaten",
2925
+ "name": "Yahukimo",
2926
+ "code": "03",
2927
+ "full_code": "9503",
2928
+ "province_id": 27
2929
+ },
2930
+ {
2931
+ "id": 367,
2932
+ "type": "Kabupaten",
2933
+ "name": "Yalimo",
2934
+ "code": "06",
2935
+ "full_code": "9506",
2936
+ "province_id": 27
2937
+ },
2938
+ {
2939
+ "id": 368,
2940
+ "type": "Kota",
2941
+ "name": "Yogyakarta",
2942
+ "code": "71",
2943
+ "full_code": "3471",
2944
+ "province_id": 5
2945
+ },
2946
+ {
2947
+ "id": 369,
2948
+ "type": "Kabupaten",
2949
+ "name": "Luwu Utara",
2950
+ "code": "22",
2951
+ "full_code": "7322",
2952
+ "province_id": 32
2953
+ },
2954
+ {
2955
+ "id": 370,
2956
+ "type": "Kabupaten",
2957
+ "name": "Madiun",
2958
+ "code": "19",
2959
+ "full_code": "3519",
2960
+ "province_id": 11
2961
+ },
2962
+ {
2963
+ "id": 371,
2964
+ "type": "Kota",
2965
+ "name": "Madiun",
2966
+ "code": "77",
2967
+ "full_code": "3577",
2968
+ "province_id": 11
2969
+ },
2970
+ {
2971
+ "id": 372,
2972
+ "type": "Kabupaten",
2973
+ "name": "Magelang",
2974
+ "code": "08",
2975
+ "full_code": "3308",
2976
+ "province_id": 10
2977
+ },
2978
+ {
2979
+ "id": 373,
2980
+ "type": "Kota",
2981
+ "name": "Magelang",
2982
+ "code": "71",
2983
+ "full_code": "3371",
2984
+ "province_id": 10
2985
+ },
2986
+ {
2987
+ "id": 374,
2988
+ "type": "Kabupaten",
2989
+ "name": "Magetan",
2990
+ "code": "20",
2991
+ "full_code": "3520",
2992
+ "province_id": 11
2993
+ },
2994
+ {
2995
+ "id": 375,
2996
+ "type": "Kabupaten",
2997
+ "name": "Mahakam Ulu",
2998
+ "code": "11",
2999
+ "full_code": "6411",
3000
+ "province_id": 15
3001
+ },
3002
+ {
3003
+ "id": 376,
3004
+ "type": "Kabupaten",
3005
+ "name": "Majalengka",
3006
+ "code": "10",
3007
+ "full_code": "3210",
3008
+ "province_id": 9
3009
+ },
3010
+ {
3011
+ "id": 377,
3012
+ "type": "Kabupaten",
3013
+ "name": "Majene",
3014
+ "code": "05",
3015
+ "full_code": "7605",
3016
+ "province_id": 31
3017
+ },
3018
+ {
3019
+ "id": 378,
3020
+ "type": "Kota",
3021
+ "name": "Makassar",
3022
+ "code": "71",
3023
+ "full_code": "7371",
3024
+ "province_id": 32
3025
+ },
3026
+ {
3027
+ "id": 379,
3028
+ "type": "Kabupaten",
3029
+ "name": "Malaka",
3030
+ "code": "21",
3031
+ "full_code": "5321",
3032
+ "province_id": 23
3033
+ },
3034
+ {
3035
+ "id": 380,
3036
+ "type": "Kabupaten",
3037
+ "name": "Malang",
3038
+ "code": "07",
3039
+ "full_code": "3507",
3040
+ "province_id": 11
3041
+ },
3042
+ {
3043
+ "id": 381,
3044
+ "type": "Kota",
3045
+ "name": "Malang",
3046
+ "code": "73",
3047
+ "full_code": "3573",
3048
+ "province_id": 11
3049
+ },
3050
+ {
3051
+ "id": 382,
3052
+ "type": "Kabupaten",
3053
+ "name": "Malinau",
3054
+ "code": "02",
3055
+ "full_code": "6502",
3056
+ "province_id": 16
3057
+ },
3058
+ {
3059
+ "id": 383,
3060
+ "type": "Kabupaten",
3061
+ "name": "Maluku Barat Daya",
3062
+ "code": "08",
3063
+ "full_code": "8108",
3064
+ "province_id": 20
3065
+ },
3066
+ {
3067
+ "id": 384,
3068
+ "type": "Kabupaten",
3069
+ "name": "Maluku Tengah",
3070
+ "code": "01",
3071
+ "full_code": "8101",
3072
+ "province_id": 20
3073
+ },
3074
+ {
3075
+ "id": 385,
3076
+ "type": "Kabupaten",
3077
+ "name": "Maluku Tenggara",
3078
+ "code": "02",
3079
+ "full_code": "8102",
3080
+ "province_id": 20
3081
+ },
3082
+ {
3083
+ "id": 386,
3084
+ "type": "Kabupaten",
3085
+ "name": "Mamasa",
3086
+ "code": "03",
3087
+ "full_code": "7603",
3088
+ "province_id": 31
3089
+ },
3090
+ {
3091
+ "id": 387,
3092
+ "type": "Kabupaten",
3093
+ "name": "Mamberamo Raya",
3094
+ "code": "20",
3095
+ "full_code": "9120",
3096
+ "province_id": 24
3097
+ },
3098
+ {
3099
+ "id": 388,
3100
+ "type": "Kabupaten",
3101
+ "name": "Mamberamo Tengah",
3102
+ "code": "05",
3103
+ "full_code": "9505",
3104
+ "province_id": 27
3105
+ },
3106
+ {
3107
+ "id": 389,
3108
+ "type": "Kabupaten",
3109
+ "name": "Mamuju",
3110
+ "code": "02",
3111
+ "full_code": "7602",
3112
+ "province_id": 31
3113
+ },
3114
+ {
3115
+ "id": 390,
3116
+ "type": "Kabupaten",
3117
+ "name": "Mamuju Tengah",
3118
+ "code": "06",
3119
+ "full_code": "7606",
3120
+ "province_id": 31
3121
+ },
3122
+ {
3123
+ "id": 391,
3124
+ "type": "Kota",
3125
+ "name": "Manado",
3126
+ "code": "71",
3127
+ "full_code": "7171",
3128
+ "province_id": 35
3129
+ },
3130
+ {
3131
+ "id": 392,
3132
+ "type": "Kabupaten",
3133
+ "name": "Mandailing Natal",
3134
+ "code": "13",
3135
+ "full_code": "1213",
3136
+ "province_id": 38
3137
+ },
3138
+ {
3139
+ "id": 393,
3140
+ "type": "Kabupaten",
3141
+ "name": "Manggarai",
3142
+ "code": "10",
3143
+ "full_code": "5310",
3144
+ "province_id": 23
3145
+ },
3146
+ {
3147
+ "id": 394,
3148
+ "type": "Kabupaten",
3149
+ "name": "Manggarai Barat",
3150
+ "code": "15",
3151
+ "full_code": "5315",
3152
+ "province_id": 23
3153
+ },
3154
+ {
3155
+ "id": 395,
3156
+ "type": "Kabupaten",
3157
+ "name": "Manggarai Timur",
3158
+ "code": "19",
3159
+ "full_code": "5319",
3160
+ "province_id": 23
3161
+ },
3162
+ {
3163
+ "id": 396,
3164
+ "type": "Kabupaten",
3165
+ "name": "Manokwari",
3166
+ "code": "02",
3167
+ "full_code": "9202",
3168
+ "province_id": 25
3169
+ },
3170
+ {
3171
+ "id": 397,
3172
+ "type": "Kabupaten",
3173
+ "name": "Manokwari Selatan",
3174
+ "code": "11",
3175
+ "full_code": "9211",
3176
+ "province_id": 25
3177
+ },
3178
+ {
3179
+ "id": 398,
3180
+ "type": "Kabupaten",
3181
+ "name": "Mappi",
3182
+ "code": "03",
3183
+ "full_code": "9303",
3184
+ "province_id": 28
3185
+ },
3186
+ {
3187
+ "id": 399,
3188
+ "type": "Kabupaten",
3189
+ "name": "Maros",
3190
+ "code": "09",
3191
+ "full_code": "7309",
3192
+ "province_id": 32
3193
+ },
3194
+ {
3195
+ "id": 400,
3196
+ "type": "Kota",
3197
+ "name": "Mataram",
3198
+ "code": "71",
3199
+ "full_code": "5271",
3200
+ "province_id": 22
3201
+ },
3202
+ {
3203
+ "id": 401,
3204
+ "type": "Kabupaten",
3205
+ "name": "Maybrat",
3206
+ "code": "10",
3207
+ "full_code": "9210",
3208
+ "province_id": 26
3209
+ },
3210
+ {
3211
+ "id": 402,
3212
+ "type": "Kota",
3213
+ "name": "Medan",
3214
+ "code": "71",
3215
+ "full_code": "1271",
3216
+ "province_id": 38
3217
+ },
3218
+ {
3219
+ "id": 403,
3220
+ "type": "Kabupaten",
3221
+ "name": "Melawi",
3222
+ "code": "10",
3223
+ "full_code": "6110",
3224
+ "province_id": 12
3225
+ },
3226
+ {
3227
+ "id": 404,
3228
+ "type": "Kabupaten",
3229
+ "name": "Mempawah",
3230
+ "code": "02",
3231
+ "full_code": "6102",
3232
+ "province_id": 12
3233
+ },
3234
+ {
3235
+ "id": 405,
3236
+ "type": "Kabupaten",
3237
+ "name": "Merangin",
3238
+ "code": "02",
3239
+ "full_code": "1502",
3240
+ "province_id": 8
3241
+ },
3242
+ {
3243
+ "id": 406,
3244
+ "type": "Kabupaten",
3245
+ "name": "Merauke",
3246
+ "code": "01",
3247
+ "full_code": "9301",
3248
+ "province_id": 28
3249
+ },
3250
+ {
3251
+ "id": 407,
3252
+ "type": "Kabupaten",
3253
+ "name": "Mesuji",
3254
+ "code": "11",
3255
+ "full_code": "1811",
3256
+ "province_id": 19
3257
+ },
3258
+ {
3259
+ "id": 408,
3260
+ "type": "Kota",
3261
+ "name": "Metro",
3262
+ "code": "72",
3263
+ "full_code": "1872",
3264
+ "province_id": 19
3265
+ },
3266
+ {
3267
+ "id": 409,
3268
+ "type": "Kabupaten",
3269
+ "name": "Mimika",
3270
+ "code": "04",
3271
+ "full_code": "9404",
3272
+ "province_id": 29
3273
+ },
3274
+ {
3275
+ "id": 410,
3276
+ "type": "Kabupaten",
3277
+ "name": "Minahasa",
3278
+ "code": "02",
3279
+ "full_code": "7102",
3280
+ "province_id": 35
3281
+ },
3282
+ {
3283
+ "id": 411,
3284
+ "type": "Kabupaten",
3285
+ "name": "Minahasa Selatan",
3286
+ "code": "05",
3287
+ "full_code": "7105",
3288
+ "province_id": 35
3289
+ },
3290
+ {
3291
+ "id": 412,
3292
+ "type": "Kabupaten",
3293
+ "name": "Minahasa Tenggara",
3294
+ "code": "07",
3295
+ "full_code": "7107",
3296
+ "province_id": 35
3297
+ },
3298
+ {
3299
+ "id": 413,
3300
+ "type": "Kabupaten",
3301
+ "name": "Minahasa Utara",
3302
+ "code": "06",
3303
+ "full_code": "7106",
3304
+ "province_id": 35
3305
+ },
3306
+ {
3307
+ "id": 414,
3308
+ "type": "Kabupaten",
3309
+ "name": "Mojokerto",
3310
+ "code": "16",
3311
+ "full_code": "3516",
3312
+ "province_id": 11
3313
+ },
3314
+ {
3315
+ "id": 415,
3316
+ "type": "Kota",
3317
+ "name": "Mojokerto",
3318
+ "code": "76",
3319
+ "full_code": "3576",
3320
+ "province_id": 11
3321
+ },
3322
+ {
3323
+ "id": 416,
3324
+ "type": "Kabupaten",
3325
+ "name": "Morowali",
3326
+ "code": "06",
3327
+ "full_code": "7206",
3328
+ "province_id": 33
3329
+ },
3330
+ {
3331
+ "id": 417,
3332
+ "type": "Kabupaten",
3333
+ "name": "Morowali Utara",
3334
+ "code": "12",
3335
+ "full_code": "7212",
3336
+ "province_id": 33
3337
+ },
3338
+ {
3339
+ "id": 418,
3340
+ "type": "Kabupaten",
3341
+ "name": "Muara Enim",
3342
+ "code": "03",
3343
+ "full_code": "1603",
3344
+ "province_id": 37
3345
+ },
3346
+ {
3347
+ "id": 419,
3348
+ "type": "Kabupaten",
3349
+ "name": "Muaro Jambi",
3350
+ "code": "05",
3351
+ "full_code": "1505",
3352
+ "province_id": 8
3353
+ },
3354
+ {
3355
+ "id": 420,
3356
+ "type": "Kabupaten",
3357
+ "name": "Muko Muko",
3358
+ "code": "06",
3359
+ "full_code": "1706",
3360
+ "province_id": 4
3361
+ },
3362
+ {
3363
+ "id": 421,
3364
+ "type": "Kabupaten",
3365
+ "name": "Muna",
3366
+ "code": "03",
3367
+ "full_code": "7403",
3368
+ "province_id": 34
3369
+ },
3370
+ {
3371
+ "id": 422,
3372
+ "type": "Kabupaten",
3373
+ "name": "Muna Barat",
3374
+ "code": "13",
3375
+ "full_code": "7413",
3376
+ "province_id": 34
3377
+ },
3378
+ {
3379
+ "id": 423,
3380
+ "type": "Kabupaten",
3381
+ "name": "Murung Raya",
3382
+ "code": "12",
3383
+ "full_code": "6212",
3384
+ "province_id": 14
3385
+ },
3386
+ {
3387
+ "id": 424,
3388
+ "type": "Kabupaten",
3389
+ "name": "Musi Banyuasin",
3390
+ "code": "06",
3391
+ "full_code": "1606",
3392
+ "province_id": 37
3393
+ },
3394
+ {
3395
+ "id": 425,
3396
+ "type": "Kabupaten",
3397
+ "name": "Musi Rawas",
3398
+ "code": "05",
3399
+ "full_code": "1605",
3400
+ "province_id": 37
3401
+ },
3402
+ {
3403
+ "id": 426,
3404
+ "type": "Kabupaten",
3405
+ "name": "Musi Rawas Utara",
3406
+ "code": "13",
3407
+ "full_code": "1613",
3408
+ "province_id": 37
3409
+ },
3410
+ {
3411
+ "id": 427,
3412
+ "type": "Kabupaten",
3413
+ "name": "Nabire",
3414
+ "code": "01",
3415
+ "full_code": "9401",
3416
+ "province_id": 29
3417
+ },
3418
+ {
3419
+ "id": 428,
3420
+ "type": "Kabupaten",
3421
+ "name": "Nagan Raya",
3422
+ "code": "15",
3423
+ "full_code": "1115",
3424
+ "province_id": 1
3425
+ },
3426
+ {
3427
+ "id": 429,
3428
+ "type": "Kabupaten",
3429
+ "name": "Nagekeo",
3430
+ "code": "16",
3431
+ "full_code": "5316",
3432
+ "province_id": 23
3433
+ },
3434
+ {
3435
+ "id": 430,
3436
+ "type": "Kabupaten",
3437
+ "name": "Natuna",
3438
+ "code": "03",
3439
+ "full_code": "2103",
3440
+ "province_id": 18
3441
+ },
3442
+ {
3443
+ "id": 431,
3444
+ "type": "Kabupaten",
3445
+ "name": "Nduga",
3446
+ "code": "08",
3447
+ "full_code": "9508",
3448
+ "province_id": 27
3449
+ },
3450
+ {
3451
+ "id": 432,
3452
+ "type": "Kabupaten",
3453
+ "name": "Ngada",
3454
+ "code": "09",
3455
+ "full_code": "5309",
3456
+ "province_id": 23
3457
+ },
3458
+ {
3459
+ "id": 433,
3460
+ "type": "Kabupaten",
3461
+ "name": "Nganjuk",
3462
+ "code": "18",
3463
+ "full_code": "3518",
3464
+ "province_id": 11
3465
+ },
3466
+ {
3467
+ "id": 434,
3468
+ "type": "Kabupaten",
3469
+ "name": "Ngawi",
3470
+ "code": "21",
3471
+ "full_code": "3521",
3472
+ "province_id": 11
3473
+ },
3474
+ {
3475
+ "id": 435,
3476
+ "type": "Kabupaten",
3477
+ "name": "Nias",
3478
+ "code": "04",
3479
+ "full_code": "1204",
3480
+ "province_id": 38
3481
+ },
3482
+ {
3483
+ "id": 436,
3484
+ "type": "Kabupaten",
3485
+ "name": "Nias Barat",
3486
+ "code": "25",
3487
+ "full_code": "1225",
3488
+ "province_id": 38
3489
+ },
3490
+ {
3491
+ "id": 437,
3492
+ "type": "Kabupaten",
3493
+ "name": "Nias Selatan",
3494
+ "code": "14",
3495
+ "full_code": "1214",
3496
+ "province_id": 38
3497
+ },
3498
+ {
3499
+ "id": 438,
3500
+ "type": "Kabupaten",
3501
+ "name": "Nias Utara",
3502
+ "code": "24",
3503
+ "full_code": "1224",
3504
+ "province_id": 38
3505
+ },
3506
+ {
3507
+ "id": 439,
3508
+ "type": "Kabupaten",
3509
+ "name": "Nunukan",
3510
+ "code": "03",
3511
+ "full_code": "6503",
3512
+ "province_id": 16
3513
+ },
3514
+ {
3515
+ "id": 440,
3516
+ "type": "Kabupaten",
3517
+ "name": "Ogan Ilir",
3518
+ "code": "10",
3519
+ "full_code": "1610",
3520
+ "province_id": 37
3521
+ },
3522
+ {
3523
+ "id": 441,
3524
+ "type": "Kabupaten",
3525
+ "name": "Ogan Komering Ilir",
3526
+ "code": "02",
3527
+ "full_code": "1602",
3528
+ "province_id": 37
3529
+ },
3530
+ {
3531
+ "id": 442,
3532
+ "type": "Kabupaten",
3533
+ "name": "Ogan Komering Ulu",
3534
+ "code": "01",
3535
+ "full_code": "1601",
3536
+ "province_id": 37
3537
+ },
3538
+ {
3539
+ "id": 443,
3540
+ "type": "Kabupaten",
3541
+ "name": "Ogan Komering Ulu Selatan",
3542
+ "code": "09",
3543
+ "full_code": "1609",
3544
+ "province_id": 37
3545
+ },
3546
+ {
3547
+ "id": 444,
3548
+ "type": "Kabupaten",
3549
+ "name": "Ogan Komering Ulu Timur",
3550
+ "code": "08",
3551
+ "full_code": "1608",
3552
+ "province_id": 37
3553
+ },
3554
+ {
3555
+ "id": 445,
3556
+ "type": "Kabupaten",
3557
+ "name": "Pacitan",
3558
+ "code": "01",
3559
+ "full_code": "3501",
3560
+ "province_id": 11
3561
+ },
3562
+ {
3563
+ "id": 446,
3564
+ "type": "Kota",
3565
+ "name": "Padang",
3566
+ "code": "71",
3567
+ "full_code": "1371",
3568
+ "province_id": 36
3569
+ },
3570
+ {
3571
+ "id": 447,
3572
+ "type": "Kabupaten",
3573
+ "name": "Padang Lawas",
3574
+ "code": "21",
3575
+ "full_code": "1221",
3576
+ "province_id": 38
3577
+ },
3578
+ {
3579
+ "id": 448,
3580
+ "type": "Kabupaten",
3581
+ "name": "Padang Lawas Utara",
3582
+ "code": "20",
3583
+ "full_code": "1220",
3584
+ "province_id": 38
3585
+ },
3586
+ {
3587
+ "id": 449,
3588
+ "type": "Kota",
3589
+ "name": "Padang Panjang",
3590
+ "code": "74",
3591
+ "full_code": "1374",
3592
+ "province_id": 36
3593
+ },
3594
+ {
3595
+ "id": 450,
3596
+ "type": "Kabupaten",
3597
+ "name": "Padang Pariaman",
3598
+ "code": "05",
3599
+ "full_code": "1305",
3600
+ "province_id": 36
3601
+ },
3602
+ {
3603
+ "id": 451,
3604
+ "type": "Kota",
3605
+ "name": "Padangsidimpuan",
3606
+ "code": "77",
3607
+ "full_code": "1277",
3608
+ "province_id": 38
3609
+ },
3610
+ {
3611
+ "id": 452,
3612
+ "type": "Kota",
3613
+ "name": "Pagar Alam",
3614
+ "code": "72",
3615
+ "full_code": "1672",
3616
+ "province_id": 37
3617
+ },
3618
+ {
3619
+ "id": 453,
3620
+ "type": "Kabupaten",
3621
+ "name": "Pahuwato",
3622
+ "code": "04",
3623
+ "full_code": "7504",
3624
+ "province_id": 7
3625
+ },
3626
+ {
3627
+ "id": 454,
3628
+ "type": "Kabupaten",
3629
+ "name": "Pakpak Bharat",
3630
+ "code": "15",
3631
+ "full_code": "1215",
3632
+ "province_id": 38
3633
+ },
3634
+ {
3635
+ "id": 455,
3636
+ "type": "Kota",
3637
+ "name": "Palangkaraya",
3638
+ "code": "71",
3639
+ "full_code": "6271",
3640
+ "province_id": 14
3641
+ },
3642
+ {
3643
+ "id": 456,
3644
+ "type": "Kota",
3645
+ "name": "Palembang",
3646
+ "code": "71",
3647
+ "full_code": "1671",
3648
+ "province_id": 37
3649
+ },
3650
+ {
3651
+ "id": 457,
3652
+ "type": "Kota",
3653
+ "name": "Palopo",
3654
+ "code": "73",
3655
+ "full_code": "7373",
3656
+ "province_id": 32
3657
+ },
3658
+ {
3659
+ "id": 458,
3660
+ "type": "Kota",
3661
+ "name": "Palu",
3662
+ "code": "71",
3663
+ "full_code": "7271",
3664
+ "province_id": 33
3665
+ },
3666
+ {
3667
+ "id": 459,
3668
+ "type": "Kabupaten",
3669
+ "name": "Pamekasan",
3670
+ "code": "28",
3671
+ "full_code": "3528",
3672
+ "province_id": 11
3673
+ },
3674
+ {
3675
+ "id": 460,
3676
+ "type": "Kabupaten",
3677
+ "name": "Pandeglang",
3678
+ "code": "01",
3679
+ "full_code": "3601",
3680
+ "province_id": 3
3681
+ },
3682
+ {
3683
+ "id": 461,
3684
+ "type": "Kabupaten",
3685
+ "name": "Pangandaran",
3686
+ "code": "18",
3687
+ "full_code": "3218",
3688
+ "province_id": 9
3689
+ },
3690
+ {
3691
+ "id": 462,
3692
+ "type": "Kabupaten",
3693
+ "name": "Pangkajene Kepulauan",
3694
+ "code": "10",
3695
+ "full_code": "7310",
3696
+ "province_id": 32
3697
+ },
3698
+ {
3699
+ "id": 463,
3700
+ "type": "Kota",
3701
+ "name": "Pangkal Pinang",
3702
+ "code": "71",
3703
+ "full_code": "1971",
3704
+ "province_id": 17
3705
+ },
3706
+ {
3707
+ "id": 464,
3708
+ "type": "Kabupaten",
3709
+ "name": "Paniai",
3710
+ "code": "03",
3711
+ "full_code": "9403",
3712
+ "province_id": 29
3713
+ },
3714
+ {
3715
+ "id": 465,
3716
+ "type": "Kota",
3717
+ "name": "Pare Pare",
3718
+ "code": "72",
3719
+ "full_code": "7372",
3720
+ "province_id": 32
3721
+ },
3722
+ {
3723
+ "id": 466,
3724
+ "type": "Kota",
3725
+ "name": "Pariaman",
3726
+ "code": "77",
3727
+ "full_code": "1377",
3728
+ "province_id": 36
3729
+ },
3730
+ {
3731
+ "id": 467,
3732
+ "type": "Kabupaten",
3733
+ "name": "Parigi Moutong",
3734
+ "code": "08",
3735
+ "full_code": "7208",
3736
+ "province_id": 33
3737
+ },
3738
+ {
3739
+ "id": 468,
3740
+ "type": "Kabupaten",
3741
+ "name": "Pasaman",
3742
+ "code": "08",
3743
+ "full_code": "1308",
3744
+ "province_id": 36
3745
+ },
3746
+ {
3747
+ "id": 469,
3748
+ "type": "Kabupaten",
3749
+ "name": "Pasaman Barat",
3750
+ "code": "12",
3751
+ "full_code": "1312",
3752
+ "province_id": 36
3753
+ },
3754
+ {
3755
+ "id": 470,
3756
+ "type": "Kabupaten",
3757
+ "name": "Pasangkayu (Mamuju Utara)",
3758
+ "code": "01",
3759
+ "full_code": "7601",
3760
+ "province_id": 31
3761
+ },
3762
+ {
3763
+ "id": 471,
3764
+ "type": "Kabupaten",
3765
+ "name": "Paser",
3766
+ "code": "01",
3767
+ "full_code": "6401",
3768
+ "province_id": 15
3769
+ },
3770
+ {
3771
+ "id": 472,
3772
+ "type": "Kabupaten",
3773
+ "name": "Pasuruan",
3774
+ "code": "14",
3775
+ "full_code": "3514",
3776
+ "province_id": 11
3777
+ },
3778
+ {
3779
+ "id": 473,
3780
+ "type": "Kota",
3781
+ "name": "Pasuruan",
3782
+ "code": "75",
3783
+ "full_code": "3575",
3784
+ "province_id": 11
3785
+ },
3786
+ {
3787
+ "id": 474,
3788
+ "type": "Kabupaten",
3789
+ "name": "Pati",
3790
+ "code": "18",
3791
+ "full_code": "3318",
3792
+ "province_id": 10
3793
+ },
3794
+ {
3795
+ "id": 475,
3796
+ "type": "Kota",
3797
+ "name": "Payakumbuh",
3798
+ "code": "76",
3799
+ "full_code": "1376",
3800
+ "province_id": 36
3801
+ },
3802
+ {
3803
+ "id": 476,
3804
+ "type": "Kabupaten",
3805
+ "name": "Pegunungan Arfak",
3806
+ "code": "12",
3807
+ "full_code": "9212",
3808
+ "province_id": 25
3809
+ },
3810
+ {
3811
+ "id": 477,
3812
+ "type": "Kabupaten",
3813
+ "name": "Pegunungan Bintang",
3814
+ "code": "02",
3815
+ "full_code": "9502",
3816
+ "province_id": 27
3817
+ },
3818
+ {
3819
+ "id": 478,
3820
+ "type": "Kabupaten",
3821
+ "name": "Pekalongan",
3822
+ "code": "26",
3823
+ "full_code": "3326",
3824
+ "province_id": 10
3825
+ },
3826
+ {
3827
+ "id": 479,
3828
+ "type": "Kota",
3829
+ "name": "Pekalongan",
3830
+ "code": "75",
3831
+ "full_code": "3375",
3832
+ "province_id": 10
3833
+ },
3834
+ {
3835
+ "id": 480,
3836
+ "type": "Kota",
3837
+ "name": "Pekanbaru",
3838
+ "code": "71",
3839
+ "full_code": "1471",
3840
+ "province_id": 30
3841
+ },
3842
+ {
3843
+ "id": 481,
3844
+ "type": "Kabupaten",
3845
+ "name": "Pelalawan",
3846
+ "code": "05",
3847
+ "full_code": "1405",
3848
+ "province_id": 30
3849
+ },
3850
+ {
3851
+ "id": 482,
3852
+ "type": "Kabupaten",
3853
+ "name": "Pemalang",
3854
+ "code": "27",
3855
+ "full_code": "3327",
3856
+ "province_id": 10
3857
+ },
3858
+ {
3859
+ "id": 483,
3860
+ "type": "Kota",
3861
+ "name": "Pematangsiantar",
3862
+ "code": "72",
3863
+ "full_code": "1272",
3864
+ "province_id": 38
3865
+ },
3866
+ {
3867
+ "id": 484,
3868
+ "type": "Kabupaten",
3869
+ "name": "Penajam Paser Utara",
3870
+ "code": "09",
3871
+ "full_code": "6409",
3872
+ "province_id": 15
3873
+ },
3874
+ {
3875
+ "id": 485,
3876
+ "type": "Kabupaten",
3877
+ "name": "Penukal Abab Lematang Ilir",
3878
+ "code": "12",
3879
+ "full_code": "1612",
3880
+ "province_id": 37
3881
+ },
3882
+ {
3883
+ "id": 486,
3884
+ "type": "Kabupaten",
3885
+ "name": "Pesawaran",
3886
+ "code": "09",
3887
+ "full_code": "1809",
3888
+ "province_id": 19
3889
+ },
3890
+ {
3891
+ "id": 487,
3892
+ "type": "Kabupaten",
3893
+ "name": "Pesisir Barat",
3894
+ "code": "13",
3895
+ "full_code": "1813",
3896
+ "province_id": 19
3897
+ },
3898
+ {
3899
+ "id": 488,
3900
+ "type": "Kabupaten",
3901
+ "name": "Pesisir Selatan",
3902
+ "code": "01",
3903
+ "full_code": "1301",
3904
+ "province_id": 36
3905
+ },
3906
+ {
3907
+ "id": 489,
3908
+ "type": "Kabupaten",
3909
+ "name": "Pidie",
3910
+ "code": "07",
3911
+ "full_code": "1107",
3912
+ "province_id": 1
3913
+ },
3914
+ {
3915
+ "id": 490,
3916
+ "type": "Kabupaten",
3917
+ "name": "Pidie Jaya",
3918
+ "code": "18",
3919
+ "full_code": "1118",
3920
+ "province_id": 1
3921
+ },
3922
+ {
3923
+ "id": 491,
3924
+ "type": "Kabupaten",
3925
+ "name": "Pinrang",
3926
+ "code": "15",
3927
+ "full_code": "7315",
3928
+ "province_id": 32
3929
+ },
3930
+ {
3931
+ "id": 492,
3932
+ "type": "Kabupaten",
3933
+ "name": "Polewali Mandar",
3934
+ "code": "04",
3935
+ "full_code": "7604",
3936
+ "province_id": 31
3937
+ },
3938
+ {
3939
+ "id": 493,
3940
+ "type": "Kabupaten",
3941
+ "name": "Ponorogo",
3942
+ "code": "02",
3943
+ "full_code": "3502",
3944
+ "province_id": 11
3945
+ },
3946
+ {
3947
+ "id": 494,
3948
+ "type": "Kota",
3949
+ "name": "Pontianak",
3950
+ "code": "71",
3951
+ "full_code": "6171",
3952
+ "province_id": 12
3953
+ },
3954
+ {
3955
+ "id": 495,
3956
+ "type": "Kabupaten",
3957
+ "name": "Poso",
3958
+ "code": "02",
3959
+ "full_code": "7202",
3960
+ "province_id": 33
3961
+ },
3962
+ {
3963
+ "id": 496,
3964
+ "type": "Kota",
3965
+ "name": "Prabumulih",
3966
+ "code": "74",
3967
+ "full_code": "1674",
3968
+ "province_id": 37
3969
+ },
3970
+ {
3971
+ "id": 497,
3972
+ "type": "Kabupaten",
3973
+ "name": "Pringsewu",
3974
+ "code": "10",
3975
+ "full_code": "1810",
3976
+ "province_id": 19
3977
+ },
3978
+ {
3979
+ "id": 498,
3980
+ "type": "Kabupaten",
3981
+ "name": "Probolinggo",
3982
+ "code": "13",
3983
+ "full_code": "3513",
3984
+ "province_id": 11
3985
+ },
3986
+ {
3987
+ "id": 499,
3988
+ "type": "Kota",
3989
+ "name": "Probolinggo",
3990
+ "code": "74",
3991
+ "full_code": "3574",
3992
+ "province_id": 11
3993
+ },
3994
+ {
3995
+ "id": 500,
3996
+ "type": "Kabupaten",
3997
+ "name": "Pulang Pisau",
3998
+ "code": "11",
3999
+ "full_code": "6211",
4000
+ "province_id": 14
4001
+ },
4002
+ {
4003
+ "id": 501,
4004
+ "type": "Kabupaten",
4005
+ "name": "Pulau Morotai",
4006
+ "code": "07",
4007
+ "full_code": "8207",
4008
+ "province_id": 21
4009
+ },
4010
+ {
4011
+ "id": 502,
4012
+ "type": "Kabupaten",
4013
+ "name": "Pulau Taliabu",
4014
+ "code": "08",
4015
+ "full_code": "8208",
4016
+ "province_id": 21
4017
+ },
4018
+ {
4019
+ "id": 503,
4020
+ "type": "Kabupaten",
4021
+ "name": "Puncak",
4022
+ "code": "05",
4023
+ "full_code": "9405",
4024
+ "province_id": 29
4025
+ },
4026
+ {
4027
+ "id": 504,
4028
+ "type": "Kabupaten",
4029
+ "name": "Puncak Jaya",
4030
+ "code": "02",
4031
+ "full_code": "9402",
4032
+ "province_id": 29
4033
+ },
4034
+ {
4035
+ "id": 505,
4036
+ "type": "Kabupaten",
4037
+ "name": "Purbalingga",
4038
+ "code": "03",
4039
+ "full_code": "3303",
4040
+ "province_id": 10
4041
+ },
4042
+ {
4043
+ "id": 506,
4044
+ "type": "Kabupaten",
4045
+ "name": "Purwakarta",
4046
+ "code": "14",
4047
+ "full_code": "3214",
4048
+ "province_id": 9
4049
+ },
4050
+ {
4051
+ "id": 507,
4052
+ "type": "Kabupaten",
4053
+ "name": "Purworejo",
4054
+ "code": "06",
4055
+ "full_code": "3306",
4056
+ "province_id": 10
4057
+ },
4058
+ {
4059
+ "id": 508,
4060
+ "type": "Kabupaten",
4061
+ "name": "Raja Ampat",
4062
+ "code": "05",
4063
+ "full_code": "9205",
4064
+ "province_id": 26
4065
+ },
4066
+ {
4067
+ "id": 509,
4068
+ "type": "Kabupaten",
4069
+ "name": "Rejang Lebong",
4070
+ "code": "02",
4071
+ "full_code": "1702",
4072
+ "province_id": 4
4073
+ },
4074
+ {
4075
+ "id": 510,
4076
+ "type": "Kabupaten",
4077
+ "name": "Rembang",
4078
+ "code": "17",
4079
+ "full_code": "3317",
4080
+ "province_id": 10
4081
+ },
4082
+ {
4083
+ "id": 511,
4084
+ "type": "Kabupaten",
4085
+ "name": "Rokan Hilir",
4086
+ "code": "07",
4087
+ "full_code": "1407",
4088
+ "province_id": 30
4089
+ },
4090
+ {
4091
+ "id": 512,
4092
+ "type": "Kabupaten",
4093
+ "name": "Rokan Hulu",
4094
+ "code": "06",
4095
+ "full_code": "1406",
4096
+ "province_id": 30
4097
+ },
4098
+ {
4099
+ "id": 513,
4100
+ "type": "Kabupaten",
4101
+ "name": "Rote Ndao",
4102
+ "code": "14",
4103
+ "full_code": "5314",
4104
+ "province_id": 23
4105
+ },
4106
+ {
4107
+ "id": 514,
4108
+ "type": "Kota",
4109
+ "name": "Sabang",
4110
+ "code": "72",
4111
+ "full_code": "1172",
4112
+ "province_id": 1
4113
+ }
4114
+ ]
assets/seeds/province.json ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "id": 1,
4
+ "name": "Aceh (NAD)",
5
+ "code": "11"
6
+ },
7
+ {
8
+ "id": 2,
9
+ "name": "Bali",
10
+ "code": "51"
11
+ },
12
+ {
13
+ "id": 3,
14
+ "name": "Banten",
15
+ "code": "36"
16
+ },
17
+ {
18
+ "id": 4,
19
+ "name": "Bengkulu",
20
+ "code": "17"
21
+ },
22
+ {
23
+ "id": 5,
24
+ "name": "DI Yogyakarta",
25
+ "code": "34"
26
+ },
27
+ {
28
+ "id": 6,
29
+ "name": "DKI Jakarta",
30
+ "code": "31"
31
+ },
32
+ {
33
+ "id": 7,
34
+ "name": "Gorontalo",
35
+ "code": "75"
36
+ },
37
+ {
38
+ "id": 8,
39
+ "name": "Jambi",
40
+ "code": "15"
41
+ },
42
+ {
43
+ "id": 9,
44
+ "name": "Jawa Barat",
45
+ "code": "32"
46
+ },
47
+ {
48
+ "id": 10,
49
+ "name": "Jawa Tengah",
50
+ "code": "33"
51
+ },
52
+ {
53
+ "id": 11,
54
+ "name": "Jawa Timur",
55
+ "code": "35"
56
+ },
57
+ {
58
+ "id": 12,
59
+ "name": "Kalimantan Barat",
60
+ "code": "61"
61
+ },
62
+ {
63
+ "id": 13,
64
+ "name": "Kalimantan Selatan",
65
+ "code": "63"
66
+ },
67
+ {
68
+ "id": 14,
69
+ "name": "Kalimantan Tengah",
70
+ "code": "62"
71
+ },
72
+ {
73
+ "id": 15,
74
+ "name": "Kalimantan Timur",
75
+ "code": "64"
76
+ },
77
+ {
78
+ "id": 16,
79
+ "name": "Kalimantan Utara",
80
+ "code": "65"
81
+ },
82
+ {
83
+ "id": 17,
84
+ "name": "Kepulauan Bangka Belitung",
85
+ "code": "19"
86
+ },
87
+ {
88
+ "id": 18,
89
+ "name": "Kepulauan Riau",
90
+ "code": "21"
91
+ },
92
+ {
93
+ "id": 19,
94
+ "name": "Lampung",
95
+ "code": "18"
96
+ },
97
+ {
98
+ "id": 20,
99
+ "name": "Maluku",
100
+ "code": "81"
101
+ },
102
+ {
103
+ "id": 21,
104
+ "name": "Maluku Utara",
105
+ "code": "82"
106
+ },
107
+ {
108
+ "id": 22,
109
+ "name": "Nusa Tenggara Barat (NTB)",
110
+ "code": "52"
111
+ },
112
+ {
113
+ "id": 23,
114
+ "name": "Nusa Tenggara Timur (NTT)",
115
+ "code": "53"
116
+ },
117
+ {
118
+ "id": 24,
119
+ "name": "Papua",
120
+ "code": "91"
121
+ },
122
+ {
123
+ "id": 25,
124
+ "name": "Papua Barat",
125
+ "code": "92"
126
+ },
127
+ {
128
+ "id": 26,
129
+ "name": "Papua Barat Daya",
130
+ "code": "92"
131
+ },
132
+ {
133
+ "id": 27,
134
+ "name": "Papua Pegunungan",
135
+ "code": "95"
136
+ },
137
+ {
138
+ "id": 28,
139
+ "name": "Papua Selatan",
140
+ "code": "93"
141
+ },
142
+ {
143
+ "id": 29,
144
+ "name": "Papua Tengah",
145
+ "code": "94"
146
+ },
147
+ {
148
+ "id": 30,
149
+ "name": "Riau",
150
+ "code": "14"
151
+ },
152
+ {
153
+ "id": 31,
154
+ "name": "Sulawesi Barat",
155
+ "code": "76"
156
+ },
157
+ {
158
+ "id": 32,
159
+ "name": "Sulawesi Selatan",
160
+ "code": "73"
161
+ },
162
+ {
163
+ "id": 33,
164
+ "name": "Sulawesi Tengah",
165
+ "code": "72"
166
+ },
167
+ {
168
+ "id": 34,
169
+ "name": "Sulawesi Tenggara",
170
+ "code": "74"
171
+ },
172
+ {
173
+ "id": 35,
174
+ "name": "Sulawesi Utara",
175
+ "code": "71"
176
+ },
177
+ {
178
+ "id": 36,
179
+ "name": "Sumatera Barat",
180
+ "code": "13"
181
+ },
182
+ {
183
+ "id": 37,
184
+ "name": "Sumatera Selatan",
185
+ "code": "16"
186
+ },
187
+ {
188
+ "id": 38,
189
+ "name": "Sumatera Utara",
190
+ "code": "12"
191
+ }
192
+ ]
controller/options/options_controller.go ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package options_controller
2
+
3
+ import (
4
+ "net/http"
5
+
6
+ "api.qobiltu.id/models"
7
+ "api.qobiltu.id/response"
8
+ "api.qobiltu.id/services"
9
+ "github.com/gin-gonic/gin"
10
+ )
11
+
12
+ type OptionsController interface {
13
+ CreateOptions(ctx *gin.Context)
14
+ ListOptions(ctx *gin.Context)
15
+ ListOptionsBySlug(ctx *gin.Context)
16
+ }
17
+
18
+ type optionsController struct {
19
+ optionService services.OptionsService
20
+ }
21
+
22
+ func NewOptionsController(optionService services.OptionsService) OptionsController {
23
+ return &optionsController{
24
+ optionService: optionService,
25
+ }
26
+ }
27
+
28
+ func (c *optionsController) CreateOptions(ctx *gin.Context) {
29
+ var req []models.MultipleOptionsRequest
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
+ res, err := c.optionService.CreateOptions(ctx, &models.CreateOptionsRequest{
40
+ Options: req,
41
+ })
42
+ if err != nil {
43
+ response.HandleError(ctx, err)
44
+ return
45
+ }
46
+
47
+ response.HandleSuccess(ctx, http.StatusOK, "Options created successfully", res, nil)
48
+ }
49
+
50
+ func (c *optionsController) ListOptions(ctx *gin.Context) {
51
+ res, err := c.optionService.ListOptions(ctx)
52
+ if err != nil {
53
+ response.HandleError(ctx, err)
54
+ return
55
+ }
56
+
57
+ response.HandleSuccess(ctx, http.StatusOK, "Options retrieved successfully", res.Options, nil)
58
+ }
59
+
60
+ func (c *optionsController) ListOptionsBySlug(ctx *gin.Context) {
61
+ slug := ctx.Param("slug")
62
+
63
+ res, err := c.optionService.ListOptionsBySlug(ctx, &models.ListOptionsBySlugRequest{
64
+ Slug: slug,
65
+ })
66
+ if err != nil {
67
+ response.HandleError(ctx, err)
68
+ return
69
+ }
70
+
71
+ response.HandleSuccess(ctx, http.StatusOK, "Options retrieved successfully", res, nil)
72
+ }
controller/region/region_controller.go ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package cv_controller
2
+
3
+ import (
4
+ "net/http"
5
+ "strconv"
6
+
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 RegionController interface {
14
+ SeedProvince(ctx *gin.Context)
15
+ SeedCity(ctx *gin.Context)
16
+
17
+ ListProvinces(ctx *gin.Context)
18
+ ListCities(ctx *gin.Context)
19
+ }
20
+
21
+ type regionController struct {
22
+ regionService services.RegionService
23
+ }
24
+
25
+ func NewRegionController(regionService services.RegionService) RegionController {
26
+ return &regionController{
27
+ regionService: regionService,
28
+ }
29
+ }
30
+
31
+ func (c *regionController) SeedProvince(ctx *gin.Context) {
32
+ err := c.regionService.SeedProvince(ctx)
33
+ if err != nil {
34
+ response.HandleError(ctx, err)
35
+ return
36
+ }
37
+
38
+ response.HandleSuccess(ctx, http.StatusOK, "Province seeded", nil, nil)
39
+ }
40
+
41
+ func (c *regionController) SeedCity(ctx *gin.Context) {
42
+ err := c.regionService.SeedCity(ctx)
43
+ if err != nil {
44
+ response.HandleError(ctx, err)
45
+ return
46
+ }
47
+
48
+ response.HandleSuccess(ctx, http.StatusOK, "City seeded", nil, nil)
49
+ }
50
+
51
+ func (c *regionController) ListProvinces(ctx *gin.Context) {
52
+ provinces, err := c.regionService.ListProvinces(ctx)
53
+ if err != nil {
54
+ response.HandleError(ctx, err)
55
+ return
56
+ }
57
+
58
+ response.HandleSuccess(ctx, http.StatusOK, "Provinces retrieved", provinces, nil)
59
+ }
60
+
61
+ func (c *regionController) ListCities(ctx *gin.Context) {
62
+ provinceID := ctx.Query("province_id")
63
+
64
+ if provinceID == "" {
65
+ cities, err := c.regionService.ListCities(ctx)
66
+ if err != nil {
67
+ response.HandleError(ctx, err)
68
+ return
69
+ }
70
+ response.HandleSuccess(ctx, http.StatusOK, "Cities retrieved", cities, nil)
71
+ }
72
+
73
+ provinceIDInt, err := strconv.ParseInt(provinceID, 10, 64)
74
+ if err != nil {
75
+ response.HandleError(ctx, err)
76
+ return
77
+ }
78
+
79
+ req := models.ListCitiesByProvinceIdRequest{
80
+ ProvinceID: provinceIDInt,
81
+ }
82
+
83
+ cities, err := c.regionService.ListCitiesByProvinceId(ctx, &req)
84
+ if err != nil {
85
+ response.HandleError(ctx, err)
86
+ return
87
+ }
88
+
89
+ response.HandleSuccess(ctx, http.StatusOK, "Cities by province retrieved", cities, nil)
90
+ }
main.go CHANGED
@@ -9,7 +9,9 @@ import (
9
  cv_controller "api.qobiltu.id/controller/cv"
10
  email_controller "api.qobiltu.id/controller/email"
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/pkg/mail"
14
  "api.qobiltu.id/pkg/storage"
15
  "api.qobiltu.id/pkg/validation"
@@ -55,6 +57,15 @@ func main() {
55
  worker.AsyncTaskDistributor = taskDistributor
56
 
57
  // setup repo, service, and controller
 
 
 
 
 
 
 
 
 
58
  emailRepository := repositories.NewEmailRepository(config.DB)
59
  emailService := services.NewEmailService(emailRepository, taskDistributor)
60
  emailController := email_controller.NewEmailController(emailService)
@@ -78,6 +89,8 @@ func main() {
78
 
79
  // create server
80
  s, err := router.NewServer(
 
 
81
  emailController,
82
  cvController,
83
  marriageReadinessProfileController,
 
9
  cv_controller "api.qobiltu.id/controller/cv"
10
  email_controller "api.qobiltu.id/controller/email"
11
  marriage_readiness_profile_controller "api.qobiltu.id/controller/marriage_readiness_profile"
12
+ options_controller "api.qobiltu.id/controller/options"
13
  partner_criteria_controller "api.qobiltu.id/controller/partner_criteria"
14
+ region_controller "api.qobiltu.id/controller/region"
15
  "api.qobiltu.id/pkg/mail"
16
  "api.qobiltu.id/pkg/storage"
17
  "api.qobiltu.id/pkg/validation"
 
57
  worker.AsyncTaskDistributor = taskDistributor
58
 
59
  // setup repo, service, and controller
60
+
61
+ regionRepository := repositories.NewRegionRepository(config.DB)
62
+ regionService := services.NewRegionService(regionRepository)
63
+ regionController := region_controller.NewRegionController(regionService)
64
+
65
+ optionRepository := repositories.NewOptionRepository(config.DB)
66
+ optionService := services.NewOptionsService(optionRepository)
67
+ optionController := options_controller.NewOptionsController(optionService)
68
+
69
  emailRepository := repositories.NewEmailRepository(config.DB)
70
  emailService := services.NewEmailService(emailRepository, taskDistributor)
71
  emailController := email_controller.NewEmailController(emailService)
 
89
 
90
  // create server
91
  s, err := router.NewServer(
92
+ regionController,
93
+ optionController,
94
  emailController,
95
  cvController,
96
  marriageReadinessProfileController,
models/database_orm_model.go CHANGED
@@ -105,17 +105,20 @@ type AcademyContent struct {
105
  AcademyMaterialID uint `json:"academy_material_id"`
106
  Description string `json:"description"`
107
  }
 
108
  type OptionCategory struct {
109
- ID uint `gorm:"primaryKey" json:"id"`
110
- OptionName string `json:"option_name"`
111
- OptionSlug string `json:"option_slug" gorm:"uniqueIndex"`
 
112
  }
113
 
114
  type OptionValues struct {
115
- ID uint `gorm:"primaryKey" json:"id"`
116
- OptionCategoryID uint `json:"option_category_id"`
117
  OptionValue string `json:"option_value"`
118
  }
 
119
  type AcademyMaterialProgress struct {
120
  ID uint `gorm:"primaryKey" json:"id"`
121
  UUID uuid.UUID `gorm:"type:uuid" json:"uuid"`
 
105
  AcademyMaterialID uint `json:"academy_material_id"`
106
  Description string `json:"description"`
107
  }
108
+
109
  type OptionCategory struct {
110
+ ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
111
+ OptionName string `json:"option_name"`
112
+ OptionSlug string `json:"option_slug" gorm:"uniqueIndex"`
113
+ OptionValues []OptionValues `gorm:"foreignKey:OptionCategoryID" json:"-"`
114
  }
115
 
116
  type OptionValues struct {
117
+ ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
118
+ OptionCategoryID int64 `json:"option_category_id"`
119
  OptionValue string `json:"option_value"`
120
  }
121
+
122
  type AcademyMaterialProgress struct {
123
  ID uint `gorm:"primaryKey" json:"id"`
124
  UUID uuid.UUID `gorm:"type:uuid" json:"uuid"`
models/request_model.go CHANGED
@@ -28,10 +28,33 @@ type CreateVerifyEmailRequest struct {
28
  Token uint `json:"token" binding:"required"`
29
  }
30
 
31
- type OptionsRequest struct {
32
- OptionName string `json:"option_name" binding:"required"`
33
- OptionValue []string `json:"option_values" binding:"required"`
34
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
 
36
  type ExternalAuthRequest struct {
37
  OauthID string `json:"oauth_id" binding:"required"`
@@ -57,6 +80,12 @@ type AnswerQuizRequest struct {
57
  Answer int `json:"answer" binding:"required"`
58
  }
59
 
 
 
 
 
 
 
60
  type (
61
  CreateEmailVerificationRequest struct {
62
  AccountID int64 `json:"-"`
 
28
  Token uint `json:"token" binding:"required"`
29
  }
30
 
31
+ type (
32
+ MultipleOptionsRequest struct {
33
+ OptionName string `json:"option_name" validate:"required"`
34
+ OptionValue []string `json:"option_values" validate:"required"`
35
+ }
36
+
37
+ CreateOptionsRequest struct {
38
+ Options []MultipleOptionsRequest `json:"options" validate:"required"`
39
+ }
40
+
41
+ CreateOptionsResponse struct {
42
+ Options []Options `json:"options"`
43
+ }
44
+
45
+ ListOptionsResponse struct {
46
+ Options []Options `json:"options"`
47
+ }
48
+
49
+ ListOptionsBySlugRequest struct {
50
+ Slug string `json:"slug"`
51
+ }
52
+
53
+ ListOptionsBySlugResponse struct {
54
+ OptionCategory *OptionCategory `json:"option_category"`
55
+ OptionValues []OptionValues `json:"option_values"`
56
+ }
57
+ )
58
 
59
  type ExternalAuthRequest struct {
60
  OauthID string `json:"oauth_id" binding:"required"`
 
80
  Answer int `json:"answer" binding:"required"`
81
  }
82
 
83
+ type (
84
+ ListCitiesByProvinceIdRequest struct {
85
+ ProvinceID int64 `json:"province_id" binding:"required"`
86
+ }
87
+ )
88
+
89
  type (
90
  CreateEmailVerificationRequest struct {
91
  AccountID int64 `json:"-"`
models/response_model.go CHANGED
@@ -18,10 +18,12 @@ type AuthenticatedUser struct {
18
  Account Account `json:"account"`
19
  Token string `json:"token"`
20
  }
 
21
  type Options struct {
22
  OptionCategory OptionCategory `json:"option_category"`
23
  OptionValues []OptionValues `json:"option_values"`
24
  }
 
25
  type OptionsResponse struct {
26
  Options []Options `json:"options"`
27
  }
 
18
  Account Account `json:"account"`
19
  Token string `json:"token"`
20
  }
21
+
22
  type Options struct {
23
  OptionCategory OptionCategory `json:"option_category"`
24
  OptionValues []OptionValues `json:"option_values"`
25
  }
26
+
27
  type OptionsResponse struct {
28
  Options []Options `json:"options"`
29
  }
repositories/option_repository.go CHANGED
@@ -1,43 +1,89 @@
1
  package repositories
2
 
3
  import (
 
 
4
  "api.qobiltu.id/models"
 
5
  )
6
 
7
- func CreateOptionCategory(categories models.OptionCategory) Repository[models.OptionCategory, models.OptionCategory] {
8
- repo := Construct[models.OptionCategory, models.OptionCategory](
9
- categories,
10
- )
11
- Create(repo)
12
- return *repo
13
- }
14
-
15
- func CreateOptionValues(values models.OptionValues) Repository[models.OptionValues, models.OptionValues] {
16
- repo := Construct[models.OptionValues, models.OptionValues](
17
- values,
18
- )
19
- Create(repo)
20
- return *repo
21
- }
22
-
23
- func GetOptionCategoryBySlug(slug string) Repository[models.OptionCategory, models.OptionCategory] {
24
- repo := Construct[models.OptionCategory, models.OptionCategory](
25
- models.OptionCategory{OptionSlug: slug},
26
- )
27
- repo.Transactions(
28
- WhereGivenConstructor[models.OptionCategory, models.OptionCategory],
29
- Find[models.OptionCategory, models.OptionCategory],
30
- )
31
- return *repo
32
- }
33
-
34
- func GetOptionValuesByCategoryId(categoryId uint) Repository[models.OptionValues, []models.OptionValues] {
35
- repo := Construct[models.OptionValues, []models.OptionValues](
36
- models.OptionValues{OptionCategoryID: categoryId},
37
- )
38
- repo.Transactions(
39
- WhereGivenConstructor[models.OptionValues, []models.OptionValues],
40
- Find[models.OptionValues, []models.OptionValues],
41
- )
42
- return *repo
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  }
 
1
  package repositories
2
 
3
  import (
4
+ "context"
5
+
6
  "api.qobiltu.id/models"
7
+ "gorm.io/gorm"
8
  )
9
 
10
+ type OptionRepository interface {
11
+ CreateOptionCategory(ctx context.Context, optionCategory *models.OptionCategory) (*models.OptionCategory, error)
12
+ CreateOptionValues(ctx context.Context, optionValue *models.OptionValues) (*models.OptionValues, error)
13
+ GetOptionCategoryBySlug(ctx context.Context, slug string) (*models.OptionCategory, error)
14
+ GetOptionValuesByCategoryId(ctx context.Context, categoryId int64) ([]models.OptionValues, error)
15
+ ListOptions(ctx context.Context) ([]models.OptionCategory, error)
16
+ DeleteOptionValuesByCategoryId(ctx context.Context, categoryId int64) error
17
+ DeleteOptionValue(ctx context.Context, id int64) error
18
+ }
19
+
20
+ type optionRepository struct {
21
+ db *gorm.DB
22
+ }
23
+
24
+ func NewOptionRepository(db *gorm.DB) OptionRepository {
25
+ return &optionRepository{db: db}
26
+ }
27
+
28
+ func (r *optionRepository) CreateOptionCategory(ctx context.Context, optionCategory *models.OptionCategory) (*models.OptionCategory, error) {
29
+ err := r.db.Save(&optionCategory).Error
30
+ if err != nil {
31
+ return nil, err
32
+ }
33
+ return optionCategory, nil
34
+ }
35
+
36
+ func (r *optionRepository) CreateOptionValues(ctx context.Context, optionValue *models.OptionValues) (*models.OptionValues, error) {
37
+ err := r.db.Save(&optionValue).Error
38
+ if err != nil {
39
+ return nil, err
40
+ }
41
+ return optionValue, nil
42
+ }
43
+
44
+ func (r *optionRepository) GetOptionCategoryBySlug(ctx context.Context, slug string) (*models.OptionCategory, error) {
45
+ var category models.OptionCategory
46
+ err := r.db.Where("option_slug = ?", slug).First(&category).Error
47
+ if err != nil {
48
+ return nil, err
49
+ }
50
+ return &category, nil
51
+ }
52
+
53
+ func (r *optionRepository) GetOptionValuesByCategoryId(ctx context.Context, categoryId int64) ([]models.OptionValues, error) {
54
+ values := make([]models.OptionValues, 0)
55
+ err := r.db.Where("option_category_id = ?", categoryId).Find(&values).Error
56
+ if err != nil {
57
+ return nil, err
58
+ }
59
+ return values, nil
60
+ }
61
+
62
+ func (r *optionRepository) ListOptions(ctx context.Context) ([]models.OptionCategory, error) {
63
+ categories := make([]models.OptionCategory, 0)
64
+
65
+ err := r.db.WithContext(ctx).
66
+ Preload("OptionValues").
67
+ Find(&categories).Error
68
+ if err != nil {
69
+ return nil, err
70
+ }
71
+
72
+ return categories, nil
73
+ }
74
+
75
+ func (r *optionRepository) DeleteOptionValuesByCategoryId(ctx context.Context, categoryId int64) error {
76
+ err := r.db.Where("option_category_id = ?", categoryId).Delete(&models.OptionValues{}).Error
77
+ if err != nil {
78
+ return err
79
+ }
80
+ return nil
81
+ }
82
+
83
+ func (r *optionRepository) DeleteOptionValue(ctx context.Context, id int64) error {
84
+ err := r.db.Where("id = ?", id).Delete(&models.OptionValues{}).Error
85
+ if err != nil {
86
+ return err
87
+ }
88
+ return nil
89
  }
repositories/region_repository.go CHANGED
@@ -1,47 +1,57 @@
1
  package repositories
2
 
3
  import (
 
 
4
  "api.qobiltu.id/models"
 
5
  )
6
 
7
- func BulkCreateProvince(provinces []models.RegionProvince) Repository[[]models.RegionProvince, []models.RegionProvince] {
8
- repo := Construct[[]models.RegionProvince, []models.RegionProvince](
9
- provinces,
10
- )
 
 
 
 
11
 
12
- Create(repo)
13
- return *repo
14
  }
15
 
16
- func BulkCreateCity(cities []models.RegionCity) Repository[[]models.RegionCity, []models.RegionCity] {
17
- repo := Construct[[]models.RegionCity, []models.RegionCity](
18
- cities,
19
- )
20
 
21
- Create(repo)
22
- return *repo
23
  }
24
 
25
- func GetListProvinces() Repository[models.RegionProvince, []models.RegionProvince] {
26
- repo := Construct[models.RegionProvince, []models.RegionProvince](
27
- models.RegionProvince{},
28
- )
 
 
 
 
 
 
 
29
 
30
- repo.Transactions(
31
- Find[models.RegionProvince, []models.RegionProvince],
32
- )
33
- return *repo
 
 
34
  }
35
 
36
- func GetListCitiesByProvinceId(provinceId uint) Repository[models.RegionCity, []models.RegionCity] {
37
- repo := Construct[models.RegionCity, []models.RegionCity](
38
- models.RegionCity{
39
- ProvinceID: provinceId,
40
- },
41
- )
42
- repo.Transactions(
43
- WhereGivenConstructor[models.RegionCity, []models.RegionCity],
44
- Find[models.RegionCity, []models.RegionCity],
45
- )
46
- return *repo
47
  }
 
1
  package repositories
2
 
3
  import (
4
+ "context"
5
+
6
  "api.qobiltu.id/models"
7
+ "gorm.io/gorm"
8
  )
9
 
10
+ type RegionRepository interface {
11
+ CreateProvince(ctx context.Context, provinces []models.RegionProvince) error
12
+ CreateCity(ctx context.Context, cities []models.RegionCity) error
13
+
14
+ ListProvinces(ctx context.Context) ([]models.RegionProvince, error)
15
+ ListCitiesByProvinceId(ctx context.Context, provinceId int64) ([]models.RegionCity, error)
16
+ ListCities(ctx context.Context) ([]models.RegionCity, error)
17
+ }
18
 
19
+ type regionRepository struct {
20
+ db *gorm.DB
21
  }
22
 
23
+ func NewRegionRepository(db *gorm.DB) RegionRepository {
24
+ return &regionRepository{db: db}
25
+ }
 
26
 
27
+ func (r *regionRepository) CreateProvince(ctx context.Context, provinces []models.RegionProvince) error {
28
+ return r.db.Save(provinces).Error
29
  }
30
 
31
+ func (r *regionRepository) CreateCity(ctx context.Context, cities []models.RegionCity) error {
32
+ return r.db.Save(cities).Error
33
+ }
34
+
35
+ func (r *regionRepository) ListProvinces(ctx context.Context) ([]models.RegionProvince, error) {
36
+ provinces := make([]models.RegionProvince, 0)
37
+ if err := r.db.Order("id ASC").Find(&provinces).Error; err != nil {
38
+ return nil, err
39
+ }
40
+ return provinces, nil
41
+ }
42
 
43
+ func (r *regionRepository) ListCitiesByProvinceId(ctx context.Context, provinceId int64) ([]models.RegionCity, error) {
44
+ cities := make([]models.RegionCity, 0)
45
+ if err := r.db.Where("province_id = ?", provinceId).Order("id ASC").Find(&cities).Error; err != nil {
46
+ return nil, err
47
+ }
48
+ return cities, nil
49
  }
50
 
51
+ func (r *regionRepository) ListCities(ctx context.Context) ([]models.RegionCity, error) {
52
+ cities := make([]models.RegionCity, 0)
53
+ if err := r.db.Find(&cities).Error; err != nil {
54
+ return nil, err
55
+ }
56
+ return cities, nil
 
 
 
 
 
57
  }
router/options_route.go CHANGED
@@ -1,20 +1,16 @@
1
  package router
2
 
3
- import (
4
- OptionsController "api.qobiltu.id/controller/options"
5
- CityController "api.qobiltu.id/controller/region/city"
6
- ProvinceController "api.qobiltu.id/controller/region/province"
7
- "github.com/gin-gonic/gin"
8
- )
9
-
10
- func OptionsRoute(router *gin.Engine) {
11
- routerGroup := router.Group("/api/v1/options")
12
  {
13
- routerGroup.POST("/create", OptionsController.AddOptions)
14
- routerGroup.GET("/list/:slug", OptionsController.List)
15
- routerGroup.GET("/region/provinces", ProvinceController.List)
16
- routerGroup.GET("/region/cities", CityController.List)
17
- routerGroup.POST("/region/seed-provinces", ProvinceController.Seeds)
18
- routerGroup.POST("/region/seed-cities", CityController.Seeds)
 
 
 
19
  }
20
  }
 
1
  package router
2
 
3
+ func (s *Server) OptionsRoute() {
4
+ routerGroup := s.router.Group("/api/v1/options")
 
 
 
 
 
 
 
5
  {
6
+ routerGroup.POST("/create", s.optionsController.CreateOptions)
7
+ routerGroup.GET("/list", s.optionsController.ListOptions)
8
+ routerGroup.GET("/list/:slug", s.optionsController.ListOptionsBySlug)
9
+
10
+ routerGroup.GET("/region/provinces", s.regionController.ListProvinces)
11
+ routerGroup.GET("/region/cities", s.regionController.ListCities)
12
+
13
+ routerGroup.POST("/region/seed-provinces", s.regionController.SeedProvince)
14
+ routerGroup.POST("/region/seed-cities", s.regionController.SeedCity)
15
  }
16
  }
router/router.go CHANGED
@@ -10,10 +10,10 @@ func (s *Server) setupRoutes() {
10
 
11
  AuthRoute(s.router)
12
  UserRoute(s.router)
13
- OptionsRoute(s.router)
14
  AcademyRoute(s.router)
15
  QuizRoute(s.router)
16
 
 
17
  s.EmailRoute()
18
  s.CVRoute()
19
  s.MarriageReadinessProfileRoute()
 
10
 
11
  AuthRoute(s.router)
12
  UserRoute(s.router)
 
13
  AcademyRoute(s.router)
14
  QuizRoute(s.router)
15
 
16
+ s.OptionsRoute()
17
  s.EmailRoute()
18
  s.CVRoute()
19
  s.MarriageReadinessProfileRoute()
router/server.go CHANGED
@@ -4,12 +4,16 @@ import (
4
  cv_controller "api.qobiltu.id/controller/cv"
5
  email_controller "api.qobiltu.id/controller/email"
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
 
11
  type Server struct {
12
  router *gin.Engine
 
 
13
  emailController email_controller.EmailController
14
  cvController cv_controller.CVController
15
  marriageReadinessProfileController marriage_readiness_profile_controller.MarriageReadinessProfileController
@@ -17,21 +21,24 @@ type Server struct {
17
  }
18
 
19
  func NewServer(
 
 
20
  emailController email_controller.EmailController,
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()
27
  router.Use(gin.Recovery())
28
 
29
  server := &Server{
 
 
 
30
  emailController: emailController,
31
  cvController: cvController,
32
  marriageReadinessProfileController: marriageReadinessProfileController,
33
  partnerCriteriaController: partnerCriteriaController,
34
- router: router,
35
  }
36
 
37
  server.setupRoutes()
 
4
  cv_controller "api.qobiltu.id/controller/cv"
5
  email_controller "api.qobiltu.id/controller/email"
6
  marriage_readiness_profile_controller "api.qobiltu.id/controller/marriage_readiness_profile"
7
+ options_controller "api.qobiltu.id/controller/options"
8
  partner_criteria_controller "api.qobiltu.id/controller/partner_criteria"
9
+ region_controller "api.qobiltu.id/controller/region"
10
  "github.com/gin-gonic/gin"
11
  )
12
 
13
  type Server struct {
14
  router *gin.Engine
15
+ regionController region_controller.RegionController
16
+ optionsController options_controller.OptionsController
17
  emailController email_controller.EmailController
18
  cvController cv_controller.CVController
19
  marriageReadinessProfileController marriage_readiness_profile_controller.MarriageReadinessProfileController
 
21
  }
22
 
23
  func NewServer(
24
+ regionController region_controller.RegionController,
25
+ optionsController options_controller.OptionsController,
26
  emailController email_controller.EmailController,
27
  cvController cv_controller.CVController,
28
  marriageReadinessProfileController marriage_readiness_profile_controller.MarriageReadinessProfileController,
29
  partnerCriteriaController partner_criteria_controller.PartnerCriteriaController,
30
  ) (*Server, error) {
 
31
  router := gin.Default()
32
  router.Use(gin.Recovery())
33
 
34
  server := &Server{
35
+ router: router,
36
+ regionController: regionController,
37
+ optionsController: optionsController,
38
  emailController: emailController,
39
  cvController: cvController,
40
  marriageReadinessProfileController: marriageReadinessProfileController,
41
  partnerCriteriaController: partnerCriteriaController,
 
42
  }
43
 
44
  server.setupRoutes()
services/options_service.go ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package services
2
+
3
+ import (
4
+ "context"
5
+ "errors"
6
+
7
+ "api.qobiltu.id/models"
8
+ "api.qobiltu.id/repositories"
9
+ "api.qobiltu.id/response"
10
+ "github.com/gosimple/slug"
11
+ "gorm.io/gorm"
12
+ )
13
+
14
+ type OptionsService interface {
15
+ CreateOptions(ctx context.Context, req *models.CreateOptionsRequest) (*models.CreateOptionsResponse, error)
16
+ ListOptions(ctx context.Context) (*models.ListOptionsResponse, error)
17
+ ListOptionsBySlug(ctx context.Context, req *models.ListOptionsBySlugRequest) (*models.ListOptionsBySlugResponse, error)
18
+ }
19
+
20
+ type optionsService struct {
21
+ optionRepository repositories.OptionRepository
22
+ }
23
+
24
+ func NewOptionsService(optionRepository repositories.OptionRepository) OptionsService {
25
+ return &optionsService{
26
+ optionRepository: optionRepository,
27
+ }
28
+ }
29
+
30
+ func (s *optionsService) CreateOptions(ctx context.Context, req *models.CreateOptionsRequest) (*models.CreateOptionsResponse, error) {
31
+ createdOptions := make([]models.Options, 0)
32
+
33
+ for _, optReq := range req.Options {
34
+ slug := slug.Make(optReq.OptionName)
35
+
36
+ // 1. Cek apakah kategori sudah ada
37
+ category, err := s.optionRepository.GetOptionCategoryBySlug(ctx, slug)
38
+ if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
39
+ return nil, response.HandleGormError(err, "Internal server error")
40
+ }
41
+
42
+ if errors.Is(err, gorm.ErrRecordNotFound) {
43
+ // 2. Kalau belum ada, buat kategori baru
44
+ newCategory := models.OptionCategory{
45
+ OptionName: optReq.OptionName,
46
+ OptionSlug: slug,
47
+ }
48
+
49
+ category, err = s.optionRepository.CreateOptionCategory(ctx, &newCategory)
50
+ if err != nil {
51
+ return nil, response.HandleGormError(err, "Internal server error")
52
+ }
53
+ }
54
+
55
+ // 3. Ambil semua option values yang ada
56
+ existingValues, err := s.optionRepository.GetOptionValuesByCategoryId(ctx, category.ID)
57
+ if err != nil {
58
+ return nil, response.HandleGormError(err, "Internal server error")
59
+ }
60
+
61
+ // 4. Buat map untuk nilai yang sudah ada
62
+ existingValuesMap := make(map[string]bool)
63
+ for _, val := range existingValues {
64
+ existingValuesMap[val.OptionValue] = true
65
+ }
66
+
67
+ // 5. Proses setiap nilai yang diminta
68
+ for _, val := range optReq.OptionValue {
69
+ // Jika nilai belum ada, buat baru
70
+ if !existingValuesMap[val] {
71
+ _, err := s.optionRepository.CreateOptionValues(ctx, &models.OptionValues{
72
+ OptionCategoryID: category.ID,
73
+ OptionValue: val,
74
+ })
75
+ if err != nil {
76
+ return nil, response.HandleGormError(err, "Failed to create option value")
77
+ }
78
+ }
79
+ // Jika nilai sudah ada, tidak perlu dilakukan apa-apa
80
+ }
81
+
82
+ // 6. Hapus nilai yang tidak ada dalam request tapi ada di database
83
+ requestedValuesMap := make(map[string]bool)
84
+ for _, val := range optReq.OptionValue {
85
+ requestedValuesMap[val] = true
86
+ }
87
+
88
+ for _, existingVal := range existingValues {
89
+ if !requestedValuesMap[existingVal.OptionValue] {
90
+ err := s.optionRepository.DeleteOptionValue(ctx, existingVal.ID)
91
+ if err != nil {
92
+ return nil, response.HandleGormError(err, "Failed to delete unused option value")
93
+ }
94
+ }
95
+ }
96
+
97
+ // 7. Ambil ulang semua value terbaru
98
+ allValues, err := s.optionRepository.GetOptionValuesByCategoryId(ctx, category.ID)
99
+ if err != nil {
100
+ return nil, response.HandleGormError(err, "Internal server error")
101
+ }
102
+
103
+ // 8. Masukkan ke hasil response
104
+ createdOptions = append(createdOptions, models.Options{
105
+ OptionCategory: *category,
106
+ OptionValues: allValues,
107
+ })
108
+ }
109
+
110
+ return &models.CreateOptionsResponse{
111
+ Options: createdOptions,
112
+ }, nil
113
+ }
114
+
115
+ func (s *optionsService) ListOptions(ctx context.Context) (*models.ListOptionsResponse, error) {
116
+ listOptions, err := s.optionRepository.ListOptions(ctx)
117
+ if err != nil {
118
+ return nil, response.HandleGormError(err, "Internal server error")
119
+ }
120
+
121
+ options := make([]models.Options, 0)
122
+ for _, option := range listOptions {
123
+ options = append(options, models.Options{
124
+ OptionCategory: option,
125
+ OptionValues: option.OptionValues,
126
+ })
127
+ }
128
+
129
+ return &models.ListOptionsResponse{
130
+ Options: options,
131
+ }, nil
132
+ }
133
+
134
+ func (s *optionsService) ListOptionsBySlug(ctx context.Context, req *models.ListOptionsBySlugRequest) (*models.ListOptionsBySlugResponse, error) {
135
+ optionCategory, err := s.optionRepository.GetOptionCategoryBySlug(ctx, req.Slug)
136
+ if err != nil {
137
+ return nil, response.HandleGormError(err, "Internal server error")
138
+ }
139
+
140
+ optionValues, err := s.optionRepository.GetOptionValuesByCategoryId(ctx, optionCategory.ID)
141
+ if err != nil {
142
+ return nil, response.HandleGormError(err, "Internal server error")
143
+ }
144
+
145
+ return &models.ListOptionsBySlugResponse{
146
+ OptionCategory: optionCategory,
147
+ OptionValues: optionValues,
148
+ }, nil
149
+ }
services/region_service.go CHANGED
@@ -1,90 +1,68 @@
1
  package services
2
 
3
  import (
 
4
  "encoding/json"
5
- "log"
6
- "os"
7
- "path/filepath"
8
- "runtime"
9
 
 
10
  "api.qobiltu.id/models"
11
  "api.qobiltu.id/repositories"
 
12
  )
13
 
14
- type ProvinceService struct {
15
- Service[models.RegionProvince, []models.RegionProvince]
 
 
 
 
 
16
  }
17
 
18
- type CityService struct {
19
- Service[models.RegionCity, []models.RegionCity]
20
  }
21
 
22
- func seedCity() ([]models.RegionCity, error) {
23
- log.Println("Seed City")
24
- _, b, _, _ := runtime.Caller(0)
25
- basePath := filepath.Dir(b)
26
- file, err := os.Open(filepath.Join(basePath, "..", "utils", "seeds", "city.json"))
27
- if err != nil {
28
- return nil, err
29
- }
30
- defer file.Close()
31
- var cities []models.RegionCity
32
- if err := json.NewDecoder(file).Decode(&cities); err != nil {
33
- return nil, err
34
- }
35
- return cities, nil
36
  }
37
 
38
- func seedProvince() ([]models.RegionProvince, error) {
39
- log.Println("Seed City")
40
- _, b, _, _ := runtime.Caller(0)
41
- basePath := filepath.Dir(b)
42
- file, err := os.Open(filepath.Join(basePath, "..", "utils", "seeds", "province.json"))
43
- if err != nil {
44
- return nil, err
45
  }
46
- defer file.Close()
47
- var provinces []models.RegionProvince
48
- if err := json.NewDecoder(file).Decode(&provinces); err != nil {
49
- return nil, err
50
  }
51
- return provinces, nil
 
52
  }
53
 
54
- func (s *ProvinceService) Create() {
55
- provinces, errSeed := seedProvince()
56
- if errSeed != nil {
57
- s.Error = errSeed
58
- s.Exception.InternalServerError = true
59
- s.Exception.Message = "Failed to seed province"
60
- return
61
  }
62
- createProvince := repositories.BulkCreateProvince(provinces)
63
- s.Error = createProvince.RowsError
64
- s.Result = createProvince.Result
65
- }
66
 
67
- func (s *CityService) Create() {
68
- cities, errSeed := seedCity()
69
- if errSeed != nil {
70
- s.Error = errSeed
71
- s.Exception.InternalServerError = true
72
- s.Exception.Message = "Failed to seed province"
73
- return
74
  }
75
- createCity := repositories.BulkCreateCity(cities)
76
- s.Error = createCity.RowsError
77
- s.Result = createCity.Result
 
 
 
78
  }
79
 
80
- func (s *ProvinceService) Retrieve() {
81
- Province := repositories.GetListProvinces()
82
- s.Error = Province.RowsError
83
- s.Result = Province.Result
84
  }
85
 
86
- func (s *CityService) Retrieve() {
87
- cities := repositories.GetListCitiesByProvinceId(s.Constructor.ProvinceID)
88
- s.Error = cities.RowsError
89
- s.Result = cities.Result
90
  }
 
1
  package services
2
 
3
  import (
4
+ "context"
5
  "encoding/json"
 
 
 
 
6
 
7
+ "api.qobiltu.id/assets"
8
  "api.qobiltu.id/models"
9
  "api.qobiltu.id/repositories"
10
+ "api.qobiltu.id/response"
11
  )
12
 
13
+ type RegionService interface {
14
+ SeedProvince(ctx context.Context) error
15
+ SeedCity(ctx context.Context) error
16
+
17
+ ListProvinces(ctx context.Context) ([]models.RegionProvince, error)
18
+ ListCitiesByProvinceId(ctx context.Context, req *models.ListCitiesByProvinceIdRequest) ([]models.RegionCity, error)
19
+ ListCities(ctx context.Context) ([]models.RegionCity, error)
20
  }
21
 
22
+ type regionService struct {
23
+ regionRepository repositories.RegionRepository
24
  }
25
 
26
+ func NewRegionService(regionRepository repositories.RegionRepository) RegionService {
27
+ return &regionService{regionRepository: regionRepository}
 
 
 
 
 
 
 
 
 
 
 
 
28
  }
29
 
30
+ func (s *regionService) SeedProvince(ctx context.Context) error {
31
+ provinces := make([]models.RegionProvince, 0)
32
+
33
+ if err := json.Unmarshal(assets.EmbeddedProvinceJSON, &provinces); err != nil {
34
+ return response.HandleGormError(err, "Internal Server Error")
 
 
35
  }
36
+
37
+ if err := s.regionRepository.CreateProvince(ctx, provinces); err != nil {
38
+ return response.HandleGormError(err, "Internal Server Error")
 
39
  }
40
+
41
+ return nil
42
  }
43
 
44
+ func (s *regionService) SeedCity(ctx context.Context) error {
45
+ cities := make([]models.RegionCity, 0)
46
+
47
+ if err := json.Unmarshal(assets.EmbeddedCityJSON, &cities); err != nil {
48
+ return response.HandleGormError(err, "Internal Server Error")
 
 
49
  }
 
 
 
 
50
 
51
+ if err := s.regionRepository.CreateCity(ctx, cities); err != nil {
52
+ return response.HandleGormError(err, "Internal Server Error")
 
 
 
 
 
53
  }
54
+
55
+ return nil
56
+ }
57
+
58
+ func (s *regionService) ListProvinces(ctx context.Context) ([]models.RegionProvince, error) {
59
+ return s.regionRepository.ListProvinces(ctx)
60
  }
61
 
62
+ func (s *regionService) ListCitiesByProvinceId(ctx context.Context, req *models.ListCitiesByProvinceIdRequest) ([]models.RegionCity, error) {
63
+ return s.regionRepository.ListCitiesByProvinceId(ctx, req.ProvinceID)
 
 
64
  }
65
 
66
+ func (s *regionService) ListCities(ctx context.Context) ([]models.RegionCity, error) {
67
+ return s.regionRepository.ListCities(ctx)
 
 
68
  }
space/services/academy_quiz_navigation_service.go CHANGED
@@ -12,7 +12,7 @@ type NavigationQuizService struct {
12
  }
13
 
14
  func (s *NavigationQuizService) Retrieve(userID uint) {
15
- attemptRepo := repositories.GetAttemptByUserId(userID)
16
  if attemptRepo.NoRecord {
17
  s.Exception.DataNotFound = true
18
  s.Exception.Message = "there is no attempt data with given user ID!"
 
12
  }
13
 
14
  func (s *NavigationQuizService) Retrieve(userID uint) {
15
+ attemptRepo := repositories.GetAttemptByIdandUserId(s.Constructor.QuizAttemptID, userID)
16
  if attemptRepo.NoRecord {
17
  s.Exception.DataNotFound = true
18
  s.Exception.Message = "there is no attempt data with given user ID!"
space/space/space/controller/quiz/navigation_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 Navigation(c *gin.Context) {
13
+ navigationQuiz := services.NavigationQuizService{}
14
+ navigationQuizController := controller.Controller[any, models.UserAnswer, []models.OnExamUserAnswerResponse]{
15
+ Service: &navigationQuiz.Service,
16
+ }
17
+
18
+ navigationQuizController.HeaderParse(c, func() {
19
+ attempt_id, _ := strconv.Atoi(c.Param("attempt_id"))
20
+ navigationQuiz.Constructor.QuizAttemptID = uint(attempt_id)
21
+ navigationQuiz.Retrieve(navigationQuizController.AccountData.UserID)
22
+ navigationQuizController.Response(c)
23
+ })
24
+ }
space/space/space/models/database_orm_model.go CHANGED
@@ -156,7 +156,7 @@ type Question struct {
156
  QuizID uint `json:"quiz_id"`
157
  Content string `json:"content"`
158
  Order int `json:"order"`
159
- CorrectAnswer uint `json:"corrent_answer"`
160
  Review string `json:"reviews"`
161
  }
162
  type Quiz struct {
@@ -186,6 +186,7 @@ type UserAnswer struct {
186
  QuizAttemptID uint `json:"quiz_attempt_id"`
187
  QuestionID uint `json:"question_id"`
188
  SelectedAnswer uint `json:"selected_answer"`
 
189
  IsCorrect bool `json:"is_correct"`
190
  }
191
  type QuizResult struct {
 
156
  QuizID uint `json:"quiz_id"`
157
  Content string `json:"content"`
158
  Order int `json:"order"`
159
+ CorrectAnswer uint `json:"correct_answer"`
160
  Review string `json:"reviews"`
161
  }
162
  type Quiz struct {
 
186
  QuizAttemptID uint `json:"quiz_attempt_id"`
187
  QuestionID uint `json:"question_id"`
188
  SelectedAnswer uint `json:"selected_answer"`
189
+ IsDoubt bool `json:"id_doubt"`
190
  IsCorrect bool `json:"is_correct"`
191
  }
192
  type QuizResult struct {
space/space/space/models/response_model.go CHANGED
@@ -53,9 +53,18 @@ type QuestionResponse struct {
53
  Question Question `json:"question"`
54
  Answer []Answer `json:"answer_options"`
55
  UserAnswer int `json:"current_user_answer"`
 
56
  }
57
 
58
  type QuizResultResponse struct {
59
  QuizAttempt QuizAttempt `json:"quiz_attempt"`
60
  Result QuizResult `json:"result"`
61
  }
 
 
 
 
 
 
 
 
 
53
  Question Question `json:"question"`
54
  Answer []Answer `json:"answer_options"`
55
  UserAnswer int `json:"current_user_answer"`
56
+ IsDoubt bool `json:"is_doubt"`
57
  }
58
 
59
  type QuizResultResponse struct {
60
  QuizAttempt QuizAttempt `json:"quiz_attempt"`
61
  Result QuizResult `json:"result"`
62
  }
63
+
64
+ type OnExamUserAnswerResponse struct {
65
+ ID uint `gorm:"primaryKey" json:"id"`
66
+ QuizAttemptID uint `json:"quiz_attempt_id"`
67
+ QuestionID uint `json:"question_id"`
68
+ SelectedAnswer uint `json:"selected_answer"`
69
+ IsDoubt bool `json:"is_doubt"`
70
+ }
space/space/space/repositories/quiz_repository.go CHANGED
@@ -132,3 +132,12 @@ func CountUserAttemptScore(attemptId uint) Repository[models.QuizAttempt, models
132
  repo.NoRecord = false
133
  return *repo
134
  }
 
 
 
 
 
 
 
 
 
 
132
  repo.NoRecord = false
133
  return *repo
134
  }
135
+
136
+ func GetUserAnswerByAttemptId(attemptId uint) Repository[models.UserAnswer, []models.OnExamUserAnswerResponse] {
137
+ repo := Construct[models.UserAnswer, []models.OnExamUserAnswerResponse](
138
+ models.UserAnswer{QuizAttemptID: attemptId},
139
+ )
140
+
141
+ repo.Transaction.Model(&repo.Constructor).Find(&repo.Result)
142
+ return *repo
143
+ }
space/space/space/router/quiz_route.go CHANGED
@@ -17,5 +17,6 @@ func QuizRoute(router *gin.Engine) {
17
  routerGroup.GET("result/:attempt_id", middleware.AuthUser, QuizController.Result)
18
  routerGroup.GET("result/", middleware.AuthUser, QuizController.Result)
19
  routerGroup.POST("/submit-attempt/:attempt_id", middleware.AuthUser, QuizController.Submit)
 
20
  }
21
  }
 
17
  routerGroup.GET("result/:attempt_id", middleware.AuthUser, QuizController.Result)
18
  routerGroup.GET("result/", middleware.AuthUser, QuizController.Result)
19
  routerGroup.POST("/submit-attempt/:attempt_id", middleware.AuthUser, QuizController.Submit)
20
+ routerGroup.GET("/navigation/:attempt_id", middleware.AuthUser, QuizController.Navigation)
21
  }
22
  }
space/space/space/services/academy_quiz_answer_service.go CHANGED
@@ -31,8 +31,11 @@ func (s *AnswerQuizService) Update(userID uint, questionNo int, answer int) {
31
  return
32
  }
33
  s.Error = errors.Join(s.Error, answerRepo.RowsError)
34
-
35
- answerRepo.Result.SelectedAnswer = uint(answer)
 
 
 
36
  answerRepo.Result.IsCorrect = (questionRepo.Result.CorrectAnswer == uint(answer))
37
 
38
  updatedAnswer := repositories.UpdateUserAnswer(answerRepo.Result)
@@ -50,6 +53,7 @@ func (s *AnswerQuizService) Update(userID uint, questionNo int, answer int) {
50
  Question: questionRepo.Result,
51
  Answer: answerOptionRepo.Result,
52
  UserAnswer: int(answerRepo.Result.SelectedAnswer),
 
53
  }
54
  return
55
  })
 
31
  return
32
  }
33
  s.Error = errors.Join(s.Error, answerRepo.RowsError)
34
+ if answer >= 0 {
35
+ answerRepo.Result.SelectedAnswer = uint(answer)
36
+ } else if answer == -1 {
37
+ answerRepo.Result.IsDoubt = true
38
+ }
39
  answerRepo.Result.IsCorrect = (questionRepo.Result.CorrectAnswer == uint(answer))
40
 
41
  updatedAnswer := repositories.UpdateUserAnswer(answerRepo.Result)
 
53
  Question: questionRepo.Result,
54
  Answer: answerOptionRepo.Result,
55
  UserAnswer: int(answerRepo.Result.SelectedAnswer),
56
+ IsDoubt: (answer == -1),
57
  }
58
  return
59
  })
space/space/space/services/academy_quiz_navigation_service.go ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package services
2
+
3
+ import (
4
+ "errors"
5
+
6
+ "api.qobiltu.id/models"
7
+ "api.qobiltu.id/repositories"
8
+ )
9
+
10
+ type NavigationQuizService struct {
11
+ Service[models.UserAnswer, []models.OnExamUserAnswerResponse]
12
+ }
13
+
14
+ func (s *NavigationQuizService) Retrieve(userID uint) {
15
+ attemptRepo := repositories.GetAttemptByUserId(userID)
16
+ if attemptRepo.NoRecord {
17
+ s.Exception.DataNotFound = true
18
+ s.Exception.Message = "there is no attempt data with given user ID!"
19
+ return
20
+ }
21
+ s.Error = attemptRepo.RowsError
22
+ userAnswersRepo := repositories.GetUserAnswerByAttemptId(s.Constructor.QuizAttemptID)
23
+ if userAnswersRepo.NoRecord {
24
+ s.Exception.DataNotFound = true
25
+ s.Exception.Message = "there is no user answers with given attempt id!"
26
+ return
27
+ }
28
+ s.Error = errors.Join(s.Error, userAnswersRepo.RowsError)
29
+ s.Result = userAnswersRepo.Result
30
+ return
31
+ }
space/space/space/space/pkg/mail/sender.go ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ package mail
2
+
3
+ type Sender interface {
4
+ Send(recipient, subject string, htmlContent string, data any) error
5
+ }
6
+
7
+ // EmailSender is a global variable to hold the email sender
8
+ var EmailSender Sender
space/space/space/space/pkg/mail/smtp.go ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package mail
2
+
3
+ import (
4
+ "errors"
5
+ "fmt"
6
+ "github.com/jordan-wright/email"
7
+ "net/mail"
8
+ "net/smtp"
9
+ )
10
+
11
+ var (
12
+ ErrEmailEmpty = errors.New("mail is empty")
13
+ ErrEmailInvalid = errors.New("invalid email")
14
+ )
15
+
16
+ // Config menyimpan pengaturan untuk koneksi SMTP.
17
+ type Config struct {
18
+ Host string
19
+ Port string
20
+ Username string
21
+ Password string
22
+ From string
23
+ }
24
+
25
+ // SMTP adalah implementasi Sender untuk mengirim mail melalui SMTP
26
+ type SMTP struct {
27
+ name string
28
+ fromEmailAddress string
29
+ smtpServerAddress string
30
+ smtpAuthAddress smtp.Auth
31
+ }
32
+
33
+ // New membuat instance baru SMTP dengan konfigurasi.
34
+ func New(cfg *Config) (Sender, error) {
35
+ if err := validateEmail(cfg.From); err != nil {
36
+ return nil, fmt.Errorf("invalid from address: %w", err)
37
+ }
38
+
39
+ return &SMTP{
40
+ name: cfg.From,
41
+ fromEmailAddress: cfg.From,
42
+ smtpServerAddress: fmt.Sprintf("%s:%s", cfg.Host, cfg.Port),
43
+ smtpAuthAddress: smtp.PlainAuth("", cfg.Username, cfg.Password, cfg.Host),
44
+ }, nil
45
+ }
46
+
47
+ // Send mengirim mail ke penerima dengan subjek, konten HTML, dan data lainnya.
48
+ func (s *SMTP) Send(recipient, subject string, htmlContent string, data any) error {
49
+ e := email.NewEmail()
50
+ e.From = s.fromEmailAddress
51
+ e.To = []string{recipient}
52
+ e.Subject = subject
53
+ e.HTML = []byte(htmlContent)
54
+ return s.sendEmail(e)
55
+ }
56
+
57
+ func (s *SMTP) sendEmail(e *email.Email) error {
58
+ return e.Send(s.smtpServerAddress, s.smtpAuthAddress)
59
+ }
60
+
61
+ func validateEmail(email string) error {
62
+ if email == "" {
63
+ return ErrEmailEmpty
64
+ }
65
+
66
+ if _, err := mail.ParseAddress(email); err != nil {
67
+ return ErrEmailInvalid
68
+ }
69
+
70
+ return nil
71
+ }
space/space/space/space/space/pkg/worker/processor.go CHANGED
@@ -1,11 +1,12 @@
1
  package worker
2
 
3
  import (
4
- "api.qobiltu.id/mail"
5
  "context"
 
 
 
6
  "github.com/hibiken/asynq"
7
  "github.com/redis/go-redis/v9"
8
- "log/slog"
9
  )
10
 
11
  const (
 
1
  package worker
2
 
3
  import (
 
4
  "context"
5
+ "log/slog"
6
+
7
+ "api.qobiltu.id/pkg/mail"
8
  "github.com/hibiken/asynq"
9
  "github.com/redis/go-redis/v9"
 
10
  )
11
 
12
  const (
space/space/space/space/space/space/controller/email/email_controller.go ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package email_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 EmailController interface {
14
+ CreateEmailVerification(ctx *gin.Context)
15
+ Verify(ctx *gin.Context)
16
+ }
17
+
18
+ type emailController struct {
19
+ emailService services.EmailService
20
+ }
21
+
22
+ func NewEmailController(emailService services.EmailService) EmailController {
23
+ return &emailController{
24
+ emailService: emailService,
25
+ }
26
+ }
27
+
28
+ func (c *emailController) CreateEmailVerification(ctx *gin.Context) {
29
+ accountData := middleware.GetAccountData(ctx)
30
+ req := models.CreateEmailVerificationRequest{
31
+ AccountID: int64(accountData.UserID),
32
+ }
33
+
34
+ res, err := c.emailService.CreateEmailVerification(ctx, &req)
35
+ if err != nil {
36
+ response.HandleError(ctx, err)
37
+ return
38
+ }
39
+
40
+ response.HandleSuccess(ctx, http.StatusOK, "Email verification created", res, nil)
41
+ }
42
+
43
+ func (c *emailController) Verify(ctx *gin.Context) {
44
+ var req models.ValidateEmailVerificationRequest
45
+ if err := ctx.ShouldBindJSON(&req); err != nil {
46
+ response.HandleError(ctx, models.Exception{
47
+ Message: "Invalid body request",
48
+ BadRequest: true,
49
+ Err: err,
50
+ })
51
+ return
52
+ }
53
+
54
+ accountData := middleware.GetAccountData(ctx)
55
+ req.AccountID = int64(accountData.UserID)
56
+
57
+ res, err := c.emailService.ValidateEmailVerification(ctx, &req)
58
+ if err != nil {
59
+ response.HandleError(ctx, err)
60
+ return
61
+ }
62
+
63
+ response.HandleSuccess(ctx, http.StatusOK, "Email verified", res, nil)
64
+ }
space/space/space/space/space/space/main.go CHANGED
@@ -7,9 +7,10 @@ import (
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"
13
  "api.qobiltu.id/pkg/storage"
14
  "api.qobiltu.id/pkg/validation"
15
  "api.qobiltu.id/pkg/worker"
@@ -54,6 +55,10 @@ func main() {
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,6 +78,7 @@ func main() {
73
 
74
  // create server
75
  s, err := router.NewServer(
 
76
  cvController,
77
  marriageReadinessProfileController,
78
  partnerCriteriaController,
 
7
 
8
  "api.qobiltu.id/config"
9
  cv_controller "api.qobiltu.id/controller/cv"
10
+ email_controller "api.qobiltu.id/controller/email"
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/pkg/mail"
14
  "api.qobiltu.id/pkg/storage"
15
  "api.qobiltu.id/pkg/validation"
16
  "api.qobiltu.id/pkg/worker"
 
55
  worker.AsyncTaskDistributor = taskDistributor
56
 
57
  // setup repo, service, and controller
58
+ emailRepository := repositories.NewEmailRepository(config.DB)
59
+ emailService := services.NewEmailService(emailRepository, taskDistributor)
60
+ emailController := email_controller.NewEmailController(emailService)
61
+
62
  cvRepository := repositories.NewCVRepository(config.DB)
63
  cvService := services.NewCVService(cvRepository, localStorage, validator)
64
  cvController := cv_controller.NewCVController(cvService)
 
78
 
79
  // create server
80
  s, err := router.NewServer(
81
+ emailController,
82
  cvController,
83
  marriageReadinessProfileController,
84
  partnerCriteriaController,
space/space/space/space/space/space/models/database_orm_model.go CHANGED
@@ -38,13 +38,15 @@ type AccountDetails struct {
38
  }
39
 
40
  type EmailVerification struct {
41
- ID uint `gorm:"primaryKey" json:"id"`
 
 
42
  UUID uuid.UUID `gorm:"type:uuid" json:"uuid" `
43
- Token uint `json:"token"`
44
- AccountID uint `json:"account_id"`
45
- IsExpired bool `json:"is_expired"`
46
- CreatedAt time.Time `json:"created_at"`
47
  ExpiredAt time.Time `json:"expired_at"`
 
 
 
48
  }
49
 
50
  type ExternalAuth struct {
 
38
  }
39
 
40
  type EmailVerification struct {
41
+ ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
42
+ AccountID int64 `gorm:"column:account_id;not null" json:"account_id"`
43
+ Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"`
44
  UUID uuid.UUID `gorm:"type:uuid" json:"uuid" `
45
+ Token int64 `json:"token"`
 
 
 
46
  ExpiredAt time.Time `json:"expired_at"`
47
+ IsExpired bool `json:"is_expired"`
48
+ CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"`
49
+ UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"`
50
  }
51
 
52
  type ExternalAuth struct {
space/space/space/space/space/space/models/request_model.go CHANGED
@@ -57,6 +57,17 @@ type AnswerQuizRequest struct {
57
  Answer int `json:"answer" binding:"required"`
58
  }
59
 
 
 
 
 
 
 
 
 
 
 
 
60
  type (
61
  SavePersonalityAndPreferenceRequest struct {
62
  AccountID int64 `json:"-"`
 
57
  Answer int `json:"answer" binding:"required"`
58
  }
59
 
60
+ type (
61
+ CreateEmailVerificationRequest struct {
62
+ AccountID int64 `json:"-"`
63
+ }
64
+
65
+ ValidateEmailVerificationRequest struct {
66
+ AccountID int64 `json:"-"`
67
+ Token int64 `json:"token" validate:"required"`
68
+ }
69
+ )
70
+
71
  type (
72
  SavePersonalityAndPreferenceRequest struct {
73
  AccountID int64 `json:"-"`
space/space/space/space/space/space/repositories/email_repository.go ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package repositories
2
+
3
+ import (
4
+ "context"
5
+
6
+ "api.qobiltu.id/models"
7
+ "gorm.io/gorm"
8
+ )
9
+
10
+ type EmailRepository interface {
11
+ SaveEmailVerification(ctx context.Context, req *models.EmailVerification) (*models.EmailVerification, error)
12
+ GetEmailVerification(ctx context.Context, accountID int64, token int64) (*models.EmailVerification, error)
13
+ DeleteEmailVerification(ctx context.Context, id int64) error
14
+
15
+ // later, will relocate to account repository
16
+ GetAccountById(ctx context.Context, id int64) (*models.Account, error)
17
+ SaveAccount(ctx context.Context, req *models.Account) (*models.Account, error)
18
+ }
19
+
20
+ type emailRepository struct {
21
+ db *gorm.DB
22
+ }
23
+
24
+ func NewEmailRepository(db *gorm.DB) EmailRepository {
25
+ return &emailRepository{db: db}
26
+ }
27
+
28
+ func (r *emailRepository) SaveEmailVerification(ctx context.Context, req *models.EmailVerification) (*models.EmailVerification, error) {
29
+ err := r.db.WithContext(ctx).Save(req).Error
30
+ if err != nil {
31
+ return nil, err
32
+ }
33
+ return req, nil
34
+ }
35
+
36
+ func (r *emailRepository) GetEmailVerification(ctx context.Context, accountID int64, token int64) (*models.EmailVerification, error) {
37
+ var emailVerification models.EmailVerification
38
+ err := r.db.WithContext(ctx).Where("account_id = ? AND token = ?", accountID, token).First(&emailVerification).Error
39
+ if err != nil {
40
+ return nil, err
41
+ }
42
+
43
+ return &emailVerification, nil
44
+ }
45
+
46
+ func (r *emailRepository) DeleteEmailVerification(ctx context.Context, id int64) error {
47
+ return r.db.WithContext(ctx).Delete(&models.EmailVerification{}, id).Error
48
+ }
49
+
50
+ func (r *emailRepository) GetAccountById(ctx context.Context, id int64) (*models.Account, error) {
51
+ var account models.Account
52
+ err := r.db.WithContext(ctx).Where("id = ?", id).First(&account).Error
53
+ if err != nil {
54
+ return nil, err
55
+ }
56
+ return &account, nil
57
+ }
58
+
59
+ func (r *emailRepository) SaveAccount(ctx context.Context, req *models.Account) (*models.Account, error) {
60
+ err := r.db.WithContext(ctx).Save(req).Error
61
+ if err != nil {
62
+ return nil, err
63
+ }
64
+ return req, nil
65
+ }
space/space/space/space/space/space/router/email_route.go CHANGED
@@ -1,15 +1,13 @@
1
  package router
2
 
3
  import (
4
- EmailController "api.qobiltu.id/controller/email"
5
  "api.qobiltu.id/middleware"
6
- "github.com/gin-gonic/gin"
7
  )
8
 
9
- func EmailRoute(router *gin.Engine) {
10
- routerGroup := router.Group("/api/v1/email")
11
  {
12
- routerGroup.POST("/verify", middleware.AuthUser, EmailController.Verify)
13
- routerGroup.POST("/create-verification", middleware.AuthUser, EmailController.CreateVerification)
14
  }
15
  }
 
1
  package router
2
 
3
  import (
 
4
  "api.qobiltu.id/middleware"
 
5
  )
6
 
7
+ func (s *Server) EmailRoute() {
8
+ routerGroup := s.router.Group("/api/v1/email").Use(middleware.AuthUser)
9
  {
10
+ routerGroup.POST("/create-verification", s.emailController.CreateEmailVerification)
11
+ routerGroup.POST("/verify", s.emailController.Verify)
12
  }
13
  }
space/space/space/space/space/space/router/router.go CHANGED
@@ -10,12 +10,11 @@ func (s *Server) setupRoutes() {
10
 
11
  AuthRoute(s.router)
12
  UserRoute(s.router)
13
- EmailRoute(s.router)
14
  OptionsRoute(s.router)
15
  AcademyRoute(s.router)
16
  QuizRoute(s.router)
17
 
18
- // another way to register routes
19
  s.CVRoute()
20
  s.MarriageReadinessProfileRoute()
21
  s.PartnerCriteriaRoute()
 
10
 
11
  AuthRoute(s.router)
12
  UserRoute(s.router)
 
13
  OptionsRoute(s.router)
14
  AcademyRoute(s.router)
15
  QuizRoute(s.router)
16
 
17
+ s.EmailRoute()
18
  s.CVRoute()
19
  s.MarriageReadinessProfileRoute()
20
  s.PartnerCriteriaRoute()
space/space/space/space/space/space/router/server.go CHANGED
@@ -2,6 +2,7 @@ package router
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,12 +10,14 @@ import (
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,6 +27,7 @@ func NewServer(
24
  router.Use(gin.Recovery())
25
 
26
  server := &Server{
 
27
  cvController: cvController,
28
  marriageReadinessProfileController: marriageReadinessProfileController,
29
  partnerCriteriaController: partnerCriteriaController,
 
2
 
3
  import (
4
  cv_controller "api.qobiltu.id/controller/cv"
5
+ email_controller "api.qobiltu.id/controller/email"
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
 
11
  type Server struct {
12
  router *gin.Engine
13
+ emailController email_controller.EmailController
14
  cvController cv_controller.CVController
15
  marriageReadinessProfileController marriage_readiness_profile_controller.MarriageReadinessProfileController
16
  partnerCriteriaController partner_criteria_controller.PartnerCriteriaController
17
  }
18
 
19
  func NewServer(
20
+ emailController email_controller.EmailController,
21
  cvController cv_controller.CVController,
22
  marriageReadinessProfileController marriage_readiness_profile_controller.MarriageReadinessProfileController,
23
  partnerCriteriaController partner_criteria_controller.PartnerCriteriaController,
 
27
  router.Use(gin.Recovery())
28
 
29
  server := &Server{
30
+ emailController: emailController,
31
  cvController: cvController,
32
  marriageReadinessProfileController: marriageReadinessProfileController,
33
  partnerCriteriaController: partnerCriteriaController,
space/space/space/space/space/space/services/email_service.go ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package services
2
+
3
+ import (
4
+ "context"
5
+ "strconv"
6
+ "time"
7
+
8
+ "api.qobiltu.id/config"
9
+ "api.qobiltu.id/pkg/worker"
10
+ "api.qobiltu.id/response"
11
+ "api.qobiltu.id/utils"
12
+ "github.com/hibiken/asynq"
13
+ uuid "github.com/satori/go.uuid"
14
+
15
+ "api.qobiltu.id/models"
16
+ "api.qobiltu.id/repositories"
17
+ )
18
+
19
+ type EmailService interface {
20
+ CreateEmailVerification(ctx context.Context, req *models.CreateEmailVerificationRequest) (*models.EmailVerification, error)
21
+ ValidateEmailVerification(ctx context.Context, req *models.ValidateEmailVerificationRequest) (*models.EmailVerification, error)
22
+ }
23
+
24
+ type emailService struct {
25
+ emailRepository repositories.EmailRepository
26
+ taskDistributor worker.TaskDistributor
27
+ }
28
+
29
+ func NewEmailService(emailRepository repositories.EmailRepository, taskDistributor worker.TaskDistributor) EmailService {
30
+ return &emailService{emailRepository: emailRepository, taskDistributor: taskDistributor}
31
+ }
32
+
33
+ func (s *emailService) CreateEmailVerification(ctx context.Context, req *models.CreateEmailVerificationRequest) (*models.EmailVerification, error) {
34
+ account, err := s.emailRepository.GetAccountById(ctx, req.AccountID)
35
+ if err != nil {
36
+ return nil, response.HandleGormError(err, "Internal Server Error")
37
+ }
38
+
39
+ token, err := utils.GenerateToken()
40
+ if err != nil {
41
+ return nil, models.Exception{
42
+ InternalServerError: true,
43
+ Message: "failed to generate token for email verification",
44
+ }
45
+ }
46
+
47
+ remainingTime := time.Duration(config.EMAIL_VERIFICATION_DURATION) * time.Minute
48
+ dueTime := time.Now().Add(remainingTime)
49
+
50
+ payload := models.EmailVerification{
51
+ AccountID: int64(account.Id),
52
+ Token: token,
53
+ UUID: uuid.NewV4(),
54
+ ExpiredAt: dueTime,
55
+ IsExpired: false,
56
+ }
57
+
58
+ emailVerification, err := s.emailRepository.SaveEmailVerification(ctx, &payload)
59
+ if err != nil {
60
+ return nil, response.HandleGormError(err, "Internal Server Error")
61
+ }
62
+
63
+ opts := []asynq.Option{
64
+ asynq.MaxRetry(worker.TaskSendVerifyEmailMaxRetry),
65
+ asynq.Queue(worker.Critical),
66
+ }
67
+
68
+ err = s.taskDistributor.DistributeTaskSendVerifyEmail(
69
+ context.Background(),
70
+ &worker.PayloadSendVerifyEmail{
71
+ EmailAddress: account.Email,
72
+ VerificationCode: strconv.Itoa(int(token)),
73
+ ExpirationInMinutes: int(remainingTime.Minutes()),
74
+ Subject: worker.TaskSendVerifyEmailSubject,
75
+ }, opts...)
76
+
77
+ if err != nil {
78
+ return nil, models.Exception{
79
+ InternalServerError: true,
80
+ Message: "failed to distribute task send verify email",
81
+ }
82
+ }
83
+
84
+ return emailVerification, nil
85
+ }
86
+
87
+ func (s *emailService) ValidateEmailVerification(ctx context.Context, req *models.ValidateEmailVerificationRequest) (*models.EmailVerification, error) {
88
+ emailVerification, err := s.emailRepository.GetEmailVerification(ctx, req.AccountID, int64(req.Token))
89
+ if err != nil {
90
+ return nil, response.HandleGormError(err, "Internal Server Error")
91
+ }
92
+
93
+ if emailVerification.ExpiredAt.Before(time.Now()) {
94
+ err = s.emailRepository.DeleteEmailVerification(ctx, emailVerification.ID)
95
+ if err != nil {
96
+ return nil, response.HandleGormError(err, "Internal Server Error")
97
+ }
98
+
99
+ return nil, models.Exception{
100
+ Unauthorized: true,
101
+ Message: "Token has expired!",
102
+ }
103
+ }
104
+
105
+ account, err := s.emailRepository.GetAccountById(ctx, emailVerification.AccountID)
106
+ if err != nil {
107
+ return nil, response.HandleGormError(err, "Internal Server Error")
108
+ }
109
+
110
+ account.IsEmailVerified = true
111
+
112
+ _, err = s.emailRepository.SaveAccount(ctx, account)
113
+ if err != nil {
114
+ return nil, response.HandleGormError(err, "Internal Server Error")
115
+ }
116
+
117
+ err = s.emailRepository.DeleteEmailVerification(ctx, emailVerification.ID)
118
+ if err != nil {
119
+ return nil, response.HandleGormError(err, "Internal Server Error")
120
+ }
121
+
122
+ return emailVerification, nil
123
+ }
space/space/space/space/space/space/space/controller/quiz/review_quiz_controller.go ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 Review(c *gin.Context) {
13
+ reviewQuiz := services.ReviewQuizService{}
14
+ reviewQuizController := controller.Controller[any, models.Quiz, models.QuestionResponse]{
15
+ Service: &reviewQuiz.Service,
16
+ }
17
+ reviewQuizController.HeaderParse(c, func() {
18
+ quizId, _ := strconv.Atoi(c.Param("quiz_id"))
19
+ academyId, _ := strconv.Atoi(c.Param("academy_id"))
20
+ questionNo, _ := strconv.Atoi(c.Query("question_no"))
21
+ reviewQuizController.Service.Constructor.ID = uint(quizId)
22
+ reviewQuizController.Service.Constructor.AcademyID = uint(academyId)
23
+ reviewQuiz.Retrieve(reviewQuizController.AccountData.UserID, questionNo)
24
+ reviewQuizController.Response(c)
25
+ })
26
+ }
space/space/space/space/space/space/space/router/quiz_route.go CHANGED
@@ -12,6 +12,7 @@ func QuizRoute(router *gin.Engine) {
12
  routerGroup.GET("/:academy_id/list", middleware.AuthUser, QuizController.List)
13
  routerGroup.POST("/:academy_id/:quiz_id/attempt", middleware.AuthUser, QuizController.Attempt)
14
  routerGroup.GET("/:academy_id/:quiz_id/question", middleware.AuthUser, QuizController.Question)
 
15
  routerGroup.PUT("/:academy_id/:quiz_id/choose-answer", middleware.AuthUser, QuizController.Answer)
16
  routerGroup.GET("result/:attempt_id", middleware.AuthUser, QuizController.Result)
17
  routerGroup.GET("result/", middleware.AuthUser, QuizController.Result)
 
12
  routerGroup.GET("/:academy_id/list", middleware.AuthUser, QuizController.List)
13
  routerGroup.POST("/:academy_id/:quiz_id/attempt", middleware.AuthUser, QuizController.Attempt)
14
  routerGroup.GET("/:academy_id/:quiz_id/question", middleware.AuthUser, QuizController.Question)
15
+ routerGroup.GET("/:academy_id/:quiz_id/review", middleware.AuthUser, QuizController.Review)
16
  routerGroup.PUT("/:academy_id/:quiz_id/choose-answer", middleware.AuthUser, QuizController.Answer)
17
  routerGroup.GET("result/:attempt_id", middleware.AuthUser, QuizController.Result)
18
  routerGroup.GET("result/", middleware.AuthUser, QuizController.Result)
space/space/space/space/space/space/space/services/academy_quiz_question_service.go CHANGED
@@ -34,6 +34,8 @@ func (s *QuestionQuizService) Retrieve(userID uint, questionNo int) {
34
  s.Exception.Message = "There is no Answer Option with given QuestionId!"
35
  return
36
  }
 
 
37
  s.Result = models.QuestionResponse{
38
  Question: questionRepo.Result,
39
  Answer: answerOptionRepo.Result,
 
34
  s.Exception.Message = "There is no Answer Option with given QuestionId!"
35
  return
36
  }
37
+ questionRepo.Result.Review = "SECRET"
38
+ questionRepo.Result.CorrectAnswer = 0
39
  s.Result = models.QuestionResponse{
40
  Question: questionRepo.Result,
41
  Answer: answerOptionRepo.Result,
space/space/space/space/space/space/space/services/academy_quiz_review_service.go ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package services
2
+
3
+ import (
4
+ "errors"
5
+
6
+ "api.qobiltu.id/models"
7
+ "api.qobiltu.id/repositories"
8
+ )
9
+
10
+ type ReviewQuizService struct {
11
+ Service[models.Quiz, models.QuestionResponse]
12
+ }
13
+
14
+ func (s *ReviewQuizService) Retrieve(userID uint, questionNo int) {
15
+ latestAttemptRepo := repositories.GetUserLastAttempt(userID, s.Constructor.ID)
16
+ s.Error = latestAttemptRepo.RowsError
17
+ if latestAttemptRepo.NoRecord {
18
+ s.Exception.DataNotFound = true
19
+ s.Exception.Message = "There is no attempt data with given user ID!"
20
+ return
21
+ }
22
+ quizRepo := repositories.GetQuizbyId(s.Constructor.ID)
23
+ s.Error = errors.Join(s.Error, quizRepo.RowsError)
24
+ if latestAttemptRepo.Result.Score >= float64(quizRepo.Result.MinScore) {
25
+ questionRepo := repositories.GetQuestionByOrder(s.Constructor.ID, questionNo)
26
+ s.Error = questionRepo.RowsError
27
+ if questionRepo.NoRecord {
28
+ s.Exception.DataNotFound = true
29
+ s.Exception.Message = "There is no quiz with given academy!"
30
+ return
31
+ }
32
+ answerRepo := repositories.GetUserAnswerByAttemptQuestionId(latestAttemptRepo.Result.ID, questionRepo.Result.ID)
33
+ if answerRepo.NoRecord {
34
+ s.Exception.DataNotFound = true
35
+ s.Exception.Message = "There is no Answer with given AttemptId!"
36
+ return
37
+ }
38
+ answerOptionRepo := repositories.GetAnswerByQuestionId(questionRepo.Result.ID)
39
+ if answerOptionRepo.NoRecord {
40
+ s.Exception.DataNotFound = true
41
+ s.Exception.Message = "There is no Answer Option with given QuestionId!"
42
+ return
43
+ }
44
+ s.Result = models.QuestionResponse{
45
+ Question: questionRepo.Result,
46
+ Answer: answerOptionRepo.Result,
47
+ UserAnswer: int(answerRepo.Result.SelectedAnswer),
48
+ }
49
+ } else {
50
+ s.Exception.Forbidden = true
51
+ s.Exception.Message = "You have to passed the exam to review the quiz! "
52
+ return
53
+ }
54
+
55
+ return
56
+ }
space/space/space/space/space/space/space/services/academy_quiz_service.go CHANGED
@@ -2,7 +2,6 @@ package services
2
 
3
  import (
4
  "errors"
5
- "fmt"
6
  "time"
7
 
8
  "api.qobiltu.id/models"
@@ -131,7 +130,8 @@ func (s *AttemptQuizService) Create(userID uint) {
131
  if latestAttemptRepo.Result.Score < float64(quizRepo.Result.MinScore) {
132
  Attempt(s, quizRepo, userID)
133
  } else {
134
- s.Result = latestAttemptRepo.Result
 
135
  return
136
  }
137
  } else {
@@ -142,10 +142,7 @@ func (s *AttemptQuizService) Create(userID uint) {
142
  }
143
 
144
  func (s *SubmitQuizService) Create() {
145
- fmt.Println(s.Constructor.ID)
146
- fmt.Println(s.Constructor.AccountID)
147
  quizAttemptRepo := repositories.GetAttemptByIdandUserId(s.Constructor.ID, s.Constructor.AccountID)
148
- fmt.Println(quizAttemptRepo.Result)
149
  if quizAttemptRepo.NoRecord {
150
  s.Exception.DataNotFound = true
151
  s.Exception.Message = "There is no quiz attempt with given user!"
 
2
 
3
  import (
4
  "errors"
 
5
  "time"
6
 
7
  "api.qobiltu.id/models"
 
130
  if latestAttemptRepo.Result.Score < float64(quizRepo.Result.MinScore) {
131
  Attempt(s, quizRepo, userID)
132
  } else {
133
+ s.Exception.Forbidden = true
134
+ s.Exception.Message = "You're alread passed the quiz, you don't have to re-attempt!"
135
  return
136
  }
137
  } else {
 
142
  }
143
 
144
  func (s *SubmitQuizService) Create() {
 
 
145
  quizAttemptRepo := repositories.GetAttemptByIdandUserId(s.Constructor.ID, s.Constructor.AccountID)
 
146
  if quizAttemptRepo.NoRecord {
147
  s.Exception.DataNotFound = true
148
  s.Exception.Message = "There is no quiz attempt with given user!"
space/space/space/space/space/space/space/space/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,
space/space/space/space/space/space/space/space/models/database_orm_model.go CHANGED
@@ -78,8 +78,10 @@ type Academy struct {
78
  Slug string `json:"slug" gorm:"uniqueIndex" `
79
  TotalMaterial int `json:"total_material"`
80
  CompletedMaterial int `json:"completed_material"`
 
 
81
  IsCompletedRead bool `json:"is_read"`
82
- IsPassedExam bool `json:"is_exam"`
83
  Description string `json:"description"`
84
  }
85
 
@@ -189,6 +191,7 @@ type QuizResult struct {
189
  TotalQuestions int `gorm:"column:total_questions" json:"total_questions"`
190
  CorrectAnswers int `gorm:"column:correct_answers" json:"correct_answers"`
191
  AverageScore float64 `gorm:"column:average_score" json:"average_score"`
 
192
  }
193
 
194
  type (
@@ -396,7 +399,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 +409,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
 
78
  Slug string `json:"slug" gorm:"uniqueIndex" `
79
  TotalMaterial int `json:"total_material"`
80
  CompletedMaterial int `json:"completed_material"`
81
+ TotalQuiz int `json:"total_quiz"`
82
+ PassedQuiz int `json:"passed_quiz"`
83
  IsCompletedRead bool `json:"is_read"`
84
+ IsPassedExam bool `json:"is_passed_exam"`
85
  Description string `json:"description"`
86
  }
87
 
 
191
  TotalQuestions int `gorm:"column:total_questions" json:"total_questions"`
192
  CorrectAnswers int `gorm:"column:correct_answers" json:"correct_answers"`
193
  AverageScore float64 `gorm:"column:average_score" json:"average_score"`
194
+ IsPassed bool `gorm:"column:is_passed" json:"is_passed"`
195
  }
196
 
197
  type (
 
399
  Account *Account `gorm:"foreignKey:AccountID;constraint:OnDelete:CASCADE" json:"account,omitempty"`
400
 
401
  // Kriteria Umum
402
+ ExpecteMinAgeLimit *int `gorm:"column:expected_min_age_limit" json:"expected_min_age_limit"` // batas usia pasangan yang diharapkan
403
+ ExpecteMaxAgeLimit *int `gorm:"column:expected_max_age_limit" json:"expected_max_age_limit"` // batas usia pasangan yang diharapkan
404
  ExpectedDomicile *string `gorm:"column:expected_domicile" json:"expected_domicile"` // domisili pasangan yang diharapkan
405
  AcceptedMaritalStatus *pq.StringArray `gorm:"column:accepted_marital_status;type:varchar(255)[]" json:"accepted_marital_status"` // status pernikahan yang diterima
406
  PartnerFamilyReligion *string `gorm:"column:partner_family_religion" json:"partner_family_religion"` // agama yang dianut keluarga pasangan
 
409
  PartnerCanCook *string `gorm:"column:partner_can_cook" json:"partner_can_cook"` // apakah pasangan diharapkan bisa memasak
410
 
411
  // Kriteria Fisik
412
+ ExpectedBodyShapes *pq.StringArray `gorm:"column:expected_body_shapes;type:varchar(255)[]" json:"expected_body_shapes"` // bentuk tubuh yang diharapkan
413
+ ExpectedSkinColors *pq.StringArray `gorm:"column:expected_skin_colors;type:varchar(255)[]" json:"expected_skin_colors"` // warna kulit yang diharapkan
414
+ ExpectedHairTypes *pq.StringArray `gorm:"column:expected_hair_types;type:varchar(255)[]" json:"expected_hair_types"` // tipe rambut yang diharapkan
415
+ ExpectedHairThickness *pq.StringArray `gorm:"column:expected_hair_thickness;type:varchar(255)[]" json:"expected_hair_thickness"` // jenis rambut yang diharapkan
416
+ ExpectedMinHeightLimit *int `gorm:"column:expected_min_height_limit" json:"expected_min_height_limit"` // tinggi badan yang diharapkan
417
+ ExpectedMaxHeightLimit *int `gorm:"column:expected_max_height_limit" json:"expected_max_height_limit"` // tinggi badan yang diharapkan
418
 
419
  // Pendidikan & Pekerjaan
420
  ExpectedPartnerIncome *string `gorm:"column:expected_partner_income" json:"expected_partner_income"` // penghasilan pasangan per bulan yang diharapkan
space/space/space/space/space/space/space/space/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
space/space/space/space/space/space/space/space/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()
space/space/space/space/space/space/space/space/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,
space/space/space/space/space/space/space/space/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/space/space/space/space/space/space/space/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/space/space/space/space/space/space/space/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/space/space/space/space/space/space/space/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