@elasticias/core 0.0.16 → 1.0.1

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.
@@ -1,7 +1,9 @@
1
1
  import * as rxjs from 'rxjs';
2
2
  import * as i0 from '@angular/core';
3
+ import { InjectionToken, Signal } from '@angular/core';
3
4
  import { CanActivateFn } from '@angular/router';
4
- import { PermissionsEnum } from '@elasticias/types';
5
+ import { Permissions } from '@elasticias/types';
6
+ import * as _primeuix_themes_lara_base from '@primeuix/themes/lara/base';
5
7
  import * as _primeuix_themes_types from '@primeuix/themes/types';
6
8
  import * as _primeuix_themes_aura_base from '@primeuix/themes/aura/base';
7
9
  import { Translation } from 'primeng/api';
@@ -29,16 +31,99 @@ declare class CacheService {
29
31
  static ɵprov: i0.ɵɵInjectableDeclaration<CacheService>;
30
32
  }
31
33
 
32
- declare class ToastService {
33
- private static readonly DEFAULT_DELAY;
34
- private static readonly DEFAULT_DELAY_INFO;
35
- private readonly messageService;
36
- showInfo(message: string, title?: string, delay?: number): void;
37
- showSuccess(message?: string, title?: string, delay?: number): void;
38
- showWarn(message: string, title?: string, delay?: number): void;
39
- showError(message?: string, title?: string, delay?: number): void;
40
- static ɵfac: i0.ɵɵFactoryDeclaration<ToastService, never>;
41
- static ɵprov: i0.ɵɵInjectableDeclaration<ToastService>;
34
+ /**
35
+ * Toast severities — mapped 1:1 to the Comptoir status spectrum:
36
+ * `info` → processing, `success` → delivered, `warn` → pending,
37
+ * `error` → cancelled.
38
+ */
39
+ type EfToastSeverity = 'info' | 'success' | 'warn' | 'error';
40
+ /** Optional inline action button rendered at the end of a toast. */
41
+ interface EfToastAction {
42
+ /** Translation key for the button label — preferred. */
43
+ labelKey?: string;
44
+ /** Direct label fallback when `labelKey` is empty. */
45
+ label?: string;
46
+ /** Visual tone — `'ghost'` (default) or `'primary'` for the
47
+ * destructive / confirm action. */
48
+ severity?: 'ghost' | 'primary';
49
+ /** Click handler. */
50
+ command?: () => void;
51
+ /** Auto-close the toast after the click runs (default `true`). */
52
+ dismissOnClick?: boolean;
53
+ }
54
+ /**
55
+ * Toast options accepted by `EfToastService.show()`. Either a literal
56
+ * `title` / `text` or their `*Key` translation variants — keys win
57
+ * unless empty.
58
+ */
59
+ interface EfToastOptions {
60
+ severity?: EfToastSeverity;
61
+ title?: string;
62
+ titleKey?: string;
63
+ text?: string;
64
+ textKey?: string;
65
+ /** Auto-dismiss in ms; `0` = sticky (no auto-dismiss). */
66
+ life?: number;
67
+ actions?: EfToastAction[];
68
+ }
69
+ /** Active toast — instance held in `EfToastService.toasts`. */
70
+ interface EfToast {
71
+ id: number;
72
+ severity: EfToastSeverity;
73
+ title: string;
74
+ text: string;
75
+ life: number;
76
+ actions?: EfToastAction[];
77
+ }
78
+ /**
79
+ * Comptoir toast service — V2 successor to the legacy `ToastService`.
80
+ *
81
+ * Owns a signal-based queue (`toasts`) consumed by
82
+ * `<ef-toast-region>`, AND forwards every toast to PrimeNG's
83
+ * `MessageService` for back-compat with `<p-toast>` (still used by
84
+ * ClientApp v1). Either renderer picks the toasts up; both work.
85
+ *
86
+ * Default i18n keys (override per-call via `titleKey` / `textKey`):
87
+ * - `ef_toast_info_title`, `ef_toast_info_default`
88
+ * - `ef_toast_success_title`, `ef_toast_success_default`
89
+ * - `ef_toast_warn_title`, `ef_toast_warn_default`
90
+ * - `ef_toast_error_title`, `ef_toast_error_default`
91
+ *
92
+ * Default lifespans: info 5s, success 4s, warn 6s, error 8s.
93
+ */
94
+ declare class EfToastService {
95
+ private static readonly LIFE_INFO;
96
+ private static readonly LIFE_SUCCESS;
97
+ private static readonly LIFE_WARN;
98
+ private static readonly LIFE_ERROR;
99
+ /**
100
+ * Lazy holders. Resolving TranslateService eagerly at construction
101
+ * time pulls in HttpClient → HTTP_INTERCEPTORS → AuthorizeInterceptor
102
+ * → AuthorizeService → ToastService → cycle. We defer to the first
103
+ * actual translate / message-publish call.
104
+ */
105
+ private readonly injector;
106
+ private _translate?;
107
+ private _messageService?;
108
+ private _messageServiceResolved;
109
+ private nextId;
110
+ /** Live queue — `<ef-toast-region>` renders this. */
111
+ readonly toasts: i0.WritableSignal<readonly EfToast[]>;
112
+ showInfo(message?: string, title?: string, life?: number): void;
113
+ showSuccess(message?: string, title?: string, life?: number): void;
114
+ showWarn(message?: string, title?: string, life?: number): void;
115
+ showError(message?: string, title?: string, life?: number): void;
116
+ /** Generic show — opts can mix `title`/`titleKey`, `text`/`textKey`. */
117
+ show(opts: EfToastOptions): EfToast;
118
+ private get messageService();
119
+ dismiss(id: number): void;
120
+ clear(): void;
121
+ private resolve;
122
+ private t;
123
+ private defaultLife;
124
+ private defaultTitleKey;
125
+ static ɵfac: i0.ɵɵFactoryDeclaration<EfToastService, never>;
126
+ static ɵprov: i0.ɵɵInjectableDeclaration<EfToastService>;
42
127
  }
43
128
 
44
129
  declare class ConfirmDialogService {
@@ -56,8 +141,8 @@ interface ScreenGuardConfig {
56
141
  grantsStorageKey?: string;
57
142
  /** Route to redirect to when access is denied (default: '/') */
58
143
  deniedRedirect?: string;
59
- /** Minimum required permission to access the screen (default: PermissionsEnum.Read) */
60
- requiredPermission?: PermissionsEnum;
144
+ /** Minimum required permission to access the screen (default: Permissions.Read) */
145
+ requiredPermission?: Permissions;
61
146
  /** Storage type to read grants from (default: 'session') */
62
147
  storageType?: 'local' | 'session';
63
148
  }
@@ -85,7 +170,7 @@ declare function screenGuard(config?: ScreenGuardConfig): CanActivateFn;
85
170
  * Utility function to check if the current user has a specific permission on a screen.
86
171
  * Can be used in components/services outside of route guards.
87
172
  */
88
- declare function hasScreenPermission(screenCode: string, permission: PermissionsEnum, grantsStorageKey?: string, storageType?: 'local' | 'session'): boolean;
173
+ declare function hasScreenPermission(screenCode: string, permission: Permissions, grantsStorageKey?: string, storageType?: 'local' | 'session'): boolean;
89
174
 
90
175
  interface AppState {
91
176
  preset?: string;
@@ -114,9 +199,9 @@ declare class EfThemeConfigService {
114
199
  platformId: Object;
115
200
  theme: i0.Signal<"dark" | "light">;
116
201
  transitionComplete: i0.WritableSignal<boolean>;
117
- private initialized;
118
202
  constructor();
119
203
  private static readonly PRESET_CLASS_MAP;
204
+ private static readonly TENANT_RAMP_STOPS;
120
205
  private static readonly ALL_THEME_CLASSES;
121
206
  private updatePresetClass;
122
207
  private handleDarkModeTransition;
@@ -135,6 +220,19 @@ declare class EfThemeConfigService {
135
220
  hideDesigner(): void;
136
221
  private loadAppState;
137
222
  private saveAppState;
223
+ /**
224
+ * Applies a tenant's brand color across both PrimeNG's primary palette
225
+ * and the Comptoir `--tenant-*` CSS variables.
226
+ *
227
+ * Generates a 50–950 ramp from the input hex via PrimeNG's `palette()`
228
+ * helper, hands the full ramp to `updatePrimaryPalette()`, and writes
229
+ * stops 50–900 onto `documentElement.style` so the SCSS layer's
230
+ * `var(--tenant-*)` references resolve to the tenant's color.
231
+ *
232
+ * Call this whenever the active tenant changes (e.g., from an effect
233
+ * watching `tenantService.storeConfig().primaryColor`).
234
+ */
235
+ setTenantAccent(hex: string): void;
138
236
  static ɵfac: i0.ɵɵFactoryDeclaration<EfThemeConfigService, never>;
139
237
  static ɵprov: i0.ɵɵInjectableDeclaration<EfThemeConfigService>;
140
238
  }
@@ -143,13 +241,31 @@ declare class EfThemeConfigService {
143
241
  * Default Elasticias theme configuration for PrimeNG.
144
242
  * Uses the Noir preset (surface-based primary colors) with dark mode support.
145
243
  *
244
+ * @deprecated Migrate to {@link EfComptoirTheme} as part of Phase 5 of the
245
+ * design-system plan. Will be removed once all consumers have moved.
246
+ */
247
+ declare const EfTheme: {
248
+ preset: _primeuix_themes_types.Preset<_primeuix_themes_aura_base.AuraBaseDesignTokens>;
249
+ options: {
250
+ darkModeSelector: string;
251
+ };
252
+ };
253
+
254
+ /**
255
+ * Comptoir theme configuration for PrimeNG (Phase 1 / design-system v0.2).
256
+ * Surface palette = ink ramp; primary palette = tenant accent
257
+ * (runtime-driven via `EfThemeConfigService.setTenantAccent(hex)`).
258
+ *
146
259
  * Usage with providePrimeNG:
147
260
  * ```ts
148
- * providePrimeNG({ theme: EfTheme, ripple: true })
261
+ * providePrimeNG({ theme: EfComptoirTheme, ripple: true })
149
262
  * ```
263
+ *
264
+ * Pair with `state.preset = 'Comptoir'` so the body class becomes
265
+ * `theme-comptoir` (avoids legacy `theme-modern` radius overrides).
150
266
  */
151
- declare const EfTheme: {
152
- preset: _primeuix_themes_types.Preset<_primeuix_themes_aura_base.AuraBaseDesignTokens>;
267
+ declare const EfComptoirTheme: {
268
+ preset: _primeuix_themes_types.Preset<_primeuix_themes_lara_base.LaraBaseDesignTokens>;
153
269
  options: {
154
270
  darkModeSelector: string;
155
271
  };
@@ -188,5 +304,205 @@ declare const PRIMENG_FR_LOCALE: Translation;
188
304
  */
189
305
  declare const PRIMENG_AR_LOCALE: Translation;
190
306
 
191
- export { CacheService, ConfirmDialogService, DEFAULT_APP_STATE, EfTheme, EfThemeConfigService, LoaderService, PRIMENG_AR_LOCALE, PRIMENG_EN_LOCALE, PRIMENG_FR_LOCALE, ToastService, hasScreenPermission, screenGuard };
192
- export type { AppState, ScreenGuardConfig };
307
+ /**
308
+ * The eight ERP modules surfaced by Comptoir. New modules require
309
+ * a matching `--m-{id}` token in the SCSS layer (libs/ui/src/lib/_comptoir.scss)
310
+ * and a labelKey in the consuming app's i18n bundles.
311
+ */
312
+ type EfModuleId = 'sales' | 'purchase' | 'stock' | 'pos' | 'marketing' | 'store' | 'finance' | 'admin';
313
+ type EfNavAction = 'read' | 'write' | 'admin';
314
+ interface EfNavItem {
315
+ /** Stable identifier — usually matches the screen code (e.g. `Users`, `SalesOrders`). */
316
+ id: string;
317
+ labelKey: string;
318
+ icon?: string;
319
+ route: string;
320
+ /** Minimum permission level required to render this item. Defaults to `read`. */
321
+ requiredAction?: EfNavAction;
322
+ /**
323
+ * Optional count chip rendered after the label (e.g. `Commandes ⟨14⟩`).
324
+ * Apps typically derive this from a service signal and patch the
325
+ * registry — it can be number, string, or anything stringifiable.
326
+ */
327
+ badge?: string | number;
328
+ }
329
+ interface EfNavSection {
330
+ id: string;
331
+ labelKey?: string;
332
+ items: EfNavItem[];
333
+ }
334
+ interface EfModule {
335
+ id: EfModuleId;
336
+ labelKey: string;
337
+ /** PrimeNG icon class used by `ef-module-rail`, e.g. `pi pi-shopping-bag`. */
338
+ icon: string;
339
+ /**
340
+ * CSS custom-property name (without the leading `--`) that the rail
341
+ * applies to the module-current accent. Always `m-{id}` and resolves
342
+ * via the SCSS layer's `[data-module]` selectors at runtime.
343
+ */
344
+ accent: `m-${EfModuleId}`;
345
+ defaultRoute: string;
346
+ navSections: EfNavSection[];
347
+ /**
348
+ * Logical grouping for rail divider placement. `ef-module-rail` renders
349
+ * a 1px divider between two consecutive visible modules whose `group`
350
+ * differs. Free string — `'operations' | 'commerce' | 'admin'` is the
351
+ * conventional set but apps can use anything stable.
352
+ */
353
+ group?: string;
354
+ }
355
+ /**
356
+ * Default skeleton for the eight ERP modules. Apps either consume this
357
+ * directly via `EF_MODULES_TOKEN` or extend it with their own `navSections`.
358
+ *
359
+ * Phase 1 ships the metadata only — `ef-module-rail` (Phase 2) uses
360
+ * `id` / `labelKey` / `icon` / `accent` / `defaultRoute`. `navSections`
361
+ * are populated per-app as each module's screens land in Phase 4-5.
362
+ */
363
+ declare const EF_MODULES: ReadonlyArray<EfModule>;
364
+ /**
365
+ * DI token that the shell components (`ef-module-rail`, `ef-module-side`)
366
+ * read from. Apps provide their own definition (typically extending
367
+ * `EF_MODULES` with populated `navSections`):
368
+ *
369
+ * ```ts
370
+ * providers: [
371
+ * { provide: EF_MODULES_TOKEN, useValue: APP_MODULES }
372
+ * ]
373
+ * ```
374
+ */
375
+ declare const EF_MODULES_TOKEN: InjectionToken<readonly EfModule[]>;
376
+
377
+ /**
378
+ * Single source of truth for "which ERP module is active right now."
379
+ *
380
+ * Watches the Router and matches the current URL against each module's
381
+ * `defaultRoute`. The longest matching prefix wins, so `/operations/sales`
382
+ * resolves to `sales` even though `/operations` could in theory match
383
+ * something shorter.
384
+ *
385
+ * Consumers — `ef-module-rail`, `ef-module-side`, `ef-app-main`,
386
+ * `ef-page-head` — read `activeModule()` and derive their state from it.
387
+ *
388
+ * Apps that navigate programmatically without a URL change (rare) can
389
+ * call `setActiveModule(id)` to override.
390
+ */
391
+ declare class EfActiveModuleService {
392
+ private readonly router;
393
+ private readonly modules;
394
+ private readonly destroyRef;
395
+ private readonly _activeModule;
396
+ private readonly _activeNavItem;
397
+ readonly activeModule: i0.Signal<EfModule | null>;
398
+ readonly activeModuleId: i0.Signal<EfModuleId | null>;
399
+ readonly activeNavItem: i0.Signal<EfNavItem | null>;
400
+ constructor();
401
+ /**
402
+ * Force the active module. Most apps don't need this — the router
403
+ * subscription keeps `activeModule()` in sync automatically.
404
+ */
405
+ setActiveModule(id: EfModuleId | null): void;
406
+ private resolveFromUrl;
407
+ private findNavItem;
408
+ /**
409
+ * Every URL prefix that should resolve back to this module: the
410
+ * `defaultRoute` plus every nav item route. Modules whose nav items
411
+ * span multiple URL prefixes (e.g. sales spread across `/operations/sales`
412
+ * and `/parameters/sales`) need this to stay active across all of them.
413
+ */
414
+ private routesFor;
415
+ static ɵfac: i0.ɵɵFactoryDeclaration<EfActiveModuleService, never>;
416
+ static ɵprov: i0.ɵɵInjectableDeclaration<EfActiveModuleService>;
417
+ }
418
+
419
+ type EfViewport = 'mobile' | 'tablet' | 'desktop';
420
+ /**
421
+ * Emits the current viewport tier based on `window.matchMedia` breakpoints.
422
+ *
423
+ * Breakpoints come from `libs/tokens/targets.json`:
424
+ * - mobile: ≤ 767px
425
+ * - tablet: 768 – 1279px
426
+ * - desktop: ≥ 1280px
427
+ *
428
+ * SSR-safe: returns `'desktop'` when `window` is unavailable.
429
+ *
430
+ * Usage:
431
+ * ```ts
432
+ * private viewport = inject(EfViewportService);
433
+ *
434
+ * isMobile = computed(() => this.viewport.current() === 'mobile');
435
+ * ```
436
+ */
437
+ declare class EfViewportService {
438
+ private readonly document;
439
+ private readonly platformId;
440
+ private readonly destroyRef;
441
+ private readonly _current;
442
+ readonly current: i0.Signal<EfViewport>;
443
+ readonly isMobile: i0.Signal<boolean>;
444
+ readonly isTablet: i0.Signal<boolean>;
445
+ readonly isDesktop: i0.Signal<boolean>;
446
+ constructor();
447
+ static ɵfac: i0.ɵɵFactoryDeclaration<EfViewportService, never>;
448
+ static ɵprov: i0.ɵɵInjectableDeclaration<EfViewportService>;
449
+ }
450
+
451
+ type EfPermissionLevel = 'none' | 'read' | 'write' | 'admin';
452
+ interface EfModulePermission {
453
+ module: EfModuleId;
454
+ level: EfPermissionLevel;
455
+ }
456
+ /**
457
+ * Module-scoped permission service.
458
+ *
459
+ * Apps populate it from their auth bootstrap once the user's profile is
460
+ * loaded — typically via `setPermissions()` or by providing a custom
461
+ * source signal:
462
+ *
463
+ * ```ts
464
+ * // bootstrap.ts
465
+ * const perms = inject(EfPermissionService);
466
+ * perms.setPermissions(profile.permissions);
467
+ * ```
468
+ *
469
+ * The service is the single source of truth for shell components
470
+ * (`ef-module-rail`, `ef-module-side`, `*efCan` directive) and routing
471
+ * defaults. Once the screens/menus → Mongo migration lands, the
472
+ * permissions list will be served denormalized on the user/profile
473
+ * document and consumed here without a join.
474
+ */
475
+ declare class EfPermissionService {
476
+ private readonly modules;
477
+ private readonly _permissions;
478
+ readonly permissions: Signal<readonly EfModulePermission[]>;
479
+ /**
480
+ * Replace the current permission set. Pass `[]` to clear (e.g., on logout).
481
+ */
482
+ setPermissions(perms: ReadonlyArray<EfModulePermission>): void;
483
+ /**
484
+ * The level granted to the current user for a given module.
485
+ * Returns `'none'` if the module is not in the permission set.
486
+ */
487
+ level(module: EfModuleId): EfPermissionLevel;
488
+ /**
489
+ * Whether the current user can perform `action` on `module`.
490
+ * Levels are hierarchical: `admin` > `write` > `read` > `none`.
491
+ */
492
+ can(module: EfModuleId, action?: Exclude<EfPermissionLevel, 'none'>): boolean;
493
+ /**
494
+ * The list of modules the user can read, in registry order.
495
+ * Used by `ef-module-rail` to decide which icons render.
496
+ */
497
+ readonly visibleModules: Signal<ReadonlyArray<EfModule>>;
498
+ /**
499
+ * The first visible module — the default landing module after login.
500
+ * Returns `null` when the user has no modules.
501
+ */
502
+ readonly defaultModule: Signal<EfModule | null>;
503
+ static ɵfac: i0.ɵɵFactoryDeclaration<EfPermissionService, never>;
504
+ static ɵprov: i0.ɵɵInjectableDeclaration<EfPermissionService>;
505
+ }
506
+
507
+ export { CacheService, ConfirmDialogService, DEFAULT_APP_STATE, EF_MODULES, EF_MODULES_TOKEN, EfActiveModuleService, EfComptoirTheme, EfPermissionService, EfTheme, EfThemeConfigService, EfToastService, EfViewportService, LoaderService, PRIMENG_AR_LOCALE, PRIMENG_EN_LOCALE, PRIMENG_FR_LOCALE, EfToastService as ToastService, hasScreenPermission, screenGuard };
508
+ export type { AppState, EfModule, EfModuleId, EfModulePermission, EfNavAction, EfNavItem, EfNavSection, EfPermissionLevel, EfToast, EfToastAction, EfToastOptions, EfToastSeverity, EfViewport, ScreenGuardConfig };