@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.
- package/README.md +52 -0
- package/dist/AdminWrapper.vue_vue_type_script_setup_true_lang-B32IylcT.js +367 -0
- package/dist/AdminWrapper.vue_vue_type_script_setup_true_lang-B32IylcT.js.map +1 -0
- package/dist/Alert.vue_vue_type_script_setup_true_lang-DxPCS-Hx.js +263 -0
- package/dist/Alert.vue_vue_type_script_setup_true_lang-DxPCS-Hx.js.map +1 -0
- package/dist/DateRange.vue_vue_type_script_setup_true_lang-BLVg1Hah.js +580 -0
- package/dist/DateRange.vue_vue_type_script_setup_true_lang-BLVg1Hah.js.map +1 -0
- package/dist/ProductBadge.vue_vue_type_script_setup_true_lang-Cmr2f4Cy.js +187 -0
- package/dist/ProductBadge.vue_vue_type_script_setup_true_lang-Cmr2f4Cy.js.map +1 -0
- package/dist/_plugin-vue_export-helper-1tPrXgE0.js +11 -0
- package/dist/_plugin-vue_export-helper-1tPrXgE0.js.map +1 -0
- package/dist/components.css +15 -0
- package/dist/composables/index.js +32 -573
- package/dist/composables/index.js.map +1 -1
- package/dist/data/index.js +18 -0
- package/dist/data/index.js.map +1 -0
- package/dist/domain/index.js +8 -0
- package/dist/domain/index.js.map +1 -0
- package/dist/filterHelpers-DgRyoYSa.js +1386 -0
- package/dist/filterHelpers-DgRyoYSa.js.map +1 -0
- package/dist/forms/index.js +6 -0
- package/dist/forms/index.js.map +1 -0
- package/dist/index-DGO_pNgG.js +79 -0
- package/dist/index-DGO_pNgG.js.map +1 -0
- package/dist/index-QK97OdqQ.js +25 -0
- package/dist/index-QK97OdqQ.js.map +1 -0
- package/dist/index.js +67 -0
- package/dist/index.js.map +1 -0
- package/dist/navigation/index.js +8 -0
- package/dist/navigation/index.js.map +1 -0
- package/dist/overlays/index.js +8 -0
- package/dist/overlays/index.js.map +1 -0
- package/dist/stores/index.js +14 -0
- package/dist/stores/index.js.map +1 -0
- package/dist/useAdminPage-GhgXp0x8.js +1070 -0
- package/dist/useAdminPage-GhgXp0x8.js.map +1 -0
- package/dist/useTable-DutR1gkg.js +293 -0
- package/dist/useTable-DutR1gkg.js.map +1 -0
- package/package.json +43 -14
- package/src/composables/composables.md +109 -0
- package/src/composables/index.ts +69 -0
- package/src/composables/useAdminPage.ts +462 -0
- package/src/composables/useConfirmation.ts +358 -0
- package/src/composables/usePageContext.ts +171 -0
- package/src/composables/useStats.ts +361 -0
- package/src/composables/useTable.ts +26 -5
- package/src/composables/useWizard.ts +448 -0
- package/src/data/DataTable.vue +553 -0
- package/src/data/Table/Table.vue +295 -0
- package/src/data/columnHelpers.ts +503 -0
- package/src/data/data.md +106 -0
- package/src/data/filterHelpers.ts +358 -0
- package/src/data/index.ts +31 -0
- package/src/domain/domain.md +102 -0
- package/src/forms/JsonSchemaForm.vue +4 -1
- package/src/forms/forms.md +89 -0
- package/src/index.ts +4 -3
- package/src/navigation/navigation.md +80 -0
- package/src/overlays/overlays.md +86 -0
- 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
|
+
```
|
package/src/composables/index.ts
CHANGED
|
@@ -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
|
+
}
|