Helpers

Menu builders

buildMenuFromManifest e buildSettingsMenuFromManifests aggregano i manifest dei MFE in strutture pronte per il menu della shell.

API

function buildMenuFromManifest(
  manifest: MFEManifest,
  section?: 'app' | 'settings', // default 'app'
): MenuGroup[]

function buildSettingsMenuFromManifests(
  manifests: MFEManifest[],
): SettingsMenuGroup[]

Aggregano i manifest dei MFE registrati in strutture pronte da renderizzare nel menu della shell, per sezione (app o settings).

Tipi prodotti

interface MenuItem {
  path: string
  label: string
  icon?: string
  order: number
  badge?: string | (() => string | number)
}

interface MenuGroup {
  category: string
  icon?: string
  items: MenuItem[]
}

interface SettingsMenuGroup {
  mfeName: string
  mfeLabel: string
  icon?: string
  groups: MenuGroup[]
}

buildMenuFromManifest(manifest, section)

Costruisce il menu di un singolo MFE per la sezione richiesta.

Comportamento

  1. Filtra le route con meta.hidden === true (escluse)
  2. Filtra per sezione: route con meta.section === section (le route senza meta.section sono trattate come 'app')
  3. Raggruppa per route.category ?? manifest.name
  4. Ordina ogni gruppo per route.meta.order ?? 99 ascendente
  5. Mappa ogni route in un MenuItem

Esempio

import { buildMenuFromManifest } from '@pzeta/mfe-contracts/helpers'

const manifest: MFEManifest = {
  name: 'crm-module',
  version: '1.0.0',
  description: 'CRM',
  icon: 'pi-building',
  routes: [
    { path: '/crm/dashboard', label: 'Dashboard', category: 'CRM', icon: 'pi-chart-line', meta: { order: 1 } },
    { path: '/crm/clienti',   label: 'Clienti',   category: 'CRM', icon: 'pi-users',     meta: { order: 2 } },
    { path: '/crm/templates', label: 'Template',  meta: { section: 'settings', order: 1 } },
    { path: '/crm/admin',     label: 'Admin',     meta: { hidden: true } },
  ],
}

const appMenu = buildMenuFromManifest(manifest, 'app')
/*
[
  {
    category: 'CRM',
    icon: 'pi-building',
    items: [
      { path: '/crm/dashboard', label: 'Dashboard', icon: 'pi-chart-line', order: 1 },
      { path: '/crm/clienti',   label: 'Clienti',   icon: 'pi-users',     order: 2 },
    ],
  },
]
*/

const settingsMenu = buildMenuFromManifest(manifest, 'settings')
/*
[
  {
    category: 'crm-module',
    icon: 'pi-building',
    items: [
      { path: '/crm/templates', label: 'Template', order: 1 },
    ],
  },
]
*/

buildSettingsMenuFromManifests(manifests)

Aggregatore multi-MFE per la sezione settings, pensato per costruire il rail "Impostazioni" della shell.

Differenze rispetto a buildMenuFromManifest(_, 'settings')

  • Aggrega più MFE in un solo passaggio
  • Pre-calcola il path completo: /settings/{manifest.name}{route.path}
  • Espone metadata del MFE proprietario (mfeName, mfeLabel, icon)
  • Esclude MFE senza route settings

Output

interface SettingsMenuGroup {
  mfeName: string      // manifest.name (usato come scope nel path)
  mfeLabel: string     // manifest.description (label leggibile)
  icon?: string        // manifest.icon
  groups: MenuGroup[]  // sotto-gruppi per meta.category
}

Esempio

import { buildSettingsMenuFromManifests } from '@pzeta/mfe-contracts/helpers'

const crmManifest: MFEManifest = {
  name: 'crm-module',
  description: 'CRM',
  icon: 'pi-building',
  /* ... */
  routes: [
    { path: '/templates', label: 'Template email', meta: { section: 'settings', order: 1 } },
    { path: '/tags', label: 'Tag', meta: { section: 'settings', order: 2 } },
  ],
}

const hrManifest: MFEManifest = {
  name: 'hr-module',
  description: 'Risorse Umane',
  icon: 'pi-users',
  /* ... */
  routes: [
    { path: '/contratti', label: 'Tipi contratto', meta: { section: 'settings', order: 1 } },
  ],
}

const settingsMenu = buildSettingsMenuFromManifests([crmManifest, hrManifest])
/*
[
  {
    mfeName: 'crm-module',
    mfeLabel: 'CRM',
    icon: 'pi-building',
    groups: [
      {
        category: 'crm-module',
        icon: 'pi-building',
        items: [
          { path: '/settings/crm-module/templates', label: 'Template email', order: 1 },
          { path: '/settings/crm-module/tags',      label: 'Tag',            order: 2 },
        ],
      },
    ],
  },
  {
    mfeName: 'hr-module',
    mfeLabel: 'Risorse Umane',
    icon: 'pi-users',
    groups: [
      {
        category: 'hr-module',
        icon: 'pi-users',
        items: [
          { path: '/settings/hr-module/contratti', label: 'Tipi contratto', order: 1 },
        ],
      },
    ],
  },
]
*/

Internamente usa buildSettingsFullPath per costruire i path completi, gestendo entrambe le convenzioni: route già prefissate con lo scope e route relative.

Rendering shell tipico

<script setup lang="ts">
import {
  buildMenuFromManifest,
  buildSettingsMenuFromManifests,
} from '@pzeta/mfe-contracts/helpers'

const props = defineProps<{ manifests: MFEManifest[] }>()

const appMenu = computed(() =>
  props.manifests.flatMap((m) => buildMenuFromManifest(m, 'app')),
)

const settingsMenu = computed(() =>
  buildSettingsMenuFromManifests(props.manifests),
)
</script>

<template>
  <!-- Rail principale -->
  <nav class="app-rail">
    <section v-for="group in appMenu" :key="group.category">
      <h3>
        <i :class="group.icon" /> {{ group.category }}
      </h3>
      <RouterLink
        v-for="item in group.items"
        :key="item.path"
        :to="item.path"
      >
        <i :class="item.icon" /> {{ item.label }}
        <span v-if="item.badge">{{ typeof item.badge === 'function' ? item.badge() : item.badge }}</span>
      </RouterLink>
    </section>
  </nav>

  <!-- Rail "Impostazioni" -->
  <nav class="settings-rail">
    <section v-for="mfeGroup in settingsMenu" :key="mfeGroup.mfeName">
      <h2>
        <i :class="mfeGroup.icon" /> {{ mfeGroup.mfeLabel }}
      </h2>
      <div v-for="group in mfeGroup.groups" :key="group.category">
        <RouterLink v-for="item in group.items" :key="item.path" :to="item.path">
          {{ item.label }}
        </RouterLink>
      </div>
    </section>
  </nav>
</template>

Badge dinamici

MenuItem.badge può essere una funzione: chiamala al rendering per ottenere il valore corrente.

<span v-if="item.badge">
  {{ typeof item.badge === 'function' ? item.badge() : item.badge }}
</span>

Vedi MFERoute.meta.badge per come dichiarare i badge nel manifest.

Tipi correlati