# Redux in Dashfront Übersicht, wie Redux Toolkit bisher im Frontend eingesetzt wird. ## Stack - **@reduxjs/toolkit** — Store, Slices, Async Thunks, Listener Middleware - **react-redux** — `Provider` in `src/main.jsx`, Anbindung der UI über `connect()` (kein `useSelector` / `useDispatch`) Einstieg: ``` src/main.jsx → src/store/index.js → configureStore + Middleware src/store/*Slice.js → State, Thunks, Selectors ``` ## Store-Form Drei Slices unter `src/store/`: | Key | Datei | Verantwortung | |------------|---------------------|----------------------------------------------------| | `auth` | `authSlice.js` | Session, Login/Logout, Firmenwahl / -anlage | | `company` | `companySlice.js` | Aktuelles Unternehmen laden / aktualisieren | | `category` | `categorySlice.js` | Kategoriebaum, Navigation, Artikel je Kategorie | ```js // src/store/index.js configureStore({ reducer: { auth, company, category }, middleware: defaults + listenerMiddleware + (DEV: reduxDevLogger), }); ``` ## Typische Muster ### Async Thunks als API-Grenze Seiten- und Dialog-Logik dispatcht Thunks; die Thunks rufen `src/api` auf und schreiben Ergebnisse in den Slice. Komponenten sprechen die API nicht direkt für diese Domänen an. Beispiele: - `auth/bootstrap`, `auth/login`, `auth/selectCompany`, `auth/createCompany`, `auth/logout` - `company/loadCurrent`, `company/update` - `category/loadTree`, `category/loadArticles`, `category/create`, `category/createArticle`, `category/delete` Fehler in Category-Thunks laufen über `catchBackendError` / `rejectWithValue` und landen als `BackendError` in `state.category.error`. Tree- und Artikel-Responses werden mit `requireObject` / `requireArray` / `requireDefined` aus `apiShape.js` validiert. ### Selectors Jeder Slice exportiert `select…`-Funktionen. UI-Komponenten mappen sie in `mapStateToProps`: ```js const mapStateToProps = (state) => ({ status: selectAuthStatus(state), needsCompanySetup: selectNeedsCompanySetup(state), }); export default connect(mapStateToProps)(App); ``` Abgeleitete Category-Selectors (Breadcrumbs, Kinder, „kann zurück“) lesen die Map `categories` und `currentCategoryId` — keine zweite Kopie des Baums in der UI. ### `connect()` statt Hooks Die Redux-gebundenen Komponenten sind Class- oder Function-Komponenten mit: ```js connect(mapStateToProps, mapDispatchToProps)(Component) ``` Dispatch-Props wrappen Thunks, oft mit `.unwrap()` in der Komponente für Erfolg/Fehler (Snackbar). ### Listener: Auth → Company Nach erfolgreichem Bootstrap, Login, Firmenwahl oder Firmenanlage dispatcht ein Listener automatisch `loadCurrentCompany`, wenn `auth.user.selectedCompanyId` gesetzt ist (`companyLoadListener` in `companySlice.js`). ### Cross-Slice-Reset - `logout.fulfilled` leert `company` und setzt `category` auf `initialState` - `loadCurrentCompany.fulfilled` mit neuer `companyId` setzt den Category-State zurück (Firmenwechsel) ## Datenfluss (Kurz) ``` App-Start └─ AuthBootstrap → bootstrapSession └─ (listener) loadCurrentCompany wenn selectedCompanyId └─ Category-UI → loadCategoryTree (braucht company.rootCategoryId) Login / Firmenwahl / -anlage └─ auth-Thunks → user.selectedCompanyId └─ (listener) loadCurrentCompany → ggf. Category-Reset Kategorien-Seite └─ navigateToCategory / navigateBack (sync) └─ loadCategoryArticles für Unterkategorien └─ createCategory / createArticle / deleteCategory (optimistisch im Map-/Listen-State) ``` Status-Felder: | Slice | Feld(er) | Werte (typisch) | |----------|----------------------------------|------------------------| | auth | `status` | `loading` / `anon` / `authed` | | company | `status` | `idle` / `loading` / `succeeded` / `failed` | | category | `treeStatus`, `articlesStatus` | `idle` / `loading` / `succeeded` / `failed` | ## Auth-Slice - **State:** `user`, `googleClientId`, `status` - **Setup-Gate:** `selectNeedsCompanySetup` → eingeloggt, aber keine `selectedCompanyId` → `CompanySetupDialog` - `mergeUser` behält beim Partial-Update ggf. die bisherige `selectedCompanyId` ## Company-Slice - **State:** `current` (normalisiert über `normalizeCompany`), `status`, `error` - Thunks lesen `auth.user.selectedCompanyId` aus dem Store (`getState`) - Selectors liefern u. a. Name, `iso639`, Steuersätze, `rootCategoryId` für Category/UI ## Category-Slice - **Baum:** `categories` als Id→Node-Map, plus `rootCategoryId`, `rootArticles`, `languageId` - **Navigation:** `currentCategoryId`; Root zeigt `rootArticles`, Unterkategorien laden per `loadCategoryArticles` - **Sync-Actions:** `navigateToCategory`, `navigateBack`, `resetCategoryState` - Hilfsfunktionen (`normalizeCategoryNode`, `parseCategoryTreeResponse`, Breadcrumbs, Slugs) leben im Slice und werden u. a. von `categoryRoute.js` für URL-Pfade wiederverwendet ## Dev-Logging In Development loggt `reduxDevLogger` Actions unter `auth/`, `company/`, `category/` inkl. Payload und kurzem Auth-/Company-Snapshot nach dem Reduce. ## Was (noch) nicht in Redux liegt Artikel-Detail, Fotos und ähnliche lokale UI-Zustände werden in Komponenten / lokalen Props gehalten, nicht als eigener Slice. Neue Domänen folgen dem bestehenden Muster: API in `src/api`, Thunk + Validierung im Slice, Selectors + `connect` in der UI.