/** * This Source Code is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright (c) Infonomic Company Limited */ import { DateTimeFormatter } from '@byline/admin/react' import { type CollectionAdminConfig, type ColumnDefinition, defineAdmin } from '@byline/core' import { ReindexButton } from '@byline/host-tanstack-start/admin-shell/collections/reindex-button' import { SummaryLength } from '~/components/summary-length.js' import { Docs } from './schema.js' /** * Column definitions for the default table-based list view. * * These are passed to the built-in `ListView` component and control which * fields appear as columns, their labels, sort behaviour, and formatters. * * Note: when a custom `listView` component is registered on the * `CollectionAdminConfig`, it receives the raw paginated data directly and * is responsible for its own layout. These column definitions can still * be used in a custom list view, but they are not automatically applied * as they are with the default table-based `ListView`. You can import * them if needed - for example if you wanted to create a toggled grid/table * custom view. */ const listViewColumns: ColumnDefinition[] = [ { fieldName: 'title', label: 'Title', sortable: true, align: 'left', className: 'w-[50%]', }, { fieldName: 'status', label: 'Status', align: 'center', className: 'w-[15%]', }, { fieldName: 'updatedAt', label: 'Last Updated', sortable: true, align: 'right', className: 'w-[35%]', formatter: { component: DateTimeFormatter }, }, ] /** * Columns rendered per row when a collection item appears as the * target of a relation picker. Usually narrower than the list * view — just enough to identify the right item at a glance. */ // const itemViewColumns: ColumnDefinition[] = [] export const DocsAdmin: CollectionAdminConfig = defineAdmin(Docs, { /** * Column definitions for the default table-based list view. * Controls which fields appear as columns, their labels, sort behaviour, and formatters. */ columns: listViewColumns, /** * Column definitions used when this collection appears as the target of a relation * picker modal (opened from a `relation` field widget). Omit to fall back to a * single-line render of `useAsTitle` + `path`. * * Shape matches `ColumnDefinition` so formatters can be reused across list and item view. * * @example * itemView: [{ fieldName: 'title', label: 'Title' }] // or defined above as `itemViewColumns` */ // itemView: [], /** * Custom list-view component that completely replaces the default table-based `ListView` * on the collection index route. Receives a `ListViewComponentProps` object and is * responsible for rendering search, ordering, results, and pagination itself. * * When omitted, the default table-based `ListView` is used with the `columns` defined above. * * @example * listView: DocsListView, */ // listView: undefined, /** * Header action components for the default list view — a reusable slot in * the list header (the Payload `beforeList`/`afterList` analog). `ReindexButton` * rebuilds this collection's search index; it self-gates on the * `collections.docs.reindex` ability, so it only appears for actors who * hold it. Drop this (and the `search` config in schema.ts) if you don't * want search on this collection. See docs/06-search/index.md. */ listActions: [ReindexButton], /** * Group name for organising this collection in the admin sidebar navigation. * * @example * group: 'Documentation', */ // group: undefined, /** * Per-field rendering overrides, keyed by field name. Use to supply custom * UI component slots for a specific field without affecting placement. * Placement is controlled exclusively through the layout primitives below. */ fields: { summary: { components: { HelpText: SummaryLength, }, }, }, /** * Preview URL builder for live preview links. Returns a URL string (relative * or absolute), or `null` to hide the preview affordance. * * `doc.path` is the top-level slug (derived from `useAsPath`), not a field. * Direct relations are auto-populated by the edit view (depth 1, picker * projection) and appear under `doc.fields.?.document`. * * @example * preview: { * url: (doc, { locale }) => { * if (!doc.path) return null * // `category` is a direct relation — auto-populated to depth 1. * const category = doc.fields.category?.document?.path * const prefix = locale && locale !== 'en' ? `/${locale}` : '' * return category * ? `${prefix}/docs/${category}/${doc.path}` * : `${prefix}/docs/${doc.path}` * }, * } */ // preview: undefined, // --------------------------------------------------------------------------- // UI Layout // // Tab, row, and group containers control how fields are grouped and positioned // in the document edit view. Field names must match those defined in the // collection schema. Names for tabSets, rows, and groups must be unique and // must not collide with any schema field name (a startup error is thrown if // they do). // --------------------------------------------------------------------------- /** * Named tab sets. Each entry creates a separate tabbed interface in the edit * view. You can define more than one tab set, though a single set is sufficient * for most collections. Tab sets may only appear in `layout.main`. * * Each tab's `fields` array accepts schema field names, row names, and group names. * An optional `condition` function can show/hide a tab based on live form data. */ tabSets: [ { name: 'tabs', tabs: [ { name: 'details', label: 'Details', fields: ['title', 'summary', 'featureImage'], }, { name: 'content', label: 'Content', fields: ['content'], }, ], }, ], /** * Named horizontal-row layouts. Fields listed inside a row are rendered * side-by-side (flex row) on desktop and stack vertically below the `sm` * breakpoint. Rows are leaf containers — they accept only schema field names. * * Reference a row by its `name` inside a tab's `fields`, a group's `fields`, * or directly in `layout.main` / `layout.sidebar`. */ // rows: [], /** * Named labelled-fieldset clusters. Groups accept schema field names and row * names (not tabSets or nested groups). An optional `label` renders a heading * above the cluster. * * Reference a group by its `name` inside a tab's `fields` or directly in * `layout.main` / `layout.sidebar`. */ // groups: [], /** * Composition of all layout primitives into the form's two render regions. * * - `main` — accepts tabSet names, group names, row names, and schema field names. * - `sidebar` — accepts group names, row names, and schema field names (no tabSets). * * When omitted entirely, the renderer synthesises a default that places every * schema field in `main` in declaration order. */ layout: { main: ['tabs'], sidebar: ['publishedOn'], }, })