@goplusvn/core 0.1.1 → 0.1.2

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 (223) hide show
  1. package/package.json +30 -175
  2. package/dist/audit/index.d.mts +0 -115
  3. package/dist/audit/index.d.ts +0 -115
  4. package/dist/audit/index.js +0 -204
  5. package/dist/audit/index.js.map +0 -1
  6. package/dist/audit/index.mjs +0 -200
  7. package/dist/audit/index.mjs.map +0 -1
  8. package/dist/auth/index.d.mts +0 -86
  9. package/dist/auth/index.d.ts +0 -86
  10. package/dist/auth/index.js +0 -210
  11. package/dist/auth/index.js.map +0 -1
  12. package/dist/auth/index.mjs +0 -198
  13. package/dist/auth/index.mjs.map +0 -1
  14. package/dist/button-1dWvP9Ib.d.mts +0 -30
  15. package/dist/button-1dWvP9Ib.d.ts +0 -30
  16. package/dist/calendar-2QzdEo1z.d.mts +0 -20
  17. package/dist/calendar-2QzdEo1z.d.ts +0 -20
  18. package/dist/code-generation/index.d.mts +0 -30
  19. package/dist/code-generation/index.d.ts +0 -30
  20. package/dist/code-generation/index.js +0 -31
  21. package/dist/code-generation/index.js.map +0 -1
  22. package/dist/code-generation/index.mjs +0 -28
  23. package/dist/code-generation/index.mjs.map +0 -1
  24. package/dist/configs/index.d.mts +0 -175
  25. package/dist/configs/index.d.ts +0 -175
  26. package/dist/configs/index.js +0 -254
  27. package/dist/configs/index.js.map +0 -1
  28. package/dist/configs/index.mjs +0 -233
  29. package/dist/configs/index.mjs.map +0 -1
  30. package/dist/crud/index.d.mts +0 -646
  31. package/dist/crud/index.d.ts +0 -646
  32. package/dist/crud/index.js +0 -11772
  33. package/dist/crud/index.js.map +0 -1
  34. package/dist/crud/index.mjs +0 -11665
  35. package/dist/crud/index.mjs.map +0 -1
  36. package/dist/crud/server.d.mts +0 -20
  37. package/dist/crud/server.d.ts +0 -20
  38. package/dist/crud/server.js +0 -123
  39. package/dist/crud/server.js.map +0 -1
  40. package/dist/crud/server.mjs +0 -120
  41. package/dist/crud/server.mjs.map +0 -1
  42. package/dist/data-table-skeleton-12NA8Mjx.d.mts +0 -39
  43. package/dist/data-table-skeleton-12NA8Mjx.d.ts +0 -39
  44. package/dist/dialog-bKfjZMTd.d.mts +0 -22
  45. package/dist/dialog-bKfjZMTd.d.ts +0 -22
  46. package/dist/dynamic-icon-DrGIiu2N.d.mts +0 -10
  47. package/dist/dynamic-icon-DrGIiu2N.d.ts +0 -10
  48. package/dist/home/index.d.mts +0 -269
  49. package/dist/home/index.d.ts +0 -269
  50. package/dist/home/index.js +0 -1678
  51. package/dist/home/index.js.map +0 -1
  52. package/dist/home/index.mjs +0 -1635
  53. package/dist/home/index.mjs.map +0 -1
  54. package/dist/hooks/index.d.mts +0 -7
  55. package/dist/hooks/index.d.ts +0 -7
  56. package/dist/hooks/index.js +0 -8316
  57. package/dist/hooks/index.js.map +0 -1
  58. package/dist/hooks/index.mjs +0 -8255
  59. package/dist/hooks/index.mjs.map +0 -1
  60. package/dist/index-50hpiPrV.d.ts +0 -116
  61. package/dist/index-B9zQVEVi.d.mts +0 -116
  62. package/dist/index.d.mts +0 -5
  63. package/dist/index.d.ts +0 -5
  64. package/dist/index.js +0 -123
  65. package/dist/index.js.map +0 -1
  66. package/dist/index.mjs +0 -118
  67. package/dist/index.mjs.map +0 -1
  68. package/dist/infrastructure/index.d.mts +0 -423
  69. package/dist/infrastructure/index.d.ts +0 -423
  70. package/dist/infrastructure/index.js +0 -633
  71. package/dist/infrastructure/index.js.map +0 -1
  72. package/dist/infrastructure/index.mjs +0 -619
  73. package/dist/infrastructure/index.mjs.map +0 -1
  74. package/dist/label-DWTEkNPo.d.ts +0 -226
  75. package/dist/label-LPpdcoBx.d.mts +0 -226
  76. package/dist/layout/index.d.mts +0 -48
  77. package/dist/layout/index.d.ts +0 -48
  78. package/dist/layout/index.js +0 -117
  79. package/dist/layout/index.js.map +0 -1
  80. package/dist/layout/index.mjs +0 -90
  81. package/dist/layout/index.mjs.map +0 -1
  82. package/dist/navigation/index.d.mts +0 -16
  83. package/dist/navigation/index.d.ts +0 -16
  84. package/dist/navigation/index.js +0 -53
  85. package/dist/navigation/index.js.map +0 -1
  86. package/dist/navigation/index.mjs +0 -50
  87. package/dist/navigation/index.mjs.map +0 -1
  88. package/dist/notification/index.d.mts +0 -105
  89. package/dist/notification/index.d.ts +0 -105
  90. package/dist/notification/index.js +0 -278
  91. package/dist/notification/index.js.map +0 -1
  92. package/dist/notification/index.mjs +0 -274
  93. package/dist/notification/index.mjs.map +0 -1
  94. package/dist/organization/index.d.mts +0 -99
  95. package/dist/organization/index.d.ts +0 -99
  96. package/dist/organization/index.js +0 -360
  97. package/dist/organization/index.js.map +0 -1
  98. package/dist/organization/index.mjs +0 -352
  99. package/dist/organization/index.mjs.map +0 -1
  100. package/dist/plugin/index.d.mts +0 -83
  101. package/dist/plugin/index.d.ts +0 -83
  102. package/dist/plugin/index.js +0 -86
  103. package/dist/plugin/index.js.map +0 -1
  104. package/dist/plugin/index.mjs +0 -84
  105. package/dist/plugin/index.mjs.map +0 -1
  106. package/dist/providers/index.d.mts +0 -25
  107. package/dist/providers/index.d.ts +0 -25
  108. package/dist/providers/index.js +0 -84
  109. package/dist/providers/index.js.map +0 -1
  110. package/dist/providers/index.mjs +0 -77
  111. package/dist/providers/index.mjs.map +0 -1
  112. package/dist/rbac/index.d.mts +0 -226
  113. package/dist/rbac/index.d.ts +0 -226
  114. package/dist/rbac/index.js +0 -4784
  115. package/dist/rbac/index.js.map +0 -1
  116. package/dist/rbac/index.mjs +0 -4722
  117. package/dist/rbac/index.mjs.map +0 -1
  118. package/dist/rbac/permissions.d.mts +0 -26
  119. package/dist/rbac/permissions.d.ts +0 -26
  120. package/dist/rbac/permissions.js +0 -94
  121. package/dist/rbac/permissions.js.map +0 -1
  122. package/dist/rbac/permissions.mjs +0 -90
  123. package/dist/rbac/permissions.mjs.map +0 -1
  124. package/dist/rbac/server.d.mts +0 -1
  125. package/dist/rbac/server.d.ts +0 -1
  126. package/dist/rbac/server.js +0 -128
  127. package/dist/rbac/server.js.map +0 -1
  128. package/dist/rbac/server.mjs +0 -124
  129. package/dist/rbac/server.mjs.map +0 -1
  130. package/dist/schemas/index.d.mts +0 -1257
  131. package/dist/schemas/index.d.ts +0 -1257
  132. package/dist/schemas/index.js +0 -572
  133. package/dist/schemas/index.js.map +0 -1
  134. package/dist/schemas/index.mjs +0 -523
  135. package/dist/schemas/index.mjs.map +0 -1
  136. package/dist/server-QuYCTa89.d.mts +0 -83
  137. package/dist/server-QuYCTa89.d.ts +0 -83
  138. package/dist/sonner-C74GlRDQ.d.mts +0 -71
  139. package/dist/sonner-C74GlRDQ.d.ts +0 -71
  140. package/dist/status-BOXZgIqX.d.mts +0 -12
  141. package/dist/status-BOXZgIqX.d.ts +0 -12
  142. package/dist/system/index.d.mts +0 -77
  143. package/dist/system/index.d.ts +0 -77
  144. package/dist/system/index.js +0 -102
  145. package/dist/system/index.js.map +0 -1
  146. package/dist/system/index.mjs +0 -100
  147. package/dist/system/index.mjs.map +0 -1
  148. package/dist/tabs-C6FfBwPY.d.mts +0 -18
  149. package/dist/tabs-C6FfBwPY.d.ts +0 -18
  150. package/dist/tenant-provider-B8eC_Wpb.d.mts +0 -27
  151. package/dist/tenant-provider-B8eC_Wpb.d.ts +0 -27
  152. package/dist/types/index.d.mts +0 -469
  153. package/dist/types/index.d.ts +0 -469
  154. package/dist/types/index.js +0 -25
  155. package/dist/types/index.js.map +0 -1
  156. package/dist/types/index.mjs +0 -21
  157. package/dist/types/index.mjs.map +0 -1
  158. package/dist/ui/auth.d.mts +0 -39
  159. package/dist/ui/auth.d.ts +0 -39
  160. package/dist/ui/auth.js +0 -4941
  161. package/dist/ui/auth.js.map +0 -1
  162. package/dist/ui/auth.mjs +0 -4896
  163. package/dist/ui/auth.mjs.map +0 -1
  164. package/dist/ui/crud.d.mts +0 -2
  165. package/dist/ui/crud.d.ts +0 -2
  166. package/dist/ui/crud.js +0 -4
  167. package/dist/ui/crud.js.map +0 -1
  168. package/dist/ui/crud.mjs +0 -3
  169. package/dist/ui/crud.mjs.map +0 -1
  170. package/dist/ui/data-display.d.mts +0 -596
  171. package/dist/ui/data-display.d.ts +0 -596
  172. package/dist/ui/data-display.js +0 -5307
  173. package/dist/ui/data-display.js.map +0 -1
  174. package/dist/ui/data-display.mjs +0 -5212
  175. package/dist/ui/data-display.mjs.map +0 -1
  176. package/dist/ui/feedback.d.mts +0 -55
  177. package/dist/ui/feedback.d.ts +0 -55
  178. package/dist/ui/feedback.js +0 -2608
  179. package/dist/ui/feedback.js.map +0 -1
  180. package/dist/ui/feedback.mjs +0 -2526
  181. package/dist/ui/feedback.mjs.map +0 -1
  182. package/dist/ui/forms.d.mts +0 -309
  183. package/dist/ui/forms.d.ts +0 -309
  184. package/dist/ui/forms.js +0 -4656
  185. package/dist/ui/forms.js.map +0 -1
  186. package/dist/ui/forms.mjs +0 -4571
  187. package/dist/ui/forms.mjs.map +0 -1
  188. package/dist/ui/index.d.mts +0 -331
  189. package/dist/ui/index.d.ts +0 -331
  190. package/dist/ui/index.js +0 -16953
  191. package/dist/ui/index.js.map +0 -1
  192. package/dist/ui/index.mjs +0 -16598
  193. package/dist/ui/index.mjs.map +0 -1
  194. package/dist/ui/primitives/client.d.mts +0 -61
  195. package/dist/ui/primitives/client.d.ts +0 -61
  196. package/dist/ui/primitives/client.js +0 -3408
  197. package/dist/ui/primitives/client.js.map +0 -1
  198. package/dist/ui/primitives/client.mjs +0 -3256
  199. package/dist/ui/primitives/client.mjs.map +0 -1
  200. package/dist/ui/primitives.d.mts +0 -113
  201. package/dist/ui/primitives.d.ts +0 -113
  202. package/dist/ui/primitives.js +0 -3356
  203. package/dist/ui/primitives.js.map +0 -1
  204. package/dist/ui/primitives.mjs +0 -3227
  205. package/dist/ui/primitives.mjs.map +0 -1
  206. package/dist/user/index.d.mts +0 -228
  207. package/dist/user/index.d.ts +0 -228
  208. package/dist/user/index.js +0 -4306
  209. package/dist/user/index.js.map +0 -1
  210. package/dist/user/index.mjs +0 -4260
  211. package/dist/user/index.mjs.map +0 -1
  212. package/dist/utils/index.d.mts +0 -205
  213. package/dist/utils/index.d.ts +0 -205
  214. package/dist/utils/index.js +0 -574
  215. package/dist/utils/index.js.map +0 -1
  216. package/dist/utils/index.mjs +0 -514
  217. package/dist/utils/index.mjs.map +0 -1
  218. package/dist/workflow/index.d.mts +0 -40
  219. package/dist/workflow/index.d.ts +0 -40
  220. package/dist/workflow/index.js +0 -3710
  221. package/dist/workflow/index.js.map +0 -1
  222. package/dist/workflow/index.mjs +0 -3677
  223. package/dist/workflow/index.mjs.map +0 -1
@@ -1,1678 +0,0 @@
1
- 'use strict';
2
-
3
- var navigation = require('next/navigation');
4
- var useSWR = require('swr');
5
- var lucideReact = require('lucide-react');
6
- var clsx = require('clsx');
7
- var tailwindMerge = require('tailwind-merge');
8
- var dateFns = require('date-fns');
9
- var locale = require('date-fns/locale');
10
- var jsxRuntime = require('react/jsx-runtime');
11
- var react = require('react');
12
- var core = require('@dnd-kit/core');
13
- var sortable = require('@dnd-kit/sortable');
14
- var framerMotion = require('framer-motion');
15
- var utilities = require('@dnd-kit/utilities');
16
- var useEmblaCarousel = require('embla-carousel-react');
17
- var Autoplay = require('embla-carousel-autoplay');
18
- var reactHookForm = require('react-hook-form');
19
- var zod = require('@hookform/resolvers/zod');
20
- var z = require('zod');
21
- var DialogPrimitive = require('@radix-ui/react-dialog');
22
-
23
- function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
24
-
25
- function _interopNamespace(e) {
26
- if (e && e.__esModule) return e;
27
- var n = Object.create(null);
28
- if (e) {
29
- Object.keys(e).forEach(function (k) {
30
- if (k !== 'default') {
31
- var d = Object.getOwnPropertyDescriptor(e, k);
32
- Object.defineProperty(n, k, d.get ? d : {
33
- enumerable: true,
34
- get: function () { return e[k]; }
35
- });
36
- }
37
- });
38
- }
39
- n.default = e;
40
- return Object.freeze(n);
41
- }
42
-
43
- var useSWR__default = /*#__PURE__*/_interopDefault(useSWR);
44
- var useEmblaCarousel__default = /*#__PURE__*/_interopDefault(useEmblaCarousel);
45
- var Autoplay__default = /*#__PURE__*/_interopDefault(Autoplay);
46
- var z__namespace = /*#__PURE__*/_interopNamespace(z);
47
- var DialogPrimitive__namespace = /*#__PURE__*/_interopNamespace(DialogPrimitive);
48
-
49
- // src/home/home-page.tsx
50
- function cn(...inputs) {
51
- return tailwindMerge.twMerge(clsx.clsx(inputs));
52
- }
53
- function getGreeting(hour) {
54
- if (hour >= 5 && hour < 12) {
55
- return { text: "Ch\xE0o bu\u1ED5i s\xE1ng", emoji: "\u{1F305}" };
56
- } else if (hour >= 12 && hour < 18) {
57
- return { text: "Ch\xE0o bu\u1ED5i chi\u1EC1u", emoji: "\u2600\uFE0F" };
58
- } else {
59
- return { text: "Ch\xE0o bu\u1ED5i t\u1ED1i", emoji: "\u{1F319}" };
60
- }
61
- }
62
- function WelcomeCard({ userName = "B\u1EA1n", className }) {
63
- const now = /* @__PURE__ */ new Date();
64
- const hour = now.getHours();
65
- const greeting = getGreeting(hour);
66
- const formattedDate = dateFns.format(now, "EEEE, dd/MM/yyyy", { locale: locale.vi });
67
- const formattedTime = dateFns.format(now, "HH:mm");
68
- return /* @__PURE__ */ jsxRuntime.jsxs(
69
- "div",
70
- {
71
- className: cn(
72
- "w-full bg-gradient-to-r from-blue-900 to-primary rounded-xl overflow-hidden shadow-lg group relative animate-in fade-in slide-in-from-bottom-4 duration-500",
73
- className
74
- ),
75
- children: [
76
- /* @__PURE__ */ jsxRuntime.jsx(
77
- "div",
78
- {
79
- className: "absolute inset-0 opacity-20 pointer-events-none",
80
- style: {
81
- backgroundImage: "radial-gradient(circle at 80% 20%, white 0%, transparent 40%), radial-gradient(circle at 10% 80%, white 0%, transparent 30%)"
82
- }
83
- }
84
- ),
85
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col md:flex-row items-center relative overflow-hidden", children: [
86
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-8 flex-1 z-10 w-full", children: [
87
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2 mb-4", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bg-white/20 text-white text-xs font-bold px-2 py-1 rounded uppercase tracking-wider backdrop-blur-sm", children: formattedDate }) }),
88
- /* @__PURE__ */ jsxRuntime.jsxs("h2", { className: "text-white text-2xl md:text-3xl font-bold mb-3", children: [
89
- greeting.text,
90
- ", ",
91
- userName
92
- ] }),
93
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-blue-100 mb-6 max-w-lg", children: "Ch\xFAc b\u1EA1n m\u1ED9t ng\xE0y l\xE0m vi\u1EC7c hi\u1EC7u qu\u1EA3. H\u1EC7 th\u1ED1ng \u0111ang ho\u1EA1t \u0111\u1ED9ng \u1ED5n \u0111\u1ECBnh." }),
94
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-3", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white/10 text-white px-5 py-2.5 rounded-lg text-sm font-bold shadow-sm backdrop-blur-sm border border-white/20 flex items-center gap-2", children: [
95
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-2 h-2 rounded-full bg-green-400 animate-pulse" }),
96
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
97
- "Online \u2022 ",
98
- formattedTime
99
- ] })
100
- ] }) })
101
- ] }),
102
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden md:flex flex-1 justify-end items-end h-full p-8 z-10 self-stretch", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-3 opacity-90 transform translate-y-4", children: [
103
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white/10 backdrop-blur-md border border-white/20 p-3 rounded-lg flex items-center gap-3", children: [
104
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TrendingUp, { className: "text-white w-6 h-6" }),
105
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
106
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-1.5 w-12 bg-white/40 rounded mb-1" }),
107
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-1.5 w-8 bg-white/20 rounded" })
108
- ] })
109
- ] }),
110
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white/10 backdrop-blur-md border border-white/20 p-3 rounded-lg flex items-center gap-3", children: [
111
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Zap, { className: "text-white w-6 h-6" }),
112
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
113
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-1.5 w-12 bg-white/40 rounded mb-1" }),
114
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-1.5 w-8 bg-white/20 rounded" })
115
- ] })
116
- ] })
117
- ] }) })
118
- ] })
119
- ]
120
- }
121
- );
122
- }
123
-
124
- // src/home/types.ts
125
- var DEFAULT_WIDGETS = [
126
- {
127
- id: "revenue",
128
- type: "revenue",
129
- position: 0,
130
- visible: true,
131
- size: "medium"
132
- },
133
- { id: "orders", type: "orders", position: 1, visible: true, size: "medium" },
134
- {
135
- id: "customers",
136
- type: "customers",
137
- position: 2,
138
- visible: true,
139
- size: "medium"
140
- },
141
- { id: "stock", type: "stock", position: 3, visible: true, size: "medium" }
142
- ];
143
- var WIDGET_LABELS = {
144
- revenue: "Doanh thu",
145
- orders: "\u0110\u01A1n h\xE0ng",
146
- customers: "Kh\xE1ch h\xE0ng",
147
- stock: "T\u1ED3n kho"
148
- };
149
- var ERP_FEATURES = [
150
- {
151
- id: "inventory",
152
- icon: "\u{1F4E6}",
153
- title: "Qu\u1EA3n l\xFD Kho",
154
- description: "Qu\u1EA3n l\xFD to\xE0n di\u1EC7n h\xE0ng h\xF3a v\xE0 t\u1ED3n kho",
155
- subFeatures: ["Nh\u1EADp/Xu\u1EA5t kho", "Ki\u1EC3m k\xEA", "\u0110i\u1EC1u chuy\u1EC3n", "C\u1EA3nh b\xE1o t\u1ED3n"],
156
- color: "from-blue-500 to-cyan-500"
157
- },
158
- {
159
- id: "sales",
160
- icon: "\u{1F4B0}",
161
- title: "Qu\u1EA3n l\xFD B\xE1n h\xE0ng",
162
- description: "X\u1EED l\xFD \u0111\u01A1n h\xE0ng v\xE0 b\xE1n h\xE0ng hi\u1EC7u qu\u1EA3",
163
- subFeatures: ["POS", "\u0110\u01A1n h\xE0ng", "H\u1EE3p \u0111\u1ED3ng", "B\xE1o gi\xE1"],
164
- color: "from-green-500 to-emerald-500"
165
- },
166
- {
167
- id: "purchasing",
168
- icon: "\u{1F6D2}",
169
- title: "Qu\u1EA3n l\xFD Mua h\xE0ng",
170
- description: "Qu\u1EA3n l\xFD nh\xE0 cung c\u1EA5p v\xE0 \u0111\u1EB7t h\xE0ng",
171
- subFeatures: ["\u0110\u1EB7t h\xE0ng NCC", "Nh\u1EADp h\xE0ng", "C\xF4ng n\u1EE3 ph\u1EA3i tr\u1EA3"],
172
- color: "from-orange-500 to-amber-500"
173
- },
174
- {
175
- id: "finance",
176
- icon: "\u{1F4B5}",
177
- title: "T\xE0i ch\xEDnh",
178
- description: "Qu\u1EA3n l\xFD d\xF2ng ti\u1EC1n v\xE0 c\xF4ng n\u1EE3",
179
- subFeatures: ["Thu/Chi", "S\u1ED5 qu\u1EF9", "C\xF4ng n\u1EE3", "B\xE1o c\xE1o t\xE0i ch\xEDnh"],
180
- color: "from-purple-500 to-violet-500"
181
- },
182
- {
183
- id: "crm",
184
- icon: "\u{1F465}",
185
- title: "CRM",
186
- description: "Ch\u0103m s\xF3c v\xE0 qu\u1EA3n l\xFD kh\xE1ch h\xE0ng",
187
- subFeatures: ["Kh\xE1ch h\xE0ng", "L\u1ECBch s\u1EED giao d\u1ECBch", "Ph\xE2n lo\u1EA1i"],
188
- color: "from-pink-500 to-rose-500"
189
- },
190
- {
191
- id: "hr",
192
- icon: "\u{1F468}\u200D\u{1F4BC}",
193
- title: "Nh\xE2n s\u1EF1",
194
- description: "Qu\u1EA3n l\xFD ng\u01B0\u1EDDi d\xF9ng v\xE0 ph\xE2n quy\u1EC1n",
195
- subFeatures: ["Users", "Ph\xE2n quy\u1EC1n", "Roles"],
196
- color: "from-indigo-500 to-blue-500"
197
- },
198
- {
199
- id: "reports",
200
- icon: "\u{1F4CA}",
201
- title: "B\xE1o c\xE1o",
202
- description: "Th\u1ED1ng k\xEA v\xE0 ph\xE2n t\xEDch d\u1EEF li\u1EC7u",
203
- subFeatures: ["Dashboard", "Th\u1ED1ng k\xEA", "Export"],
204
- color: "from-teal-500 to-green-500"
205
- }
206
- ];
207
- var sizeClasses = {
208
- small: "col-span-1",
209
- medium: "col-span-1 md:col-span-1",
210
- large: "col-span-1 md:col-span-2"
211
- };
212
- function BaseWidget({
213
- config,
214
- title,
215
- children,
216
- loading = false,
217
- error,
218
- onRemove,
219
- onToggleVisibility,
220
- onSettings,
221
- className,
222
- isDragging = false
223
- }) {
224
- const {
225
- attributes,
226
- listeners,
227
- setNodeRef,
228
- transform,
229
- transition,
230
- isDragging: isSortableDragging
231
- } = sortable.useSortable({ id: config.id });
232
- const style = {
233
- transform: utilities.CSS.Transform.toString(transform),
234
- transition
235
- };
236
- const isCurrentlyDragging = isDragging || isSortableDragging;
237
- if (!config.visible) {
238
- return null;
239
- }
240
- return /* @__PURE__ */ jsxRuntime.jsxs(
241
- framerMotion.motion.div,
242
- {
243
- ref: setNodeRef,
244
- style,
245
- initial: { opacity: 0, scale: 0.95 },
246
- animate: { opacity: 1, scale: 1 },
247
- exit: { opacity: 0, scale: 0.95 },
248
- className: cn(
249
- "group relative flex flex-col justify-between gap-3 rounded-xl border border-slate-200 bg-white p-5 shadow-sm transition-all hover:border-primary/30 hover:shadow-md dark:border-slate-700 dark:bg-slate-800",
250
- sizeClasses[config.size],
251
- isCurrentlyDragging && "z-50 shadow-lg ring-2 ring-primary/20",
252
- className
253
- ),
254
- children: [
255
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-1", children: [
256
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
257
- /* @__PURE__ */ jsxRuntime.jsx(
258
- "button",
259
- {
260
- ...attributes,
261
- ...listeners,
262
- className: "cursor-grab touch-none rounded text-slate-400 hover:text-slate-600 dark:hover:text-slate-300 active:cursor-grabbing opacity-0 group-hover:opacity-100 transition-opacity",
263
- "aria-label": "Drag to reorder",
264
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.GripVertical, { className: "h-4 w-4" })
265
- }
266
- ),
267
- /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "flex items-center gap-2 text-sm font-semibold text-slate-900 dark:text-white", children: title })
268
- ] }),
269
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100", children: [
270
- onToggleVisibility && /* @__PURE__ */ jsxRuntime.jsx(
271
- "button",
272
- {
273
- onClick: onToggleVisibility,
274
- className: "rounded p-1 text-slate-400 hover:bg-slate-100 hover:text-slate-700 dark:hover:bg-slate-700 dark:hover:text-slate-300",
275
- "aria-label": config.visible ? "Hide widget" : "Show widget",
276
- children: config.visible ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Eye, { className: "h-4 w-4" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.EyeOff, { className: "h-4 w-4" })
277
- }
278
- ),
279
- onSettings && /* @__PURE__ */ jsxRuntime.jsx(
280
- "button",
281
- {
282
- onClick: onSettings,
283
- className: "rounded p-1 text-slate-400 hover:bg-slate-100 hover:text-slate-700 dark:hover:bg-slate-700 dark:hover:text-slate-300",
284
- "aria-label": "Widget settings",
285
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Settings, { className: "h-4 w-4" })
286
- }
287
- ),
288
- onRemove && /* @__PURE__ */ jsxRuntime.jsx(
289
- "button",
290
- {
291
- onClick: onRemove,
292
- className: "rounded p-1 text-slate-400 hover:bg-red-50 hover:text-red-500 dark:hover:bg-red-900/20",
293
- "aria-label": "Remove widget",
294
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-4 w-4" })
295
- }
296
- )
297
- ] })
298
- ] }),
299
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center py-4", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-6 w-6 animate-spin rounded-full border-2 border-primary border-t-transparent" }) }) : error ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center py-4 text-xs text-red-500", children: error }) : children })
300
- ]
301
- }
302
- );
303
- }
304
- function WidgetStat({
305
- value,
306
- label,
307
- change,
308
- valueClassName
309
- }) {
310
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
311
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("text-3xl font-bold text-foreground", valueClassName), children: value }),
312
- label && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: label }),
313
- change && /* @__PURE__ */ jsxRuntime.jsxs(
314
- "div",
315
- {
316
- className: cn(
317
- "inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium",
318
- change.type === "increase" ? "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400" : "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400"
319
- ),
320
- children: [
321
- change.type === "increase" ? /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "h-3 w-3", viewBox: "0 0 12 12", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6 2L10 7H2L6 2Z" }) }) : /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "h-3 w-3", viewBox: "0 0 12 12", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6 10L2 5H10L6 10Z" }) }),
322
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
323
- change.value > 0 ? "+" : "",
324
- change.value,
325
- "%"
326
- ] }),
327
- change.period && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground", children: [
328
- "vs ",
329
- change.period
330
- ] })
331
- ]
332
- }
333
- )
334
- ] });
335
- }
336
- function formatCurrency(value) {
337
- return new Intl.NumberFormat("vi-VN", {
338
- style: "currency",
339
- currency: "VND",
340
- maximumFractionDigits: 0
341
- }).format(value);
342
- }
343
- function RevenueWidget({
344
- config,
345
- data,
346
- loading,
347
- error,
348
- onRemove,
349
- onToggleVisibility,
350
- onSettings
351
- }) {
352
- const formattedValue = data ? formatCurrency(data.total) : "0 \u20AB";
353
- return /* @__PURE__ */ jsxRuntime.jsx(
354
- BaseWidget,
355
- {
356
- config,
357
- title: "Doanh thu",
358
- loading,
359
- error,
360
- onRemove,
361
- onToggleVisibility,
362
- onSettings,
363
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between", children: [
364
- /* @__PURE__ */ jsxRuntime.jsx(
365
- WidgetStat,
366
- {
367
- value: formattedValue,
368
- label: "H\xF4m nay",
369
- change: data?.change !== void 0 ? {
370
- value: data.change,
371
- type: data.change >= 0 ? "increase" : "decrease",
372
- period: data.changePeriod || "h\xF4m qua"
373
- } : void 0,
374
- valueClassName: "text-green-600 dark:text-green-400"
375
- }
376
- ),
377
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-full bg-green-100 p-3 dark:bg-green-900/30", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.DollarSign, { className: "h-6 w-6 text-green-600 dark:text-green-400" }) })
378
- ] })
379
- }
380
- );
381
- }
382
- function OrdersWidget({
383
- config,
384
- data,
385
- loading,
386
- error,
387
- onRemove,
388
- onToggleVisibility,
389
- onSettings
390
- }) {
391
- return /* @__PURE__ */ jsxRuntime.jsx(
392
- BaseWidget,
393
- {
394
- config,
395
- title: "\u0110\u01A1n h\xE0ng",
396
- loading,
397
- error,
398
- onRemove,
399
- onToggleVisibility,
400
- onSettings,
401
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between", children: [
402
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
403
- /* @__PURE__ */ jsxRuntime.jsx(
404
- WidgetStat,
405
- {
406
- value: data?.total ?? 0,
407
- label: "\u0110\u01A1n h\xF4m nay",
408
- change: data?.change !== void 0 ? {
409
- value: data.change,
410
- type: data.change >= 0 ? "increase" : "decrease",
411
- period: data.changePeriod || "h\xF4m qua"
412
- } : void 0,
413
- valueClassName: "text-blue-600 dark:text-blue-400"
414
- }
415
- ),
416
- (data?.pending !== void 0 || data?.completed !== void 0) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-4 text-sm", children: [
417
- data?.pending !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
418
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Ch\u1EDD x\u1EED l\xFD: " }),
419
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-amber-600 dark:text-amber-400", children: data.pending })
420
- ] }),
421
- data?.completed !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
422
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Ho\xE0n th\xE0nh: " }),
423
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-green-600 dark:text-green-400", children: data.completed })
424
- ] })
425
- ] })
426
- ] }),
427
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-full bg-blue-100 p-3 dark:bg-blue-900/30", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ShoppingCart, { className: "h-6 w-6 text-blue-600 dark:text-blue-400" }) })
428
- ] })
429
- }
430
- );
431
- }
432
- function CustomersWidget({
433
- config,
434
- data,
435
- loading,
436
- error,
437
- onRemove,
438
- onToggleVisibility,
439
- onSettings
440
- }) {
441
- return /* @__PURE__ */ jsxRuntime.jsx(
442
- BaseWidget,
443
- {
444
- config,
445
- title: "Kh\xE1ch h\xE0ng",
446
- loading,
447
- error,
448
- onRemove,
449
- onToggleVisibility,
450
- onSettings,
451
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between", children: [
452
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
453
- /* @__PURE__ */ jsxRuntime.jsx(
454
- WidgetStat,
455
- {
456
- value: data?.total ?? 0,
457
- label: "T\u1ED5ng kh\xE1ch h\xE0ng",
458
- change: data?.change !== void 0 ? {
459
- value: data.change,
460
- type: data.change >= 0 ? "increase" : "decrease",
461
- period: data.changePeriod || "th\xE1ng tr\u01B0\u1EDBc"
462
- } : void 0,
463
- valueClassName: "text-purple-600 dark:text-purple-400"
464
- }
465
- ),
466
- data?.newToday !== void 0 && data.newToday > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm", children: [
467
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Kh\xE1ch m\u1EDBi h\xF4m nay: " }),
468
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-medium text-green-600 dark:text-green-400", children: [
469
- "+",
470
- data.newToday
471
- ] })
472
- ] })
473
- ] }),
474
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-full bg-purple-100 p-3 dark:bg-purple-900/30", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Users, { className: "h-6 w-6 text-purple-600 dark:text-purple-400" }) })
475
- ] })
476
- }
477
- );
478
- }
479
- function StockWidget({
480
- config,
481
- data,
482
- loading,
483
- error,
484
- onRemove,
485
- onToggleVisibility,
486
- onSettings
487
- }) {
488
- const hasLowStock = (data?.lowStockCount ?? 0) > 0;
489
- return /* @__PURE__ */ jsxRuntime.jsx(
490
- BaseWidget,
491
- {
492
- config,
493
- title: "T\u1ED3n kho",
494
- loading,
495
- error,
496
- onRemove,
497
- onToggleVisibility,
498
- onSettings,
499
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
500
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between", children: [
501
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
502
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-3xl font-bold text-foreground", children: data?.totalItems ?? 0 }),
503
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: "T\u1ED5ng m\u1EB7t h\xE0ng" })
504
- ] }),
505
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-full bg-orange-100 p-3 dark:bg-orange-900/30", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Package, { className: "h-6 w-6 text-orange-600 dark:text-orange-400" }) })
506
- ] }),
507
- hasLowStock && /* @__PURE__ */ jsxRuntime.jsxs(
508
- "div",
509
- {
510
- className: cn(
511
- "flex items-center gap-2 rounded-lg px-3 py-2",
512
- "bg-amber-50 text-amber-800 dark:bg-amber-900/20 dark:text-amber-400"
513
- ),
514
- children: [
515
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "h-4 w-4 flex-shrink-0" }),
516
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm font-medium", children: [
517
- data?.lowStockCount,
518
- " s\u1EA3n ph\u1EA9m s\u1EAFp h\u1EBFt h\xE0ng"
519
- ] })
520
- ]
521
- }
522
- ),
523
- data?.lowStockItems && data.lowStockItems.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
524
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wider", children: "C\u1EA3nh b\xE1o t\u1ED3n kho" }),
525
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5 max-h-32 overflow-y-auto", children: [
526
- data.lowStockItems.slice(0, 5).map((item) => /* @__PURE__ */ jsxRuntime.jsxs(
527
- "div",
528
- {
529
- className: "flex items-center justify-between text-sm",
530
- children: [
531
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-foreground", children: item.name }),
532
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex-shrink-0 font-medium text-red-600 dark:text-red-400", children: [
533
- item.quantity,
534
- "/",
535
- item.minStock
536
- ] })
537
- ]
538
- },
539
- item.id
540
- )),
541
- data.lowStockItems.length > 5 && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground", children: [
542
- "+",
543
- data.lowStockItems.length - 5,
544
- " s\u1EA3n ph\u1EA9m kh\xE1c"
545
- ] })
546
- ] })
547
- ] })
548
- ] })
549
- }
550
- );
551
- }
552
- function WidgetContainer({
553
- widgets,
554
- onWidgetsChange,
555
- widgetData,
556
- loading = false,
557
- className
558
- }) {
559
- const [showHiddenWidgets, setShowHiddenWidgets] = react.useState(false);
560
- const sensors = core.useSensors(
561
- core.useSensor(core.PointerSensor, {
562
- activationConstraint: {
563
- distance: 8
564
- }
565
- }),
566
- core.useSensor(core.KeyboardSensor, {
567
- coordinateGetter: sortable.sortableKeyboardCoordinates
568
- })
569
- );
570
- const handleDragEnd = react.useCallback(
571
- (event) => {
572
- const { active, over } = event;
573
- if (over && active.id !== over.id) {
574
- const oldIndex = widgets.findIndex((w) => w.id === active.id);
575
- const newIndex = widgets.findIndex((w) => w.id === over.id);
576
- const newWidgets = sortable.arrayMove(widgets, oldIndex, newIndex).map(
577
- (widget, index) => ({
578
- ...widget,
579
- position: index
580
- })
581
- );
582
- onWidgetsChange(newWidgets);
583
- }
584
- },
585
- [widgets, onWidgetsChange]
586
- );
587
- const handleToggleVisibility = react.useCallback(
588
- (widgetId) => {
589
- const newWidgets = widgets.map(
590
- (widget) => widget.id === widgetId ? { ...widget, visible: !widget.visible } : widget
591
- );
592
- onWidgetsChange(newWidgets);
593
- },
594
- [widgets, onWidgetsChange]
595
- );
596
- const handleRemoveWidget = react.useCallback(
597
- (widgetId) => {
598
- const newWidgets = widgets.map(
599
- (widget) => widget.id === widgetId ? { ...widget, visible: false } : widget
600
- );
601
- onWidgetsChange(newWidgets);
602
- },
603
- [widgets, onWidgetsChange]
604
- );
605
- const handleResetWidgets = react.useCallback(() => {
606
- onWidgetsChange(DEFAULT_WIDGETS);
607
- }, [onWidgetsChange]);
608
- const visibleWidgets = widgets.filter((w) => w.visible).sort((a, b) => a.position - b.position);
609
- const hiddenWidgets = widgets.filter((w) => !w.visible);
610
- const renderWidget = (config) => {
611
- const commonProps = {
612
- config,
613
- loading,
614
- onRemove: () => handleRemoveWidget(config.id),
615
- onToggleVisibility: () => handleToggleVisibility(config.id)
616
- };
617
- switch (config.type) {
618
- case "revenue":
619
- return /* @__PURE__ */ jsxRuntime.jsx(RevenueWidget, { ...commonProps, data: widgetData?.revenue });
620
- case "orders":
621
- return /* @__PURE__ */ jsxRuntime.jsx(OrdersWidget, { ...commonProps, data: widgetData?.orders });
622
- case "customers":
623
- return /* @__PURE__ */ jsxRuntime.jsx(CustomersWidget, { ...commonProps, data: widgetData?.customers });
624
- case "stock":
625
- return /* @__PURE__ */ jsxRuntime.jsx(StockWidget, { ...commonProps, data: widgetData?.stock });
626
- default:
627
- return null;
628
- }
629
- };
630
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("space-y-4", className), children: [
631
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
632
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-xl font-semibold text-foreground", children: "Widget Dashboard" }),
633
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
634
- hiddenWidgets.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
635
- "button",
636
- {
637
- onClick: () => setShowHiddenWidgets(!showHiddenWidgets),
638
- className: "inline-flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-sm font-medium text-muted-foreground hover:bg-muted hover:text-foreground transition-colors",
639
- children: [
640
- showHiddenWidgets ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.EyeOff, { className: "h-4 w-4" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Eye, { className: "h-4 w-4" }),
641
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
642
- hiddenWidgets.length,
643
- " \u1EA9n"
644
- ] })
645
- ]
646
- }
647
- ),
648
- /* @__PURE__ */ jsxRuntime.jsx(
649
- "button",
650
- {
651
- onClick: handleResetWidgets,
652
- className: "inline-flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-sm font-medium text-muted-foreground hover:bg-muted hover:text-foreground transition-colors",
653
- title: "Kh\xF4i ph\u1EE5c m\u1EB7c \u0111\u1ECBnh",
654
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RotateCcw, { className: "h-4 w-4" })
655
- }
656
- )
657
- ] })
658
- ] }),
659
- /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: showHiddenWidgets && hiddenWidgets.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
660
- framerMotion.motion.div,
661
- {
662
- initial: { opacity: 0, height: 0 },
663
- animate: { opacity: 1, height: "auto" },
664
- exit: { opacity: 0, height: 0 },
665
- className: "overflow-hidden",
666
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-dashed border-border bg-muted/30 p-4", children: [
667
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-3 text-sm font-medium text-muted-foreground", children: "Widget \u0111\xE3 \u1EA9n - Click \u0111\u1EC3 hi\u1EC3n th\u1ECB l\u1EA1i" }),
668
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2", children: hiddenWidgets.map((widget) => /* @__PURE__ */ jsxRuntime.jsxs(
669
- "button",
670
- {
671
- onClick: () => handleToggleVisibility(widget.id),
672
- className: "inline-flex items-center gap-1.5 rounded-lg bg-background px-3 py-2 text-sm font-medium shadow-sm border border-border hover:bg-muted transition-colors",
673
- children: [
674
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-4 w-4" }),
675
- WIDGET_LABELS[widget.type]
676
- ]
677
- },
678
- widget.id
679
- )) })
680
- ] })
681
- }
682
- ) }),
683
- /* @__PURE__ */ jsxRuntime.jsx(
684
- core.DndContext,
685
- {
686
- sensors,
687
- collisionDetection: core.closestCenter,
688
- onDragEnd: handleDragEnd,
689
- children: /* @__PURE__ */ jsxRuntime.jsx(
690
- sortable.SortableContext,
691
- {
692
- items: visibleWidgets.map((w) => w.id),
693
- strategy: sortable.rectSortingStrategy,
694
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-4", children: /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { mode: "popLayout", children: visibleWidgets.map((widget) => /* @__PURE__ */ jsxRuntime.jsx("div", { children: renderWidget(widget) }, widget.id)) }) })
695
- }
696
- )
697
- }
698
- ),
699
- visibleWidgets.length === 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center rounded-lg border border-dashed border-border py-12 text-center", children: [
700
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-full bg-muted p-3 mb-4", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-6 w-6 text-muted-foreground" }) }),
701
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "font-medium text-foreground mb-1", children: "Ch\u01B0a c\xF3 widget n\xE0o" }),
702
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mb-4", children: "Th\xEAm widget \u0111\u1EC3 theo d\xF5i c\xE1c ch\u1EC9 s\u1ED1 quan tr\u1ECDng" }),
703
- /* @__PURE__ */ jsxRuntime.jsxs(
704
- "button",
705
- {
706
- onClick: handleResetWidgets,
707
- className: "inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors",
708
- children: [
709
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RotateCcw, { className: "h-4 w-4" }),
710
- "Kh\xF4i ph\u1EE5c m\u1EB7c \u0111\u1ECBnh"
711
- ]
712
- }
713
- )
714
- ] })
715
- ] });
716
- }
717
- function FeatureShowcase({ className }) {
718
- const [emblaRef, emblaApi] = useEmblaCarousel__default.default(
719
- { loop: true, align: "start" },
720
- [Autoplay__default.default({ delay: 5e3, stopOnInteraction: true })]
721
- );
722
- const [prevBtnEnabled, setPrevBtnEnabled] = react.useState(false);
723
- const [nextBtnEnabled, setNextBtnEnabled] = react.useState(false);
724
- const [selectedIndex, setSelectedIndex] = react.useState(0);
725
- const [scrollSnaps, setScrollSnaps] = react.useState([]);
726
- const scrollPrev = react.useCallback(
727
- () => emblaApi && emblaApi.scrollPrev(),
728
- [emblaApi]
729
- );
730
- const scrollNext = react.useCallback(
731
- () => emblaApi && emblaApi.scrollNext(),
732
- [emblaApi]
733
- );
734
- const scrollTo = react.useCallback(
735
- (index) => emblaApi && emblaApi.scrollTo(index),
736
- [emblaApi]
737
- );
738
- const onSelect = react.useCallback(() => {
739
- if (!emblaApi) return;
740
- setSelectedIndex(emblaApi.selectedScrollSnap());
741
- setPrevBtnEnabled(emblaApi.canScrollPrev());
742
- setNextBtnEnabled(emblaApi.canScrollNext());
743
- }, [emblaApi]);
744
- react.useEffect(() => {
745
- if (!emblaApi) return;
746
- onSelect();
747
- setScrollSnaps(emblaApi.scrollSnapList());
748
- emblaApi.on("select", onSelect);
749
- emblaApi.on("reInit", onSelect);
750
- return () => {
751
- emblaApi.off("select", onSelect);
752
- emblaApi.off("reInit", onSelect);
753
- };
754
- }, [emblaApi, onSelect]);
755
- return /* @__PURE__ */ jsxRuntime.jsxs(
756
- "div",
757
- {
758
- className: cn(
759
- "bg-white dark:bg-slate-800 rounded-xl border border-slate-200 dark:border-slate-700 shadow-sm relative overflow-hidden flex flex-col h-full",
760
- className
761
- ),
762
- children: [
763
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-6 py-4 border-b border-slate-100 dark:border-slate-700/50 bg-white/50 dark:bg-slate-800/50 backdrop-blur-sm z-20", children: [
764
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
765
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-blue-100 dark:bg-blue-900/30 text-primary p-1 rounded-md", children: /* @__PURE__ */ jsxRuntime.jsx(
766
- "svg",
767
- {
768
- className: "w-[18px] h-[18px]",
769
- viewBox: "0 0 24 24",
770
- fill: "none",
771
- stroke: "currentColor",
772
- strokeWidth: "2",
773
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 2v20M2 12h20M12 2a10 10 0 0 1 10 10 10 10 0 0 1-10 10 10 10 0 0 1-10-10 10 10 0 0 1 10-10z" })
774
- }
775
- ) }),
776
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-bold text-slate-700 dark:text-slate-200", children: "Feature Tour" })
777
- ] }),
778
- /* @__PURE__ */ jsxRuntime.jsx("button", { className: "text-xs font-medium text-slate-500 hover:text-slate-800 dark:text-slate-400 dark:hover:text-slate-200 px-3 py-1.5 rounded hover:bg-slate-100 dark:hover:bg-slate-700 transition-colors", children: "Skip Tour" })
779
- ] }),
780
- /* @__PURE__ */ jsxRuntime.jsx(
781
- "div",
782
- {
783
- className: "flex-1 relative overflow-hidden bg-slate-50/50 dark:bg-slate-800/50",
784
- ref: emblaRef,
785
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex touch-pan-y h-full", children: ERP_FEATURES.map((feature, index) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-[0_0_100%] min-w-0", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6 sm:p-8 flex flex-col items-center text-center gap-6 h-full justify-center", children: [
786
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-2 text-xs font-semibold text-primary uppercase tracking-wider", children: [
787
- "Step ",
788
- index + 1,
789
- " of ",
790
- ERP_FEATURES.length
791
- ] }),
792
- /* @__PURE__ */ jsxRuntime.jsx(
793
- "div",
794
- {
795
- className: cn(
796
- "flex h-16 w-16 shrink-0 items-center justify-center rounded-2xl bg-gradient-to-br text-3xl shadow-md",
797
- feature.color
798
- ),
799
- children: feature.icon
800
- }
801
- ),
802
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl md:text-3xl font-bold text-slate-900 dark:text-white", children: feature.title }),
803
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-slate-500 dark:text-slate-400 text-sm md:text-base leading-relaxed max-w-md", children: feature.description }),
804
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-2 justify-center flex-wrap", children: feature.subFeatures.map((sub) => /* @__PURE__ */ jsxRuntime.jsx(
805
- "span",
806
- {
807
- className: "px-2 py-1 bg-white dark:bg-slate-700 border border-slate-200 dark:border-slate-600 rounded-md text-xs font-medium text-slate-600 dark:text-slate-300",
808
- children: sub
809
- },
810
- sub
811
- )) })
812
- ] }) }, feature.id)) })
813
- }
814
- ),
815
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 py-4 bg-white dark:bg-slate-800 border-t border-slate-100 dark:border-slate-700 flex items-center justify-between z-20", children: [
816
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-1.5", children: scrollSnaps.map((_, index) => /* @__PURE__ */ jsxRuntime.jsx(
817
- "button",
818
- {
819
- className: cn(
820
- "h-1.5 rounded-full transition-all duration-300",
821
- index === selectedIndex ? "w-6 bg-primary" : "w-1.5 bg-slate-300 dark:bg-slate-600"
822
- ),
823
- onClick: () => scrollTo(index)
824
- },
825
- index
826
- )) }),
827
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-3", children: [
828
- /* @__PURE__ */ jsxRuntime.jsx(
829
- "button",
830
- {
831
- onClick: scrollPrev,
832
- disabled: !prevBtnEnabled,
833
- className: "w-9 h-9 flex items-center justify-center rounded-lg border border-slate-200 dark:border-slate-700 text-slate-400 hover:bg-slate-50 dark:hover:bg-slate-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors",
834
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "w-4 h-4" })
835
- }
836
- ),
837
- /* @__PURE__ */ jsxRuntime.jsxs(
838
- "button",
839
- {
840
- onClick: scrollNext,
841
- className: "h-9 px-4 rounded-lg bg-primary text-white text-sm font-medium flex items-center gap-2 hover:bg-primary/90 transition-colors shadow-sm",
842
- children: [
843
- "Next",
844
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "w-4 h-4" })
845
- ]
846
- }
847
- )
848
- ] })
849
- ] })
850
- ]
851
- }
852
- );
853
- }
854
- var iconMap = {
855
- "shopping-cart": /* @__PURE__ */ jsxRuntime.jsx(
856
- "svg",
857
- {
858
- className: "h-5 w-5",
859
- fill: "none",
860
- stroke: "currentColor",
861
- viewBox: "0 0 24 24",
862
- children: /* @__PURE__ */ jsxRuntime.jsx(
863
- "path",
864
- {
865
- strokeLinecap: "round",
866
- strokeLinejoin: "round",
867
- strokeWidth: 2,
868
- d: "M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"
869
- }
870
- )
871
- }
872
- ),
873
- users: /* @__PURE__ */ jsxRuntime.jsx(
874
- "svg",
875
- {
876
- className: "h-5 w-5",
877
- fill: "none",
878
- stroke: "currentColor",
879
- viewBox: "0 0 24 24",
880
- children: /* @__PURE__ */ jsxRuntime.jsx(
881
- "path",
882
- {
883
- strokeLinecap: "round",
884
- strokeLinejoin: "round",
885
- strokeWidth: 2,
886
- d: "M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"
887
- }
888
- )
889
- }
890
- ),
891
- package: /* @__PURE__ */ jsxRuntime.jsx(
892
- "svg",
893
- {
894
- className: "h-5 w-5",
895
- fill: "none",
896
- stroke: "currentColor",
897
- viewBox: "0 0 24 24",
898
- children: /* @__PURE__ */ jsxRuntime.jsx(
899
- "path",
900
- {
901
- strokeLinecap: "round",
902
- strokeLinejoin: "round",
903
- strokeWidth: 2,
904
- d: "M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4"
905
- }
906
- )
907
- }
908
- ),
909
- "chart-bar": /* @__PURE__ */ jsxRuntime.jsx(
910
- "svg",
911
- {
912
- className: "h-5 w-5",
913
- fill: "none",
914
- stroke: "currentColor",
915
- viewBox: "0 0 24 24",
916
- children: /* @__PURE__ */ jsxRuntime.jsx(
917
- "path",
918
- {
919
- strokeLinecap: "round",
920
- strokeLinejoin: "round",
921
- strokeWidth: 2,
922
- d: "M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
923
- }
924
- )
925
- }
926
- ),
927
- "document-text": /* @__PURE__ */ jsxRuntime.jsx(
928
- "svg",
929
- {
930
- className: "h-5 w-5",
931
- fill: "none",
932
- stroke: "currentColor",
933
- viewBox: "0 0 24 24",
934
- children: /* @__PURE__ */ jsxRuntime.jsx(
935
- "path",
936
- {
937
- strokeLinecap: "round",
938
- strokeLinejoin: "round",
939
- strokeWidth: 2,
940
- d: "M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
941
- }
942
- )
943
- }
944
- ),
945
- cog: /* @__PURE__ */ jsxRuntime.jsxs(
946
- "svg",
947
- {
948
- className: "h-5 w-5",
949
- fill: "none",
950
- stroke: "currentColor",
951
- viewBox: "0 0 24 24",
952
- children: [
953
- /* @__PURE__ */ jsxRuntime.jsx(
954
- "path",
955
- {
956
- strokeLinecap: "round",
957
- strokeLinejoin: "round",
958
- strokeWidth: 2,
959
- d: "M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"
960
- }
961
- ),
962
- /* @__PURE__ */ jsxRuntime.jsx(
963
- "path",
964
- {
965
- strokeLinecap: "round",
966
- strokeLinejoin: "round",
967
- strokeWidth: 2,
968
- d: "M15 12a3 3 0 11-6 0 3 3 0 016 0z"
969
- }
970
- )
971
- ]
972
- }
973
- ),
974
- "currency-dollar": /* @__PURE__ */ jsxRuntime.jsx(
975
- "svg",
976
- {
977
- className: "h-5 w-5",
978
- fill: "none",
979
- stroke: "currentColor",
980
- viewBox: "0 0 24 24",
981
- children: /* @__PURE__ */ jsxRuntime.jsx(
982
- "path",
983
- {
984
- strokeLinecap: "round",
985
- strokeLinejoin: "round",
986
- strokeWidth: 2,
987
- d: "M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
988
- }
989
- )
990
- }
991
- ),
992
- "clipboard-list": /* @__PURE__ */ jsxRuntime.jsx(
993
- "svg",
994
- {
995
- className: "h-5 w-5",
996
- fill: "none",
997
- stroke: "currentColor",
998
- viewBox: "0 0 24 24",
999
- children: /* @__PURE__ */ jsxRuntime.jsx(
1000
- "path",
1001
- {
1002
- strokeLinecap: "round",
1003
- strokeLinejoin: "round",
1004
- strokeWidth: 2,
1005
- d: "M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-3 7h3m-3 4h3m-6-4h.01M9 16h.01"
1006
- }
1007
- )
1008
- }
1009
- )
1010
- };
1011
- var colorClasses = {
1012
- blue: "bg-blue-500 hover:bg-blue-600 text-white",
1013
- green: "bg-green-500 hover:bg-green-600 text-white",
1014
- purple: "bg-purple-500 hover:bg-purple-600 text-white",
1015
- orange: "bg-orange-500 hover:bg-orange-600 text-white",
1016
- pink: "bg-pink-500 hover:bg-pink-600 text-white",
1017
- indigo: "bg-indigo-500 hover:bg-indigo-600 text-white",
1018
- teal: "bg-teal-500 hover:bg-teal-600 text-white",
1019
- red: "bg-red-500 hover:bg-red-600 text-white"
1020
- };
1021
- var AVAILABLE_ICONS = Object.keys(iconMap);
1022
- var AVAILABLE_COLORS = [
1023
- "blue",
1024
- "green",
1025
- "purple",
1026
- "orange",
1027
- "pink",
1028
- "indigo",
1029
- "teal",
1030
- "red"
1031
- ];
1032
- var formSchema = z__namespace.object({
1033
- label: z__namespace.string().min(1, "Vui l\xF2ng nh\u1EADp t\xEAn l\u1ED1i t\u1EAFt"),
1034
- href: z__namespace.string().min(1, "Vui l\xF2ng nh\u1EADp \u0111\u01B0\u1EDDng d\u1EABn"),
1035
- icon: z__namespace.string(),
1036
- color: z__namespace.string()
1037
- });
1038
- function QuickAccessDialog({
1039
- open,
1040
- onOpenChange,
1041
- onSubmit,
1042
- initialData,
1043
- availableFeatures
1044
- }) {
1045
- const form = reactHookForm.useForm({
1046
- resolver: zod.zodResolver(formSchema),
1047
- defaultValues: {
1048
- label: "",
1049
- href: "",
1050
- icon: "document-text",
1051
- color: "blue"
1052
- }
1053
- });
1054
- react.useEffect(() => {
1055
- if (open) {
1056
- form.reset(
1057
- initialData ? {
1058
- label: initialData.label,
1059
- href: initialData.href,
1060
- icon: initialData.icon,
1061
- color: initialData.color
1062
- } : {
1063
- label: "",
1064
- href: "",
1065
- icon: "document-text",
1066
- color: "blue"
1067
- }
1068
- );
1069
- }
1070
- }, [open, initialData, form]);
1071
- const handleSubmit = (values) => {
1072
- onSubmit(values);
1073
- onOpenChange(false);
1074
- };
1075
- const handleFeatureChange = (e) => {
1076
- const selectedHref = e.target.value;
1077
- const feature = availableFeatures?.find((f) => f.href === selectedHref);
1078
- if (feature) {
1079
- form.setValue("href", feature.href);
1080
- form.setValue("label", feature.label);
1081
- if (feature.icon && iconMap[feature.icon]) {
1082
- form.setValue("icon", feature.icon);
1083
- } else if (feature.icon && iconMap[feature.icon.replace(/-/g, "")]) ;
1084
- }
1085
- };
1086
- return /* @__PURE__ */ jsxRuntime.jsx(DialogPrimitive__namespace.Root, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(DialogPrimitive__namespace.Portal, { children: [
1087
- /* @__PURE__ */ jsxRuntime.jsx(DialogPrimitive__namespace.Overlay, { className: "fixed inset-0 z-50 bg-black/50 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0" }),
1088
- /* @__PURE__ */ jsxRuntime.jsxs(DialogPrimitive__namespace.Content, { className: "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg", children: [
1089
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col space-y-1.5 text-center sm:text-left", children: [
1090
- /* @__PURE__ */ jsxRuntime.jsx(DialogPrimitive__namespace.Title, { className: "text-lg font-semibold leading-none tracking-tight", children: initialData ? "S\u1EEDa l\u1ED1i t\u1EAFt" : "Th\xEAm l\u1ED1i t\u1EAFt m\u1EDBi" }),
1091
- /* @__PURE__ */ jsxRuntime.jsx(DialogPrimitive__namespace.Description, { className: "text-sm text-muted-foreground", children: "T\u1EA1o l\u1ED1i t\u1EAFt \u0111\u1EC3 truy c\u1EADp nhanh c\xE1c t\xEDnh n\u0103ng th\u01B0\u1EDDng d\xF9ng" })
1092
- ] }),
1093
- /* @__PURE__ */ jsxRuntime.jsxs(
1094
- "form",
1095
- {
1096
- onSubmit: form.handleSubmit(handleSubmit),
1097
- className: "space-y-4",
1098
- children: [
1099
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-4 py-4", children: [
1100
- availableFeatures && availableFeatures.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-4 items-center gap-4", children: [
1101
- /* @__PURE__ */ jsxRuntime.jsx(
1102
- "label",
1103
- {
1104
- htmlFor: "feature-select",
1105
- className: "text-right text-sm font-medium",
1106
- children: "Ch\u1EE9c n\u0103ng"
1107
- }
1108
- ),
1109
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-3", children: /* @__PURE__ */ jsxRuntime.jsxs(
1110
- "select",
1111
- {
1112
- id: "feature-select",
1113
- className: "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
1114
- onChange: handleFeatureChange,
1115
- defaultValue: initialData?.href || "",
1116
- children: [
1117
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "Ch\u1ECDn ch\u1EE9c n\u0103ng..." }),
1118
- availableFeatures.map((f) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: f.href, children: f.label }, f.href))
1119
- ]
1120
- }
1121
- ) })
1122
- ] }),
1123
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-4 items-center gap-4", children: [
1124
- /* @__PURE__ */ jsxRuntime.jsx(
1125
- "label",
1126
- {
1127
- htmlFor: "label",
1128
- className: "text-right text-sm font-medium",
1129
- children: "T\xEAn l\u1ED1i t\u1EAFt"
1130
- }
1131
- ),
1132
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "col-span-3", children: [
1133
- /* @__PURE__ */ jsxRuntime.jsx(
1134
- "input",
1135
- {
1136
- id: "label",
1137
- className: "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
1138
- ...form.register("label")
1139
- }
1140
- ),
1141
- form.formState.errors.label && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-red-500 mt-1", children: form.formState.errors.label.message })
1142
- ] })
1143
- ] }),
1144
- /* @__PURE__ */ jsxRuntime.jsxs(
1145
- "div",
1146
- {
1147
- className: cn(
1148
- "grid grid-cols-4 items-center gap-4",
1149
- availableFeatures?.length ? "hidden" : ""
1150
- ),
1151
- children: [
1152
- /* @__PURE__ */ jsxRuntime.jsx(
1153
- "label",
1154
- {
1155
- htmlFor: "href",
1156
- className: "text-right text-sm font-medium",
1157
- children: "\u0110\u01B0\u1EDDng d\u1EABn"
1158
- }
1159
- ),
1160
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "col-span-3", children: [
1161
- /* @__PURE__ */ jsxRuntime.jsx(
1162
- "input",
1163
- {
1164
- id: "href",
1165
- className: "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
1166
- ...form.register("href"),
1167
- readOnly: !!availableFeatures?.length
1168
- }
1169
- ),
1170
- form.formState.errors.href && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-red-500 mt-1", children: form.formState.errors.href.message })
1171
- ] })
1172
- ]
1173
- }
1174
- ),
1175
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-4 items-start gap-4", children: [
1176
- /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-right text-sm font-medium pt-2", children: "Icon" }),
1177
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-3 flex flex-wrap gap-2", children: AVAILABLE_ICONS.map((icon) => /* @__PURE__ */ jsxRuntime.jsx(
1178
- "button",
1179
- {
1180
- type: "button",
1181
- onClick: () => form.setValue("icon", icon),
1182
- className: cn(
1183
- "flex h-8 w-8 items-center justify-center rounded-md border transition-colors",
1184
- form.watch("icon") === icon ? "border-primary bg-primary/10 text-primary" : "border-input hover:bg-muted"
1185
- ),
1186
- children: iconMap[icon]
1187
- },
1188
- icon
1189
- )) })
1190
- ] }),
1191
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-4 items-start gap-4", children: [
1192
- /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-right text-sm font-medium pt-2", children: "M\xE0u s\u1EAFc" }),
1193
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-3 flex flex-wrap gap-2", children: AVAILABLE_COLORS.map((color) => /* @__PURE__ */ jsxRuntime.jsx(
1194
- "button",
1195
- {
1196
- type: "button",
1197
- onClick: () => form.setValue("color", color),
1198
- className: cn(
1199
- "h-8 w-8 rounded-full border-2 transition-all",
1200
- `bg-${color}-500`,
1201
- form.watch("color") === color ? "border-primary scale-110 shadow-sm" : "border-transparent hover:scale-105"
1202
- ),
1203
- "aria-label": color
1204
- },
1205
- color
1206
- )) })
1207
- ] })
1208
- ] }),
1209
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2", children: [
1210
- /* @__PURE__ */ jsxRuntime.jsx(DialogPrimitive__namespace.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
1211
- "button",
1212
- {
1213
- type: "button",
1214
- className: "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground h-9 px-4 py-2",
1215
- children: "H\u1EE7y"
1216
- }
1217
- ) }),
1218
- /* @__PURE__ */ jsxRuntime.jsx(
1219
- "button",
1220
- {
1221
- type: "submit",
1222
- className: "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-primary text-primary-foreground shadow hover:bg-primary/90 h-9 px-4 py-2",
1223
- children: initialData ? "L\u01B0u thay \u0111\u1ED5i" : "Th\xEAm m\u1EDBi"
1224
- }
1225
- )
1226
- ] })
1227
- ]
1228
- }
1229
- ),
1230
- /* @__PURE__ */ jsxRuntime.jsxs(DialogPrimitive__namespace.Close, { className: "absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground", children: [
1231
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-4 w-4" }),
1232
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Close" })
1233
- ] })
1234
- ] })
1235
- ] }) });
1236
- }
1237
- var containerVariants = {
1238
- hidden: { opacity: 0 },
1239
- visible: {
1240
- opacity: 1,
1241
- transition: {
1242
- staggerChildren: 0.05,
1243
- delayChildren: 0.1
1244
- }
1245
- }
1246
- };
1247
- var itemVariants = {
1248
- hidden: { opacity: 0, scale: 0.9 },
1249
- visible: {
1250
- opacity: 1,
1251
- scale: 1,
1252
- transition: {
1253
- duration: 0.2,
1254
- ease: "easeOut"
1255
- }
1256
- }
1257
- };
1258
- function QuickAccessMenu({
1259
- items,
1260
- onItemClick,
1261
- onItemsChange,
1262
- className,
1263
- editable = false,
1264
- availableFeatures
1265
- }) {
1266
- const [isEditing, setIsEditing] = react.useState(false);
1267
- const [dialogOpen, setDialogOpen] = react.useState(false);
1268
- const [editingItem, setEditingItem] = react.useState(
1269
- void 0
1270
- );
1271
- const handleAddStart = () => {
1272
- setEditingItem(void 0);
1273
- setDialogOpen(true);
1274
- };
1275
- const handleEditStart = (item, e) => {
1276
- e.stopPropagation();
1277
- setEditingItem(item);
1278
- setDialogOpen(true);
1279
- };
1280
- const handleDelete = (itemId, e) => {
1281
- e.stopPropagation();
1282
- if (confirm("B\u1EA1n c\xF3 ch\u1EAFc mu\u1ED1n x\xF3a l\u1ED1i t\u1EAFt n\xE0y?")) {
1283
- onItemsChange?.(items.filter((item) => item.id !== itemId));
1284
- }
1285
- };
1286
- const handleSave = (data) => {
1287
- const isDuplicate = items.some(
1288
- (item) => item.href === data.href && item.id !== editingItem?.id
1289
- );
1290
- if (isDuplicate) {
1291
- alert("L\u1ED1i t\u1EAFt n\xE0y \u0111\xE3 t\u1ED3n t\u1EA1i!");
1292
- return;
1293
- }
1294
- if (editingItem) {
1295
- onItemsChange?.(
1296
- items.map(
1297
- (item) => item.id === editingItem.id ? { ...item, ...data } : item
1298
- )
1299
- );
1300
- } else {
1301
- const newItem = {
1302
- ...data,
1303
- id: `custom-${Date.now()}`
1304
- };
1305
- onItemsChange?.([...items, newItem]);
1306
- }
1307
- };
1308
- if (items.length === 0 && !editable) {
1309
- return null;
1310
- }
1311
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("space-y-4", className), children: [
1312
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-slate-900 dark:text-white text-lg font-bold", children: "Quick Shortcuts" }) }),
1313
- /* @__PURE__ */ jsxRuntime.jsx(
1314
- framerMotion.motion.div,
1315
- {
1316
- variants: containerVariants,
1317
- initial: "hidden",
1318
- animate: "visible",
1319
- className: "grid grid-cols-2 gap-4 sm:grid-cols-4",
1320
- children: /* @__PURE__ */ jsxRuntime.jsxs(framerMotion.AnimatePresence, { mode: "popLayout", children: [
1321
- items.map((item) => /* @__PURE__ */ jsxRuntime.jsxs(
1322
- framerMotion.motion.button,
1323
- {
1324
- variants: itemVariants,
1325
- whileHover: { scale: 1.02 },
1326
- whileTap: { scale: 0.98 },
1327
- onClick: () => {
1328
- if (!isEditing) {
1329
- onItemClick?.(item);
1330
- }
1331
- },
1332
- className: cn(
1333
- "group relative flex flex-col items-center justify-center gap-3 bg-white dark:bg-slate-800 p-5 rounded-xl border border-slate-200 dark:border-slate-700 shadow-sm hover:shadow-md hover:border-primary/50 transition-all text-left",
1334
- isEditing && "cursor-default opacity-90 hover:opacity-100"
1335
- ),
1336
- children: [
1337
- isEditing && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1338
- /* @__PURE__ */ jsxRuntime.jsx(
1339
- "div",
1340
- {
1341
- onClick: (e) => handleDelete(item.id, e),
1342
- className: "absolute -right-2 -top-2 z-10 flex h-6 w-6 cursor-pointer items-center justify-center rounded-full bg-destructive text-white shadow-sm ring-2 ring-white hover:bg-destructive/90",
1343
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-3 w-3" })
1344
- }
1345
- ),
1346
- /* @__PURE__ */ jsxRuntime.jsx(
1347
- "div",
1348
- {
1349
- onClick: (e) => handleEditStart(item, e),
1350
- className: "absolute -left-2 -top-2 z-10 flex h-6 w-6 cursor-pointer items-center justify-center rounded-full bg-background text-foreground shadow-sm ring-2 ring-white hover:bg-muted",
1351
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { className: "h-3 w-3" })
1352
- }
1353
- )
1354
- ] }),
1355
- editable && /* @__PURE__ */ jsxRuntime.jsx(
1356
- "span",
1357
- {
1358
- onClick: () => setIsEditing(!isEditing),
1359
- className: "absolute top-2 right-2 text-slate-300 dark:text-slate-600 cursor-move opacity-100 hover:text-primary transition-colors",
1360
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.GripVertical, { size: 18 })
1361
- }
1362
- ),
1363
- /* @__PURE__ */ jsxRuntime.jsx(
1364
- "div",
1365
- {
1366
- className: cn(
1367
- "size-12 rounded-full flex items-center justify-center group-hover:scale-110 transition-transform",
1368
- colorClasses[item.color || "blue"] || colorClasses.blue
1369
- ),
1370
- children: iconMap[item.icon] || iconMap["document-text"]
1371
- }
1372
- ),
1373
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-slate-700 dark:text-slate-200 font-semibold text-sm", children: item.label })
1374
- ]
1375
- },
1376
- item.id
1377
- )),
1378
- editable && /* @__PURE__ */ jsxRuntime.jsxs(
1379
- framerMotion.motion.button,
1380
- {
1381
- variants: itemVariants,
1382
- onClick: handleAddStart,
1383
- whileHover: { scale: 1.02 },
1384
- whileTap: { scale: 0.98 },
1385
- className: "flex flex-col items-center justify-center gap-2 bg-slate-50 dark:bg-slate-800/50 p-5 rounded-xl border-2 border-dashed border-slate-300 dark:border-slate-600 hover:border-primary hover:bg-slate-100 dark:hover:bg-slate-800 transition-all text-slate-500 hover:text-primary cursor-pointer group h-full min-h-[140px]",
1386
- children: [
1387
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-8 w-8 group-hover:scale-110 transition-transform" }),
1388
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium", children: "Add Shortcut" })
1389
- ]
1390
- }
1391
- )
1392
- ] })
1393
- }
1394
- ),
1395
- /* @__PURE__ */ jsxRuntime.jsx(
1396
- QuickAccessDialog,
1397
- {
1398
- open: dialogOpen,
1399
- onOpenChange: setDialogOpen,
1400
- onSubmit: handleSave,
1401
- initialData: editingItem,
1402
- availableFeatures
1403
- }
1404
- )
1405
- ] });
1406
- }
1407
- var DEFAULT_QUICK_ACCESS = [
1408
- {
1409
- id: "pos",
1410
- label: "B\xE1n h\xE0ng",
1411
- href: "/sales/pos",
1412
- icon: "shopping-cart",
1413
- color: "green"
1414
- },
1415
- {
1416
- id: "orders",
1417
- label: "\u0110\u01A1n h\xE0ng",
1418
- href: "/sales-orders",
1419
- icon: "clipboard-list",
1420
- color: "blue"
1421
- },
1422
- {
1423
- id: "customers",
1424
- label: "Kh\xE1ch h\xE0ng",
1425
- href: "/customers",
1426
- icon: "users",
1427
- color: "purple"
1428
- },
1429
- {
1430
- id: "inventory",
1431
- label: "T\u1ED3n kho",
1432
- href: "/inventory",
1433
- icon: "package",
1434
- color: "orange"
1435
- },
1436
- {
1437
- id: "reports",
1438
- label: "B\xE1o c\xE1o",
1439
- href: "/reports",
1440
- icon: "chart-bar",
1441
- color: "teal"
1442
- },
1443
- {
1444
- id: "finance",
1445
- label: "Thu chi",
1446
- href: "/finance",
1447
- icon: "currency-dollar",
1448
- color: "pink"
1449
- },
1450
- {
1451
- id: "products",
1452
- label: "S\u1EA3n ph\u1EA9m",
1453
- href: "/crud/products",
1454
- icon: "package",
1455
- color: "indigo"
1456
- },
1457
- {
1458
- id: "settings",
1459
- label: "C\xE0i \u0111\u1EB7t",
1460
- href: "/admin",
1461
- icon: "cog",
1462
- color: "red"
1463
- }
1464
- ];
1465
- var LOCAL_STORAGE_KEY = "goerp_home_preferences";
1466
- async function fetchPreferences(url) {
1467
- const res = await fetch(url);
1468
- if (!res.ok) {
1469
- throw new Error("Failed to fetch preferences");
1470
- }
1471
- return res.json();
1472
- }
1473
- function useWidgetPreferences({
1474
- userId,
1475
- apiEndpoint = "/api/user/preferences",
1476
- fallbackToLocalStorage = false
1477
- } = {}) {
1478
- const [localPreferences, setLocalPreferences] = react.useState(null);
1479
- const { data, error, isLoading, mutate } = useSWR__default.default(
1480
- userId ? `${apiEndpoint}?userId=${userId}` : null,
1481
- fetchPreferences,
1482
- {
1483
- revalidateOnFocus: false,
1484
- // Only use localPreferences as fallback if explicitly enabled
1485
- fallbackData: fallbackToLocalStorage ? localPreferences || void 0 : void 0
1486
- }
1487
- );
1488
- const storageKey = userId ? `${LOCAL_STORAGE_KEY}_${userId}` : LOCAL_STORAGE_KEY;
1489
- react.useEffect(() => {
1490
- if (fallbackToLocalStorage && typeof window !== "undefined") {
1491
- try {
1492
- const stored = localStorage.getItem(storageKey);
1493
- if (stored) {
1494
- setLocalPreferences(JSON.parse(stored));
1495
- } else {
1496
- setLocalPreferences(null);
1497
- }
1498
- } catch {
1499
- }
1500
- }
1501
- }, [fallbackToLocalStorage, storageKey]);
1502
- const preferences = data || localPreferences || {
1503
- widgets: DEFAULT_WIDGETS,
1504
- quickAccess: []
1505
- };
1506
- const savePreferences = react.useCallback(
1507
- async (newPreferences) => {
1508
- mutate(newPreferences, false);
1509
- if (fallbackToLocalStorage && typeof window !== "undefined") {
1510
- try {
1511
- localStorage.setItem(storageKey, JSON.stringify(newPreferences));
1512
- setLocalPreferences(newPreferences);
1513
- } catch {
1514
- }
1515
- }
1516
- if (userId) {
1517
- try {
1518
- const res = await fetch(apiEndpoint, {
1519
- method: "PUT",
1520
- headers: { "Content-Type": "application/json" },
1521
- body: JSON.stringify({
1522
- userId,
1523
- ...newPreferences
1524
- })
1525
- });
1526
- if (!res.ok) {
1527
- throw new Error("Failed to save preferences");
1528
- }
1529
- mutate();
1530
- } catch (err) {
1531
- console.error("Failed to save preferences to API:", err);
1532
- mutate();
1533
- }
1534
- }
1535
- },
1536
- [userId, apiEndpoint, fallbackToLocalStorage, mutate, storageKey]
1537
- );
1538
- const updateWidgets = react.useCallback(
1539
- async (widgets) => {
1540
- await savePreferences({
1541
- ...preferences,
1542
- widgets
1543
- });
1544
- },
1545
- [preferences, savePreferences]
1546
- );
1547
- const updateQuickAccess = react.useCallback(
1548
- async (quickAccess) => {
1549
- await savePreferences({
1550
- ...preferences,
1551
- quickAccess
1552
- });
1553
- },
1554
- [preferences, savePreferences]
1555
- );
1556
- const resetToDefaults = react.useCallback(async () => {
1557
- await savePreferences({
1558
- widgets: DEFAULT_WIDGETS,
1559
- quickAccess: []
1560
- });
1561
- }, [savePreferences]);
1562
- return {
1563
- widgets: preferences.widgets,
1564
- quickAccess: preferences.quickAccess,
1565
- loading: isLoading,
1566
- error: error?.message || null,
1567
- updateWidgets,
1568
- updateQuickAccess,
1569
- resetToDefaults
1570
- };
1571
- }
1572
- function HomePage({
1573
- userName = "B\u1EA1n",
1574
- userId,
1575
- widgetData: initialWidgetData,
1576
- availableFeatures,
1577
- widgetDataLoading = false,
1578
- showFeatureShowcase = false,
1579
- className,
1580
- basePath = "",
1581
- dashboardApiUrl
1582
- }) {
1583
- const router = navigation.useRouter();
1584
- const { data: fetchedWidgetData, isLoading: isFetching } = useSWR__default.default(
1585
- initialWidgetData ? null : dashboardApiUrl,
1586
- async (url) => {
1587
- const res = await fetch(url);
1588
- if (!res.ok) throw new Error("Failed to fetch widget data");
1589
- return res.json();
1590
- },
1591
- {
1592
- revalidateOnFocus: false,
1593
- dedupingInterval: 6e4
1594
- // 1 minute
1595
- }
1596
- );
1597
- const {
1598
- widgets,
1599
- quickAccess,
1600
- loading: prefsLoading,
1601
- updateWidgets,
1602
- updateQuickAccess
1603
- } = useWidgetPreferences({
1604
- userId
1605
- });
1606
- const filteredFeatures = availableFeatures?.filter(
1607
- (f) => f.href !== "/home" && f.href !== basePath + "/home" && f.href !== "/"
1608
- );
1609
- const handleQuickAccessClick = (item) => {
1610
- router.push(`${basePath}${item.href}`);
1611
- };
1612
- const getAuthorizedDefaults = () => {
1613
- if (filteredFeatures && filteredFeatures.length > 0) {
1614
- const colors = [
1615
- "blue",
1616
- "green",
1617
- "purple",
1618
- "orange",
1619
- "teal",
1620
- "pink",
1621
- "indigo",
1622
- "red"
1623
- ];
1624
- return filteredFeatures.slice(0, 5).map((feature, index) => ({
1625
- id: `default-${index}`,
1626
- label: feature.label,
1627
- href: feature.href,
1628
- icon: feature.icon || "document-text",
1629
- color: colors[index % colors.length]
1630
- }));
1631
- }
1632
- return [];
1633
- };
1634
- const displayQuickAccess = quickAccess && quickAccess.length > 0 ? quickAccess : getAuthorizedDefaults();
1635
- return /* @__PURE__ */ jsxRuntime.jsx(
1636
- "div",
1637
- {
1638
- className: cn(
1639
- "min-h-screen bg-slate-50/50 dark:bg-slate-900/50",
1640
- className
1641
- ),
1642
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full mx-auto px-4 md:px-6 lg:px-8 py-6 space-y-8", children: [
1643
- /* @__PURE__ */ jsxRuntime.jsx(WelcomeCard, { userName }),
1644
- false,
1645
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "animate-in fade-in slide-in-from-bottom-4 duration-500 delay-300", children: /* @__PURE__ */ jsxRuntime.jsx(
1646
- QuickAccessMenu,
1647
- {
1648
- items: displayQuickAccess,
1649
- onItemClick: handleQuickAccessClick,
1650
- editable: true,
1651
- onItemsChange: updateQuickAccess,
1652
- availableFeatures: filteredFeatures
1653
- }
1654
- ) }),
1655
- showFeatureShowcase && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "animate-in fade-in slide-in-from-bottom-4 duration-500 delay-500 min-h-[400px]", children: /* @__PURE__ */ jsxRuntime.jsx(FeatureShowcase, {}) })
1656
- ] })
1657
- }
1658
- );
1659
- }
1660
-
1661
- exports.BaseWidget = BaseWidget;
1662
- exports.CustomersWidget = CustomersWidget;
1663
- exports.DEFAULT_QUICK_ACCESS = DEFAULT_QUICK_ACCESS;
1664
- exports.DEFAULT_WIDGETS = DEFAULT_WIDGETS;
1665
- exports.ERP_FEATURES = ERP_FEATURES;
1666
- exports.FeatureShowcase = FeatureShowcase;
1667
- exports.HomePage = HomePage;
1668
- exports.OrdersWidget = OrdersWidget;
1669
- exports.QuickAccessMenu = QuickAccessMenu;
1670
- exports.RevenueWidget = RevenueWidget;
1671
- exports.StockWidget = StockWidget;
1672
- exports.WIDGET_LABELS = WIDGET_LABELS;
1673
- exports.WelcomeCard = WelcomeCard;
1674
- exports.WidgetContainer = WidgetContainer;
1675
- exports.WidgetStat = WidgetStat;
1676
- exports.useWidgetPreferences = useWidgetPreferences;
1677
- //# sourceMappingURL=index.js.map
1678
- //# sourceMappingURL=index.js.map