Architektura
Aplikacja frontendowa
Interfejs użytkownika platformy DataFlow AI to aplikacja jednostronicowa zbudowana na React 19, Vite 7 i TypeScript 5.9. Prezentuje cały produkt ETL/ELT — projektowanie potoków, monitoring, nadzór, migrację i administrację — poprzez jedną adaptującą się do persony powłokę, która komunikuje się wyłącznie z bramą API pod /api/v1.
Stos technologiczny
SPA znajduje się w frontend/ i jest budowana oraz serwowana niezależnie od usług platformowych JVM. Zestaw zależności jest celowo nowoczesny i skupiony na niewielkiej liczbie dobrze wspieranych bibliotek.
| Zagadnienie | Biblioteka | Wersja |
|---|---|---|
| Środowisko uruchomieniowe UI | react / react-dom | 19.2 |
| Build / serwer deweloperski | vite + @vitejs/plugin-react | 7.3 |
| Routing | react-router-dom | 7.13 (createBrowserRouter) |
| Stan klienta | zustand | 5.0 (z middleware persist) |
| Stan serwera | @tanstack/react-query | 5.90 |
| Tabele | @tanstack/react-table 8.21, @tanstack/react-virtual 3.13 | |
| Edytor przepływów / grafów | @xyflow/react | 12.10 (React Flow) |
| Wykresy | recharts | 3.7 |
| Edytor kodu | @monaco-editor/react | 4.7 |
| Formularze | react-hook-form 7.71 + @hookform/resolvers + zod 4.3 | |
| Uwierzytelnianie | keycloak-js | 26.2 |
| HTTP | axios | 1.13 |
| i18n | i18next 25.8 + react-i18next 16.5 + detektor języka przeglądarki + http-backend | |
| Ikony | lucide-react | 0.576 |
| Paleta poleceń | cmdk | 1.1 |
| Daty | date-fns | 4.1 |
| Obsługa błędów | react-error-boundary | 5.0 |
| Stylowanie | tailwindcss 4.2 poprzez @tailwindcss/vite | |
| Testowanie | vitest 3.2, @testing-library/react, playwright 1.58, msw 2.12 |
Aplikacja jest serwowana produkcyjnie przez nginx (port hosta 3006) i dociera do backendu wyłącznie poprzez bramę API.
Pojedynczy punkt wejścia
Frontend nigdy nie wywołuje mikrousługi bezpośrednio. Każde żądanie przechodzi przez bramę API pod /api/v1, która waliduje JWT Keycloak, wstrzykuje nagłówki tożsamości i stosuje ograniczanie szybkości przed proxowaniem do usługi w dół stosu.
Rozruch aplikacji
Komponent główny App.tsx jest celowo minimalny. Opakowuje <RouterProvider router={router}> w ErrorBoundary react-error-boundary najwyższego poziomu, którego fallbackiem jest ErrorFallback, a którego akcja resetu nawiguje przeglądarkę do /. Przy montowaniu odczytuje theme z useThemeStore i synchronizuje klasę dark na document.documentElement.
App.tsx
└─ ErrorBoundary (fallback: ErrorFallback, reset → window.location.href = '/')
└─ RouterProvider (router from router.tsx)
└─ AppShell (route '/')
└─ ProtectedRoute → <Outlet/> (every in-shell page)
Każda strona jest ładowana przez lazy() i opakowana w pomocnika SuspenseWrap — ErrorBoundary plus Suspense z fallbackiem spinnera — tak aby awaria lub wolny fragment na jednej stronie nigdy nie zepsuł reszty powłoki.
Powłoka aplikacji
components/shell/AppShell.tsx renderuje trwały układ dla każdej trasy wewnątrzaplikacyjnej. Komponuje chrome nawigacyjne, główny outlet treści, AI Copilot oraz kilka globalnych nakładek.
┌──────────────────────────────────────────────────────────────────────┐
│ TopBar [breadcrumbs] [search ⌘K] [lang] [notifs] [user] │
├────────────┬───────────────────────────────────────────────────────────┤
│ │ │
│ Sidebar │ <main id="main-content"> │
│ │ │
│ ▸ Home │ <Outlet/> — the active page │
│ ▸ Design │ │
│ Studio │ │
│ ▸ Monitor │ │
│ ▸ Govern. │ ┌────────┐ │
│ ▸ Migrate │ │ AI │ │
│ ▸ Data │ │Copilot │ │
│ ▸ Admin │ │Sidebar │ │
│ │ │(toggle)│ │
│ [collapse] │ └────────┘ │
├────────────┴───────────────────────────────────────────────────────────┤
│ MobileBottomNav (mobile only) │
└──────────────────────────────────────────────────────────────────────┘
Kompozycja powłoki
- Sidebar (
Sidebar.tsx) — nawigacja na desktopie i tablecie; składany między 60px a 240px, z rozwijaniem przy najechaniu na tablecie. - MobileMenu i MobileBottomNav — nakładki nawigacyjne wyłącznie mobilne.
- TopBar — nagłówek z okruszkami nawigacji, wyzwalaczem wyszukiwania globalnego, przełącznikiem języka, powiadomieniami i stopką użytkownika.
<main id="main-content">— responsywne wcięcie, renderuje routowany<Outlet />.- AICopilotTrigger (pływający przycisk akcji) oraz AICopilotSidebar, pokazywane warunkowo, gdy ustawione jest
copilotOpen. - Modal GlobalSearch — otwierany za pomocą
Ctrl/Cmd+Kpoprzez handler keydown w powłoce; oparty na prawdziwym, rankingowanym endpointcie wyszukiwania. - OnboardingTour — wyświetlany, dopóki
tourCompletedwonboardingStorenie ma wartości true. - HelpDrawer i CatalogAskPanel — sterowane lokalnym stanem komponentu (
helpOpen,askOpen). - Link pominięcia nawigacji do
#main-contentdla użytkowników klawiatury i czytników ekranu.
Zachowanie responsywne jest obliczane przez useIsMobile, useIsTablet i useSidebarState z hooks/useResponsive; wartość effectiveMargin przesuwa główną treść, aby dopasować ją do bieżącej szerokości paska bocznego.
ProtectedRoute
components/shell/ProtectedRoute.tsx opakowuje każdy element trasy wewnątrz powłoki i wymusza uwierzytelnianie oraz autoryzację:
- Podczas
isLoadingrenderuje spinner z etykietą „Sprawdzanie dostępu...". - Jeśli użytkownik nie jest uwierzytelniony, renderuje
<Navigate to="/login">, przenosząc pierwotną lokalizacjęfromw stanie routera. - Oblicza
effectivePersona— w trybie deweloperskim zpersonaStore, w przeciwnym razie zuser.personawyprowadzonej z ról Keycloak. - Wywołuje
canAccess(persona, pathname)zdata/permissions; jeśli dostęp jest odmówiony, renderuje<AccessDenied />.
Każda decyzja jest logowana przez createLogger('auth.protected-route').
Mapa tras
Routing jest zdefiniowany w frontend/src/router.tsx przy użyciu createBrowserRouter. Trasy dzielą się na dwie grupy: niewielki zestaw renderowany poza powłoką (bez chrome) oraz większość renderowana wewnątrz AppShell i opakowana w ProtectedRoute.
Trasy poza powłoką
| Ścieżka | Strona | Przeznaczenie |
|---|---|---|
/login | Login | Wejście logowania SSO Keycloak |
/callback | Callback | Handler callbacku przekierowania OIDC |
/unauthorized | Unauthorized | Strona niepowodzenia uwierzytelniania / braku sesji |
/onboarding | Onboarding | Przepływ onboardingu przy pierwszym logowaniu |
/report/:pipelineId | PipelineReport | Samodzielny raport potoku, czysty układ drukowania do PDF |
Główne trasy wewnątrz powłoki
| Ścieżka | Strona | Przeznaczenie |
|---|---|---|
/ (indeks) | HomeDashboard | Pulpit główny / przeglądowy |
/design-studio | DesignStudio | Wizualny projektant potoków (React Flow) |
/design-studio/:pipelineId | DesignStudio | Edycja istniejącego potoku |
/data-browser | DataBrowser | Katalog danych / przeglądarka schematów |
/data-browser/:tableId | DataBrowser | Szczegóły tabeli |
/data-browser/:tableId/columns/:columnName | DataBrowser | Szczegóły kolumny |
/my-pipelines | MyPipelines | Lista potoków użytkownika |
/pipelines/new | CreatePipelineWizard | Kreator tworzenia potoku |
/pipelines/:id | PipelineDetail | Widok szczegółów potoku |
/pipelines/:id/edit | CreatePipelineWizard | Edycja potoku poprzez kreator |
/connections/new | ConnectionWizard | Kreator konfiguracji połączenia |
/connections/cdc | CdcConnectionsPage | Strona operatora połączeń CDC |
/data-marketplace | DataMarketplacePage | Marketplace produktów danych |
/data-marketplace/products/:id | DataProductDetail | Szczegóły produktu danych |
/marketplace | ConnectorMarketplace | Marketplace konektorów |
/templates | PipelineTemplates | Galeria szablonów potoków |
/telecom/subscriber-360 | Subscriber360TemplatePage | Telekomunikacyjny szablon Subscriber 360 |
Monitor — /monitor (MonitorLayout, zagnieżdżony)
| Ścieżka | Strona | Przeznaczenie |
|---|---|---|
/monitor | przekierowuje do runs | |
/monitor/runs | PipelineRunsDashboard | Lista uruchomień potoków |
/monitor/runs/:runId | PipelineRunDetail | Szczegóły pojedynczego uruchomienia |
/monitor/runs/:runId/logs | LogViewerPage | Przeglądarka logów uruchomienia |
/monitor/performance | PerformanceAnalytics | Analityka wydajności |
/monitor/alerts | AlertManagement | Zarządzanie alertami |
/monitor/logs | LogViewerPage | Globalna przeglądarka logów |
/monitor/self-healing | SelfHealingDashboard | Pulpit samonaprawy |
/monitor/quarantine | DataQuarantine | Dane poddane kwarantannie |
/monitor/freshness | DataFreshness | Monitor świeżości danych |
/monitor/sla | SlaBurnRate | Wskaźnik wypalania SLA |
/monitor/costs | PipelineCosts | Pulpit kosztów potoków |
Migracja — /migration (MigrationLayout, zagnieżdżony)
| Ścieżka | Strona | Przeznaczenie |
|---|---|---|
/migration | przekierowuje do import | |
/migration/import | ImportWizardPage | Kreator importu |
/migration/conversion | ConversionDashboardPage | Pulpit konwersji |
/migration/validation | ValidationSuitePage | Zestaw walidacji |
/migration/progress | ProgressTrackerPage | Tracker postępu migracji |
Nadzór — /governance (GovernanceHub, zagnieżdżony)
| Ścieżka | Strona | Przeznaczenie |
|---|---|---|
/governance | przekierowuje do lineage | |
/governance/lineage | LineageExplorer | Graf pochodzenia danych |
/governance/quality | QualityMonitoring | Monitorowanie jakości danych |
/governance/reviews | ReviewQueue | Kolejka przeglądów nadzoru |
/governance/reviews/:reviewId | GovernanceReviewDetailPage | Szczegóły przeglądu |
/governance/glossary | BusinessGlossary | Glosariusz biznesowy |
/governance/audit | AuditTrail | Ślad audytu |
/governance/schema-evolution | SchemaEvolution | Śledzenie ewolucji schematu |
/governance/contracts | DataContracts | Kontrakty danych |
/governance/compliance | ComplianceDashboard | Zgodność z polskimi regulacjami |
/governance/dsar | DsarRequestsPage | Lista żądań DSAR RODO |
/governance/dsar/:id | DsarRequestDetailPage | Szczegóły żądania DSAR |
/governance/masking | MaskingPolicyEditor | Edytor polityki maskowania PII |
/governance/access-revocation | AccessRevocationQueue | Kolejka odbierania dostępu |
/governance/data-versions | DataVersionsPage | Historia wersji danych |
Administracja — /admin (AdminConsole, zagnieżdżony)
| Ścieżka | Strona | Przeznaczenie |
|---|---|---|
/admin | przekierowuje do users | |
/admin/users | UsersWorkspaces | Użytkownicy i obszary robocze |
/admin/security | Security | Ustawienia bezpieczeństwa (SSO, sesje) |
/admin/infrastructure | Infrastructure | Kondycja usług i infrastruktura |
/admin/costs | CostManagement | Zarządzanie kosztami |
/admin/environments | Environments | Promocja środowisk |
/admin/reports | ScheduledReports | Zaplanowane raporty |
/admin/ai-provider | AiProviderSettings | Konfiguracja dostawcy AI/LLM |
/admin/bootstrap | BootstrapWizardPage | Kreator rozruchu platformy |
/admin/incidents/runbooks | RunbookListPage | Lista runbooków incydentów |
/admin/incidents/runbooks/:id | RunbookExecutionPage | Wykonanie runbooka |
/admin/audit-log | AuditLogPage | Systemowy log audytu |
/admin/access-reviews | AccessReviewCycles | Cykle przeglądów dostępu |
/admin/access-reviews/cycles/:cycleId | CycleDetail | Szczegóły cyklu przeglądu dostępu |
/admin/tag-policies | TagPoliciesPage | Polityki tagów |
/admin/regulatory-frameworks | RegulatoryFrameworks | Szablony ram regulacyjnych |
/admin/workflows | WorkflowDesigner | Projektant automatyzacji przepływów pracy |
Trasa wychwytująca wszystko /* renderuje <Navigate to="/" replace />.
Nierouting starsze pliki
Kilka plików w pages/ nie jest podpiętych do routera — starsze lub zduplikowane warianty, takie jak LoginPage.tsx, MigrationCenter.tsx, MonitorCenter.tsx, ConnectorMarketplacePage.tsx i PolishCompliance.tsx. Router używa wariantów bez sufiksu. Plik tras zawiera komentarz inwentarza lock-in dla testu wave6-router-contract.test.ts.
Architektura komponentów
Komponenty są zorganizowane według obszaru funkcjonalnego pod frontend/src/components/.
| Obszar | Folder | Kluczowe komponenty |
|---|---|---|
| Powłoka / nawigacja | shell/ | AppShell, Sidebar, TopBar, ProtectedRoute, MobileMenu, MobileBottomNav, BreadcrumbNav, CommandPalette, SearchModal, LanguageSwitcher, NotificationCenter, OnboardingTour |
| Design Studio | design-studio/ | PipelineToolbar, PipelineSelector, NodeDetailPanel, SaveIndicator, VersionHistory, GitDiffViewer, ExportReportModal, StreamingNodeTypes, nodes/PipelineNode, editors/SchemaExplorer |
| Monitor | monitor/ | MonitorLayout, GanttChart, PipelineDetailPanel, PipelineRunsTable |
| Nadzór | governance/ | GovernanceReviewModal, quality/QualityOverview, lineage/ (LineageGraph, LineageNode, LineageMiniMap, ImpactAnalysisPanel) |
| Administracja | admin/ | IncidentPanel, users/ (UserTable, RoleMatrix, ADGroupMapping), infra/ (ServiceHealthGrid), costs/, envs/, security/ (SSOConfig, SessionManager) |
| Migracja | migration/ | MigrationLayout (strony kreatora znajdują się w pages/migration/) |
| AI Copilot | ai-copilot/ | AICopilotSidebar, AICopilotTrigger, ChatMessage, ChatInput, QuickActions, InsightCard, DiffViewer, ErrorDiagnosis |
| Data Browser | data-browser/ | CatalogSearch, ConnectionTree, SchemaViewer, TableCard, ColumnDetail, DataProfile, SampleDataPreview |
| Potoki | pipelines/ | CreatePipelineWizard, PipelineCard, PipelineListItem, PipelinePagination, DeleteConfirmModal |
| Połączenia | connections/ | ConnectionWizard, ConnectorTypeCard, DatabaseIcons |
| Prymitywy układu | layout/ | PageContainer, PageHeader, SplitPane |
| Zestaw UI | ui/ | ~40 prymitywów — DataTable, DataGrid, Tabs, Badge, Input, Select, Timeline, CodeBlock, Toast, wykresy, PermissionGate |
Zestaw ui/ zawiera około czterdzieści prymitywów prezentacyjnych — kontrolki formularzy, tabele, pomoce nawigacyjne, wykresy (LineChart, BarChart, GaugeChart), pomocniki układu (Stack, ResponsiveGrid, Divider) oraz pomocniki dostępności, takie jak SkipNav i PermissionGate.
Zarządzanie stanem
Magazyny Zustand
Stan klienta znajduje się w dwunastu magazynach Zustand pod frontend/src/stores/. Wszystkie są tworzone przez create(); cztery opakowują middleware persist, aby utrwalić swój stan w localStorage pod kluczami dataflow-*.
| Magazyn | Klucz utrwalenia | Przeznaczenie |
|---|---|---|
themeStore | dataflow-theme | theme: 'dark' | 'light' (domyślnie dark); przełącza klasę <html>.dark |
personaStore | dataflow-persona | activePersona (domyślnie engineer), isKeycloakAuthenticated; cztery persony |
shellStore | dataflow-shell (częściowo) | pasek boczny rozwinięty/najechany/zwinięty, menu mobilne, flagi otwarcia paneli |
onboardingStore | utrwalony | stan ukończenia wprowadzającego tour onboardingu |
copilotStore | — | stan czatu i wglądu AI copilota |
pipelineStore | — | stan projektanta potoków |
catalogStore | — | stan przeglądania katalogu danych |
governanceStore | — | stan funkcji nadzoru |
migrationStore | — | stan przepływu pracy migracji |
monitorStore | — | stan widoku monitora |
connectionStore | — | stan kreatora połączeń |
adminStore | — | stan konsoli administracyjnej |
Stan serwera
Dane serwera są zarządzane za pomocą TanStack React Query 5 poprzez generyczny hook useApiQuery oraz per-funkcjonalne hooki danych, takie jak usePipelineData, useCatalogData, useMonitorData, useGovernanceData i useAdminData. React Query obsługuje buforowanie, deduplikację i rewalidację; Axios wykonuje transport leżący u podstaw.
Warstwa klienta API
Klient bazowy
Klient bazowy to instancja Axios w api/client.ts. Jego baseURL to VITE_API_URL ?? '/api/v1', jego timeout to VITE_API_TIMEOUT ?? 30s, a withCredentials jest celowo ustawione na false — ustawienie go na true zatrzymywało fragmentowane odpowiedzi bramy.
Klient implementuje kilka zachowań przekrojowych:
- Wstrzykiwanie akcesora tokena.
registerTokenAccessor(fn)jest wywoływane raz przezAuthProvider, unikając cyklicznego importu singletona Keycloak. - Bramka gotowości uwierzytelniania.
signalAuthReady()rozwiązuje obietnicę po zakończeniuKeycloak.init(). Interceptor żądańawait-uje ją — ścigając się zAbortSignalżądania oraz 5-sekundowym timeoutem bezpieczeństwa — tak aby pierwsze wywołania/api/v1/*zawsze niosły JWT i nie zwracały 401. - Interceptor żądań. Dołącza
Authorization: Bearer <token>; przy POST/PUT/PATCH/DELETE dodajeX-CSRF-Token, odczytywany ze znacznika<meta name="csrf-token">lub ciasteczkaXSRF-TOKEN; emituje logowanie deweloperskie. - Interceptor odpowiedzi. Przy 401 odświeża token raz — chroniony nagłówkiem
x-retry-after-refresh— i ponawia żądanie. Rekurencyjnie konwertuje kluczesnake_casenacamelCase(Jackson backendu dostarcza SNAKE_CASE) i normalizuje każdy błąd doApiErrorniosącegostatus,codeierrors, z pomocniczymi getteramiisUnauthorized,isForbidden,isNotFoundiisValidation.
Na górze znajdują się wygodne opakowania apiGet, apiPost, apiPut, apiPatch i apiDelete.
Moduły domenowe API
Około pięćdziesiąt per-funkcjonalnych typowanych opakowań znajduje się pod api/ — na przykład pipelines, pipelineTemplates, connections, cdc, cdr, catalog, copilot, governance, lineage, quality, monitoring, metrics, migration, dataProducts, masking, gdpr, auditLog, admin i search. api/index.ts to barel re-eksportu.
Transport czasu rzeczywistego
Folder services/ zawiera sse.ts / SSEManager.ts dla Server-Sent Events (alerty na żywo, metryki, strumieniowanie copilota), websocket.ts / WebSocketManager.ts dla transportu WebSocket oraz pipelineValidator.ts dla walidacji potoków po stronie klienta. Dane czasu rzeczywistego są konsumowane przez hooki takie jak useAlertStream, useMetricsStream, useCopilotStream, usePipelineStream i useMonitorWebSocket.
Uwierzytelnianie
Konfiguracja Keycloak
auth/keycloak.ts przechowuje singleton keycloakInstance skonfigurowany z VITE_KEYCLOAK_URL (domyślnie http://localhost:8180), VITE_KEYCLOAK_REALM (dataflow) i VITE_KEYCLOAK_CLIENT_ID (dataflow-app). Instancja jest udostępniana na window.__keycloak dla diagnostyki Playwright. Opcje inicjalizacji to onLoad: 'check-sso', pkceMethod: 'S256', silentCheckSsoRedirectUri wskazujący na /silent-check-sso.html oraz checkLoginIframe: false.
ROLE_PERSONA_MAP akceptuje zarówno natywne dla Keycloak nazwy ról (org_admin, workspace_admin, developer, operator, data_steward, analyst), jak i uprawnienia Spring ROLE_*, mapując je — uszeregowane priorytetowo, z domyślnym analyst — na cztery persony: admin, steward, engineer i analyst. Kluczowe stałe to TOKEN_REFRESH_BUFFER_SECONDS = 30, SESSION_TIMEOUT_WARNING_SECONDS = 60 oraz MIN_TOKEN_VALIDITY_SECONDS = 60.
Przepływ uwierzytelniania
auth/AuthProvider.tsx dostarcza kontekst React, konsumowany przez useAuth(), udostępniający isAuthenticated, isLoading, isDevMode, user, token, personas, login(), logout(), hasRole() i hasPermission().
mount AuthProvider
│
├─ register token accessor with api/client
│
├─ keycloakInstance.init() ──── Promise.race ──── 15s timeout
│ │ │
│ success failure
│ │ │
│ build KeycloakUserProfile allowDevModeFallback?
│ from tokenParsed: ┌──────┴───────┐
│ realm_access.roles, names, yes no
│ email, workspace_id │ │
│ │ dev mode unauthenticated
│ derive persona + permissions (personaStore +
│ │ buildDevProfile)
│ schedule silent refresh (30s pre-expiry)
│ schedule session warning (60s pre-expiry)
│ │
└────────┴──► signalAuthReady() → unblocks the API interceptor
- Przy montowaniu
AuthProviderrejestruje akcesor tokena wapi/clienti uruchamiakeycloakInstance.init()pod 15-sekundowym timeoutemPromise.race. - Przy powodzeniu buduje
KeycloakUserProfileztokenParsed—realm_access.roles,given_name/family_name,email,workspace_id— i wyprowadza personę pluspersonaPermissions. - Planuje ciche odświeżenie tokena poprzez
updateToken30 sekund przed wygaśnięciem oraz ostrzeżenie o przekroczeniu czasu sesji 60 sekund przed wygaśnięciem, które otwieraSessionTimeoutModal. - Przy niepowodzeniu inicjalizacji, jeśli
allowDevModeFallbackma wartość true, wchodzi w tryb deweloperski, używającpersonaStoreibuildDevProfile; w przeciwnym razie pozostaje nieuwierzytelniony. - Zawsze wywołuje
signalAuthReady()po inicjalizacji, niezależnie od wyniku, co odblokowuje interceptor żądań API. login()przekierowuje do Keycloak (domyślne przekierowanie/);logout()czyści timery i stan oraz przekierowuje do/login.
Uwierzytelnianie deweloperskie
auth/devAuth.ts definiuje cztery zakodowane na stałe persony deweloperskie — Jan Kowalski (engineer), Anna Nowak (analyst), Piotr Wiśniewski (admin) i Maria Kamińska (steward), wszystkie w domenie @polkomtel.pl. Testy end-to-end Playwright używają obejścia VITE_AUTH_MODE=dev. SessionTimeoutModal jest pokazywany tylko w rzeczywistym, niedeweloperskim trybie.
Internacjonalizacja
i18n/index.ts podpina i18next z react-i18next oraz i18next-browser-languagedetector.
- Języki:
enipl— angielski i polski.supportedLanguages = ['en','pl']zfallbackLng: 'en'. - Osiem przestrzeni nazw:
common,admin,governance,pipeline,monitor,migration,catalogicopilot. Zasoby JSON są dołączane per język podi18n/locales/{en,pl}/. - Kolejność wykrywania:
localStorage(kluczdataflow-language), następnienavigator; wybrany język jest buforowany z powrotem dolocalStorage. react.useSuspensema wartośćfalse;returnNullireturnEmptyStringmają obie wartośćfalse.- Tłumaczenia są dostępne przez hook
useT, a powłoka udostępnia komponentLanguageSwitcher.
Model nawigacji paska bocznego w data/navigation.ts kluczuje swoje etykiety przez navI18nKeys pod prefiksem nav.*, dzięki czemu całe drzewo nawigacji jest tłumaczalne.
Motywy i tokeny projektowe
Motywy są sterowane przez themeStore z domyślnym ustawieniem dark. Magazyn przełącza klasę dark na <html>, a Tailwind CSS 4 rozwiązuje semantyczne tokeny projektowe względem tej klasy. Komponenty odwołują się do klas tokenów, a nie surowych kolorów — bg-surface-0/1/2, text-heading/body/secondary/muted, border-primary — z amber-500 jako akcentem marki.
Formularze używają react-hook-form z resolverami zod poprzez hook useFormWithSchema. Błędy są wychwytywane na trzech poziomach: globalny react-error-boundary w App.tsx, per-trasowy ErrorBoundary wewnątrz SuspenseWrap oraz PanelErrorFallback dla awarii ograniczonych do panelu. Każda strona jest ładowana przez lazy() z fallbackiem spinnera Suspense dla podziału kodu.
Zobacz także
Język projektowy — tokeny, ciemny motyw amber/zinc, konwencje komponentów i adaptująca się do persony powłoka — jest w pełni udokumentowany na stronie System Design Studio i konwencje UI.