@finema/finework-layer 0.2.77 → 0.2.79

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 (41) hide show
  1. package/.husky/pre-commit +1 -1
  2. package/.playground/app/assets/css/main.css +6 -6
  3. package/.playground/app/pages/layout-admin/[id]/index.vue +145 -145
  4. package/.playground/app/pages/layout-admin/test/[id]/index.vue +286 -286
  5. package/.playground/app/pages/layout-admin.vue +283 -285
  6. package/.playground/app/pages/layout-user.vue +284 -284
  7. package/.playground/app/pages/submenu/layout-admin.vue +210 -210
  8. package/.vscode/settings.json +5 -1
  9. package/CHANGELOG.md +382 -374
  10. package/app/app.config.ts +144 -144
  11. package/app/app.vue +10 -10
  12. package/app/assets/css/main.css +77 -77
  13. package/app/components/Button/ActionIcon.vue +29 -29
  14. package/app/components/Button/Back.vue +22 -22
  15. package/app/components/Format/Currency.vue +17 -17
  16. package/app/components/Format/Date.vue +24 -24
  17. package/app/components/Format/DateTime.vue +24 -24
  18. package/app/components/Format/Number.vue +17 -17
  19. package/app/components/Format/Percent.vue +38 -38
  20. package/app/components/Format/TimeFromNow.vue +38 -38
  21. package/app/components/InfoItemList.vue +196 -196
  22. package/app/components/Layout/Admin/Sidebar.vue +343 -329
  23. package/app/components/Layout/Admin/index.vue +240 -224
  24. package/app/components/Layout/Apps.vue +45 -45
  25. package/app/components/Layout/User/index.vue +102 -102
  26. package/app/components/Notifications/index.vue +162 -162
  27. package/app/components/StatusBox.vue +56 -56
  28. package/app/composables/useAuth.ts +207 -207
  29. package/app/composables/useNotification.ts +76 -76
  30. package/app/composables/useRequestOptions.ts +86 -86
  31. package/app/constants/routes.ts +86 -86
  32. package/app/error.vue +218 -218
  33. package/app/middleware/auth.ts +45 -45
  34. package/app/middleware/common.ts +12 -12
  35. package/app/middleware/guest.ts +7 -7
  36. package/app/middleware/permissions.ts +29 -29
  37. package/bun.lock +2758 -2758
  38. package/eslint.config.js +206 -2
  39. package/index.d.ts +16 -16
  40. package/nuxt.config.ts +41 -41
  41. package/package.json +38 -38
@@ -1,284 +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>
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>