mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-19 15:06:20 +00:00
feat(housekeeping): in-client admin panel
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.
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
{
|
||||
"housekeeping.title": "Housekeeping",
|
||||
"housekeeping.mode.light": "Light",
|
||||
|
||||
"housekeeping.tab.dashboard": "Dashboard",
|
||||
"housekeeping.tab.users": "Users",
|
||||
"housekeeping.tab.rooms": "Rooms",
|
||||
"housekeeping.tab.economy": "Economy",
|
||||
"housekeeping.tab.audit": "Audit",
|
||||
|
||||
"housekeeping.confirm.title": "Confirm action",
|
||||
"housekeeping.confirm.proceed": "Proceed",
|
||||
"housekeeping.confirm.cancel": "Cancel",
|
||||
"housekeeping.status.dismiss": "Dismiss",
|
||||
|
||||
"housekeeping.action.pending": "Action pending…",
|
||||
"housekeeping.action.success": "Action completed",
|
||||
"housekeeping.action.error": "Action failed",
|
||||
"housekeeping.error.invalid_input": "Invalid input — check the user id and the value you provided.",
|
||||
"housekeeping.error.user_not_found": "User not found.",
|
||||
"housekeeping.error.user_offline": "User is offline — this action only works on online users.",
|
||||
"housekeeping.error.target_unkickable": "This user cannot be kicked.",
|
||||
"housekeeping.error.ban_failed": "Ban could not be applied — server refused the request.",
|
||||
"housekeeping.error.no_active_ban": "No active ban to clear for this user.",
|
||||
"housekeeping.error.rank_not_found": "Rank not found — pick a rank that exists in permission_ranks.",
|
||||
"housekeeping.error.db_failed": "Database error — see the emulator log for the SQL exception.",
|
||||
"housekeeping.error.hash_failed": "Could not hash the new password — SHA-256 unavailable on this JVM.",
|
||||
"housekeeping.error.room_not_found": "Room not found.",
|
||||
"housekeeping.error.room_action_failed": "Room action could not be applied.",
|
||||
"housekeeping.error.new_owner_not_found": "New owner not found.",
|
||||
"housekeeping.error.economy_failed": "Economy action could not be applied — check the user id and the amount.",
|
||||
"housekeeping.error.alert_empty": "Hotel alert message cannot be empty.",
|
||||
"housekeeping.action.ban_h": "Ban %h%h",
|
||||
"housekeeping.action.mute_min": "Mute %m%m",
|
||||
"housekeeping.action.trade_lock_h": "Trade lock %h%h",
|
||||
"housekeeping.action.kick": "Kick",
|
||||
"housekeeping.action.unban": "Unban",
|
||||
"housekeeping.action.force_disconnect": "Force disconnect",
|
||||
"housekeeping.action.set_rank": "Set rank",
|
||||
"housekeeping.action.reset_password": "Reset password",
|
||||
|
||||
"housekeeping.user.search.placeholder": "Search by username…",
|
||||
"housekeeping.user.search.button": "Search",
|
||||
"housekeeping.user.clear": "Clear selection",
|
||||
"housekeeping.user.none": "No user selected — search above to pick one.",
|
||||
"housekeeping.user.not_found": "User not found.",
|
||||
"housekeeping.user.credits": "Credits",
|
||||
"housekeeping.user.duckets": "Duckets / pixels",
|
||||
"housekeeping.user.diamonds": "Diamonds",
|
||||
"housekeeping.user.audit_hint": "All actions are logged in the audit tab.",
|
||||
"housekeeping.user.live.label": "Live (in current room)",
|
||||
"housekeeping.user.live.kick": "Kick",
|
||||
"housekeeping.user.live.mute_2m": "Mute 2m",
|
||||
"housekeeping.user.live.mute_10m": "Mute 10m",
|
||||
"housekeeping.user.live.ban_h": "Ban 1h",
|
||||
"housekeeping.user.live.ban_d": "Ban 1d",
|
||||
|
||||
"housekeeping.room.search.placeholder": "Room ID…",
|
||||
"housekeeping.room.search.button": "Search",
|
||||
"housekeeping.room.clear": "Clear selection",
|
||||
"housekeeping.room.none": "No room selected — enter an ID above.",
|
||||
"housekeeping.room.not_found": "Room not found.",
|
||||
"housekeeping.room.open": "Open",
|
||||
"housekeeping.room.close": "Close",
|
||||
"housekeeping.room.mute_min": "Mute %m%m",
|
||||
"housekeeping.room.kick_all": "Kick all",
|
||||
"housekeeping.room.kick_all.confirm": "Kick every user currently in the room?",
|
||||
"housekeeping.room.delete": "Delete room",
|
||||
"housekeeping.room.delete.confirm": "Delete this room and all its furniture permanently?",
|
||||
"housekeeping.room.transfer": "Transfer",
|
||||
"housekeeping.room.transfer.label": "Transfer ownership",
|
||||
"housekeeping.room.transfer.new_owner": "New owner ID",
|
||||
|
||||
"housekeeping.economy.select_user": "Pick a user in the Users tab first.",
|
||||
"housekeeping.economy.target": "Target: %username% (#%id%)",
|
||||
"housekeeping.economy.give_credits": "Give credits",
|
||||
"housekeeping.economy.give_duckets": "Give duckets",
|
||||
"housekeeping.economy.give_diamonds": "Give diamonds",
|
||||
"housekeeping.economy.grant_item": "Grant item",
|
||||
"housekeeping.economy.grant_item.label": "Grant catalog item",
|
||||
"housekeeping.economy.item_id": "Item ID",
|
||||
"housekeeping.economy.item_quantity": "Qty",
|
||||
"housekeeping.economy.set_hc_days": "Set HC days",
|
||||
|
||||
"housekeeping.hotel.alert.label": "Hotel-wide alert",
|
||||
"housekeeping.hotel.alert.placeholder": "Message broadcast to every connected user…",
|
||||
"housekeeping.hotel.alert.send": "Send to hotel",
|
||||
"housekeeping.hotel.alert.confirm": "Broadcast %count%-character alert to every connected user?",
|
||||
|
||||
"housekeeping.dashboard.title": "Overview",
|
||||
"housekeeping.dashboard.refresh": "Refresh",
|
||||
"housekeeping.dashboard.loading": "Loading dashboard…",
|
||||
"housekeeping.dashboard.unavailable": "Dashboard unavailable — check the admin endpoint.",
|
||||
"housekeeping.dashboard.online": "Online",
|
||||
"housekeeping.dashboard.total_users": "%count% total",
|
||||
"housekeeping.dashboard.rooms_active": "Active rooms",
|
||||
"housekeeping.dashboard.total_rooms": "%count% total",
|
||||
"housekeeping.dashboard.peak_today": "Peak today",
|
||||
"housekeeping.dashboard.peak_alltime": "All-time peak %count%",
|
||||
"housekeeping.dashboard.pending_tickets": "Tickets",
|
||||
"housekeeping.dashboard.sanctions_24h": "%count% sanctions / 24h",
|
||||
"housekeeping.dashboard.server": "Server",
|
||||
"housekeeping.dashboard.recent_sanctions": "Recent sanctions",
|
||||
"housekeeping.dashboard.recent_lookups": "Recent lookups",
|
||||
|
||||
"housekeeping.audit.title": "Audit log",
|
||||
"housekeeping.audit.refresh": "Refresh",
|
||||
"housekeeping.audit.filter.all": "All",
|
||||
"housekeeping.audit.filter.users": "Users",
|
||||
"housekeeping.audit.filter.rooms": "Rooms",
|
||||
"housekeeping.audit.filter.hotel": "Hotel",
|
||||
"housekeeping.audit.search.placeholder": "Search actor / target / action…",
|
||||
"housekeeping.audit.empty": "No audit entries yet.",
|
||||
"housekeeping.audit.no_match": "No entries match the current filters.",
|
||||
|
||||
"housekeeping.field.reason": "Reason",
|
||||
"housekeeping.field.reason.placeholder": "Free-text reason (optional)",
|
||||
"housekeeping.field.duration": "Duration",
|
||||
"housekeeping.reason.default": "No reason provided.",
|
||||
|
||||
"housekeeping.menu.send_to_hk": "Send to HK",
|
||||
|
||||
"housekeeping.bulk.done": "Bulk done",
|
||||
"housekeeping.bulk.success": "All bulk actions succeeded.",
|
||||
"housekeeping.bulk.partial": "Bulk completed with some failures.",
|
||||
"housekeeping.bulk.failed": "Every bulk action failed.",
|
||||
"housekeeping.bulk.confirm": "Apply %action% to %count% selected users?",
|
||||
"housekeeping.bulk.label": "%count% selected",
|
||||
"housekeeping.bulk.clear": "Clear selection",
|
||||
"housekeeping.bulk.apply": "Apply to selected",
|
||||
|
||||
"housekeeping.telemetry.title": "Telemetry",
|
||||
"housekeeping.telemetry.empty": "No actions observed yet.",
|
||||
"housekeeping.telemetry.reset": "Reset metrics",
|
||||
|
||||
"housekeeping.live.no_room": "No active room session.",
|
||||
"housekeeping.live.kicked": "Kicked from room.",
|
||||
"housekeeping.live.banned": "Banned from room.",
|
||||
"housekeeping.live.muted": "Muted in room.",
|
||||
|
||||
"housekeeping.validation.empty_username": "Username can't be empty.",
|
||||
"housekeeping.validation.invalid_user_id": "Invalid user ID.",
|
||||
"housekeeping.validation.invalid_room_id": "Invalid room ID.",
|
||||
"housekeeping.validation.invalid_amount": "Invalid amount.",
|
||||
"housekeeping.validation.amount_too_large": "Amount exceeds the safety cap.",
|
||||
"housekeeping.validation.empty_reason": "Reason can't be empty.",
|
||||
"housekeeping.validation.invalid_hours": "Invalid duration in hours.",
|
||||
"housekeeping.validation.invalid_rank": "Invalid rank — must be between 1 and 12."
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
{
|
||||
"housekeeping.title": "Housekeeping",
|
||||
"housekeeping.mode.light": "Light",
|
||||
|
||||
"housekeeping.tab.dashboard": "Dashboard",
|
||||
"housekeeping.tab.users": "Utenti",
|
||||
"housekeeping.tab.rooms": "Stanze",
|
||||
"housekeeping.tab.economy": "Economia",
|
||||
"housekeeping.tab.audit": "Audit",
|
||||
|
||||
"housekeeping.confirm.title": "Conferma azione",
|
||||
"housekeeping.confirm.proceed": "Procedi",
|
||||
"housekeeping.confirm.cancel": "Annulla",
|
||||
"housekeeping.status.dismiss": "Chiudi",
|
||||
|
||||
"housekeeping.action.pending": "Azione in corso…",
|
||||
"housekeeping.action.success": "Azione completata",
|
||||
"housekeeping.action.error": "Azione fallita",
|
||||
"housekeeping.error.invalid_input": "Input non valido — controlla l'id utente e il valore inserito.",
|
||||
"housekeeping.error.user_not_found": "Utente non trovato.",
|
||||
"housekeeping.error.user_offline": "Utente offline — questa azione funziona solo sugli utenti online.",
|
||||
"housekeeping.error.target_unkickable": "Questo utente non può essere espulso.",
|
||||
"housekeeping.error.ban_failed": "Impossibile applicare il ban — il server ha rifiutato la richiesta.",
|
||||
"housekeeping.error.no_active_ban": "Nessun ban attivo da rimuovere per questo utente.",
|
||||
"housekeeping.error.rank_not_found": "Rank non trovato — scegli un rank presente in permission_ranks.",
|
||||
"housekeeping.error.db_failed": "Errore database — controlla il log dell'emulatore per l'eccezione SQL.",
|
||||
"housekeeping.error.hash_failed": "Impossibile generare l'hash della nuova password — SHA-256 non disponibile sulla JVM.",
|
||||
"housekeeping.error.room_not_found": "Stanza non trovata.",
|
||||
"housekeeping.error.room_action_failed": "Impossibile applicare l'azione sulla stanza.",
|
||||
"housekeeping.error.new_owner_not_found": "Nuovo proprietario non trovato.",
|
||||
"housekeeping.error.economy_failed": "Impossibile applicare l'azione economy — controlla id utente e importo.",
|
||||
"housekeeping.error.alert_empty": "Il messaggio di hotel-alert non può essere vuoto.",
|
||||
"housekeeping.action.ban_h": "Ban %h%h",
|
||||
"housekeeping.action.mute_min": "Mute %m%m",
|
||||
"housekeeping.action.trade_lock_h": "Blocco scambi %h%h",
|
||||
"housekeeping.action.kick": "Kick",
|
||||
"housekeeping.action.unban": "Sblocca",
|
||||
"housekeeping.action.force_disconnect": "Forza disconnessione",
|
||||
"housekeeping.action.set_rank": "Imposta rank",
|
||||
"housekeeping.action.reset_password": "Reset password",
|
||||
|
||||
"housekeeping.user.search.placeholder": "Cerca per nome…",
|
||||
"housekeeping.user.search.button": "Cerca",
|
||||
"housekeeping.user.clear": "Deseleziona",
|
||||
"housekeeping.user.none": "Nessun utente selezionato — cerca uno qui sopra.",
|
||||
"housekeeping.user.not_found": "Utente non trovato.",
|
||||
"housekeeping.user.credits": "Crediti",
|
||||
"housekeeping.user.duckets": "Pixel / duckets",
|
||||
"housekeeping.user.diamonds": "Diamanti",
|
||||
"housekeeping.user.audit_hint": "Ogni azione viene registrata nel tab Audit.",
|
||||
"housekeeping.user.live.label": "Live (stanza corrente)",
|
||||
"housekeeping.user.live.kick": "Kick",
|
||||
"housekeeping.user.live.mute_2m": "Mute 2m",
|
||||
"housekeeping.user.live.mute_10m": "Mute 10m",
|
||||
"housekeeping.user.live.ban_h": "Ban 1h",
|
||||
"housekeeping.user.live.ban_d": "Ban 1g",
|
||||
|
||||
"housekeeping.room.search.placeholder": "ID stanza…",
|
||||
"housekeeping.room.search.button": "Cerca",
|
||||
"housekeeping.room.clear": "Deseleziona",
|
||||
"housekeeping.room.none": "Nessuna stanza selezionata — inserisci un ID.",
|
||||
"housekeeping.room.not_found": "Stanza non trovata.",
|
||||
"housekeeping.room.open": "Apri",
|
||||
"housekeeping.room.close": "Chiudi",
|
||||
"housekeeping.room.mute_min": "Mute %m%m",
|
||||
"housekeeping.room.kick_all": "Kick tutti",
|
||||
"housekeeping.room.kick_all.confirm": "Cacciare ogni utente attualmente nella stanza?",
|
||||
"housekeeping.room.delete": "Elimina stanza",
|
||||
"housekeeping.room.delete.confirm": "Eliminare la stanza e tutti i suoi mobili in modo permanente?",
|
||||
"housekeeping.room.transfer": "Trasferisci",
|
||||
"housekeeping.room.transfer.label": "Trasferisci proprietà",
|
||||
"housekeeping.room.transfer.new_owner": "ID nuovo owner",
|
||||
|
||||
"housekeeping.economy.select_user": "Seleziona prima un utente nel tab Utenti.",
|
||||
"housekeeping.economy.target": "Target: %username% (#%id%)",
|
||||
"housekeeping.economy.give_credits": "Dai crediti",
|
||||
"housekeeping.economy.give_duckets": "Dai duckets",
|
||||
"housekeeping.economy.give_diamonds": "Dai diamanti",
|
||||
"housekeeping.economy.grant_item": "Assegna",
|
||||
"housekeeping.economy.grant_item.label": "Assegna oggetto del catalogo",
|
||||
"housekeeping.economy.item_id": "ID oggetto",
|
||||
"housekeeping.economy.item_quantity": "Q.tà",
|
||||
"housekeeping.economy.set_hc_days": "Imposta giorni HC",
|
||||
|
||||
"housekeeping.hotel.alert.label": "Avviso a tutto l'hotel",
|
||||
"housekeeping.hotel.alert.placeholder": "Messaggio mandato a ogni utente connesso…",
|
||||
"housekeeping.hotel.alert.send": "Invia a tutti",
|
||||
"housekeeping.hotel.alert.confirm": "Trasmettere un avviso di %count% caratteri a ogni utente connesso?",
|
||||
|
||||
"housekeeping.dashboard.title": "Panoramica",
|
||||
"housekeeping.dashboard.refresh": "Aggiorna",
|
||||
"housekeeping.dashboard.loading": "Caricamento dashboard…",
|
||||
"housekeeping.dashboard.unavailable": "Dashboard non disponibile — verifica l'endpoint admin.",
|
||||
"housekeeping.dashboard.online": "Online",
|
||||
"housekeeping.dashboard.total_users": "%count% totali",
|
||||
"housekeeping.dashboard.rooms_active": "Stanze attive",
|
||||
"housekeeping.dashboard.total_rooms": "%count% totali",
|
||||
"housekeeping.dashboard.peak_today": "Picco oggi",
|
||||
"housekeeping.dashboard.peak_alltime": "Picco assoluto %count%",
|
||||
"housekeeping.dashboard.pending_tickets": "Ticket",
|
||||
"housekeeping.dashboard.sanctions_24h": "%count% sanzioni / 24h",
|
||||
"housekeeping.dashboard.server": "Server",
|
||||
"housekeeping.dashboard.recent_sanctions": "Sanzioni recenti",
|
||||
"housekeeping.dashboard.recent_lookups": "Ricerche recenti",
|
||||
|
||||
"housekeeping.audit.title": "Audit log",
|
||||
"housekeeping.audit.refresh": "Aggiorna",
|
||||
"housekeeping.audit.filter.all": "Tutto",
|
||||
"housekeeping.audit.filter.users": "Utenti",
|
||||
"housekeeping.audit.filter.rooms": "Stanze",
|
||||
"housekeeping.audit.filter.hotel": "Hotel",
|
||||
"housekeeping.audit.search.placeholder": "Cerca attore / target / azione…",
|
||||
"housekeeping.audit.empty": "Nessuna voce di audit.",
|
||||
"housekeeping.audit.no_match": "Nessuna voce corrisponde ai filtri.",
|
||||
|
||||
"housekeeping.field.reason": "Motivo",
|
||||
"housekeeping.field.reason.placeholder": "Motivo libero (opzionale)",
|
||||
"housekeeping.field.duration": "Durata",
|
||||
"housekeeping.reason.default": "Nessun motivo fornito.",
|
||||
|
||||
"housekeeping.menu.send_to_hk": "Manda all'HK",
|
||||
|
||||
"housekeeping.bulk.done": "Bulk completato",
|
||||
"housekeeping.bulk.success": "Tutte le azioni bulk sono riuscite.",
|
||||
"housekeeping.bulk.partial": "Bulk terminato con alcuni errori.",
|
||||
"housekeeping.bulk.failed": "Ogni azione bulk è fallita.",
|
||||
"housekeeping.bulk.confirm": "Applicare %action% a %count% utenti selezionati?",
|
||||
"housekeeping.bulk.label": "%count% selezionati",
|
||||
"housekeeping.bulk.clear": "Pulisci selezione",
|
||||
"housekeeping.bulk.apply": "Applica ai selezionati",
|
||||
|
||||
"housekeeping.telemetry.title": "Telemetria",
|
||||
"housekeeping.telemetry.empty": "Nessuna azione ancora osservata.",
|
||||
"housekeeping.telemetry.reset": "Resetta metriche",
|
||||
|
||||
"housekeeping.live.no_room": "Nessuna stanza attiva.",
|
||||
"housekeeping.live.kicked": "Cacciato dalla stanza.",
|
||||
"housekeeping.live.banned": "Bannato dalla stanza.",
|
||||
"housekeeping.live.muted": "Mutato nella stanza.",
|
||||
|
||||
"housekeeping.validation.empty_username": "Il nome utente non può essere vuoto.",
|
||||
"housekeeping.validation.invalid_user_id": "ID utente non valido.",
|
||||
"housekeeping.validation.invalid_room_id": "ID stanza non valido.",
|
||||
"housekeeping.validation.invalid_amount": "Quantità non valida.",
|
||||
"housekeeping.validation.amount_too_large": "Quantità oltre il limite di sicurezza.",
|
||||
"housekeeping.validation.empty_reason": "Il motivo non può essere vuoto.",
|
||||
"housekeeping.validation.invalid_hours": "Durata in ore non valida.",
|
||||
"housekeeping.validation.invalid_rank": "Rank non valido — deve essere tra 1 e 12."
|
||||
}
|
||||
Reference in New Issue
Block a user