@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,358 @@
1
+ /**
2
+ * Filter Definition Helpers
3
+ *
4
+ * Pre-built filter definitions for common patterns, reducing boilerplate
5
+ * when defining DataTable filters.
6
+ */
7
+
8
+ import type { DataTableFilter } from "./DataTable.vue";
9
+
10
+ /**
11
+ * Filter option for select filters
12
+ */
13
+ export interface FilterOption {
14
+ label: string;
15
+ value: string | number | boolean;
16
+ }
17
+
18
+ /**
19
+ * Common status options
20
+ */
21
+ export const statusOptions: FilterOption[] = [
22
+ { label: "Active", value: "active" },
23
+ { label: "Inactive", value: "inactive" },
24
+ { label: "Pending", value: "pending" },
25
+ { label: "Archived", value: "archived" },
26
+ ];
27
+
28
+ /**
29
+ * Boolean options
30
+ */
31
+ export const booleanOptions: FilterOption[] = [
32
+ { label: "Yes", value: "true" },
33
+ { label: "No", value: "false" },
34
+ ];
35
+
36
+ /**
37
+ * Pre-built filter helpers
38
+ */
39
+ export const filters = {
40
+ /**
41
+ * Text search filter
42
+ *
43
+ * @example
44
+ * filters.text('name', 'Name')
45
+ * filters.text('email', 'Email', 'Search by email address...')
46
+ */
47
+ text: (key: string, label: string, placeholder?: string): DataTableFilter => ({
48
+ key,
49
+ label,
50
+ type: "text",
51
+ placeholder: placeholder ?? `Search by ${label.toLowerCase()}...`,
52
+ }),
53
+
54
+ /**
55
+ * Select dropdown filter
56
+ *
57
+ * @example
58
+ * filters.select('role', 'Role', [
59
+ * { label: 'Admin', value: 'admin' },
60
+ * { label: 'User', value: 'user' },
61
+ * ])
62
+ */
63
+ select: (key: string, label: string, options: FilterOption[]): DataTableFilter => ({
64
+ key,
65
+ label,
66
+ type: "select",
67
+ options,
68
+ }),
69
+
70
+ /**
71
+ * Status filter with common status options
72
+ *
73
+ * @example
74
+ * filters.status()
75
+ * filters.status('state', 'State')
76
+ * filters.status('status', 'Status', [
77
+ * { label: 'Active', value: 'active' },
78
+ * { label: 'Draft', value: 'draft' },
79
+ * ])
80
+ */
81
+ status: (key: string = "status", label: string = "Status", options?: FilterOption[]): DataTableFilter => ({
82
+ key,
83
+ label,
84
+ type: "select",
85
+ options: options ?? statusOptions,
86
+ }),
87
+
88
+ /**
89
+ * Boolean (Yes/No) filter
90
+ *
91
+ * @example
92
+ * filters.boolean('verified', 'Verified')
93
+ * filters.boolean('active', 'Active', 'Enabled', 'Disabled')
94
+ */
95
+ boolean: (key: string, label: string, trueLabel: string = "Yes", falseLabel: string = "No"): DataTableFilter => ({
96
+ key,
97
+ label,
98
+ type: "select",
99
+ options: [
100
+ { label: trueLabel, value: "true" },
101
+ { label: falseLabel, value: "false" },
102
+ ],
103
+ }),
104
+
105
+ /**
106
+ * Number filter
107
+ *
108
+ * @example
109
+ * filters.number('age', 'Age')
110
+ * filters.number('amount', 'Amount', 'Enter amount...')
111
+ */
112
+ number: (key: string, label: string, placeholder?: string): DataTableFilter => ({
113
+ key,
114
+ label,
115
+ type: "number",
116
+ placeholder: placeholder ?? `Enter ${label.toLowerCase()}...`,
117
+ }),
118
+
119
+ /**
120
+ * Date filter
121
+ *
122
+ * @example
123
+ * filters.date('createdAt', 'Created Date')
124
+ */
125
+ date: (key: string, label: string): DataTableFilter => ({
126
+ key,
127
+ label,
128
+ type: "date",
129
+ }),
130
+
131
+ /**
132
+ * Date range filter
133
+ *
134
+ * @example
135
+ * filters.dateRange('createdAt', 'Created')
136
+ */
137
+ dateRange: (key: string, label: string): DataTableFilter => ({
138
+ key,
139
+ label,
140
+ type: "dateRange",
141
+ }),
142
+
143
+ /**
144
+ * Role filter with common role options
145
+ *
146
+ * @example
147
+ * filters.role()
148
+ * filters.role('userRole', 'User Role')
149
+ */
150
+ role: (key: string = "role", label: string = "Role", options?: FilterOption[]): DataTableFilter => ({
151
+ key,
152
+ label,
153
+ type: "select",
154
+ options: options ?? [
155
+ { label: "Admin", value: "admin" },
156
+ { label: "Manager", value: "manager" },
157
+ { label: "User", value: "user" },
158
+ { label: "Guest", value: "guest" },
159
+ ],
160
+ }),
161
+
162
+ /**
163
+ * Type filter (generic entity type)
164
+ *
165
+ * @example
166
+ * filters.type('campaignType', 'Type', [
167
+ * { label: 'Email', value: 'email' },
168
+ * { label: 'SMS', value: 'sms' },
169
+ * ])
170
+ */
171
+ type: (key: string, label: string, options: FilterOption[]): DataTableFilter => ({
172
+ key,
173
+ label,
174
+ type: "select",
175
+ options,
176
+ }),
177
+
178
+ /**
179
+ * Priority filter
180
+ *
181
+ * @example
182
+ * filters.priority()
183
+ * filters.priority('urgency', 'Urgency')
184
+ */
185
+ priority: (key: string = "priority", label: string = "Priority"): DataTableFilter => ({
186
+ key,
187
+ label,
188
+ type: "select",
189
+ options: [
190
+ { label: "Low", value: "low" },
191
+ { label: "Medium", value: "medium" },
192
+ { label: "High", value: "high" },
193
+ { label: "Critical", value: "critical" },
194
+ ],
195
+ }),
196
+
197
+ /**
198
+ * Category filter from dynamic options
199
+ *
200
+ * @example
201
+ * // From array of objects
202
+ * filters.fromData('categoryId', 'Category', categories, 'name', 'id')
203
+ */
204
+ fromData: <T extends Record<string, any>>(
205
+ key: string,
206
+ label: string,
207
+ data: T[],
208
+ labelKey: keyof T,
209
+ valueKey: keyof T
210
+ ): DataTableFilter => ({
211
+ key,
212
+ label,
213
+ type: "select",
214
+ options: data.map((item) => ({
215
+ label: String(item[labelKey]),
216
+ value: item[valueKey] as string | number,
217
+ })),
218
+ }),
219
+
220
+ /**
221
+ * Enum filter from TypeScript enum
222
+ *
223
+ * @example
224
+ * enum Status { Active = 'active', Inactive = 'inactive' }
225
+ * filters.fromEnum('status', 'Status', Status)
226
+ */
227
+ fromEnum: (key: string, label: string, enumObj: Record<string, string | number>): DataTableFilter => ({
228
+ key,
229
+ label,
230
+ type: "select",
231
+ options: Object.entries(enumObj)
232
+ .filter(([k]) => Number.isNaN(Number(k))) // Filter out numeric keys from numeric enums
233
+ .map(([enumKey, enumValue]) => ({
234
+ label: enumKey.replace(/([A-Z])/g, " $1").trim(), // Convert camelCase to Title Case
235
+ value: enumValue,
236
+ })),
237
+ }),
238
+ };
239
+
240
+ /**
241
+ * Create a filter group with common filters
242
+ *
243
+ * @example
244
+ * const tableFilters = createFilterGroup({
245
+ * search: ['name', 'email'],
246
+ * status: true,
247
+ * dateRange: 'createdAt',
248
+ * custom: [
249
+ * filters.role('userRole', 'Role'),
250
+ * ],
251
+ * });
252
+ */
253
+ export interface FilterGroupConfig {
254
+ /** Fields to include as text search filters */
255
+ search?: string[];
256
+ /** Include status filter */
257
+ status?: boolean | { key?: string; label?: string; options?: FilterOption[] };
258
+ /** Field for date range filter */
259
+ dateRange?: string;
260
+ /** Additional custom filters */
261
+ custom?: DataTableFilter[];
262
+ }
263
+
264
+ export function createFilterGroup(config: FilterGroupConfig): DataTableFilter[] {
265
+ const result: DataTableFilter[] = [];
266
+
267
+ // Add search filters
268
+ if (config.search) {
269
+ config.search.forEach((field) => {
270
+ result.push(filters.text(field, field.charAt(0).toUpperCase() + field.slice(1)));
271
+ });
272
+ }
273
+
274
+ // Add status filter
275
+ if (config.status) {
276
+ if (typeof config.status === "boolean") {
277
+ result.push(filters.status());
278
+ } else {
279
+ result.push(filters.status(config.status.key, config.status.label, config.status.options));
280
+ }
281
+ }
282
+
283
+ // Add date range filter
284
+ if (config.dateRange) {
285
+ result.push(filters.dateRange(config.dateRange, "Date Range"));
286
+ }
287
+
288
+ // Add custom filters
289
+ if (config.custom) {
290
+ result.push(...config.custom);
291
+ }
292
+
293
+ return result;
294
+ }
295
+
296
+ /**
297
+ * Common filter presets for specific entity types
298
+ */
299
+ export const filterPresets = {
300
+ /**
301
+ * Filters for user/account tables
302
+ */
303
+ users: (): DataTableFilter[] => [
304
+ filters.text("name", "Name"),
305
+ filters.text("email", "Email"),
306
+ filters.role(),
307
+ filters.status(),
308
+ ],
309
+
310
+ /**
311
+ * Filters for campaign tables
312
+ */
313
+ campaigns: (): DataTableFilter[] => [
314
+ filters.text("name", "Campaign Name"),
315
+ filters.status("status", "Status", [
316
+ { label: "Active", value: "active" },
317
+ { label: "Draft", value: "draft" },
318
+ { label: "Completed", value: "completed" },
319
+ { label: "Archived", value: "archived" },
320
+ { label: "Failed", value: "failed" },
321
+ ]),
322
+ filters.dateRange("createdAt", "Created Date"),
323
+ ],
324
+
325
+ /**
326
+ * Filters for contact tables
327
+ */
328
+ contacts: (): DataTableFilter[] => [
329
+ filters.text("name", "Name"),
330
+ filters.text("email", "Email"),
331
+ filters.text("phone", "Phone"),
332
+ filters.status(),
333
+ ],
334
+
335
+ /**
336
+ * Filters for booking/reservation tables
337
+ */
338
+ bookings: (): DataTableFilter[] => [
339
+ filters.text("guestName", "Guest Name"),
340
+ filters.text("confirmationNumber", "Confirmation #"),
341
+ filters.status("status", "Status", [
342
+ { label: "Confirmed", value: "confirmed" },
343
+ { label: "Pending", value: "pending" },
344
+ { label: "Cancelled", value: "cancelled" },
345
+ { label: "Completed", value: "completed" },
346
+ ]),
347
+ filters.dateRange("checkIn", "Check-in Date"),
348
+ ],
349
+
350
+ /**
351
+ * Filters for audit/log tables
352
+ */
353
+ auditLogs: (): DataTableFilter[] => [
354
+ filters.text("action", "Action"),
355
+ filters.text("user", "User"),
356
+ filters.dateRange("timestamp", "Date"),
357
+ ],
358
+ };
package/src/data/index.ts CHANGED
@@ -3,3 +3,34 @@ export { default as DataList } from './DataList.vue';
3
3
  export { default as SearchableSelect } from './SearchableSelect.vue';
4
4
  export { default as Chart } from './Chart.vue';
5
5
  export type { ChartSeries, ChartOptions, ChartDateRange, ChartAnnotation, NpsLiterals } from './Chart.vue';
6
+
7
+ // DataTable - Simplified table with automatic useTable integration
8
+ export { default as DataTable } from './DataTable.vue';
9
+ export type {
10
+ DataTableColumn,
11
+ DataTableFilter,
12
+ BulkAction,
13
+ RowAction,
14
+ NoResultsConfig,
15
+ } from './DataTable.vue';
16
+
17
+ // Column definition helpers
18
+ export {
19
+ columns,
20
+ statusColors,
21
+ getStatusColor,
22
+ createStatusColorFn,
23
+ type DateFormatOptions,
24
+ type TagColorFn,
25
+ } from './columnHelpers';
26
+
27
+ // Filter definition helpers
28
+ export {
29
+ filters,
30
+ filterPresets,
31
+ createFilterGroup,
32
+ statusOptions,
33
+ booleanOptions,
34
+ type FilterOption,
35
+ type FilterGroupConfig,
36
+ } from './filterHelpers';
@@ -0,0 +1,102 @@
1
+ # Domain Module
2
+
3
+ Domain-specific components for the Hotelinking platform.
4
+
5
+ ## Components
6
+
7
+ ### BrandCard
8
+
9
+ Card component for displaying brand information.
10
+
11
+ ```vue
12
+ <script setup>
13
+ import { BrandCard } from '@htlkg/components/domain';
14
+ </script>
15
+
16
+ <template>
17
+ <BrandCard
18
+ :brand="brand"
19
+ :show-status="true"
20
+ @click="handleClick"
21
+ @edit="handleEdit"
22
+ />
23
+ </template>
24
+ ```
25
+
26
+ ### BrandSelector
27
+
28
+ Grid-based brand selection component.
29
+
30
+ ```vue
31
+ <script setup>
32
+ import { ref } from 'vue';
33
+ import { BrandSelector } from '@htlkg/components/domain';
34
+
35
+ const selectedBrand = ref(null);
36
+ </script>
37
+
38
+ <template>
39
+ <BrandSelector
40
+ v-model="selectedBrand"
41
+ :brands="brands"
42
+ :loading="loading"
43
+ searchable
44
+ />
45
+ </template>
46
+ ```
47
+
48
+ ### ProductBadge
49
+
50
+ Badge component for product status indicators.
51
+
52
+ ```vue
53
+ <script setup>
54
+ import { ProductBadge } from '@htlkg/components/domain';
55
+ </script>
56
+
57
+ <template>
58
+ <ProductBadge
59
+ :product="product"
60
+ :enabled="isEnabled"
61
+ size="sm"
62
+ />
63
+ </template>
64
+ ```
65
+
66
+ ### UserAvatar
67
+
68
+ User avatar with profile pictures or initials.
69
+
70
+ ```vue
71
+ <script setup>
72
+ import { UserAvatar } from '@htlkg/components/domain';
73
+ </script>
74
+
75
+ <template>
76
+ <UserAvatar
77
+ :user="user"
78
+ size="md"
79
+ :show-name="true"
80
+ :show-role="true"
81
+ />
82
+ </template>
83
+ ```
84
+
85
+ ## Props
86
+
87
+ ### BrandCard
88
+
89
+ | Prop | Type | Description |
90
+ |------|------|-------------|
91
+ | brand | Brand | Brand object |
92
+ | showStatus | boolean | Show status badge |
93
+ | clickable | boolean | Enable click handler |
94
+
95
+ ### BrandSelector
96
+
97
+ | Prop | Type | Description |
98
+ |------|------|-------------|
99
+ | brands | Brand[] | Available brands |
100
+ | modelValue | Brand | Selected brand (v-model) |
101
+ | searchable | boolean | Enable search |
102
+ | loading | boolean | Loading state |
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { ref, computed } from 'vue';
3
- import Ajv from 'ajv';
3
+ import _Ajv from 'ajv';
4
4
  import addFormats from 'ajv-formats';
5
5
  import {
6
6
  uiInput,
@@ -11,6 +11,9 @@ import {
11
11
  uiButton
12
12
  } from '@hotelinking/ui';
13
13
 
14
+ // Handle ESM/CJS interop for ajv
15
+ const Ajv = (_Ajv as unknown as { default: typeof _Ajv }).default ?? _Ajv;
16
+
14
17
  // Initialize AJV for JSON Schema validation with format support
15
18
  const ajv = new Ajv({ allErrors: true });
16
19
  addFormats(ajv); // Add support for format keywords like "email", "uri", "date", etc.
@@ -0,0 +1,89 @@
1
+ # Forms Module
2
+
3
+ Components for form creation and validation.
4
+
5
+ ## Components
6
+
7
+ ### JsonSchemaForm
8
+
9
+ Dynamic form generator from JSON Schema with AJV validation.
10
+
11
+ ```vue
12
+ <script setup>
13
+ import { ref } from 'vue';
14
+ import { JsonSchemaForm } from '@htlkg/components/forms';
15
+
16
+ const formData = ref({});
17
+ const schema = {
18
+ type: 'object',
19
+ properties: {
20
+ name: { type: 'string', title: 'Name' },
21
+ email: { type: 'string', format: 'email', title: 'Email' },
22
+ age: { type: 'number', minimum: 0, title: 'Age' },
23
+ },
24
+ required: ['name', 'email'],
25
+ };
26
+
27
+ const uiSchema = {
28
+ name: { 'ui:placeholder': 'Enter name' },
29
+ email: { 'ui:widget': 'email' },
30
+ };
31
+ </script>
32
+
33
+ <template>
34
+ <JsonSchemaForm
35
+ v-model="formData"
36
+ :schema="schema"
37
+ :ui-schema="uiSchema"
38
+ @submit="handleSubmit"
39
+ @validation-error="handleError"
40
+ />
41
+ </template>
42
+ ```
43
+
44
+ ### DateRange
45
+
46
+ Date range selector with start/end dates.
47
+
48
+ ```vue
49
+ <script setup>
50
+ import { ref } from 'vue';
51
+ import { DateRange } from '@htlkg/components/forms';
52
+
53
+ const dateRange = ref({
54
+ start: null,
55
+ end: null,
56
+ });
57
+ </script>
58
+
59
+ <template>
60
+ <DateRange
61
+ v-model="dateRange"
62
+ :min-date="minDate"
63
+ :max-date="maxDate"
64
+ @search="handleSearch"
65
+ />
66
+ </template>
67
+ ```
68
+
69
+ ## Validation
70
+
71
+ JsonSchemaForm uses AJV for validation:
72
+
73
+ ```typescript
74
+ const schema = {
75
+ type: 'object',
76
+ properties: {
77
+ password: {
78
+ type: 'string',
79
+ minLength: 8,
80
+ pattern: '^(?=.*[A-Z])(?=.*[0-9])',
81
+ },
82
+ },
83
+ errorMessage: {
84
+ properties: {
85
+ password: 'Password must be 8+ chars with uppercase and number',
86
+ },
87
+ },
88
+ };
89
+ ```
package/src/index.ts CHANGED
@@ -13,8 +13,9 @@ export * from './overlays';
13
13
  // Domain components
14
14
  export * from './domain';
15
15
 
16
- // Composables
16
+ // Composables (includes page context with setUser, $user, etc.)
17
17
  export * from './composables';
18
18
 
19
- // Stores
20
- export * from './stores';
19
+ // Note: Stores are not re-exported here to avoid naming conflicts
20
+ // Use composables/usePageContext for page-level user state
21
+ // Import from '@htlkg/components/stores' for other store utilities
@@ -0,0 +1,80 @@
1
+ # Navigation Module
2
+
3
+ Components for navigation and page structure.
4
+
5
+ ## Components
6
+
7
+ ### Breadcrumbs
8
+
9
+ Hierarchical navigation showing current page location.
10
+
11
+ ```vue
12
+ <script setup>
13
+ import { Breadcrumbs } from '@htlkg/components/navigation';
14
+
15
+ const items = [
16
+ { label: 'Home', href: '/' },
17
+ { label: 'Brands', href: '/brands' },
18
+ { label: 'Brand Name' },
19
+ ];
20
+ </script>
21
+
22
+ <template>
23
+ <Breadcrumbs :items="items" />
24
+ </template>
25
+ ```
26
+
27
+ ### Stepper
28
+
29
+ Multi-step progress indicator for wizards and forms.
30
+
31
+ ```vue
32
+ <script setup>
33
+ import { ref } from 'vue';
34
+ import { Stepper } from '@htlkg/components/navigation';
35
+
36
+ const currentStep = ref(0);
37
+ const steps = [
38
+ { label: 'Account', valid: true },
39
+ { label: 'Profile', valid: false },
40
+ { label: 'Review', valid: false },
41
+ ];
42
+ </script>
43
+
44
+ <template>
45
+ <Stepper
46
+ v-model="currentStep"
47
+ :steps="steps"
48
+ @step-change="handleStepChange"
49
+ />
50
+ </template>
51
+ ```
52
+
53
+ ### Tabs
54
+
55
+ Tabbed interface for organizing content.
56
+
57
+ ```vue
58
+ <script setup>
59
+ import { ref } from 'vue';
60
+ import { Tabs } from '@htlkg/components/navigation';
61
+
62
+ const activeTab = ref('overview');
63
+ const tabs = [
64
+ { id: 'overview', label: 'Overview' },
65
+ { id: 'settings', label: 'Settings', count: 3 },
66
+ { id: 'users', label: 'Users', disabled: true },
67
+ ];
68
+ </script>
69
+
70
+ <template>
71
+ <Tabs v-model="activeTab" :tabs="tabs">
72
+ <template #overview>
73
+ <OverviewContent />
74
+ </template>
75
+ <template #settings>
76
+ <SettingsContent />
77
+ </template>
78
+ </Tabs>
79
+ </template>
80
+ ```