Files
dashfront/docs/brandAutomat.md
2026-09-23 17:24:33 +02:00

169 lines
4.6 KiB
Markdown

# `brandAutomat`
> **Source File**: [`src/automats/brandAutomat.js`](file:///home/seb/src/dashfront/src/automats/brandAutomat.js)
> **Registration Key**: `'brand'` (`Automat.get('brand')` / `window.__AUTOMATS__.get('brand')`)
---
## 1. Overview & Purpose
`brandAutomat` manages the active company's catalog brands (manufacturers). Brands are referenced by articles for catalog organization, filtering, and GPSR (General Product Safety Regulation) manufacturer compliance info.
### Core Responsibilities
- **Company Scoping**: Caches all brands for the currently selected company.
- **Alphabetical Sorting**: Automatically sorts brands by name using German (`'de'`) collation.
- **Autonomous Bootstrap**: Automatically triggers `load()` when `authAutomat` enters `'authed'` with a selected company, and calls `reset()` when the session ends.
- **Upsert Mutation**: Handles creating or updating a brand with immediate in-memory cache synchronization.
---
## 2. State Structure
```ts
interface Brand {
id: number | string;
name: string;
gsprpinfo?: string | null;
[key: string]: any;
}
interface BrandState {
/** Alphabetically sorted list of company brands. */
brands: Array<Brand>;
/** Async lifecycle status. */
status: 'idle' | 'loading' | 'succeeded' | 'failed';
/** Error message if loading failed. */
error: string | null;
}
```
### Initial State
```js
const initialState = {
brands: [],
status: 'idle',
error: null,
};
```
---
## 3. Configuration & Options
```js
new Automat(initialState, actions, { name: 'brand' });
```
- **`name`**: `'brand'`
- **`persist`**: `false`
---
## 4. Actions & Methods
Access actions via `brandAutomat.actions.<method>()`.
### `load(): Promise<Array<Brand>>`
Fetches all brands for the current company via `GET /api/brand`. Sorts them by name, updates `status` to `'succeeded'`, and returns the array. If the request fails, sets `status` to `'failed'` and records `error`.
### `upsert(body: { name: string, gsprpinfo?: string }): Promise<Brand>`
Creates or updates a brand via `POST /api/brand/upsert`. Merges the updated brand into `state.brands`, re-sorts the list, updates state, and returns the modified brand object.
### `reset(): void`
Resets the state back to `initialState` (`brands: []`, `status: 'idle'`, `error: null`).
---
## 5. Cross-Automat Subscriptions
`brandAutomat` subscribes to `authAutomat` at module initialization:
```js
authAutomat.subscribe((auth) => {
const companyId = auth.user?.selectedCompanyId;
if (auth.status === 'authed' && companyId) {
void brandAutomat.actions.load();
} else if (auth.status === 'anon') {
brandAutomat.actions.reset();
}
});
```
---
## 6. Usage Examples
### A. React `PureComponent` Brand Selector Dropdown
```jsx
import { PureComponent } from 'react';
import PropTypes from 'prop-types';
import brandAutomat from '../automats/brandAutomat';
export class BrandSelect extends PureComponent {
static propTypes = {
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
onChange: PropTypes.func.isRequired,
};
constructor(props) {
super(props);
this.state = {
brands: brandAutomat.state.brands,
status: brandAutomat.state.status,
};
}
componentDidMount() {
this.unsub = brandAutomat.subscribe(this, ['brands', 'status']);
if (this.state.status === 'idle') {
brandAutomat.actions.load().catch(console.error);
}
}
componentWillUnmount() {
this.unsub?.();
}
render() {
const { brands, status } = this.state;
const { value, onChange } = this.props;
if (status === 'loading') {
return <select disabled><option>Marken laden...</option></select>;
}
return (
<select value={value ?? ''} onChange={(e) => onChange(e.target.value)}>
<option value="">-- Keine Marke --</option>
{brands.map((b) => (
<option key={b.id} value={b.id}>{b.name}</option>
))}
</select>
);
}
}
```
### B. Creating or Updating a Brand
```js
import brandAutomat from '../automats/brandAutomat';
async function handleSaveBrand(name, gsprpinfo) {
try {
const brand = await brandAutomat.actions.upsert({ name, gsprpinfo });
console.log('Saved brand:', brand);
} catch (err) {
console.error('Failed to save brand:', err);
}
}
```
---
## 7. Best Practices & Gotchas
1. **Automatic Loading**: In normal usage, you rarely need to invoke `load()` manually; `authAutomat` triggers it as soon as the user logs in or switches company.
2. **Alphabetical Guarantee**: The `brands` array in state is guaranteed to be sorted alphabetically (`localeCompare` with German rules).