@webx-ui/module-admin
v0.33.0
Published
The frame a WebX UI admin panel runs in: bootstrap, the shell, the HTTP client and the module registry that pairs with webx-ui/module-admin on the server.
Maintainers
Readme
@webx-ui/module-admin
The frame a WebX UI admin panel runs in: the bootstrap, the shell, the HTTP client and the module registry.
It is the front-end half of the Composer package webx-ui/module-admin,
and the two are paired by the manifest the server publishes — so a section appears in the panel
when both halves have it, and the menu is what the installation actually has rather than a list
written twice.
Install
pnpm add @webx-ui/module-admin @webx-ui/core @webx-ui/tokens vue vue-routerOn the Laravel side, php artisan webx:panel writes the entry file below and wires it into
Vite for you.
The panel
import { createAdmin } from '@webx-ui/module-admin'
import { auth, WxUserMenu } from '@webx-ui/module-auth'
import { pages } from '@webx-ui/module-pages'
createAdmin({
modules: [pages],
plugins: [auth()],
userMenu: WxUserMenu,
}).mount()createAdmin() mounts into #webx-app — the element the server's Blade shell renders — and
reads the manifest address out of the webx-manifest meta tag beside it. Both are overridable
for a panel served some other way.
The name in the head of the sidebar, an arrow on the rail and an entry of the account menu lead
to the site, in a new tab: to the home page in its main language the server names, to the
address in siteLink when the project gives one, and nowhere at all with siteLink: false.
Mounting does not wait for the server. The manifest needs a signed-in session, so a visit that starts at the sign-in screen would otherwise stare at a blank page waiting for a request it is bound to lose.
A module
import type { AdminModule } from '@webx-ui/module-admin'
export const pages: AdminModule = {
id: 'pages', // the same id the server-side module answers to
path: '/pages',
routes: [{ path: '/pages', component: () => import('./PagesScreen.vue') }],
}A module the server reports with no front end installed has nowhere to send anybody, so it stays out of the menu; one installed but not reported is not there at all.
Language
The panel draws itself in the language of whoever is reading it — their choice, stored against them on the server, not a setting of the site. A site published only in Ukrainian can still be maintained by somebody who wants English menus.
import { useI18n, useTranslate } from '@webx-ui/module-admin'
const t = useTranslate('webx-admin') // t('shell.retry')
const i18n = useI18n() // i18n.state.locale, i18n.state.panelLocalesThe words come from the Composer package's lang files, fetched as one dictionary at boot and
merged over the English this package ships in its own code. So a module is translated once,
in the half that also writes the server's validation messages, and a panel with no server
behind it still has labels.
i18n.state.contentLocales is the other list: the languages the site publishes content in.
That is what an editing screen builds its tabs from, and it has nothing to do with the language
of the interface around them.
Dates
One way to say when something happened, for every screen in the panel.
<script setup lang="ts">
import { useDates, WxDate } from '@webx-ui/module-admin'
const dates = useDates() // dates.short(row.updated_at), dates.exact(…), dates.iso(…)
</script>
<template>
<wx-date :value="row.last_login_at" />
</template>| When | What it says |
| ------------ | ----------------------- |
| today | today at 08:10 |
| yesterday | yesterday at 14:03 |
| this year | 16 September at 14:03 |
| earlier | 16 September 2025 |
| never at all | never |
Twenty-four hours, no seconds; WxDate keeps the exact moment in a tip and in <time
datetime>, so a column is read at a glance and an argument about the exact second still has an
answer. The month names and the order of the parts come from Intl in the panel's language,
not the browser's; today is counted in the reader's own day, which is why a server sending UTC
does not turn somebody's morning into yesterday.
Nothing here touches ordering: a sortable column sorts on the value the server sent.
Talking to the backend
import { useAdmin, HttpError } from '@webx-ui/module-admin'
const { http, can, state } = useAdmin()
const body = await http.get<{ data: Page[] }>(`${state.manifest?.apiPath}/pages`)The client speaks this backend's conventions rather than HTTP in general: a session cookie, a
CSRF token fetched when needed and refreshed once if the server says it has gone stale, 422
arriving as errors ready for WxForm, 429 carrying retryAfter.
try {
await http.post('/api/cms/pages', page)
} catch (error) {
if (error instanceof HttpError && error.isValidation) {
formErrors.value = error.errors
}
}A host that refuses PUT
Some shared hosting lets nothing but GET and POST past the web server: the panel opens and
reads, and every save or delete gets a 403 or 405 before PHP is reached. methodOverride sends
PUT, PATCH and DELETE as POST and says what they really are:
createAdmin({
modules: [pages],
plugins: [auth()],
methodOverride: true, // or 'header', or 'field'
}).mount()| Value | On the wire |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| false (default) | Every method as it is |
| true or 'header' | POST with X-HTTP-Method-Override: PUT |
| 'field' | POST with _method: "PUT" in the JSON body, in a form, or in the query for raw bytes — for a firewall that strips the header too |
GET and POST are never touched, and the CSRF token goes with every overridden request as it
did before. Laravel reads both forms on its own; the server needs nothing. The option belongs
to the panel's own client — a project that passes its own http passes methodOverride to
createHttp() instead.
php artisan webx:doctor says whether the site's host is one of them: it sends a PUT and a
DELETE to its own APP_URL and warns when the web server, not Laravel, refuses them.
can('pages.manage') answers from the signed-in session — a super administrator passes
everything, the same rule the server applies.
Exports
| Export | What it is |
| ------------------------- | ---------------------------------------------- |
| createAdmin | Assembles and mounts the panel |
| useAdmin | The context: http, state, nav, can |
| createHttp, HttpError | The client, usable on its own |
| AdminShell, AdminNav | The layout, for a panel that assembles its own |
| useDates, WxDate | When something happened, in words |
Styles come with @webx-ui/core; this package adds a little of its own:
import '@webx-ui/module-admin/style.css'Licence
MIT.
