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,63 @@
1
+ 'use client';
2
+
3
+ import { Button, Tooltip } from 'antd';
4
+ import type { ButtonProps as AntButtonProps } from 'antd';
5
+ import { forwardRef } from 'react';
6
+
7
+ interface ActionButtonProps {
8
+ /** HTML button type: submit / button / reset */
9
+ htmlType?: AntButtonProps['htmlType'];
10
+ /** Visual variant — maps to antd Button `type` */
11
+ variant?: 'primary' | 'dashed' | 'link' | 'text' | 'default';
12
+ btnSize?: 'small' | 'middle' | 'large';
13
+ btnStyle?: React.CSSProperties;
14
+ danger?: boolean;
15
+ tooltipContent?: string;
16
+ buttonContent?: React.ReactNode;
17
+ icon?: React.ReactNode;
18
+ lastIcon?: React.ReactNode;
19
+ isPending?: boolean;
20
+ handleOpen?: (e?: React.MouseEvent<HTMLButtonElement>) => void;
21
+ side?: 'top' | 'right' | 'bottom' | 'left';
22
+ loadingContent?: React.ReactNode;
23
+ disabled?: boolean;
24
+ ghost?: boolean;
25
+ block?: boolean;
26
+ }
27
+
28
+ const ActionButton = forwardRef<HTMLButtonElement, ActionButtonProps>(
29
+ ({ htmlType, btnSize = 'middle', btnStyle, variant = 'primary',
30
+ danger = false, tooltipContent, buttonContent, icon, lastIcon,
31
+ isPending = false, handleOpen, side = 'top', loadingContent,
32
+ disabled = false, ghost, block }, ref) => {
33
+ const buttonEl = (
34
+ <Button
35
+ ref={ref}
36
+ type={variant as any}
37
+ size={btnSize}
38
+ onClick={handleOpen}
39
+ danger={danger}
40
+ disabled={isPending || disabled}
41
+ loading={isPending}
42
+ icon={!isPending ? icon : undefined}
43
+ htmlType={htmlType}
44
+ ghost={ghost}
45
+ block={block}
46
+ style={{ textTransform: 'none', ...btnStyle }}
47
+ >
48
+ {isPending ? (loadingContent ?? buttonContent) : buttonContent}
49
+ {lastIcon && !isPending && <span style={{ marginLeft: 8 }}>{lastIcon}</span>}
50
+ </Button>
51
+ );
52
+
53
+ if (!tooltipContent) return buttonEl;
54
+ return (
55
+ <Tooltip title={tooltipContent} placement={side}>
56
+ <span>{buttonEl}</span>
57
+ </Tooltip>
58
+ );
59
+ }
60
+ );
61
+ ActionButton.displayName = 'ActionButton';
62
+
63
+ export { ActionButton };
@@ -0,0 +1,63 @@
1
+ 'use client';
2
+
3
+ import { Modal, Typography } from 'antd';
4
+ import type { ReactNode } from 'react';
5
+
6
+ const { Text } = Typography;
7
+
8
+ interface DialogWrapperProps {
9
+ open: boolean;
10
+ setOpen: (open: boolean) => void;
11
+ triggerContent?: ReactNode;
12
+ title?: ReactNode;
13
+ description?: string;
14
+ children: ReactNode;
15
+ closer?: boolean;
16
+ footer?: ReactNode;
17
+ width?: number | string;
18
+ style?: React.CSSProperties;
19
+ }
20
+
21
+ export function DialogWrapper({
22
+ open, setOpen, triggerContent, title, description,
23
+ children, closer = true, footer, width = 520, style,
24
+ }: DialogWrapperProps) {
25
+ return (
26
+ <>
27
+ {triggerContent}
28
+ <Modal
29
+ open={open}
30
+ onCancel={() => setOpen(false)}
31
+ title={
32
+ title || description ? (
33
+ <div>
34
+ <div style={{ fontWeight: 600, fontSize: 16 }}>
35
+ {typeof title === 'string' ? title : title}
36
+ </div>
37
+ {description && (
38
+ <Text type="secondary" style={{ fontSize: 13, marginTop: 2, display: 'block' }}>
39
+ {description}
40
+ </Text>
41
+ )}
42
+ </div>
43
+ ) : undefined
44
+ }
45
+ closable={closer}
46
+ footer={footer}
47
+ width={width}
48
+ destroyOnClose
49
+ styles={{
50
+ body: {
51
+ maxHeight: 'calc(90vh - 200px)',
52
+ overflowY: 'auto',
53
+ paddingTop: 16,
54
+ paddingBottom: 16,
55
+ },
56
+ ...(style ? { ...style } : {}),
57
+ }}
58
+ >
59
+ {children}
60
+ </Modal>
61
+ </>
62
+ );
63
+ }
@@ -0,0 +1,55 @@
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 CheckField({
10
+ form, name, labelName, required = false, disabled = false, options = [],
11
+ }: CheckboxProps) {
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 checkOptions = (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
+ <Checkbox.Group
33
+ {...field}
34
+ disabled={disabled}
35
+ options={checkOptions}
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
+ <Checkbox.Group disabled={disabled} options={checkOptions} />
53
+ </div>
54
+ );
55
+ }
@@ -0,0 +1,80 @@
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 { DatePickerProps } from '../../assets/interface/input-props.type';
7
+
8
+ const { Text } = Typography;
9
+
10
+ export function DatePickerField({
11
+ form, name, labelName, placeholder, required = false, disabled = false,
12
+ viewOnly = false, customMessage, picker = 'date',
13
+ }: DatePickerProps) {
14
+ const fieldName = name || labelName?.toLowerCase().replace(/\s+/g, '_') || 'date';
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' }}>
29
+ {form.watch(fieldName) ? dayjs(form.watch(fieldName)).format('YYYY-MM-DD') : ''}
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
+ <DatePicker
40
+ {...field}
41
+ style={{ width: '100%' }}
42
+ placeholder={placeholder || `Select ${labelName || 'date'}`}
43
+ disabled={disabled}
44
+ status={isError ? 'error' : undefined}
45
+ picker={picker}
46
+ value={field.value ? dayjs(field.value) : null}
47
+ onChange={(date) => {
48
+ field.onChange(date ? date.toISOString() : null);
49
+ }}
50
+ />
51
+ {isError && (
52
+ <div style={{ color: '#ff4d4f', fontSize: 12, marginTop: 4 }}>
53
+ {String(fieldState.error?.message || '')}
54
+ </div>
55
+ )}
56
+ {!isError && customMessage && (
57
+ <div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>
58
+ )}
59
+ </div>
60
+ );
61
+ }}
62
+ />
63
+ )}
64
+ </div>
65
+ );
66
+ }
67
+
68
+ return (
69
+ <div style={{ marginBottom: 16 }}>
70
+ {labelEl}
71
+ <DatePicker
72
+ style={{ width: '100%' }}
73
+ placeholder={placeholder || `Select ${labelName || 'date'}`}
74
+ disabled={disabled}
75
+ picker={picker}
76
+ />
77
+ {customMessage && <div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>}
78
+ </div>
79
+ );
80
+ }
@@ -0,0 +1,26 @@
1
+ 'use client';
2
+
3
+ import { Select, Typography, Space } from 'antd';
4
+ import type { LimitFieldProps } from '../../assets/interface/input-props.type';
5
+
6
+ const { Text } = Typography;
7
+
8
+ export function LimitField({
9
+ limit, setLimit, totalItems, options = [5, 10, 20, 50, 100],
10
+ }: LimitFieldProps) {
11
+ return (
12
+ <Space>
13
+ <Text type="secondary" style={{ fontSize: 13 }}>Show</Text>
14
+ <Select
15
+ value={limit}
16
+ onChange={(val) => setLimit(val)}
17
+ style={{ width: 80 }}
18
+ options={options.map((o) => ({ label: String(o), value: String(o) }))}
19
+ size="small"
20
+ />
21
+ <Text type="secondary" style={{ fontSize: 13 }}>
22
+ entries{totalItems !== undefined ? ` (${totalItems} total)` : ''}
23
+ </Text>
24
+ </Space>
25
+ );
26
+ }
@@ -0,0 +1,56 @@
1
+ 'use client';
2
+
3
+ import { Controller } from 'react-hook-form';
4
+ import { Checkbox, Typography, Space } from 'antd';
5
+ import type { CheckboxProps } from '../../assets/interface/input-props.type';
6
+
7
+ const { Text } = Typography;
8
+
9
+ export function MultiCheckField({
10
+ form, name, labelName, required = false, disabled = false, options = [],
11
+ }: CheckboxProps) {
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 checkOptions = (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
+ defaultValue={[]}
31
+ render={({ field, fieldState }) => (
32
+ <div>
33
+ <Checkbox.Group
34
+ {...field}
35
+ disabled={disabled}
36
+ options={checkOptions}
37
+ />
38
+ {fieldState.error && (
39
+ <div style={{ color: '#ff4d4f', fontSize: 12, marginTop: 4 }}>
40
+ {String(fieldState.error.message || '')}
41
+ </div>
42
+ )}
43
+ </div>
44
+ )}
45
+ />
46
+ </div>
47
+ );
48
+ }
49
+
50
+ return (
51
+ <div style={{ marginBottom: 16 }}>
52
+ {labelEl}
53
+ <Checkbox.Group disabled={disabled} options={checkOptions} />
54
+ </div>
55
+ );
56
+ }
@@ -0,0 +1,100 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ 'use client';
3
+
4
+ import { Controller } from 'react-hook-form';
5
+ import { InputNumber } from 'antd';
6
+ import type { NumberInputProps } from '../../assets/interface/input-props.type';
7
+
8
+ /**
9
+ * Number — Ant Design numeric input with:
10
+ * - Integer or float mode
11
+ * - Scroll prevention
12
+ * - Minimum value of 0
13
+ * - Edge case handling
14
+ */
15
+ export function Number({
16
+ form, name, labelName, placeholder, required = false, disabled = false,
17
+ viewOnly = false, numberType = 'float', customMessage,
18
+ min = 0, max, step, prefix, suffix,
19
+ }: NumberInputProps) {
20
+ const fieldName = name || labelName?.toLowerCase().replace(/\s+/g, '_') || 'number';
21
+
22
+ const labelEl = labelName ? (
23
+ <label className="font-semibold leading-6 text-[14px] tracking-[0.02em]" style={{ display: 'block', marginBottom: 4 }}>
24
+ {labelName}
25
+ {required && <span style={{ color: '#ff4d4f', marginLeft: 2 }}>*</span>}
26
+ </label>
27
+ ) : null;
28
+
29
+ const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
30
+ const allowed = ['Backspace', 'Delete', 'Tab', 'Escape', 'Enter', 'ArrowLeft', 'ArrowRight', 'Home', 'End'];
31
+ if (allowed.includes(e.key) || e.ctrlKey || e.metaKey) return;
32
+ if (numberType === 'integer' && !/^\d$/.test(e.key)) { e.preventDefault(); return; }
33
+ if (numberType === 'float') {
34
+ if (e.key === '.' && !(e.target as HTMLInputElement).value.includes('.')) return;
35
+ if (!/^\d$/.test(e.key)) { e.preventDefault(); }
36
+ }
37
+ };
38
+
39
+ const commonProps = {
40
+ style: { width: '100%' as const },
41
+ placeholder: placeholder || labelName,
42
+ disabled,
43
+ min,
44
+ max,
45
+ step: step ?? (numberType === 'integer' ? 1 : 0.1),
46
+ precision: numberType === 'integer' ? 0 : undefined,
47
+ stringMode: false as const,
48
+ onKeyDown: handleKeyDown,
49
+ onWheel: (e: React.WheelEvent<HTMLInputElement>) => (e.target as HTMLInputElement).blur(),
50
+ prefix,
51
+ suffix,
52
+ };
53
+
54
+ if (form) {
55
+ return (
56
+ <div style={{ marginBottom: 16 }}>
57
+ {labelEl}
58
+ {viewOnly ? (
59
+ <div style={{ padding: '4px 11px', border: '1px solid #d9d9d9', borderRadius: 6, minHeight: 32, background: '#fafafa' }}>
60
+ {form.watch(fieldName) ?? ''}
61
+ </div>
62
+ ) : (
63
+ <Controller
64
+ name={fieldName}
65
+ control={form.control}
66
+ render={({ field, fieldState }) => {
67
+ const isError = !!fieldState.error;
68
+ return (
69
+ <div>
70
+ <InputNumber
71
+ {...field}
72
+ {...commonProps}
73
+ status={isError ? 'error' : undefined}
74
+ onChange={(val) => field.onChange(val ?? '')}
75
+ />
76
+ {isError && (
77
+ <div style={{ color: '#ff4d4f', fontSize: 12, marginTop: 4 }}>
78
+ {String(fieldState.error?.message || '')}
79
+ </div>
80
+ )}
81
+ {!isError && customMessage && (
82
+ <div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>
83
+ )}
84
+ </div>
85
+ );
86
+ }}
87
+ />
88
+ )}
89
+ </div>
90
+ );
91
+ }
92
+
93
+ return (
94
+ <div style={{ marginBottom: 16 }}>
95
+ {labelEl}
96
+ <InputNumber {...commonProps} />
97
+ {customMessage && <div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>}
98
+ </div>
99
+ );
100
+ }
@@ -0,0 +1,63 @@
1
+ 'use client';
2
+
3
+ import { Controller } from 'react-hook-form';
4
+ import { Input, Typography } from 'antd';
5
+ import type { OTPProps } from '../../assets/interface/input-props.type';
6
+
7
+ const { Text } = Typography;
8
+
9
+ export function OTP({
10
+ form, name, labelName, required = false, disabled = false,
11
+ length = 6, customMessage,
12
+ }: OTPProps) {
13
+ const fieldName = name || labelName?.toLowerCase().replace(/\s+/g, '_') || 'otp';
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
+ <Controller
27
+ name={fieldName}
28
+ control={form.control}
29
+ render={({ field, fieldState }) => {
30
+ const isError = !!fieldState.error;
31
+ return (
32
+ <div>
33
+ <Input.OTP
34
+ {...field}
35
+ length={length}
36
+ disabled={disabled}
37
+ status={isError ? 'error' : undefined}
38
+ onChange={(val) => field.onChange(val)}
39
+ />
40
+ {isError && (
41
+ <div style={{ color: '#ff4d4f', fontSize: 12, marginTop: 4 }}>
42
+ {String(fieldState.error?.message || '')}
43
+ </div>
44
+ )}
45
+ {!isError && customMessage && (
46
+ <div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>
47
+ )}
48
+ </div>
49
+ );
50
+ }}
51
+ />
52
+ </div>
53
+ );
54
+ }
55
+
56
+ return (
57
+ <div style={{ marginBottom: 16 }}>
58
+ {labelEl}
59
+ <Input.OTP length={length} disabled={disabled} />
60
+ {customMessage && <div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>}
61
+ </div>
62
+ );
63
+ }
@@ -0,0 +1,106 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ 'use client';
3
+
4
+ import { useState } from 'react';
5
+ import { Controller } from 'react-hook-form';
6
+ import { Input } from 'antd';
7
+ import type { PasswordInputProps } from '../../assets/interface/input-props.type';
8
+
9
+ /** Standard password strength rules */
10
+ const passwordRules = [
11
+ { label: 'At least 8 characters', test: (val: string) => val.length >= 8 },
12
+ { label: 'At least one uppercase letter', test: (val: string) => /[A-Z]/.test(val) },
13
+ { label: 'At least one number', test: (val: string) => /\d/.test(val) },
14
+ { label: 'At least one special character', test: (val: string) => /[!@#$%^&*(),.?":{}|<>_\-+=~`[\]\\;/]/.test(val) },
15
+ { label: 'No sequential numbers (e.g. 1234)', test: (val: string) => !/012|123|234|345|456|567|678|789/.test(val) },
16
+ ];
17
+
18
+ export function Password({
19
+ form, name, labelName, placeholder, required = false, disabled = false,
20
+ mode = 'normal', customMessage,
21
+ }: PasswordInputProps) {
22
+ const [passwordValue, setPasswordValue] = useState('');
23
+ const fieldName = name || labelName?.toLowerCase().replace(/\s+/g, '_') || 'password';
24
+ const isValidate = mode === 'validate';
25
+
26
+ const labelEl = (label?: string, req?: boolean) => label ? (
27
+ <label className="font-semibold leading-6 text-[14px] tracking-[0.02em]" style={{ display: 'block', marginBottom: 4 }}>
28
+ {label}
29
+ {req && <span style={{ color: '#ff4d4f', marginLeft: 2 }}>*</span>}
30
+ </label>
31
+ ) : null;
32
+
33
+ const renderStrengthRules = () => {
34
+ if (!isValidate || !passwordValue || passwordRules.every(r => r.test(passwordValue))) return null;
35
+ return (
36
+ <ul style={{ margin: '8px 0 0', padding: 0, listStyle: 'none', fontSize: '0.8rem' }}>
37
+ {passwordRules.map((rule, i) => {
38
+ const passed = rule.test(passwordValue);
39
+ return (
40
+ <li key={i} style={{
41
+ color: passed ? '#16a34a' : '#94a3b8',
42
+ display: 'flex', alignItems: 'center', gap: 6, padding: '2px 0',
43
+ }}>
44
+ <span style={{
45
+ width: 8, height: 8, borderRadius: '50%', display: 'inline-block',
46
+ background: passed ? '#16a34a' : '#d9d9d9',
47
+ }} />
48
+ {rule.label}
49
+ </li>
50
+ );
51
+ })}
52
+ </ul>
53
+ );
54
+ };
55
+
56
+ if (form) {
57
+ return (
58
+ <div style={{ marginBottom: 16 }}>
59
+ {labelEl(labelName, required)}
60
+ <Controller
61
+ name={fieldName}
62
+ control={form.control}
63
+ render={({ field, fieldState }) => {
64
+ const isError = !!fieldState.error;
65
+ return (
66
+ <div>
67
+ <Input.Password
68
+ {...field}
69
+ placeholder={placeholder || `Enter ${labelName || 'password'}`}
70
+ disabled={disabled}
71
+ status={isError ? 'error' : undefined}
72
+ onChange={(e) => {
73
+ field.onChange(e);
74
+ setPasswordValue(e.target.value);
75
+ }}
76
+ />
77
+ {renderStrengthRules()}
78
+ {isError && (
79
+ <div style={{ color: '#ff4d4f', fontSize: 12, marginTop: 4 }}>
80
+ {String(fieldState.error.message || '')}
81
+ </div>
82
+ )}
83
+ {!isError && customMessage && (
84
+ <div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>
85
+ )}
86
+ </div>
87
+ );
88
+ }}
89
+ />
90
+ </div>
91
+ );
92
+ }
93
+
94
+ return (
95
+ <div style={{ marginBottom: 16 }}>
96
+ {labelEl(labelName, required)}
97
+ <Input.Password
98
+ placeholder={placeholder || `Enter ${labelName || 'password'}`}
99
+ disabled={disabled}
100
+ onChange={(e) => setPasswordValue(e.target.value)}
101
+ />
102
+ {renderStrengthRules()}
103
+ {customMessage && <div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>}
104
+ </div>
105
+ );
106
+ }