diff --git a/deploy/nginx-dashfront.conf b/deploy/nginx-dashfront.conf
index f056f4b..5ff2370 100644
--- a/deploy/nginx-dashfront.conf
+++ b/deploy/nginx-dashfront.conf
@@ -90,13 +90,13 @@ server {
proxy_pass http://127.0.0.1:4208;
proxy_http_version 1.1;
- # Clear Connection & Accept-Encoding to prevent upstream gzip/chunk buffering
- proxy_set_header Connection "";
- proxy_set_header Accept-Encoding "";
- proxy_set_header Host $http_host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
+ # Clear Connection & Accept-Encoding to prevent upstream buffering
+ proxy_set_header Connection "";
+ proxy_set_header Accept-Encoding "";
+ proxy_set_header Host $http_host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
# Disable buffering and compression completely for immediate push
proxy_buffering off;
@@ -104,7 +104,8 @@ server {
chunked_transfer_encoding off;
gzip off;
- # Prevent premature disconnections; keep SSE open for 24 hours
+ # Disable HTTP/2 request limits for persistent SSE connection
+ # (Prevents NGINX from resetting long-lived streams)
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 0000000..198ac7d
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,86 @@
+# Dashfront Automats Documentation
+
+This directory contains reference documentation for all state automats in `dashfront` (`src/automats/`), powered by [`react-automat`](https://github.com/sebgreen/automat).
+
+---
+
+## Architecture Overview
+
+In `dashfront`, state is managed outside the React component lifecycle using **Automats**. An Automat is an observable, fine-grained state store living in module scope.
+
+### Core Principles
+
+1. **Module Scope**: State persists across route changes and component mount/unmount cycles.
+2. **Synchronous Reads**: Components read state synchronously in their `constructor` (or initial state) — zero loading flicker or blank frames.
+3. **Fine-Grained Subscriptions**: React `PureComponent` instances subscribe in `componentDidMount` with selector functions or key lists, skipping unnecessary re-renders.
+4. **Explicit Cleanup**: Every subscription returns an `unsub()` function called in `componentWillUnmount()`.
+5. **No Context / No Hooks in PureComponents**: Automats bypass React Context providers and hooks, eliminating reconciliation cascades.
+6. **Automatic Coordination**: Domain and configuration automats subscribe to [`authAutomat`](./authAutomat.md) to automatically bootstrap on login, switch data when the active company changes, and reset on logout.
+
+---
+
+## Automats Directory
+
+| Automat | State File | Documentation | Description |
+|---|---|---|---|
+| **`artikelAutomat`** | [`src/automats/artikelAutomat.js`](file:///home/seb/src/dashfront/src/automats/artikelAutomat.js) | [artikelAutomat.md](./artikelAutomat.md) | Cache and reactive distributor for single article detail payloads (`GET /api/article/:sku`) with inflight request deduplication. |
+| **`authAutomat`** | [`src/automats/authAutomat.js`](file:///home/seb/src/dashfront/src/automats/authAutomat.js) | [authAutomat.md](./authAutomat.md) | Manages authentication lifecycle, session bootstrap, Google One Tap login, and multi-tenant company selection. |
+| **`brandAutomat`** | [`src/automats/brandAutomat.js`](file:///home/seb/src/dashfront/src/automats/brandAutomat.js) | [brandAutomat.md](./brandAutomat.md) | Company-scoped catalog brands / manufacturers list (`GET /api/brand`, upsert). Auto-loads on company auth. |
+| **`categoryAutomat`** | [`src/automats/categoryAutomat.js`](file:///home/seb/src/dashfront/src/automats/categoryAutomat.js) | [categoryAutomat.md](./categoryAutomat.md) | Hierarchical category tree (id → node map), route view article automats, tree ordering, drag-and-drop moves, and image uploads. |
+| **`companyAutomat`** | [`src/automats/companyAutomat.js`](file:///home/seb/src/dashfront/src/automats/companyAutomat.js) | [companyAutomat.md](./companyAutomat.md) | Active company configuration, tax rates (standard, reduced), root category ID, and company profile info. |
+| **`deliveryEditorAutomat`** | [`src/automats/deliveryEditorAutomat.js`](file:///home/seb/src/dashfront/src/automats/deliveryEditorAutomat.js) | [deliveryEditorAutomat.md](./deliveryEditorAutomat.md) | Multi-line inbound delivery editor, OCR/PDF receipt reconciliation, supplier article auto-matching, and draft badge tracking. |
+| **`navigationAutomat`** | [`src/automats/navigationAutomat.js`](file:///home/seb/src/dashfront/src/automats/navigationAutomat.js) | [navigationAutomat.md](./navigationAutomat.md) | Tracks and restores last visited article SKU and delivery paths across top-level section navigation. |
+| **`posterminalAutomat`** | [`src/automats/posterminalAutomat.js`](file:///home/seb/src/dashfront/src/automats/posterminalAutomat.js) | [posterminalAutomat.md](./posterminalAutomat.md) | Point-of-Sale hardware terminals and enabled category assignments per terminal. |
+| **`supplierAutomat`** | [`src/automats/supplierAutomat.js`](file:///home/seb/src/dashfront/src/automats/supplierAutomat.js) | [supplierAutomat.md](./supplierAutomat.md) | Company-scoped supplier directory (`GET /api/supplier`, create, update, delete) with inflight deduplication. |
+| **`unitAutomat`** | [`src/automats/unitAutomat.js`](file:///home/seb/src/dashfront/src/automats/unitAutomat.js) | [unitAutomat.md](./unitAutomat.md) | Standard units of measure (Stück, kg, Liter, etc.) for the active company (`GET /api/unit`, upsert, delete). |
+| **`userSettingsAutomat`** | [`src/automats/userSettingsAutomat.js`](file:///home/seb/src/dashfront/src/automats/userSettingsAutomat.js) | [userSettingsAutomat.md](./userSettingsAutomat.md) | Client-side user UI preferences (e.g. viewer docking position). Persisted to IndexedDB (`persist: true`). |
+
+---
+
+## Canonical PureComponent Consumption Pattern
+
+```jsx
+import { PureComponent } from 'react';
+import { artikelAutomat, createArticleSelector, getArticle } from '../automats';
+
+class ArticleHeader extends PureComponent {
+ constructor(props) {
+ super(props);
+ // 1. Synchronous read — no initial flash of empty state
+ this.state = {
+ article: getArticle(props.sku),
+ };
+ }
+
+ componentDidMount() {
+ // 2. Subscribe with fine-grained selector
+ this.unsub = artikelAutomat.subscribe(this, createArticleSelector(this.props.sku));
+ }
+
+ componentDidUpdate(prevProps) {
+ // 3. Re-subscribe if props change
+ if (prevProps.sku !== this.props.sku) {
+ this.unsub?.();
+ this.setState({ article: getArticle(this.props.sku) });
+ this.unsub = artikelAutomat.subscribe(this, createArticleSelector(this.props.sku));
+ }
+ }
+
+ componentWillUnmount() {
+ // 4. Always unsubscribe
+ this.unsub?.();
+ }
+
+ render() {
+ const { article } = this.state;
+ if (!article) return
Lädt...
;
+ return {article.name}
;
+ }
+}
+```
+
+---
+
+## Debugging
+
+All registered automats are available on `window.__AUTOMATS__` and accessible via `Automat.get(name)`. In development mode, the docked **AutomatInspector** (`src/devtools/AutomatInspector.jsx`) renders live state, subscriber counts, and action triggers.
diff --git a/docs/artikelAutomat.md b/docs/artikelAutomat.md
new file mode 100644
index 0000000..d14a2dc
--- /dev/null
+++ b/docs/artikelAutomat.md
@@ -0,0 +1,196 @@
+# `artikelAutomat`
+
+> **Source File**: [`src/automats/artikelAutomat.js`](file:///home/seb/src/dashfront/src/automats/artikelAutomat.js)
+> **Registration Key**: `'artikel'` (`Automat.get('artikel')` / `window.__AUTOMATS__.get('artikel')`)
+
+---
+
+## 1. Overview & Purpose
+
+`artikelAutomat` serves as the centralized cache and reactive distributor for individual article detail payloads (`GET /api/article/:sku`).
+
+### Core Responsibilities
+- **In-flight Deduplication**: Multiple components or route guards requesting the same SKU concurrently share a single HTTP request via an internal `Map>`.
+- **Zero-Flicker Navigation**: Navigating between tabs or returning to previously viewed articles reuses the cached detail object instantly without round-trips.
+- **Optimistic Updates**: Immediate UI updates (such as renaming an article via `setArticleName`) without awaiting network round-trips.
+- **Granular Subscriptions**: Components can subscribe to an individual SKU via `createArticleSelector(sku)` rather than re-rendering on every cache change.
+
+---
+
+## 2. State Structure
+
+```ts
+interface ArtikelState {
+ /**
+ * Cached article detail payloads keyed by SKU string.
+ * A missing key indicates the article is not yet loaded into memory.
+ * Values include full article details: prices, photos, eans, brand, unit,
+ * categoryIds, manifests, child variants, etc.
+ */
+ bySku: Record;
+}
+```
+
+### Initial State
+```js
+const initialState = {
+ bySku: {},
+};
+```
+
+---
+
+## 3. Configuration & Options
+
+```js
+new Automat(initialState, actions, { name: 'artikel' });
+```
+
+- **`name`**: `'artikel'` — registers the automat globally.
+- **`persist`**: `false` (default in-memory window lifecycle).
+
+---
+
+## 4. Actions & Methods
+
+Access actions via `artikelAutomat.actions.()`.
+
+### `loadArticle(sku: string): Promise