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:

  1. Controller — extend Tiger_Controller_Admin_Action. The base sets the admin layout; you keep a thin init() that calls parent::init(). It reads and renders — every mutation is an /api call, never a page-POST.
  2. ACL — admin+, deny-by-default. The controller and its /api service are resources in the module's configs/acl.ini, allowed to admin (or superadmin for platform tools).
  3. 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.
  4. Form — extend Tiger_Form. Declare elements(); the view owns all markup. Validators run at submit and on blur (convenience validation).
  5. 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.

See also