u
This commit is contained in:
129
src/automats/propertyAutomat.js
Normal file
129
src/automats/propertyAutomat.js
Normal file
@@ -0,0 +1,129 @@
|
||||
import { Automat } from 'react-automat';
|
||||
import { propertyname as propertynameApi } from '../api';
|
||||
import authAutomat from './authAutomat';
|
||||
|
||||
/**
|
||||
* @typedef {Object} PropertyName
|
||||
* @property {number} id
|
||||
* @property {string} name
|
||||
* @property {number} sort
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} PropertyState
|
||||
* @property {PropertyName[]} properties
|
||||
* @property {'idle'|'loading'|'succeeded'|'failed'} status
|
||||
* @property {string|null} error
|
||||
*/
|
||||
|
||||
/** @type {PropertyState} */
|
||||
const initialState = {
|
||||
properties: [],
|
||||
status: 'idle',
|
||||
error: null,
|
||||
};
|
||||
|
||||
function normalizeProperties(properties) {
|
||||
if (!Array.isArray(properties)) return [];
|
||||
return [...properties]
|
||||
.map((p) => ({
|
||||
...p,
|
||||
exclusive: Boolean(p.exclusive),
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
const sDiff = (a.sort ?? 0) - (b.sort ?? 0);
|
||||
if (sDiff !== 0) return sDiff;
|
||||
return (a.id ?? 0) - (b.id ?? 0);
|
||||
});
|
||||
}
|
||||
|
||||
let propertyAutomat;
|
||||
|
||||
const actions = {
|
||||
/** Load all property names for the selected company. */
|
||||
async load() {
|
||||
propertyAutomat.setState({ status: 'loading', error: null });
|
||||
try {
|
||||
const res = await propertynameApi.list();
|
||||
const properties = normalizeProperties(res.propertynames);
|
||||
propertyAutomat.setState({ properties, status: 'succeeded' });
|
||||
return properties;
|
||||
} catch (err) {
|
||||
propertyAutomat.setState({ status: 'failed', error: err?.message ?? String(err) });
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a property name.
|
||||
* @param {{ name: string, sort?: number, exclusive?: boolean }} body
|
||||
*/
|
||||
async createProperty(body) {
|
||||
const res = await propertynameApi.create(body);
|
||||
await actions.load();
|
||||
return res.propertyname;
|
||||
},
|
||||
|
||||
/**
|
||||
* Update a property name.
|
||||
* @param {number} id
|
||||
* @param {{ name?: string, sort?: number, exclusive?: boolean }} body
|
||||
*/
|
||||
async updateProperty(id, body) {
|
||||
const res = await propertynameApi.update(id, body);
|
||||
await actions.load();
|
||||
return res.propertyname;
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete a property name.
|
||||
* @param {number} id
|
||||
*/
|
||||
async deleteProperty(id) {
|
||||
await propertynameApi.delete(id);
|
||||
await actions.load();
|
||||
},
|
||||
|
||||
/**
|
||||
* Reorder property names.
|
||||
* @param {number[]} propertynameIds
|
||||
*/
|
||||
async reorderProperties(propertynameIds) {
|
||||
await propertynameApi.order(propertynameIds);
|
||||
await actions.load();
|
||||
},
|
||||
|
||||
/** Reset to idle state (called on logout / company switch). */
|
||||
reset() {
|
||||
propertyAutomat.setState(initialState);
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Global property state container.
|
||||
* Loads automatically when authAutomat reaches 'authed' with a selectedCompanyId.
|
||||
*
|
||||
* @type {Automat<PropertyState>}
|
||||
*/
|
||||
propertyAutomat = new Automat(initialState, actions, { name: 'property' });
|
||||
|
||||
// Selectors
|
||||
export function selectProperties() {
|
||||
return propertyAutomat.state.properties;
|
||||
}
|
||||
|
||||
export function selectPropertyStatus() {
|
||||
return propertyAutomat.state.status;
|
||||
}
|
||||
|
||||
// Subscribe to auth changes
|
||||
authAutomat.subscribe((auth) => {
|
||||
const companyId = auth.user?.selectedCompanyId;
|
||||
if (auth.status === 'authed' && companyId) {
|
||||
void propertyAutomat.actions.load();
|
||||
} else if (auth.status === 'anon') {
|
||||
propertyAutomat.actions.reset();
|
||||
}
|
||||
});
|
||||
|
||||
export default propertyAutomat;
|
||||
Reference in New Issue
Block a user