@htlkg/components 0.0.2 → 0.0.4

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 (60) hide show
  1. package/README.md +52 -0
  2. package/dist/AdminWrapper.vue_vue_type_script_setup_true_lang-B32IylcT.js +367 -0
  3. package/dist/AdminWrapper.vue_vue_type_script_setup_true_lang-B32IylcT.js.map +1 -0
  4. package/dist/Alert.vue_vue_type_script_setup_true_lang-DxPCS-Hx.js +263 -0
  5. package/dist/Alert.vue_vue_type_script_setup_true_lang-DxPCS-Hx.js.map +1 -0
  6. package/dist/DateRange.vue_vue_type_script_setup_true_lang-BLVg1Hah.js +580 -0
  7. package/dist/DateRange.vue_vue_type_script_setup_true_lang-BLVg1Hah.js.map +1 -0
  8. package/dist/ProductBadge.vue_vue_type_script_setup_true_lang-Cmr2f4Cy.js +187 -0
  9. package/dist/ProductBadge.vue_vue_type_script_setup_true_lang-Cmr2f4Cy.js.map +1 -0
  10. package/dist/_plugin-vue_export-helper-1tPrXgE0.js +11 -0
  11. package/dist/_plugin-vue_export-helper-1tPrXgE0.js.map +1 -0
  12. package/dist/components.css +15 -0
  13. package/dist/composables/index.js +32 -573
  14. package/dist/composables/index.js.map +1 -1
  15. package/dist/data/index.js +18 -0
  16. package/dist/data/index.js.map +1 -0
  17. package/dist/domain/index.js +8 -0
  18. package/dist/domain/index.js.map +1 -0
  19. package/dist/filterHelpers-DgRyoYSa.js +1386 -0
  20. package/dist/filterHelpers-DgRyoYSa.js.map +1 -0
  21. package/dist/forms/index.js +6 -0
  22. package/dist/forms/index.js.map +1 -0
  23. package/dist/index-DGO_pNgG.js +79 -0
  24. package/dist/index-DGO_pNgG.js.map +1 -0
  25. package/dist/index-QK97OdqQ.js +25 -0
  26. package/dist/index-QK97OdqQ.js.map +1 -0
  27. package/dist/index.js +67 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/navigation/index.js +8 -0
  30. package/dist/navigation/index.js.map +1 -0
  31. package/dist/overlays/index.js +8 -0
  32. package/dist/overlays/index.js.map +1 -0
  33. package/dist/stores/index.js +14 -0
  34. package/dist/stores/index.js.map +1 -0
  35. package/dist/useAdminPage-GhgXp0x8.js +1070 -0
  36. package/dist/useAdminPage-GhgXp0x8.js.map +1 -0
  37. package/dist/useTable-DutR1gkg.js +293 -0
  38. package/dist/useTable-DutR1gkg.js.map +1 -0
  39. package/package.json +43 -14
  40. package/src/composables/composables.md +109 -0
  41. package/src/composables/index.ts +69 -0
  42. package/src/composables/useAdminPage.ts +462 -0
  43. package/src/composables/useConfirmation.ts +358 -0
  44. package/src/composables/usePageContext.ts +171 -0
  45. package/src/composables/useStats.ts +361 -0
  46. package/src/composables/useTable.ts +26 -5
  47. package/src/composables/useWizard.ts +448 -0
  48. package/src/data/DataTable.vue +553 -0
  49. package/src/data/Table/Table.vue +295 -0
  50. package/src/data/columnHelpers.ts +503 -0
  51. package/src/data/data.md +106 -0
  52. package/src/data/filterHelpers.ts +358 -0
  53. package/src/data/index.ts +31 -0
  54. package/src/domain/domain.md +102 -0
  55. package/src/forms/JsonSchemaForm.vue +4 -1
  56. package/src/forms/forms.md +89 -0
  57. package/src/index.ts +4 -3
  58. package/src/navigation/navigation.md +80 -0
  59. package/src/overlays/overlays.md +86 -0
  60. package/src/stores/stores.md +82 -0
@@ -0,0 +1,109 @@
1
+ # Composables Module
2
+
3
+ Vue composables for component state management.
4
+
5
+ ## Available Composables
6
+
7
+ ### useModal
8
+
9
+ Manage modal state.
10
+
11
+ ```vue
12
+ <script setup>
13
+ import { useModal } from '@htlkg/components/composables';
14
+
15
+ const { isOpen, open, close, toggle } = useModal();
16
+ </script>
17
+
18
+ <template>
19
+ <button @click="open">Open Modal</button>
20
+ <Modal v-model:open="isOpen" @close="close" />
21
+ </template>
22
+ ```
23
+
24
+ ### useTable
25
+
26
+ Manage table state (pagination, sorting, selection).
27
+
28
+ ```vue
29
+ <script setup>
30
+ import { useTable } from '@htlkg/components/composables';
31
+
32
+ const {
33
+ currentPage,
34
+ pageSize,
35
+ sortBy,
36
+ sortOrder,
37
+ selectedRows,
38
+ setPage,
39
+ setSort,
40
+ toggleSelection,
41
+ clearSelection,
42
+ } = useTable({
43
+ initialPage: 1,
44
+ initialPageSize: 10,
45
+ });
46
+ </script>
47
+ ```
48
+
49
+ ### useForm
50
+
51
+ Manage form state and validation.
52
+
53
+ ```vue
54
+ <script setup>
55
+ import { useForm } from '@htlkg/components/composables';
56
+
57
+ const {
58
+ values,
59
+ errors,
60
+ isValid,
61
+ isDirty,
62
+ setValue,
63
+ validate,
64
+ reset,
65
+ submit,
66
+ } = useForm({
67
+ initialValues: { name: '', email: '' },
68
+ validationSchema: schema,
69
+ });
70
+ </script>
71
+ ```
72
+
73
+ ### useNotification
74
+
75
+ Manage notifications.
76
+
77
+ ```vue
78
+ <script setup>
79
+ import { useNotification } from '@htlkg/components/composables';
80
+
81
+ const { show, success, error, warning, info, clear } = useNotification();
82
+
83
+ const handleSave = async () => {
84
+ try {
85
+ await save();
86
+ success('Saved successfully');
87
+ } catch (e) {
88
+ error('Failed to save');
89
+ }
90
+ };
91
+ </script>
92
+ ```
93
+
94
+ ### useLoading
95
+
96
+ Manage loading states.
97
+
98
+ ```vue
99
+ <script setup>
100
+ import { useLoading } from '@htlkg/components/composables';
101
+
102
+ const { isLoading, start, stop, wrap } = useLoading();
103
+
104
+ const fetchData = wrap(async () => {
105
+ const data = await api.fetch();
106
+ return data;
107
+ });
108
+ </script>
109
+ ```
@@ -4,3 +4,72 @@ export { useTabs, type UseTabsOptions, type UseTabsReturn, type Tab } from './us
4
4
  export { useForm, type UseFormOptions, type UseFormReturn, type ValidationRule, type FieldConfig } from './useForm';
5
5
  export { useFormValidation } from './useFormValidation';
6
6
  export { useNotifications, type Notification, type UseNotificationsReturn } from './useNotifications';
7
+
8
+ // Page context
9
+ export {
10
+ usePageContext,
11
+ useHasAccessToBrand,
12
+ useHasAccessToAccount,
13
+ useUserRoles,
14
+ useHasRole,
15
+ setUser,
16
+ setCurrentBrand,
17
+ $user,
18
+ $currentBrand,
19
+ PAGE_CONTEXT_KEY,
20
+ type PageContext,
21
+ type PageUser,
22
+ type PageBrand,
23
+ } from './usePageContext';
24
+
25
+ // Wizard - Multi-step form state management
26
+ export {
27
+ useWizard,
28
+ type WizardStep,
29
+ type StepStatus,
30
+ type StepperStep,
31
+ type UseWizardOptions,
32
+ type UseWizardReturn,
33
+ type JsonSchema,
34
+ } from './useWizard';
35
+
36
+ // Stats - Dashboard statistics with staggered loading
37
+ export {
38
+ useStats,
39
+ countStat,
40
+ sumStat,
41
+ averageStat,
42
+ percentageStat,
43
+ type StatDefinition,
44
+ type StatItem,
45
+ type StatColor,
46
+ type ChangeType,
47
+ type UseStatsOptions,
48
+ type UseStatsReturn,
49
+ } from './useStats';
50
+
51
+ // Confirmation - Action confirmation dialogs
52
+ export {
53
+ useConfirmation,
54
+ useGlobalConfirmation,
55
+ resetGlobalConfirmation,
56
+ type ConfirmationConfig,
57
+ type ConfirmationVariant,
58
+ type UseConfirmationOptions,
59
+ type UseConfirmationReturn,
60
+ } from './useConfirmation';
61
+
62
+ // Admin Page - Common page-level setup
63
+ export {
64
+ useAdminPage,
65
+ useListPage,
66
+ useDetailPage,
67
+ useFormPage,
68
+ type BreadcrumbItem,
69
+ type PageAction,
70
+ type UseAdminPageOptions,
71
+ type UseAdminPageReturn,
72
+ type UseListPageOptions,
73
+ type UseDetailPageOptions,
74
+ type UseFormPageOptions,
75
+ } from './useAdminPage';
@@ -0,0 +1,462 @@
1
+ /**
2
+ * Admin Page Composable
3
+ *
4
+ * Provides common page-level state and configuration for admin pages.
5
+ * Integrates with AdminWrapper, Breadcrumbs, and page context.
6
+ */
7
+
8
+ import { ref, computed, onMounted, type Ref, type ComputedRef, type Component } from 'vue';
9
+ import { usePageContext, type PageUser, type PageBrand } from './usePageContext';
10
+
11
+ /**
12
+ * Breadcrumb item
13
+ */
14
+ export interface BreadcrumbItem {
15
+ /** Display name */
16
+ name: string;
17
+ /** Route path or name */
18
+ href?: string;
19
+ /** Whether this is the current page */
20
+ current?: boolean;
21
+ /** Icon component */
22
+ icon?: Component;
23
+ }
24
+
25
+ /**
26
+ * Page action button
27
+ */
28
+ export interface PageAction {
29
+ /** Unique identifier */
30
+ id: string;
31
+ /** Button text */
32
+ text: string;
33
+ /** Button icon */
34
+ icon?: Component;
35
+ /** Button color variant */
36
+ color?: 'primary' | 'secondary' | 'danger' | 'warning';
37
+ /** Whether button is disabled */
38
+ disabled?: boolean;
39
+ /** Whether button shows loading state */
40
+ loading?: boolean;
41
+ /** Click handler */
42
+ onClick?: () => void | Promise<void>;
43
+ }
44
+
45
+ /**
46
+ * Options for useAdminPage
47
+ */
48
+ export interface UseAdminPageOptions {
49
+ /** Page title */
50
+ title: string;
51
+ /** Page subtitle (optional) */
52
+ subtitle?: string;
53
+ /** Page description (optional) */
54
+ description?: string;
55
+ /** Breadcrumb items (without current page) */
56
+ breadcrumbs?: BreadcrumbItem[];
57
+ /** Active sidebar item ID */
58
+ sidebarActive?: string;
59
+ /** Page action buttons */
60
+ actions?: PageAction[];
61
+ /** Initial loading state */
62
+ loading?: boolean;
63
+ /** Loading reveal delay (ms) */
64
+ loadingDelay?: number;
65
+ }
66
+
67
+ /**
68
+ * Return type for useAdminPage
69
+ */
70
+ export interface UseAdminPageReturn {
71
+ // Page info
72
+ title: string;
73
+ subtitle: string | undefined;
74
+ description: string | undefined;
75
+
76
+ // Breadcrumbs
77
+ breadcrumbs: ComputedRef<BreadcrumbItem[]>;
78
+
79
+ // Loading state (with staggered reveal)
80
+ loadingHeader: Ref<boolean>;
81
+ loadingContent: Ref<boolean>;
82
+ setLoading: (loading: boolean) => void;
83
+
84
+ // Page context
85
+ user: ComputedRef<PageUser | null>;
86
+ brand: ComputedRef<PageBrand | null>;
87
+ isAdmin: ComputedRef<boolean>;
88
+ isSuperAdmin: ComputedRef<boolean>;
89
+
90
+ // Actions
91
+ actions: Ref<PageAction[]>;
92
+ updateAction: (id: string, updates: Partial<PageAction>) => void;
93
+ setActionLoading: (id: string, loading: boolean) => void;
94
+
95
+ // Wrapper props
96
+ wrapperProps: ComputedRef<{
97
+ currentPage: string | undefined;
98
+ }>;
99
+
100
+ // Header props (for uiViewHeader)
101
+ headerProps: ComputedRef<{
102
+ title: string;
103
+ subtitle?: string;
104
+ description?: string;
105
+ pages: BreadcrumbItem[];
106
+ loading: boolean;
107
+ }>;
108
+ }
109
+
110
+ /**
111
+ * Creates an admin page composable for common page setup
112
+ *
113
+ * @example
114
+ * ```typescript
115
+ * const page = useAdminPage({
116
+ * title: 'Campaigns',
117
+ * subtitle: 'Manage your email campaigns',
118
+ * breadcrumbs: [
119
+ * { name: 'Dashboard', href: '/dashboard' },
120
+ * { name: 'Marketing', href: '/marketing' },
121
+ * ],
122
+ * sidebarActive: 'campaigns',
123
+ * actions: [
124
+ * {
125
+ * id: 'create',
126
+ * text: 'Create Campaign',
127
+ * icon: PlusIcon,
128
+ * color: 'primary',
129
+ * onClick: () => navigateTo('/campaigns/create'),
130
+ * },
131
+ * ],
132
+ * });
133
+ *
134
+ * // In template
135
+ * <AdminWrapper v-bind="page.wrapperProps">
136
+ * <uiViewHeader v-bind="page.headerProps" />
137
+ * <!-- Content -->
138
+ * </AdminWrapper>
139
+ * ```
140
+ */
141
+ export function useAdminPage(options: UseAdminPageOptions): UseAdminPageReturn {
142
+ const {
143
+ title,
144
+ subtitle,
145
+ description,
146
+ breadcrumbs: initialBreadcrumbs = [],
147
+ sidebarActive,
148
+ actions: initialActions = [],
149
+ loading: initialLoading = true,
150
+ loadingDelay = 200,
151
+ } = options;
152
+
153
+ // Get page context
154
+ const context = usePageContext();
155
+
156
+ // Loading states
157
+ const loadingHeader = ref(initialLoading);
158
+ const loadingContent = ref(initialLoading);
159
+
160
+ // Actions state
161
+ const actions = ref<PageAction[]>([...initialActions]);
162
+
163
+ // Staggered loading reveal on mount
164
+ onMounted(() => {
165
+ if (initialLoading) {
166
+ setTimeout(() => {
167
+ loadingHeader.value = false;
168
+ }, loadingDelay);
169
+
170
+ setTimeout(() => {
171
+ loadingContent.value = false;
172
+ }, loadingDelay * 2);
173
+ }
174
+ });
175
+
176
+ // Computed: User from context
177
+ const user = computed(() => context.value.user);
178
+
179
+ // Computed: Brand from context
180
+ const brand = computed(() => context.value.brand);
181
+
182
+ // Computed: Admin status
183
+ const isAdmin = computed(() => user.value?.isAdmin ?? false);
184
+
185
+ // Computed: Super admin status
186
+ const isSuperAdmin = computed(() => user.value?.isSuperAdmin ?? false);
187
+
188
+ // Computed: Breadcrumbs with current page
189
+ const breadcrumbs = computed<BreadcrumbItem[]>(() => [
190
+ ...initialBreadcrumbs.map(b => ({ ...b, current: false })),
191
+ { name: title, current: true },
192
+ ]);
193
+
194
+ // Computed: Props for AdminWrapper
195
+ const wrapperProps = computed(() => ({
196
+ currentPage: sidebarActive,
197
+ }));
198
+
199
+ // Computed: Props for uiViewHeader
200
+ const headerProps = computed(() => ({
201
+ title,
202
+ subtitle,
203
+ description,
204
+ pages: breadcrumbs.value,
205
+ loading: loadingHeader.value,
206
+ }));
207
+
208
+ // Set loading state
209
+ function setLoading(loading: boolean): void {
210
+ if (loading) {
211
+ loadingHeader.value = true;
212
+ loadingContent.value = true;
213
+ } else {
214
+ setTimeout(() => {
215
+ loadingHeader.value = false;
216
+ }, loadingDelay);
217
+
218
+ setTimeout(() => {
219
+ loadingContent.value = false;
220
+ }, loadingDelay * 2);
221
+ }
222
+ }
223
+
224
+ // Update action by ID
225
+ function updateAction(id: string, updates: Partial<PageAction>): void {
226
+ const index = actions.value.findIndex(a => a.id === id);
227
+ if (index !== -1) {
228
+ actions.value[index] = { ...actions.value[index], ...updates };
229
+ }
230
+ }
231
+
232
+ // Set action loading state
233
+ function setActionLoading(id: string, loading: boolean): void {
234
+ updateAction(id, { loading });
235
+ }
236
+
237
+ return {
238
+ // Page info
239
+ title,
240
+ subtitle,
241
+ description,
242
+
243
+ // Breadcrumbs
244
+ breadcrumbs,
245
+
246
+ // Loading
247
+ loadingHeader,
248
+ loadingContent,
249
+ setLoading,
250
+
251
+ // Context
252
+ user,
253
+ brand,
254
+ isAdmin,
255
+ isSuperAdmin,
256
+
257
+ // Actions
258
+ actions,
259
+ updateAction,
260
+ setActionLoading,
261
+
262
+ // Props
263
+ wrapperProps,
264
+ headerProps,
265
+ };
266
+ }
267
+
268
+ /**
269
+ * Helper: Create a list page configuration
270
+ *
271
+ * @example
272
+ * const page = useListPage({
273
+ * title: 'Users',
274
+ * entityName: 'user',
275
+ * breadcrumbs: [{ name: 'Settings', href: '/settings' }],
276
+ * sidebarActive: 'users',
277
+ * onCreateClick: () => navigateTo('/users/create'),
278
+ * });
279
+ */
280
+ export interface UseListPageOptions extends Omit<UseAdminPageOptions, 'actions'> {
281
+ /** Entity name for create button (e.g., 'user' -> 'Create User') */
282
+ entityName?: string;
283
+ /** Create button click handler */
284
+ onCreateClick?: () => void;
285
+ /** Create button icon */
286
+ createIcon?: Component;
287
+ /** Hide create button */
288
+ hideCreate?: boolean;
289
+ }
290
+
291
+ export function useListPage(options: UseListPageOptions): UseAdminPageReturn {
292
+ const {
293
+ entityName,
294
+ onCreateClick,
295
+ createIcon,
296
+ hideCreate = false,
297
+ ...pageOptions
298
+ } = options;
299
+
300
+ const actions: PageAction[] = [];
301
+
302
+ if (!hideCreate && entityName) {
303
+ actions.push({
304
+ id: 'create',
305
+ text: `Create ${entityName.charAt(0).toUpperCase() + entityName.slice(1)}`,
306
+ icon: createIcon,
307
+ color: 'primary',
308
+ onClick: onCreateClick,
309
+ });
310
+ }
311
+
312
+ return useAdminPage({
313
+ ...pageOptions,
314
+ actions,
315
+ });
316
+ }
317
+
318
+ /**
319
+ * Helper: Create a detail page configuration
320
+ *
321
+ * @example
322
+ * const page = useDetailPage({
323
+ * title: 'User Details',
324
+ * entityName: 'user',
325
+ * breadcrumbs: [
326
+ * { name: 'Settings', href: '/settings' },
327
+ * { name: 'Users', href: '/users' },
328
+ * ],
329
+ * onEditClick: () => navigateTo(`/users/${id}/edit`),
330
+ * onDeleteClick: () => confirmDelete(),
331
+ * });
332
+ */
333
+ export interface UseDetailPageOptions extends Omit<UseAdminPageOptions, 'actions'> {
334
+ /** Entity name for action buttons */
335
+ entityName?: string;
336
+ /** Edit button click handler */
337
+ onEditClick?: () => void;
338
+ /** Delete button click handler */
339
+ onDeleteClick?: () => void;
340
+ /** Edit button icon */
341
+ editIcon?: Component;
342
+ /** Delete button icon */
343
+ deleteIcon?: Component;
344
+ /** Hide edit button */
345
+ hideEdit?: boolean;
346
+ /** Hide delete button */
347
+ hideDelete?: boolean;
348
+ }
349
+
350
+ export function useDetailPage(options: UseDetailPageOptions): UseAdminPageReturn {
351
+ const {
352
+ entityName,
353
+ onEditClick,
354
+ onDeleteClick,
355
+ editIcon,
356
+ deleteIcon,
357
+ hideEdit = false,
358
+ hideDelete = false,
359
+ ...pageOptions
360
+ } = options;
361
+
362
+ const actions: PageAction[] = [];
363
+
364
+ if (!hideEdit && onEditClick) {
365
+ actions.push({
366
+ id: 'edit',
367
+ text: 'Edit',
368
+ icon: editIcon,
369
+ color: 'secondary',
370
+ onClick: onEditClick,
371
+ });
372
+ }
373
+
374
+ if (!hideDelete && onDeleteClick) {
375
+ actions.push({
376
+ id: 'delete',
377
+ text: 'Delete',
378
+ icon: deleteIcon,
379
+ color: 'danger',
380
+ onClick: onDeleteClick,
381
+ });
382
+ }
383
+
384
+ return useAdminPage({
385
+ ...pageOptions,
386
+ actions,
387
+ });
388
+ }
389
+
390
+ /**
391
+ * Helper: Create a form page configuration
392
+ *
393
+ * @example
394
+ * const page = useFormPage({
395
+ * title: 'Create User',
396
+ * breadcrumbs: [
397
+ * { name: 'Settings', href: '/settings' },
398
+ * { name: 'Users', href: '/users' },
399
+ * ],
400
+ * onSaveClick: () => saveUser(),
401
+ * onCancelClick: () => navigateTo('/users'),
402
+ * });
403
+ */
404
+ export interface UseFormPageOptions extends Omit<UseAdminPageOptions, 'actions'> {
405
+ /** Save button click handler */
406
+ onSaveClick?: () => void | Promise<void>;
407
+ /** Cancel button click handler */
408
+ onCancelClick?: () => void;
409
+ /** Save button text */
410
+ saveText?: string;
411
+ /** Cancel button text */
412
+ cancelText?: string;
413
+ /** Save button icon */
414
+ saveIcon?: Component;
415
+ /** Cancel button icon */
416
+ cancelIcon?: Component;
417
+ /** Hide save button */
418
+ hideSave?: boolean;
419
+ /** Hide cancel button */
420
+ hideCancel?: boolean;
421
+ }
422
+
423
+ export function useFormPage(options: UseFormPageOptions): UseAdminPageReturn {
424
+ const {
425
+ onSaveClick,
426
+ onCancelClick,
427
+ saveText = 'Save',
428
+ cancelText = 'Cancel',
429
+ saveIcon,
430
+ cancelIcon,
431
+ hideSave = false,
432
+ hideCancel = false,
433
+ ...pageOptions
434
+ } = options;
435
+
436
+ const actions: PageAction[] = [];
437
+
438
+ if (!hideCancel && onCancelClick) {
439
+ actions.push({
440
+ id: 'cancel',
441
+ text: cancelText,
442
+ icon: cancelIcon,
443
+ color: 'secondary',
444
+ onClick: onCancelClick,
445
+ });
446
+ }
447
+
448
+ if (!hideSave && onSaveClick) {
449
+ actions.push({
450
+ id: 'save',
451
+ text: saveText,
452
+ icon: saveIcon,
453
+ color: 'primary',
454
+ onClick: onSaveClick,
455
+ });
456
+ }
457
+
458
+ return useAdminPage({
459
+ ...pageOptions,
460
+ actions,
461
+ });
462
+ }