u
This commit is contained in:
168
docs/brandAutomat.md
Normal file
168
docs/brandAutomat.md
Normal file
@@ -0,0 +1,168 @@
|
||||
# `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).
|
||||
Reference in New Issue
Block a user