Documentation

Entity manifest example

Declare a list/detail entity with lazy facet loaders and detail pages.

Use manifests to keep entity-level routing and facet loading explicit. Lazy facet loaders let large backoffice entities split list, picker, layout, and detail-page code into separate chunks.

import {
  createListDetailManifest,
  type BackofficeDetailPageManifestItem,
  type BackofficeListDetailFacetLoaderMap,
} from '@plumile/backoffice-core';

export const projectDetailPages = [
  {
    id: 'overview',
    pathSegment: 'overview',
    label: (t) => t('project.detail.overview'),
    isDefault: true,
  },
  {
    id: 'activity',
    pathSegment: 'activity',
    label: (t) => t('project.detail.activity'),
  },
] as const satisfies readonly BackofficeDetailPageManifestItem[];

export const projectFacetLoaders: BackofficeListDetailFacetLoaderMap = {
  list: async () => {
    const module = await import('./facets/list.js');
    return module.loadedProjectListFacetModule;
  },
  picker: async () => {
    const module = await import('./facets/picker.js');
    return module.loadedProjectPickerFacetModule;
  },
  detailLayout: async () => {
    const module = await import('./facets/detail-layout.js');
    return module.loadedProjectDetailLayoutFacetModule;
  },
  detailPage: async (pageId) => {
    if (pageId === 'overview') {
      const module = await import('./facets/detail-pages/overview.js');
      return module.loadedProjectOverviewDetailPageFacetModule;
    }
    throw new Error(`Unknown project detail page: ${pageId}`);
  },
};

export const projectManifest = createListDetailManifest({
  id: 'projects',
  label: (t) => t('project.entity.plural'),
  detailPages: projectDetailPages,
  facets: projectFacetLoaders,
});

Prefer one folder per entity when the entity has multiple facets. Keep the manifest small and move list/detail logic into facet modules.