@makolabs/ripple 1.2.3 → 1.2.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 (106) hide show
  1. package/README.md +77 -0
  2. package/dist/adapters/ai/OpenAIAdapter.js +16 -11
  3. package/dist/adapters/ai/types.d.ts +3 -3
  4. package/dist/adapters/storage/BaseAdapter.d.ts +1 -1
  5. package/dist/adapters/storage/BaseAdapter.js +1 -1
  6. package/dist/adapters/storage/S3Adapter.js +2 -2
  7. package/dist/ai/AIChatInterface.svelte +32 -34
  8. package/dist/ai/AIChatInterface.svelte.d.ts +0 -1
  9. package/dist/ai/AIChatInterfaceTestWrapper.svelte +26 -0
  10. package/dist/ai/AIChatInterfaceTestWrapper.svelte.d.ts +17 -0
  11. package/dist/ai/ChatInput.svelte +7 -15
  12. package/dist/ai/ChatInput.svelte.d.ts +0 -2
  13. package/dist/ai/CodeRenderer.svelte +25 -12
  14. package/dist/ai/ComposeDropdown.svelte +17 -14
  15. package/dist/ai/MermaidRenderer.svelte +21 -17
  16. package/dist/ai/MermaidRenderer.svelte.d.ts +0 -1
  17. package/dist/ai/MessageBox.svelte +10 -7
  18. package/dist/ai/ThinkingDisplay.svelte +67 -43
  19. package/dist/ai/ai-chat-interface.d.ts +22 -21
  20. package/dist/ai/ai-chat-interface.js +8 -7
  21. package/dist/ai/content-detector.js +2 -2
  22. package/dist/button/ButtonTestWrapper.svelte +10 -0
  23. package/dist/button/ButtonTestWrapper.svelte.d.ts +7 -0
  24. package/dist/charts/Chart.svelte +6 -1
  25. package/dist/config/ai.js +1 -0
  26. package/dist/drawer/DrawerTestWrapper.svelte +19 -0
  27. package/dist/drawer/DrawerTestWrapper.svelte.d.ts +9 -0
  28. package/dist/drawer/drawer.d.ts +19 -18
  29. package/dist/drawer/drawer.js +7 -6
  30. package/dist/elements/accordion/Accordion.svelte +1 -1
  31. package/dist/elements/accordion/Accordion.svelte.d.ts +1 -1
  32. package/dist/elements/accordion/AccordionTestWrapper.svelte +21 -0
  33. package/dist/elements/accordion/AccordionTestWrapper.svelte.d.ts +10 -0
  34. package/dist/elements/badge/Badge.svelte +5 -4
  35. package/dist/elements/badge/BadgeTestWrapper.svelte +14 -0
  36. package/dist/elements/badge/BadgeTestWrapper.svelte.d.ts +9 -0
  37. package/dist/elements/badge/badge.d.ts +40 -39
  38. package/dist/elements/badge/badge.js +14 -13
  39. package/dist/elements/dropdown/Dropdown.svelte +0 -1
  40. package/dist/elements/pagination/Pagination.svelte +20 -26
  41. package/dist/elements/progress/Progress.svelte +3 -3
  42. package/dist/elements/timeline/Timeline.svelte +1 -1
  43. package/dist/file-browser/FileBrowser.svelte +7 -10
  44. package/dist/filters/CompactFilters.svelte +3 -3
  45. package/dist/forms/Checkbox.svelte +0 -1
  46. package/dist/forms/CheckboxTestWrapper.svelte +8 -0
  47. package/dist/forms/CheckboxTestWrapper.svelte.d.ts +4 -0
  48. package/dist/forms/DateRange.svelte +186 -198
  49. package/dist/forms/Form.svelte +1 -0
  50. package/dist/forms/Input.svelte +14 -5
  51. package/dist/forms/InputTestWrapper.svelte +8 -0
  52. package/dist/forms/InputTestWrapper.svelte.d.ts +4 -0
  53. package/dist/forms/NumberInput.svelte +2 -2
  54. package/dist/forms/RadioInputs.svelte +1 -1
  55. package/dist/forms/RadioPill.svelte +1 -1
  56. package/dist/forms/Slider.svelte +2 -2
  57. package/dist/forms/Tags.svelte +3 -3
  58. package/dist/forms/ToggleTestWrapper.svelte +8 -0
  59. package/dist/forms/ToggleTestWrapper.svelte.d.ts +7 -0
  60. package/dist/forms/slider.js +1 -1
  61. package/dist/header/PageHeader.svelte +2 -1
  62. package/dist/header/breadcrumbs.d.ts +47 -33
  63. package/dist/header/breadcrumbs.js +12 -11
  64. package/dist/index.d.ts +3 -2
  65. package/dist/index.js +2 -0
  66. package/dist/layout/activity-list/ActivityList.svelte +9 -11
  67. package/dist/layout/card/CardTestWrapper.svelte +15 -0
  68. package/dist/layout/card/CardTestWrapper.svelte.d.ts +7 -0
  69. package/dist/layout/card/RankedCard.svelte +2 -3
  70. package/dist/layout/navbar/navbar.d.ts +19 -18
  71. package/dist/layout/navbar/navbar.js +7 -6
  72. package/dist/layout/sidebar/NavGroup.svelte +1 -0
  73. package/dist/layout/table/Cells.svelte +5 -5
  74. package/dist/layout/table/Table.svelte +8 -8
  75. package/dist/layout/table/table.d.ts +28 -24
  76. package/dist/layout/table/table.js +14 -13
  77. package/dist/modal/Modal.svelte +1 -1
  78. package/dist/modal/ModalTestWrapper.svelte +20 -0
  79. package/dist/modal/ModalTestWrapper.svelte.d.ts +8 -0
  80. package/dist/modal/modal.d.ts +1 -20
  81. package/dist/pipeline/Pipeline.svelte +29 -17
  82. package/dist/user-management/README.md +417 -0
  83. package/dist/user-management/UserManagement.svelte +184 -0
  84. package/dist/user-management/UserManagement.svelte.d.ts +4 -0
  85. package/dist/user-management/UserManagementTestWrapper.svelte +47 -0
  86. package/dist/user-management/UserManagementTestWrapper.svelte.d.ts +7 -0
  87. package/dist/user-management/UserModal.svelte +303 -0
  88. package/dist/user-management/UserModal.svelte.d.ts +4 -0
  89. package/dist/user-management/UserModalTestWrapper.svelte +22 -0
  90. package/dist/user-management/UserModalTestWrapper.svelte.d.ts +7 -0
  91. package/dist/user-management/UserTable.svelte +219 -0
  92. package/dist/user-management/UserTable.svelte.d.ts +4 -0
  93. package/dist/user-management/UserTableTestWrapper.svelte +41 -0
  94. package/dist/user-management/UserTableTestWrapper.svelte.d.ts +7 -0
  95. package/dist/user-management/UserViewModal.svelte +282 -0
  96. package/dist/user-management/UserViewModal.svelte.d.ts +4 -0
  97. package/dist/user-management/UserViewModalTestWrapper.svelte +22 -0
  98. package/dist/user-management/UserViewModalTestWrapper.svelte.d.ts +7 -0
  99. package/dist/user-management/index.d.ts +10 -0
  100. package/dist/user-management/index.js +11 -0
  101. package/dist/user-management/user-management.d.ts +99 -0
  102. package/dist/user-management/user-management.js +42 -0
  103. package/package.json +3 -1
  104. package/dist/types/markdown.d.ts +0 -14
  105. package/dist/types/variants.d.ts +0 -1
  106. package/dist/types/variants.js +0 -1
@@ -0,0 +1,417 @@
1
+ # User Management Module
2
+
3
+ A comprehensive, self-contained user management system built with Svelte 5 and TypeScript. This module provides all the components needed to build a complete user management interface with CRUD operations, role management, and permissions.
4
+
5
+ ## Features
6
+
7
+ - 📊 **User Table**: Fully-featured data table with sorting, pagination, and search
8
+ - ✏️ **User Modal**: Create and edit users with role and permission management
9
+ - 👁️ **User View Modal**: View detailed user information in a read-only modal
10
+ - 🎯 **User Management**: Complete orchestrator component that combines all features
11
+ - 🔐 **Role-Based Access**: Built-in support for roles and permissions
12
+ - 🎨 **Customizable**: All components accept custom classes and styling
13
+ - ♿ **Accessible**: Built with accessibility in mind
14
+ - 📱 **Responsive**: Works seamlessly on all screen sizes
15
+ - 🧪 **Well-Tested**: Comprehensive test suite included
16
+ - 📖 **Storybook Ready**: Full Storybook stories for all components
17
+
18
+ ## Installation
19
+
20
+ The user management module is included in `@makolabs/ripple`. Simply import the components you need:
21
+
22
+ ```typescript
23
+ import { UserManagement, UserTable, UserModal, UserViewModal } from '@makolabs/ripple';
24
+ ```
25
+
26
+ ## Components
27
+
28
+ ### UserManagement
29
+
30
+ The main orchestrator component that combines all user management functionality.
31
+
32
+ ```svelte
33
+ <script lang="ts">
34
+ import { UserManagement } from '@makolabs/ripple';
35
+ import type { User } from '@makolabs/ripple';
36
+
37
+ let users = $state<User[]>([
38
+ /* your users */
39
+ ]);
40
+ let totalUsers = $state(100);
41
+ let currentPage = $state(1);
42
+ let pageSize = $state(10);
43
+
44
+ const roles = [
45
+ {
46
+ value: 'admin',
47
+ label: 'Administrator',
48
+ description: 'Full access',
49
+ permissions: ['read', 'write', 'delete']
50
+ },
51
+ {
52
+ value: 'user',
53
+ label: 'User',
54
+ description: 'Basic access',
55
+ permissions: ['read']
56
+ }
57
+ ];
58
+
59
+ async function handleCreateUser(userData: Partial<User>) {
60
+ // Your create logic
61
+ console.log('Creating user:', userData);
62
+ }
63
+
64
+ async function handleUpdateUser(userId: string, userData: Partial<User>) {
65
+ // Your update logic
66
+ console.log('Updating user:', userId, userData);
67
+ }
68
+
69
+ async function handleDeleteUser(userId: string) {
70
+ // Your delete logic
71
+ console.log('Deleting user:', userId);
72
+ }
73
+ </script>
74
+
75
+ <UserManagement
76
+ {users}
77
+ {totalUsers}
78
+ {currentPage}
79
+ {pageSize}
80
+ {roles}
81
+ onPageChange={(page) => (currentPage = page)}
82
+ onPageSizeChange={(size) => (pageSize = size)}
83
+ onCreateUser={handleCreateUser}
84
+ onUpdateUser={handleUpdateUser}
85
+ onDeleteUser={handleDeleteUser}
86
+ />
87
+ ```
88
+
89
+ ### UserTable
90
+
91
+ Display users in a sortable, paginated table.
92
+
93
+ ```svelte
94
+ <script lang="ts">
95
+ import { UserTable } from '@makolabs/ripple';
96
+ import type { User } from '@makolabs/ripple';
97
+
98
+ let users = $state<User[]>([
99
+ /* your users */
100
+ ]);
101
+
102
+ function handleView(user: User) {
103
+ console.log('View user:', user);
104
+ }
105
+
106
+ function handleEdit(user: User) {
107
+ console.log('Edit user:', user);
108
+ }
109
+
110
+ function handleDelete(userId: string) {
111
+ console.log('Delete user:', userId);
112
+ }
113
+ </script>
114
+
115
+ <UserTable
116
+ {users}
117
+ totalUsers={100}
118
+ currentPage={1}
119
+ pageSize={10}
120
+ onPageChange={(page) => console.log('Page:', page)}
121
+ onPageSizeChange={(size) => console.log('Size:', size)}
122
+ onView={handleView}
123
+ onEdit={handleEdit}
124
+ onDelete={handleDelete}
125
+ />
126
+ ```
127
+
128
+ ### UserModal
129
+
130
+ Create or edit users with role and permission selection.
131
+
132
+ ```svelte
133
+ <script lang="ts">
134
+ import { UserModal } from '@makolabs/ripple';
135
+ import type { User, Role } from '@makolabs/ripple';
136
+
137
+ let open = $state(false);
138
+ let selectedUser = $state<User | null>(null);
139
+
140
+ const roles: Role[] = [
141
+ {
142
+ value: 'admin',
143
+ label: 'Administrator',
144
+ description: 'Full access',
145
+ permissions: ['read', 'write', 'delete']
146
+ }
147
+ ];
148
+
149
+ async function handleSave(user: User, mode: 'create' | 'edit') {
150
+ console.log('Save user:', user, 'Mode:', mode);
151
+ // Your save logic here
152
+ open = false;
153
+ }
154
+ </script>
155
+
156
+ <button onclick={() => (open = true)}>Open Modal</button>
157
+
158
+ <UserModal
159
+ bind:open
160
+ bind:user={selectedUser}
161
+ {roles}
162
+ onSave={handleSave}
163
+ onClose={() => (open = false)}
164
+ />
165
+ ```
166
+
167
+ ### UserViewModal
168
+
169
+ View user details in a read-only modal with tabs for profile and permissions.
170
+
171
+ ```svelte
172
+ <script lang="ts">
173
+ import { UserViewModal } from '@makolabs/ripple';
174
+ import type { User } from '@makolabs/ripple';
175
+
176
+ let open = $state(false);
177
+ let selectedUser = $state<User | null>(null);
178
+
179
+ function handleEdit(user: User) {
180
+ console.log('Edit user:', user);
181
+ open = false;
182
+ }
183
+ </script>
184
+
185
+ <UserViewModal
186
+ bind:open
187
+ bind:user={selectedUser}
188
+ onEdit={handleEdit}
189
+ onClose={() => (open = false)}
190
+ />
191
+ ```
192
+
193
+ ## TypeScript Types
194
+
195
+ ### User
196
+
197
+ ```typescript
198
+ interface User {
199
+ id: string;
200
+ first_name?: string;
201
+ last_name?: string;
202
+ username?: string;
203
+ email_addresses?: UserEmail[];
204
+ phone_numbers?: UserPhone[];
205
+ image_url?: string;
206
+ created_at?: number;
207
+ last_sign_in_at?: number;
208
+ private_metadata?: Record<string, any>;
209
+ public_metadata?: Record<string, any>;
210
+ permissions?: string[];
211
+ role?: string;
212
+ }
213
+ ```
214
+
215
+ ### Role
216
+
217
+ ```typescript
218
+ interface Role {
219
+ value: string;
220
+ label: string;
221
+ description?: string;
222
+ permissions: string[];
223
+ }
224
+ ```
225
+
226
+ ### Permission
227
+
228
+ ```typescript
229
+ interface Permission {
230
+ id: string;
231
+ name: string;
232
+ description?: string;
233
+ category?: string;
234
+ }
235
+ ```
236
+
237
+ ## Utility Functions
238
+
239
+ ### createUser
240
+
241
+ Create a user object with default values:
242
+
243
+ ```typescript
244
+ import { createUser } from '@makolabs/ripple';
245
+
246
+ const newUser = createUser({
247
+ first_name: 'John',
248
+ last_name: 'Doe',
249
+ email_addresses: [{ email_address: 'john@example.com' }]
250
+ });
251
+ ```
252
+
253
+ ### getUserDisplayName
254
+
255
+ Get a user's display name:
256
+
257
+ ```typescript
258
+ import { getUserDisplayName } from '@makolabs/ripple';
259
+
260
+ const displayName = getUserDisplayName(user);
261
+ // Returns: "John Doe" or username or email or ID
262
+ ```
263
+
264
+ ### getUserInitials
265
+
266
+ Get a user's initials for avatars:
267
+
268
+ ```typescript
269
+ import { getUserInitials } from '@makolabs/ripple';
270
+
271
+ const initials = getUserInitials(user);
272
+ // Returns: "J" or "?" if no name
273
+ ```
274
+
275
+ ## Styling
276
+
277
+ All components accept a `class` prop for custom styling:
278
+
279
+ ```svelte
280
+ <UserManagement
281
+ class="my-custom-class"
282
+ {users}
283
+ {totalUsers}
284
+ {currentPage}
285
+ {pageSize}
286
+ onPageChange={handlePageChange}
287
+ onPageSizeChange={handlePageSizeChange}
288
+ />
289
+ ```
290
+
291
+ Components use Tailwind CSS classes by default, but you can override them with your own styles.
292
+
293
+ ## Data Flow
294
+
295
+ The user management module is designed to be **data-driven** and **framework-agnostic**. It doesn't make any API calls directly. Instead, you provide the data and handle the API calls:
296
+
297
+ ```svelte
298
+ <script lang="ts">
299
+ import { UserManagement } from '@makolabs/ripple';
300
+ import type { User } from '@makolabs/ripple';
301
+
302
+ // Your data source
303
+ let users = $state<User[]>([]);
304
+ let totalUsers = $state(0);
305
+ let currentPage = $state(1);
306
+ let pageSize = $state(10);
307
+ let loading = $state(false);
308
+
309
+ // Fetch users when page or size changes
310
+ $effect(() => {
311
+ loadUsers();
312
+ });
313
+
314
+ async function loadUsers() {
315
+ loading = true;
316
+ try {
317
+ const response = await fetch(`/api/users?page=${currentPage}&size=${pageSize}`);
318
+ const data = await response.json();
319
+ users = data.users;
320
+ totalUsers = data.total;
321
+ } catch (error) {
322
+ console.error('Failed to load users:', error);
323
+ } finally {
324
+ loading = false;
325
+ }
326
+ }
327
+
328
+ async function handleCreateUser(userData: Partial<User>) {
329
+ const response = await fetch('/api/users', {
330
+ method: 'POST',
331
+ headers: { 'Content-Type': 'application/json' },
332
+ body: JSON.stringify(userData)
333
+ });
334
+ if (response.ok) {
335
+ await loadUsers(); // Reload the list
336
+ }
337
+ }
338
+
339
+ // Similar handlers for update, delete, etc.
340
+ </script>
341
+
342
+ <UserManagement
343
+ {users}
344
+ {totalUsers}
345
+ {loading}
346
+ {currentPage}
347
+ {pageSize}
348
+ onPageChange={(page) => (currentPage = page)}
349
+ onPageSizeChange={(size) => {
350
+ pageSize = size;
351
+ currentPage = 1;
352
+ }}
353
+ onCreateUser={handleCreateUser}
354
+ onUpdateUser={handleUpdateUser}
355
+ onDeleteUser={handleDeleteUser}
356
+ />
357
+ ```
358
+
359
+ ## Examples
360
+
361
+ Check out the Storybook stories for interactive examples:
362
+
363
+ ```bash
364
+ npm run storybook
365
+ ```
366
+
367
+ Then navigate to:
368
+
369
+ - **User Management > User Management** - Full user management interface
370
+ - **User Management > User Table** - Table component examples
371
+ - **User Management > User Modal** - Modal component examples (if available)
372
+ - **User Management > User View Modal** - View modal examples (if available)
373
+
374
+ ## Testing
375
+
376
+ Run the test suite:
377
+
378
+ ```bash
379
+ npm run test:unit
380
+ ```
381
+
382
+ Tests are located in:
383
+
384
+ - `UserTable.svelte.test.ts`
385
+ - `UserModal.svelte.test.ts`
386
+ - (Additional test files as needed)
387
+
388
+ ## Browser Support
389
+
390
+ The user management module supports all modern browsers:
391
+
392
+ - Chrome/Edge (latest)
393
+ - Firefox (latest)
394
+ - Safari (latest)
395
+ - Mobile browsers (iOS Safari, Chrome Mobile)
396
+
397
+ ## Accessibility
398
+
399
+ All components are built with accessibility in mind:
400
+
401
+ - ✅ Keyboard navigation
402
+ - ✅ ARIA labels and roles
403
+ - ✅ Screen reader support
404
+ - ✅ Focus management
405
+ - ✅ Semantic HTML
406
+
407
+ ## License
408
+
409
+ Part of `@makolabs/ripple` - see main package for license information.
410
+
411
+ ## Contributing
412
+
413
+ Contributions are welcome! Please see the main repository for contribution guidelines.
414
+
415
+ ## Support
416
+
417
+ For issues, questions, or feature requests, please open an issue on the main repository.
@@ -0,0 +1,184 @@
1
+ <script lang="ts">
2
+ import { PageHeader, MetricCard, Button, cn } from '../index.js';
3
+ import UserTable from './UserTable.svelte';
4
+ import UserModal from './UserModal.svelte';
5
+ import UserViewModal from './UserViewModal.svelte';
6
+ import type { User, UserManagementProps } from './user-management.js';
7
+ import { SvelteSet } from 'svelte/reactivity';
8
+
9
+ let {
10
+ users = [],
11
+ totalUsers = 0,
12
+ loading = false,
13
+ currentPage = 1,
14
+ pageSize = 10,
15
+ roles = [],
16
+ permissions = [],
17
+ onPageChange,
18
+ onPageSizeChange,
19
+ onSort,
20
+ onCreateUser,
21
+ onUpdateUser,
22
+ onDeleteUser,
23
+ onDeleteUsers,
24
+ class: className
25
+ }: UserManagementProps = $props();
26
+
27
+ // Modal states
28
+ let showEditCreateModal = $state(false);
29
+ let showViewModal = $state(false);
30
+ let selectedUser = $state<User | null>(null);
31
+
32
+ // Bulk action states
33
+ let selectedUsers = new SvelteSet<string>();
34
+ let bulkAction = $state<'delete' | ''>('');
35
+
36
+ // Derived states
37
+ const hasSelectedUsers = $derived(selectedUsers.size > 0);
38
+ const activeUsers = $derived(users.filter((u) => !!u.id));
39
+
40
+ // Modal handlers
41
+ function openViewModal(user: User) {
42
+ selectedUser = user;
43
+ showViewModal = true;
44
+ }
45
+
46
+ function openEditModal(user: User) {
47
+ selectedUser = user;
48
+ showEditCreateModal = true;
49
+ }
50
+
51
+ function openCreateModal() {
52
+ selectedUser = null;
53
+ showEditCreateModal = true;
54
+ }
55
+
56
+ function handleEditFromView() {
57
+ showViewModal = false;
58
+ if (selectedUser) {
59
+ openEditModal(selectedUser);
60
+ }
61
+ }
62
+
63
+ // Save handlers
64
+ async function handleUserSave(user: User, mode: 'create' | 'edit') {
65
+ if (mode === 'create' && onCreateUser) {
66
+ await onCreateUser(user);
67
+ } else if (mode === 'edit' && onUpdateUser) {
68
+ await onUpdateUser(user.id, user);
69
+ }
70
+ }
71
+
72
+ // Delete handlers
73
+ async function handleDeleteUser(userId: string) {
74
+ if (!confirm('Are you sure you want to delete this user? This action cannot be undone.')) {
75
+ return;
76
+ }
77
+ if (onDeleteUser) {
78
+ await onDeleteUser(userId);
79
+ }
80
+ }
81
+
82
+ // Bulk actions
83
+ async function executeBulkAction() {
84
+ if (!bulkAction || selectedUsers.size === 0) return;
85
+
86
+ const userIds = Array.from(selectedUsers);
87
+
88
+ if (!confirm(`Are you sure you want to ${bulkAction} ${userIds.length} user(s)?`)) {
89
+ return;
90
+ }
91
+
92
+ if (bulkAction === 'delete' && onDeleteUsers) {
93
+ await onDeleteUsers(userIds);
94
+ selectedUsers.clear();
95
+ selectedUsers = new SvelteSet(selectedUsers);
96
+ bulkAction = '';
97
+ }
98
+ }
99
+ </script>
100
+
101
+ {#snippet PlusIcon()}
102
+ <svg class="mr-2 h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
103
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path>
104
+ </svg>
105
+ {/snippet}
106
+
107
+ <div class={cn(className)}>
108
+ <!-- Page Header -->
109
+ <PageHeader
110
+ title="User Management"
111
+ subtitle="Manage and monitor user accounts"
112
+ layout="horizontal"
113
+ class="mb-6"
114
+ >
115
+ <Button onclick={openCreateModal} color="primary" disabled={!onCreateUser}>
116
+ {@render PlusIcon()}
117
+ Add User
118
+ </Button>
119
+ </PageHeader>
120
+
121
+ <!-- Stats Cards -->
122
+ <div class="mb-8 grid grid-cols-1 gap-6 md:grid-cols-2">
123
+ <MetricCard title="Total Users" value={totalUsers.toLocaleString()} />
124
+ <MetricCard title="Active Users" value={activeUsers.length.toLocaleString()} />
125
+ </div>
126
+
127
+ <!-- Bulk Actions Bar -->
128
+ {#if hasSelectedUsers}
129
+ <div
130
+ class="mb-4 flex flex-wrap items-center justify-between gap-3 rounded-lg border border-amber-200 bg-amber-50 p-4"
131
+ >
132
+ <div class="flex items-center">
133
+ <span class="text-sm font-medium text-amber-900">
134
+ {selectedUsers.size} user{selectedUsers.size === 1 ? '' : 's'} selected
135
+ </span>
136
+ </div>
137
+ <div class="flex items-center gap-2">
138
+ <select
139
+ bind:value={bulkAction}
140
+ class="rounded-md border-amber-300 text-sm focus:border-amber-500 focus:ring-amber-500"
141
+ >
142
+ <option value="">Select Action</option>
143
+ <option value="delete">Delete Selected</option>
144
+ </select>
145
+ <Button onclick={executeBulkAction} disabled={!bulkAction} color="warning" size="sm">
146
+ Execute
147
+ </Button>
148
+ </div>
149
+ </div>
150
+ {/if}
151
+
152
+ <!-- Users Table Component -->
153
+ <UserTable
154
+ {users}
155
+ {loading}
156
+ {currentPage}
157
+ {pageSize}
158
+ {totalUsers}
159
+ {onPageChange}
160
+ {onPageSizeChange}
161
+ {onSort}
162
+ onView={openViewModal}
163
+ onEdit={openEditModal}
164
+ onDelete={handleDeleteUser}
165
+ />
166
+
167
+ <!-- User View Modal -->
168
+ <UserViewModal
169
+ bind:open={showViewModal}
170
+ bind:user={selectedUser}
171
+ {permissions}
172
+ onEdit={handleEditFromView}
173
+ onClose={() => (selectedUser = null)}
174
+ />
175
+
176
+ <!-- User Edit/Create Modal -->
177
+ <UserModal
178
+ bind:open={showEditCreateModal}
179
+ bind:user={selectedUser}
180
+ {roles}
181
+ onSave={handleUserSave}
182
+ onClose={() => (selectedUser = null)}
183
+ />
184
+ </div>
@@ -0,0 +1,4 @@
1
+ import type { UserManagementProps } from './user-management.js';
2
+ declare const UserManagement: import("svelte").Component<UserManagementProps, {}, "">;
3
+ type UserManagement = ReturnType<typeof UserManagement>;
4
+ export default UserManagement;
@@ -0,0 +1,47 @@
1
+ <script lang="ts">
2
+ import UserManagement from './UserManagement.svelte';
3
+ import type { UserManagementProps } from './user-management.js';
4
+
5
+ interface Props extends UserManagementProps {
6
+ testId?: string;
7
+ }
8
+
9
+ let {
10
+ users = [],
11
+ totalUsers = 0,
12
+ loading = false,
13
+ currentPage = 1,
14
+ pageSize = 10,
15
+ roles = [],
16
+ permissions = [],
17
+ onPageChange = () => {},
18
+ onPageSizeChange = () => {},
19
+ onSort,
20
+ onCreateUser,
21
+ onUpdateUser,
22
+ onDeleteUser,
23
+ onDeleteUsers,
24
+ testId,
25
+ ...rest
26
+ }: Props = $props();
27
+ </script>
28
+
29
+ <div data-testid={testId}>
30
+ <UserManagement
31
+ {users}
32
+ {totalUsers}
33
+ {loading}
34
+ {currentPage}
35
+ {pageSize}
36
+ {roles}
37
+ {permissions}
38
+ {onPageChange}
39
+ {onPageSizeChange}
40
+ {onSort}
41
+ {onCreateUser}
42
+ {onUpdateUser}
43
+ {onDeleteUser}
44
+ {onDeleteUsers}
45
+ {...rest}
46
+ />
47
+ </div>
@@ -0,0 +1,7 @@
1
+ import type { UserManagementProps } from './user-management.js';
2
+ interface Props extends UserManagementProps {
3
+ testId?: string;
4
+ }
5
+ declare const UserManagementTestWrapper: import("svelte").Component<Props, {}, "">;
6
+ type UserManagementTestWrapper = ReturnType<typeof UserManagementTestWrapper>;
7
+ export default UserManagementTestWrapper;