Skip to content
Orbix Vue · Bootstrap 5 v1.0.3

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') },
FieldMeaning
pathURL of the page (relative to the base path); Vue Router params work too (/reports/:id)
idPage id: marks the current menu entry (the menu entry with the same id)
titleTranslation key of the document title (… · Orbix)
layoutapp, auth (plus variant) or blank
attrsOptional attributes set on <html> while the page is shown
loadThe 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' }],
    },
  ],
},
KeyMeaning
idUnique navigation id; the page with the same id marks the entry as current and opens its group
toRoute path of the page
keyTranslation key of the label
iconIcon id in the sprite: lc-* (Lucide), tb-* (Tabler), fd-i-* or ox-i-* (Orbix)
badgeOptional counter: { count: 4, tone: 'primary' }

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