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.

ZagadnienieBibliotekaWersja
Środowisko uruchomieniowe UIreact / react-dom19.2
Build / serwer deweloperskivite + @vitejs/plugin-react7.3
Routingreact-router-dom7.13 (createBrowserRouter)
Stan klientazustand5.0 (z middleware persist)
Stan serwera@tanstack/react-query5.90
Tabele@tanstack/react-table 8.21, @tanstack/react-virtual 3.13
Edytor przepływów / grafów@xyflow/react12.10 (React Flow)
Wykresyrecharts3.7
Edytor kodu@monaco-editor/react4.7
Formularzereact-hook-form 7.71 + @hookform/resolvers + zod 4.3
Uwierzytelnianiekeycloak-js26.2
HTTPaxios1.13
i18ni18next 25.8 + react-i18next 16.5 + detektor języka przeglądarki + http-backend
Ikonylucide-react0.576
Paleta poleceńcmdk1.1
Datydate-fns4.1
Obsługa błędówreact-error-boundary5.0
Stylowanietailwindcss 4.2 poprzez @tailwindcss/vite
Testowanievitest 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 SuspenseWrapErrorBoundary 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+K poprzez handler keydown w powłoce; oparty na prawdziwym, rankingowanym endpointcie wyszukiwania.
  • OnboardingTour — wyświetlany, dopóki tourCompleted w onboardingStore nie ma wartości true.
  • HelpDrawer i CatalogAskPanel — sterowane lokalnym stanem komponentu (helpOpen, askOpen).
  • Link pominięcia nawigacji do #main-content dla 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ę:

  1. Podczas isLoading renderuje spinner z etykietą „Sprawdzanie dostępu...".
  2. Jeśli użytkownik nie jest uwierzytelniony, renderuje <Navigate to="/login">, przenosząc pierwotną lokalizację from w stanie routera.
  3. Oblicza effectivePersona — w trybie deweloperskim z personaStore, w przeciwnym razie z user.persona wyprowadzonej z ról Keycloak.
  4. Wywołuje canAccess(persona, pathname) z data/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żkaStronaPrzeznaczenie
/loginLoginWejście logowania SSO Keycloak
/callbackCallbackHandler callbacku przekierowania OIDC
/unauthorizedUnauthorizedStrona niepowodzenia uwierzytelniania / braku sesji
/onboardingOnboardingPrzepływ onboardingu przy pierwszym logowaniu
/report/:pipelineIdPipelineReportSamodzielny raport potoku, czysty układ drukowania do PDF

Główne trasy wewnątrz powłoki

ŚcieżkaStronaPrzeznaczenie
/ (indeks)HomeDashboardPulpit główny / przeglądowy
/design-studioDesignStudioWizualny projektant potoków (React Flow)
/design-studio/:pipelineIdDesignStudioEdycja istniejącego potoku
/data-browserDataBrowserKatalog danych / przeglądarka schematów
/data-browser/:tableIdDataBrowserSzczegóły tabeli
/data-browser/:tableId/columns/:columnNameDataBrowserSzczegóły kolumny
/my-pipelinesMyPipelinesLista potoków użytkownika
/pipelines/newCreatePipelineWizardKreator tworzenia potoku
/pipelines/:idPipelineDetailWidok szczegółów potoku
/pipelines/:id/editCreatePipelineWizardEdycja potoku poprzez kreator
/connections/newConnectionWizardKreator konfiguracji połączenia
/connections/cdcCdcConnectionsPageStrona operatora połączeń CDC
/data-marketplaceDataMarketplacePageMarketplace produktów danych
/data-marketplace/products/:idDataProductDetailSzczegóły produktu danych
/marketplaceConnectorMarketplaceMarketplace konektorów
/templatesPipelineTemplatesGaleria szablonów potoków
/telecom/subscriber-360Subscriber360TemplatePageTelekomunikacyjny szablon Subscriber 360

Monitor — /monitor (MonitorLayout, zagnieżdżony)

ŚcieżkaStronaPrzeznaczenie
/monitorprzekierowuje do runs
/monitor/runsPipelineRunsDashboardLista uruchomień potoków
/monitor/runs/:runIdPipelineRunDetailSzczegóły pojedynczego uruchomienia
/monitor/runs/:runId/logsLogViewerPagePrzeglądarka logów uruchomienia
/monitor/performancePerformanceAnalyticsAnalityka wydajności
/monitor/alertsAlertManagementZarządzanie alertami
/monitor/logsLogViewerPageGlobalna przeglądarka logów
/monitor/self-healingSelfHealingDashboardPulpit samonaprawy
/monitor/quarantineDataQuarantineDane poddane kwarantannie
/monitor/freshnessDataFreshnessMonitor świeżości danych
/monitor/slaSlaBurnRateWskaźnik wypalania SLA
/monitor/costsPipelineCostsPulpit kosztów potoków

Migracja — /migration (MigrationLayout, zagnieżdżony)

ŚcieżkaStronaPrzeznaczenie
/migrationprzekierowuje do import
/migration/importImportWizardPageKreator importu
/migration/conversionConversionDashboardPagePulpit konwersji
/migration/validationValidationSuitePageZestaw walidacji
/migration/progressProgressTrackerPageTracker postępu migracji

Nadzór — /governance (GovernanceHub, zagnieżdżony)

ŚcieżkaStronaPrzeznaczenie
/governanceprzekierowuje do lineage
/governance/lineageLineageExplorerGraf pochodzenia danych
/governance/qualityQualityMonitoringMonitorowanie jakości danych
/governance/reviewsReviewQueueKolejka przeglądów nadzoru
/governance/reviews/:reviewIdGovernanceReviewDetailPageSzczegóły przeglądu
/governance/glossaryBusinessGlossaryGlosariusz biznesowy
/governance/auditAuditTrailŚlad audytu
/governance/schema-evolutionSchemaEvolutionŚledzenie ewolucji schematu
/governance/contractsDataContractsKontrakty danych
/governance/complianceComplianceDashboardZgodność z polskimi regulacjami
/governance/dsarDsarRequestsPageLista żądań DSAR RODO
/governance/dsar/:idDsarRequestDetailPageSzczegóły żądania DSAR
/governance/maskingMaskingPolicyEditorEdytor polityki maskowania PII
/governance/access-revocationAccessRevocationQueueKolejka odbierania dostępu
/governance/data-versionsDataVersionsPageHistoria wersji danych

Administracja — /admin (AdminConsole, zagnieżdżony)

ŚcieżkaStronaPrzeznaczenie
/adminprzekierowuje do users
/admin/usersUsersWorkspacesUżytkownicy i obszary robocze
/admin/securitySecurityUstawienia bezpieczeństwa (SSO, sesje)
/admin/infrastructureInfrastructureKondycja usług i infrastruktura
/admin/costsCostManagementZarządzanie kosztami
/admin/environmentsEnvironmentsPromocja środowisk
/admin/reportsScheduledReportsZaplanowane raporty
/admin/ai-providerAiProviderSettingsKonfiguracja dostawcy AI/LLM
/admin/bootstrapBootstrapWizardPageKreator rozruchu platformy
/admin/incidents/runbooksRunbookListPageLista runbooków incydentów
/admin/incidents/runbooks/:idRunbookExecutionPageWykonanie runbooka
/admin/audit-logAuditLogPageSystemowy log audytu
/admin/access-reviewsAccessReviewCyclesCykle przeglądów dostępu
/admin/access-reviews/cycles/:cycleIdCycleDetailSzczegóły cyklu przeglądu dostępu
/admin/tag-policiesTagPoliciesPagePolityki tagów
/admin/regulatory-frameworksRegulatoryFrameworksSzablony ram regulacyjnych
/admin/workflowsWorkflowDesignerProjektant 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/.

ObszarFolderKluczowe komponenty
Powłoka / nawigacjashell/AppShell, Sidebar, TopBar, ProtectedRoute, MobileMenu, MobileBottomNav, BreadcrumbNav, CommandPalette, SearchModal, LanguageSwitcher, NotificationCenter, OnboardingTour
Design Studiodesign-studio/PipelineToolbar, PipelineSelector, NodeDetailPanel, SaveIndicator, VersionHistory, GitDiffViewer, ExportReportModal, StreamingNodeTypes, nodes/PipelineNode, editors/SchemaExplorer
Monitormonitor/MonitorLayout, GanttChart, PipelineDetailPanel, PipelineRunsTable
Nadzórgovernance/GovernanceReviewModal, quality/QualityOverview, lineage/ (LineageGraph, LineageNode, LineageMiniMap, ImpactAnalysisPanel)
Administracjaadmin/IncidentPanel, users/ (UserTable, RoleMatrix, ADGroupMapping), infra/ (ServiceHealthGrid), costs/, envs/, security/ (SSOConfig, SessionManager)
Migracjamigration/MigrationLayout (strony kreatora znajdują się w pages/migration/)
AI Copilotai-copilot/AICopilotSidebar, AICopilotTrigger, ChatMessage, ChatInput, QuickActions, InsightCard, DiffViewer, ErrorDiagnosis
Data Browserdata-browser/CatalogSearch, ConnectionTree, SchemaViewer, TableCard, ColumnDetail, DataProfile, SampleDataPreview
Potokipipelines/CreatePipelineWizard, PipelineCard, PipelineListItem, PipelinePagination, DeleteConfirmModal
Połączeniaconnections/ConnectionWizard, ConnectorTypeCard, DatabaseIcons
Prymitywy układulayout/PageContainer, PageHeader, SplitPane
Zestaw UIui/~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-*.

MagazynKlucz utrwaleniaPrzeznaczenie
themeStoredataflow-themetheme: 'dark' | 'light' (domyślnie dark); przełącza klasę <html>.dark
personaStoredataflow-personaactivePersona (domyślnie engineer), isKeycloakAuthenticated; cztery persony
shellStoredataflow-shell (częściowo)pasek boczny rozwinięty/najechany/zwinięty, menu mobilne, flagi otwarcia paneli
onboardingStoreutrwalonystan ukończenia wprowadzającego tour onboardingu
copilotStorestan czatu i wglądu AI copilota
pipelineStorestan projektanta potoków
catalogStorestan przeglądania katalogu danych
governanceStorestan funkcji nadzoru
migrationStorestan przepływu pracy migracji
monitorStorestan widoku monitora
connectionStorestan kreatora połączeń
adminStorestan 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 przez AuthProvider, unikając cyklicznego importu singletona Keycloak.
  • Bramka gotowości uwierzytelniania. signalAuthReady() rozwiązuje obietnicę po zakończeniu Keycloak.init(). Interceptor żądań await-uje ją — ścigając się z AbortSignal żą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 dodaje X-CSRF-Token, odczytywany ze znacznika <meta name="csrf-token"> lub ciasteczka XSRF-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 klucze snake_case na camelCase (Jackson backendu dostarcza SNAKE_CASE) i normalizuje każdy błąd do ApiError niosącego status, code i errors, z pomocniczymi getterami isUnauthorized, isForbidden, isNotFound i isValidation.

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
  1. Przy montowaniu AuthProvider rejestruje akcesor tokena w api/client i uruchamia keycloakInstance.init() pod 15-sekundowym timeoutem Promise.race.
  2. Przy powodzeniu buduje KeycloakUserProfile z tokenParsedrealm_access.roles, given_name/family_name, email, workspace_id — i wyprowadza personę plus personaPermissions.
  3. Planuje ciche odświeżenie tokena poprzez updateToken 30 sekund przed wygaśnięciem oraz ostrzeżenie o przekroczeniu czasu sesji 60 sekund przed wygaśnięciem, które otwiera SessionTimeoutModal.
  4. Przy niepowodzeniu inicjalizacji, jeśli allowDevModeFallback ma wartość true, wchodzi w tryb deweloperski, używając personaStore i buildDevProfile; w przeciwnym razie pozostaje nieuwierzytelniony.
  5. Zawsze wywołuje signalAuthReady() po inicjalizacji, niezależnie od wyniku, co odblokowuje interceptor żądań API.
  6. 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: en i pl — angielski i polski. supportedLanguages = ['en','pl'] z fallbackLng: 'en'.
  • Osiem przestrzeni nazw: common, admin, governance, pipeline, monitor, migration, catalog i copilot. Zasoby JSON są dołączane per język pod i18n/locales/{en,pl}/.
  • Kolejność wykrywania: localStorage (klucz dataflow-language), następnie navigator; wybrany język jest buforowany z powrotem do localStorage.
  • react.useSuspense ma wartość false; returnNull i returnEmptyString mają obie wartość false.
  • Tłumaczenia są dostępne przez hook useT, a powłoka udostępnia komponent LanguageSwitcher.

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.

Poprzednia
Usługi AI