Building an admin screen
Every admin surface in Tiger — core's and every module's — is built the same way, so the whole back office looks and behaves like one product even though modules ship it. Don't invent a new shell; follow the template.
The five pieces
An admin feature is always the same five parts:
- Controller — extend
Tiger_Controller_Admin_Action. The base sets theadminlayout; you keep a thininit()that callsparent::init(). It reads and renders — every mutation is an/apicall, never a page-POST. - ACL — admin+, deny-by-default. The controller and its
/apiservice are resources in the module'sconfigs/acl.ini, allowed toadmin(orsuperadminfor platform tools). - Register it in the nav.
Tiger_Admin_Settings::register([...])adds a page under Settings;Tiger_Admin_Nav::register([...])adds a top-level sidebar item. Both ACL-filtered live. - Form — extend
Tiger_Form. Declareelements(); the view owns all markup. Validators run at submit and on blur (convenience validation). - View — the standard skeleton. Header (title + one-line description left, primary action right) ·
a feedback mount ·
.card(s) for content · a footer script that saves through/api.
The save is always this shape
Never a page-POST. Disable-during-flight via TigerButton.run, POST to /api, feedback via
TigerDOM.notify, inline field errors from res.form:
TigerButton.run(this, function () {
return fetch('/api', { method: 'POST', body: fd }).then(function (r) { return r.json(); });
}).then(function (res) {
if (res.result === 1) { TigerDOM.notify(fb, 'Settings saved.', { type: 'success' }); }
});
The matching /api service validates the form, writes via the model (config tier for settings — the
live-override pattern, no deploy), and returns the standard envelope.
Why a template at all
The admin layout is one theme file; the screens are per-module views. Keeping the screen HTML
consistent and semantic (Bootstrap utilities only, no bespoke CSS) means reskinning the admin
layout — or a tenant swapping a skin — restyles every module's screens at once. Consistency lives in
the convention, not in copied CSS.