nexstruct 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (229) hide show
  1. package/AGENTS.md +122 -0
  2. package/LICENSE +21 -0
  3. package/README.md +103 -0
  4. package/package.json +99 -0
  5. package/scaffold/generator.js +409 -0
  6. package/scaffold/index.js +20 -0
  7. package/scaffold/prompts.js +108 -0
  8. package/templates/api/axios/src/api/axios/client.api.ts +30 -0
  9. package/templates/api/axios/src/api/axios/users.api.ts +15 -0
  10. package/templates/api/fetch/src/api/fetch/client.api.ts +68 -0
  11. package/templates/api/fetch/src/api/fetch/users.api.ts +15 -0
  12. package/templates/api/trpc/src/api/trpc/client.api.ts +4 -0
  13. package/templates/api/trpc/src/api/trpc/router.api.ts +15 -0
  14. package/templates/api/trpc/src/api/trpc/server.client.api.ts +4 -0
  15. package/templates/api/trpc/src/providers/trpc.provider.tsx +24 -0
  16. package/templates/auth/clerk/src/auth/clerk/auth.service.ts +4 -0
  17. package/templates/auth/clerk/src/hooks/use-auth.hook.ts +13 -0
  18. package/templates/auth/clerk/src/middleware.ts +7 -0
  19. package/templates/auth/clerk/src/providers/auth.provider.tsx +6 -0
  20. package/templates/auth/next-auth/src/app/api/auth/[...nextauth]/route.ts +5 -0
  21. package/templates/auth/next-auth/src/auth/next-auth/auth.service.ts +45 -0
  22. package/templates/auth/next-auth/src/hooks/use-session.hook.ts +13 -0
  23. package/templates/auth/next-auth/src/providers/session.provider.tsx +6 -0
  24. package/templates/forms/formik/src/components/forms/login-form.component.tsx +30 -0
  25. package/templates/forms/formik/src/forms/formik/hooks/use-form-config.hook.ts +7 -0
  26. package/templates/forms/formik/src/forms/formik/schemas/example.schema.ts +8 -0
  27. package/templates/forms/react-hook-form/src/components/forms/login-form.component.tsx +27 -0
  28. package/templates/forms/react-hook-form/src/forms/react-hook-form/hooks/use-form.hook.ts +13 -0
  29. package/templates/forms/react-hook-form/src/forms/react-hook-form/schemas/example.schema.ts +15 -0
  30. package/templates/nextjs-base/next.config.ts +5 -0
  31. package/templates/nextjs-base/postcss.config.mjs +9 -0
  32. package/templates/nextjs-base/src/app/_components/navbar.tsx +88 -0
  33. package/templates/nextjs-base/src/app/_components/sidebar.tsx +223 -0
  34. package/templates/nextjs-base/src/app/error.tsx +39 -0
  35. package/templates/nextjs-base/src/app/globals.css +71 -0
  36. package/templates/nextjs-base/src/app/layout.tsx +21 -0
  37. package/templates/nextjs-base/src/app/loading.tsx +13 -0
  38. package/templates/nextjs-base/src/app/not-found.tsx +22 -0
  39. package/templates/nextjs-base/src/app/page.tsx +10 -0
  40. package/templates/nextjs-base/tailwind.config.ts +69 -0
  41. package/templates/shared/src/components/common/theme-toggle.component.tsx +31 -0
  42. package/templates/shared/src/components/common/toast/custom-message.component.tsx +18 -0
  43. package/templates/shared/src/components/common/toast/index.ts +8 -0
  44. package/templates/shared/src/components/common/toast/toast-message.component.tsx +112 -0
  45. package/templates/shared/src/hooks/use-debounce.hook.ts +12 -0
  46. package/templates/shared/src/hooks/use-fetch.hook.ts +42 -0
  47. package/templates/shared/src/hooks/use-intersection-observer.hook.ts +39 -0
  48. package/templates/shared/src/hooks/use-local-storage.hook.ts +30 -0
  49. package/templates/shared/src/hooks/use-media-query.hook.ts +26 -0
  50. package/templates/shared/src/hooks/use-toggle.hook.ts +12 -0
  51. package/templates/shared/src/lib/utils.util.ts +361 -0
  52. package/templates/shared/src/providers/theme.provider.tsx +17 -0
  53. package/templates/shared/src/providers/toast.provider.tsx +32 -0
  54. package/templates/shared/src/types/common.type.ts +34 -0
  55. package/templates/state/context/src/store/context/auth.context.tsx +47 -0
  56. package/templates/state/context/src/store/context/counter.context.tsx +41 -0
  57. package/templates/state/context/src/store/context/index.ts +2 -0
  58. package/templates/state/redux/src/providers/redux.provider.tsx +7 -0
  59. package/templates/state/redux/src/store/redux/hooks.store.ts +5 -0
  60. package/templates/state/redux/src/store/redux/index.ts +4 -0
  61. package/templates/state/redux/src/store/redux/slices/api.slice.ts +8 -0
  62. package/templates/state/redux/src/store/redux/slices/counter.slice.ts +24 -0
  63. package/templates/state/redux/src/store/redux/store.store.ts +13 -0
  64. package/templates/state/zustand/src/store/zustand/counter.store.ts +15 -0
  65. package/templates/state/zustand/src/store/zustand/index.ts +2 -0
  66. package/templates/state/zustand/src/store/zustand/user.store.ts +32 -0
  67. package/templates/ui/antd/COMPONENT_GUIDE.md +326 -0
  68. package/templates/ui/antd/src/app/examples/dialog/page.tsx +205 -0
  69. package/templates/ui/antd/src/app/examples/form/page.tsx +160 -0
  70. package/templates/ui/antd/src/app/examples/layout.tsx +125 -0
  71. package/templates/ui/antd/src/app/examples/page.tsx +64 -0
  72. package/templates/ui/antd/src/app/examples/table/page.tsx +118 -0
  73. package/templates/ui/antd/src/app/page.tsx +283 -0
  74. package/templates/ui/antd/src/components/common/DynamicTable/dynamic-table.component.tsx +79 -0
  75. package/templates/ui/antd/src/components/common/button/action-button.component.tsx +63 -0
  76. package/templates/ui/antd/src/components/common/dialog/dialog-wrapper.component.tsx +63 -0
  77. package/templates/ui/antd/src/components/common/fields/assets/components/check-field.component.tsx +55 -0
  78. package/templates/ui/antd/src/components/common/fields/assets/components/date-picker-field.component.tsx +80 -0
  79. package/templates/ui/antd/src/components/common/fields/assets/components/limit-field.component.tsx +26 -0
  80. package/templates/ui/antd/src/components/common/fields/assets/components/multi-check-field.component.tsx +56 -0
  81. package/templates/ui/antd/src/components/common/fields/assets/components/number-field.component.tsx +100 -0
  82. package/templates/ui/antd/src/components/common/fields/assets/components/otp-field.component.tsx +63 -0
  83. package/templates/ui/antd/src/components/common/fields/assets/components/password-field.component.tsx +106 -0
  84. package/templates/ui/antd/src/components/common/fields/assets/components/phone-number-field.component.tsx +78 -0
  85. package/templates/ui/antd/src/components/common/fields/assets/components/radio-field.component.tsx +55 -0
  86. package/templates/ui/antd/src/components/common/fields/assets/components/range-date-picker.component.tsx +66 -0
  87. package/templates/ui/antd/src/components/common/fields/assets/components/search-field.component.tsx +24 -0
  88. package/templates/ui/antd/src/components/common/fields/assets/components/select-field.component.tsx +82 -0
  89. package/templates/ui/antd/src/components/common/fields/assets/components/single-check-field.component.tsx +50 -0
  90. package/templates/ui/antd/src/components/common/fields/assets/components/single-select-field.component.tsx +86 -0
  91. package/templates/ui/antd/src/components/common/fields/assets/components/string-number-field.component.tsx +80 -0
  92. package/templates/ui/antd/src/components/common/fields/assets/components/switch-field.component.tsx +62 -0
  93. package/templates/ui/antd/src/components/common/fields/assets/components/text-area-field.component.tsx +85 -0
  94. package/templates/ui/antd/src/components/common/fields/assets/components/text-field.component.tsx +88 -0
  95. package/templates/ui/antd/src/components/common/fields/assets/interface/input-props.type.ts +233 -0
  96. package/templates/ui/antd/src/components/common/fields/cusInputField.component.tsx +40 -0
  97. package/templates/ui/antd/src/components/common/pagination/pagination.component.tsx +27 -0
  98. package/templates/ui/antd/src/components/ui/avatar.component.tsx +8 -0
  99. package/templates/ui/antd/src/components/ui/badge.component.tsx +8 -0
  100. package/templates/ui/antd/src/components/ui/button.component.tsx +8 -0
  101. package/templates/ui/antd/src/components/ui/card.component.tsx +8 -0
  102. package/templates/ui/antd/src/components/ui/checkbox.component.tsx +8 -0
  103. package/templates/ui/antd/src/components/ui/dialog.component.tsx +9 -0
  104. package/templates/ui/antd/src/components/ui/dropdown-menu.component.tsx +10 -0
  105. package/templates/ui/antd/src/components/ui/form.component.tsx +12 -0
  106. package/templates/ui/antd/src/components/ui/input.component.tsx +13 -0
  107. package/templates/ui/antd/src/components/ui/label.component.tsx +18 -0
  108. package/templates/ui/antd/src/components/ui/popover.component.tsx +8 -0
  109. package/templates/ui/antd/src/components/ui/progress.component.tsx +8 -0
  110. package/templates/ui/antd/src/components/ui/radio-group.component.tsx +10 -0
  111. package/templates/ui/antd/src/components/ui/scroll-area.component.tsx +25 -0
  112. package/templates/ui/antd/src/components/ui/select.component.tsx +8 -0
  113. package/templates/ui/antd/src/components/ui/separator.component.tsx +8 -0
  114. package/templates/ui/antd/src/components/ui/sheet.component.tsx +8 -0
  115. package/templates/ui/antd/src/components/ui/switch.component.tsx +8 -0
  116. package/templates/ui/antd/src/components/ui/table.component.tsx +8 -0
  117. package/templates/ui/antd/src/components/ui/tabs.component.tsx +8 -0
  118. package/templates/ui/antd/src/components/ui/textarea.component.tsx +9 -0
  119. package/templates/ui/antd/src/components/ui/tooltip.component.tsx +8 -0
  120. package/templates/ui/antd/src/lib/theme.util.ts +40 -0
  121. package/templates/ui/antd/src/providers/antd.provider.tsx +13 -0
  122. package/templates/ui/mui/src/app/examples/layout.tsx +113 -0
  123. package/templates/ui/mui/src/app/examples/page.tsx +716 -0
  124. package/templates/ui/mui/src/app/page.tsx +298 -0
  125. package/templates/ui/mui/src/components/common/DynamicTable/dynamic-table.component.tsx +131 -0
  126. package/templates/ui/mui/src/components/common/button/action-button.component.tsx +57 -0
  127. package/templates/ui/mui/src/components/common/dialog/dialog-wrapper.component.tsx +55 -0
  128. package/templates/ui/mui/src/components/common/fields/assets/components/check-field.component.tsx +51 -0
  129. package/templates/ui/mui/src/components/common/fields/assets/components/date-picker-field.component.tsx +50 -0
  130. package/templates/ui/mui/src/components/common/fields/assets/components/multi-check-field.component.tsx +14 -0
  131. package/templates/ui/mui/src/components/common/fields/assets/components/number-field.component.tsx +59 -0
  132. package/templates/ui/mui/src/components/common/fields/assets/components/password-field.component.tsx +87 -0
  133. package/templates/ui/mui/src/components/common/fields/assets/components/phone-number-field.component.tsx +48 -0
  134. package/templates/ui/mui/src/components/common/fields/assets/components/radio-field.component.tsx +37 -0
  135. package/templates/ui/mui/src/components/common/fields/assets/components/search-field.component.tsx +41 -0
  136. package/templates/ui/mui/src/components/common/fields/assets/components/select-field.component.tsx +77 -0
  137. package/templates/ui/mui/src/components/common/fields/assets/components/single-check-field.component.tsx +39 -0
  138. package/templates/ui/mui/src/components/common/fields/assets/components/single-select-field.component.tsx +56 -0
  139. package/templates/ui/mui/src/components/common/fields/assets/components/string-number-field.component.tsx +52 -0
  140. package/templates/ui/mui/src/components/common/fields/assets/components/switch-field.component.tsx +35 -0
  141. package/templates/ui/mui/src/components/common/fields/assets/components/text-area-field.component.tsx +46 -0
  142. package/templates/ui/mui/src/components/common/fields/assets/components/text-field.component.tsx +51 -0
  143. package/templates/ui/mui/src/components/common/fields/assets/interface/input-props.type.ts +193 -0
  144. package/templates/ui/mui/src/components/common/fields/cusInputField.component.tsx +34 -0
  145. package/templates/ui/mui/src/components/common/pagination/pagination.component.tsx +59 -0
  146. package/templates/ui/mui/src/components/ui/avatar.component.tsx +19 -0
  147. package/templates/ui/mui/src/components/ui/badge.component.tsx +18 -0
  148. package/templates/ui/mui/src/components/ui/button.component.tsx +22 -0
  149. package/templates/ui/mui/src/components/ui/card.component.tsx +39 -0
  150. package/templates/ui/mui/src/components/ui/checkbox.component.tsx +21 -0
  151. package/templates/ui/mui/src/components/ui/dialog.component.tsx +38 -0
  152. package/templates/ui/mui/src/components/ui/dropdown-menu.component.tsx +43 -0
  153. package/templates/ui/mui/src/components/ui/form.component.tsx +98 -0
  154. package/templates/ui/mui/src/components/ui/input.component.tsx +15 -0
  155. package/templates/ui/mui/src/components/ui/label.component.tsx +15 -0
  156. package/templates/ui/mui/src/components/ui/popover.component.tsx +20 -0
  157. package/templates/ui/mui/src/components/ui/progress.component.tsx +19 -0
  158. package/templates/ui/mui/src/components/ui/radio-group.component.tsx +25 -0
  159. package/templates/ui/mui/src/components/ui/scroll-area.component.tsx +27 -0
  160. package/templates/ui/mui/src/components/ui/select.component.tsx +26 -0
  161. package/templates/ui/mui/src/components/ui/separator.component.tsx +11 -0
  162. package/templates/ui/mui/src/components/ui/sheet.component.tsx +44 -0
  163. package/templates/ui/mui/src/components/ui/switch.component.tsx +23 -0
  164. package/templates/ui/mui/src/components/ui/table.component.tsx +34 -0
  165. package/templates/ui/mui/src/components/ui/tabs.component.tsx +38 -0
  166. package/templates/ui/mui/src/components/ui/textarea.component.tsx +18 -0
  167. package/templates/ui/mui/src/components/ui/tooltip.component.tsx +24 -0
  168. package/templates/ui/mui/src/lib/theme.util.ts +73 -0
  169. package/templates/ui/mui/src/providers/mui.provider.tsx +13 -0
  170. package/templates/ui/shadcn/COMPONENT_GUIDE.md +306 -0
  171. package/templates/ui/shadcn/src/app/examples/dialog/page.tsx +122 -0
  172. package/templates/ui/shadcn/src/app/examples/form/page.tsx +107 -0
  173. package/templates/ui/shadcn/src/app/examples/layout.tsx +24 -0
  174. package/templates/ui/shadcn/src/app/examples/page.tsx +30 -0
  175. package/templates/ui/shadcn/src/app/examples/table/page.tsx +77 -0
  176. package/templates/ui/shadcn/src/app/page.tsx +20 -0
  177. package/templates/ui/shadcn/src/components/common/DynamicTable/dynamic-table.component.tsx +136 -0
  178. package/templates/ui/shadcn/src/components/common/button/action-button.component.tsx +68 -0
  179. package/templates/ui/shadcn/src/components/common/dialog/dialog-wrapper.component.tsx +58 -0
  180. package/templates/ui/shadcn/src/components/common/fields/assets/components/check-field.component.tsx +52 -0
  181. package/templates/ui/shadcn/src/components/common/fields/assets/components/date-picker-field.component.tsx +62 -0
  182. package/templates/ui/shadcn/src/components/common/fields/assets/components/dynamic-file-upload-field.component.tsx +152 -0
  183. package/templates/ui/shadcn/src/components/common/fields/assets/components/limit-field.component.tsx +73 -0
  184. package/templates/ui/shadcn/src/components/common/fields/assets/components/multi-check-field.component.tsx +46 -0
  185. package/templates/ui/shadcn/src/components/common/fields/assets/components/number-field.component.tsx +124 -0
  186. package/templates/ui/shadcn/src/components/common/fields/assets/components/otp-field.component.tsx +61 -0
  187. package/templates/ui/shadcn/src/components/common/fields/assets/components/password-field.component.tsx +110 -0
  188. package/templates/ui/shadcn/src/components/common/fields/assets/components/phone-number-field.component.tsx +90 -0
  189. package/templates/ui/shadcn/src/components/common/fields/assets/components/radio-field.component.tsx +41 -0
  190. package/templates/ui/shadcn/src/components/common/fields/assets/components/range-date-picker.component.tsx +71 -0
  191. package/templates/ui/shadcn/src/components/common/fields/assets/components/rich-text-editor.component.tsx +91 -0
  192. package/templates/ui/shadcn/src/components/common/fields/assets/components/search-field.component.tsx +34 -0
  193. package/templates/ui/shadcn/src/components/common/fields/assets/components/select-field.component.tsx +231 -0
  194. package/templates/ui/shadcn/src/components/common/fields/assets/components/single-check-field.component.tsx +42 -0
  195. package/templates/ui/shadcn/src/components/common/fields/assets/components/single-select-field.component.tsx +82 -0
  196. package/templates/ui/shadcn/src/components/common/fields/assets/components/string-number-field.component.tsx +68 -0
  197. package/templates/ui/shadcn/src/components/common/fields/assets/components/switch-field.component.tsx +61 -0
  198. package/templates/ui/shadcn/src/components/common/fields/assets/components/text-area-field.component.tsx +62 -0
  199. package/templates/ui/shadcn/src/components/common/fields/assets/components/text-area-with-file.component.tsx +142 -0
  200. package/templates/ui/shadcn/src/components/common/fields/assets/components/text-field.component.tsx +80 -0
  201. package/templates/ui/shadcn/src/components/common/fields/assets/components/tiny-editor.component.tsx +51 -0
  202. package/templates/ui/shadcn/src/components/common/fields/assets/components/upload-profile-picture.component.tsx +103 -0
  203. package/templates/ui/shadcn/src/components/common/fields/assets/components/upload-video-file.component.tsx +86 -0
  204. package/templates/ui/shadcn/src/components/common/fields/assets/interface/input-props.type.ts +198 -0
  205. package/templates/ui/shadcn/src/components/common/fields/cusInputField.component.tsx +52 -0
  206. package/templates/ui/shadcn/src/components/common/pagination/pagination.component.tsx +68 -0
  207. package/templates/ui/shadcn/src/components/ui/avatar.component.tsx +37 -0
  208. package/templates/ui/shadcn/src/components/ui/badge.component.tsx +28 -0
  209. package/templates/ui/shadcn/src/components/ui/button.component.tsx +52 -0
  210. package/templates/ui/shadcn/src/components/ui/card.component.tsx +46 -0
  211. package/templates/ui/shadcn/src/components/ui/checkbox.component.tsx +25 -0
  212. package/templates/ui/shadcn/src/components/ui/dialog.component.tsx +98 -0
  213. package/templates/ui/shadcn/src/components/ui/dropdown-menu.component.tsx +163 -0
  214. package/templates/ui/shadcn/src/components/ui/form.component.tsx +110 -0
  215. package/templates/ui/shadcn/src/components/ui/input-otp.component.tsx +64 -0
  216. package/templates/ui/shadcn/src/components/ui/input.component.tsx +23 -0
  217. package/templates/ui/shadcn/src/components/ui/label.component.tsx +23 -0
  218. package/templates/ui/shadcn/src/components/ui/popover.component.tsx +27 -0
  219. package/templates/ui/shadcn/src/components/ui/progress.component.tsx +22 -0
  220. package/templates/ui/shadcn/src/components/ui/radio-group.component.tsx +33 -0
  221. package/templates/ui/shadcn/src/components/ui/scroll-area.component.tsx +37 -0
  222. package/templates/ui/shadcn/src/components/ui/select.component.tsx +139 -0
  223. package/templates/ui/shadcn/src/components/ui/separator.component.tsx +23 -0
  224. package/templates/ui/shadcn/src/components/ui/sheet.component.tsx +89 -0
  225. package/templates/ui/shadcn/src/components/ui/switch.component.tsx +26 -0
  226. package/templates/ui/shadcn/src/components/ui/table.component.tsx +71 -0
  227. package/templates/ui/shadcn/src/components/ui/tabs.component.tsx +52 -0
  228. package/templates/ui/shadcn/src/components/ui/textarea.component.tsx +20 -0
  229. package/templates/ui/shadcn/src/components/ui/tooltip.component.tsx +25 -0
@@ -0,0 +1,78 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ 'use client';
3
+
4
+ import { Controller } from 'react-hook-form';
5
+ import { Input } from 'antd';
6
+ import { maskString } from '@/lib/utils.util';
7
+ import type { PhoneNumberProps } from '../../assets/interface/input-props.type';
8
+
9
+ export function PhoneNumber({
10
+ form, name, labelName, placeholder, required = false, disabled = false,
11
+ viewOnly = false, customMessage, onValueChange, hasPhone = false,
12
+ }: PhoneNumberProps) {
13
+ const fieldName = name || labelName?.toLowerCase().replace(/\s+/g, '_') || 'phone';
14
+
15
+ const labelEl = labelName ? (
16
+ <label className="font-semibold leading-6 text-[14px] tracking-[0.02em]" style={{ display: 'block', marginBottom: 4 }}>
17
+ {labelName}
18
+ {required && <span style={{ color: '#ff4d4f', marginLeft: 2 }}>*</span>}
19
+ </label>
20
+ ) : null;
21
+
22
+ if (form) {
23
+ return (
24
+ <div style={{ marginBottom: 16 }}>
25
+ {labelEl}
26
+ {viewOnly ? (
27
+ <div style={{ padding: '4px 11px', border: '1px solid #d9d9d9', borderRadius: 6, minHeight: 32, background: '#fafafa' }}>
28
+ {hasPhone ? maskString(form.watch(fieldName) || '') : form.watch(fieldName) || ''}
29
+ </div>
30
+ ) : (
31
+ <Controller
32
+ name={fieldName}
33
+ control={form.control}
34
+ render={({ field, fieldState }) => {
35
+ const isError = !!fieldState.error;
36
+ return (
37
+ <div>
38
+ <Input
39
+ {...field}
40
+ type="tel"
41
+ placeholder={placeholder || `Enter ${labelName || 'phone number'}`}
42
+ disabled={disabled}
43
+ status={isError ? 'error' : undefined}
44
+ maxLength={20}
45
+ onChange={(e) => {
46
+ field.onChange(e);
47
+ onValueChange?.(e.target.value);
48
+ }}
49
+ />
50
+ {isError && (
51
+ <div style={{ color: '#ff4d4f', fontSize: 12, marginTop: 4 }}>
52
+ {String(fieldState.error?.message || '')}
53
+ </div>
54
+ )}
55
+ {!isError && customMessage && (
56
+ <div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>
57
+ )}
58
+ </div>
59
+ );
60
+ }}
61
+ />
62
+ )}
63
+ </div>
64
+ );
65
+ }
66
+
67
+ return (
68
+ <div style={{ marginBottom: 16 }}>
69
+ {labelEl}
70
+ <Input
71
+ type="tel"
72
+ placeholder={placeholder || `Enter ${labelName || 'phone number'}`}
73
+ disabled={disabled}
74
+ />
75
+ {customMessage && <div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>}
76
+ </div>
77
+ );
78
+ }
@@ -0,0 +1,55 @@
1
+ 'use client';
2
+
3
+ import { Controller } from 'react-hook-form';
4
+ import { Radio, Typography } from 'antd';
5
+ import type { RadioProps } from '../../assets/interface/input-props.type';
6
+
7
+ const { Text } = Typography;
8
+
9
+ export function RadioField({
10
+ form, name, labelName, required = false, disabled = false, options = [],
11
+ }: RadioProps) {
12
+ const labelEl = labelName ? (
13
+ <label className="font-semibold leading-6 text-[14px] tracking-[0.02em]" style={{ display: 'block', marginBottom: 4 }}>
14
+ {labelName}
15
+ {required && <span style={{ color: '#ff4d4f', marginLeft: 2 }}>*</span>}
16
+ </label>
17
+ ) : null;
18
+
19
+ const radioOptions = (options || []).map((opt) =>
20
+ typeof opt === 'string' ? { label: opt, value: opt } : opt
21
+ );
22
+
23
+ if (form) {
24
+ return (
25
+ <div style={{ marginBottom: 16 }}>
26
+ {labelEl}
27
+ <Controller
28
+ name={name}
29
+ control={form.control}
30
+ render={({ field, fieldState }) => (
31
+ <div>
32
+ <Radio.Group
33
+ {...field}
34
+ disabled={disabled}
35
+ options={radioOptions}
36
+ />
37
+ {fieldState.error && (
38
+ <div style={{ color: '#ff4d4f', fontSize: 12, marginTop: 4 }}>
39
+ {String(fieldState.error.message || '')}
40
+ </div>
41
+ )}
42
+ </div>
43
+ )}
44
+ />
45
+ </div>
46
+ );
47
+ }
48
+
49
+ return (
50
+ <div style={{ marginBottom: 16 }}>
51
+ {labelEl}
52
+ <Radio.Group disabled={disabled} options={radioOptions} />
53
+ </div>
54
+ );
55
+ }
@@ -0,0 +1,66 @@
1
+ 'use client';
2
+
3
+ import { Controller } from 'react-hook-form';
4
+ import { DatePicker, Typography } from 'antd';
5
+ import dayjs from 'dayjs';
6
+ import type { RangeDatePickerProps } from '../../assets/interface/input-props.type';
7
+
8
+ const { RangePicker } = DatePicker;
9
+ const { Text } = Typography;
10
+
11
+ export function RangeDatePicker({
12
+ form, name, labelName, required = false, disabled = false, customMessage,
13
+ }: RangeDatePickerProps) {
14
+ const fieldName = name || labelName?.toLowerCase().replace(/\s+/g, '_') || 'dateRange';
15
+
16
+ const labelEl = labelName ? (
17
+ <label className="font-semibold leading-6 text-[14px] tracking-[0.02em]" style={{ display: 'block', marginBottom: 4 }}>
18
+ {labelName}
19
+ {required && <span style={{ color: '#ff4d4f', marginLeft: 2 }}>*</span>}
20
+ </label>
21
+ ) : null;
22
+
23
+ if (form) {
24
+ return (
25
+ <div style={{ marginBottom: 16 }}>
26
+ {labelEl}
27
+ <Controller
28
+ name={fieldName}
29
+ control={form.control}
30
+ render={({ field, fieldState }) => {
31
+ const isError = !!fieldState.error;
32
+ return (
33
+ <div>
34
+ <RangePicker
35
+ style={{ width: '100%' }}
36
+ disabled={disabled}
37
+ status={isError ? 'error' : undefined}
38
+ value={field.value ? field.value.map((d: string | null) => d ? dayjs(d) : null) : null}
39
+ onChange={(dates) => {
40
+ field.onChange(dates ? dates.map((d) => d?.toISOString() || null) : [null, null]);
41
+ }}
42
+ />
43
+ {isError && (
44
+ <div style={{ color: '#ff4d4f', fontSize: 12, marginTop: 4 }}>
45
+ {String(fieldState.error?.message || '')}
46
+ </div>
47
+ )}
48
+ {!isError && customMessage && (
49
+ <div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>
50
+ )}
51
+ </div>
52
+ );
53
+ }}
54
+ />
55
+ </div>
56
+ );
57
+ }
58
+
59
+ return (
60
+ <div style={{ marginBottom: 16 }}>
61
+ {labelEl}
62
+ <RangePicker style={{ width: '100%' }} disabled={disabled} />
63
+ {customMessage && <div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>}
64
+ </div>
65
+ );
66
+ }
@@ -0,0 +1,24 @@
1
+ 'use client';
2
+
3
+ import { Input } from 'antd';
4
+ import type { SearchFieldProps } from '../../assets/interface/input-props.type';
5
+
6
+ const { Search: AntSearch } = Input;
7
+
8
+ export function SearchField({
9
+ placeholder, onSearch, value, setValue, loading,
10
+ }: SearchFieldProps) {
11
+ return (
12
+ <div style={{ marginBottom: 16 }}>
13
+ <AntSearch
14
+ placeholder={placeholder || 'Search...'}
15
+ value={value}
16
+ onChange={(e) => setValue?.(e.target.value)}
17
+ onSearch={(val) => onSearch?.(val)}
18
+ loading={loading}
19
+ allowClear
20
+ enterButton
21
+ />
22
+ </div>
23
+ );
24
+ }
@@ -0,0 +1,82 @@
1
+ 'use client';
2
+
3
+ import { Controller } from 'react-hook-form';
4
+ import { Select } from 'antd';
5
+ import type { SelectFieldProps } from '../../assets/interface/input-props.type';
6
+
7
+ export function SelectField({
8
+ form, name, labelName, required = false, disabled = false,
9
+ options = [], placeholder, showSearch = true, type = 'single',
10
+ viewOnly = false, onValueChange, isLoading = false, onSearch, customMessage,
11
+ }: SelectFieldProps) {
12
+ const labelEl = labelName ? (
13
+ <label className="font-semibold leading-6 text-[14px] tracking-[0.02em]" style={{ display: 'block', marginBottom: 4 }}>
14
+ {labelName}
15
+ {required && <span style={{ color: '#ff4d4f', marginLeft: 2 }}>*</span>}
16
+ </label>
17
+ ) : null;
18
+
19
+ const normalizedOptions = (options || []).map((opt) =>
20
+ typeof opt === 'string' ? { label: opt, value: opt } : opt
21
+ );
22
+
23
+ if (viewOnly) {
24
+ return (
25
+ <div style={{ marginBottom: 16 }}>
26
+ {labelEl}
27
+ <div style={{ padding: '4px 11px', border: '1px solid #d9d9d9', borderRadius: 6, minHeight: 32, background: '#fafafa' }}>
28
+ {form?.watch(name) ? (
29
+ type === 'multiple'
30
+ ? (form.watch(name) || []).join(', ')
31
+ : normalizedOptions.find((o) => o.value === form.watch(name))?.label || form.watch(name)
32
+ ) : ''}
33
+ </div>
34
+ </div>
35
+ );
36
+ }
37
+
38
+ return (
39
+ <div style={{ marginBottom: 16 }}>
40
+ {labelEl}
41
+ <Controller
42
+ name={name}
43
+ control={form?.control}
44
+ defaultValue={type === 'multiple' ? [] : undefined}
45
+ render={({ field, fieldState }) => {
46
+ const isError = !!fieldState?.error;
47
+ return (
48
+ <div>
49
+ <Select
50
+ {...field}
51
+ style={{ width: '100%' }}
52
+ placeholder={placeholder || `Select ${labelName || 'option'}`}
53
+ disabled={disabled || isLoading}
54
+ status={isError ? 'error' : undefined}
55
+ loading={isLoading}
56
+ mode={type === 'multiple' ? 'multiple' : undefined}
57
+ showSearch={showSearch}
58
+ filterOption={showSearch ? (input: string, option?: any) =>
59
+ (option?.label ?? '').toLowerCase().includes(input.toLowerCase())
60
+ : undefined}
61
+ options={normalizedOptions}
62
+ onSearch={onSearch}
63
+ onChange={(val) => {
64
+ field.onChange(val);
65
+ onValueChange?.(val);
66
+ }}
67
+ />
68
+ {isError && (
69
+ <div style={{ color: '#ff4d4f', fontSize: 12, marginTop: 4 }}>
70
+ {String(fieldState.error?.message || '')}
71
+ </div>
72
+ )}
73
+ {!isError && customMessage && (
74
+ <div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>
75
+ )}
76
+ </div>
77
+ );
78
+ }}
79
+ />
80
+ </div>
81
+ );
82
+ }
@@ -0,0 +1,50 @@
1
+ 'use client';
2
+
3
+ import { Controller } from 'react-hook-form';
4
+ import { Checkbox, Typography } from 'antd';
5
+ import type { CheckboxProps } from '../../assets/interface/input-props.type';
6
+
7
+ const { Text } = Typography;
8
+
9
+ export function SingleCheckField({
10
+ form, name, labelName, required = false, disabled = false,
11
+ }: CheckboxProps) {
12
+ if (form) {
13
+ return (
14
+ <div style={{ marginBottom: 16 }}>
15
+ <Controller
16
+ name={name}
17
+ control={form.control}
18
+ defaultValue={false}
19
+ render={({ field, fieldState }) => (
20
+ <div>
21
+ <Checkbox
22
+ {...field}
23
+ checked={!!field.value}
24
+ disabled={disabled}
25
+ onChange={(e) => field.onChange(e.target.checked)}
26
+ >
27
+ {labelName}
28
+ {required && <span style={{ color: '#ff4d4f', marginLeft: 2 }}>*</span>}
29
+ </Checkbox>
30
+ {fieldState.error && (
31
+ <div style={{ color: '#ff4d4f', fontSize: 12, marginTop: 4 }}>
32
+ {String(fieldState.error.message || '')}
33
+ </div>
34
+ )}
35
+ </div>
36
+ )}
37
+ />
38
+ </div>
39
+ );
40
+ }
41
+
42
+ return (
43
+ <div style={{ marginBottom: 16 }}>
44
+ <Checkbox disabled={disabled}>
45
+ {labelName}
46
+ {required && <span style={{ color: '#ff4d4f', marginLeft: 2 }}>*</span>}
47
+ </Checkbox>
48
+ </div>
49
+ );
50
+ }
@@ -0,0 +1,86 @@
1
+ 'use client';
2
+
3
+ import { Controller } from 'react-hook-form';
4
+ import { Select } from 'antd';
5
+ import type { SingleSelectProps } from '../../assets/interface/input-props.type';
6
+
7
+ export function SingleSelectField({
8
+ form, name, labelName, placeholder, required = false, disabled = false,
9
+ options = [], viewOnly = false, isLoading = false, defaultValue,
10
+ onValueChange, customMessage,
11
+ }: SingleSelectProps) {
12
+ const fieldName = name || labelName?.toLowerCase().replace(/\s+/g, '_') || 'singleselect';
13
+
14
+ const labelEl = labelName ? (
15
+ <label className="font-semibold leading-6 text-[14px] tracking-[0.02em]" style={{ display: 'block', marginBottom: 4 }}>
16
+ {labelName}
17
+ {required && <span style={{ color: '#ff4d4f', marginLeft: 2 }}>*</span>}
18
+ </label>
19
+ ) : null;
20
+
21
+ const normalizedOptions = options.map((opt) =>
22
+ typeof opt === 'string' ? { label: opt, value: opt } : opt
23
+ );
24
+
25
+ if (form) {
26
+ return (
27
+ <div style={{ marginBottom: 16 }}>
28
+ {labelEl}
29
+ {viewOnly ? (
30
+ <div style={{ padding: '4px 11px', border: '1px solid #d9d9d9', borderRadius: 6, minHeight: 32, background: '#fafafa' }}>
31
+ {form.watch(fieldName) || ''}
32
+ </div>
33
+ ) : (
34
+ <Controller
35
+ name={fieldName}
36
+ control={form.control}
37
+ defaultValue={defaultValue || ''}
38
+ render={({ field, fieldState }) => {
39
+ const isError = !!fieldState.error;
40
+ return (
41
+ <div>
42
+ <Select
43
+ {...field}
44
+ style={{ width: '100%' }}
45
+ placeholder={placeholder || `Select ${labelName || 'option'}`}
46
+ disabled={disabled || isLoading}
47
+ status={isError ? 'error' : undefined}
48
+ loading={isLoading}
49
+ options={normalizedOptions}
50
+ onChange={(val) => {
51
+ field.onChange(val);
52
+ onValueChange?.(val);
53
+ }}
54
+ />
55
+ {isError && (
56
+ <div style={{ color: '#ff4d4f', fontSize: 12, marginTop: 4 }}>
57
+ {String(fieldState.error?.message || '')}
58
+ </div>
59
+ )}
60
+ {!isError && customMessage && (
61
+ <div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>
62
+ )}
63
+ </div>
64
+ );
65
+ }}
66
+ />
67
+ )}
68
+ </div>
69
+ );
70
+ }
71
+
72
+ return (
73
+ <div style={{ marginBottom: 16 }}>
74
+ {labelEl}
75
+ <Select
76
+ style={{ width: '100%' }}
77
+ placeholder={placeholder || `Select ${labelName || 'option'}`}
78
+ disabled={disabled || isLoading}
79
+ loading={isLoading}
80
+ options={normalizedOptions}
81
+ defaultValue={defaultValue}
82
+ />
83
+ {customMessage && <div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>}
84
+ </div>
85
+ );
86
+ }
@@ -0,0 +1,80 @@
1
+ 'use client';
2
+
3
+ import { Controller } from 'react-hook-form';
4
+ import { Input } from 'antd';
5
+ import type { NumberInputProps } from '../../assets/interface/input-props.type';
6
+
7
+ export function StringNumber({
8
+ form, name, labelName, placeholder, required = false, disabled = false,
9
+ viewOnly = false, customMessage, prefix, suffix,
10
+ }: NumberInputProps) {
11
+ const fieldName = name || labelName?.toLowerCase().replace(/\s+/g, '_') || 'stringnumber';
12
+
13
+ const labelEl = labelName ? (
14
+ <label className="font-semibold leading-6 text-[14px] tracking-[0.02em]" style={{ display: 'block', marginBottom: 4 }}>
15
+ {labelName}
16
+ {required && <span style={{ color: '#ff4d4f', marginLeft: 2 }}>*</span>}
17
+ </label>
18
+ ) : null;
19
+
20
+ const filterNumber = (val: string) => val.replace(/[^0-9.]/g, '');
21
+
22
+ if (form) {
23
+ return (
24
+ <div style={{ marginBottom: 16 }}>
25
+ {labelEl}
26
+ {viewOnly ? (
27
+ <div style={{ padding: '4px 11px', border: '1px solid #d9d9d9', borderRadius: 6, minHeight: 32, background: '#fafafa' }}>
28
+ {form.watch(fieldName) || ''}
29
+ </div>
30
+ ) : (
31
+ <Controller
32
+ name={fieldName}
33
+ control={form.control}
34
+ render={({ field, fieldState }) => {
35
+ const isError = !!fieldState.error;
36
+ return (
37
+ <div>
38
+ <Input
39
+ {...field}
40
+ placeholder={placeholder || labelName}
41
+ disabled={disabled}
42
+ status={isError ? 'error' : undefined}
43
+ prefix={prefix}
44
+ suffix={suffix}
45
+ onChange={(e) => {
46
+ const filtered = filterNumber(e.target.value);
47
+ field.onChange({ target: { value: filtered } });
48
+ }}
49
+ />
50
+ {isError && (
51
+ <div style={{ color: '#ff4d4f', fontSize: 12, marginTop: 4 }}>
52
+ {String(fieldState.error?.message || '')}
53
+ </div>
54
+ )}
55
+ {!isError && customMessage && (
56
+ <div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>
57
+ )}
58
+ </div>
59
+ );
60
+ }}
61
+ />
62
+ )}
63
+ </div>
64
+ );
65
+ }
66
+
67
+ return (
68
+ <div style={{ marginBottom: 16 }}>
69
+ {labelEl}
70
+ <Input
71
+ placeholder={placeholder || labelName}
72
+ value={value}
73
+ onChange={(e) => setValue?.(filterNumber(e.target.value))}
74
+ prefix={prefix}
75
+ suffix={suffix}
76
+ />
77
+ {customMessage && <div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>}
78
+ </div>
79
+ );
80
+ }
@@ -0,0 +1,62 @@
1
+ 'use client';
2
+
3
+ import { Controller } from 'react-hook-form';
4
+ import { Switch, Typography } from 'antd';
5
+ import type { SwitchProps } from '../../assets/interface/input-props.type';
6
+
7
+ const { Text } = Typography;
8
+
9
+ export function SwitchField({
10
+ form, name, labelName, required = false, disabled = false,
11
+ viewOnly = false, customMessage, description, border = false,
12
+ value, setValue, onCheckedChange,
13
+ }: SwitchProps) {
14
+ const fieldName = name || labelName?.toLowerCase().replace(/\s+/g, '_') || 'switch';
15
+
16
+ const labelEl = labelName ? (
17
+ <div style={{ marginBottom: 4 }}>
18
+ <Text strong style={{ fontSize: 14 }}>
19
+ {labelName}
20
+ {required && <span style={{ color: '#ff4d4f', marginLeft: 2 }}>*</span>}
21
+ </Text>
22
+ {description && <Text type="secondary" style={{ display: 'block', fontSize: 12 }}>{description}</Text>}
23
+ </div>
24
+ ) : null;
25
+
26
+ const renderSwitch = (checked: boolean, onChange: (v: boolean) => void) => (
27
+ <div style={{
28
+ display: 'flex', alignItems: 'center', gap: 8,
29
+ padding: border ? 12 : 0,
30
+ border: border ? '1px solid #d9d9d9' : 'none',
31
+ borderRadius: border ? 6 : 0,
32
+ }}>
33
+ <Switch
34
+ checked={checked}
35
+ onChange={(v) => { onChange(v); onCheckedChange?.(v); }}
36
+ disabled={disabled || viewOnly}
37
+ />
38
+ {labelEl}
39
+ </div>
40
+ );
41
+
42
+ if (form) {
43
+ return (
44
+ <div style={{ marginBottom: 16 }}>
45
+ <Controller
46
+ name={fieldName}
47
+ control={form.control}
48
+ defaultValue={false}
49
+ render={({ field }) => renderSwitch(!!field.value, (v) => { field.onChange(v); setValue?.(v); })}
50
+ />
51
+ {customMessage && <Text type="secondary" style={{ fontSize: 12 }}>{customMessage}</Text>}
52
+ </div>
53
+ );
54
+ }
55
+
56
+ return (
57
+ <div style={{ marginBottom: 16 }}>
58
+ {renderSwitch(!!value, (v) => setValue?.(v))}
59
+ {customMessage && <Text type="secondary" style={{ fontSize: 12 }}>{customMessage}</Text>}
60
+ </div>
61
+ );
62
+ }
@@ -0,0 +1,85 @@
1
+ 'use client';
2
+
3
+ import { Controller } from 'react-hook-form';
4
+ import { Input } from 'antd';
5
+ import type { TextAreaInputProps } from '../../assets/interface/input-props.type';
6
+
7
+ const { TextArea: AntTextArea } = Input;
8
+
9
+ export function TextArea({
10
+ form, name, labelName, placeholder, required = false, disabled = false,
11
+ viewOnly = false, rows = 3, customMessage, value, setValue,
12
+ maxLength, showCount,
13
+ }: TextAreaInputProps) {
14
+ const fieldName = name || labelName?.toLowerCase().replace(/\s+/g, '_') || 'textarea';
15
+
16
+ const labelEl = labelName ? (
17
+ <label className="font-semibold leading-6 text-[14px] tracking-[0.02em]" style={{ display: 'block', marginBottom: 4 }}>
18
+ {labelName}
19
+ {required && <span style={{ color: '#ff4d4f', marginLeft: 2 }}>*</span>}
20
+ </label>
21
+ ) : null;
22
+
23
+ if (form) {
24
+ return (
25
+ <div style={{ marginBottom: 16 }}>
26
+ {labelEl}
27
+ {viewOnly ? (
28
+ <div style={{ padding: '4px 11px', border: '1px solid #d9d9d9', borderRadius: 6, minHeight: 32, background: '#fafafa', whiteSpace: 'pre-wrap' }}>
29
+ {form.watch(fieldName) || ''}
30
+ </div>
31
+ ) : (
32
+ <Controller
33
+ name={fieldName}
34
+ control={form.control}
35
+ render={({ field, fieldState }) => {
36
+ const isError = !!fieldState.error;
37
+ return (
38
+ <div>
39
+ <AntTextArea
40
+ {...field}
41
+ rows={rows}
42
+ placeholder={placeholder || labelName}
43
+ disabled={disabled}
44
+ status={isError ? 'error' : undefined}
45
+ maxLength={maxLength}
46
+ showCount={showCount}
47
+ />
48
+ {isError && (
49
+ <div style={{ color: '#ff4d4f', fontSize: 12, marginTop: 4 }}>
50
+ {String(fieldState.error?.message || '')}
51
+ </div>
52
+ )}
53
+ {!isError && customMessage && (
54
+ <div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>
55
+ )}
56
+ </div>
57
+ );
58
+ }}
59
+ />
60
+ )}
61
+ </div>
62
+ );
63
+ }
64
+
65
+ return (
66
+ <div style={{ marginBottom: 16 }}>
67
+ {labelEl}
68
+ {viewOnly ? (
69
+ <div style={{ padding: '4px 11px', border: '1px solid #d9d9d9', borderRadius: 6, minHeight: 32, background: '#fafafa', whiteSpace: 'pre-wrap' }}>
70
+ {value || ''}
71
+ </div>
72
+ ) : (
73
+ <AntTextArea
74
+ rows={rows}
75
+ placeholder={placeholder || labelName}
76
+ value={value}
77
+ onChange={(e) => setValue?.(e.target.value)}
78
+ maxLength={maxLength}
79
+ showCount={showCount}
80
+ />
81
+ )}
82
+ {customMessage && <div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>}
83
+ </div>
84
+ );
85
+ }