/** * @file posterminalAutomat.js * @description Manages registered Point-of-Sale (POS) hardware terminals and * their enabled category mappings for the active company. * * Upstream Triggers: * - `authAutomat`: Subscribes to auth state changes — calls `actions.load()` on * `'authed'` with a `selectedCompanyId`, and `actions.reset()` on `'anon'`. * - `companyAutomat`: Reads `companyAutomat.state.current?.id` as fallback when loading. * - UI / Settings: POS terminal actions (`create`, `delete`, `toggleCategory`, `setCategoryEnabled`). */ import { Automat } from 'react-automat'; import { posterminal as posterminalApi } from '../api'; import authAutomat from './authAutomat'; import companyAutomat from './companyAutomat'; /** * @typedef {Object} PosterminalState * @property {Array} terminals * @property {number|string|null} companyId * @property {'idle'|'loading'|'succeeded'|'failed'} status * @property {string|null} error */ /** @type {PosterminalState} */ const initialState = { terminals: [], companyId: null, status: 'idle', error: null, }; let posterminalAutomat; const actions = { /** Load all posterminals for the selected company. */ async load() { const companyId = authAutomat.state.user?.selectedCompanyId ?? companyAutomat.state.current?.id; if (!companyId) return []; posterminalAutomat.setState({ status: 'loading', error: null }); try { const res = await posterminalApi.list(); const terminals = Array.isArray(res.posterminals) ? res.posterminals : []; posterminalAutomat.setState({ terminals, companyId, status: 'succeeded' }); return terminals; } catch (err) { posterminalAutomat.setState({ status: 'failed', error: err?.message ?? String(err) }); throw err; } }, /** * Create a new posterminal. * @param {{ name: string }} param0 */ async create({ name }) { const res = await posterminalApi.create({ name }); const terminal = res.posterminal; posterminalAutomat.setState({ terminals: [...posterminalAutomat.state.terminals, terminal], }); return terminal; }, /** * Delete a posterminal. * @param {number} terminalId */ async delete(terminalId) { await posterminalApi.delete(terminalId); posterminalAutomat.setState({ terminals: posterminalAutomat.state.terminals.filter((t) => t.id !== terminalId), }); }, /** * Enable all categories on a terminal. * @param {{ terminalId: number }} param0 */ async enableAll({ terminalId }) { const res = await posterminalApi.enableAllCategories(terminalId); patchTerminalCategories(terminalId, res.categoryIds); return res; }, /** * Disable all categories on a terminal. * @param {{ terminalId: number }} param0 */ async disableAll({ terminalId }) { const res = await posterminalApi.disableAllCategories(terminalId); patchTerminalCategories(terminalId, res.categoryIds); return res; }, /** * Enable a specific category on a terminal. * @param {{ terminalId: number, categoryId: number }} param0 */ async enableCategory({ terminalId, categoryId }) { const res = await posterminalApi.enableCategory(terminalId, categoryId); patchTerminalCategories(terminalId, res.categoryIds); return res; }, /** * Disable a specific category on a terminal. * @param {{ terminalId: number, categoryId: number }} param0 */ async disableCategory({ terminalId, categoryId }) { const res = await posterminalApi.disableCategory(terminalId, categoryId); patchTerminalCategories(terminalId, res.categoryIds); return res; }, /** Reset to idle state (called on logout / company switch). */ reset() { posterminalAutomat.setState(initialState); }, }; function patchTerminalCategories(terminalId, categoryIds) { posterminalAutomat.setState({ terminals: posterminalAutomat.state.terminals.map((t) => t.id === terminalId ? { ...t, categoryIds } : t, ), }); } /** * Global posterminal state container. * Loads automatically when authAutomat reaches 'authed' with a selectedCompanyId. * * @type {Automat} */ posterminalAutomat = new Automat(initialState, actions, { name: 'posterminal' }); // Subscribe to auth changes 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(); } }); // Selectors export const selectPosterminals = () => posterminalAutomat.state.terminals; export const selectPosterminalStatus = () => posterminalAutomat.state.status; export const selectHasPosterminals = () => posterminalAutomat.state.terminals.length > 0; export default posterminalAutomat;