@webx-ui/module-admin

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.
12,819
Install
npm install @webx-ui/module-admin
Latest Version:0.32.0
Vue:3
License:MIT
Last Updated:Oct 21, 2026
Links: GitHub  ·  npm
Maintainer: ahabzig

@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-router

On 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.panelLocales

The 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.

Related Packages

@webx-ui/module-pages

Pages for the WebX UI admin panel: the tree of a site's pages, their addresses,...

7,462 0
@webx-ui/module-blog

Blog for the WebX UI admin panel: articles made of blocks, rubrics and tags, sch...

6,583 0
@webx-ui/module-services

Services for the WebX UI admin panel: a catalogue of services made of blocks, fl...

4,031 0
@webx-ui/module-menu

Menus for the WebX UI admin panel: named menus, a tree of items, and what each o...

3,844 0
@webx-ui/module-faq

FAQ for the WebX UI admin panel: questions and answers with flat categories, ord...

3,160 0

Version History

Version Released License
0.32.0 MIT
0.31.0 MIT
0.30.0 MIT
0.29.0 MIT
0.28.0 MIT