@xeplr/ui-account 1.0.0 → 1.0.2

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 (40) hide show
  1. package/package.json +25 -5
  2. package/src/AccessContext.jsx +86 -4
  3. package/src/ProtectedRoute.jsx +4 -1
  4. package/src/activeScope.js +71 -0
  5. package/src/adminApi.js +0 -29
  6. package/src/api.js +321 -22
  7. package/src/authRoutes.jsx +125 -0
  8. package/src/designs/AccountMenu.jsx +71 -0
  9. package/src/designs/ActivateSample.jsx +8 -16
  10. package/src/designs/ChangePasswordSample.jsx +3 -3
  11. package/src/designs/ForgotPasswordSample.jsx +3 -3
  12. package/src/designs/LoginSample.jsx +3 -2
  13. package/src/designs/NavDrawer.jsx +194 -0
  14. package/src/designs/NavFloatingSettings.jsx +25 -0
  15. package/src/designs/NavTopSample.jsx +34 -0
  16. package/src/designs/NotificationsBell.jsx +31 -0
  17. package/src/designs/ProfileSample.jsx +3 -3
  18. package/src/designs/RegisterSample.jsx +3 -3
  19. package/src/designs/ResetPasswordSample.jsx +7 -9
  20. package/src/designs/admin.css +13 -3
  21. package/src/designs/auth.css +6 -60
  22. package/src/designs/index.js +5 -2
  23. package/src/designs/nav.css +439 -0
  24. package/src/index.js +24 -8
  25. package/src/mt.js +31 -0
  26. package/src/pages.jsx +95 -38
  27. package/src/returnTo.js +24 -0
  28. package/src/useActivateController.js +19 -4
  29. package/src/useChangePasswordController.js +8 -2
  30. package/src/useForgotPasswordController.js +3 -0
  31. package/src/useLoginController.js +8 -1
  32. package/src/useNavController.js +224 -0
  33. package/src/useProfileController.js +6 -1
  34. package/src/useRegisterController.js +5 -1
  35. package/src/useResetPasswordController.js +12 -1
  36. package/src/validateDesign.js +20 -2
  37. package/src/designs/TenantPickerSample.jsx +0 -39
  38. package/src/designs/TenantSample.jsx +0 -182
  39. package/src/useTenantController.js +0 -146
  40. package/src/useTenantPickerController.js +0 -97
@@ -0,0 +1,439 @@
1
+ /* Uses the shared --xeplr-* theme tokens (see theme.css) — correct in both
2
+ light and dark automatically, as long as the app applies a .xeplr-theme-*
3
+ class (see ThemeContext.jsx / theme.css usage notes). */
4
+
5
+ /* ─── Top bar: 10% / 80% / 10% ───
6
+ flexbox, not grid: fixed percentage grid tracks don't account for `gap`
7
+ or box-sizing the way flex-basis does, and 10%+80%+10% already sums to
8
+ 100% before the gaps/padding are added — grid pushed the real content
9
+ width past the container and forced a horizontal scrollbar. flex-basis
10
+ percentages combined with box-sizing:border-box and the default
11
+ flex-shrink correctly negotiate the gaps back out of the 100%. */
12
+
13
+ .xeplr-nav-top {
14
+ display: flex;
15
+ align-items: center;
16
+ gap: 16px;
17
+ padding: 10px 20px;
18
+ box-sizing: border-box;
19
+ background: var(--xeplr-bg-tertiary);
20
+ border-bottom: 1px solid var(--xeplr-border-primary);
21
+ }
22
+
23
+ .xeplr-nav-left { flex: 0 1 10%; display: flex; align-items: center; gap: 8px; min-width: 0; }
24
+ .xeplr-nav-right { flex: 0 1 10%; display: flex; align-items: center; justify-content: flex-end; gap: 8px; }
25
+
26
+ .xeplr-nav-logo { height: 32px; width: auto; display: block; }
27
+
28
+ .xeplr-nav-middle {
29
+ flex: 1 1 80%;
30
+ display: flex;
31
+ align-items: center;
32
+ gap: 12px;
33
+ min-width: 0;
34
+ }
35
+
36
+ /* ─── Settings trigger + dropdown ─── */
37
+
38
+ .xeplr-nav-account { position: relative; }
39
+
40
+ .xeplr-nav-settings-trigger {
41
+ position: relative;
42
+ width: 34px;
43
+ height: 34px;
44
+ border: none;
45
+ background: none;
46
+ border-radius: 50%;
47
+ color: var(--xeplr-text-secondary);
48
+ display: grid;
49
+ place-items: center;
50
+ cursor: pointer;
51
+ align-self: center;
52
+ }
53
+ .xeplr-nav-settings-trigger:hover { background: var(--xeplr-bg-hover); color: var(--xeplr-text-primary); }
54
+ .xeplr-nav-settings-trigger svg { width: 19px; height: 19px; flex-shrink: 0; }
55
+
56
+ /* Drawer, expanded: a full-width labeled row instead of a circular icon
57
+ button, matching .xeplr-nav-drawer-link's look. */
58
+ .xeplr-nav-settings-trigger-labeled {
59
+ width: 100%;
60
+ height: auto;
61
+ border-radius: 6px;
62
+ display: flex;
63
+ justify-content: flex-start;
64
+ gap: 10px;
65
+ padding: 9px 10px;
66
+ }
67
+ .xeplr-nav-settings-trigger-label { font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
68
+
69
+ .xeplr-nav-avatar {
70
+ width: 34px;
71
+ height: 34px;
72
+ border-radius: 50%;
73
+ background: var(--xeplr-accent);
74
+ color: var(--xeplr-accent-text);
75
+ display: grid;
76
+ place-items: center;
77
+ font-size: 14px;
78
+ font-weight: 700;
79
+ flex-shrink: 0;
80
+ }
81
+ .xeplr-nav-avatar-img { object-fit: cover; }
82
+
83
+ .xeplr-nav-bell {
84
+ position: relative;
85
+ width: 34px;
86
+ height: 34px;
87
+ border: none;
88
+ background: none;
89
+ border-radius: 50%;
90
+ color: var(--xeplr-text-secondary);
91
+ display: grid;
92
+ place-items: center;
93
+ cursor: pointer;
94
+ align-self: center;
95
+ }
96
+ .xeplr-nav-bell:hover { background: var(--xeplr-bg-hover); color: var(--xeplr-text-primary); }
97
+ .xeplr-nav-bell svg { width: 19px; height: 19px; flex-shrink: 0; }
98
+
99
+ /* Drawer, expanded: a full-width labeled row instead of a circular icon
100
+ button, matching .xeplr-nav-drawer-link's look. */
101
+ .xeplr-nav-bell-labeled {
102
+ position: relative;
103
+ width: 100%;
104
+ height: auto;
105
+ border-radius: 6px;
106
+ display: flex;
107
+ justify-content: flex-start;
108
+ gap: 10px;
109
+ padding: 9px 10px;
110
+ }
111
+ .xeplr-nav-bell-labeled .xeplr-nav-badge { position: static; margin-left: auto; }
112
+ .xeplr-nav-bell-label { font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
113
+
114
+ .xeplr-nav-badge {
115
+ position: absolute;
116
+ top: -4px;
117
+ right: -4px;
118
+ min-width: 16px;
119
+ height: 16px;
120
+ padding: 0 4px;
121
+ border-radius: 999px;
122
+ background: var(--xeplr-danger);
123
+ color: #fff;
124
+ font-size: 10px;
125
+ font-weight: 700;
126
+ line-height: 1;
127
+ display: grid;
128
+ place-items: center;
129
+ }
130
+
131
+ .xeplr-nav-account-menu {
132
+ position: absolute;
133
+ top: calc(100% + 8px);
134
+ right: 0;
135
+ min-width: 220px;
136
+ background: var(--xeplr-bg-tertiary);
137
+ border: 1px solid var(--xeplr-border-primary);
138
+ border-radius: 8px;
139
+ box-shadow: var(--xeplr-shadow-md);
140
+ padding: 6px;
141
+ z-index: 60;
142
+ }
143
+
144
+ /* Drawer placement: trigger sits near the bottom-left of the viewport, so the
145
+ menu opens upward (not off the bottom edge) and rightward from the trigger
146
+ (not off the left edge — the default `right: 0` anchoring would run the
147
+ menu's width past x=0 given how narrow/close-to-the-edge the drawer is). */
148
+ .xeplr-nav-account-menu-top {
149
+ top: auto;
150
+ bottom: calc(100% + 8px);
151
+ left: 0;
152
+ right: auto;
153
+ }
154
+
155
+ .xeplr-nav-account-header {
156
+ display: flex;
157
+ align-items: center;
158
+ gap: 10px;
159
+ padding: 8px 10px 10px;
160
+ margin-bottom: 6px;
161
+ border-bottom: 1px solid var(--xeplr-border-secondary);
162
+ }
163
+
164
+ .xeplr-nav-account-header-text { min-width: 0; }
165
+ .xeplr-nav-account-name { font-size: 13px; font-weight: 600; color: var(--xeplr-text-primary); }
166
+ .xeplr-nav-account-email { margin-top: 2px; font-size: 12px; color: var(--xeplr-text-muted); }
167
+
168
+ .xeplr-nav-account-item {
169
+ display: block;
170
+ width: 100%;
171
+ text-align: left;
172
+ padding: 8px 10px;
173
+ border: none;
174
+ border-radius: 6px;
175
+ background: none;
176
+ color: var(--xeplr-text-primary);
177
+ font-size: 13px;
178
+ text-decoration: none;
179
+ cursor: pointer;
180
+ }
181
+
182
+ .xeplr-nav-account-item:hover { background: var(--xeplr-bg-hover); }
183
+ .xeplr-nav-account-logout { color: var(--xeplr-danger); }
184
+ .xeplr-nav-account-divider { height: 1px; margin: 6px 0; background: var(--xeplr-border-secondary); }
185
+
186
+ /* ─── Drawer rail — pinned to the viewport's top-left corner (0,0), full
187
+ height, `position: fixed` with a high z-index. A pure overlay: it never
188
+ pushes, offsets, or otherwise interacts with the top bar or the app's own
189
+ content — there IS no top bar when this renders (see pages.jsx, they're
190
+ mutually exclusive), and content underneath simply renders at its normal
191
+ position, with the drawer floating on top of whatever's in that corner.
192
+ No overflow set here (default: visible) so the account-menu popup in the
193
+ footer can escape this box — only the scrollable nav-links list below
194
+ clips/scrolls on its own. */
195
+
196
+ .xeplr-nav-drawer {
197
+ position: fixed;
198
+ top: 0;
199
+ left: 0;
200
+ /* ABOVE .xeplr-nav-floating (400). Both are global chrome, so the tie goes
201
+ to the one being operated: widen the drawer and it reaches under the
202
+ floating bell/settings in the top-right corner, where those two icons
203
+ swallow clicks meant for the drawer — the resize handle's top edge
204
+ included. The floating cluster keeps 400 and still outranks page-level
205
+ chrome, which is what that value is actually for.
206
+
207
+ THE CEILING THIS SETS: a real full-screen modal must out-stack this, or
208
+ the rail punches through it. Consumers with one are expected to sit above
209
+ 500 — xeplr-bi's .dsh-expanded-backdrop was moved to 600 when this
210
+ changed. */
211
+ z-index: 500;
212
+ display: flex;
213
+ flex-direction: column;
214
+ height: 100vh;
215
+ padding: 16px 10px;
216
+ background: var(--xeplr-bg-tertiary);
217
+ border-right: 1px solid var(--xeplr-border-primary);
218
+ box-shadow: var(--xeplr-shadow-lg);
219
+ transition: width 0.15s;
220
+ flex-shrink: 0;
221
+ }
222
+
223
+ .xeplr-nav-drawer-collapsed { width: 60px; }
224
+ /* The 240px is the DEFAULT only — NavDrawer sets an inline width from the
225
+ remembered drag, which outranks this. Kept here so the drawer still has a
226
+ sane width if the controller's stored value is unreadable. */
227
+ .xeplr-nav-drawer-expanded { width: 240px; padding: 16px; }
228
+
229
+ /* Mid-drag the width changes every pointermove, and a 0.15s transition on it
230
+ means the edge chases the cursor instead of tracking it. Kill it for the
231
+ duration; the toggle animation is unaffected because it never coincides
232
+ with a drag. */
233
+ .xeplr-nav-drawer-resizing { transition: none; }
234
+
235
+ /* ── the resize handle ─────────────────────────────────────────────────
236
+ Straddles the right border (right: -3px on a 6px strip) rather than sitting
237
+ inside it, so the grab target covers the edge the user is actually aiming
238
+ at — an inside-only strip makes you undershoot into the nav. */
239
+ .xeplr-nav-drawer-resize {
240
+ position: absolute;
241
+ top: 0;
242
+ right: -3px;
243
+ width: 6px;
244
+ height: 100%;
245
+ cursor: col-resize;
246
+ /* Invisible until wanted: an always-on divider line reads as a border and
247
+ invites clicks that do nothing. */
248
+ background: transparent;
249
+ transition: background 0.12s;
250
+ /* Above the drawer's own content so the grab always lands on the handle.
251
+ Local to the drawer's stacking context, so it stays under the footer's
252
+ account popup rather than competing with the page. */
253
+ z-index: 1;
254
+ }
255
+ .xeplr-nav-drawer-resize:hover,
256
+ .xeplr-nav-drawer-resize:focus-visible,
257
+ .xeplr-nav-drawer-resizing .xeplr-nav-drawer-resize {
258
+ background: var(--xeplr-accent, #2563eb);
259
+ }
260
+ /* The focus ring would be a 6px sliver and easy to miss; the accent fill above
261
+ is the visible focus state, so the default outline only gets in its way. */
262
+ .xeplr-nav-drawer-resize:focus-visible { outline: none; }
263
+
264
+ /* Set on <body> for the length of the drag (see useNavController). A drag
265
+ routinely outruns the 6px handle, and without this the cursor flips back to
266
+ a caret and the page starts blue-selecting the moment it does. */
267
+ body.xeplr-nav-resizing { user-select: none; }
268
+ body.xeplr-nav-resizing * { cursor: col-resize !important; }
269
+
270
+ .xeplr-nav-drawer-toggle {
271
+ display: block;
272
+ flex-shrink: 0;
273
+ border: none;
274
+ background: none;
275
+ padding: 0;
276
+ margin-bottom: 12px;
277
+ cursor: pointer;
278
+ align-self: flex-start;
279
+ }
280
+ /* CENTRED WHEN COLLAPSED, like everything under it.
281
+ The rail's links get `justify-content: center`, so their icons sit in the
282
+ middle of the 40px of content width. The toggle had `padding: 0` and
283
+ `align-self: flex-start`, which pinned the logo hard against the left edge —
284
+ so the mark and the first menu icon were visibly on two different vertical
285
+ lines, which is the one thing a rail of icons cannot afford. */
286
+ .xeplr-nav-drawer-collapsed .xeplr-nav-drawer-toggle { align-self: center; }
287
+
288
+ /* The collapse affordance, when the lockup below is carrying the brand. */
289
+ .xeplr-nav-drawer-collapse {
290
+ display: block;
291
+ width: 22px;
292
+ line-height: 22px;
293
+ text-align: center;
294
+ font-size: 15px;
295
+ color: var(--xeplr-text-secondary, #8b8f9a);
296
+ }
297
+
298
+ /* Fixed, small, and never swapped for a different image (see NavDrawer.jsx) —
299
+ deliberately smaller than the collapsed rail's content width (60px rail -
300
+ 20px padding = 40px) so it can never force the rail wider than intended. */
301
+ .xeplr-nav-drawer-logo { width: 22px; height: 22px; object-fit: contain; display: block; }
302
+
303
+ /* Its own row, real height — typically a stacked icon+wordmark lockup
304
+ (near-square, not a wide banner), so 22px (the toggle icon's height) would
305
+ crush the wordmark to a few illegible pixels. */
306
+ .xeplr-nav-drawer-expanded-logo {
307
+ height: 40px;
308
+ width: auto;
309
+ object-fit: contain;
310
+ display: block;
311
+ margin-bottom: 16px;
312
+ align-self: flex-start;
313
+ }
314
+
315
+ .xeplr-nav-drawer-search {
316
+ width: 100%;
317
+ margin-bottom: 12px;
318
+ padding: 8px 10px;
319
+ border: 1px solid var(--xeplr-border-secondary);
320
+ border-radius: 6px;
321
+ background: var(--xeplr-bg-primary);
322
+ color: var(--xeplr-text-primary);
323
+ font-size: 13px;
324
+ }
325
+
326
+ .xeplr-nav-drawer-links {
327
+ display: flex;
328
+ flex-direction: column;
329
+ gap: 2px;
330
+ flex: 1;
331
+ overflow-y: auto;
332
+ overflow-x: hidden;
333
+ }
334
+
335
+ .xeplr-nav-drawer-group { display: flex; flex-direction: column; gap: 2px; margin-top: 10px; }
336
+ .xeplr-nav-drawer-group-label {
337
+ padding: 4px 10px;
338
+ font-size: 11px;
339
+ font-weight: 600;
340
+ letter-spacing: 0.04em;
341
+ text-transform: uppercase;
342
+ color: var(--xeplr-text-muted);
343
+ }
344
+
345
+ .xeplr-nav-drawer-link {
346
+ display: flex;
347
+ align-items: center;
348
+ gap: 10px;
349
+ padding: 9px 10px;
350
+ border: none;
351
+ border-radius: 6px;
352
+ background: none;
353
+ color: var(--xeplr-text-primary);
354
+ font-size: 14px;
355
+ text-align: left;
356
+ cursor: pointer;
357
+ }
358
+ .xeplr-nav-drawer-link:hover { background: var(--xeplr-bg-hover); }
359
+ .xeplr-nav-drawer-collapsed .xeplr-nav-drawer-link { justify-content: center; }
360
+
361
+ .xeplr-nav-drawer-icon { display: grid; place-items: center; flex-shrink: 0; }
362
+ .xeplr-nav-drawer-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
363
+
364
+ /* ── item badges ─────────────────────────────────────────────────────────
365
+ Optional `item.badge`. The pill takes the remaining width (margin-left
366
+ auto) so counts line up down the rail instead of tracking label lengths.
367
+ The label keeps flex:1 so a long name still ellipsises rather than pushing
368
+ the pill off the edge. */
369
+ .xeplr-nav-drawer-link-badged .xeplr-nav-drawer-label { flex: 1; }
370
+ .xeplr-nav-drawer-badge {
371
+ margin-left: auto;
372
+ flex-shrink: 0;
373
+ min-width: 18px;
374
+ padding: 1px 6px;
375
+ border-radius: 9px;
376
+ background: var(--xeplr-danger, #c0392b);
377
+ color: #fff;
378
+ font-size: 11px;
379
+ font-weight: 600;
380
+ line-height: 16px;
381
+ text-align: center;
382
+ }
383
+ /* Collapsed: the rail is icons only, so the count becomes a dot pinned to the
384
+ icon's corner. `position: relative` on the icon rather than the button
385
+ because the button is centred and the icon is what the eye is on. */
386
+ .xeplr-nav-drawer-collapsed .xeplr-nav-drawer-icon { position: relative; }
387
+ .xeplr-nav-drawer-dot {
388
+ position: absolute;
389
+ top: -2px;
390
+ right: -3px;
391
+ width: 8px;
392
+ height: 8px;
393
+ border-radius: 50%;
394
+ background: var(--xeplr-danger, #c0392b);
395
+ /* Against the rail's own background, so the dot reads as ON the icon rather
396
+ than as part of it. */
397
+ box-shadow: 0 0 0 2px var(--xeplr-bg-elevated, #fff);
398
+ }
399
+
400
+ .xeplr-nav-drawer-empty { padding: 10px 12px; color: var(--xeplr-text-muted); font-size: 13px; }
401
+
402
+ .xeplr-nav-drawer-promo { padding-top: 12px; }
403
+
404
+ /* Settings (AccountMenu, placement="top") + notifications — the drawer's
405
+ footer, since there's no top bar for them to live in when the drawer is
406
+ the whole nav. `.xeplr-nav-drawer-links` already has flex:1, so this and
407
+ any promo above it are naturally pushed to the bottom without needing
408
+ their own margin-top:auto. */
409
+ .xeplr-nav-drawer-footer {
410
+ display: flex;
411
+ flex-direction: column;
412
+ /* stretch so the expanded (labeled) rows can go full-width; the collapsed
413
+ circular icon buttons self-center via their own align-self: center. */
414
+ align-items: stretch;
415
+ gap: 6px;
416
+ padding-top: 12px;
417
+ margin-top: 12px;
418
+ border-top: 1px solid var(--xeplr-border-secondary);
419
+ }
420
+
421
+ /* Opt-in alternative to the drawer footer above (see NavPage's `floatingSettings`
422
+ prop) — same bell + account trigger, floating over the page instead of
423
+ pinned to the drawer. Just the two icons themselves, no card/box around
424
+ them — no background, border, or shadow.
425
+
426
+ z-index sits above page-level floating chrome, not below it. The icons
427
+ themselves never overlap anything, but the account menu drops DOWN over the
428
+ page — and a settings menu that opens behind a page toolbar is unusable.
429
+ Global chrome wins over page tools; a consumer's own floating toolbar
430
+ should stay below this and position itself clear of the icons. */
431
+ .xeplr-nav-floating {
432
+ position: fixed;
433
+ top: 16px;
434
+ right: 16px;
435
+ z-index: 400;
436
+ display: flex;
437
+ align-items: center;
438
+ gap: 8px;
439
+ }
package/src/index.js CHANGED
@@ -1,9 +1,24 @@
1
1
  // API client
2
- export { configure, registerUser, loginUser, activateAccount, forgotPassword, resetPassword, changePassword, getProfile, updateProfile, logoutUser, getMyTenants, authFetch } from './api.js';
2
+ export { configure, setSessionExpiredHandler, registerUser, loginUser, activateAccount, forgotPassword, resetPassword, changePassword, getMe, getProfile, updateProfile, uploadAvatar, logoutUser, authFetch } from './api.js';
3
3
 
4
4
  // Token helpers
5
5
  export { getToken, setToken, getRefreshToken, setRefreshToken, getUser, setUser, clearAuth, isAuthenticated } from './token.js';
6
6
 
7
+ // Multi-tenant levels — call registerMTs() once at app boot with the SAME
8
+ // shape passed to @xeplr/db's registerMTs() on the backend. Teaches authFetch
9
+ // which header to attach for each level, read from activeScope. See mt.js.
10
+ export { registerMTs, getMtConfig } from './mt.js';
11
+
12
+ // Active scope (e.g. company/workspace), keyed by MT level (l1, l2, ...) — a
13
+ // bare, app-interpreted value per level, attached to every authFetch call as
14
+ // that level's configured header. See activeScope.js.
15
+ export { getActiveScope, setActiveScope, clearActiveScope, getLastScope, setLastScope } from './activeScope.js';
16
+
17
+ // Where to send the user once a redirect-driven gate (auth, or an app's own
18
+ // gate) resolves — e.g. back to the exact dashboard a deep link pointed at,
19
+ // rather than always home. See returnTo.js.
20
+ export { saveReturnTo, consumeReturnTo } from './returnTo.js';
21
+
7
22
  // Controller hooks
8
23
  export { useLoginController } from './useLoginController.js';
9
24
  export { useRegisterController } from './useRegisterController.js';
@@ -15,8 +30,7 @@ export { useProfileController } from './useProfileController.js';
15
30
  export { useUserRolesController } from './useUserRolesController.js';
16
31
  export { useAccessMatrixController } from './useAccessMatrixController.js';
17
32
  export { useMasterSettingsController } from './useMasterSettingsController.js';
18
- export { useTenantController } from './useTenantController.js';
19
- export { useTenantPickerController, getActiveTenant, setActiveTenant, clearActiveTenant, hasMultipleTenants } from './useTenantPickerController.js';
33
+ export { useNavController } from './useNavController.js';
20
34
 
21
35
  // Theme
22
36
  export { ThemeProvider, useTheme, useThemeStrict, BUILT_IN_THEMES, DEFAULT_THEME } from './ThemeContext.jsx';
@@ -29,13 +43,15 @@ export { AccessGuard } from './AccessGuard.jsx';
29
43
  // Admin API (model layer for RBAC management)
30
44
  export { getUsers, getRoles, getAccessItems, toggleUserRole, toggleAccessRole, toggleModuleRole } from './adminApi.js';
31
45
  export { getMasterItems, saveMasterItem, deleteMasterItem, MASTER_TYPES } from './masterApi.js';
32
- export { getTenants, saveTenant, deleteTenant, assignUserTenant } from './adminApi.js';
33
46
 
34
47
  // Design validation (use when building custom designs)
35
- export { useDesignValidator, LOGIN_RULES, REGISTER_RULES, FORGOT_PASSWORD_RULES, RESET_PASSWORD_RULES, CHANGE_PASSWORD_RULES, PROFILE_RULES, USER_ROLES_MATRIX_RULES, ACCESS_MATRIX_RULES, MASTER_SETTINGS_RULES } from './validateDesign.js';
48
+ export { useDesignValidator, LOGIN_RULES, REGISTER_RULES, FORGOT_PASSWORD_RULES, RESET_PASSWORD_RULES, CHANGE_PASSWORD_RULES, PROFILE_RULES, USER_ROLES_MATRIX_RULES, ACCESS_MATRIX_RULES, MASTER_SETTINGS_RULES, NAV_RULES } from './validateDesign.js';
36
49
 
37
50
  // Sample designs (use as reference or starting point)
38
- export { LoginSample, RegisterSample, ForgotPasswordSample, ResetPasswordSample, ActivateSample, NotActivatedSample, ChangePasswordSample, ProfileSample, UserRolesMatrixSample, AccessMatrixSample, MasterSettingsSample, TenantSample } from './designs/index.js';
51
+ export { LoginSample, RegisterSample, ForgotPasswordSample, ResetPasswordSample, ActivateSample, NotActivatedSample, ChangePasswordSample, ProfileSample, UserRolesMatrixSample, AccessMatrixSample, MasterSettingsSample, NavTopSample, AccountMenu, NavDrawer, NavFloatingSettings, NotificationsBell } from './designs/index.js';
52
+
53
+ // Ready-made pages (controller + sample design wired together; each takes an optional `design` prop)
54
+ export { LoginPage, RegisterPage, ForgotPasswordPage, ResetPasswordPage, ActivatePage, NotActivatedPage, ChangePasswordPage, ProfilePage, UserRolesPage, AccessMatrixPage, MasterSettingsPage, NavPage } from './pages.jsx';
39
55
 
40
- // Ready-made pages (controller + sample design wired together)
41
- export { LoginPage, RegisterPage, ForgotPasswordPage, ResetPasswordPage, ActivatePage, NotActivatedPage, ChangePasswordPage, ProfilePage, UserRolesPage, AccessMatrixPage, MasterSettingsPage, TenantPage, TenantPickerPage } from './pages.jsx';
56
+ // One-call auth routing — mount every auth page with no boilerplate; override just what you want
57
+ export { authRoutes, authPath } from './authRoutes.jsx';
package/src/mt.js ADDED
@@ -0,0 +1,31 @@
1
+ // Frontend counterpart to @xeplr/db's BaseModel.registerMTs() — call this once
2
+ // at app boot with the SAME config object (same level keys, names, headers).
3
+ // There's no shared runtime package between frontend and backend (by design —
4
+ // duplicated by convention); keep one literal config in your own app and
5
+ // import it into both boot entry points so it can't drift.
6
+ //
7
+ // registerMTs({
8
+ // l1: { name: 'companyId', header: 'x-company-id' },
9
+ // l2: { name: 'workspaceId', header: 'x-workspace-id' }
10
+ // });
11
+ //
12
+ // This only teaches api.js's authFetch which header to attach for each level
13
+ // (read from activeScope.js) — it doesn't validate anything itself.
14
+
15
+ var SLOT_KEYS = ['l1', 'l2', 'l3', 'l4'];
16
+ var _config = { slots: {} };
17
+
18
+ export function registerMTs(config) {
19
+ config = config || {};
20
+ var slots = {};
21
+ SLOT_KEYS.forEach(function(key) {
22
+ if (config[key]) {
23
+ slots[key] = { name: config[key].name, header: String(config[key].header).toLowerCase() };
24
+ }
25
+ });
26
+ _config = { slots: slots };
27
+ }
28
+
29
+ export function getMtConfig() {
30
+ return { slots: Object.assign({}, _config.slots) };
31
+ }