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

5.7 KiB

posterminalAutomat

Source File: src/automats/posterminalAutomat.js
Registration Key: 'posterminal' (Automat.get('posterminal') / window.__AUTOMATS__.get('posterminal'))


1. Overview & Purpose

posterminalAutomat manages hardware Point-of-Sale (POS) terminals registered for the active company. In Dashfront, POS terminals can have specific category subsets enabled or disabled to control which products appear on specific physical checkouts or handheld POS devices.

Core Responsibilities

  • Terminal Registry: Lists all POS terminals (id, name, categoryIds).
  • Category Filter Management: Enabling or disabling categories for individual terminals (enableAll, disableAll, enableCategory, disableCategory).
  • Terminal CRUD: Creating new hardware terminal entries and deleting retired ones.
  • Autonomous Bootstrap: Automatically loads whenever authAutomat authenticates with a company and resets on logout.

2. State Structure

interface PosTerminal {
  id: number;
  name: string;
  categoryIds?: Array<number>;
  [key: string]: any;
}

interface PosterminalState {
  /** Registered POS terminals for active company. */
  terminals: Array<PosTerminal>;
  /** Company ID these terminals belong to. */
  companyId: number | string | null;
  /** Async lifecycle status. */
  status: 'idle' | 'loading' | 'succeeded' | 'failed';
  /** Last error message if load failed. */
  error: string | null;
}

Initial State

const initialState = {
  terminals: [],
  companyId: null,
  status: 'idle',
  error: null,
};

3. Configuration & Options

new Automat(initialState, actions, { name: 'posterminal' });
  • name: 'posterminal'
  • persist: false

4. Actions & Methods

Access actions via posterminalAutomat.actions.<method>().

load(): Promise<Array<PosTerminal>>

Determines the selected company ID from authAutomat or companyAutomat. Fetches terminals via GET /api/posterminal, updates state, and returns the terminals list.

create({ name: string }): Promise<PosTerminal>

Creates a new POS terminal via POST /api/posterminal and appends it to state.terminals.

delete(terminalId: number): Promise<void>

Deletes a terminal via DELETE /api/posterminal/:id and filters it out of state.terminals.

enableAll({ terminalId: number }): Promise<object>

Enables all categories on the given terminal via POST /api/posterminal/:id/category/enable-all. Patches categoryIds on the terminal in state.

disableAll({ terminalId: number }): Promise<object>

Disables all categories on the given terminal via POST /api/posterminal/:id/category/disable-all. Patches categoryIds on the terminal in state.

enableCategory({ terminalId: number, categoryId: number }): Promise<object>

Enables a specific category on the given terminal via POST /api/posterminal/:id/category/:catId/enable.

disableCategory({ terminalId: number, categoryId: number }): Promise<object>

Disables a specific category on the given terminal via DELETE /api/posterminal/:id/category/:catId/disable.

reset(): void

Resets the automat back to initialState.


5. Exported Standalone Selectors

  • selectPosterminals(): () => Array<PosTerminal> — Returns the list of registered terminals.
  • selectPosterminalStatus(): () => string — Returns the current loading status.
  • selectHasPosterminals(): () => boolean — Returns true if one or more terminals exist.

6. Cross-Automat Subscriptions

posterminalAutomat coordinates with authAutomat at module load:

authAutomat.subscribe((auth) => {
  const companyId = auth.user?.selectedCompanyId;
  if (auth.status === 'authed' && companyId) {
    void posterminalAutomat.actions.load();
  } else if (auth.status === 'anon') {
    posterminalAutomat.actions.reset();
  }
});

7. Usage Examples

A. React PureComponent Terminal Management List

import { PureComponent } from 'react';
import posterminalAutomat, { selectPosterminals } from '../automats/posterminalAutomat';

export class TerminalSettings extends PureComponent {
  constructor(props) {
    super(props);
    this.state = {
      terminals: selectPosterminals(),
      status: posterminalAutomat.state.status,
    };
  }

  componentDidMount() {
    this.unsub = posterminalAutomat.subscribe(this, ['terminals', 'status']);
  }

  componentWillUnmount() {
    this.unsub?.();
  }

  handleCreate = async () => {
    const name = window.prompt('Name des neuen POS-Terminals:');
    if (name) {
      await posterminalAutomat.actions.create({ name });
    }
  };

  handleDelete = async (terminalId) => {
    if (window.confirm('Terminal wirklich löschen?')) {
      await posterminalAutomat.actions.delete(terminalId);
    }
  };

  render() {
    const { terminals, status } = this.state;
    if (status === 'loading') return <div>Lade POS-Terminals...</div>;

    return (
      <div>
        <h3>Registrierte POS-Terminals</h3>
        <ul>
          {terminals.map((t) => (
            <li key={t.id}>
              <strong>{t.name}</strong> (ID: {t.id})
              <button onClick={() => this.handleDelete(t.id)}>Löschen</button>
            </li>
          ))}
        </ul>
        <button onClick={this.handleCreate}>+ Neues Terminal anlegen</button>
      </div>
    );
  }
}

8. Best Practices & Gotchas

  1. Category synchronization: Terminal category assignments are also referenced inside categoryAutomat.actions.patchCategoryPosterminalIds. Both stores stay in sync when configuring POS terminals.
  2. Terminal IDs: Use integer IDs when calling delete, enableCategory, or disableCategory.