Adding a page and a menu item
Add a page
1. Create the component src/pages/reports/sales.vue:
<script setup lang="ts">
import PageHeader from '@/components/shell/PageHeader.vue';
import { useI18n } from '@/composables/useI18n';
import { usePageScript } from '@/composables/usePageScript';
const { t } = useI18n();
usePageScript();
</script>
<template>
<PageHeader title="reports.sales.title" subtitle="reports.sales.subtitle" section="nav.reports" sectionHref="/reports/sales" />
<div class="fd-page">
<section class="fd-card">
<div class="fd-card__body">
<p data-i18n="reports.sales.empty">{{ t('reports.sales.empty') }}</p>
</div>
</section>
</div>
</template>2. Register the route in src/app/pages.ts:
{ path: '/reports/sales', id: 'reports-sales', title: 'reports.sales.title', layout: 'app', load: () => import('@/pages/reports/sales.vue') },| Field | Meaning |
|---|---|
path | URL of the page (relative to the base path); Vue Router params work too (/reports/:id) |
id | Page id: marks the current menu entry (the menu entry with the same id) |
title | Translation key of the document title (… · Orbix) |
layout | app, auth (plus variant) or blank |
attrs | Optional attributes set on <html> while the page is shown |
load | The page component, loaded on demand (its own JavaScript chunk) |
src/app/router.ts builds the Vue Router route table from this list: each page becomes a layout route (the
layout component with the page as its child) and its data goes to the route meta, read by the layouts with
useRoute().
3. Add the texts to every src/locales/<locale>/ folder, for example a new reports.json (see
Languages):
{
"sales": {
"title": "Sales report",
"subtitle": "Revenue by channel and region",
"empty": "No sales in this period yet."
}
}4. Optional page script. Code that works on the rendered markup (third-party widgets, charts, timers) goes in a
function passed to usePageScript(). It runs once the page is mounted; listeners on document/window added with
listen(), timers added with every() and charts are released when the page unmounts:
// src/scripts/reports.ts
import { listen } from '../core/scope';
export function init(): void {
listen(document, 'keydown', (event) => {
if (event.key === 'r' && event.altKey) document.querySelector<HTMLButtonElement>('[data-report-refresh]')?.click();
});
}import { init } from '@/scripts/reports';
// ...
usePageScript(init);For state that belongs to Vue (filters, lists, forms), use regular ref, computed, v-model and composables
inside the component.
Visit http://localhost:5173/reports/sales.
Pages that need real data fetch it from your API in the component (for example in
onMounted, with a composable, or with a data library such as TanStack Query for Vue) and render it with the same markup (v-for,v-if).
Add a menu item
The sidebar, the horizontal menu, the two-column rail and the command palette are all built from src/app/menu.ts.
Sections hold items and groups; groups hold children:
{
id: 'reports',
key: 'nav.reports', // section title (translation key)
icon: 'lc-chart-column',
items: [
{ id: 'reports-sales', to: '/reports/sales', key: 'nav.sales_report', icon: 'lc-chart-line' },
{
id: 'reports-more',
key: 'nav.more_reports',
icon: 'lc-folder-open',
children: [{ id: 'reports-stock', to: '/reports/stock', key: 'nav.stock_report' }],
},
],
},| Key | Meaning |
|---|---|
id | Unique navigation id; the page with the same id marks the entry as current and opens its group |
to | Route path of the page |
key | Translation key of the label |
icon | Icon id in the sprite: lc-* (Lucide), tb-* (Tabler), fd-i-* or ox-i-* (Orbix) |
badge | Optional counter: { count: 4, tone: 'primary' } |
Links
Use <AppLink to="/reports/sales"> (src/components/shell/AppLink.vue) for links between pages: a plain <a>
that navigates through Vue Router (useLink) without adding classes of its own, so the fd-* markup stays
exactly as designed. <RouterLink> works too. From code, use useRouter().push('/reports/sales'). Plain
<a href> links created at runtime (for example inside a data grid cell) are handled by the router as well, so
the app never reloads.
Icons
Icons of the design system (fd-i-*) are a component:
<script setup lang="ts">
import { FdIcon } from '@foundry/ui/vue';
</script>
<template>
<FdIcon name="search" size="sm" />
</template>Any icon of the bundled sets is an SVG <use> reference:
<svg class="fd-icon" aria-hidden="true"><use href="#lc-chart-line" /></svg>Browse the sets on the Icons pages of the template. Only the icons you reference are included in the sprite:
after adding a new lc-* or tb-* icon, run npm run assets (it also runs with npm run dev, npm run build
and npm test).