sme-pos-package 1.0.7 → 1.0.9

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 (318) hide show
  1. package/dist/index.d.mts +37 -2
  2. package/dist/index.mjs +17 -104
  3. package/dist/index.mjs.map +1 -1
  4. package/dist/tailwind.preset.d.mts +27 -0
  5. package/dist/tailwind.preset.mjs +2 -0
  6. package/dist/tailwind.preset.mjs.map +1 -0
  7. package/package.json +18 -30
  8. package/public/assets/images/bank-logos.png +0 -0
  9. package/public/assets/images/banner1.png +0 -0
  10. package/public/assets/images/banner2.png +0 -0
  11. package/public/assets/images/banner3.png +0 -0
  12. package/public/assets/images/banner4.png +0 -0
  13. package/public/assets/images/cashier.svg +9 -0
  14. package/public/assets/images/empty-cart.png +0 -0
  15. package/public/assets/images/empty-table-state.png +0 -0
  16. package/public/assets/images/layout-display/left-layout.png +0 -0
  17. package/public/assets/images/layout-display/right-layout.png +0 -0
  18. package/public/assets/images/modal-illustration.png +0 -0
  19. package/public/assets/images/order-empty-state.png +0 -0
  20. package/public/assets/images/order.png +0 -0
  21. package/public/assets/images/payment-success-label.png +0 -0
  22. package/public/assets/images/table-empty.png +0 -0
  23. package/public/assets/images/take-away.png +0 -0
  24. package/public/assets/lottie/loading.json +1 -0
  25. package/public/assets/lottie/soft_pos.json +1 -0
  26. package/public/assets/lottie/success.json +1 -0
  27. package/public/config.json +27 -0
  28. package/public/env.json +17 -0
  29. package/src/animations/success.json +9730 -0
  30. package/src/assets/icons/breadcrumbs-icon.tsx +85 -0
  31. package/src/assets/icons/cart-icon.tsx +14 -0
  32. package/src/assets/icons/common.tsx +270 -0
  33. package/src/assets/icons/customer-view-icon.tsx +20 -0
  34. package/src/assets/icons/dining-table.tsx +34 -0
  35. package/src/assets/icons/edit-line-icon.tsx +18 -0
  36. package/src/assets/icons/emenu-icon.tsx +39 -0
  37. package/src/assets/icons/file-close-line-icon.tsx +18 -0
  38. package/src/assets/icons/helper-icon.tsx +84 -0
  39. package/src/assets/icons/home-gear-line-icon.tsx +18 -0
  40. package/src/assets/icons/kanban-icon.tsx +16 -0
  41. package/src/assets/icons/loading-icon.tsx +45 -0
  42. package/src/assets/icons/logout-box-line-icon.tsx +18 -0
  43. package/src/assets/icons/order-icon.tsx +92 -0
  44. package/src/assets/icons/payment-icon.tsx +429 -0
  45. package/src/assets/icons/pincode.tsx +196 -0
  46. package/src/assets/icons/pos-connect-icon.tsx +67 -0
  47. package/src/assets/icons/pos-device-icon.tsx +52 -0
  48. package/src/assets/icons/pos-icon.tsx +75 -0
  49. package/src/assets/icons/printer-line-icon.tsx +18 -0
  50. package/src/assets/icons/star-icon.tsx +174 -0
  51. package/src/assets/icons/table-icon.tsx +77 -0
  52. package/src/assets/icons/user-add-line-icon.tsx +33 -0
  53. package/src/assets/icons/user-icon.tsx +65 -0
  54. package/src/components/common/button-action.tsx +46 -0
  55. package/src/components/common/button-icon.tsx +23 -0
  56. package/src/components/common/dropdown/index.tsx +79 -0
  57. package/src/components/common/empty-state.tsx +60 -0
  58. package/src/components/common/header/index.tsx +89 -0
  59. package/src/components/common/header/tabs.tsx +119 -0
  60. package/src/components/common/image.tsx +20 -0
  61. package/src/components/common/input/index.tsx +55 -0
  62. package/src/components/common/input/radio-group.tsx +75 -0
  63. package/src/components/common/input/text-area.tsx +28 -0
  64. package/src/components/common/modal/delete-confirm-modal-container.tsx +65 -0
  65. package/src/components/common/modal/index.ts +2 -0
  66. package/src/components/common/modal/modal-direction.tsx +125 -0
  67. package/src/components/common/modal/modal-header.tsx +37 -0
  68. package/src/components/common/modal/modal-wrapper.tsx +56 -0
  69. package/src/components/common/modal/modal-zoom.tsx +61 -0
  70. package/src/components/common/pagination.tsx +95 -0
  71. package/src/components/common/refresh-button.tsx +28 -0
  72. package/src/components/common/search-bar.tsx +27 -0
  73. package/src/components/common/search.tsx +59 -0
  74. package/src/components/common/skeleton.tsx +7 -0
  75. package/src/components/common/tabs-layout.tsx +98 -0
  76. package/src/components/common/use-check-session.tsx +19 -0
  77. package/src/components/customer-management/add-customer/add-customer-drawer.tsx +286 -0
  78. package/src/components/customer-management/add-customer/address-select.tsx +51 -0
  79. package/src/components/customer-management/choose-customer-drawer.tsx +177 -0
  80. package/src/components/customer-management/customer-info-drawer.tsx +166 -0
  81. package/src/components/customer-management/customer-no-data.tsx +18 -0
  82. package/src/components/emenu/emenu-qr-code.tsx +59 -0
  83. package/src/components/emenu/menu-link.tsx +67 -0
  84. package/src/components/index.ts +8 -0
  85. package/src/components/kanban-template/kitchen-template.tsx +60 -0
  86. package/src/components/kanban-template/pos-order-template.tsx +77 -0
  87. package/src/components/kanban-template/pos-template.tsx +170 -0
  88. package/src/components/kitchen-management/change-status-confirm-modal.tsx +61 -0
  89. package/src/components/kitchen-management/index.ts +1 -0
  90. package/src/components/kitchen-management/kitchen-order/index.tsx +153 -0
  91. package/src/components/kitchen-management/kitchen-order/line.tsx +78 -0
  92. package/src/components/kitchen-management/kitchen-order/state.tsx +52 -0
  93. package/src/components/kitchen-management/kitchen-status.tsx +69 -0
  94. package/src/components/kitchen-management/orders-kitchen-management.tsx +113 -0
  95. package/src/components/order-management/index.tsx +128 -0
  96. package/src/components/order-management/order-actions.tsx +46 -0
  97. package/src/components/order-management/order-detail/header.tsx +37 -0
  98. package/src/components/order-management/order-detail/index.ts +3 -0
  99. package/src/components/order-management/order-detail/information.tsx +96 -0
  100. package/src/components/order-management/order-detail/order-detail.tsx +84 -0
  101. package/src/components/order-management/order-detail/order-line.tsx +56 -0
  102. package/src/components/order-management/order-detail/summary.tsx +221 -0
  103. package/src/components/order-management/order-header.tsx +113 -0
  104. package/src/components/order-management/order-pagination.tsx +49 -0
  105. package/src/components/order-management/order-status.tsx +58 -0
  106. package/src/components/order-management/order-table.tsx +179 -0
  107. package/src/components/order-management/order-tabs.tsx +48 -0
  108. package/src/components/pos-management/add-product-from-barcode/index.tsx +97 -0
  109. package/src/components/pos-management/cart-content/action-line.tsx +81 -0
  110. package/src/components/pos-management/cart-content/cancel-order-line-modal.tsx +41 -0
  111. package/src/components/pos-management/cart-content/cart-item.tsx +173 -0
  112. package/src/components/pos-management/cart-content/cart-items.tsx +130 -0
  113. package/src/components/pos-management/cart-content/draft-orders/draft-order-line.tsx +67 -0
  114. package/src/components/pos-management/cart-content/draft-orders/index.tsx +40 -0
  115. package/src/components/pos-management/cart-content/edit-cart-modal.tsx +11 -0
  116. package/src/components/pos-management/cart-content/index.tsx +260 -0
  117. package/src/components/pos-management/cart-content/no-select-order.tsx +26 -0
  118. package/src/components/pos-management/cart-content/order-info/customer-info.tsx +53 -0
  119. package/src/components/pos-management/cart-content/order-info/index.tsx +109 -0
  120. package/src/components/pos-management/cart-content/order-total.tsx +36 -0
  121. package/src/components/pos-management/cart-content/orders-line.tsx +72 -0
  122. package/src/components/pos-management/menu-content/category-tab.tsx +38 -0
  123. package/src/components/pos-management/menu-content/create-order-modal.tsx +65 -0
  124. package/src/components/pos-management/menu-content/product-content.tsx +93 -0
  125. package/src/components/pos-management/menu-content/product-detail/info.tsx +28 -0
  126. package/src/components/pos-management/menu-content/product-detail/modal.tsx +148 -0
  127. package/src/components/pos-management/menu-content/product-detail/option.tsx +87 -0
  128. package/src/components/pos-management/menu-content/product-detail/quantity-control.tsx +155 -0
  129. package/src/components/pos-management/menu-content/product-detail/sale-keypad.tsx +100 -0
  130. package/src/components/pos-management/menu-content/product-item.tsx +65 -0
  131. package/src/components/pos-management/order-helper/cancel-order-confirm.tsx +43 -0
  132. package/src/components/pos-management/order-helper/history-call.tsx +105 -0
  133. package/src/components/pos-management/order-helper/order-helper-modal.tsx +116 -0
  134. package/src/components/pos-management/payment-content/bill/actions.tsx +46 -0
  135. package/src/components/pos-management/payment-content/bill/content.tsx +207 -0
  136. package/src/components/pos-management/payment-content/bill/index.tsx +18 -0
  137. package/src/components/pos-management/payment-content/card-payment/index.tsx +124 -0
  138. package/src/components/pos-management/payment-content/card-payment/pos-payment/pos-connect.tsx +183 -0
  139. package/src/components/pos-management/payment-content/card-payment/pos-payment/qr-connect.tsx +185 -0
  140. package/src/components/pos-management/payment-content/card-payment/waiting-for-payment-pos-modal.tsx +37 -0
  141. package/src/components/pos-management/payment-content/cash-payment/index.tsx +216 -0
  142. package/src/components/pos-management/payment-content/index.tsx +70 -0
  143. package/src/components/pos-management/payment-content/payment-info.tsx +67 -0
  144. package/src/components/pos-management/payment-content/payment-layout.tsx +16 -0
  145. package/src/components/pos-management/payment-content/payment-method-cards.tsx +99 -0
  146. package/src/components/pos-management/payment-content/qr-payment/index.tsx +118 -0
  147. package/src/components/pos-management/payment-content/qr-payment/qr_code.js +29 -0
  148. package/src/components/pos-management/payment-content/qr-payment/vietqr.js +436 -0
  149. package/src/components/pos-management/payment-content/session-close-modal.tsx +35 -0
  150. package/src/components/pos-management/payment-content/soft-pos-payment/index.tsx +48 -0
  151. package/src/components/pos-management/pos-helpers/back-confirm.tsx +45 -0
  152. package/src/components/pos-management/pos-helpers/index.tsx +135 -0
  153. package/src/components/pos-management/pos-helpers/layout-display-modal.tsx +99 -0
  154. package/src/components/pos-management/search.tsx +48 -0
  155. package/src/components/pos-management/table-content/filtered-tables.tsx +59 -0
  156. package/src/components/pos-management/table-content/floor-tabs.tsx +65 -0
  157. package/src/components/pos-management/table-content/index.tsx +76 -0
  158. package/src/components/pos-management/table-content/table-item.tsx +127 -0
  159. package/src/components/pos-management/table-content/tables.tsx +96 -0
  160. package/src/components/restaurant-management/add-floor-modal.tsx +133 -0
  161. package/src/components/restaurant-management/add-table-modal.tsx +198 -0
  162. package/src/components/restaurant-management/delete-floor-confirm-modal.tsx +42 -0
  163. package/src/components/restaurant-management/delete-table-confirm-modal.tsx +51 -0
  164. package/src/components/restaurant-management/header.tsx +22 -0
  165. package/src/components/restaurant-management/index.tsx +54 -0
  166. package/src/components/restaurant-management/left-side.tsx +51 -0
  167. package/src/components/restaurant-management/right-side.tsx +179 -0
  168. package/src/components/restaurant-management/table-empty.tsx +43 -0
  169. package/src/components/session-management/close-session-modal/actions.tsx +45 -0
  170. package/src/components/session-management/close-session-modal/close-session-confirm.tsx +157 -0
  171. package/src/components/session-management/close-session-modal/close-session-ticket.tsx +218 -0
  172. package/src/components/session-management/close-session-modal/container.tsx +25 -0
  173. package/src/components/session-management/close-session-modal/draft-orders-confirm.tsx +53 -0
  174. package/src/components/session-management/close-session-modal/draft-orders-summary.tsx +82 -0
  175. package/src/components/session-management/close-session-modal/draft-orders.tsx +34 -0
  176. package/src/components/session-management/close-session-modal/index.tsx +35 -0
  177. package/src/components/session-management/close-session-modal/order/index.ts +1 -0
  178. package/src/components/session-management/close-session-modal/order/order-line.tsx +64 -0
  179. package/src/components/session-management/close-session-modal/order/orders-in-draft.tsx +31 -0
  180. package/src/components/session-management/close-session-modal/session-summary.tsx +165 -0
  181. package/src/components/session-management/index.ts +1 -0
  182. package/src/components/session-management/open-shift/index.tsx +172 -0
  183. package/src/components/table-management/index.tsx +0 -0
  184. package/src/constants/api/index.ts +1 -0
  185. package/src/constants/headers.constant.tsx +24 -0
  186. package/src/constants/index.ts +1 -0
  187. package/src/constants/language.store.ts +21 -0
  188. package/src/constants/methods.constant.ts +10 -0
  189. package/src/constants/order-state.ts +8 -0
  190. package/src/constants/payment-color.constant.ts +26 -0
  191. package/src/constants/query.constant.ts +14 -0
  192. package/src/constants/routes.constants.ts +23 -0
  193. package/src/constants/template-name.constant.ts +9 -0
  194. package/src/hooks/bill/use-get-bill-information.ts +49 -0
  195. package/src/hooks/bill/use-print-bill.tsx +0 -0
  196. package/src/hooks/common/load-env-config.ts +98 -0
  197. package/src/hooks/common/use-back.ts +13 -0
  198. package/src/hooks/common/use-broadcast.ts +22 -0
  199. package/src/hooks/common/use-click-outside.ts +58 -0
  200. package/src/hooks/common/use-get-base64-photo.ts +71 -0
  201. package/src/hooks/common/use-get-query.ts +25 -0
  202. package/src/hooks/common/use-pos.ts +21 -0
  203. package/src/hooks/common/use-query-params.ts +89 -0
  204. package/src/hooks/common/use-speech.ts +36 -0
  205. package/src/hooks/customer/use-customer-provider.ts +247 -0
  206. package/src/hooks/floor/use-floor-provider.ts +172 -0
  207. package/src/hooks/kitchen/use-kitchen-provider.ts +80 -0
  208. package/src/hooks/order/use-get-point-reward.ts +50 -0
  209. package/src/hooks/order/use-loop-fetch-order.ts +30 -0
  210. package/src/hooks/order/use-order-items.ts +61 -0
  211. package/src/hooks/order/use-orders.ts +76 -0
  212. package/src/hooks/payment/use-payment-provider.ts +221 -0
  213. package/src/hooks/product/use-product-provider.ts +87 -0
  214. package/src/hooks/session/use-check-session.tsx +19 -0
  215. package/src/hooks/session/use-session-provider.ts +79 -0
  216. package/src/hooks/table/use-pos-table-provider.ts +193 -0
  217. package/src/index.css +5 -0
  218. package/src/index.ts +10 -0
  219. package/src/layouts/common.tsx +15 -0
  220. package/src/layouts/index.ts +4 -0
  221. package/src/layouts/pos-layout.tsx +33 -0
  222. package/src/layouts/pos-order-layout.tsx +43 -0
  223. package/src/layouts/sale-layout.tsx +25 -0
  224. package/src/locales/en.json +394 -0
  225. package/src/locales/vi.json +400 -0
  226. package/src/pages/index.ts +4 -0
  227. package/src/pages/login/index.tsx +128 -0
  228. package/src/pages/pincode/index.tsx +198 -0
  229. package/src/pages/pos/kitchen-management/layout.tsx +18 -0
  230. package/src/pages/pos/management-floor/index.tsx +7 -0
  231. package/src/pages/pos/management-order/index.tsx +7 -0
  232. package/src/pages/pos/management-order/order-detail/index.tsx +7 -0
  233. package/src/pages/pos/management-table/index.tsx +0 -0
  234. package/src/pages/pos/payment-success/index.tsx +75 -0
  235. package/src/provider/app-provider.tsx +56 -0
  236. package/src/provider/fallback/custom-error.tsx +26 -0
  237. package/src/provider/main-provider.tsx +41 -0
  238. package/src/provider/meta-provider.tsx +72 -0
  239. package/src/provider/modal-provider.tsx +48 -0
  240. package/src/provider/notification-provider.tsx +64 -0
  241. package/src/provider/pos-provider/close-session-provider/index.tsx +207 -0
  242. package/src/provider/pos-provider/customer-provider/index.tsx +30 -0
  243. package/src/provider/pos-provider/floor-provider/index.tsx +39 -0
  244. package/src/provider/pos-provider/index.ts +6 -0
  245. package/src/provider/pos-provider/kitchen-provider/index.tsx +30 -0
  246. package/src/provider/pos-provider/order-detail-provider/index.tsx +233 -0
  247. package/src/provider/pos-provider/order-provider/index.tsx +803 -0
  248. package/src/provider/pos-provider/payment-provider/index.tsx +30 -0
  249. package/src/provider/pos-provider/product-provider/index.tsx +30 -0
  250. package/src/provider/pos-provider/sale-provider/index.tsx +168 -0
  251. package/src/provider/pos-provider/session-provider/index.tsx +30 -0
  252. package/src/provider/pos-provider/socket-provider/index.tsx +118 -0
  253. package/src/provider/pos-provider/table-provider/index.tsx +30 -0
  254. package/src/provider/public-provider.tsx +104 -0
  255. package/src/provider/use-session-provider.ts +79 -0
  256. package/src/router/app-router.tsx +38 -0
  257. package/src/router/index.tsx +26 -0
  258. package/src/router/pos-route.tsx +75 -0
  259. package/src/router/protected-route.tsx +16 -0
  260. package/src/router/public-route.tsx +11 -0
  261. package/src/store/header/index.ts +21 -0
  262. package/src/store/session/index.ts +25 -0
  263. package/src/store/states/cart.state.ts +135 -0
  264. package/src/store/states/close-session.state.ts +20 -0
  265. package/src/store/states/customer.state.ts +19 -0
  266. package/src/store/states/payment.state.ts +14 -0
  267. package/src/styles/base.css +212 -0
  268. package/src/styles/common.css +65 -0
  269. package/src/styles/lib/tailwind.css +0 -0
  270. package/src/styles/lib/tailwind.theme.css +34 -0
  271. package/src/styles/loading.css +72 -0
  272. package/src/styles/main.css +5 -0
  273. package/src/styles/refreshing-content.css +55 -0
  274. package/src/styles/text.css +44 -0
  275. package/src/types/account.ts +61 -0
  276. package/src/types/branch.ts +6 -0
  277. package/src/types/category.ts +13 -0
  278. package/src/types/common.ts +4 -0
  279. package/src/types/customer.ts +52 -0
  280. package/src/types/dashboard.ts +18 -0
  281. package/src/types/edc.ts +37 -0
  282. package/src/types/export-e-invoice.ts +17 -0
  283. package/src/types/filter.d.ts +12 -0
  284. package/src/types/floor/floor.ts +16 -0
  285. package/src/types/floor.d.ts +36 -0
  286. package/src/types/index.ts +127 -0
  287. package/src/types/invoice.ts +32 -0
  288. package/src/types/modal.ts +15 -0
  289. package/src/types/notification.ts +12 -0
  290. package/src/types/order.ts +182 -0
  291. package/src/types/payment.ts +44 -0
  292. package/src/types/product/product.ts +47 -0
  293. package/src/types/product.ts +137 -0
  294. package/src/types/provider-info.d.ts +14 -0
  295. package/src/types/qr.d.ts +25 -0
  296. package/src/types/report.d.ts +43 -0
  297. package/src/types/sale.d.ts +68 -0
  298. package/src/types/search.d.ts +7 -0
  299. package/src/types/session.d.ts +30 -0
  300. package/src/types/shop-qr.d.ts +89 -0
  301. package/src/types/staff.d.ts +35 -0
  302. package/src/types/store.d.ts +25 -0
  303. package/src/types/table.d.ts +57 -0
  304. package/src/utils/close-session-ticket.tsx +129 -0
  305. package/src/utils/functions.ts +299 -0
  306. package/src/utils/i18n.ts +25 -0
  307. package/src/utils/pos/bill.ts +156 -0
  308. package/src/utils/pos/convert-order.ts +58 -0
  309. package/src/utils/pos/helpers.ts +48 -0
  310. package/src/utils/pos/session.ts +44 -0
  311. package/src/utils/storages/index.ts +1 -0
  312. package/src/utils/storages/local-storage.ts +72 -0
  313. package/tailwind.preset.ts +25 -0
  314. package/tsconfig.json +30 -0
  315. package/tsup.config.ts +30 -0
  316. package/dist/index.d.ts +0 -58
  317. package/dist/index.js +0 -145
  318. package/dist/index.js.map +0 -1
@@ -0,0 +1,75 @@
1
+ import { cn } from "@utils/functions";
2
+
3
+ type TRadioOption = {
4
+ id: number;
5
+ name: string;
6
+ };
7
+
8
+ type RadioGroupProps = {
9
+ title: string;
10
+ name: string;
11
+ options: TRadioOption[];
12
+ register: any;
13
+ isRequired?: boolean;
14
+ errorMessage?: string;
15
+ direction?: "row" | "column";
16
+ className?: string;
17
+ optionClassName?: string;
18
+ };
19
+
20
+ const RadioGroup = ({
21
+ title,
22
+ options,
23
+ register,
24
+ isRequired,
25
+ errorMessage,
26
+ direction = "row",
27
+ className,
28
+ optionClassName,
29
+ }: RadioGroupProps) => {
30
+ return (
31
+ <div className="flex flex-col gap-1">
32
+ <div
33
+ className={cn(
34
+ "flex w-full flex-col justify-between gap-1.5",
35
+ className
36
+ )}
37
+ >
38
+ <p className="text-text-primary text-sm font-medium leading-5 tracking-[-0.12px]">
39
+ {title}
40
+ {isRequired && <span className="text-state-error-base"> *</span>}
41
+ </p>
42
+
43
+ <div
44
+ className={cn(
45
+ direction === "row" ? "flex gap-4" : "flex flex-col gap-2"
46
+ )}
47
+ >
48
+ {options.map((opt) => (
49
+ <label
50
+ key={opt.id}
51
+ className={cn(
52
+ "flex items-center gap-2 cursor-pointer",
53
+ optionClassName
54
+ )}
55
+ >
56
+ <input
57
+ type="radio"
58
+ value={opt.id}
59
+ {...register}
60
+ className="w-4 h-4 cursor-pointer bg-white border border-neutral-300-85"
61
+ />
62
+ <span className="text-sm text-text-primary">{opt.name}</span>
63
+ </label>
64
+ ))}
65
+ </div>
66
+ </div>
67
+
68
+ {errorMessage && (
69
+ <p className="text-state-error-base text-xs">{errorMessage}</p>
70
+ )}
71
+ </div>
72
+ );
73
+ };
74
+
75
+ export default RadioGroup;
@@ -0,0 +1,28 @@
1
+ import { UseFormRegisterReturn } from "react-hook-form";
2
+
3
+ type Props = React.InputHTMLAttributes<HTMLTextAreaElement> & {
4
+ title: string;
5
+ register?: UseFormRegisterReturn;
6
+ };
7
+
8
+ const TextArea = ({ title, register, ...props }: Props) => {
9
+ return (
10
+ <div className="flex flex-col gap-1.5 justify-center">
11
+ <p className="text-text-primary text-sm font-medium leading-5 tracking-[-0.12px]">
12
+ {title}
13
+ </p>
14
+
15
+ <div className="flex flex-col justify-end gap-2 w-full p-3 rounded-lg border border-stroke-disabled bg-white">
16
+ <textarea
17
+ {...props}
18
+ className="outline-none border-none"
19
+ placeholder={props.placeholder}
20
+ />
21
+
22
+ <p className="text-xs leading-4 text-end text-neutral-400-68">0/3000</p>
23
+ </div>
24
+ </div>
25
+ );
26
+ };
27
+
28
+ export default TextArea;
@@ -0,0 +1,65 @@
1
+ import { IcClose } from "@assets/icons/common";
2
+ import ButtonAction from "@components/common/button-action";
3
+ import React from "react";
4
+ import { useTranslation } from "react-i18next";
5
+ import ModalZoom from "./modal-zoom";
6
+
7
+ type TProps = {
8
+ isOpen: boolean;
9
+ onClose: () => void;
10
+ onAction: () => void;
11
+ title: string;
12
+ description: string | React.ReactNode;
13
+ };
14
+
15
+ const DeleteConfirmModalContainer = ({
16
+ isOpen,
17
+ onClose,
18
+ onAction,
19
+ title,
20
+ description,
21
+ }: TProps) => {
22
+ const { t } = useTranslation();
23
+
24
+ return (
25
+ <ModalZoom
26
+ isOpen={isOpen}
27
+ onClose={onClose}
28
+ className="p-6 flex flex-col gap-6 rounded-2xl border border-stroke-disabled w-[420px]"
29
+ >
30
+ <div className="flex items-center justify-between">
31
+ <p className="text-xl font-semibold leading-7 tracking-[-0.2px] text-text-primary">
32
+ {title}
33
+ </p>
34
+
35
+ <button onClick={onClose} className="text-text-primary">
36
+ <IcClose />
37
+ </button>
38
+ </div>
39
+
40
+ <div className="w-[232px] h-[164px] mx-auto">
41
+ <img src="/assets/images/modal-illustration.png" alt="Delete table" />
42
+ </div>
43
+
44
+ <p className="text-sm font-medium leading-5 tracking-[-0.12px] text-text-disabled">
45
+ {description}
46
+ </p>
47
+
48
+ <div className="flex gap-2 w-full">
49
+ <ButtonAction
50
+ onClick={onClose}
51
+ text={t("cancel_button")}
52
+ className="w-full button-secondary"
53
+ />
54
+
55
+ <ButtonAction
56
+ text={t("confirm_button")}
57
+ onClick={onAction}
58
+ className="w-full button-primary"
59
+ />
60
+ </div>
61
+ </ModalZoom>
62
+ );
63
+ };
64
+
65
+ export default DeleteConfirmModalContainer;
@@ -0,0 +1,2 @@
1
+ export { default as ModalDirection } from "./modal-direction";
2
+ export { default as ModalWrapper } from "./modal-wrapper";
@@ -0,0 +1,125 @@
1
+ import { useModal } from "@provider/modal-provider";
2
+ import { motion } from "framer-motion";
3
+ import { t } from "i18next";
4
+ import { useMemo } from "react";
5
+ import ButtonAction from "../button-action";
6
+ import ModalWrapper, { ModalTransition } from "./modal-wrapper";
7
+
8
+ interface ModalDirectionProps extends ModalProps {
9
+ direction?: "left" | "right" | "bottom";
10
+ safeAreaClassName?: string;
11
+ actionText?: string;
12
+ onAction?: () => void;
13
+ title?: string;
14
+ }
15
+
16
+ const ModalDirection = (
17
+ props: Omit<ModalDirectionProps, "onClose"> & { onClose?: () => void }
18
+ ) => {
19
+ const { isAppScale } = useModal();
20
+ const {
21
+ direction = "right",
22
+ isOpen,
23
+ onClose,
24
+ children,
25
+ className,
26
+ title,
27
+ actionText,
28
+ onAction,
29
+ } = props;
30
+
31
+ const variant = useMemo(() => {
32
+ let res: any = {};
33
+ if (direction == "left" || direction == "right") {
34
+ const initial = ((direction == "left" ? -1 : 1) * 100).toString();
35
+ res = {
36
+ initial: { x: `${initial}%` },
37
+ animate: { x: "0" },
38
+ exit: { x: `${initial}%` },
39
+ };
40
+ } else {
41
+ const initial = ((direction == "bottom" ? 1 : -1) * 100).toString();
42
+ res = {
43
+ initial: { y: `${initial}%` },
44
+ animate: { y: "0" },
45
+ exit: { y: `${initial}%` },
46
+ };
47
+ }
48
+
49
+ delete res["exit"];
50
+
51
+ return res;
52
+ }, [direction]);
53
+
54
+ return (
55
+ <ModalWrapper isOpen={isOpen}>
56
+ <div
57
+ data-scale={isAppScale}
58
+ className={`direction-modal group size-full bg-black/35 pt-safe-area-inset-top transition-all duration-150 data-[scale=true]:!bg-black/100 ${
59
+ props.safeAreaClassName ?? ""
60
+ } flex ${direction == "right" ? "justify-end" : ""}`}
61
+ onClick={() => onClose && onClose()}
62
+ >
63
+ <motion.div
64
+ {...variant}
65
+ {...ModalTransition}
66
+ className={`size-full bg-white ${direction ? "w-[420px]" : ""} p-4`}
67
+ onClick={(e) => e.stopPropagation()}
68
+ >
69
+ <div
70
+ className={`relative size-full rounded-none transition-all duration-300 group-data-[scale=true]:scale-[0.94] group-data-[scale=true]:overflow-hidden group-data-[scale=true]:rounded-xl`}
71
+ onClick={() => {
72
+ if (onClose) onClose();
73
+ }}
74
+ >
75
+ <div
76
+ className={`relative flex size-full flex-col gap-6 ${className}`}
77
+ >
78
+ <div className="flex justify-between items-center">
79
+ <p className="text-lg font-semibold text-[#060606]">{title}</p>
80
+ <button onClick={onClose}>
81
+ <svg
82
+ xmlns="http://www.w3.org/2000/svg"
83
+ width="24"
84
+ height="24"
85
+ viewBox="0 0 24 24"
86
+ fill="none"
87
+ >
88
+ <path
89
+ d="M12.0001 10.7275L16.4551 6.27246L17.7277 7.54506L13.2727 12.0001L17.7277 16.4551L16.4551 17.7277L12.0001 13.2727L7.54506 17.7277L6.27246 16.4551L10.7275 12.0001L6.27246 7.54506L7.54506 6.27246L12.0001 10.7275Z"
90
+ fill="#060606"
91
+ />
92
+ </svg>
93
+ </button>
94
+ </div>
95
+ <div
96
+ className="w-full flex-1 overflow-auto"
97
+ onClick={(e) => {
98
+ e.stopPropagation();
99
+ }}
100
+ >
101
+ {children}
102
+ </div>
103
+
104
+ {actionText && (
105
+ <div className="flex gap-2 w-full">
106
+ <ButtonAction
107
+ text={t("cancel_button")}
108
+ className="button-secondary w-full"
109
+ />
110
+ <ButtonAction
111
+ onClick={onAction}
112
+ text={actionText}
113
+ className="button-primary w-full"
114
+ />
115
+ </div>
116
+ )}
117
+ </div>
118
+ </div>
119
+ </motion.div>
120
+ </div>
121
+ </ModalWrapper>
122
+ );
123
+ };
124
+
125
+ export default ModalDirection;
@@ -0,0 +1,37 @@
1
+ import { IcClose } from "@assets/icons/common";
2
+ import { cn } from "@utils/functions";
3
+
4
+ const ModalHeader = ({
5
+ onClose,
6
+ title,
7
+ headerClassName,
8
+ headerAction,
9
+ }: {
10
+ onClose: () => void;
11
+ title?: string;
12
+ headerClassName?: string;
13
+ headerAction?: React.ReactNode;
14
+ }) => {
15
+ return (
16
+ <div
17
+ className={cn(
18
+ "p-6 py-4 flex items-center justify-between border-b border-stroke-disabled",
19
+ headerClassName
20
+ )}
21
+ >
22
+ <div className="flex items-center gap-2">
23
+ <p className="font-semibold leading-6 text-text-primary tracking-[-0.24px]">
24
+ {title}
25
+ </p>
26
+
27
+ {headerAction}
28
+ </div>
29
+
30
+ <button onClick={onClose}>
31
+ <IcClose />
32
+ </button>
33
+ </div>
34
+ );
35
+ };
36
+
37
+ export default ModalHeader;
@@ -0,0 +1,56 @@
1
+ import { useMemo } from "react";
2
+ import { createPortal } from "react-dom";
3
+ import { AnimatePresence, motion } from "motion/react";
4
+
5
+ const ModalWrapper = ({
6
+ isOpen,
7
+ children,
8
+ className,
9
+ id,
10
+ }: {
11
+ isOpen: boolean;
12
+ children: React.ReactNode;
13
+ className?: string;
14
+ id?: string;
15
+ }) => {
16
+ const container = useMemo(() => document.getElementById(id ?? "root"), [id]);
17
+
18
+ return createPortal(
19
+ <AnimatePresence mode="wait">
20
+ {isOpen && (
21
+ <motion.div
22
+ className={`fixed inset-0 z-50 flex items-center justify-center ${
23
+ className ?? ""
24
+ }`}
25
+ onTouchStart={(e) => e.stopPropagation()}
26
+ onScroll={(e) => e.stopPropagation()}
27
+ onClick={(e) => e.stopPropagation()}
28
+ onKeyDown={(e) => e.stopPropagation()}
29
+ >
30
+ <div className="size-full overflow-hidden sm:w-limitWidth">
31
+ {children}
32
+ </div>
33
+ </motion.div>
34
+ )}
35
+ </AnimatePresence>,
36
+ container ?? document.body
37
+ );
38
+ };
39
+
40
+ export default ModalWrapper;
41
+
42
+ export interface ModalProps {
43
+ children?: React.ReactNode;
44
+ isOpen: boolean;
45
+ onClose: () => void;
46
+ className?: string;
47
+ classNameIosPb?: string;
48
+ header?: React.ReactNode;
49
+ footer?: React.ReactNode;
50
+ disableChangeColorTheme?: boolean;
51
+ isHideHeader?: boolean;
52
+ }
53
+
54
+ export const ModalTransition = {
55
+ transition: { duration: 0.3 },
56
+ };
@@ -0,0 +1,61 @@
1
+ import { cn } from "@utils/functions";
2
+ import { motion } from "motion/react";
3
+ import ModalHeader from "./modal-header";
4
+ import ModalWrapper from "./modal-wrapper";
5
+
6
+ const ModalTransition = {
7
+ transition: { duration: 0.3 },
8
+ };
9
+
10
+ const ModalZoom = (props: ModalProps) => {
11
+ const {
12
+ isOpen,
13
+ onClose,
14
+ children,
15
+ className,
16
+ title,
17
+ footer,
18
+ modalClassName,
19
+ headerClassName,
20
+ headerAction,
21
+ } = props;
22
+ return (
23
+ <ModalWrapper isOpen={isOpen}>
24
+ <motion.div
25
+ initial={{ opacity: 0 }}
26
+ animate={{ opacity: 1 }}
27
+ exit={{ opacity: 0 }}
28
+ {...ModalTransition}
29
+ className={`flex size-full items-center justify-center bg-overlay pt-safe-area-inset-top`}
30
+ onClick={(e) => {
31
+ onClose();
32
+ e.stopPropagation();
33
+ }}
34
+ >
35
+ <motion.div
36
+ {...ModalTransition}
37
+ onClick={(e) => e.stopPropagation()}
38
+ className={cn(
39
+ "relative h-fit max-h-[80%] max-w-[80%] bg-white rounded-2xl flex flex-col overflow-hidden",
40
+ modalClassName
41
+ )}
42
+ >
43
+ {title && (
44
+ <ModalHeader
45
+ onClose={onClose}
46
+ title={title}
47
+ headerClassName={headerClassName}
48
+ headerAction={headerAction}
49
+ />
50
+ )}
51
+ <div className={cn("overflow-y-auto p-6 flex-1", className)}>
52
+ {children}
53
+ </div>
54
+ {footer && <div className="sticky bottom-0">{footer}</div>}
55
+ </motion.div>
56
+ </motion.div>
57
+ </ModalWrapper>
58
+ );
59
+ };
60
+
61
+ export default ModalZoom;
@@ -0,0 +1,95 @@
1
+ import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from 'lucide-react';
2
+ import React from 'react';
3
+
4
+ interface PaginationProps {
5
+ page: number;
6
+ totalPages: number;
7
+ onPageChange: (page: number) => void;
8
+ }
9
+
10
+ export const Pagination: React.FC<PaginationProps> = ({ page, totalPages, onPageChange }) => {
11
+ const generatePages = () => {
12
+ const pages: (number | string)[] = [];
13
+ if (totalPages <= 6) {
14
+ for (let i = 1; i <= totalPages; i++) pages.push(i);
15
+ } else {
16
+ if (page <= 3) pages.push(1, 2, 3, 4, '...', totalPages);
17
+ else if (page >= totalPages - 2)
18
+ pages.push(1, '...', totalPages - 3, totalPages - 2, totalPages - 1, totalPages);
19
+ else pages.push(1, '...', page - 1, page, page + 1, '...', totalPages);
20
+ }
21
+ return pages;
22
+ };
23
+
24
+ const pages = generatePages();
25
+
26
+ const goToPage = (p: number) => {
27
+ if (p < 1 || p > totalPages || p === page) return;
28
+ onPageChange(p);
29
+ };
30
+
31
+ return (
32
+ <div className="flex items-center justify-center gap-1 select-none">
33
+ {totalPages > 1 && (
34
+ <button
35
+ onClick={() => goToPage(1)}
36
+ disabled={page === 1}
37
+ className="p-1.5 disabled:opacity-30 hover:bg-gray-100 rounded"
38
+ >
39
+ <ChevronsLeft size={18} />
40
+ </button>
41
+ )}
42
+
43
+ {totalPages > 1 && (
44
+ <button
45
+ onClick={() => goToPage(page - 1)}
46
+ disabled={page === 1}
47
+ className="p-1.5 disabled:opacity-30 hover:bg-gray-100 rounded"
48
+ >
49
+ <ChevronLeft size={18} />
50
+ </button>
51
+ )}
52
+
53
+ {pages.map((p, i) =>
54
+ p === '...' ? (
55
+ <span key={i} className="px-2 text-gray-500">
56
+ ...
57
+ </span>
58
+ ) : (
59
+ <button
60
+ key={i}
61
+ onClick={() => goToPage(p as number)}
62
+ className={`w-8 h-8 rounded-md text-sm flex items-center justify-center transition-all
63
+ ${
64
+ p === page
65
+ ? 'bg-[#392F86] text-white font-semibold'
66
+ : 'text-gray-800 hover:bg-gray-100'
67
+ }`}
68
+ >
69
+ {p}
70
+ </button>
71
+ ),
72
+ )}
73
+
74
+ {totalPages > 1 && (
75
+ <button
76
+ onClick={() => goToPage(page + 1)}
77
+ disabled={page === totalPages}
78
+ className="p-1.5 disabled:opacity-30 hover:bg-gray-100 rounded"
79
+ >
80
+ <ChevronRight size={18} />
81
+ </button>
82
+ )}
83
+
84
+ {totalPages > 1 && (
85
+ <button
86
+ onClick={() => goToPage(totalPages)}
87
+ disabled={page === totalPages}
88
+ className="p-1.5 disabled:opacity-30 hover:bg-gray-100 rounded"
89
+ >
90
+ <ChevronsRight size={18} />
91
+ </button>
92
+ )}
93
+ </div>
94
+ );
95
+ };
@@ -0,0 +1,28 @@
1
+ import { IcRefresh } from '@assets/icons/breadcrumbs-icon';
2
+ import { useState } from 'react';
3
+
4
+ const RefreshButton = ({ onRefresh }: { onRefresh: () => void }) => {
5
+ const [isRefresh, setIsRefresh] = useState(false);
6
+
7
+ setTimeout(() => {
8
+ if (isRefresh) {
9
+ setIsRefresh(false);
10
+ }
11
+ }, 3000);
12
+
13
+ return (
14
+ <button
15
+ className={`size-11 flex items-center justify-center text-[--text-color] hover:bg-gray-100 rounded-lg border bg-white shadow-sm`}
16
+ onClick={() => {
17
+ setIsRefresh(true);
18
+ onRefresh();
19
+ }}
20
+ >
21
+ <div className={`${isRefresh ? 'spin' : ''}`}>
22
+ <IcRefresh />
23
+ </div>
24
+ </button>
25
+ );
26
+ };
27
+
28
+ export default RefreshButton;
@@ -0,0 +1,27 @@
1
+ import { IcSearch } from "@assets/icons/breadcrumbs-icon";
2
+ import { t } from "i18next";
3
+
4
+ export const SearchBar = ({
5
+ onSearchChange,
6
+ placeholder,
7
+ className,
8
+ }: {
9
+ onSearchChange: (value: string) => void;
10
+ placeholder?: string;
11
+ className?: string;
12
+ }) => {
13
+ return (
14
+ <div className={`relative flex-1 max-w-md ${className}`}>
15
+ <div className="absolute left-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400">
16
+ <IcSearch />
17
+ </div>
18
+ <input
19
+ type="text"
20
+ placeholder={placeholder ?? t("search...")}
21
+ onChange={(e) => onSearchChange(e.target.value)}
22
+ className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:outline-none
23
+ focus:ring-[2px] focus:ring-gray-300 focus:border-transparent text-sm shadow-[0_2px_4px_0_rgba(27,28,29,0.04)]"
24
+ />
25
+ </div>
26
+ );
27
+ };
@@ -0,0 +1,59 @@
1
+ import { IcRefresh, IcSearch } from "@assets/icons/breadcrumbs-icon";
2
+ import { IcBack, IcPlus } from "@assets/icons/common";
3
+
4
+ interface SearchAndActionsProps {
5
+ searchValue: string;
6
+ onSearchChange: (value: string) => void;
7
+ actionText?: string;
8
+ showReset?: boolean;
9
+ showAction?: boolean;
10
+ showDropdown?: boolean;
11
+ }
12
+
13
+ export const SearchAndActions: React.FC<SearchAndActionsProps> = ({
14
+ searchValue,
15
+ onSearchChange,
16
+ actionText = "",
17
+ showReset = true,
18
+ showAction = true,
19
+ showDropdown = false,
20
+ }) => {
21
+ return (
22
+ <div className="bg-white px-6 py-6 flex items-center justify-between border-b border-gray-100">
23
+ <div className="relative flex-1 max-w-md">
24
+ <IcSearch className="absolute left-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-[#AEAEAE]" />
25
+ <input
26
+ type="text"
27
+ placeholder="Tìm kiếm..."
28
+ value={searchValue}
29
+ onChange={(e) => onSearchChange(e.target.value)}
30
+ className="w-full pl-10 pr-12 py-2 border border-[#E8E8EA] rounded-lg text-sm
31
+ focus:outline-none focus:ring-2 focus:ring-[#363793]
32
+ focus:border-transparent bg-white text-[#060606] placeholder-[#AEAEAE]"
33
+ />
34
+ {showDropdown && (
35
+ <div className="absolute right-3 top-1/2 transform -translate-y-1/2 flex items-center text-[#AEAEAE]">
36
+ <div className="w-px h-5 bg-[#E8E8EA] mr-2"></div>
37
+ <div className="rotate-90">
38
+ <IcBack />
39
+ </div>
40
+ </div>
41
+ )}
42
+ </div>
43
+
44
+ <div className="flex items-center space-x-3 ml-4">
45
+ {showReset && (
46
+ <button className="w-11 h-11 flex items-center justify-center text-[#060606] hover:bg-gray-50 rounded-xl border border-[#E8E8EA] bg-white shadow-sm transition-colors">
47
+ <IcRefresh />
48
+ </button>
49
+ )}
50
+ {showAction && (
51
+ <button className="bg-[#363793] text-white w-30 h-11 px-4 py-2 rounded-md text-sm font-medium hover:bg-[#2D2B7A] transition-colors flex items-center gap-2">
52
+ <IcPlus />
53
+ <span>{actionText}</span>
54
+ </button>
55
+ )}
56
+ </div>
57
+ </div>
58
+ );
59
+ };
@@ -0,0 +1,7 @@
1
+ import { cn } from "@utils/functions";
2
+
3
+ export const Skeleton = ({ className }: { className?: string }) => {
4
+ return (
5
+ <div className={cn("w-full animate-pulse bg-gray-200", className)}></div>
6
+ );
7
+ };