@finema/finework-layer 0.2.75 → 0.2.76

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 (51) hide show
  1. package/.husky/pre-commit +1 -1
  2. package/.playground/.env.example +5 -0
  3. package/.playground/app/assets/css/main.css +6 -6
  4. package/.playground/app/pages/layout-admin/[id]/index.vue +145 -145
  5. package/.playground/app/pages/layout-admin/test/[id]/index.vue +286 -286
  6. package/.playground/app/pages/layout-admin.vue +285 -285
  7. package/.playground/app/pages/layout-user.vue +284 -0
  8. package/.playground/app/pages/submenu/layout-admin.vue +210 -210
  9. package/.playground/public/admin/clock-in-admin-logo.png +0 -0
  10. package/.playground/public/admin/clock-in-logo.png +0 -0
  11. package/.playground/public/admin/clock-in.png +0 -0
  12. package/.playground/public/admin/pmo-logo.png +0 -0
  13. package/.playground/public/admin/spider-web.png +0 -0
  14. package/.playground/public/admin/super-admin-logo.png +0 -0
  15. package/.playground/public/admin/super-admin.png +0 -0
  16. package/.playground/public/admin/timesheet-admin-logo.png +0 -0
  17. package/.playground/public/admin/timesheet-logo.png +0 -0
  18. package/.playground/public/admin/timesheet.png +0 -0
  19. package/CHANGELOG.md +374 -367
  20. package/app/app.config.ts +144 -144
  21. package/app/app.vue +10 -10
  22. package/app/assets/css/main.css +77 -77
  23. package/app/components/Button/ActionIcon.vue +29 -29
  24. package/app/components/Button/Back.vue +22 -22
  25. package/app/components/Format/Currency.vue +17 -17
  26. package/app/components/Format/Date.vue +24 -24
  27. package/app/components/Format/DateTime.vue +24 -24
  28. package/app/components/Format/Number.vue +17 -17
  29. package/app/components/Format/Percent.vue +38 -38
  30. package/app/components/Format/TimeFromNow.vue +38 -38
  31. package/app/components/InfoItemList.vue +196 -196
  32. package/app/components/Layout/Admin/Sidebar.vue +329 -329
  33. package/app/components/Layout/Admin/index.vue +224 -270
  34. package/app/components/Layout/Apps.vue +45 -45
  35. package/app/components/Layout/User/index.vue +102 -145
  36. package/app/components/Notifications/index.vue +147 -0
  37. package/app/components/StatusBox.vue +56 -56
  38. package/app/composables/useAuth.ts +207 -207
  39. package/app/composables/useNotification.ts +64 -0
  40. package/app/composables/useRequestOptions.ts +86 -86
  41. package/app/constants/routes.ts +86 -86
  42. package/app/error.vue +218 -218
  43. package/app/middleware/auth.ts +45 -45
  44. package/app/middleware/common.ts +12 -12
  45. package/app/middleware/guest.ts +7 -7
  46. package/app/middleware/permissions.ts +29 -29
  47. package/bun.lock +2758 -2758
  48. package/index.d.ts +16 -16
  49. package/nuxt.config.ts +41 -41
  50. package/package.json +38 -38
  51. package/app/components/NotificationList.vue +0 -65
@@ -0,0 +1,284 @@
1
+ <template>
2
+ <LayoutUser
3
+ :sidebar-items="sidebarItems"
4
+ is-sidebar-group
5
+ root-app-path="/layout-user"
6
+ >
7
+ <NuxtLayout>
8
+ <div class="mb-4 flex gap-4">
9
+ <FormFields
10
+ :form="form"
11
+ :options="formSearchFields"
12
+ class="w-full max-w-[600px] gap-3"
13
+ />
14
+ <Button
15
+ icon="material-symbols:filter-list-rounded"
16
+ variant="outline"
17
+ color="neutral"
18
+ class="w-fit"
19
+ @click="showFilter = !showFilter"
20
+ >
21
+ Filters
22
+ <Badge
23
+ v-if="countForm"
24
+ class="size-6 items-center justify-center rounded-full"
25
+ :label="countForm"
26
+ />
27
+ </Button>
28
+ </div>
29
+ <Card> asdasdasdasdasdasd </Card>
30
+ <div class="bg-white p-4">
31
+ <Tabs
32
+ variant="link"
33
+ :items="items"
34
+ class="w-full"
35
+ />
36
+ <Tabs
37
+ orientation="vertical"
38
+ :items="items"
39
+ class="w-full"
40
+ />
41
+ </div>
42
+ <Button
43
+ variant="outline"
44
+ color="error"
45
+ icon="ph:trash"
46
+ />
47
+ <Button
48
+ variant="outline"
49
+ color="neutral"
50
+ icon="ph:trash"
51
+ />
52
+ <InfoItemList
53
+ :items="[
54
+ {
55
+ label: 'Status:',
56
+ value: 'Active',
57
+ },
58
+ {
59
+ label: '',
60
+ value: '42',
61
+ },
62
+ ]"
63
+ />
64
+ </NuxtLayout>
65
+ </LayoutUser>
66
+ </template>
67
+
68
+ <script lang="ts" setup>
69
+ import type { NavigationMenuItem } from '@nuxt/ui'
70
+
71
+ const auth = useAuth()
72
+ const showFilter = useState('pageFilterOpen')
73
+ const items = ref([
74
+ {
75
+ label: 'Account',
76
+ icon: 'i-lucide-user',
77
+ content: 'This is the account content.',
78
+ },
79
+ {
80
+ label: 'Password',
81
+ icon: 'i-lucide-lock',
82
+ content: 'This is the password content.',
83
+ },
84
+ ])
85
+
86
+ useApp().definePage({
87
+ title: 'Test Layout',
88
+ breadcrumbs: [
89
+ {
90
+ label: 'Home',
91
+ to: '/',
92
+ },
93
+ {
94
+ label: 'Test Layout',
95
+ to: '/test-layout',
96
+ },
97
+ ],
98
+ })
99
+
100
+ const form = useForm({
101
+ validationSchema: toTypedSchema(
102
+ v.object({
103
+ q: v.optional(v.pipe(v.string()), ''),
104
+ status: v.optional(v.pipe(v.string()), ''),
105
+ user_id: v.optional(v.pipe(v.string()), ''),
106
+ date_range: v.nullish(
107
+ v.object({
108
+ start: v.union([v.date(), v.string()]),
109
+ end: v.union([v.date(), v.string()]),
110
+ }),
111
+ ),
112
+ }),
113
+ ),
114
+ })
115
+
116
+ const countForm = computed(
117
+ () =>
118
+ [
119
+ form.values.q,
120
+ form.values.status,
121
+ form.values.user_id,
122
+ form.values.date_range?.start || form.values.date_range?.end,
123
+ ].filter(Boolean).length,
124
+ )
125
+
126
+ auth.me.set({
127
+ id: 'edab2396-2b6f-4855-b04e-a7c9ae9b70ae',
128
+ created_at: '2025-09-02T10:16:37.195Z',
129
+ updated_at: '2025-10-11T10:14:10.296Z',
130
+ email: 'long@finema.co',
131
+ full_name: 'Passakon Puttasuwan',
132
+ display_name: 'Long',
133
+ position: 'Technical Specialist',
134
+ team_code: 'DEV',
135
+ company: 'finema',
136
+ avatar_url:
137
+ 'https://avatars.slack-edge.com/2023-09-25/5968557597936_c750902e3ffc4f690d80_512.jpg',
138
+ slack_id: 'U0147KLKKH8',
139
+ azure_id: 'fd7fbb55-a249-4b79-b439-2270981bd3ae',
140
+ is_active: true,
141
+ joined_date: '2025-09-02T00:00:00Z',
142
+ access_level: {
143
+ user_id: 'edab2396-2b6f-4855-b04e-a7c9ae9b70ae',
144
+ clockin: 'ADMIN',
145
+ timesheet: 'ADMIN',
146
+ pmo: 'SUPER',
147
+ setting: 'SUPER',
148
+ created_at: '2025-08-15T16:40:29.319Z',
149
+ updated_at: '2025-09-30T10:14:59.228Z',
150
+ },
151
+ team: {
152
+ id: 'ef2abb00-d57f-4a38-a5d1-660ee63d2e1f',
153
+ created_at: '2025-08-26T10:36:53.598Z',
154
+ updated_at: '2025-10-01T06:00:29.941Z',
155
+ name: 'Developer Team',
156
+ code: 'DEV',
157
+ color: 'violet',
158
+ description: '',
159
+ working_start_at: '12:02:00',
160
+ working_end_at: '20:00:00',
161
+ created_by_id: null,
162
+ updated_by_id: '96d3ec63-be14-41c6-9268-ea8095d2a73b',
163
+ deleted_by_id: null,
164
+ },
165
+ })
166
+
167
+ const sidebarItems: NavigationMenuItem[] = [
168
+ {
169
+ children: [
170
+ {
171
+ label: 'Submenu 1',
172
+ to: '/layout-admin',
173
+ icon: 'i-heroicons-outline:home',
174
+ },
175
+ {
176
+ label: 'Submenu 2',
177
+ to: '/layout-admin/test/1',
178
+ icon: 'i-heroicons-outline:home',
179
+ },
180
+ ],
181
+ },
182
+ {
183
+ label: 'DASHBOARD',
184
+ children: [
185
+ {
186
+ label: 'Submenu 1',
187
+ to: '/submenu',
188
+ icon: 'i-heroicons-outline:home',
189
+ },
190
+ {
191
+ label: 'Submenu 2',
192
+ to: '/submenu/layout-admin',
193
+ icon: 'i-heroicons-outline:home',
194
+ },
195
+ ],
196
+ },
197
+ {
198
+ label: 'Users',
199
+ children: [
200
+ {
201
+ label: 'Submenu 1',
202
+ to: '/submenu1',
203
+ icon: 'i-heroicons-outline:home',
204
+ },
205
+ {
206
+ label: 'Submenu 2',
207
+ to: '/layout-admin',
208
+ icon: 'i-heroicons-outline:home',
209
+ children: [
210
+ {
211
+ label: 'Submenu 2-1',
212
+ to: '/layout-admin/1',
213
+ icon: 'i-heroicons-outline:home',
214
+ },
215
+ {
216
+ label: 'Submenu 2-2',
217
+ to: '/layout-admin/2',
218
+ },
219
+ ],
220
+ },
221
+ ],
222
+ },
223
+ ]
224
+
225
+ const formSearchFields = createFormFields(() => [
226
+ {
227
+ type: INPUT_TYPES.SEARCH,
228
+ props: {
229
+ name: 'q',
230
+ placeholder: 'ค้นหา',
231
+ },
232
+ },
233
+ ])
234
+
235
+ const formFields = createFormFields(() => [
236
+ {
237
+ type: INPUT_TYPES.SEARCH,
238
+ props: {
239
+ name: 'q',
240
+ placeholder: 'ค้นหา',
241
+ },
242
+ },
243
+ {
244
+ type: INPUT_TYPES.DATE_RANGE,
245
+ props: {
246
+ label: 'Tender Date',
247
+ name: 'date_range',
248
+ placeholder: 'ระบุช่วงเวลา Tender Date',
249
+ clearable: true,
250
+ },
251
+ },
252
+ {
253
+ type: INPUT_TYPES.SELECT,
254
+ props: {
255
+ name: 'user_id',
256
+ label: 'Main Responsibility',
257
+ placeholder: 'Main Responsibility',
258
+ options: [
259
+ {
260
+ value: 'test',
261
+ label: 'test',
262
+ },
263
+ ],
264
+ searchable: true,
265
+ clearable: true,
266
+ },
267
+ },
268
+ {
269
+ type: INPUT_TYPES.SELECT,
270
+ props: {
271
+ name: 'status',
272
+ label: 'status',
273
+ placeholder: 'All Status',
274
+ clearable: true,
275
+ options: [
276
+ {
277
+ value: 'test',
278
+ label: 'test',
279
+ },
280
+ ],
281
+ },
282
+ },
283
+ ])
284
+ </script>