mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-19 23:16:21 +00:00
eeab548917
Adds the Housekeeping in-client admin panel — a Modtools-adjacent
surface that runs entirely inside the React client, talking to the
emulator over the existing wire instead of a separate REST/CMS layer.
Surface:
- `src/components/housekeeping/` — panel shell + 5 tabs (Dashboard,
Users, Rooms, Economy, Audit). Each tab drives one domain of the
matching emulator handlers (find/sanction/admin/economy/catalog/
hotel-wide).
- `src/api/housekeeping/` — composer/parser orchestration:
`HousekeepingApi.ts` exposes 30+ typed actions, each one running
through `runHkAction()` which awaits the shared
`HousekeepingActionResultEvent` correlated by action key.
- `src/hooks/housekeeping/` — `useHousekeeping` (the public hook),
`useHousekeepingStore` (useBetween singleton: shared selection +
audit polling + sanction templates), `useHousekeepingActions`,
`useHousekeepingConfirm`.
- `src/api/nitro/awaitMessageEvent.ts` — Promise adapter over
`CommunicationManager.subscribeMessage` with a sync `select`
callback that snapshots the parser INSIDE the subscribe handler
before the renderer recycles the parser instance after the
Promise resolves.
- `public/configuration/housekeeping-texts-{en,it}.example` —
149 EN + 149 IT i18n keys under `housekeeping.*` for every panel
string + every server-side error slug the emulator may emit.
Wiring (additive only):
- `src/components/MainView.tsx` — `<HousekeepingView />` mounted
alongside `<ModToolsView />`.
- `src/api/index.ts`, `src/hooks/index.ts`, `src/api/nitro/index.ts`
— added the `housekeeping` and `awaitMessageEvent` re-exports.
Wire contract: pairs against the Arcturus PR (#120 on
duckietm/Arcturus-Morningstar-Extended) and the renderer PR (#77 on
duckietm/Nitro_Render_V3). Incoming events 9100..9129, outgoing
composers 9200..9207. Permission gate `acc_housekeeping` enforced
server-side; the panel is hidden client-side via
`housekeeping.enabled` in the runtime ui-config.
78 lines
1.9 KiB
TypeScript
78 lines
1.9 KiB
TypeScript
const STORAGE_KEY = 'nitro.housekeeping.recent';
|
|
const MAX_ENTRIES = 8;
|
|
|
|
export interface RecentLookupEntry
|
|
{
|
|
kind: 'user' | 'room';
|
|
id: number;
|
|
label: string;
|
|
at: number;
|
|
}
|
|
|
|
const isEntry = (value: unknown): value is RecentLookupEntry =>
|
|
{
|
|
if(!value || typeof value !== 'object') return false;
|
|
|
|
const obj = value as Record<string, unknown>;
|
|
|
|
return (
|
|
(obj.kind === 'user' || obj.kind === 'room') &&
|
|
Number.isFinite(obj.id) &&
|
|
typeof obj.label === 'string' &&
|
|
Number.isFinite(obj.at)
|
|
);
|
|
};
|
|
|
|
const readStore = (): RecentLookupEntry[] =>
|
|
{
|
|
try
|
|
{
|
|
const raw = window.localStorage.getItem(STORAGE_KEY);
|
|
|
|
if(!raw) return [];
|
|
|
|
const parsed = JSON.parse(raw);
|
|
|
|
if(!Array.isArray(parsed)) return [];
|
|
|
|
return parsed.filter(isEntry);
|
|
}
|
|
catch
|
|
{
|
|
return [];
|
|
}
|
|
};
|
|
|
|
const writeStore = (entries: RecentLookupEntry[]): void =>
|
|
{
|
|
try
|
|
{
|
|
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(entries));
|
|
}
|
|
catch
|
|
{}
|
|
};
|
|
|
|
export const loadRecentLookups = (): RecentLookupEntry[] => readStore();
|
|
|
|
/**
|
|
* Push an entry to the front of the recent-lookups stack. Existing
|
|
* entries with the same kind+id are deduped (so reopening the same
|
|
* user doesn't bury fresher entries), and the list is trimmed to
|
|
* MAX_ENTRIES. Pure for the in-memory transform — the persistence is
|
|
* a side effect on top.
|
|
*/
|
|
export const pushRecentLookup = (current: RecentLookupEntry[], entry: RecentLookupEntry): RecentLookupEntry[] =>
|
|
{
|
|
const filtered = current.filter(item => !(item.kind === entry.kind && item.id === entry.id));
|
|
const next = [ entry, ...filtered ].slice(0, MAX_ENTRIES);
|
|
|
|
return next;
|
|
};
|
|
|
|
export const persistRecentLookups = (entries: RecentLookupEntry[]): void => writeStore(entries);
|
|
|
|
export const clearRecentLookups = (): void => writeStore([]);
|
|
|
|
export const RECENT_LOOKUPS_LIMIT = MAX_ENTRIES;
|