@vention/machine-apps-components 0.0.0-dev.4527.a3d343ca7

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 (50) hide show
  1. package/README.md +859 -0
  2. package/index.esm.d.ts +1 -0
  3. package/index.esm.js +3658 -0
  4. package/package.json +40 -0
  5. package/src/constants/z-index.d.ts +8 -0
  6. package/src/contexts/i18n-context.d.ts +12 -0
  7. package/src/contexts/i18n-provider.d.ts +13 -0
  8. package/src/hooks/use-auto-scroll-input.d.ts +6 -0
  9. package/src/hooks/use-i18n.d.ts +19 -0
  10. package/src/i18n/config.d.ts +8 -0
  11. package/src/i18n/locales/de.d.ts +131 -0
  12. package/src/i18n/locales/en.d.ts +131 -0
  13. package/src/i18n/locales/es.d.ts +131 -0
  14. package/src/i18n/locales/fr.d.ts +131 -0
  15. package/src/i18n/utils.d.ts +13 -0
  16. package/src/index.d.ts +27 -0
  17. package/src/lib/action-button/action-button.d.ts +14 -0
  18. package/src/lib/action-button/action-button.stories.d.ts +15 -0
  19. package/src/lib/file-upload-panel/file-upload-panel.d.ts +23 -0
  20. package/src/lib/file-upload-panel/file-upload-panel.stories.d.ts +12 -0
  21. package/src/lib/i18n-settings/i18n-settings.d.ts +8 -0
  22. package/src/lib/i18n-settings/i18n-settings.stories.d.ts +7 -0
  23. package/src/lib/logs/log-filter-form.d.ts +9 -0
  24. package/src/lib/logs/logs-pagination.d.ts +8 -0
  25. package/src/lib/logs/logs-panel.d.ts +59 -0
  26. package/src/lib/logs/logs-panel.stories.d.ts +6 -0
  27. package/src/lib/logs/logs-table.d.ts +15 -0
  28. package/src/lib/navigation-bar/navigation-bar-item.d.ts +17 -0
  29. package/src/lib/navigation-bar/navigation-bar.d.ts +20 -0
  30. package/src/lib/navigation-bar/navigation-bar.stories.d.ts +6 -0
  31. package/src/lib/navigation-bar/navigation-confirmation-modal.d.ts +7 -0
  32. package/src/lib/navigation-bar/password-protection-modal.d.ts +8 -0
  33. package/src/lib/navigation-bar/password-protection-modal.stories.d.ts +6 -0
  34. package/src/lib/navigation-bar/time-label.d.ts +6 -0
  35. package/src/lib/product-form-list/product-form-list.d.ts +27 -0
  36. package/src/lib/product-form-list/product-form-list.stories.d.ts +18 -0
  37. package/src/lib/settings-page/settings-page.d.ts +11 -0
  38. package/src/lib/settings-page/settings-page.stories.d.ts +9 -0
  39. package/src/lib/sidebar/sidebar.d.ts +18 -0
  40. package/src/lib/sidebar/sidebar.stories.d.ts +8 -0
  41. package/src/lib/status-top-bar/status-top-bar-button.d.ts +17 -0
  42. package/src/lib/status-top-bar/status-top-bar.d.ts +27 -0
  43. package/src/lib/status-top-bar/status-top-bar.stories.d.ts +10 -0
  44. package/src/lib/step-progress-circle/step-progress-circle.d.ts +14 -0
  45. package/src/lib/step-progress-circle/step-progress-circle.stories.d.ts +16 -0
  46. package/src/lib/utils/api-config-utils.d.ts +34 -0
  47. package/src/lib/utils/device-utils.d.ts +14 -0
  48. package/src/test-setup.d.ts +1 -0
  49. package/src/test-utils.d.ts +13 -0
  50. package/src/types/user-level.d.ts +6 -0
package/index.esm.js ADDED
@@ -0,0 +1,3658 @@
1
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
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';
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
+ }));
942
+
943
+ function TimeLabel({
944
+ className,
945
+ color
946
+ }) {
947
+ const theme = useTheme();
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) {
968
+ setTimeLabel(new Date().toLocaleTimeString([], {
969
+ hour: "numeric",
970
+ minute: "2-digit"
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
+ }
985
+ }, 1000);
986
+ return () => clearInterval(intervalId);
987
+ }, [formatDate, timezone, locale]);
988
+ return jsx(Typography, {
989
+ className: className,
990
+ color: color !== null && color !== void 0 ? color : theme.palette.common.white,
991
+ children: timeLabel
992
+ });
993
+ }
994
+
995
+ /**
996
+ * Detects if the current device has touch screen capabilities
997
+ * @returns true if the device supports touch events, false otherwise
998
+ */
999
+ const isTouchScreenDevice = () => {
1000
+ return "ontouchstart" in window || navigator.maxTouchPoints > 0;
1001
+ };
1002
+ /**
1003
+ * Closes the custom UI by sending a message to the parent window
1004
+ */
1005
+ const closeCustomUi = () => {
1006
+ window.parent.postMessage("closeCustomUi", "*");
1007
+ };
1008
+ /**
1009
+ * Navigates to a specific route in the Control Center
1010
+ * @param route - The route to navigate to (remoteSupport or controlCenterHomepage)
1011
+ */
1012
+ const navigateControlCenter = route => {
1013
+ const capitalizedRoute = route.charAt(0).toUpperCase() + route.slice(1);
1014
+ window.parent.postMessage(`navigateTo${capitalizedRoute}`, "*");
1015
+ };
1016
+
1017
+ const NavigationConfirmationModal = function NavigationConfirmationModal(props) {
1018
+ const {
1019
+ isOpen,
1020
+ destination,
1021
+ onClose
1022
+ } = props;
1023
+ const {
1024
+ classes
1025
+ } = useStyles$f();
1026
+ const {
1027
+ t
1028
+ } = useI18n();
1029
+ const handleConfirm = function handleConfirm() {
1030
+ if (!destination) return;
1031
+ onClose();
1032
+ if (destination === "controlCenter") {
1033
+ closeCustomUi();
1034
+ } else {
1035
+ navigateControlCenter(destination);
1036
+ }
1037
+ };
1038
+ const getButtonText = function getButtonText() {
1039
+ if (destination === "controlCenter") {
1040
+ return t("navigation.modal.goToControlCenter", {
1041
+ defaultValue: "Go to Control Center"
1042
+ });
1043
+ }
1044
+ return t("navigation.modal.goToRemoteSupport", {
1045
+ defaultValue: "Go to Remote Support"
1046
+ });
1047
+ };
1048
+ return jsx(VentionModalBase, {
1049
+ isOpen: isOpen,
1050
+ onClose: onClose,
1051
+ modalSize: "xx-large",
1052
+ isTouchDevice: isTouchScreenDevice(),
1053
+ width: "1312px",
1054
+ children: jsxs(Box, {
1055
+ className: classes.modalContent,
1056
+ children: [jsx(Box, {
1057
+ className: classes.iconContainer,
1058
+ children: jsx(VentionIcon, {
1059
+ type: "alert-triangle-filled",
1060
+ size: 56,
1061
+ color: "#F59E0B"
1062
+ })
1063
+ }), jsx(Typography, {
1064
+ className: classes.title,
1065
+ children: t("navigation.modal.exit.title", {
1066
+ defaultValue: "You are about to exit the application"
1067
+ })
1068
+ }), jsx(Typography, {
1069
+ className: classes.body,
1070
+ children: t("navigation.modal.exit.message", {
1071
+ defaultValue: "Please make sure your changes are saved before you leave the app."
1072
+ })
1073
+ }), jsx(Button, {
1074
+ onClick: handleConfirm,
1075
+ className: classes.button,
1076
+ disableRipple: true,
1077
+ children: jsx(Box, {
1078
+ className: classes.buttonLabel,
1079
+ children: jsx(Typography, {
1080
+ className: classes.buttonText,
1081
+ children: getButtonText()
1082
+ })
1083
+ })
1084
+ })]
1085
+ })
1086
+ });
1087
+ };
1088
+ const useStyles$f = tss.create(({
1089
+ theme
1090
+ }) => ({
1091
+ modalContent: {
1092
+ display: "flex",
1093
+ flexDirection: "column",
1094
+ alignItems: "center",
1095
+ padding: "0 96px 24px 96px",
1096
+ height: "360px",
1097
+ boxSizing: "border-box"
1098
+ },
1099
+ iconContainer: {
1100
+ marginBottom: theme.spacing(7)
1101
+ },
1102
+ title: Object.assign(Object.assign({}, theme.typography.hmiText28SemiBold), {
1103
+ textAlign: "center",
1104
+ marginBottom: theme.spacing(6)
1105
+ }),
1106
+ body: Object.assign(Object.assign({}, theme.typography.hmiText22Regular), {
1107
+ textAlign: "center"
1108
+ }),
1109
+ button: {
1110
+ minWidth: "320px",
1111
+ width: "auto",
1112
+ maxWidth: "100%",
1113
+ height: "80px",
1114
+ backgroundColor: theme.palette.button.filled,
1115
+ borderRadius: "4px",
1116
+ padding: "8px 16px",
1117
+ boxSizing: "border-box",
1118
+ marginTop: "auto",
1119
+ "&:hover": {
1120
+ backgroundColor: theme.palette.button.filledHover
1121
+ }
1122
+ },
1123
+ buttonLabel: {
1124
+ display: "flex",
1125
+ alignItems: "center",
1126
+ justifyContent: "center",
1127
+ padding: "0 8px",
1128
+ boxSizing: "border-box",
1129
+ width: "100%"
1130
+ },
1131
+ buttonText: {
1132
+ fontFamily: "Inter",
1133
+ fontSize: "24px",
1134
+ fontWeight: 600,
1135
+ lineHeight: "32px",
1136
+ color: theme.palette.common.white,
1137
+ textAlign: "center",
1138
+ whiteSpace: "nowrap"
1139
+ }
1140
+ }));
1141
+
1142
+ const PasswordProtectionModal = function PasswordProtectionModal(props) {
1143
+ const {
1144
+ isOpen,
1145
+ onClose,
1146
+ onSuccess,
1147
+ correctPassword
1148
+ } = props;
1149
+ const [isKeyboardVisible, setIsKeyboardVisible] = useState(false);
1150
+ const {
1151
+ classes
1152
+ } = useStyles$e();
1153
+ const {
1154
+ t
1155
+ } = useI18n();
1156
+ const [password, setPassword] = useState("");
1157
+ const [showPassword, setShowPassword] = useState(false);
1158
+ const [error, setError] = useState(false);
1159
+ const handleConfirm = function handleConfirm() {
1160
+ if (password === correctPassword) {
1161
+ setPassword("");
1162
+ setError(false);
1163
+ onSuccess();
1164
+ onClose();
1165
+ } else {
1166
+ setError(true);
1167
+ }
1168
+ };
1169
+ const handleClose = function handleClose() {
1170
+ setPassword("");
1171
+ setError(false);
1172
+ onClose();
1173
+ };
1174
+ useEffect(function handleKeyboardVisibility() {
1175
+ if (!isOpen || !isTouchScreenDevice()) {
1176
+ setIsKeyboardVisible(false);
1177
+ return;
1178
+ }
1179
+ const checkKeyboard = () => {
1180
+ const keyboard = document.getElementById("virtual_keyboard");
1181
+ setIsKeyboardVisible(keyboard !== null);
1182
+ };
1183
+ checkKeyboard();
1184
+ const observer = new MutationObserver(checkKeyboard);
1185
+ observer.observe(document.body, {
1186
+ childList: true,
1187
+ subtree: true
1188
+ });
1189
+ return () => {
1190
+ observer.disconnect();
1191
+ setIsKeyboardVisible(false);
1192
+ };
1193
+ }, [isOpen]);
1194
+ return jsxs(Fragment, {
1195
+ children: [jsx("style", {
1196
+ children: isKeyboardVisible && `
1197
+ [data-testid="vention-modal-container"] {
1198
+ transform: translateY(-22.5vh) !important;
1199
+ transition: transform 0.3s ease-out;
1200
+ }
1201
+ `
1202
+ }), jsx(VentionModalBase, {
1203
+ isOpen: isOpen,
1204
+ onClose: handleClose,
1205
+ modalSize: "x-large",
1206
+ isTouchDevice: isTouchScreenDevice(),
1207
+ width: "1120px",
1208
+ backDropClickClosable: false,
1209
+ children: jsxs(Box, {
1210
+ className: classes.modalContent,
1211
+ children: [jsx(Typography, {
1212
+ className: classes.title,
1213
+ children: t("navigation.modal.password.title", {
1214
+ defaultValue: "Administrator login required"
1215
+ })
1216
+ }), jsx(Box, {
1217
+ component: "form",
1218
+ id: "password-form",
1219
+ onSubmit: event => {
1220
+ event.preventDefault();
1221
+ handleConfirm();
1222
+ },
1223
+ className: classes.inputContainer,
1224
+ children: jsx(VentionTextInput, {
1225
+ label: t("navigation.modal.password.label", {
1226
+ defaultValue: "Password"
1227
+ }),
1228
+ type: showPassword ? "text" : "password",
1229
+ value: password,
1230
+ onChange: event => {
1231
+ setPassword(event.target.value);
1232
+ setError(false);
1233
+ },
1234
+ state: error ? "error" : "default",
1235
+ helperText: error ? t("navigation.modal.password.error", {
1236
+ defaultValue: "Incorrect password"
1237
+ }) : "",
1238
+ fullWidth: true,
1239
+ size: "xx-large",
1240
+ InputProps: {
1241
+ endAdornment: jsx(InputAdornment, {
1242
+ position: "end",
1243
+ children: jsx(IconButton, {
1244
+ onClick: () => setShowPassword(!showPassword),
1245
+ edge: "end",
1246
+ disableRipple: true,
1247
+ type: "button",
1248
+ children: jsx(VentionIcon, {
1249
+ type: showPassword ? "eye-closed" : "eye",
1250
+ size: 28,
1251
+ color: "#0f172a"
1252
+ })
1253
+ })
1254
+ })
1255
+ }
1256
+ })
1257
+ }), jsx(Button, {
1258
+ className: classes.button,
1259
+ disableRipple: true,
1260
+ type: "submit",
1261
+ form: "password-form",
1262
+ children: jsx(Box, {
1263
+ className: classes.buttonLabel,
1264
+ children: jsx(Typography, {
1265
+ className: classes.buttonText,
1266
+ children: t("navigation.modal.password.confirm", {
1267
+ defaultValue: "Confirm"
1268
+ })
1269
+ })
1270
+ })
1271
+ })]
1272
+ })
1273
+ })]
1274
+ });
1275
+ };
1276
+ const useStyles$e = tss.create(({
1277
+ theme
1278
+ }) => ({
1279
+ modalContent: {
1280
+ display: "flex",
1281
+ flexDirection: "column",
1282
+ alignItems: "center",
1283
+ padding: "64px 96px 64px 96px",
1284
+ gap: theme.spacing(7),
1285
+ boxSizing: "border-box",
1286
+ height: "324px"
1287
+ },
1288
+ title: {
1289
+ fontFamily: "Inter",
1290
+ fontSize: "36px",
1291
+ fontWeight: 700,
1292
+ lineHeight: "48px",
1293
+ textAlign: "center",
1294
+ color: theme.palette.text.primary
1295
+ },
1296
+ inputContainer: {
1297
+ width: "499px",
1298
+ display: "flex",
1299
+ flexDirection: "column",
1300
+ gap: theme.spacing(1)
1301
+ },
1302
+ button: {
1303
+ width: "250px",
1304
+ height: "64px",
1305
+ backgroundColor: theme.palette.button.filled,
1306
+ borderRadius: "4px",
1307
+ padding: "8px",
1308
+ boxSizing: "border-box",
1309
+ "&:hover": {
1310
+ backgroundColor: theme.palette.button.filledHover
1311
+ }
1312
+ },
1313
+ buttonLabel: {
1314
+ display: "flex",
1315
+ alignItems: "center",
1316
+ justifyContent: "center",
1317
+ padding: "0 8px",
1318
+ boxSizing: "border-box"
1319
+ },
1320
+ buttonText: {
1321
+ fontFamily: "Inter",
1322
+ fontSize: "24px",
1323
+ fontWeight: 600,
1324
+ lineHeight: "32px",
1325
+ color: theme.palette.common.white,
1326
+ textAlign: "center",
1327
+ whiteSpace: "nowrap"
1328
+ }
1329
+ }));
1330
+
1331
+ const NavigationBarItem = function NavigationBarItem(props) {
1332
+ const {
1333
+ icon,
1334
+ iconDisabled,
1335
+ label,
1336
+ path,
1337
+ onClick,
1338
+ password,
1339
+ isDisabled = false,
1340
+ location,
1341
+ handleNavigate,
1342
+ currentPassword,
1343
+ setPasswordProtectedItem
1344
+ } = props;
1345
+ const {
1346
+ classes,
1347
+ cx
1348
+ } = useStyles$d();
1349
+ const {
1350
+ t
1351
+ } = useI18n();
1352
+ const isActive = (location === null || location === void 0 ? void 0 : location.pathname) === path;
1353
+ const currentIcon = isDisabled && iconDisabled ? iconDisabled : icon;
1354
+ const handleItemClick = function handleItemClick() {
1355
+ if (isDisabled || !handleNavigate || !setPasswordProtectedItem) return;
1356
+ if (password && password !== currentPassword) {
1357
+ setPasswordProtectedItem(props);
1358
+ return;
1359
+ }
1360
+ onClick ? onClick() : handleNavigate(path);
1361
+ };
1362
+ return jsxs(Button, {
1363
+ disableRipple: true,
1364
+ disabled: isDisabled,
1365
+ onClick: handleItemClick,
1366
+ className: cx(classes.tabButton, {
1367
+ [classes.active]: isActive,
1368
+ [classes.disabled]: isDisabled
1369
+ }),
1370
+ children: [jsx(Box, {
1371
+ className: classes.navIcon,
1372
+ children: currentIcon
1373
+ }), jsx(Typography, {
1374
+ className: classes.navLabel,
1375
+ children: t(label, {
1376
+ defaultValue: label
1377
+ })
1378
+ })]
1379
+ });
1380
+ };
1381
+ const useStyles$d = tss.create(({
1382
+ theme
1383
+ }) => ({
1384
+ tabButton: {
1385
+ display: "flex",
1386
+ alignItems: "center",
1387
+ justifyContent: "center",
1388
+ flexDirection: "column",
1389
+ height: "96px",
1390
+ width: 184,
1391
+ gap: theme.spacing(2),
1392
+ borderRadius: 8,
1393
+ backgroundColor: "transparent",
1394
+ transition: "background-color 0.2s",
1395
+ "&:hover": {
1396
+ backgroundColor: theme.palette.button.filledHover
1397
+ },
1398
+ color: theme.palette.common.white,
1399
+ "&.Mui-disabled": {
1400
+ color: theme.palette.common.white
1401
+ }
1402
+ },
1403
+ active: {
1404
+ backgroundColor: theme.palette.button.filledPressed
1405
+ },
1406
+ disabled: {
1407
+ opacity: 0.5,
1408
+ cursor: "not-allowed",
1409
+ "&:hover": {
1410
+ backgroundColor: "transparent"
1411
+ }
1412
+ },
1413
+ navIcon: {
1414
+ width: 32,
1415
+ height: 32,
1416
+ display: "flex",
1417
+ alignItems: "center",
1418
+ justifyContent: "center"
1419
+ },
1420
+ navLabel: Object.assign({}, theme.typography.hmiText22Regular)
1421
+ }));
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
+
1432
+ const iconSize = 32;
1433
+ const NavigationBarRoot = function NavigationBarRoot(props) {
1434
+ var _a;
1435
+ const {
1436
+ children,
1437
+ showTimer = true,
1438
+ onControlCenterClick,
1439
+ onSupportClick,
1440
+ disabledNavigationItems = [],
1441
+ isControlCenterDisabled = false,
1442
+ isSupportDisabled = false,
1443
+ showControlCenterButton = true,
1444
+ showSupportButton = true,
1445
+ variant = "fixed"
1446
+ } = props;
1447
+ const location = useLocation();
1448
+ const navigate = useNavigate();
1449
+ const theme = useTheme();
1450
+ const {
1451
+ classes
1452
+ } = useStyles$c({
1453
+ variant
1454
+ });
1455
+ const {
1456
+ t
1457
+ } = useI18n();
1458
+ const [modalDestination, setModalDestination] = useState(null);
1459
+ const [passwordProtectedItem, setPasswordProtectedItem] = useState(null);
1460
+ const [currentPassword, setCurrentPassword] = useState(null);
1461
+ const disabledSet = new Set(disabledNavigationItems);
1462
+ const getFadedColor = function getFadedColor(color, opacity = 1) {
1463
+ return `${color}${Math.round(opacity * 255).toString(16).padStart(2, "0")}`;
1464
+ };
1465
+ const handleNavigate = function handleNavigate(path) {
1466
+ if (location.pathname !== path) navigate(path);
1467
+ };
1468
+ useEffect(function updateCurrentPassword() {
1469
+ const currentPage = React.Children.toArray(children).find(child => React.isValidElement(child) && child.type === NavigationBarItem && child.props.path === location.pathname);
1470
+ const currentPagePassword = React.isValidElement(currentPage) ? currentPage.props.password : undefined;
1471
+ setCurrentPassword(currentPagePassword !== null && currentPagePassword !== void 0 ? currentPagePassword : null);
1472
+ }, [location.pathname, children]);
1473
+ const handleControlCenterClick = function handleControlCenterClick() {
1474
+ if (isControlCenterDisabled) return;
1475
+ if (onControlCenterClick) {
1476
+ onControlCenterClick();
1477
+ } else {
1478
+ setModalDestination("controlCenter");
1479
+ }
1480
+ };
1481
+ const handleSupportClick = function handleSupportClick() {
1482
+ if (isSupportDisabled) return;
1483
+ if (onSupportClick) {
1484
+ onSupportClick();
1485
+ } else {
1486
+ setModalDestination("remoteSupport");
1487
+ }
1488
+ };
1489
+ const handlePasswordSuccess = function handlePasswordSuccess() {
1490
+ if (passwordProtectedItem) {
1491
+ if (passwordProtectedItem.onClick) {
1492
+ passwordProtectedItem.onClick();
1493
+ } else {
1494
+ handleNavigate(passwordProtectedItem.path);
1495
+ }
1496
+ }
1497
+ };
1498
+ const processedChildren = React.Children.map(children, function processChild(child) {
1499
+ if (!React.isValidElement(child)) return child;
1500
+ if (child.type !== NavigationBarItem) return child;
1501
+ const itemId = child.props.id;
1502
+ const isDisabled = disabledSet.has(itemId);
1503
+ return React.cloneElement(child, Object.assign(Object.assign({}, child.props), {
1504
+ isDisabled: isDisabled,
1505
+ location: location,
1506
+ handleNavigate: handleNavigate,
1507
+ currentPassword: currentPassword,
1508
+ setPasswordProtectedItem: setPasswordProtectedItem
1509
+ }));
1510
+ });
1511
+ const handleCloseConfirmation = function handleCloseConfirmation() {
1512
+ setModalDestination(null);
1513
+ };
1514
+ const handleClosePassword = function handleClosePassword() {
1515
+ setPasswordProtectedItem(null);
1516
+ };
1517
+ return jsxs(Fragment, {
1518
+ children: [jsx(NavigationConfirmationModal, {
1519
+ isOpen: modalDestination !== null,
1520
+ destination: modalDestination,
1521
+ onClose: handleCloseConfirmation
1522
+ }), jsx(PasswordProtectionModal, {
1523
+ isOpen: passwordProtectedItem !== null,
1524
+ onClose: handleClosePassword,
1525
+ onSuccess: handlePasswordSuccess,
1526
+ correctPassword: (_a = passwordProtectedItem === null || passwordProtectedItem === void 0 ? void 0 : passwordProtectedItem.password) !== null && _a !== void 0 ? _a : ""
1527
+ }), jsx("footer", {
1528
+ className: classes.root,
1529
+ children: jsxs(Box, {
1530
+ className: classes.container,
1531
+ children: [jsx(Box, {
1532
+ className: classes.sideClusterLeft,
1533
+ children: showControlCenterButton && jsx(VentionButton, {
1534
+ disableRipple: true,
1535
+ onClick: handleControlCenterClick,
1536
+ disabled: isControlCenterDisabled,
1537
+ className: classes.sideButton,
1538
+ style: {
1539
+ backgroundColor: isControlCenterDisabled ? getFadedColor(theme.palette.background.brand) : theme.palette.background.brand,
1540
+ borderColor: isControlCenterDisabled ? getFadedColor(theme.palette.border.onColor) : theme.palette.border.onColor,
1541
+ opacity: isControlCenterDisabled ? 0.5 : 1,
1542
+ cursor: isControlCenterDisabled ? "not-allowed" : "pointer"
1543
+ },
1544
+ children: jsx(Typography, {
1545
+ className: classes.sideButtonLabel,
1546
+ style: {
1547
+ color: isControlCenterDisabled ? getFadedColor(theme.palette.common.white) : theme.palette.common.white
1548
+ },
1549
+ children: t("navigation.bar.controlCenter", {
1550
+ defaultValue: "Control Center"
1551
+ })
1552
+ })
1553
+ })
1554
+ }), jsx("nav", {
1555
+ className: classes.tabBarButtonContainer,
1556
+ children: processedChildren
1557
+ }), jsxs(Box, {
1558
+ className: classes.sideClusterRight,
1559
+ children: [showSupportButton && jsx(VentionButton, {
1560
+ disableRipple: true,
1561
+ onClick: handleSupportClick,
1562
+ disabled: isSupportDisabled,
1563
+ className: classes.sideButton,
1564
+ style: {
1565
+ backgroundColor: isSupportDisabled ? getFadedColor(theme.palette.background.brand) : theme.palette.background.brand,
1566
+ borderColor: isSupportDisabled ? getFadedColor(theme.palette.border.onColor) : theme.palette.border.onColor,
1567
+ opacity: isSupportDisabled ? 0.5 : 1,
1568
+ cursor: isSupportDisabled ? "not-allowed" : "pointer"
1569
+ },
1570
+ iconLeft: jsx(VentionIcon, {
1571
+ size: iconSize,
1572
+ color: isSupportDisabled ? getFadedColor(theme.palette.common.white) : theme.palette.common.white,
1573
+ type: "headset-filled"
1574
+ }),
1575
+ children: jsx(Typography, {
1576
+ className: classes.sideButtonLabel,
1577
+ style: {
1578
+ color: isSupportDisabled ? getFadedColor(theme.palette.common.white) : theme.palette.common.white
1579
+ },
1580
+ children: t("navigation.bar.support", {
1581
+ defaultValue: "Support"
1582
+ })
1583
+ })
1584
+ }), showTimer && jsx(TimeLabel, {
1585
+ className: classes.timeLabel,
1586
+ color: theme.palette.common.white
1587
+ })]
1588
+ })]
1589
+ })
1590
+ })]
1591
+ });
1592
+ };
1593
+ const useStyles$c = tss.withParams().create(({
1594
+ theme,
1595
+ variant
1596
+ }) => ({
1597
+ root: Object.assign({
1598
+ boxSizing: "border-box",
1599
+ display: "flex",
1600
+ justifyContent: "center",
1601
+ alignItems: "center",
1602
+ height: "120px",
1603
+ padding: theme.spacing(2, 6)
1604
+ }, variant === "fixed" ? {
1605
+ backgroundColor: COLORS.slate[900],
1606
+ position: "fixed",
1607
+ left: 0,
1608
+ right: 0,
1609
+ bottom: 0,
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
+ }),
1619
+ container: {
1620
+ display: "flex",
1621
+ alignItems: "center",
1622
+ justifyContent: "space-between",
1623
+ position: "relative",
1624
+ width: "100%",
1625
+ gap: "20px",
1626
+ height: theme.spacing(13)
1627
+ },
1628
+ sideClusterLeft: {
1629
+ display: "flex",
1630
+ alignItems: "center",
1631
+ gap: theme.spacing(8)
1632
+ },
1633
+ sideClusterRight: {
1634
+ display: "flex",
1635
+ alignItems: "center",
1636
+ gap: theme.spacing(8)
1637
+ },
1638
+ sideButton: {
1639
+ display: "flex",
1640
+ flexDirection: "row",
1641
+ alignItems: "center",
1642
+ justifyContent: "center",
1643
+ height: theme.spacing(11),
1644
+ padding: theme.spacing(2, 4),
1645
+ backgroundColor: theme.palette.background.brand,
1646
+ borderRadius: 4,
1647
+ border: `2px solid ${theme.palette.border.onColor}`,
1648
+ color: theme.palette.common.white,
1649
+ textTransform: "none",
1650
+ minWidth: 0,
1651
+ "&:hover": {
1652
+ backgroundColor: theme.palette.button.filledHover
1653
+ },
1654
+ "&.Mui-disabled": {
1655
+ color: theme.palette.common.white
1656
+ }
1657
+ },
1658
+ sideButtonLabel: {
1659
+ display: "flex",
1660
+ justifyContent: "center",
1661
+ alignItems: "center",
1662
+ fontSize: 24,
1663
+ fontFamily: "Inter",
1664
+ fontStyle: "normal",
1665
+ fontWeight: 600,
1666
+ lineHeight: "32px"
1667
+ },
1668
+ timeLabel: Object.assign(Object.assign({}, theme.typography.hmiText22Regular), {
1669
+ fontWeight: 600
1670
+ }),
1671
+ tabBarButtonContainer: {
1672
+ display: "flex",
1673
+ alignItems: "center",
1674
+ justifyContent: "center",
1675
+ position: "absolute",
1676
+ left: "50%",
1677
+ transform: "translateX(-50%)",
1678
+ gap: theme.spacing(5),
1679
+ height: "104px"
1680
+ }
1681
+ }));
1682
+ const NavigationBar = Object.assign(NavigationBarRoot, {
1683
+ Item: NavigationBarItem
1684
+ });
1685
+
1686
+ const StatusTopBarButton = function StatusTopBarButton(props) {
1687
+ const {
1688
+ label,
1689
+ onClick,
1690
+ backgroundColor,
1691
+ backgroundColorHover,
1692
+ borderColor,
1693
+ textColor,
1694
+ width = 208,
1695
+ height = 80,
1696
+ icon,
1697
+ iconDisabled,
1698
+ visible = true,
1699
+ disabled = false
1700
+ } = props;
1701
+ const {
1702
+ classes
1703
+ } = useStyles$b();
1704
+ if (!visible) return null;
1705
+ const getFadedColor = function getFadedColor(color, opacity = 1) {
1706
+ return `${color}${Math.round(opacity * 255).toString(16).padStart(2, "0")}`;
1707
+ };
1708
+ const hasBackgroundColor = !!backgroundColor;
1709
+ const variant = hasBackgroundColor ? "filled-brand" : "outline";
1710
+ const finalBackgroundColor = disabled && backgroundColor ? getFadedColor(backgroundColor) : backgroundColor;
1711
+ const finalBackgroundColorHover = disabled && backgroundColorHover ? getFadedColor(backgroundColorHover) : backgroundColorHover;
1712
+ const finalBorderColor = disabled && borderColor ? getFadedColor(borderColor) : borderColor;
1713
+ const finalTextColor = disabled && textColor ? getFadedColor(textColor) : textColor;
1714
+ const finalIcon = disabled && iconDisabled ? iconDisabled : icon;
1715
+ const handleClick = function handleClick() {
1716
+ if (!disabled && onClick) {
1717
+ onClick();
1718
+ }
1719
+ };
1720
+ return jsx(VentionButton, {
1721
+ variant: variant,
1722
+ onClick: handleClick,
1723
+ disabled: disabled || !onClick,
1724
+ className: classes.actionButton,
1725
+ style: {
1726
+ width,
1727
+ height,
1728
+ backgroundColor: finalBackgroundColor || "transparent",
1729
+ border: finalBorderColor ? `1px solid ${finalBorderColor}` : undefined
1730
+ },
1731
+ sx: {
1732
+ "&:hover": {
1733
+ backgroundColor: finalBackgroundColorHover || finalBackgroundColor
1734
+ }
1735
+ },
1736
+ iconLeft: finalIcon ? jsx(Box, {
1737
+ className: classes.iconWrapper,
1738
+ children: finalIcon
1739
+ }) : undefined,
1740
+ children: jsx(Typography, {
1741
+ className: classes.actionLabel,
1742
+ style: {
1743
+ color: finalTextColor
1744
+ },
1745
+ children: label
1746
+ })
1747
+ });
1748
+ };
1749
+ const useStyles$b = tss.create(({
1750
+ theme
1751
+ }) => ({
1752
+ actionButton: {
1753
+ display: "flex",
1754
+ alignItems: "center",
1755
+ justifyContent: "center",
1756
+ gap: theme.spacing(1),
1757
+ padding: theme.spacing(1, 2),
1758
+ borderRadius: 4,
1759
+ minHeight: 0,
1760
+ minWidth: 0
1761
+ },
1762
+ iconWrapper: {
1763
+ padding: "5px",
1764
+ display: "flex",
1765
+ alignItems: "center",
1766
+ justifyContent: "center"
1767
+ },
1768
+ actionLabel: {
1769
+ fontWeight: 600,
1770
+ fontSize: 24,
1771
+ lineHeight: "32px",
1772
+ textTransform: "none",
1773
+ padding: theme.spacing(1)
1774
+ }
1775
+ }));
1776
+
1777
+ const StatusTopBarRoot = function StatusTopBarRoot(props) {
1778
+ const {
1779
+ status,
1780
+ buttonConfigs = [],
1781
+ variant = "light"
1782
+ } = props;
1783
+ const {
1784
+ classes
1785
+ } = useStyles$a({
1786
+ variant
1787
+ });
1788
+ const visibleButtons = buttonConfigs.filter(config => config.visible !== false);
1789
+ const hasVisibleButtons = visibleButtons.length > 0;
1790
+ return jsxs("header", {
1791
+ className: classes.root,
1792
+ children: [jsxs("div", {
1793
+ className: classes.content,
1794
+ children: [jsx("div", {
1795
+ className: classes.leftArea,
1796
+ children: status && jsx(VentionStatusIndicator, {
1797
+ size: "xx-large",
1798
+ dotColor: status.color,
1799
+ label: status.label
1800
+ })
1801
+ }), hasVisibleButtons && jsx("div", {
1802
+ className: classes.rightArea,
1803
+ children: visibleButtons.map(config => jsx(StatusTopBarButton, Object.assign({}, config, {
1804
+ visible: true
1805
+ }), config.id))
1806
+ })]
1807
+ }), jsx("div", {
1808
+ className: classes.bottomBorder
1809
+ })]
1810
+ });
1811
+ };
1812
+ const useStyles$a = tss.withParams().create(({
1813
+ theme,
1814
+ variant
1815
+ }) => ({
1816
+ root: {
1817
+ boxSizing: "border-box",
1818
+ position: "relative",
1819
+ width: "100%",
1820
+ backgroundColor: variant === "dark" ? theme.palette.background.defaultInverse : theme.palette.common.white,
1821
+ height: "120px",
1822
+ flexShrink: 0
1823
+ },
1824
+ content: {
1825
+ display: "flex",
1826
+ alignItems: "center",
1827
+ justifyContent: "space-between",
1828
+ padding: theme.spacing(5, 4)
1829
+ },
1830
+ leftArea: {
1831
+ display: "flex",
1832
+ alignItems: "center",
1833
+ "& .MuiTypography-root": {
1834
+ color: variant === "dark" ? theme.palette.common.white : theme.palette.text.primary
1835
+ }
1836
+ },
1837
+ rightArea: {
1838
+ display: "flex",
1839
+ alignItems: "center",
1840
+ gap: theme.spacing(2),
1841
+ height: theme.spacing(10)
1842
+ },
1843
+ bottomBorder: {
1844
+ position: "absolute",
1845
+ left: 0,
1846
+ right: 0,
1847
+ bottom: 0,
1848
+ height: 1,
1849
+ backgroundColor: theme.palette.divider
1850
+ }
1851
+ }));
1852
+ const StatusTopBar = StatusTopBarRoot;
1853
+
1854
+ const Sidebar = function Sidebar(props) {
1855
+ const {
1856
+ items,
1857
+ variant = "card"
1858
+ } = props;
1859
+ const {
1860
+ classes
1861
+ } = useStyles$9({
1862
+ variant
1863
+ });
1864
+ return jsx(Box, {
1865
+ className: classes.container,
1866
+ children: items.map(item => jsx(SidebarItemComponent, {
1867
+ item: item,
1868
+ variant: variant
1869
+ }, item.id))
1870
+ });
1871
+ };
1872
+ const ICON_SIZE = 32;
1873
+ const SidebarItemComponent = function SidebarItemComponent(props) {
1874
+ const {
1875
+ item,
1876
+ variant
1877
+ } = props;
1878
+ const {
1879
+ classes
1880
+ } = useSidebarItemStyles({
1881
+ state: item.state,
1882
+ variant
1883
+ });
1884
+ const isDisabled = item.state === "disabled";
1885
+ return jsx(Button, {
1886
+ className: classes.button,
1887
+ onClick: item.onClick,
1888
+ disabled: isDisabled,
1889
+ disableRipple: true,
1890
+ disableFocusRipple: true,
1891
+ disableTouchRipple: true,
1892
+ "data-testid": `sidebar-item-${item.id}`,
1893
+ children: jsxs(Box, {
1894
+ className: classes.content,
1895
+ children: [jsxs(Box, {
1896
+ className: classes.leftSection,
1897
+ children: [item.icon ? jsx(Box, {
1898
+ className: classes.iconWrapper,
1899
+ children: item.icon
1900
+ }) : jsx(Box, {
1901
+ className: classes.iconPlaceholder
1902
+ }), jsxs(Box, {
1903
+ className: classes.textContainer,
1904
+ children: [jsx(Typography, {
1905
+ className: classes.title,
1906
+ children: item.title
1907
+ }), item.subtitle && jsx(Typography, {
1908
+ className: classes.subtitle,
1909
+ children: item.subtitle
1910
+ })]
1911
+ })]
1912
+ }), item.iconType && jsx(Box, {
1913
+ className: classes.rightSection,
1914
+ children: jsx(Box, {
1915
+ className: classes.rightIcon,
1916
+ children: jsx(VentionIcon, {
1917
+ type: item.iconType,
1918
+ size: ICON_SIZE
1919
+ })
1920
+ })
1921
+ })]
1922
+ })
1923
+ });
1924
+ };
1925
+ const useStyles$9 = tss.withParams().create(({
1926
+ theme,
1927
+ variant
1928
+ }) => ({
1929
+ container: Object.assign({
1930
+ display: "flex",
1931
+ flexDirection: "column",
1932
+ width: "100%"
1933
+ }, variant === "card" ? {
1934
+ padding: theme.spacing(2),
1935
+ gap: theme.spacing(1)
1936
+ } : {
1937
+ padding: 0,
1938
+ gap: 0
1939
+ })
1940
+ }));
1941
+ const useSidebarItemStyles = tss.withParams().create(function createSidebarItemStyles({
1942
+ theme,
1943
+ state,
1944
+ variant
1945
+ }) {
1946
+ const isActive = state === "active";
1947
+ const isDisabled = state === "disabled";
1948
+ const isAccentVariant = variant === "accent";
1949
+ const getButtonStyles = () => {
1950
+ const baseStyles = {
1951
+ width: "100%",
1952
+ minHeight: theme.spacing(14),
1953
+ padding: theme.spacing(3, 4),
1954
+ textAlign: "left",
1955
+ textTransform: "none",
1956
+ display: "flex",
1957
+ alignItems: "center",
1958
+ justifyContent: "flex-start",
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}`,
1976
+ "&: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
1979
+ }
1980
+ });
1981
+ };
1982
+ return {
1983
+ button: getButtonStyles(),
1984
+ content: {
1985
+ display: "flex",
1986
+ alignItems: "center",
1987
+ justifyContent: "space-between",
1988
+ width: "100%",
1989
+ height: "100%"
1990
+ },
1991
+ leftSection: {
1992
+ display: "flex",
1993
+ alignItems: "center",
1994
+ gap: theme.spacing(3),
1995
+ flex: 1
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
+ },
2006
+ iconPlaceholder: {
2007
+ width: theme.spacing(4)
2008
+ },
2009
+ textContainer: {
2010
+ display: "flex",
2011
+ flexDirection: "column",
2012
+ gap: theme.spacing(1)
2013
+ },
2014
+ title: Object.assign(Object.assign({}, theme.typography.heading24SemiBold), {
2015
+ color: isDisabled ? theme.palette.text.disabled : theme.palette.text.primary
2016
+ }),
2017
+ subtitle: Object.assign(Object.assign({}, theme.typography.hmiText20Regular), {
2018
+ color: isDisabled ? theme.palette.text.disabled : theme.palette.text.primary
2019
+ }),
2020
+ rightSection: {
2021
+ display: "flex",
2022
+ alignItems: "center",
2023
+ marginLeft: theme.spacing(2)
2024
+ },
2025
+ rightIcon: {
2026
+ display: "flex",
2027
+ alignItems: "center",
2028
+ color: isActive ? theme.palette.icon.primary : theme.palette.icon.tertiary,
2029
+ "& svg": {
2030
+ color: isActive ? theme.palette.icon.primary : theme.palette.icon.tertiary
2031
+ }
2032
+ }
2033
+ };
2034
+ });
2035
+
2036
+ const LogsTable = memo(({
2037
+ logs = [],
2038
+ isLoading = false,
2039
+ error = null,
2040
+ onLoadMoreLogs,
2041
+ hasMoreLogs,
2042
+ onLogClick,
2043
+ tableHeight,
2044
+ emptyStateMessage,
2045
+ emptyStateIcon
2046
+ }) => {
2047
+ const {
2048
+ classes,
2049
+ cx
2050
+ } = useStyles$8({
2051
+ tableHeight
2052
+ });
2053
+ const theme = useTheme();
2054
+ const {
2055
+ t,
2056
+ formatDate
2057
+ } = useI18n();
2058
+ const loadMoreRef = useRef(null);
2059
+ const emptyMessage = emptyStateMessage || t("logs.emptyState.noLogs", {
2060
+ defaultValue: "You have no logs"
2061
+ });
2062
+ useEffect(function setupInfiniteScroll() {
2063
+ if (!onLoadMoreLogs || !hasMoreLogs || !loadMoreRef.current) return;
2064
+ const observer = new IntersectionObserver(entries => {
2065
+ if (entries[0].isIntersecting && hasMoreLogs) {
2066
+ onLoadMoreLogs();
2067
+ }
2068
+ }, {
2069
+ threshold: 0.1
2070
+ });
2071
+ observer.observe(loadMoreRef.current);
2072
+ return function cleanupInfiniteScroll() {
2073
+ observer.disconnect();
2074
+ };
2075
+ }, [onLoadMoreLogs, hasMoreLogs]);
2076
+ if (isLoading) {
2077
+ return jsx(Box, {
2078
+ className: classes.tableSection,
2079
+ children: jsxs(Box, {
2080
+ className: classes.centerState,
2081
+ children: [jsx(VentionSpinner, {
2082
+ size: "large"
2083
+ }), jsx(Typography, {
2084
+ variant: "heading24Medium",
2085
+ sx: {
2086
+ marginTop: 2
2087
+ },
2088
+ children: t("logs.loading", {
2089
+ defaultValue: "Loading logs..."
2090
+ })
2091
+ })]
2092
+ })
2093
+ });
2094
+ }
2095
+ if (error) {
2096
+ return jsx(Box, {
2097
+ className: classes.tableSection,
2098
+ children: jsx(Box, {
2099
+ className: classes.errorState,
2100
+ children: jsx(VentionAlert, {
2101
+ severity: "error",
2102
+ title: t("logs.error.loading", {
2103
+ defaultValue: "Error loading logs"
2104
+ }),
2105
+ descriptionText: error,
2106
+ size: "large"
2107
+ })
2108
+ })
2109
+ });
2110
+ }
2111
+ if (!logs || logs.length === 0) {
2112
+ return jsx(Box, {
2113
+ className: classes.tableSection,
2114
+ children: jsxs(Box, {
2115
+ className: classes.centerState,
2116
+ children: [emptyStateIcon && jsx(Box, {
2117
+ className: classes.emptyStateIcon,
2118
+ children: emptyStateIcon
2119
+ }), jsx(Typography, {
2120
+ variant: "heading24Medium",
2121
+ children: emptyMessage
2122
+ })]
2123
+ })
2124
+ });
2125
+ }
2126
+ const getTypeClassName = level => {
2127
+ switch (level) {
2128
+ case "error":
2129
+ return classes.errorType;
2130
+ case "warning":
2131
+ return classes.warningType;
2132
+ case "info":
2133
+ return classes.infoType;
2134
+ default:
2135
+ return "";
2136
+ }
2137
+ };
2138
+ const renderTypeIcon = level => {
2139
+ if (level === "error") {
2140
+ return jsx(VentionIcon, {
2141
+ size: 20,
2142
+ type: "alert-circle-filled",
2143
+ color: theme.palette.error.main
2144
+ });
2145
+ }
2146
+ if (level === "warning") {
2147
+ return jsx(VentionIcon, {
2148
+ size: 20,
2149
+ type: "alert-triangle-filled",
2150
+ color: theme.palette.warning.main
2151
+ });
2152
+ }
2153
+ return jsx(VentionIcon, {
2154
+ size: 20,
2155
+ type: "info-circle-filled",
2156
+ color: theme.palette.info.main
2157
+ });
2158
+ };
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
+ }
2183
+ };
2184
+ return jsxs(Box, {
2185
+ className: classes.tableSection,
2186
+ children: [jsx(TableContainer, {
2187
+ component: Paper,
2188
+ className: classes.tableContainer,
2189
+ children: jsxs(Table, {
2190
+ stickyHeader: true,
2191
+ children: [jsx(TableHead, {
2192
+ className: classes.tableHead,
2193
+ children: jsxs(TableRow, {
2194
+ children: [jsx(TableCell, {
2195
+ className: classes.iconHeaderCell
2196
+ }), jsx(TableCell, {
2197
+ className: classes.tableHeaderCell,
2198
+ children: jsx(Typography, {
2199
+ variant: "heading24Medium",
2200
+ className: classes.tableText,
2201
+ children: t("logs.table.date", {
2202
+ defaultValue: "Date"
2203
+ })
2204
+ })
2205
+ }), jsx(TableCell, {
2206
+ className: classes.tableHeaderCell,
2207
+ children: jsx(Typography, {
2208
+ variant: "heading24Medium",
2209
+ className: classes.tableText,
2210
+ children: t("logs.table.type", {
2211
+ defaultValue: "Type"
2212
+ })
2213
+ })
2214
+ }), jsx(TableCell, {
2215
+ className: classes.tableHeaderCell,
2216
+ children: jsx(Typography, {
2217
+ variant: "heading24Medium",
2218
+ className: classes.tableText,
2219
+ children: t("logs.table.code", {
2220
+ defaultValue: "Code"
2221
+ })
2222
+ })
2223
+ }), jsx(TableCell, {
2224
+ className: classes.tableHeaderCell,
2225
+ children: jsx(Typography, {
2226
+ variant: "heading24Medium",
2227
+ className: classes.tableText,
2228
+ children: t("logs.table.message", {
2229
+ defaultValue: "Message"
2230
+ })
2231
+ })
2232
+ }), jsx(TableCell, {
2233
+ className: classes.tableHeaderCell,
2234
+ children: jsx(Typography, {
2235
+ variant: "heading24Medium",
2236
+ className: classes.tableText,
2237
+ children: t("logs.table.description", {
2238
+ defaultValue: "Description"
2239
+ })
2240
+ })
2241
+ })]
2242
+ })
2243
+ }), jsx(TableBody, {
2244
+ children: logs.map(log => jsxs(TableRow, {
2245
+ hover: true,
2246
+ onClick: onLogClick ? () => onLogClick(log) : undefined,
2247
+ className: onLogClick ? classes.clickableRow : undefined,
2248
+ children: [jsx(TableCell, {
2249
+ className: classes.iconCell,
2250
+ children: jsx(Box, {
2251
+ className: classes.iconWrapper,
2252
+ children: renderTypeIcon(log.level)
2253
+ })
2254
+ }), jsx(TableCell, {
2255
+ children: jsx(Typography, {
2256
+ variant: "heading24Medium",
2257
+ className: classes.tableText,
2258
+ children: formatLogDate(log.date)
2259
+ })
2260
+ }), jsx(TableCell, {
2261
+ className: cx(classes.typeCell, getTypeClassName(log.level)),
2262
+ children: jsx(Typography, {
2263
+ variant: "heading24Medium",
2264
+ className: classes.tableText,
2265
+ children: t(`logs.type.${log.level}`, {
2266
+ defaultValue: log.level
2267
+ })
2268
+ })
2269
+ }), jsx(TableCell, {
2270
+ children: jsx(Typography, {
2271
+ variant: "heading24Medium",
2272
+ className: classes.tableText,
2273
+ children: log.code
2274
+ })
2275
+ }), jsx(TableCell, {
2276
+ children: jsx(Typography, {
2277
+ variant: "heading24Medium",
2278
+ className: classes.tableText,
2279
+ children: log.message
2280
+ })
2281
+ }), jsx(TableCell, {
2282
+ children: jsx(Typography, {
2283
+ variant: "heading24Medium",
2284
+ className: classes.tableText,
2285
+ children: log.description
2286
+ })
2287
+ })]
2288
+ }, log.id))
2289
+ })]
2290
+ })
2291
+ }), onLoadMoreLogs && hasMoreLogs && jsxs(Box, {
2292
+ ref: loadMoreRef,
2293
+ className: classes.loadMoreTrigger,
2294
+ children: [jsx(VentionSpinner, {
2295
+ size: "medium"
2296
+ }), jsx(Typography, {
2297
+ variant: "uiText14Regular",
2298
+ className: classes.loadMoreText,
2299
+ children: t("logs.loadingMore", {
2300
+ defaultValue: "Loading more logs..."
2301
+ })
2302
+ })]
2303
+ }), onLoadMoreLogs && !hasMoreLogs && logs.length > 0 && jsx(Box, {
2304
+ className: classes.endMessage,
2305
+ children: jsx(Typography, {
2306
+ variant: "uiText14Regular",
2307
+ children: t("logs.noMoreLogs", {
2308
+ defaultValue: "No more logs to load"
2309
+ })
2310
+ })
2311
+ })]
2312
+ });
2313
+ });
2314
+ LogsTable.displayName = "LogsTable";
2315
+ const useStyles$8 = tss.withParams().create(({
2316
+ theme,
2317
+ tableHeight
2318
+ }) => ({
2319
+ tableSection: {
2320
+ display: "flex",
2321
+ flexDirection: "column",
2322
+ alignItems: "center",
2323
+ gap: theme.spacing(4),
2324
+ alignSelf: "stretch",
2325
+ flex: 1,
2326
+ height: "auto"
2327
+ },
2328
+ centerState: {
2329
+ display: "flex",
2330
+ flexDirection: "column",
2331
+ alignItems: "center",
2332
+ justifyContent: "center",
2333
+ flex: 1,
2334
+ color: theme.palette.text.secondary
2335
+ },
2336
+ errorState: {
2337
+ display: "flex",
2338
+ alignItems: "center",
2339
+ justifyContent: "center",
2340
+ flex: 1,
2341
+ padding: theme.spacing(4)
2342
+ },
2343
+ tableContainer: Object.assign({
2344
+ width: "100%",
2345
+ flex: 1,
2346
+ overflow: "auto",
2347
+ minHeight: "560px"
2348
+ }, tableHeight && {
2349
+ maxHeight: tableHeight
2350
+ }),
2351
+ tableHead: {
2352
+ backgroundColor: theme.palette.background.default,
2353
+ borderBottom: `1px solid ${theme.palette.background.mutedSlate}`
2354
+ },
2355
+ iconHeaderCell: {
2356
+ width: 40,
2357
+ backgroundColor: theme.palette.background.default
2358
+ },
2359
+ tableHeaderCell: {
2360
+ fontWeight: 200,
2361
+ backgroundColor: theme.palette.background.default,
2362
+ color: theme.palette.text.tertiary
2363
+ },
2364
+ iconCell: {
2365
+ width: 40
2366
+ },
2367
+ iconWrapper: {
2368
+ display: "flex",
2369
+ alignItems: "center",
2370
+ justifyContent: "center"
2371
+ },
2372
+ typeCell: {
2373
+ textTransform: "capitalize",
2374
+ fontWeight: 500
2375
+ },
2376
+ errorType: {
2377
+ color: theme.palette.error.main
2378
+ },
2379
+ warningType: {
2380
+ color: theme.palette.warning.main
2381
+ },
2382
+ infoType: {
2383
+ color: theme.palette.info.main
2384
+ },
2385
+ loadMoreTrigger: {
2386
+ display: "flex",
2387
+ flexDirection: "column",
2388
+ alignItems: "center",
2389
+ justifyContent: "center",
2390
+ padding: theme.spacing(4),
2391
+ color: theme.palette.text.secondary
2392
+ },
2393
+ loadMoreText: {
2394
+ marginTop: theme.spacing(1)
2395
+ },
2396
+ clickableRow: {
2397
+ cursor: "pointer",
2398
+ "&:hover": {
2399
+ backgroundColor: theme.palette.action.hover
2400
+ }
2401
+ },
2402
+ emptyStateIcon: {
2403
+ marginBottom: theme.spacing(2),
2404
+ display: "flex",
2405
+ alignItems: "center",
2406
+ justifyContent: "center"
2407
+ },
2408
+ endMessage: {
2409
+ display: "flex",
2410
+ alignItems: "center",
2411
+ justifyContent: "center",
2412
+ padding: theme.spacing(4),
2413
+ color: theme.palette.text.secondary
2414
+ },
2415
+ tableText: {
2416
+ fontSize: "20px",
2417
+ lineHeight: "24px"
2418
+ }
2419
+ }));
2420
+
2421
+ const LogFilterForm = memo(({
2422
+ onFilterChange,
2423
+ onReset,
2424
+ initialFilters
2425
+ }) => {
2426
+ var _a, _b, _c, _d;
2427
+ const {
2428
+ classes
2429
+ } = useStyles$7();
2430
+ const {
2431
+ t
2432
+ } = useI18n();
2433
+ const handleFilterChangeRef = useRef(onFilterChange);
2434
+ useEffect(function updateFilterChangeRef() {
2435
+ handleFilterChangeRef.current = onFilterChange;
2436
+ }, [onFilterChange]);
2437
+ const [fromDate, setFromDate] = useState((_a = initialFilters === null || initialFilters === void 0 ? void 0 : initialFilters.fromDate) !== null && _a !== void 0 ? _a : "");
2438
+ const [toDate, setToDate] = useState((_b = initialFilters === null || initialFilters === void 0 ? void 0 : initialFilters.toDate) !== null && _b !== void 0 ? _b : "");
2439
+ const [logType, setLogType] = useState((_c = initialFilters === null || initialFilters === void 0 ? void 0 : initialFilters.logType) !== null && _c !== void 0 ? _c : null);
2440
+ const [sortOrder, setSortOrder] = useState((_d = initialFilters === null || initialFilters === void 0 ? void 0 : initialFilters.sortOrder) !== null && _d !== void 0 ? _d : "latest");
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(() => [{
2454
+ value: "",
2455
+ displayText: t("logs.filter.typeOptions.all", {
2456
+ defaultValue: "All"
2457
+ })
2458
+ }, {
2459
+ value: "error",
2460
+ displayText: t("logs.filter.typeOptions.error", {
2461
+ defaultValue: "Error"
2462
+ })
2463
+ }, {
2464
+ value: "warning",
2465
+ displayText: t("logs.filter.typeOptions.warning", {
2466
+ defaultValue: "Warning"
2467
+ })
2468
+ }, {
2469
+ value: "info",
2470
+ displayText: t("logs.filter.typeOptions.info", {
2471
+ defaultValue: "Info"
2472
+ })
2473
+ }], [t]);
2474
+ const sortOptions = useMemo(() => [{
2475
+ value: "latest",
2476
+ displayText: t("logs.filter.sortOptions.latest", {
2477
+ defaultValue: "From latest"
2478
+ })
2479
+ }, {
2480
+ value: "oldest",
2481
+ displayText: t("logs.filter.sortOptions.oldest", {
2482
+ defaultValue: "From oldest"
2483
+ })
2484
+ }], [t]);
2485
+ const handleReset = () => {
2486
+ setFromDate("");
2487
+ setToDate("");
2488
+ setLogType(null);
2489
+ setSortOrder("latest");
2490
+ onReset === null || onReset === void 0 ? void 0 : onReset();
2491
+ };
2492
+ useEffect(function notifyFilterChange() {
2493
+ var _a;
2494
+ (_a = handleFilterChangeRef.current) === null || _a === void 0 ? void 0 : _a.call(handleFilterChangeRef, {
2495
+ fromDate,
2496
+ toDate,
2497
+ logType,
2498
+ sortOrder
2499
+ });
2500
+ }, [fromDate, toDate, logType, sortOrder]);
2501
+ return jsxs(Box, {
2502
+ className: classes.filterSection,
2503
+ children: [jsx(Box, {
2504
+ className: classes.filterInput,
2505
+ children: jsx(VentionTextInput, {
2506
+ size: "xx-large",
2507
+ label: t("logs.filter.from", {
2508
+ defaultValue: "From"
2509
+ }),
2510
+ value: fromDate,
2511
+ onChange: event => setFromDate(event.target.value),
2512
+ type: "date"
2513
+ })
2514
+ }), jsx(Box, {
2515
+ className: classes.filterInput,
2516
+ children: jsx(VentionTextInput, {
2517
+ size: "xx-large",
2518
+ label: t("logs.filter.to", {
2519
+ defaultValue: "To"
2520
+ }),
2521
+ value: toDate,
2522
+ onChange: event => setToDate(event.target.value),
2523
+ type: "date"
2524
+ })
2525
+ }), jsx(Box, {
2526
+ className: classes.filterInput,
2527
+ children: jsx(VentionSelect, {
2528
+ size: "xx-large",
2529
+ variant: "outlined",
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
+ }),
2541
+ menuItems: typeOptions
2542
+ })
2543
+ }), jsx(Box, {
2544
+ className: classes.filterInput,
2545
+ children: jsx(VentionSelect, {
2546
+ size: "xx-large",
2547
+ variant: "outlined",
2548
+ labelText: t("logs.filter.sort", {
2549
+ defaultValue: "Sort"
2550
+ }),
2551
+ value: sortOrder,
2552
+ onChange: event => setSortOrder(event.target.value),
2553
+ menuItems: sortOptions
2554
+ })
2555
+ }), jsx(VentionButton, {
2556
+ size: "x-large",
2557
+ onClick: handleReset,
2558
+ className: classes.resetButton,
2559
+ children: t("logs.filter.reset", {
2560
+ defaultValue: "Reset"
2561
+ })
2562
+ })]
2563
+ });
2564
+ });
2565
+ LogFilterForm.displayName = "LogFilterForm";
2566
+ const useStyles$7 = tss.create(({
2567
+ theme
2568
+ }) => ({
2569
+ filterSection: {
2570
+ display: "flex",
2571
+ alignItems: "flex-end",
2572
+ gap: theme.spacing(4),
2573
+ alignSelf: "stretch",
2574
+ flexWrap: "wrap"
2575
+ },
2576
+ filterInput: {
2577
+ flex: "1 1 200px",
2578
+ minWidth: "200px"
2579
+ },
2580
+ resetButton: {
2581
+ width: "103px",
2582
+ height: "64px",
2583
+ backgroundColor: theme.palette.background.default,
2584
+ color: theme.palette.text.primary,
2585
+ "&:hover": {
2586
+ backgroundColor: theme.palette.background.default
2587
+ }
2588
+ }
2589
+ }));
2590
+
2591
+ /******************************************************************************
2592
+ Copyright (c) Microsoft Corporation.
2593
+
2594
+ Permission to use, copy, modify, and/or distribute this software for any
2595
+ purpose with or without fee is hereby granted.
2596
+
2597
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
2598
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
2599
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
2600
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
2601
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
2602
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
2603
+ PERFORMANCE OF THIS SOFTWARE.
2604
+ ***************************************************************************** */
2605
+ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
2606
+
2607
+
2608
+ function __awaiter(thisArg, _arguments, P, generator) {
2609
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
2610
+ return new (P || (P = Promise))(function (resolve, reject) {
2611
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
2612
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
2613
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
2614
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
2615
+ });
2616
+ }
2617
+
2618
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
2619
+ var e = new Error(message);
2620
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
2621
+ };
2622
+
2623
+ const LogsPagination = memo(({
2624
+ currentPage,
2625
+ totalPages,
2626
+ onPageChange
2627
+ }) => {
2628
+ const {
2629
+ classes
2630
+ } = useStyles$6();
2631
+ const {
2632
+ t
2633
+ } = useI18n();
2634
+ const handlePrevious = () => {
2635
+ if (currentPage > 1) {
2636
+ onPageChange(currentPage - 1);
2637
+ }
2638
+ };
2639
+ const handleNext = () => {
2640
+ if (currentPage < totalPages) {
2641
+ onPageChange(currentPage + 1);
2642
+ }
2643
+ };
2644
+ return jsxs(Box, {
2645
+ className: classes.paginationSection,
2646
+ children: [jsx(VentionButton, {
2647
+ size: "x-large",
2648
+ className: classes.paginationButton,
2649
+ onClick: handlePrevious,
2650
+ disabled: currentPage === 1,
2651
+ children: "\u2190"
2652
+ }), jsxs(Typography, {
2653
+ variant: "heading18SemiBold",
2654
+ children: [t("pagination.page", {
2655
+ defaultValue: "Page"
2656
+ }), " ", currentPage, " ", t("pagination.of", {
2657
+ defaultValue: "of"
2658
+ }), " ", totalPages]
2659
+ }), jsx(VentionButton, {
2660
+ size: "x-large",
2661
+ className: classes.paginationButton,
2662
+ onClick: handleNext,
2663
+ disabled: currentPage === totalPages,
2664
+ children: "\u2192"
2665
+ })]
2666
+ });
2667
+ });
2668
+ LogsPagination.displayName = "LogsPagination";
2669
+ const useStyles$6 = tss.create(({
2670
+ theme
2671
+ }) => ({
2672
+ paginationSection: {
2673
+ display: "flex",
2674
+ justifyContent: "center",
2675
+ alignItems: "center",
2676
+ alignSelf: "stretch",
2677
+ gap: theme.spacing(4)
2678
+ },
2679
+ paginationButton: {
2680
+ width: "64px",
2681
+ height: "64px",
2682
+ backgroundColor: theme.palette.background.default,
2683
+ color: theme.palette.text.primary,
2684
+ "&:hover": {
2685
+ backgroundColor: theme.palette.grey[300]
2686
+ },
2687
+ "&:disabled": {
2688
+ opacity: 0.5,
2689
+ cursor: "not-allowed",
2690
+ backgroundColor: theme.palette.background.slate
2691
+ }
2692
+ }
2693
+ }));
2694
+
2695
+ const formatDateToInput = date => {
2696
+ return dayjs(date).format("YYYY-MM-DD");
2697
+ };
2698
+ const parseLogDate = dateStr => {
2699
+ const parsed = dayjs(dateStr);
2700
+ return parsed.isValid() ? parsed.valueOf() : 0;
2701
+ };
2702
+ const LogsPanel = forwardRef((props, ref) => {
2703
+ var _a, _b, _c, _d, _e, _f, _g;
2704
+ const {
2705
+ dataFetcher,
2706
+ initialFilters,
2707
+ onError,
2708
+ pagination,
2709
+ onFilterChange,
2710
+ onLogClick,
2711
+ className,
2712
+ tableHeight,
2713
+ emptyStateMessage,
2714
+ emptyStateIcon
2715
+ } = props;
2716
+ const {
2717
+ classes,
2718
+ cx
2719
+ } = useStyles$5();
2720
+ const [logs, setLogs] = useState([]);
2721
+ const [isLoading, setIsLoading] = useState(true);
2722
+ const [error, setError] = useState(null);
2723
+ const [filters, setFilters] = useState({
2724
+ fromDate: (_a = initialFilters === null || initialFilters === void 0 ? void 0 : initialFilters.fromDate) !== null && _a !== void 0 ? _a : null,
2725
+ toDate: (_b = initialFilters === null || initialFilters === void 0 ? void 0 : initialFilters.toDate) !== null && _b !== void 0 ? _b : null,
2726
+ logType: (_c = initialFilters === null || initialFilters === void 0 ? void 0 : initialFilters.logType) !== null && _c !== void 0 ? _c : null,
2727
+ sortOrder: (_d = initialFilters === null || initialFilters === void 0 ? void 0 : initialFilters.sortOrder) !== null && _d !== void 0 ? _d : "latest"
2728
+ });
2729
+ const paginationMode = (_e = pagination === null || pagination === void 0 ? void 0 : pagination.mode) !== null && _e !== void 0 ? _e : "none";
2730
+ const pageSize = (_f = pagination === null || pagination === void 0 ? void 0 : pagination.pageSize) !== null && _f !== void 0 ? _f : 10;
2731
+ const [currentPage, setCurrentPage] = useState((_g = pagination === null || pagination === void 0 ? void 0 : pagination.initialPage) !== null && _g !== void 0 ? _g : 1);
2732
+ const [totalPages, setTotalPages] = useState(1);
2733
+ const [hasMorePages, setHasMorePages] = useState(false);
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);
2754
+ }
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
+ }
2763
+ }
2764
+ }
2765
+ });
2766
+ }, [dataFetcher, filters, pageSize, paginationMode, onError]);
2767
+ useEffect(function fetchDataOnChange() {
2768
+ const controller = new AbortController();
2769
+ fetchData(currentPage, controller.signal);
2770
+ return function cleanup() {
2771
+ controller.abort();
2772
+ };
2773
+ }, [fetchData, currentPage]);
2774
+ const handlePageChange = useCallback(page => {
2775
+ setCurrentPage(page);
2776
+ }, []);
2777
+ const handleLoadMorePages = useCallback(() => {
2778
+ if (hasMorePages) {
2779
+ setCurrentPage(prev => prev + 1);
2780
+ }
2781
+ }, [hasMorePages]);
2782
+ const handleFilterChange = useCallback(next => {
2783
+ setFilters(next);
2784
+ setCurrentPage(1);
2785
+ if (paginationMode === "infinite-scroll") {
2786
+ setLogs([]);
2787
+ }
2788
+ if (onFilterChange) {
2789
+ onFilterChange(next);
2790
+ }
2791
+ }, [onFilterChange, paginationMode]);
2792
+ const handleReset = useCallback(() => {
2793
+ const resetFilters = {
2794
+ fromDate: null,
2795
+ toDate: null,
2796
+ logType: null,
2797
+ sortOrder: "latest"
2798
+ };
2799
+ setFilters(resetFilters);
2800
+ setCurrentPage(1);
2801
+ if (paginationMode === "infinite-scroll") {
2802
+ setLogs([]);
2803
+ }
2804
+ if (onFilterChange) {
2805
+ onFilterChange(resetFilters);
2806
+ }
2807
+ }, [onFilterChange, paginationMode]);
2808
+ const refresh = useCallback(() => __awaiter(void 0, void 0, void 0, function* () {
2809
+ yield fetchData(currentPage);
2810
+ }), [fetchData, currentPage]);
2811
+ const resetFilters = useCallback(() => {
2812
+ handleReset();
2813
+ }, [handleReset]);
2814
+ const applyFilters = useCallback(newFilters => {
2815
+ const updatedFilters = Object.assign(Object.assign({}, filters), newFilters);
2816
+ setFilters(updatedFilters);
2817
+ if (onFilterChange) {
2818
+ onFilterChange(updatedFilters);
2819
+ }
2820
+ }, [filters, onFilterChange]);
2821
+ const getCurrentFilters = useCallback(() => filters, [filters]);
2822
+ const exportLogs = useCallback(() => logs, [logs]);
2823
+ useImperativeHandle(ref, () => ({
2824
+ refresh,
2825
+ resetFilters,
2826
+ applyFilters,
2827
+ getCurrentFilters,
2828
+ exportLogs
2829
+ }), [refresh, resetFilters, applyFilters, getCurrentFilters, exportLogs]);
2830
+ return jsxs(Box, {
2831
+ className: cx(classes.root, className),
2832
+ children: [jsx(LogFilterForm, {
2833
+ onFilterChange: handleFilterChange,
2834
+ onReset: handleReset,
2835
+ initialFilters: filters
2836
+ }), jsx(LogsTable, {
2837
+ logs: logs,
2838
+ isLoading: isLoading,
2839
+ error: error,
2840
+ onLoadMoreLogs: paginationMode === "infinite-scroll" ? handleLoadMorePages : undefined,
2841
+ hasMoreLogs: paginationMode === "infinite-scroll" ? hasMorePages : undefined,
2842
+ onLogClick: onLogClick,
2843
+ tableHeight: tableHeight,
2844
+ emptyStateMessage: emptyStateMessage,
2845
+ emptyStateIcon: emptyStateIcon
2846
+ }), paginationMode === "pagination" && !isLoading && !error && logs.length > 0 && jsx(LogsPagination, {
2847
+ currentPage: currentPage,
2848
+ totalPages: totalPages,
2849
+ onPageChange: handlePageChange
2850
+ })]
2851
+ });
2852
+ });
2853
+ LogsPanel.displayName = "LogsPanel";
2854
+ const useStyles$5 = tss.create(({
2855
+ theme
2856
+ }) => ({
2857
+ root: {
2858
+ display: "flex",
2859
+ flexDirection: "column",
2860
+ gap: theme.spacing(4),
2861
+ alignSelf: "stretch"
2862
+ }
2863
+ }));
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
+
3602
+ /**
3603
+ * This module provides utilities for generating API base URLs for machine apps.
3604
+ * It automatically detects the environment and returns the appropriate URL:
3605
+ * - Local/Edge: http://hostname:8000
3606
+ * - Deployed: Uses passthrough URL for digital twin infrastructure
3607
+ */
3608
+ /**
3609
+ * Gets the execution engine HTTP URL for deployed environments
3610
+ * @param processName - The name of the process/service
3611
+ * @returns The full URL for the execution engine API
3612
+ */
3613
+ function getExecutionEngineHttpUrl(processName) {
3614
+ const {
3615
+ href,
3616
+ port,
3617
+ hostname,
3618
+ origin
3619
+ } = window.location;
3620
+ const pathParts = new URL(href).pathname.split("/").filter(Boolean);
3621
+ const passthroughIndex = pathParts.indexOf("passthrough");
3622
+ const hmiIndex = pathParts.indexOf("hmi");
3623
+ const sessionId = passthroughIndex >= 0 && passthroughIndex < pathParts.length - 1 ? pathParts[passthroughIndex + 1] : hmiIndex > 0 ? pathParts[hmiIndex - 1] : "";
3624
+ const basePath = `/digital-twin/machine-motion/passthrough/${sessionId}/80/v1/machineCode/services/${processName}`;
3625
+ const isDev = port === "5173" || hostname === "localhost";
3626
+ const baseOrigin = isDev ? "https://digital-twin.vention.foo" : origin;
3627
+ return `${baseOrigin}${basePath}`;
3628
+ }
3629
+ /**
3630
+ * Checks if the current environment is running on edge (local controller)
3631
+ * @returns true if running on edge, false otherwise
3632
+ */
3633
+ function isOnEdge() {
3634
+ return window.location.hostname.startsWith("192.168") || window.location.hostname.startsWith("localhost");
3635
+ }
3636
+ /**
3637
+ * Gets the API base URL for the specified process/service
3638
+ * @param options - Configuration options including process name and optional edge port
3639
+ * @returns The API base URL for all backend API calls
3640
+ * @example
3641
+ * ```ts
3642
+ * const apiUrl = getApiBaseUrl({ processName: "ai-operator" })
3643
+ * // Returns: http://192.168.1.100:8000 (on edge)
3644
+ * // or https://digital-twin.../passthrough/.../ai-operator (deployed)
3645
+ * ```
3646
+ */
3647
+ function getApiBaseUrl(options) {
3648
+ const {
3649
+ processName,
3650
+ edgePort = 8000
3651
+ } = options;
3652
+ if (isOnEdge()) {
3653
+ return `http://${window.location.hostname}:${edgePort}`;
3654
+ }
3655
+ return getExecutionEngineHttpUrl(processName);
3656
+ }
3657
+
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 };