@vention/machine-apps-components 0.3.22 → 0.4.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.
Files changed (36) hide show
  1. package/index.esm.js +2124 -208
  2. package/package.json +8 -2
  3. package/src/constants/z-index.d.ts +8 -0
  4. package/src/contexts/i18n-context.d.ts +12 -0
  5. package/src/contexts/i18n-provider.d.ts +13 -0
  6. package/src/hooks/use-auto-scroll-input.d.ts +6 -0
  7. package/src/hooks/use-i18n.d.ts +19 -0
  8. package/src/i18n/config.d.ts +8 -0
  9. package/src/i18n/locales/de.d.ts +131 -0
  10. package/src/i18n/locales/en.d.ts +131 -0
  11. package/src/i18n/locales/es.d.ts +131 -0
  12. package/src/i18n/locales/fr.d.ts +131 -0
  13. package/src/i18n/utils.d.ts +13 -0
  14. package/src/index.d.ts +14 -2
  15. package/src/lib/action-button/action-button.d.ts +14 -0
  16. package/src/lib/action-button/action-button.stories.d.ts +15 -0
  17. package/src/lib/file-upload-panel/file-upload-panel.d.ts +23 -0
  18. package/src/lib/file-upload-panel/file-upload-panel.stories.d.ts +12 -0
  19. package/src/lib/i18n-settings/i18n-settings.d.ts +8 -0
  20. package/src/lib/i18n-settings/i18n-settings.stories.d.ts +7 -0
  21. package/src/lib/logs/logs-panel.stories.d.ts +6 -0
  22. package/src/lib/navigation-bar/navigation-bar.d.ts +3 -0
  23. package/src/lib/navigation-bar/navigation-bar.stories.d.ts +6 -0
  24. package/src/lib/navigation-bar/password-protection-modal.stories.d.ts +6 -0
  25. package/src/lib/product-form-list/product-form-list.d.ts +27 -0
  26. package/src/lib/product-form-list/product-form-list.stories.d.ts +18 -0
  27. package/src/lib/settings-page/settings-page.d.ts +11 -0
  28. package/src/lib/settings-page/settings-page.stories.d.ts +9 -0
  29. package/src/lib/sidebar/sidebar.d.ts +4 -1
  30. package/src/lib/sidebar/sidebar.stories.d.ts +8 -0
  31. package/src/lib/status-top-bar/status-top-bar.d.ts +2 -0
  32. package/src/lib/status-top-bar/status-top-bar.stories.d.ts +10 -0
  33. package/src/lib/step-progress-circle/step-progress-circle.d.ts +14 -0
  34. package/src/lib/step-progress-circle/step-progress-circle.stories.d.ts +16 -0
  35. package/src/test-utils.d.ts +7 -6
  36. package/src/types/user-level.d.ts +6 -0
package/index.esm.js CHANGED
@@ -1,29 +1,990 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
- import { useLocation, useNavigate } from 'react-router-dom';
3
- import { useTheme, Typography, Box, Button, InputAdornment, IconButton, Divider, TableContainer, Paper, Table, TableHead, TableRow, TableCell, TableBody } from '@mui/material';
4
- import { tss } from 'tss-react/mui';
5
- import { COLORS, VentionModalBase, VentionIcon, VentionTextInput, VentionButton, VentionStatusIndicator, VentionSpinner, VentionAlert, VentionSelect } from '@vention/machine-ui';
6
- import React, { useState, useEffect, memo, useRef, forwardRef, useCallback, useImperativeHandle } from 'react';
2
+ import React, { createContext, useState, useEffect, useCallback, useContext, useMemo, memo, useRef, forwardRef, useImperativeHandle } from 'react';
3
+ import i18n from 'i18next';
4
+ import { initReactI18next, useTranslation } from 'react-i18next';
5
+ import LanguageDetector from 'i18next-browser-languagedetector';
7
6
  import dayjs from 'dayjs';
7
+ import utc from 'dayjs/plugin/utc';
8
+ import timezone from 'dayjs/plugin/timezone';
9
+ import 'dayjs/locale/en';
10
+ import 'dayjs/locale/fr';
11
+ import 'dayjs/locale/de';
12
+ import 'dayjs/locale/es';
13
+ import { Box, useTheme, Typography, Button, InputAdornment, IconButton, TableContainer, Paper, Table, TableHead, TableRow, TableCell, TableBody, List, ListItem, Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material';
14
+ import { VentionSelect, VentionModalBase, VentionIcon, VentionTextInput, COLORS, VentionButton, VentionStatusIndicator, VentionSpinner, VentionAlert, VentionDropZone, VentionUploadFile, VentionIconButton } from '@vention/machine-ui';
15
+ import { tss } from 'tss-react/mui';
16
+ import { useLocation, useNavigate } from 'react-router-dom';
17
+
18
+ const I18nContext = createContext(undefined);
19
+
20
+ var enTranslations = {
21
+ common: {
22
+ units: {
23
+ mm: "mm",
24
+ in: "in",
25
+ metric: "Metric",
26
+ imperial: "Imperial"
27
+ },
28
+ settings: {
29
+ unitSystem: "Unit System",
30
+ timezone: "Timezone",
31
+ autoDetect: "Auto-detect",
32
+ locale: "Language"
33
+ }
34
+ },
35
+ actionButton: {
36
+ home: "Home",
37
+ start: "Start",
38
+ stop: "Stop",
39
+ pause: "Pause",
40
+ reset: "Reset",
41
+ clear: "Clear",
42
+ run: "Run",
43
+ resume: "Resume"
44
+ },
45
+ logs: {
46
+ table: {
47
+ date: "Date",
48
+ type: "Type",
49
+ code: "Code",
50
+ message: "Message",
51
+ description: "Description"
52
+ },
53
+ type: {
54
+ error: "Error",
55
+ warning: "Warning",
56
+ info: "Info"
57
+ },
58
+ emptyState: {
59
+ noLogs: "You have no logs"
60
+ },
61
+ loading: "Loading logs...",
62
+ loadingMore: "Loading more logs...",
63
+ noMoreLogs: "No more logs to load",
64
+ error: {
65
+ loading: "Error loading logs"
66
+ },
67
+ filter: {
68
+ from: "From",
69
+ to: "To",
70
+ type: "Type",
71
+ sort: "Sort",
72
+ reset: "Reset",
73
+ typeOptions: {
74
+ all: "All",
75
+ error: "Error",
76
+ warning: "Warning",
77
+ info: "Info"
78
+ },
79
+ sortOptions: {
80
+ latest: "From latest",
81
+ oldest: "From oldest"
82
+ },
83
+ chooseType: "Choose type"
84
+ }
85
+ },
86
+ navigation: {
87
+ pages: {
88
+ home: "Home",
89
+ operation: "Operation",
90
+ logs: "Logs",
91
+ calibration: "Calibration",
92
+ settings: "Settings"
93
+ },
94
+ bar: {
95
+ controlCenter: "Control Center",
96
+ support: "Support"
97
+ },
98
+ modal: {
99
+ exit: {
100
+ title: "You are about to exit the application",
101
+ message: "Please make sure your changes are saved before you leave the app."
102
+ },
103
+ goToControlCenter: "Go to Control Center",
104
+ goToRemoteSupport: "Go to Remote Support",
105
+ password: {
106
+ title: "Administrator login required",
107
+ label: "Password",
108
+ error: "Incorrect password",
109
+ confirm: "Confirm"
110
+ }
111
+ }
112
+ },
113
+ pagination: {
114
+ page: "Page",
115
+ of: "of"
116
+ },
117
+ fileUploadPanel: {
118
+ defaultTitle: "Upload Files",
119
+ dropzoneTitle: "Drop files here or click to browse",
120
+ defaultFileCriteria: "All file types accepted"
121
+ },
122
+ stepProgressCircle: {
123
+ title: {
124
+ progress: "Progress",
125
+ manufacturing: "Manufacturing",
126
+ processing: "Processing",
127
+ uploading: "Uploading",
128
+ notstarted: "Not Started",
129
+ ready: "Ready",
130
+ inprogress: "In Progress",
131
+ complete: "Complete",
132
+ loading: "Loading",
133
+ waiting: "Waiting",
134
+ warning: "Warning",
135
+ error: "Error"
136
+ }
137
+ },
138
+ productFormList: {
139
+ add: "Add",
140
+ cancel: "Cancel",
141
+ save: "Save",
142
+ create: "Create",
143
+ modify: "Edit",
144
+ new: "New",
145
+ delete: "Delete",
146
+ deleteConfirmation: "Are you sure you want to delete this item?",
147
+ emptyState: "No items yet. Click Add to create one."
148
+ }
149
+ };
150
+
151
+ var frTranslations = {
152
+ common: {
153
+ units: {
154
+ mm: "mm",
155
+ in: "po",
156
+ metric: "Métrique",
157
+ imperial: "Impérial"
158
+ },
159
+ settings: {
160
+ unitSystem: "Système d'unités",
161
+ timezone: "Fuseau horaire",
162
+ autoDetect: "Détection automatique",
163
+ locale: "Langue"
164
+ }
165
+ },
166
+ actionButton: {
167
+ home: "Origine",
168
+ start: "Démarrer",
169
+ stop: "Arrêter",
170
+ pause: "Pause",
171
+ reset: "Réinitialiser",
172
+ clear: "Effacer",
173
+ run: "Exécuter",
174
+ resume: "Reprendre"
175
+ },
176
+ logs: {
177
+ table: {
178
+ date: "Date",
179
+ type: "Type",
180
+ code: "Code",
181
+ message: "Message",
182
+ description: "Description"
183
+ },
184
+ type: {
185
+ error: "Erreur",
186
+ warning: "Avertissement",
187
+ info: "Information"
188
+ },
189
+ emptyState: {
190
+ noLogs: "Vous n'avez aucun journal"
191
+ },
192
+ loading: "Chargement des journaux...",
193
+ loadingMore: "Chargement de plus de journaux...",
194
+ noMoreLogs: "Aucun autre journal à charger",
195
+ error: {
196
+ loading: "Erreur lors du chargement des journaux"
197
+ },
198
+ filter: {
199
+ from: "De",
200
+ to: "À",
201
+ type: "Type",
202
+ sort: "Trier",
203
+ reset: "Réinitialiser",
204
+ typeOptions: {
205
+ all: "Tous",
206
+ error: "Erreur",
207
+ warning: "Avertissement",
208
+ info: "Information"
209
+ },
210
+ sortOptions: {
211
+ latest: "Du plus récent",
212
+ oldest: "Du plus ancien"
213
+ },
214
+ chooseType: "Choisir le type"
215
+ }
216
+ },
217
+ navigation: {
218
+ pages: {
219
+ home: "Accueil",
220
+ operation: "Opération",
221
+ logs: "Journaux",
222
+ calibration: "Calibration",
223
+ settings: "Paramètres"
224
+ },
225
+ bar: {
226
+ controlCenter: "Centre de contrôle",
227
+ support: "Support"
228
+ },
229
+ modal: {
230
+ exit: {
231
+ title: "Vous êtes sur le point de quitter l'application",
232
+ message: "Veuillez vous assurer que vos modifications sont enregistrées avant de quitter l'application."
233
+ },
234
+ goToControlCenter: "Aller au Centre de contrôle",
235
+ goToRemoteSupport: "Aller au Support à distance",
236
+ password: {
237
+ title: "Connexion administrateur requise",
238
+ label: "Mot de passe",
239
+ error: "Mot de passe incorrect",
240
+ confirm: "Confirmer"
241
+ }
242
+ }
243
+ },
244
+ pagination: {
245
+ page: "Page",
246
+ of: "sur"
247
+ },
248
+ fileUploadPanel: {
249
+ defaultTitle: "Téléverser des fichiers",
250
+ dropzoneTitle: "Déposez les fichiers ici ou cliquez pour parcourir",
251
+ defaultFileCriteria: "Tous les types de fichiers acceptés"
252
+ },
253
+ stepProgressCircle: {
254
+ title: {
255
+ progress: "Progression",
256
+ manufacturing: "Fabrication",
257
+ processing: "Traitement",
258
+ uploading: "Téléversement",
259
+ notstarted: "Non démarré",
260
+ ready: "Prêt",
261
+ inprogress: "En cours",
262
+ complete: "Terminé",
263
+ loading: "Chargement",
264
+ waiting: "En attente",
265
+ warning: "Avertissement",
266
+ error: "Erreur"
267
+ }
268
+ },
269
+ productFormList: {
270
+ add: "Ajouter",
271
+ cancel: "Annuler",
272
+ save: "Sauvegarder",
273
+ create: "Créer",
274
+ modify: "Modifier",
275
+ new: "Nouveau",
276
+ delete: "Supprimer",
277
+ deleteConfirmation: "Êtes-vous sûr de vouloir supprimer cet élément?",
278
+ emptyState: "Aucun élément. Cliquez sur Ajouter pour en créer un."
279
+ }
280
+ };
281
+
282
+ var deTranslations = {
283
+ common: {
284
+ units: {
285
+ mm: "mm",
286
+ in: "Zoll",
287
+ metric: "Metrisch",
288
+ imperial: "Imperial"
289
+ },
290
+ settings: {
291
+ unitSystem: "Einheitensystem",
292
+ timezone: "Zeitzone",
293
+ autoDetect: "Automatisch erkennen",
294
+ locale: "Sprache"
295
+ }
296
+ },
297
+ actionButton: {
298
+ home: "Referenzfahrt",
299
+ start: "Starten",
300
+ stop: "Stoppen",
301
+ pause: "Pause",
302
+ reset: "Zurücksetzen",
303
+ clear: "Löschen",
304
+ run: "Ausführen",
305
+ resume: "Fortsetzen"
306
+ },
307
+ logs: {
308
+ table: {
309
+ date: "Datum",
310
+ type: "Typ",
311
+ code: "Code",
312
+ message: "Nachricht",
313
+ description: "Beschreibung"
314
+ },
315
+ type: {
316
+ error: "Fehler",
317
+ warning: "Warnung",
318
+ info: "Information"
319
+ },
320
+ emptyState: {
321
+ noLogs: "Sie haben keine Protokolle"
322
+ },
323
+ loading: "Protokolle werden geladen...",
324
+ loadingMore: "Weitere Protokolle werden geladen...",
325
+ noMoreLogs: "Keine weiteren Protokolle zum Laden",
326
+ error: {
327
+ loading: "Fehler beim Laden der Protokolle"
328
+ },
329
+ filter: {
330
+ from: "Von",
331
+ to: "Bis",
332
+ type: "Typ",
333
+ sort: "Sortieren",
334
+ reset: "Zurücksetzen",
335
+ typeOptions: {
336
+ all: "Alle",
337
+ error: "Fehler",
338
+ warning: "Warnung",
339
+ info: "Information"
340
+ },
341
+ sortOptions: {
342
+ latest: "Neueste zuerst",
343
+ oldest: "Älteste zuerst"
344
+ },
345
+ chooseType: "Typ auswählen"
346
+ }
347
+ },
348
+ navigation: {
349
+ pages: {
350
+ home: "Startseite",
351
+ operation: "Betrieb",
352
+ logs: "Protokolle",
353
+ calibration: "Kalibrierung",
354
+ settings: "Einstellungen"
355
+ },
356
+ bar: {
357
+ controlCenter: "Kontrollzentrum",
358
+ support: "Support"
359
+ },
360
+ modal: {
361
+ exit: {
362
+ title: "Sie sind dabei, die Anwendung zu verlassen",
363
+ message: "Bitte stellen Sie sicher, dass Ihre Änderungen gespeichert sind, bevor Sie die App verlassen."
364
+ },
365
+ goToControlCenter: "Zum Kontrollzentrum gehen",
366
+ goToRemoteSupport: "Zum Remote-Support gehen",
367
+ password: {
368
+ title: "Administrator-Anmeldung erforderlich",
369
+ label: "Passwort",
370
+ error: "Falsches Passwort",
371
+ confirm: "Bestätigen"
372
+ }
373
+ }
374
+ },
375
+ pagination: {
376
+ page: "Seite",
377
+ of: "von"
378
+ },
379
+ fileUploadPanel: {
380
+ defaultTitle: "Dateien hochladen",
381
+ dropzoneTitle: "Dateien hier ablegen oder klicken zum Durchsuchen",
382
+ defaultFileCriteria: "Alle Dateitypen akzeptiert"
383
+ },
384
+ stepProgressCircle: {
385
+ title: {
386
+ progress: "Fortschritt",
387
+ manufacturing: "Fertigung",
388
+ processing: "Verarbeitung",
389
+ uploading: "Hochladen",
390
+ notstarted: "Nicht gestartet",
391
+ ready: "Bereit",
392
+ inprogress: "In Bearbeitung",
393
+ complete: "Abgeschlossen",
394
+ loading: "Laden",
395
+ waiting: "Warten",
396
+ warning: "Warnung",
397
+ error: "Fehler"
398
+ }
399
+ },
400
+ productFormList: {
401
+ add: "Hinzufügen",
402
+ cancel: "Abbrechen",
403
+ save: "Speichern",
404
+ create: "Erstellen",
405
+ modify: "Bearbeiten",
406
+ new: "Neu",
407
+ delete: "Löschen",
408
+ deleteConfirmation: "Sind Sie sicher, dass Sie dieses Element löschen möchten?",
409
+ emptyState: "Noch keine Elemente. Klicken Sie auf Hinzufügen, um eines zu erstellen."
410
+ }
411
+ };
412
+
413
+ var esTranslations = {
414
+ common: {
415
+ units: {
416
+ mm: "mm",
417
+ in: "pulg",
418
+ metric: "Métrico",
419
+ imperial: "Imperial"
420
+ },
421
+ settings: {
422
+ unitSystem: "Sistema de unidades",
423
+ timezone: "Zona horaria",
424
+ autoDetect: "Detección automática",
425
+ locale: "Idioma"
426
+ }
427
+ },
428
+ actionButton: {
429
+ home: "Origen",
430
+ start: "Iniciar",
431
+ stop: "Detener",
432
+ pause: "Pausar",
433
+ reset: "Restablecer",
434
+ clear: "Limpiar",
435
+ run: "Ejecutar",
436
+ resume: "Reanudar"
437
+ },
438
+ logs: {
439
+ table: {
440
+ date: "Fecha",
441
+ type: "Tipo",
442
+ code: "Código",
443
+ message: "Mensaje",
444
+ description: "Descripción"
445
+ },
446
+ type: {
447
+ error: "Error",
448
+ warning: "Advertencia",
449
+ info: "Información"
450
+ },
451
+ emptyState: {
452
+ noLogs: "No tienes registros"
453
+ },
454
+ loading: "Cargando registros...",
455
+ loadingMore: "Cargando más registros...",
456
+ noMoreLogs: "No hay más registros para cargar",
457
+ error: {
458
+ loading: "Error al cargar los registros"
459
+ },
460
+ filter: {
461
+ from: "Desde",
462
+ to: "Hasta",
463
+ type: "Tipo",
464
+ sort: "Ordenar",
465
+ reset: "Restablecer",
466
+ typeOptions: {
467
+ all: "Todos",
468
+ error: "Error",
469
+ warning: "Advertencia",
470
+ info: "Información"
471
+ },
472
+ sortOptions: {
473
+ latest: "Más recientes",
474
+ oldest: "Más antiguos"
475
+ },
476
+ chooseType: "Elegir tipo"
477
+ }
478
+ },
479
+ navigation: {
480
+ pages: {
481
+ home: "Inicio",
482
+ operation: "Operación",
483
+ logs: "Registros",
484
+ calibration: "Calibración",
485
+ settings: "Ajustes"
486
+ },
487
+ bar: {
488
+ controlCenter: "Centro de control",
489
+ support: "Soporte"
490
+ },
491
+ modal: {
492
+ exit: {
493
+ title: "Estás a punto de salir de la aplicación",
494
+ message: "Por favor, asegúrate de que tus cambios estén guardados antes de salir de la aplicación."
495
+ },
496
+ goToControlCenter: "Ir al Centro de control",
497
+ goToRemoteSupport: "Ir al Soporte remoto",
498
+ password: {
499
+ title: "Se requiere inicio de sesión de administrador",
500
+ label: "Contraseña",
501
+ error: "Contraseña incorrecta",
502
+ confirm: "Confirmar"
503
+ }
504
+ }
505
+ },
506
+ pagination: {
507
+ page: "Página",
508
+ of: "de"
509
+ },
510
+ fileUploadPanel: {
511
+ defaultTitle: "Subir archivos",
512
+ dropzoneTitle: "Suelta los archivos aquí o haz clic para explorar",
513
+ defaultFileCriteria: "Se aceptan todos los tipos de archivos"
514
+ },
515
+ stepProgressCircle: {
516
+ title: {
517
+ progress: "Progreso",
518
+ manufacturing: "Fabricación",
519
+ processing: "Procesamiento",
520
+ uploading: "Subiendo",
521
+ notstarted: "No iniciado",
522
+ ready: "Listo",
523
+ inprogress: "En progreso",
524
+ complete: "Completo",
525
+ loading: "Cargando",
526
+ waiting: "Esperando",
527
+ warning: "Advertencia",
528
+ error: "Error"
529
+ }
530
+ },
531
+ productFormList: {
532
+ add: "Agregar",
533
+ cancel: "Cancelar",
534
+ save: "Guardar",
535
+ create: "Crear",
536
+ modify: "Editar",
537
+ new: "Nuevo",
538
+ delete: "Eliminar",
539
+ deleteConfirmation: "¿Está seguro de que desea eliminar este elemento?",
540
+ emptyState: "Aún no hay elementos. Haz clic en Agregar para crear uno."
541
+ }
542
+ };
543
+
544
+ dayjs.extend(utc);
545
+ dayjs.extend(timezone);
546
+ if (!i18n.isInitialized) {
547
+ const getStoredTimezone = function getStoredTimezone() {
548
+ if (typeof window === "undefined") return "auto";
549
+ try {
550
+ const stored = localStorage.getItem("machine-apps-components:timezone");
551
+ return stored || "auto";
552
+ } catch (_a) {
553
+ return "auto";
554
+ }
555
+ };
556
+ const initialTimezone = getStoredTimezone();
557
+ if (initialTimezone !== "auto" && typeof window !== "undefined") {
558
+ try {
559
+ dayjs.tz.setDefault(initialTimezone);
560
+ } catch (error) {
561
+ console.warn("i18n: Failed to set default timezone", error);
562
+ }
563
+ }
564
+ i18n.use(LanguageDetector).use(initReactI18next).init({
565
+ resources: {
566
+ en: {
567
+ translation: enTranslations
568
+ },
569
+ fr: {
570
+ translation: frTranslations
571
+ },
572
+ de: {
573
+ translation: deTranslations
574
+ },
575
+ es: {
576
+ translation: esTranslations
577
+ }
578
+ },
579
+ fallbackLng: "en",
580
+ defaultNS: "translation",
581
+ interpolation: {
582
+ escapeValue: false
583
+ },
584
+ detection: {
585
+ order: ["localStorage", "navigator"],
586
+ lookupLocalStorage: "machine-apps-components:locale",
587
+ caches: ["localStorage"]
588
+ }
589
+ });
590
+ i18n.on("languageChanged", function handleLanguageChanged(lng) {
591
+ const dayjsLocale = lng.split("-")[0];
592
+ try {
593
+ dayjs.locale(dayjsLocale);
594
+ } catch (error) {
595
+ console.warn("i18n: Failed to set dayjs locale", error);
596
+ }
597
+ });
598
+ }
599
+ const getSupportedLanguages = function getSupportedLanguages() {
600
+ try {
601
+ if (!i18n.isInitialized) {
602
+ return ["en", "fr", "de", "es"];
603
+ }
604
+ const resources = i18n.options.resources || {};
605
+ const languages = Object.keys(resources);
606
+ return languages.length > 0 ? languages : ["en", "fr", "de", "es"];
607
+ } catch (error) {
608
+ console.warn("Error getting supported languages:", error);
609
+ return ["en", "fr", "de", "es"];
610
+ }
611
+ };
612
+ const getLanguageDisplayName = function getLanguageDisplayName(code) {
613
+ const languageNames = {
614
+ en: "English",
615
+ fr: "Français",
616
+ es: "Español",
617
+ de: "Deutsch"
618
+ };
619
+ return languageNames[code] || code.toUpperCase();
620
+ };
621
+
622
+ const STORAGE_KEYS = {
623
+ locale: "machine-apps-components:locale",
624
+ unitSystem: "machine-apps-components:unitSystem",
625
+ timezone: "machine-apps-components:timezone"
626
+ };
627
+ const normalizeLocale = function normalizeLocale(locale, supportedLanguages) {
628
+ const normalized = locale.split("-")[0].toLowerCase();
629
+ if (supportedLanguages.includes(normalized)) {
630
+ return normalized;
631
+ }
632
+ return supportedLanguages.length > 0 ? supportedLanguages[0] : "en";
633
+ };
634
+ const I18nProvider = function I18nProvider({
635
+ children,
636
+ defaultLocale = "en",
637
+ defaultUnitSystem = "metric",
638
+ defaultTimezone = "auto",
639
+ supportedLanguages = ["en", "fr", "de", "es"]
640
+ }) {
641
+ const [locale, setLocaleState] = useState(function getInitialLocale() {
642
+ if (typeof window === "undefined") return normalizeLocale(defaultLocale, supportedLanguages);
643
+ try {
644
+ const stored = localStorage.getItem(STORAGE_KEYS.locale);
645
+ if (stored) {
646
+ return normalizeLocale(stored, supportedLanguages);
647
+ }
648
+ return normalizeLocale(defaultLocale, supportedLanguages);
649
+ } catch (_a) {
650
+ return normalizeLocale(defaultLocale, supportedLanguages);
651
+ }
652
+ });
653
+ const [unitSystem, setUnitSystemState] = useState(function getInitialUnitSystem() {
654
+ if (typeof window === "undefined") return defaultUnitSystem;
655
+ try {
656
+ const stored = localStorage.getItem(STORAGE_KEYS.unitSystem);
657
+ if (stored === "metric" || stored === "imperial") {
658
+ return stored;
659
+ }
660
+ return defaultUnitSystem;
661
+ } catch (_a) {
662
+ return defaultUnitSystem;
663
+ }
664
+ });
665
+ const [timezone, setTimezoneState] = useState(function getInitialTimezone() {
666
+ if (typeof window === "undefined") return defaultTimezone;
667
+ try {
668
+ const stored = localStorage.getItem(STORAGE_KEYS.timezone);
669
+ return stored || defaultTimezone;
670
+ } catch (_a) {
671
+ return defaultTimezone;
672
+ }
673
+ });
674
+ useEffect(function syncI18nLocale() {
675
+ const normalizedLocale = normalizeLocale(locale, supportedLanguages);
676
+ i18n.changeLanguage(normalizedLocale);
677
+ if (typeof window !== "undefined") {
678
+ localStorage.setItem(STORAGE_KEYS.locale, normalizedLocale);
679
+ }
680
+ }, [locale, supportedLanguages]);
681
+ useEffect(function persistUnitSystem() {
682
+ if (typeof window !== "undefined") {
683
+ localStorage.setItem(STORAGE_KEYS.unitSystem, unitSystem);
684
+ }
685
+ }, [unitSystem]);
686
+ useEffect(function syncDayjsTimezone() {
687
+ if (typeof window === "undefined") return;
688
+ localStorage.setItem(STORAGE_KEYS.timezone, timezone);
689
+ try {
690
+ const dayjsWithTz = dayjs;
691
+ if (dayjsWithTz.tz && typeof dayjsWithTz.tz.setDefault === "function") {
692
+ if (timezone !== "auto") {
693
+ dayjsWithTz.tz.setDefault(timezone);
694
+ } else {
695
+ const detected = Intl.DateTimeFormat().resolvedOptions().timeZone;
696
+ dayjsWithTz.tz.setDefault(detected);
697
+ }
698
+ }
699
+ } catch (error) {
700
+ console.warn("Failed to set timezone:", error);
701
+ }
702
+ }, [timezone]);
703
+ const setLocale = useCallback(function setLocale(newLocale) {
704
+ setLocaleState(normalizeLocale(newLocale, supportedLanguages));
705
+ }, [supportedLanguages]);
706
+ const setUnitSystem = useCallback(function setUnitSystem(newSystem) {
707
+ setUnitSystemState(newSystem);
708
+ }, []);
709
+ const setTimezone = useCallback(function setTimezone(newTimezone) {
710
+ setTimezoneState(newTimezone);
711
+ }, []);
712
+ const value = {
713
+ locale,
714
+ unitSystem,
715
+ timezone,
716
+ supportedLanguages,
717
+ setLocale,
718
+ setUnitSystem,
719
+ setTimezone
720
+ };
721
+ return jsx(I18nContext.Provider, {
722
+ value: value,
723
+ children: children
724
+ });
725
+ };
726
+ I18nProvider.displayName = "I18nProvider";
727
+
728
+ const MM_TO_INCH = 0.0393701;
729
+ const INCH_TO_MM = 25.4;
730
+ function mmToInches(mm) {
731
+ return mm * MM_TO_INCH;
732
+ }
733
+ function inchesToMm(inches) {
734
+ return inches * INCH_TO_MM;
735
+ }
736
+ function formatValueToDisplayUnit(value, unitSystem, options) {
737
+ var _a;
738
+ const decimals = (_a = options === null || options === void 0 ? void 0 : options.decimals) !== null && _a !== void 0 ? _a : 2;
739
+ const showUnit = (options === null || options === void 0 ? void 0 : options.showUnit) !== false;
740
+ if (unitSystem === "imperial") {
741
+ const inches = mmToInches(value);
742
+ return showUnit ? `${inches.toFixed(decimals)} in` : inches.toFixed(decimals);
743
+ }
744
+ return showUnit ? `${value.toFixed(decimals)} mm` : value.toFixed(decimals);
745
+ }
746
+ function convertDisplayValueToMm(displayValue, unitSystem) {
747
+ if (unitSystem === "imperial") {
748
+ return inchesToMm(displayValue);
749
+ }
750
+ return displayValue;
751
+ }
752
+ function getUnitDisplayLabel(unitSystem) {
753
+ return unitSystem === "imperial" ? "in" : "mm";
754
+ }
755
+ function getAvailableTimezones() {
756
+ return ["America/New_York", "America/Chicago", "America/Denver", "America/Los_Angeles", "America/Toronto", "America/Vancouver", "Europe/London", "Europe/Paris", "Europe/Berlin", "Europe/Madrid", "Europe/Rome", "Asia/Tokyo", "Asia/Shanghai", "Asia/Hong_Kong", "Asia/Singapore", "Australia/Sydney", "Australia/Melbourne", "UTC"];
757
+ }
758
+
759
+ const DEFAULT_VALUES = {
760
+ locale: "en",
761
+ unitSystem: "metric",
762
+ timezone: "auto",
763
+ supportedLanguages: ["en"],
764
+ setLocale: function defaultSetLocale(_newLocale) {
765
+ return undefined;
766
+ },
767
+ setUnitSystem: function defaultSetUnitSystem(_system) {
768
+ return undefined;
769
+ },
770
+ setTimezone: function defaultSetTimezone(_tz) {
771
+ return undefined;
772
+ }
773
+ };
774
+ const useI18n = function useI18n() {
775
+ const {
776
+ t
777
+ } = useTranslation();
778
+ const context = useContext(I18nContext);
779
+ const {
780
+ locale = DEFAULT_VALUES.locale,
781
+ unitSystem = DEFAULT_VALUES.unitSystem,
782
+ timezone = DEFAULT_VALUES.timezone,
783
+ supportedLanguages = DEFAULT_VALUES.supportedLanguages,
784
+ setLocale = DEFAULT_VALUES.setLocale,
785
+ setUnitSystem = DEFAULT_VALUES.setUnitSystem,
786
+ setTimezone = DEFAULT_VALUES.setTimezone
787
+ } = context || {};
788
+ const formatDate = useCallback(function formatDate(utcTimestamp, format) {
789
+ const tz = timezone === "auto" ? Intl.DateTimeFormat().resolvedOptions().timeZone : timezone;
790
+ try {
791
+ const dayjsWithTz = dayjs;
792
+ const hasTimezonePlugin = dayjsWithTz.tz && typeof dayjsWithTz.utc === "function";
793
+ if (hasTimezonePlugin) {
794
+ const utcInstance = dayjsWithTz.utc(utcTimestamp);
795
+ return utcInstance.tz(tz).locale(locale.split("-")[0]).format(format || "YYYY-MM-DD HH:mm:ss");
796
+ }
797
+ return dayjs(utcTimestamp).locale(locale.split("-")[0]).format(format || "YYYY-MM-DD HH:mm:ss");
798
+ } catch (error) {
799
+ return dayjs(utcTimestamp).locale(locale.split("-")[0]).format(format || "YYYY-MM-DD HH:mm:ss");
800
+ }
801
+ }, [timezone, locale]);
802
+ const formatValueToDisplayUnit$1 = useCallback(function formatValueToDisplayUnit$1(value, options) {
803
+ return formatValueToDisplayUnit(value, unitSystem, options);
804
+ }, [unitSystem]);
805
+ const convertDisplayValueToMmValue = useCallback(function convertDisplayValueToMmValue(value) {
806
+ return convertDisplayValueToMm(value, unitSystem);
807
+ }, [unitSystem]);
808
+ const getUnitDisplayLabelValue = useCallback(function getUnitDisplayLabelValue() {
809
+ return getUnitDisplayLabel(unitSystem);
810
+ }, [unitSystem]);
811
+ return {
812
+ t,
813
+ formatDate,
814
+ formatValueToDisplayUnit: formatValueToDisplayUnit$1,
815
+ convertDisplayValueToMm: convertDisplayValueToMmValue,
816
+ getUnitDisplayLabel: getUnitDisplayLabelValue,
817
+ locale,
818
+ unitSystem,
819
+ timezone,
820
+ supportedLanguages,
821
+ setLocale,
822
+ setUnitSystem,
823
+ setTimezone
824
+ };
825
+ };
826
+
827
+ const I18nSettings = function I18nSettings({
828
+ className,
829
+ layout = "stacked"
830
+ }) {
831
+ const {
832
+ classes,
833
+ cx
834
+ } = useStyles$g({
835
+ layout
836
+ });
837
+ const {
838
+ locale,
839
+ unitSystem,
840
+ timezone,
841
+ supportedLanguages,
842
+ setLocale,
843
+ setUnitSystem,
844
+ setTimezone,
845
+ t
846
+ } = useI18n();
847
+ const unitSystemOptions = useMemo(function getUnitSystemOptions() {
848
+ return [{
849
+ value: "metric",
850
+ displayText: t("common.units.metric", {
851
+ defaultValue: "Metric"
852
+ })
853
+ }, {
854
+ value: "imperial",
855
+ displayText: t("common.units.imperial", {
856
+ defaultValue: "Imperial"
857
+ })
858
+ }];
859
+ }, [t]);
860
+ const timezoneOptions = useMemo(function getTimezoneOptions() {
861
+ const timezones = getAvailableTimezones();
862
+ return [{
863
+ value: "auto",
864
+ displayText: t("common.settings.autoDetect", {
865
+ defaultValue: "Auto-detect"
866
+ })
867
+ }, ...timezones.map(tz => ({
868
+ value: tz,
869
+ displayText: tz
870
+ }))];
871
+ }, [t]);
872
+ const localeOptions = useMemo(function getLocaleOptions() {
873
+ return supportedLanguages.map(langCode => ({
874
+ value: langCode,
875
+ displayText: getLanguageDisplayName(langCode)
876
+ }));
877
+ }, [supportedLanguages]);
878
+ const handleUnitSystemChange = function handleUnitSystemChange(event) {
879
+ setUnitSystem(event.target.value);
880
+ };
881
+ const handleTimezoneChange = function handleTimezoneChange(event) {
882
+ setTimezone(event.target.value);
883
+ };
884
+ const handleLocaleChange = function handleLocaleChange(event) {
885
+ setLocale(event.target.value);
886
+ };
887
+ return jsxs(Box, {
888
+ className: cx(classes.container, className),
889
+ children: [jsx(Box, {
890
+ className: classes.settingItem,
891
+ children: jsx(VentionSelect, {
892
+ size: "xx-large",
893
+ variant: "outlined",
894
+ labelText: t("common.settings.unitSystem"),
895
+ value: unitSystem,
896
+ onChange: handleUnitSystemChange,
897
+ menuItems: unitSystemOptions
898
+ })
899
+ }), jsx(Box, {
900
+ className: classes.settingItem,
901
+ children: jsx(VentionSelect, {
902
+ size: "xx-large",
903
+ variant: "outlined",
904
+ labelText: t("common.settings.timezone"),
905
+ value: timezone,
906
+ onChange: handleTimezoneChange,
907
+ menuItems: timezoneOptions
908
+ })
909
+ }), supportedLanguages.length > 1 && jsx(Box, {
910
+ className: classes.settingItem,
911
+ children: jsx(VentionSelect, {
912
+ size: "xx-large",
913
+ variant: "outlined",
914
+ labelText: t("common.settings.locale"),
915
+ value: locale,
916
+ onChange: handleLocaleChange,
917
+ menuItems: localeOptions
918
+ })
919
+ })]
920
+ });
921
+ };
922
+ I18nSettings.displayName = "I18nSettings";
923
+ const useStyles$g = tss.withParams().create(({
924
+ theme,
925
+ layout
926
+ }) => ({
927
+ container: {
928
+ display: "flex",
929
+ flexDirection: layout === "horizontal" ? "row" : "column",
930
+ flexWrap: layout === "horizontal" ? "wrap" : "nowrap",
931
+ gap: theme.spacing(3),
932
+ width: "100%"
933
+ },
934
+ settingItem: layout === "horizontal" ? {
935
+ flex: "1 1 250px",
936
+ maxWidth: "350px"
937
+ } : {
938
+ width: "100%",
939
+ maxWidth: "400px"
940
+ }
941
+ }));
8
942
 
9
943
  function TimeLabel({
10
944
  className,
11
945
  color
12
946
  }) {
13
947
  const theme = useTheme();
14
- const [timeLabel, setTimeLabel] = useState(() => new Date().toLocaleTimeString([], {
15
- hour: "numeric",
16
- minute: "2-digit"
17
- }).toLowerCase().replace(" ", ""));
18
- useEffect(function updateTimeLabel() {
19
- const intervalId = setInterval(() => {
948
+ const {
949
+ formatDate,
950
+ timezone,
951
+ locale
952
+ } = useI18n();
953
+ const [timeLabel, setTimeLabel] = useState(() => {
954
+ try {
955
+ return formatDate(Date.now(), "HH:mm");
956
+ } catch (_a) {
957
+ return new Date().toLocaleTimeString([], {
958
+ hour: "numeric",
959
+ minute: "2-digit"
960
+ }).toLowerCase().replace(" ", "");
961
+ }
962
+ });
963
+ useEffect(function updateTimeLabelOnTimezoneChange() {
964
+ try {
965
+ const formatted = formatDate(Date.now(), "HH:mm");
966
+ setTimeLabel(formatted);
967
+ } catch (_a) {
20
968
  setTimeLabel(new Date().toLocaleTimeString([], {
21
969
  hour: "numeric",
22
970
  minute: "2-digit"
23
971
  }).toLowerCase().replace(" ", ""));
972
+ }
973
+ }, [formatDate, timezone, locale]);
974
+ useEffect(function updateTimeLabel() {
975
+ const intervalId = setInterval(() => {
976
+ try {
977
+ const formatted = formatDate(Date.now(), "HH:mm");
978
+ setTimeLabel(formatted);
979
+ } catch (_a) {
980
+ setTimeLabel(new Date().toLocaleTimeString([], {
981
+ hour: "numeric",
982
+ minute: "2-digit"
983
+ }).toLowerCase().replace(" ", ""));
984
+ }
24
985
  }, 1000);
25
986
  return () => clearInterval(intervalId);
26
- }, []);
987
+ }, [formatDate, timezone, locale]);
27
988
  return jsx(Typography, {
28
989
  className: className,
29
990
  color: color !== null && color !== void 0 ? color : theme.palette.common.white,
@@ -53,7 +1014,7 @@ const navigateControlCenter = route => {
53
1014
  window.parent.postMessage(`navigateTo${capitalizedRoute}`, "*");
54
1015
  };
55
1016
 
56
- const NavigationConfirmationModal = props => {
1017
+ const NavigationConfirmationModal = function NavigationConfirmationModal(props) {
57
1018
  const {
58
1019
  isOpen,
59
1020
  destination,
@@ -61,8 +1022,11 @@ const NavigationConfirmationModal = props => {
61
1022
  } = props;
62
1023
  const {
63
1024
  classes
64
- } = useStyles$a();
65
- const handleConfirm = () => {
1025
+ } = useStyles$f();
1026
+ const {
1027
+ t
1028
+ } = useI18n();
1029
+ const handleConfirm = function handleConfirm() {
66
1030
  if (!destination) return;
67
1031
  onClose();
68
1032
  if (destination === "controlCenter") {
@@ -71,11 +1035,15 @@ const NavigationConfirmationModal = props => {
71
1035
  navigateControlCenter(destination);
72
1036
  }
73
1037
  };
74
- const getButtonText = () => {
1038
+ const getButtonText = function getButtonText() {
75
1039
  if (destination === "controlCenter") {
76
- return "Go to Control Center";
1040
+ return t("navigation.modal.goToControlCenter", {
1041
+ defaultValue: "Go to Control Center"
1042
+ });
77
1043
  }
78
- return "Go to Remote Support";
1044
+ return t("navigation.modal.goToRemoteSupport", {
1045
+ defaultValue: "Go to Remote Support"
1046
+ });
79
1047
  };
80
1048
  return jsx(VentionModalBase, {
81
1049
  isOpen: isOpen,
@@ -94,10 +1062,14 @@ const NavigationConfirmationModal = props => {
94
1062
  })
95
1063
  }), jsx(Typography, {
96
1064
  className: classes.title,
97
- children: "You are about to exit the application"
1065
+ children: t("navigation.modal.exit.title", {
1066
+ defaultValue: "You are about to exit the application"
1067
+ })
98
1068
  }), jsx(Typography, {
99
1069
  className: classes.body,
100
- children: "Please make sure your changes are saved before you leave the app."
1070
+ children: t("navigation.modal.exit.message", {
1071
+ defaultValue: "Please make sure your changes are saved before you leave the app."
1072
+ })
101
1073
  }), jsx(Button, {
102
1074
  onClick: handleConfirm,
103
1075
  className: classes.button,
@@ -113,7 +1085,7 @@ const NavigationConfirmationModal = props => {
113
1085
  })
114
1086
  });
115
1087
  };
116
- const useStyles$a = tss.create(({
1088
+ const useStyles$f = tss.create(({
117
1089
  theme
118
1090
  }) => ({
119
1091
  modalContent: {
@@ -135,20 +1107,26 @@ const useStyles$a = tss.create(({
135
1107
  textAlign: "center"
136
1108
  }),
137
1109
  button: {
138
- width: "320px",
1110
+ minWidth: "320px",
1111
+ width: "auto",
1112
+ maxWidth: "100%",
139
1113
  height: "80px",
140
- backgroundColor: COLORS.slate[800],
1114
+ backgroundColor: theme.palette.button.filled,
141
1115
  borderRadius: "4px",
142
- padding: "8px",
1116
+ padding: "8px 16px",
143
1117
  boxSizing: "border-box",
144
- marginTop: "auto"
1118
+ marginTop: "auto",
1119
+ "&:hover": {
1120
+ backgroundColor: theme.palette.button.filledHover
1121
+ }
145
1122
  },
146
1123
  buttonLabel: {
147
1124
  display: "flex",
148
1125
  alignItems: "center",
149
1126
  justifyContent: "center",
150
1127
  padding: "0 8px",
151
- boxSizing: "border-box"
1128
+ boxSizing: "border-box",
1129
+ width: "100%"
152
1130
  },
153
1131
  buttonText: {
154
1132
  fontFamily: "Inter",
@@ -161,7 +1139,7 @@ const useStyles$a = tss.create(({
161
1139
  }
162
1140
  }));
163
1141
 
164
- const PasswordProtectionModal = props => {
1142
+ const PasswordProtectionModal = function PasswordProtectionModal(props) {
165
1143
  const {
166
1144
  isOpen,
167
1145
  onClose,
@@ -171,11 +1149,14 @@ const PasswordProtectionModal = props => {
171
1149
  const [isKeyboardVisible, setIsKeyboardVisible] = useState(false);
172
1150
  const {
173
1151
  classes
174
- } = useStyles$9();
1152
+ } = useStyles$e();
1153
+ const {
1154
+ t
1155
+ } = useI18n();
175
1156
  const [password, setPassword] = useState("");
176
1157
  const [showPassword, setShowPassword] = useState(false);
177
1158
  const [error, setError] = useState(false);
178
- const handleConfirm = () => {
1159
+ const handleConfirm = function handleConfirm() {
179
1160
  if (password === correctPassword) {
180
1161
  setPassword("");
181
1162
  setError(false);
@@ -185,17 +1166,12 @@ const PasswordProtectionModal = props => {
185
1166
  setError(true);
186
1167
  }
187
1168
  };
188
- const handleClose = () => {
1169
+ const handleClose = function handleClose() {
189
1170
  setPassword("");
190
1171
  setError(false);
191
1172
  onClose();
192
1173
  };
193
- const handleKeyPress = event => {
194
- if (event.key === "Enter") {
195
- handleConfirm();
196
- }
197
- };
198
- useEffect(() => {
1174
+ useEffect(function handleKeyboardVisibility() {
199
1175
  if (!isOpen || !isTouchScreenDevice()) {
200
1176
  setIsKeyboardVisible(false);
201
1177
  return;
@@ -234,20 +1210,31 @@ const PasswordProtectionModal = props => {
234
1210
  className: classes.modalContent,
235
1211
  children: [jsx(Typography, {
236
1212
  className: classes.title,
237
- children: "Administrator login required"
1213
+ children: t("navigation.modal.password.title", {
1214
+ defaultValue: "Administrator login required"
1215
+ })
238
1216
  }), jsx(Box, {
1217
+ component: "form",
1218
+ id: "password-form",
1219
+ onSubmit: event => {
1220
+ event.preventDefault();
1221
+ handleConfirm();
1222
+ },
239
1223
  className: classes.inputContainer,
240
1224
  children: jsx(VentionTextInput, {
241
- label: "Password",
1225
+ label: t("navigation.modal.password.label", {
1226
+ defaultValue: "Password"
1227
+ }),
242
1228
  type: showPassword ? "text" : "password",
243
1229
  value: password,
244
1230
  onChange: event => {
245
1231
  setPassword(event.target.value);
246
1232
  setError(false);
247
1233
  },
248
- onKeyPress: handleKeyPress,
249
1234
  state: error ? "error" : "default",
250
- helperText: error ? "Incorrect password" : "",
1235
+ helperText: error ? t("navigation.modal.password.error", {
1236
+ defaultValue: "Incorrect password"
1237
+ }) : "",
251
1238
  fullWidth: true,
252
1239
  size: "xx-large",
253
1240
  InputProps: {
@@ -257,24 +1244,28 @@ const PasswordProtectionModal = props => {
257
1244
  onClick: () => setShowPassword(!showPassword),
258
1245
  edge: "end",
259
1246
  disableRipple: true,
1247
+ type: "button",
260
1248
  children: jsx(VentionIcon, {
261
1249
  type: showPassword ? "eye-closed" : "eye",
262
1250
  size: 28,
263
- color: COLORS.slate[900]
1251
+ color: "#0f172a"
264
1252
  })
265
1253
  })
266
1254
  })
267
1255
  }
268
1256
  })
269
1257
  }), jsx(Button, {
270
- onClick: handleConfirm,
271
1258
  className: classes.button,
272
1259
  disableRipple: true,
1260
+ type: "submit",
1261
+ form: "password-form",
273
1262
  children: jsx(Box, {
274
1263
  className: classes.buttonLabel,
275
1264
  children: jsx(Typography, {
276
1265
  className: classes.buttonText,
277
- children: "Confirm"
1266
+ children: t("navigation.modal.password.confirm", {
1267
+ defaultValue: "Confirm"
1268
+ })
278
1269
  })
279
1270
  })
280
1271
  })]
@@ -282,7 +1273,7 @@ const PasswordProtectionModal = props => {
282
1273
  })]
283
1274
  });
284
1275
  };
285
- const useStyles$9 = tss.create(({
1276
+ const useStyles$e = tss.create(({
286
1277
  theme
287
1278
  }) => ({
288
1279
  modalContent: {
@@ -300,7 +1291,7 @@ const useStyles$9 = tss.create(({
300
1291
  fontWeight: 700,
301
1292
  lineHeight: "48px",
302
1293
  textAlign: "center",
303
- color: COLORS.slate[900]
1294
+ color: theme.palette.text.primary
304
1295
  },
305
1296
  inputContainer: {
306
1297
  width: "499px",
@@ -311,12 +1302,12 @@ const useStyles$9 = tss.create(({
311
1302
  button: {
312
1303
  width: "250px",
313
1304
  height: "64px",
314
- backgroundColor: COLORS.slate[800],
1305
+ backgroundColor: theme.palette.button.filled,
315
1306
  borderRadius: "4px",
316
1307
  padding: "8px",
317
1308
  boxSizing: "border-box",
318
1309
  "&:hover": {
319
- backgroundColor: COLORS.slate[700]
1310
+ backgroundColor: theme.palette.button.filledHover
320
1311
  }
321
1312
  },
322
1313
  buttonLabel: {
@@ -337,7 +1328,7 @@ const useStyles$9 = tss.create(({
337
1328
  }
338
1329
  }));
339
1330
 
340
- const NavigationBarItem = props => {
1331
+ const NavigationBarItem = function NavigationBarItem(props) {
341
1332
  const {
342
1333
  icon,
343
1334
  iconDisabled,
@@ -354,17 +1345,18 @@ const NavigationBarItem = props => {
354
1345
  const {
355
1346
  classes,
356
1347
  cx
357
- } = useStyles$8();
1348
+ } = useStyles$d();
1349
+ const {
1350
+ t
1351
+ } = useI18n();
358
1352
  const isActive = (location === null || location === void 0 ? void 0 : location.pathname) === path;
359
1353
  const currentIcon = isDisabled && iconDisabled ? iconDisabled : icon;
360
- const handleItemClick = () => {
1354
+ const handleItemClick = function handleItemClick() {
361
1355
  if (isDisabled || !handleNavigate || !setPasswordProtectedItem) return;
362
- // If destination has a password and it's different from current password, show modal
363
1356
  if (password && password !== currentPassword) {
364
1357
  setPasswordProtectedItem(props);
365
1358
  return;
366
1359
  }
367
- // Otherwise navigate directly
368
1360
  onClick ? onClick() : handleNavigate(path);
369
1361
  };
370
1362
  return jsxs(Button, {
@@ -380,11 +1372,13 @@ const NavigationBarItem = props => {
380
1372
  children: currentIcon
381
1373
  }), jsx(Typography, {
382
1374
  className: classes.navLabel,
383
- children: label
1375
+ children: t(label, {
1376
+ defaultValue: label
1377
+ })
384
1378
  })]
385
1379
  });
386
1380
  };
387
- const useStyles$8 = tss.create(({
1381
+ const useStyles$d = tss.create(({
388
1382
  theme
389
1383
  }) => ({
390
1384
  tabButton: {
@@ -399,7 +1393,7 @@ const useStyles$8 = tss.create(({
399
1393
  backgroundColor: "transparent",
400
1394
  transition: "background-color 0.2s",
401
1395
  "&:hover": {
402
- backgroundColor: COLORS.slate[700]
1396
+ backgroundColor: theme.palette.button.filledHover
403
1397
  },
404
1398
  color: theme.palette.common.white,
405
1399
  "&.Mui-disabled": {
@@ -407,7 +1401,7 @@ const useStyles$8 = tss.create(({
407
1401
  }
408
1402
  },
409
1403
  active: {
410
- backgroundColor: COLORS.slate[600]
1404
+ backgroundColor: theme.palette.button.filledPressed
411
1405
  },
412
1406
  disabled: {
413
1407
  opacity: 0.5,
@@ -426,8 +1420,17 @@ const useStyles$8 = tss.create(({
426
1420
  navLabel: Object.assign({}, theme.typography.hmiText22Regular)
427
1421
  }));
428
1422
 
1423
+ const Z_INDEX = {
1424
+ NAVIGATION: 1100,
1425
+ MENU: 1200,
1426
+ MENU_OVERLAY: 1210,
1427
+ MODAL: 1300,
1428
+ SNACKBAR: 1400,
1429
+ TOOLTIP: 1500
1430
+ };
1431
+
429
1432
  const iconSize = 32;
430
- const NavigationBarRoot = props => {
1433
+ const NavigationBarRoot = function NavigationBarRoot(props) {
431
1434
  var _a;
432
1435
  const {
433
1436
  children,
@@ -438,22 +1441,28 @@ const NavigationBarRoot = props => {
438
1441
  isControlCenterDisabled = false,
439
1442
  isSupportDisabled = false,
440
1443
  showControlCenterButton = true,
441
- showSupportButton = true
1444
+ showSupportButton = true,
1445
+ variant = "fixed"
442
1446
  } = props;
443
1447
  const location = useLocation();
444
1448
  const navigate = useNavigate();
445
1449
  const theme = useTheme();
446
1450
  const {
447
1451
  classes
448
- } = useStyles$7();
1452
+ } = useStyles$c({
1453
+ variant
1454
+ });
1455
+ const {
1456
+ t
1457
+ } = useI18n();
449
1458
  const [modalDestination, setModalDestination] = useState(null);
450
1459
  const [passwordProtectedItem, setPasswordProtectedItem] = useState(null);
451
1460
  const [currentPassword, setCurrentPassword] = useState(null);
452
1461
  const disabledSet = new Set(disabledNavigationItems);
453
- const getFadedColor = (color, opacity = 1) => {
1462
+ const getFadedColor = function getFadedColor(color, opacity = 1) {
454
1463
  return `${color}${Math.round(opacity * 255).toString(16).padStart(2, "0")}`;
455
1464
  };
456
- const handleNavigate = path => {
1465
+ const handleNavigate = function handleNavigate(path) {
457
1466
  if (location.pathname !== path) navigate(path);
458
1467
  };
459
1468
  useEffect(function updateCurrentPassword() {
@@ -461,7 +1470,7 @@ const NavigationBarRoot = props => {
461
1470
  const currentPagePassword = React.isValidElement(currentPage) ? currentPage.props.password : undefined;
462
1471
  setCurrentPassword(currentPagePassword !== null && currentPagePassword !== void 0 ? currentPagePassword : null);
463
1472
  }, [location.pathname, children]);
464
- const handleControlCenterClick = () => {
1473
+ const handleControlCenterClick = function handleControlCenterClick() {
465
1474
  if (isControlCenterDisabled) return;
466
1475
  if (onControlCenterClick) {
467
1476
  onControlCenterClick();
@@ -469,7 +1478,7 @@ const NavigationBarRoot = props => {
469
1478
  setModalDestination("controlCenter");
470
1479
  }
471
1480
  };
472
- const handleSupportClick = () => {
1481
+ const handleSupportClick = function handleSupportClick() {
473
1482
  if (isSupportDisabled) return;
474
1483
  if (onSupportClick) {
475
1484
  onSupportClick();
@@ -477,7 +1486,7 @@ const NavigationBarRoot = props => {
477
1486
  setModalDestination("remoteSupport");
478
1487
  }
479
1488
  };
480
- const handlePasswordSuccess = () => {
1489
+ const handlePasswordSuccess = function handlePasswordSuccess() {
481
1490
  if (passwordProtectedItem) {
482
1491
  if (passwordProtectedItem.onClick) {
483
1492
  passwordProtectedItem.onClick();
@@ -486,7 +1495,7 @@ const NavigationBarRoot = props => {
486
1495
  }
487
1496
  }
488
1497
  };
489
- const processedChildren = React.Children.map(children, child => {
1498
+ const processedChildren = React.Children.map(children, function processChild(child) {
490
1499
  if (!React.isValidElement(child)) return child;
491
1500
  if (child.type !== NavigationBarItem) return child;
492
1501
  const itemId = child.props.id;
@@ -499,14 +1508,20 @@ const NavigationBarRoot = props => {
499
1508
  setPasswordProtectedItem: setPasswordProtectedItem
500
1509
  }));
501
1510
  });
1511
+ const handleCloseConfirmation = function handleCloseConfirmation() {
1512
+ setModalDestination(null);
1513
+ };
1514
+ const handleClosePassword = function handleClosePassword() {
1515
+ setPasswordProtectedItem(null);
1516
+ };
502
1517
  return jsxs(Fragment, {
503
1518
  children: [jsx(NavigationConfirmationModal, {
504
1519
  isOpen: modalDestination !== null,
505
1520
  destination: modalDestination,
506
- onClose: () => setModalDestination(null)
1521
+ onClose: handleCloseConfirmation
507
1522
  }), jsx(PasswordProtectionModal, {
508
1523
  isOpen: passwordProtectedItem !== null,
509
- onClose: () => setPasswordProtectedItem(null),
1524
+ onClose: handleClosePassword,
510
1525
  onSuccess: handlePasswordSuccess,
511
1526
  correctPassword: (_a = passwordProtectedItem === null || passwordProtectedItem === void 0 ? void 0 : passwordProtectedItem.password) !== null && _a !== void 0 ? _a : ""
512
1527
  }), jsx("footer", {
@@ -521,8 +1536,8 @@ const NavigationBarRoot = props => {
521
1536
  disabled: isControlCenterDisabled,
522
1537
  className: classes.sideButton,
523
1538
  style: {
524
- backgroundColor: isControlCenterDisabled ? getFadedColor(COLORS.slate[800]) : COLORS.slate[800],
525
- borderColor: isControlCenterDisabled ? getFadedColor(COLORS.slate[600]) : COLORS.slate[600],
1539
+ backgroundColor: isControlCenterDisabled ? getFadedColor(theme.palette.background.brand) : theme.palette.background.brand,
1540
+ borderColor: isControlCenterDisabled ? getFadedColor(theme.palette.border.onColor) : theme.palette.border.onColor,
526
1541
  opacity: isControlCenterDisabled ? 0.5 : 1,
527
1542
  cursor: isControlCenterDisabled ? "not-allowed" : "pointer"
528
1543
  },
@@ -531,7 +1546,9 @@ const NavigationBarRoot = props => {
531
1546
  style: {
532
1547
  color: isControlCenterDisabled ? getFadedColor(theme.palette.common.white) : theme.palette.common.white
533
1548
  },
534
- children: "Control Center"
1549
+ children: t("navigation.bar.controlCenter", {
1550
+ defaultValue: "Control Center"
1551
+ })
535
1552
  })
536
1553
  })
537
1554
  }), jsx("nav", {
@@ -545,8 +1562,8 @@ const NavigationBarRoot = props => {
545
1562
  disabled: isSupportDisabled,
546
1563
  className: classes.sideButton,
547
1564
  style: {
548
- backgroundColor: isSupportDisabled ? getFadedColor(COLORS.slate[800]) : COLORS.slate[800],
549
- borderColor: isSupportDisabled ? getFadedColor(COLORS.slate[600]) : COLORS.slate[600],
1565
+ backgroundColor: isSupportDisabled ? getFadedColor(theme.palette.background.brand) : theme.palette.background.brand,
1566
+ borderColor: isSupportDisabled ? getFadedColor(theme.palette.border.onColor) : theme.palette.border.onColor,
550
1567
  opacity: isSupportDisabled ? 0.5 : 1,
551
1568
  cursor: isSupportDisabled ? "not-allowed" : "pointer"
552
1569
  },
@@ -560,7 +1577,9 @@ const NavigationBarRoot = props => {
560
1577
  style: {
561
1578
  color: isSupportDisabled ? getFadedColor(theme.palette.common.white) : theme.palette.common.white
562
1579
  },
563
- children: "Support"
1580
+ children: t("navigation.bar.support", {
1581
+ defaultValue: "Support"
1582
+ })
564
1583
  })
565
1584
  }), showTimer && jsx(TimeLabel, {
566
1585
  className: classes.timeLabel,
@@ -571,23 +1590,32 @@ const NavigationBarRoot = props => {
571
1590
  })]
572
1591
  });
573
1592
  };
574
- const useStyles$7 = tss.create(({
575
- theme
1593
+ const useStyles$c = tss.withParams().create(({
1594
+ theme,
1595
+ variant
576
1596
  }) => ({
577
- root: {
1597
+ root: Object.assign({
578
1598
  boxSizing: "border-box",
579
1599
  display: "flex",
580
1600
  justifyContent: "center",
581
1601
  alignItems: "center",
582
1602
  height: "120px",
583
- padding: theme.spacing(2, 6),
1603
+ padding: theme.spacing(2, 6)
1604
+ }, variant === "fixed" ? {
584
1605
  backgroundColor: COLORS.slate[900],
585
1606
  position: "fixed",
586
1607
  left: 0,
587
1608
  right: 0,
588
1609
  bottom: 0,
589
- borderTop: `1px solid ${COLORS.slate[800]}`
590
- },
1610
+ borderTop: `1px solid ${COLORS.slate[800]}`,
1611
+ zIndex: Z_INDEX.NAVIGATION
1612
+ } : {
1613
+ backgroundColor: theme.palette.background.defaultInverse,
1614
+ position: "relative",
1615
+ width: "100%",
1616
+ borderTop: `1px solid ${theme.palette.border.inverse}`,
1617
+ flexShrink: 0
1618
+ }),
591
1619
  container: {
592
1620
  display: "flex",
593
1621
  alignItems: "center",
@@ -614,14 +1642,14 @@ const useStyles$7 = tss.create(({
614
1642
  justifyContent: "center",
615
1643
  height: theme.spacing(11),
616
1644
  padding: theme.spacing(2, 4),
617
- backgroundColor: COLORS.slate[800],
1645
+ backgroundColor: theme.palette.background.brand,
618
1646
  borderRadius: 4,
619
- border: `2px solid ${COLORS.slate[600]}`,
1647
+ border: `2px solid ${theme.palette.border.onColor}`,
620
1648
  color: theme.palette.common.white,
621
1649
  textTransform: "none",
622
1650
  minWidth: 0,
623
1651
  "&:hover": {
624
- backgroundColor: COLORS.slate[700]
1652
+ backgroundColor: theme.palette.button.filledHover
625
1653
  },
626
1654
  "&.Mui-disabled": {
627
1655
  color: theme.palette.common.white
@@ -655,7 +1683,7 @@ const NavigationBar = Object.assign(NavigationBarRoot, {
655
1683
  Item: NavigationBarItem
656
1684
  });
657
1685
 
658
- const StatusTopBarButton = props => {
1686
+ const StatusTopBarButton = function StatusTopBarButton(props) {
659
1687
  const {
660
1688
  label,
661
1689
  onClick,
@@ -672,9 +1700,9 @@ const StatusTopBarButton = props => {
672
1700
  } = props;
673
1701
  const {
674
1702
  classes
675
- } = useStyles$6();
1703
+ } = useStyles$b();
676
1704
  if (!visible) return null;
677
- const getFadedColor = (color, opacity = 1) => {
1705
+ const getFadedColor = function getFadedColor(color, opacity = 1) {
678
1706
  return `${color}${Math.round(opacity * 255).toString(16).padStart(2, "0")}`;
679
1707
  };
680
1708
  const hasBackgroundColor = !!backgroundColor;
@@ -684,7 +1712,7 @@ const StatusTopBarButton = props => {
684
1712
  const finalBorderColor = disabled && borderColor ? getFadedColor(borderColor) : borderColor;
685
1713
  const finalTextColor = disabled && textColor ? getFadedColor(textColor) : textColor;
686
1714
  const finalIcon = disabled && iconDisabled ? iconDisabled : icon;
687
- const handleClick = () => {
1715
+ const handleClick = function handleClick() {
688
1716
  if (!disabled && onClick) {
689
1717
  onClick();
690
1718
  }
@@ -718,7 +1746,7 @@ const StatusTopBarButton = props => {
718
1746
  })
719
1747
  });
720
1748
  };
721
- const useStyles$6 = tss.create(({
1749
+ const useStyles$b = tss.create(({
722
1750
  theme
723
1751
  }) => ({
724
1752
  actionButton: {
@@ -746,14 +1774,17 @@ const useStyles$6 = tss.create(({
746
1774
  }
747
1775
  }));
748
1776
 
749
- const StatusTopBarRoot = props => {
1777
+ const StatusTopBarRoot = function StatusTopBarRoot(props) {
750
1778
  const {
751
1779
  status,
752
- buttonConfigs = []
1780
+ buttonConfigs = [],
1781
+ variant = "light"
753
1782
  } = props;
754
1783
  const {
755
1784
  classes
756
- } = useStyles$5();
1785
+ } = useStyles$a({
1786
+ variant
1787
+ });
757
1788
  const visibleButtons = buttonConfigs.filter(config => config.visible !== false);
758
1789
  const hasVisibleButtons = visibleButtons.length > 0;
759
1790
  return jsxs("header", {
@@ -778,17 +1809,17 @@ const StatusTopBarRoot = props => {
778
1809
  })]
779
1810
  });
780
1811
  };
781
- const useStyles$5 = tss.create(({
782
- theme
1812
+ const useStyles$a = tss.withParams().create(({
1813
+ theme,
1814
+ variant
783
1815
  }) => ({
784
1816
  root: {
785
1817
  boxSizing: "border-box",
786
- position: "fixed",
787
- top: 0,
788
- right: 0,
789
- left: 0,
790
- backgroundColor: theme.palette.common.white,
791
- height: "128px"
1818
+ position: "relative",
1819
+ width: "100%",
1820
+ backgroundColor: variant === "dark" ? theme.palette.background.defaultInverse : theme.palette.common.white,
1821
+ height: "120px",
1822
+ flexShrink: 0
792
1823
  },
793
1824
  content: {
794
1825
  display: "flex",
@@ -798,7 +1829,10 @@ const useStyles$5 = tss.create(({
798
1829
  },
799
1830
  leftArea: {
800
1831
  display: "flex",
801
- alignItems: "center"
1832
+ alignItems: "center",
1833
+ "& .MuiTypography-root": {
1834
+ color: variant === "dark" ? theme.palette.common.white : theme.palette.text.primary
1835
+ }
802
1836
  },
803
1837
  rightArea: {
804
1838
  display: "flex",
@@ -817,30 +1851,35 @@ const useStyles$5 = tss.create(({
817
1851
  }));
818
1852
  const StatusTopBar = StatusTopBarRoot;
819
1853
 
820
- const Sidebar = props => {
1854
+ const Sidebar = function Sidebar(props) {
1855
+ const {
1856
+ items,
1857
+ variant = "card"
1858
+ } = props;
821
1859
  const {
822
1860
  classes
823
- } = useStyles$4();
1861
+ } = useStyles$9({
1862
+ variant
1863
+ });
824
1864
  return jsx(Box, {
825
1865
  className: classes.container,
826
- children: props.items.map((item, index) => jsxs(React.Fragment, {
827
- children: [jsx(SidebarItemComponent, {
828
- item: item
829
- }), index < props.items.length - 1 && jsx(Divider, {
830
- className: classes.divider
831
- })]
1866
+ children: items.map(item => jsx(SidebarItemComponent, {
1867
+ item: item,
1868
+ variant: variant
832
1869
  }, item.id))
833
1870
  });
834
1871
  };
835
1872
  const ICON_SIZE = 32;
836
- const SidebarItemComponent = props => {
1873
+ const SidebarItemComponent = function SidebarItemComponent(props) {
837
1874
  const {
838
- item
1875
+ item,
1876
+ variant
839
1877
  } = props;
840
1878
  const {
841
1879
  classes
842
1880
  } = useSidebarItemStyles({
843
- state: item.state
1881
+ state: item.state,
1882
+ variant
844
1883
  });
845
1884
  const isDisabled = item.state === "disabled";
846
1885
  return jsx(Button, {
@@ -855,7 +1894,10 @@ const SidebarItemComponent = props => {
855
1894
  className: classes.content,
856
1895
  children: [jsxs(Box, {
857
1896
  className: classes.leftSection,
858
- children: [item.icon ? item.icon : jsx(Box, {
1897
+ children: [item.icon ? jsx(Box, {
1898
+ className: classes.iconWrapper,
1899
+ children: item.icon
1900
+ }) : jsx(Box, {
859
1901
  className: classes.iconPlaceholder
860
1902
  }), jsxs(Box, {
861
1903
  className: classes.textContainer,
@@ -880,42 +1922,65 @@ const SidebarItemComponent = props => {
880
1922
  })
881
1923
  });
882
1924
  };
883
- const useStyles$4 = tss.create(({
884
- theme
1925
+ const useStyles$9 = tss.withParams().create(({
1926
+ theme,
1927
+ variant
885
1928
  }) => ({
886
- container: {
1929
+ container: Object.assign({
887
1930
  display: "flex",
888
1931
  flexDirection: "column",
889
1932
  width: "100%"
890
- },
891
- divider: {
892
- borderColor: theme.palette.border.main
893
- }
1933
+ }, variant === "card" ? {
1934
+ padding: theme.spacing(2),
1935
+ gap: theme.spacing(1)
1936
+ } : {
1937
+ padding: 0,
1938
+ gap: 0
1939
+ })
894
1940
  }));
895
- const useSidebarItemStyles = tss.withParams().create(({
1941
+ const useSidebarItemStyles = tss.withParams().create(function createSidebarItemStyles({
896
1942
  theme,
897
- state
898
- }) => {
1943
+ state,
1944
+ variant
1945
+ }) {
899
1946
  const isActive = state === "active";
900
1947
  const isDisabled = state === "disabled";
901
- return {
902
- button: {
1948
+ const isAccentVariant = variant === "accent";
1949
+ const getButtonStyles = () => {
1950
+ const baseStyles = {
903
1951
  width: "100%",
904
- height: theme.spacing(14),
905
- padding: theme.spacing(7, 5),
1952
+ minHeight: theme.spacing(14),
1953
+ padding: theme.spacing(3, 4),
906
1954
  textAlign: "left",
907
1955
  textTransform: "none",
908
- borderRadius: 0,
909
- backgroundColor: isActive ? theme.palette.background.surface1active : "transparent",
910
- border: isActive ? `1px solid ${theme.palette.border.main}` : "1px solid transparent",
911
1956
  display: "flex",
912
1957
  alignItems: "center",
913
1958
  justifyContent: "flex-start",
914
- transition: "background-color 0.2s ease",
1959
+ transition: "all 0.2s ease"
1960
+ };
1961
+ if (isAccentVariant) {
1962
+ return Object.assign(Object.assign({}, baseStyles), {
1963
+ borderRadius: 0,
1964
+ backgroundColor: isActive ? theme.palette.background.surface1active : "transparent",
1965
+ border: "none",
1966
+ borderBottom: `1px solid ${theme.palette.divider}`,
1967
+ "&:hover": {
1968
+ backgroundColor: isDisabled ? "transparent" : isActive ? theme.palette.background.surface1active : theme.palette.action.hover
1969
+ }
1970
+ });
1971
+ }
1972
+ return Object.assign(Object.assign({}, baseStyles), {
1973
+ borderRadius: theme.spacing(2),
1974
+ backgroundColor: isActive ? theme.palette.background.surface1active : theme.palette.background.paper,
1975
+ border: `1px solid ${isActive ? theme.palette.border.main : theme.palette.divider}`,
915
1976
  "&:hover": {
916
- backgroundColor: isDisabled ? "transparent" : isActive ? theme.palette.background.surface1active : theme.palette.action.hover
1977
+ backgroundColor: isDisabled ? theme.palette.background.paper : isActive ? theme.palette.background.surface1active : theme.palette.action.hover,
1978
+ borderColor: isDisabled ? theme.palette.divider : theme.palette.border.main
917
1979
  }
918
- },
1980
+ });
1981
+ };
1982
+ return {
1983
+ button: getButtonStyles(),
919
1984
  content: {
920
1985
  display: "flex",
921
1986
  alignItems: "center",
@@ -929,6 +1994,15 @@ const useSidebarItemStyles = tss.withParams().create(({
929
1994
  gap: theme.spacing(3),
930
1995
  flex: 1
931
1996
  },
1997
+ iconWrapper: {
1998
+ display: "flex",
1999
+ alignItems: "center",
2000
+ justifyContent: "center",
2001
+ color: isActive ? theme.palette.background.defaultInverse : theme.palette.icon.tertiary,
2002
+ "& svg": {
2003
+ color: isActive ? theme.palette.background.defaultInverse : theme.palette.icon.tertiary
2004
+ }
2005
+ },
932
2006
  iconPlaceholder: {
933
2007
  width: theme.spacing(4)
934
2008
  },
@@ -967,17 +2041,24 @@ const LogsTable = memo(({
967
2041
  hasMoreLogs,
968
2042
  onLogClick,
969
2043
  tableHeight,
970
- emptyStateMessage = "You have no logs",
2044
+ emptyStateMessage,
971
2045
  emptyStateIcon
972
2046
  }) => {
973
2047
  const {
974
2048
  classes,
975
2049
  cx
976
- } = useStyles$3({
2050
+ } = useStyles$8({
977
2051
  tableHeight
978
2052
  });
979
2053
  const theme = useTheme();
2054
+ const {
2055
+ t,
2056
+ formatDate
2057
+ } = useI18n();
980
2058
  const loadMoreRef = useRef(null);
2059
+ const emptyMessage = emptyStateMessage || t("logs.emptyState.noLogs", {
2060
+ defaultValue: "You have no logs"
2061
+ });
981
2062
  useEffect(function setupInfiniteScroll() {
982
2063
  if (!onLoadMoreLogs || !hasMoreLogs || !loadMoreRef.current) return;
983
2064
  const observer = new IntersectionObserver(entries => {
@@ -1004,7 +2085,9 @@ const LogsTable = memo(({
1004
2085
  sx: {
1005
2086
  marginTop: 2
1006
2087
  },
1007
- children: "Loading logs..."
2088
+ children: t("logs.loading", {
2089
+ defaultValue: "Loading logs..."
2090
+ })
1008
2091
  })]
1009
2092
  })
1010
2093
  });
@@ -1016,7 +2099,9 @@ const LogsTable = memo(({
1016
2099
  className: classes.errorState,
1017
2100
  children: jsx(VentionAlert, {
1018
2101
  severity: "error",
1019
- title: "Error loading logs",
2102
+ title: t("logs.error.loading", {
2103
+ defaultValue: "Error loading logs"
2104
+ }),
1020
2105
  descriptionText: error,
1021
2106
  size: "large"
1022
2107
  })
@@ -1033,7 +2118,7 @@ const LogsTable = memo(({
1033
2118
  children: emptyStateIcon
1034
2119
  }), jsx(Typography, {
1035
2120
  variant: "heading24Medium",
1036
- children: emptyStateMessage
2121
+ children: emptyMessage
1037
2122
  })]
1038
2123
  })
1039
2124
  });
@@ -1071,8 +2156,30 @@ const LogsTable = memo(({
1071
2156
  color: theme.palette.info.main
1072
2157
  });
1073
2158
  };
1074
- const formatDate = dateStr => {
1075
- return dayjs(dateStr).format("YYYY-MM-DD h:mm:ssa");
2159
+ const formatLogDate = dateStr => {
2160
+ try {
2161
+ const timestamp = new Date(dateStr).getTime();
2162
+ if (isNaN(timestamp)) {
2163
+ return dateStr;
2164
+ }
2165
+ return formatDate(timestamp, "YYYY-MM-DD h:mm:ssa");
2166
+ } catch (_a) {
2167
+ try {
2168
+ const date = new Date(dateStr);
2169
+ if (isNaN(date.getTime())) return dateStr;
2170
+ return date.toLocaleString("en-US", {
2171
+ year: "numeric",
2172
+ month: "2-digit",
2173
+ day: "2-digit",
2174
+ hour: "numeric",
2175
+ minute: "2-digit",
2176
+ second: "2-digit",
2177
+ hour12: true
2178
+ });
2179
+ } catch (_b) {
2180
+ return dateStr;
2181
+ }
2182
+ }
1076
2183
  };
1077
2184
  return jsxs(Box, {
1078
2185
  className: classes.tableSection,
@@ -1091,35 +2198,45 @@ const LogsTable = memo(({
1091
2198
  children: jsx(Typography, {
1092
2199
  variant: "heading24Medium",
1093
2200
  className: classes.tableText,
1094
- children: "Date"
2201
+ children: t("logs.table.date", {
2202
+ defaultValue: "Date"
2203
+ })
1095
2204
  })
1096
2205
  }), jsx(TableCell, {
1097
2206
  className: classes.tableHeaderCell,
1098
2207
  children: jsx(Typography, {
1099
2208
  variant: "heading24Medium",
1100
2209
  className: classes.tableText,
1101
- children: "Type"
2210
+ children: t("logs.table.type", {
2211
+ defaultValue: "Type"
2212
+ })
1102
2213
  })
1103
2214
  }), jsx(TableCell, {
1104
2215
  className: classes.tableHeaderCell,
1105
2216
  children: jsx(Typography, {
1106
2217
  variant: "heading24Medium",
1107
2218
  className: classes.tableText,
1108
- children: "Code"
2219
+ children: t("logs.table.code", {
2220
+ defaultValue: "Code"
2221
+ })
1109
2222
  })
1110
2223
  }), jsx(TableCell, {
1111
2224
  className: classes.tableHeaderCell,
1112
2225
  children: jsx(Typography, {
1113
2226
  variant: "heading24Medium",
1114
2227
  className: classes.tableText,
1115
- children: "Message"
2228
+ children: t("logs.table.message", {
2229
+ defaultValue: "Message"
2230
+ })
1116
2231
  })
1117
2232
  }), jsx(TableCell, {
1118
2233
  className: classes.tableHeaderCell,
1119
2234
  children: jsx(Typography, {
1120
2235
  variant: "heading24Medium",
1121
2236
  className: classes.tableText,
1122
- children: "Description"
2237
+ children: t("logs.table.description", {
2238
+ defaultValue: "Description"
2239
+ })
1123
2240
  })
1124
2241
  })]
1125
2242
  })
@@ -1138,14 +2255,16 @@ const LogsTable = memo(({
1138
2255
  children: jsx(Typography, {
1139
2256
  variant: "heading24Medium",
1140
2257
  className: classes.tableText,
1141
- children: formatDate(log.date)
2258
+ children: formatLogDate(log.date)
1142
2259
  })
1143
2260
  }), jsx(TableCell, {
1144
2261
  className: cx(classes.typeCell, getTypeClassName(log.level)),
1145
2262
  children: jsx(Typography, {
1146
2263
  variant: "heading24Medium",
1147
2264
  className: classes.tableText,
1148
- children: log.level
2265
+ children: t(`logs.type.${log.level}`, {
2266
+ defaultValue: log.level
2267
+ })
1149
2268
  })
1150
2269
  }), jsx(TableCell, {
1151
2270
  children: jsx(Typography, {
@@ -1177,19 +2296,23 @@ const LogsTable = memo(({
1177
2296
  }), jsx(Typography, {
1178
2297
  variant: "uiText14Regular",
1179
2298
  className: classes.loadMoreText,
1180
- children: "Loading more logs..."
2299
+ children: t("logs.loadingMore", {
2300
+ defaultValue: "Loading more logs..."
2301
+ })
1181
2302
  })]
1182
2303
  }), onLoadMoreLogs && !hasMoreLogs && logs.length > 0 && jsx(Box, {
1183
2304
  className: classes.endMessage,
1184
2305
  children: jsx(Typography, {
1185
2306
  variant: "uiText14Regular",
1186
- children: "No more logs to load"
2307
+ children: t("logs.noMoreLogs", {
2308
+ defaultValue: "No more logs to load"
2309
+ })
1187
2310
  })
1188
2311
  })]
1189
2312
  });
1190
2313
  });
1191
2314
  LogsTable.displayName = "LogsTable";
1192
- const useStyles$3 = tss.withParams().create(({
2315
+ const useStyles$8 = tss.withParams().create(({
1193
2316
  theme,
1194
2317
  tableHeight
1195
2318
  }) => ({
@@ -1303,31 +2426,62 @@ const LogFilterForm = memo(({
1303
2426
  var _a, _b, _c, _d;
1304
2427
  const {
1305
2428
  classes
1306
- } = useStyles$2();
2429
+ } = useStyles$7();
2430
+ const {
2431
+ t
2432
+ } = useI18n();
2433
+ const handleFilterChangeRef = useRef(onFilterChange);
2434
+ useEffect(function updateFilterChangeRef() {
2435
+ handleFilterChangeRef.current = onFilterChange;
2436
+ }, [onFilterChange]);
1307
2437
  const [fromDate, setFromDate] = useState((_a = initialFilters === null || initialFilters === void 0 ? void 0 : initialFilters.fromDate) !== null && _a !== void 0 ? _a : "");
1308
2438
  const [toDate, setToDate] = useState((_b = initialFilters === null || initialFilters === void 0 ? void 0 : initialFilters.toDate) !== null && _b !== void 0 ? _b : "");
1309
2439
  const [logType, setLogType] = useState((_c = initialFilters === null || initialFilters === void 0 ? void 0 : initialFilters.logType) !== null && _c !== void 0 ? _c : null);
1310
2440
  const [sortOrder, setSortOrder] = useState((_d = initialFilters === null || initialFilters === void 0 ? void 0 : initialFilters.sortOrder) !== null && _d !== void 0 ? _d : "latest");
1311
- const typeOptions = [{
2441
+ const prevInitialFiltersRef = useRef(initialFilters);
2442
+ useEffect(function syncInitialFilters() {
2443
+ if (prevInitialFiltersRef.current !== initialFilters) {
2444
+ prevInitialFiltersRef.current = initialFilters;
2445
+ if (initialFilters) {
2446
+ if (initialFilters.fromDate !== undefined) setFromDate(initialFilters.fromDate);
2447
+ if (initialFilters.toDate !== undefined) setToDate(initialFilters.toDate);
2448
+ if (initialFilters.logType !== undefined) setLogType(initialFilters.logType);
2449
+ if (initialFilters.sortOrder !== undefined) setSortOrder(initialFilters.sortOrder);
2450
+ }
2451
+ }
2452
+ }, [initialFilters]);
2453
+ const typeOptions = useMemo(() => [{
1312
2454
  value: "",
1313
- displayText: "All"
2455
+ displayText: t("logs.filter.typeOptions.all", {
2456
+ defaultValue: "All"
2457
+ })
1314
2458
  }, {
1315
2459
  value: "error",
1316
- displayText: "Error"
2460
+ displayText: t("logs.filter.typeOptions.error", {
2461
+ defaultValue: "Error"
2462
+ })
1317
2463
  }, {
1318
2464
  value: "warning",
1319
- displayText: "Warning"
2465
+ displayText: t("logs.filter.typeOptions.warning", {
2466
+ defaultValue: "Warning"
2467
+ })
1320
2468
  }, {
1321
2469
  value: "info",
1322
- displayText: "Info"
1323
- }];
1324
- const sortOptions = [{
2470
+ displayText: t("logs.filter.typeOptions.info", {
2471
+ defaultValue: "Info"
2472
+ })
2473
+ }], [t]);
2474
+ const sortOptions = useMemo(() => [{
1325
2475
  value: "latest",
1326
- displayText: "From latest"
2476
+ displayText: t("logs.filter.sortOptions.latest", {
2477
+ defaultValue: "From latest"
2478
+ })
1327
2479
  }, {
1328
2480
  value: "oldest",
1329
- displayText: "From oldest"
1330
- }];
2481
+ displayText: t("logs.filter.sortOptions.oldest", {
2482
+ defaultValue: "From oldest"
2483
+ })
2484
+ }], [t]);
1331
2485
  const handleReset = () => {
1332
2486
  setFromDate("");
1333
2487
  setToDate("");
@@ -1336,20 +2490,23 @@ const LogFilterForm = memo(({
1336
2490
  onReset === null || onReset === void 0 ? void 0 : onReset();
1337
2491
  };
1338
2492
  useEffect(function notifyFilterChange() {
1339
- onFilterChange === null || onFilterChange === void 0 ? void 0 : onFilterChange({
2493
+ var _a;
2494
+ (_a = handleFilterChangeRef.current) === null || _a === void 0 ? void 0 : _a.call(handleFilterChangeRef, {
1340
2495
  fromDate,
1341
2496
  toDate,
1342
2497
  logType,
1343
2498
  sortOrder
1344
2499
  });
1345
- }, [fromDate, toDate, logType, sortOrder, onFilterChange]);
2500
+ }, [fromDate, toDate, logType, sortOrder]);
1346
2501
  return jsxs(Box, {
1347
2502
  className: classes.filterSection,
1348
2503
  children: [jsx(Box, {
1349
2504
  className: classes.filterInput,
1350
2505
  children: jsx(VentionTextInput, {
1351
2506
  size: "xx-large",
1352
- label: "From",
2507
+ label: t("logs.filter.from", {
2508
+ defaultValue: "From"
2509
+ }),
1353
2510
  value: fromDate,
1354
2511
  onChange: event => setFromDate(event.target.value),
1355
2512
  type: "date"
@@ -1358,7 +2515,9 @@ const LogFilterForm = memo(({
1358
2515
  className: classes.filterInput,
1359
2516
  children: jsx(VentionTextInput, {
1360
2517
  size: "xx-large",
1361
- label: "To",
2518
+ label: t("logs.filter.to", {
2519
+ defaultValue: "To"
2520
+ }),
1362
2521
  value: toDate,
1363
2522
  onChange: event => setToDate(event.target.value),
1364
2523
  type: "date"
@@ -1368,10 +2527,17 @@ const LogFilterForm = memo(({
1368
2527
  children: jsx(VentionSelect, {
1369
2528
  size: "xx-large",
1370
2529
  variant: "outlined",
1371
- labelText: "Type",
1372
- value: logType,
1373
- onChange: event => setLogType(event.target.value),
1374
- placeholder: "Choose type",
2530
+ labelText: t("logs.filter.type", {
2531
+ defaultValue: "Type"
2532
+ }),
2533
+ value: logType !== null && logType !== void 0 ? logType : "",
2534
+ onChange: event => {
2535
+ const value = event.target.value;
2536
+ setLogType(value === "" ? null : value);
2537
+ },
2538
+ placeholder: t("logs.filter.chooseType", {
2539
+ defaultValue: "Choose type"
2540
+ }),
1375
2541
  menuItems: typeOptions
1376
2542
  })
1377
2543
  }), jsx(Box, {
@@ -1379,7 +2545,9 @@ const LogFilterForm = memo(({
1379
2545
  children: jsx(VentionSelect, {
1380
2546
  size: "xx-large",
1381
2547
  variant: "outlined",
1382
- labelText: "Sort",
2548
+ labelText: t("logs.filter.sort", {
2549
+ defaultValue: "Sort"
2550
+ }),
1383
2551
  value: sortOrder,
1384
2552
  onChange: event => setSortOrder(event.target.value),
1385
2553
  menuItems: sortOptions
@@ -1388,12 +2556,14 @@ const LogFilterForm = memo(({
1388
2556
  size: "x-large",
1389
2557
  onClick: handleReset,
1390
2558
  className: classes.resetButton,
1391
- children: "Reset"
2559
+ children: t("logs.filter.reset", {
2560
+ defaultValue: "Reset"
2561
+ })
1392
2562
  })]
1393
2563
  });
1394
2564
  });
1395
2565
  LogFilterForm.displayName = "LogFilterForm";
1396
- const useStyles$2 = tss.create(({
2566
+ const useStyles$7 = tss.create(({
1397
2567
  theme
1398
2568
  }) => ({
1399
2569
  filterSection: {
@@ -1457,7 +2627,10 @@ const LogsPagination = memo(({
1457
2627
  }) => {
1458
2628
  const {
1459
2629
  classes
1460
- } = useStyles$1();
2630
+ } = useStyles$6();
2631
+ const {
2632
+ t
2633
+ } = useI18n();
1461
2634
  const handlePrevious = () => {
1462
2635
  if (currentPage > 1) {
1463
2636
  onPageChange(currentPage - 1);
@@ -1478,7 +2651,11 @@ const LogsPagination = memo(({
1478
2651
  children: "\u2190"
1479
2652
  }), jsxs(Typography, {
1480
2653
  variant: "heading18SemiBold",
1481
- children: ["Page ", currentPage, " / ", totalPages]
2654
+ children: [t("pagination.page", {
2655
+ defaultValue: "Page"
2656
+ }), " ", currentPage, " ", t("pagination.of", {
2657
+ defaultValue: "of"
2658
+ }), " ", totalPages]
1482
2659
  }), jsx(VentionButton, {
1483
2660
  size: "x-large",
1484
2661
  className: classes.paginationButton,
@@ -1489,7 +2666,7 @@ const LogsPagination = memo(({
1489
2666
  });
1490
2667
  });
1491
2668
  LogsPagination.displayName = "LogsPagination";
1492
- const useStyles$1 = tss.create(({
2669
+ const useStyles$6 = tss.create(({
1493
2670
  theme
1494
2671
  }) => ({
1495
2672
  paginationSection: {
@@ -1539,7 +2716,7 @@ const LogsPanel = forwardRef((props, ref) => {
1539
2716
  const {
1540
2717
  classes,
1541
2718
  cx
1542
- } = useStyles();
2719
+ } = useStyles$5();
1543
2720
  const [logs, setLogs] = useState([]);
1544
2721
  const [isLoading, setIsLoading] = useState(true);
1545
2722
  const [error, setError] = useState(null);
@@ -1554,43 +2731,45 @@ const LogsPanel = forwardRef((props, ref) => {
1554
2731
  const [currentPage, setCurrentPage] = useState((_g = pagination === null || pagination === void 0 ? void 0 : pagination.initialPage) !== null && _g !== void 0 ? _g : 1);
1555
2732
  const [totalPages, setTotalPages] = useState(1);
1556
2733
  const [hasMorePages, setHasMorePages] = useState(false);
1557
- const fetchData = useCallback(page => __awaiter(void 0, void 0, void 0, function* () {
1558
- let isCancelled = false;
1559
- setIsLoading(true);
1560
- setError(null);
1561
- try {
1562
- const result = yield dataFetcher({
1563
- filters,
1564
- page,
1565
- pageSize
1566
- });
1567
- if (!isCancelled) {
1568
- if (paginationMode === "infinite-scroll" && page > 1) {
1569
- setLogs(prev => [...prev, ...result.logs]);
1570
- } else {
1571
- setLogs(result.logs);
2734
+ const fetchData = useCallback(function fetchData(page, signal) {
2735
+ return __awaiter(this, void 0, void 0, function* () {
2736
+ setIsLoading(true);
2737
+ setError(null);
2738
+ try {
2739
+ const result = yield dataFetcher({
2740
+ filters,
2741
+ page,
2742
+ pageSize
2743
+ });
2744
+ if (!(signal === null || signal === void 0 ? void 0 : signal.aborted)) {
2745
+ if (paginationMode === "infinite-scroll" && page > 1) {
2746
+ setLogs(prev => [...prev, ...result.logs]);
2747
+ } else {
2748
+ setLogs(result.logs);
2749
+ }
2750
+ setTotalPages(result.totalPages);
2751
+ setHasMorePages(result.hasMorePages);
2752
+ setCurrentPage(result.currentPage);
2753
+ setIsLoading(false);
1572
2754
  }
1573
- setTotalPages(result.totalPages);
1574
- setHasMorePages(result.hasMorePages);
1575
- setCurrentPage(result.currentPage);
1576
- setIsLoading(false);
1577
- }
1578
- } catch (error) {
1579
- if (!isCancelled) {
1580
- const errorMessage = error instanceof Error ? error.message : "Failed to load logs";
1581
- setError(errorMessage);
1582
- setIsLoading(false);
1583
- if (onError) {
1584
- onError(error);
2755
+ } catch (error) {
2756
+ if (!(signal === null || signal === void 0 ? void 0 : signal.aborted)) {
2757
+ const errorMessage = error instanceof Error ? error.message : "Failed to load logs";
2758
+ setError(errorMessage);
2759
+ setIsLoading(false);
2760
+ if (onError) {
2761
+ onError(error);
2762
+ }
1585
2763
  }
1586
2764
  }
1587
- }
2765
+ });
2766
+ }, [dataFetcher, filters, pageSize, paginationMode, onError]);
2767
+ useEffect(function fetchDataOnChange() {
2768
+ const controller = new AbortController();
2769
+ fetchData(currentPage, controller.signal);
1588
2770
  return function cleanup() {
1589
- isCancelled = true;
2771
+ controller.abort();
1590
2772
  };
1591
- }), [dataFetcher, filters, pageSize, paginationMode, onError]);
1592
- useEffect(function fetchDataOnChange() {
1593
- fetchData(currentPage);
1594
2773
  }, [fetchData, currentPage]);
1595
2774
  const handlePageChange = useCallback(page => {
1596
2775
  setCurrentPage(page);
@@ -1672,7 +2851,7 @@ const LogsPanel = forwardRef((props, ref) => {
1672
2851
  });
1673
2852
  });
1674
2853
  LogsPanel.displayName = "LogsPanel";
1675
- const useStyles = tss.create(({
2854
+ const useStyles$5 = tss.create(({
1676
2855
  theme
1677
2856
  }) => ({
1678
2857
  root: {
@@ -1683,6 +2862,743 @@ const useStyles = tss.create(({
1683
2862
  }
1684
2863
  }));
1685
2864
 
2865
+ const SettingsPage = function SettingsPage({
2866
+ sidebarItems,
2867
+ children,
2868
+ sidebarWidth = 320
2869
+ }) {
2870
+ const {
2871
+ classes
2872
+ } = useStyles$4({
2873
+ sidebarWidth
2874
+ });
2875
+ return jsxs(Box, {
2876
+ className: classes.root,
2877
+ children: [jsx(Box, {
2878
+ className: classes.sidebar,
2879
+ children: jsx(Sidebar, {
2880
+ items: sidebarItems
2881
+ })
2882
+ }), jsx(Box, {
2883
+ className: classes.content,
2884
+ children: children
2885
+ })]
2886
+ });
2887
+ };
2888
+ SettingsPage.displayName = "SettingsPage";
2889
+ const useStyles$4 = tss.withParams().create(({
2890
+ theme,
2891
+ sidebarWidth
2892
+ }) => ({
2893
+ root: {
2894
+ display: "flex",
2895
+ flex: 1,
2896
+ height: "100%"
2897
+ },
2898
+ sidebar: {
2899
+ width: sidebarWidth,
2900
+ borderRight: `1px solid ${theme.palette.divider}`,
2901
+ backgroundColor: theme.palette.background.paper
2902
+ },
2903
+ content: {
2904
+ flex: 1,
2905
+ padding: theme.spacing(4),
2906
+ overflow: "auto"
2907
+ }
2908
+ }));
2909
+
2910
+ function formatFileSize(bytes) {
2911
+ if (bytes === 0) return "0 B";
2912
+ const units = ["B", "KB", "MB", "GB"];
2913
+ const exponent = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);
2914
+ const size = (bytes / Math.pow(1024, exponent)).toFixed(1);
2915
+ return `${size} ${units[exponent]}`;
2916
+ }
2917
+ function FileUploadPanel({
2918
+ title,
2919
+ files,
2920
+ onFilesSelect,
2921
+ onFileRemove,
2922
+ onFileRetry,
2923
+ onFileCancel,
2924
+ fileCriteriaDescription,
2925
+ dropzoneTitle,
2926
+ disabled = false,
2927
+ maxHeight
2928
+ }) {
2929
+ const {
2930
+ classes
2931
+ } = useStyles$3({
2932
+ maxHeight
2933
+ });
2934
+ const {
2935
+ t
2936
+ } = useTranslation();
2937
+ const displayTitle = title !== null && title !== void 0 ? title : t("fileUploadPanel.defaultTitle");
2938
+ const displayDropzoneTitle = dropzoneTitle !== null && dropzoneTitle !== void 0 ? dropzoneTitle : t("fileUploadPanel.dropzoneTitle");
2939
+ const displayFileCriteria = fileCriteriaDescription !== null && fileCriteriaDescription !== void 0 ? fileCriteriaDescription : t("fileUploadPanel.defaultFileCriteria");
2940
+ function handleFileRemove(fileId) {
2941
+ onFileRemove(fileId);
2942
+ }
2943
+ function handleFileRetry(fileId) {
2944
+ if (onFileRetry) {
2945
+ onFileRetry(fileId);
2946
+ }
2947
+ }
2948
+ function handleFileCancel(fileId) {
2949
+ if (onFileCancel) {
2950
+ onFileCancel(fileId);
2951
+ }
2952
+ }
2953
+ return jsxs(Box, {
2954
+ className: classes.container,
2955
+ children: [displayTitle && jsx(Typography, {
2956
+ variant: "heading18SemiBold",
2957
+ className: classes.title,
2958
+ children: displayTitle
2959
+ }), jsx(VentionDropZone, {
2960
+ style: "outline",
2961
+ size: "large",
2962
+ onFilesSelect: onFilesSelect,
2963
+ fileCriteriaDescription: displayFileCriteria,
2964
+ defaultTitle: displayDropzoneTitle,
2965
+ disabled: disabled
2966
+ }), files.length > 0 && jsx(Box, {
2967
+ className: classes.fileList,
2968
+ children: files.map(file => jsx(VentionUploadFile, {
2969
+ fileName: file.name,
2970
+ fileSize: formatFileSize(file.size),
2971
+ state: file.state,
2972
+ errorMessage: file.errorMessage,
2973
+ size: "large",
2974
+ style: "shaded",
2975
+ onRemove: () => handleFileRemove(file.id),
2976
+ onRetry: () => handleFileRetry(file.id),
2977
+ onCancel: () => handleFileCancel(file.id)
2978
+ }, file.id))
2979
+ })]
2980
+ });
2981
+ }
2982
+ FileUploadPanel.displayName = "FileUploadPanel";
2983
+ const useStyles$3 = tss.withParams().create(({
2984
+ theme,
2985
+ maxHeight
2986
+ }) => ({
2987
+ container: {
2988
+ display: "flex",
2989
+ flexDirection: "column",
2990
+ gap: theme.spacing(2),
2991
+ width: "100%"
2992
+ },
2993
+ title: {
2994
+ marginBottom: theme.spacing(1)
2995
+ },
2996
+ fileList: {
2997
+ display: "flex",
2998
+ flexDirection: "column",
2999
+ gap: theme.spacing(1),
3000
+ maxHeight: maxHeight !== null && maxHeight !== void 0 ? maxHeight : "auto",
3001
+ overflowY: maxHeight ? "auto" : "visible",
3002
+ "& .MuiGrid-container": {
3003
+ width: "100%"
3004
+ },
3005
+ "& .MuiGrid-root:nth-of-type(2)": {
3006
+ flex: "1 1 0",
3007
+ minWidth: 0
3008
+ }
3009
+ }
3010
+ }));
3011
+
3012
+ /** Padding between the circle edge and the SVG boundary */
3013
+ const CIRCLE_PADDING = 10;
3014
+ function StepProgressCircle({
3015
+ currentStep,
3016
+ totalSteps,
3017
+ title,
3018
+ variant = "default",
3019
+ size = 180,
3020
+ strokeWidth = 12,
3021
+ showTitle = true
3022
+ }) {
3023
+ var _a;
3024
+ const {
3025
+ t
3026
+ } = useTranslation();
3027
+ const {
3028
+ classes
3029
+ } = useStyles$2({
3030
+ size,
3031
+ strokeWidth,
3032
+ variant
3033
+ });
3034
+ const titleKey = (_a = title === null || title === void 0 ? void 0 : title.toLowerCase().replace(/\s/g, "")) !== null && _a !== void 0 ? _a : "progress";
3035
+ const displayTitle = t(`stepProgressCircle.title.${titleKey}`, {
3036
+ defaultValue: title !== null && title !== void 0 ? title : "Progress"
3037
+ });
3038
+ const percentage = totalSteps > 0 ? Math.round(currentStep / totalSteps * 100) : 0;
3039
+ const radius = (size - strokeWidth) / 2 - CIRCLE_PADDING;
3040
+ const circumference = 2 * Math.PI * radius;
3041
+ const strokeDashoffset = circumference - percentage / 100 * circumference;
3042
+ const center = size / 2;
3043
+ const progressText = totalSteps > 0 ? `${currentStep}/${totalSteps}` : "-/-";
3044
+ return jsxs(Box, {
3045
+ className: classes.container,
3046
+ children: [showTitle && jsx(Typography, {
3047
+ variant: "heading18SemiBold",
3048
+ className: classes.title,
3049
+ children: displayTitle
3050
+ }), jsxs(Box, {
3051
+ className: classes.circleContainer,
3052
+ children: [jsxs("svg", {
3053
+ className: classes.svg,
3054
+ width: size,
3055
+ height: size,
3056
+ viewBox: `0 0 ${size} ${size}`,
3057
+ children: [jsx("circle", {
3058
+ cx: center,
3059
+ cy: center,
3060
+ r: radius,
3061
+ className: classes.backgroundCircle,
3062
+ fill: "none"
3063
+ }), jsx("circle", {
3064
+ cx: center,
3065
+ cy: center,
3066
+ r: radius,
3067
+ className: classes.progressCircle,
3068
+ fill: "none",
3069
+ strokeLinecap: "round",
3070
+ transform: `rotate(-90 ${center} ${center})`,
3071
+ strokeDasharray: circumference,
3072
+ strokeDashoffset: strokeDashoffset
3073
+ })]
3074
+ }), jsx(Box, {
3075
+ className: classes.textContainer,
3076
+ children: jsx(Typography, {
3077
+ variant: "heading24SemiBold",
3078
+ className: classes.progressText,
3079
+ children: progressText
3080
+ })
3081
+ })]
3082
+ })]
3083
+ });
3084
+ }
3085
+ StepProgressCircle.displayName = "StepProgressCircle";
3086
+ const useStyles$2 = tss.withParams().create(({
3087
+ theme,
3088
+ size,
3089
+ strokeWidth,
3090
+ variant
3091
+ }) => {
3092
+ const variantColors = {
3093
+ default: theme.palette.primary.main,
3094
+ success: theme.palette.success.main,
3095
+ error: theme.palette.error.main,
3096
+ warning: theme.palette.warning.main
3097
+ };
3098
+ return {
3099
+ container: {
3100
+ display: "flex",
3101
+ flexDirection: "column",
3102
+ alignItems: "center",
3103
+ justifyContent: "center"
3104
+ },
3105
+ title: {
3106
+ marginBottom: theme.spacing(2),
3107
+ color: theme.palette.text.primary
3108
+ },
3109
+ circleContainer: {
3110
+ position: "relative",
3111
+ width: size,
3112
+ height: size
3113
+ },
3114
+ svg: {
3115
+ width: size,
3116
+ height: size
3117
+ },
3118
+ backgroundCircle: {
3119
+ stroke: theme.palette.grey[200],
3120
+ strokeWidth
3121
+ },
3122
+ progressCircle: {
3123
+ stroke: variantColors[variant],
3124
+ strokeWidth,
3125
+ transition: "stroke-dashoffset 0.5s ease-in-out, stroke 0.3s ease-in-out"
3126
+ },
3127
+ textContainer: {
3128
+ position: "absolute",
3129
+ top: "50%",
3130
+ left: "50%",
3131
+ transform: "translate(-50%, -50%)",
3132
+ display: "flex",
3133
+ alignItems: "center",
3134
+ justifyContent: "center"
3135
+ },
3136
+ progressText: {
3137
+ color: theme.palette.text.primary
3138
+ }
3139
+ };
3140
+ });
3141
+
3142
+ const ActionButton = function ActionButton({
3143
+ variant = "primary",
3144
+ onClick,
3145
+ disabled = false,
3146
+ icon,
3147
+ label,
3148
+ size = 96
3149
+ }) {
3150
+ const {
3151
+ classes
3152
+ } = useStyles$1({
3153
+ size
3154
+ });
3155
+ const {
3156
+ t
3157
+ } = useTranslation();
3158
+ const translationKey = label.toLowerCase().replace(/\s+/g, "_");
3159
+ const translatedLabel = t(`actionButton.${translationKey}`, {
3160
+ defaultValue: label
3161
+ });
3162
+ return jsx(VentionIconButton, {
3163
+ onClick: onClick,
3164
+ disabled: disabled,
3165
+ size: "x-large",
3166
+ variant: variant === "destructive" ? "destructive" : "filled",
3167
+ className: classes.button,
3168
+ "data-testid": "action-button",
3169
+ children: jsxs(Box, {
3170
+ className: classes.content,
3171
+ children: [jsx(Box, {
3172
+ className: classes.iconContainer,
3173
+ children: icon
3174
+ }), jsx(Typography, {
3175
+ variant: "uiText14SemiBold",
3176
+ className: classes.label,
3177
+ children: translatedLabel
3178
+ })]
3179
+ })
3180
+ });
3181
+ };
3182
+ ActionButton.displayName = "ActionButton";
3183
+ const useStyles$1 = tss.withParams().create(({
3184
+ theme,
3185
+ size
3186
+ }) => ({
3187
+ button: {
3188
+ height: size,
3189
+ width: size,
3190
+ minWidth: size,
3191
+ padding: theme.spacing(2)
3192
+ },
3193
+ content: {
3194
+ display: "flex",
3195
+ flexDirection: "column",
3196
+ alignItems: "center",
3197
+ justifyContent: "center",
3198
+ gap: theme.spacing(1)
3199
+ },
3200
+ iconContainer: {
3201
+ display: "flex",
3202
+ alignItems: "center",
3203
+ justifyContent: "center"
3204
+ },
3205
+ label: {
3206
+ textAlign: "center"
3207
+ }
3208
+ }));
3209
+
3210
+ function useAutoScrollInput(enabled, options = {}) {
3211
+ const {
3212
+ targetPositionPercent = 25,
3213
+ scrollableContainerSelector = '[role="dialog"] .MuiDialogContent-root',
3214
+ scrollBehavior = "smooth"
3215
+ } = options;
3216
+ useEffect(() => {
3217
+ if (!enabled) return;
3218
+ function findScrollableParent(element) {
3219
+ let parent = element.parentElement;
3220
+ while (parent) {
3221
+ const style = window.getComputedStyle(parent);
3222
+ const hasOverflow = style.overflow === "auto" || style.overflow === "scroll" || style.overflowY === "auto" || style.overflowY === "scroll";
3223
+ if (hasOverflow) {
3224
+ return parent;
3225
+ }
3226
+ if (parent === document.body || parent === document.documentElement) {
3227
+ break;
3228
+ }
3229
+ parent = parent.parentElement;
3230
+ }
3231
+ return null;
3232
+ }
3233
+ function handleFocus(event) {
3234
+ const target = event.target;
3235
+ if (!target || !(target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement || target instanceof HTMLSelectElement)) {
3236
+ return;
3237
+ }
3238
+ const targetRect = target.getBoundingClientRect();
3239
+ const viewportHeight = window.innerHeight;
3240
+ const targetY = viewportHeight * (targetPositionPercent / 100);
3241
+ const currentY = targetRect.top;
3242
+ const scrollNeeded = currentY - targetY;
3243
+ let scrollableContainer = null;
3244
+ if (scrollableContainerSelector) {
3245
+ const selectors = scrollableContainerSelector.split(",").map(selector => selector.trim());
3246
+ for (const selector of selectors) {
3247
+ const found = document.querySelector(selector);
3248
+ if (found) {
3249
+ scrollableContainer = found;
3250
+ break;
3251
+ }
3252
+ }
3253
+ }
3254
+ if (!scrollableContainer) {
3255
+ scrollableContainer = findScrollableParent(target);
3256
+ }
3257
+ if (scrollableContainer) {
3258
+ const currentScrollTop = scrollableContainer.scrollTop;
3259
+ const newScrollTop = currentScrollTop + scrollNeeded;
3260
+ scrollableContainer.scrollTo({
3261
+ top: newScrollTop,
3262
+ behavior: scrollBehavior
3263
+ });
3264
+ }
3265
+ }
3266
+ document.addEventListener("focusin", handleFocus);
3267
+ return () => {
3268
+ document.removeEventListener("focusin", handleFocus);
3269
+ };
3270
+ }, [enabled, targetPositionPercent, scrollableContainerSelector, scrollBehavior]);
3271
+ }
3272
+
3273
+ function ProductFormListComponent({
3274
+ title = "Products",
3275
+ items,
3276
+ onDelete,
3277
+ onSubmit,
3278
+ getItemId,
3279
+ fields,
3280
+ maxHeight = 600
3281
+ }) {
3282
+ const [editDialogOpen, setEditDialogOpen] = useState(false);
3283
+ const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
3284
+ const [deleteId, setDeleteId] = useState(null);
3285
+ const [formData, setFormData] = useState({});
3286
+ const {
3287
+ t
3288
+ } = useTranslation();
3289
+ const {
3290
+ classes
3291
+ } = useStyles({
3292
+ maxHeight
3293
+ });
3294
+ useAutoScrollInput(editDialogOpen);
3295
+ function handleEditClick(item) {
3296
+ setFormData(item);
3297
+ setEditDialogOpen(true);
3298
+ }
3299
+ function handleEditClose() {
3300
+ setEditDialogOpen(false);
3301
+ setFormData({});
3302
+ }
3303
+ function handleDeleteClick(id) {
3304
+ setDeleteId(id);
3305
+ setDeleteDialogOpen(true);
3306
+ }
3307
+ function handleDeleteConfirm() {
3308
+ return __awaiter(this, void 0, void 0, function* () {
3309
+ if (deleteId) {
3310
+ yield onDelete(deleteId);
3311
+ setDeleteDialogOpen(false);
3312
+ setDeleteId(null);
3313
+ }
3314
+ });
3315
+ }
3316
+ function handleDeleteCancel() {
3317
+ setDeleteDialogOpen(false);
3318
+ setDeleteId(null);
3319
+ }
3320
+ function handleFieldChange(name, value) {
3321
+ setFormData(prev => Object.assign(Object.assign({}, prev), {
3322
+ [name]: value
3323
+ }));
3324
+ }
3325
+ function getFieldDisplayValue(field, value) {
3326
+ var _a, _b;
3327
+ if (field.type === "select") {
3328
+ return ((_b = (_a = field.options) === null || _a === void 0 ? void 0 : _a.find(opt => opt.value === value)) === null || _b === void 0 ? void 0 : _b.label) || String(value);
3329
+ }
3330
+ return (value === null || value === void 0 ? void 0 : value.toString()) || "";
3331
+ }
3332
+ function renderItemContent(item) {
3333
+ const mainField = fields[0];
3334
+ const secondaryFields = fields.slice(1);
3335
+ return jsxs(Box, {
3336
+ className: classes.itemContent,
3337
+ children: [jsx(Typography, {
3338
+ variant: "heading18SemiBold",
3339
+ className: classes.itemTitle,
3340
+ children: getFieldDisplayValue(mainField, item[mainField.name])
3341
+ }), jsx(Typography, {
3342
+ variant: "uiText14Regular",
3343
+ color: "textSecondary",
3344
+ className: classes.itemSubtitle,
3345
+ children: secondaryFields.map(field => `${field.label}: ${getFieldDisplayValue(field, item[field.name])}`).join(" | ")
3346
+ })]
3347
+ });
3348
+ }
3349
+ function renderField(field) {
3350
+ var _a;
3351
+ const fieldValue = formData[field.name];
3352
+ if (field.type === "select") {
3353
+ const menuItems = ((_a = field.options) === null || _a === void 0 ? void 0 : _a.map(opt => ({
3354
+ value: opt.value,
3355
+ displayText: opt.label
3356
+ }))) || [];
3357
+ return jsx(VentionSelect, {
3358
+ labelText: field.label,
3359
+ value: fieldValue || "",
3360
+ onChange: event => {
3361
+ handleFieldChange(field.name, event.target.value);
3362
+ },
3363
+ menuItems: menuItems,
3364
+ size: "large",
3365
+ variant: "outlined"
3366
+ }, field.name);
3367
+ }
3368
+ return jsx(VentionTextInput, {
3369
+ label: field.label,
3370
+ value: fieldValue !== null && fieldValue !== void 0 ? fieldValue : "",
3371
+ onChange: event => {
3372
+ handleFieldChange(field.name, field.type === "number" ? Number(event.target.value) : event.target.value);
3373
+ },
3374
+ type: field.type === "number" ? "number" : "text",
3375
+ fullWidth: true,
3376
+ size: "large",
3377
+ inputProps: field.type === "number" ? {
3378
+ min: field.min,
3379
+ max: field.max
3380
+ } : undefined
3381
+ }, field.name);
3382
+ }
3383
+ const isEditing = Object.keys(formData).length > 0 && "id" in formData;
3384
+ return jsxs(Box, {
3385
+ className: classes.container,
3386
+ children: [jsxs(Box, {
3387
+ className: classes.header,
3388
+ children: [jsx(Typography, {
3389
+ variant: "heading24SemiBold",
3390
+ children: title
3391
+ }), jsx(VentionButton, {
3392
+ onClick: () => {
3393
+ setFormData({});
3394
+ setEditDialogOpen(true);
3395
+ },
3396
+ size: "large",
3397
+ variant: "filled",
3398
+ startIcon: jsx(VentionIcon, {
3399
+ type: "plus",
3400
+ size: 24,
3401
+ color: "white"
3402
+ }),
3403
+ children: t("productFormList.add")
3404
+ })]
3405
+ }), jsx(Box, {
3406
+ className: classes.listContainer,
3407
+ children: items.length === 0 ? jsx(Box, {
3408
+ className: classes.emptyState,
3409
+ children: jsx(Typography, {
3410
+ variant: "uiText14Regular",
3411
+ color: "textSecondary",
3412
+ children: t("productFormList.emptyState", {
3413
+ defaultValue: "No items yet. Click Add to create one."
3414
+ })
3415
+ })
3416
+ }) : jsx(List, {
3417
+ children: items.map(item => jsxs(ListItem, {
3418
+ className: classes.listItem,
3419
+ children: [renderItemContent(item), jsxs(Box, {
3420
+ className: classes.actions,
3421
+ children: [jsx(IconButton, {
3422
+ onClick: () => handleEditClick(item),
3423
+ size: "medium",
3424
+ children: jsx(VentionIcon, {
3425
+ type: "edit",
3426
+ size: 24
3427
+ })
3428
+ }), jsx(IconButton, {
3429
+ onClick: () => handleDeleteClick(getItemId(item)),
3430
+ size: "medium",
3431
+ children: jsx(VentionIcon, {
3432
+ type: "trash",
3433
+ size: 24
3434
+ })
3435
+ })]
3436
+ })]
3437
+ }, getItemId(item)))
3438
+ })
3439
+ }), jsxs(Dialog, {
3440
+ open: editDialogOpen,
3441
+ onClose: handleEditClose,
3442
+ maxWidth: "sm",
3443
+ fullWidth: true,
3444
+ children: [jsx(DialogTitle, {
3445
+ children: jsx(Typography, {
3446
+ variant: "heading18SemiBold",
3447
+ textAlign: "center",
3448
+ children: isEditing ? t("productFormList.modify") : t("productFormList.new")
3449
+ })
3450
+ }), jsx(DialogContent, {
3451
+ className: classes.dialogContent,
3452
+ children: jsx(Box, {
3453
+ className: classes.dialogForm,
3454
+ children: fields.map(field => renderField(field))
3455
+ })
3456
+ }), jsxs(DialogActions, {
3457
+ className: classes.dialogActions,
3458
+ children: [jsx(VentionButton, {
3459
+ onClick: handleEditClose,
3460
+ variant: "outline",
3461
+ size: "large",
3462
+ children: t("productFormList.cancel")
3463
+ }), jsx(VentionButton, {
3464
+ onClick: () => __awaiter(this, void 0, void 0, function* () {
3465
+ if (yield onSubmit(formData)) {
3466
+ handleEditClose();
3467
+ }
3468
+ }),
3469
+ variant: "filled",
3470
+ size: "large",
3471
+ children: isEditing ? t("productFormList.save") : t("productFormList.create")
3472
+ })]
3473
+ })]
3474
+ }), jsxs(Dialog, {
3475
+ open: deleteDialogOpen,
3476
+ onClose: handleDeleteCancel,
3477
+ maxWidth: "xs",
3478
+ fullWidth: true,
3479
+ children: [jsx(DialogTitle, {
3480
+ children: jsx(Typography, {
3481
+ variant: "heading18SemiBold",
3482
+ textAlign: "center",
3483
+ children: t("productFormList.delete")
3484
+ })
3485
+ }), jsx(DialogContent, {
3486
+ children: jsx(Typography, {
3487
+ variant: "uiText14Regular",
3488
+ className: classes.deleteMessage,
3489
+ children: t("productFormList.deleteConfirmation")
3490
+ })
3491
+ }), jsxs(DialogActions, {
3492
+ className: classes.dialogActions,
3493
+ children: [jsx(VentionButton, {
3494
+ onClick: handleDeleteCancel,
3495
+ variant: "outline",
3496
+ size: "large",
3497
+ children: t("productFormList.cancel")
3498
+ }), jsx(VentionButton, {
3499
+ onClick: handleDeleteConfirm,
3500
+ variant: "destructive",
3501
+ size: "large",
3502
+ children: t("productFormList.delete")
3503
+ })]
3504
+ })]
3505
+ })]
3506
+ });
3507
+ }
3508
+ ProductFormListComponent.displayName = "ProductFormList";
3509
+ const ProductFormList = memo(ProductFormListComponent);
3510
+ const useStyles = tss.withParams().create(({
3511
+ theme,
3512
+ maxHeight
3513
+ }) => ({
3514
+ container: {
3515
+ display: "flex",
3516
+ flexDirection: "column",
3517
+ height: "100%",
3518
+ flex: 1,
3519
+ minWidth: 0
3520
+ },
3521
+ header: {
3522
+ flexShrink: 0,
3523
+ display: "flex",
3524
+ justifyContent: "space-between",
3525
+ alignItems: "center",
3526
+ marginBottom: theme.spacing(3)
3527
+ },
3528
+ listContainer: {
3529
+ flexGrow: 1,
3530
+ overflowY: "auto",
3531
+ maxHeight
3532
+ },
3533
+ listItem: {
3534
+ backgroundColor: theme.palette.background.paper,
3535
+ borderRadius: theme.shape.borderRadius,
3536
+ marginBottom: theme.spacing(2),
3537
+ alignItems: "center",
3538
+ overflow: "hidden",
3539
+ display: "flex",
3540
+ width: "100%"
3541
+ },
3542
+ itemContent: {
3543
+ minWidth: 0,
3544
+ flex: 1,
3545
+ overflow: "hidden",
3546
+ paddingRight: theme.spacing(2)
3547
+ },
3548
+ itemTitle: {
3549
+ overflow: "hidden",
3550
+ textOverflow: "ellipsis",
3551
+ whiteSpace: "nowrap",
3552
+ width: "100%"
3553
+ },
3554
+ itemSubtitle: {
3555
+ overflow: "hidden",
3556
+ textOverflow: "ellipsis",
3557
+ whiteSpace: "nowrap",
3558
+ width: "100%"
3559
+ },
3560
+ actions: {
3561
+ display: "flex",
3562
+ alignItems: "center",
3563
+ flexShrink: 0,
3564
+ gap: theme.spacing(1)
3565
+ },
3566
+ emptyState: {
3567
+ display: "flex",
3568
+ alignItems: "center",
3569
+ justifyContent: "center",
3570
+ padding: theme.spacing(4),
3571
+ flex: 1
3572
+ },
3573
+ dialogContent: {
3574
+ borderTop: `1px solid ${theme.palette.divider}`,
3575
+ borderBottom: `1px solid ${theme.palette.divider}`
3576
+ },
3577
+ dialogForm: {
3578
+ display: "flex",
3579
+ flexDirection: "column",
3580
+ gap: theme.spacing(3),
3581
+ paddingTop: theme.spacing(2)
3582
+ },
3583
+ dialogActions: {
3584
+ padding: theme.spacing(2),
3585
+ gap: theme.spacing(2)
3586
+ },
3587
+ deleteMessage: {
3588
+ paddingTop: theme.spacing(1)
3589
+ }
3590
+ }));
3591
+
3592
+ var UserLevel;
3593
+ (function (UserLevel) {
3594
+ UserLevel[UserLevel["Operator"] = 1] = "Operator";
3595
+ UserLevel[UserLevel["Maintenance"] = 2] = "Maintenance";
3596
+ UserLevel[UserLevel["Admin"] = 3] = "Admin";
3597
+ })(UserLevel || (UserLevel = {}));
3598
+ function hasSufficientLevel(requiredLevel, currentLevel) {
3599
+ return currentLevel >= requiredLevel;
3600
+ }
3601
+
1686
3602
  /**
1687
3603
  * This module provides utilities for generating API base URLs for machine apps.
1688
3604
  * It automatically detects the environment and returns the appropriate URL:
@@ -1739,4 +3655,4 @@ function getApiBaseUrl(options) {
1739
3655
  return getExecutionEngineHttpUrl(processName);
1740
3656
  }
1741
3657
 
1742
- export { LogFilterForm, LogsPagination, LogsPanel, LogsTable, NavigationBar, NavigationConfirmationModal, PasswordProtectionModal, Sidebar, StatusTopBar, TimeLabel, closeCustomUi, formatDateToInput, getApiBaseUrl, isOnEdge, isTouchScreenDevice, navigateControlCenter, parseLogDate };
3658
+ export { ActionButton, FileUploadPanel, I18nProvider, I18nSettings, LogFilterForm, LogsPagination, LogsPanel, LogsTable, NavigationBar, NavigationConfirmationModal, PasswordProtectionModal, ProductFormList, SettingsPage, Sidebar, StatusTopBar, StepProgressCircle, TimeLabel, UserLevel, Z_INDEX, closeCustomUi, formatDateToInput, getApiBaseUrl, getAvailableTimezones, getLanguageDisplayName, getSupportedLanguages, hasSufficientLevel, isOnEdge, isTouchScreenDevice, navigateControlCenter, parseLogDate, useAutoScrollInput, useI18n };