import { t } from '@/i18n' import type { BuiltinField, ContentEntry } from '@/lib/contentTypes' import { describeCell, plainCellText } from '@/lib/entryCells' import { ruleMessage, type EntryRule } from '@/lib/entryMessages' import { isBlank, sameValue } from '@/lib/entryValue' import { fieldTypeSpec } from '@/lib/fieldTypes' import { mediaOf, type MediaMap } from '@/lib/mediaField' import { relationSummariesFor, type RelationSummaries } from '@/lib/relationField' export type EntryValues = Record export interface FieldIssue { rule: EntryRule message: string } export type EntryIssues = Record export function initialValues(fields: BuiltinField[], stored: ContentEntry | null): EntryValues { return Object.fromEntries( fields.map((field) => [ field.key, fieldTypeSpec(field.type).editor.initial( field, stored?.data[field.key], stored === null, ), ]), ) } export function initialImages(fields: BuiltinField[], stored: ContentEntry | null): MediaMap { const images: MediaMap = {} for (const field of fields) { const summary = mediaOf(stored, field.key) if (summary && stored?.data[field.key] === summary.id) { images[field.key] = summary } } return images } export function isRelationField(field: BuiltinField) { return fieldTypeSpec(field.type).editor.control === 'relation' } export function initialRelations( fields: BuiltinField[], stored: ContentEntry | null, ): RelationSummaries { return relationSummariesFor(fields, stored, isRelationField) } export function buildData(fields: BuiltinField[], values: EntryValues) { return Object.fromEntries( fields.map((field) => [ field.key, fieldTypeSpec(field.type).editor.payload(field, values[field.key]), ]), ) } export function issueFor(field: BuiltinField, rule: EntryRule): FieldIssue { return { rule, message: ruleMessage(rule, field) } } export function validateEntry( fields: BuiltinField[], values: EntryValues, images: MediaMap = {}, relations: RelationSummaries = {}, ): EntryIssues { const issues: EntryIssues = {} for (const field of fields) { const rule = fieldTypeSpec(field.type).editor.validate(field, values[field.key], { image: images[field.key] ?? null, relations: relations[field.key] ?? [], }) if (rule) { issues[field.key] = issueFor(field, rule) } } return issues } export function firstIssueKey(fields: BuiltinField[], issues: EntryIssues) { return fields.find((field) => issues[field.key])?.key ?? null } export function valuesDiffer(fields: BuiltinField[], current: EntryValues, saved: EntryValues) { return fields.some((field) => !sameValue(current[field.key], saved[field.key])) } export function carriesDefault(field: BuiltinField, value: unknown) { if (isBlank(field.default)) { return false } const initial = fieldTypeSpec(field.type).editor.initial(field, undefined, true) return Object.is(initial, value) } export function entryTitle(fields: BuiltinField[], entry: ContentEntry | null) { const first = fields[0] if (!first || !entry) { return entry ? t('Entry {0}', [entry.id]) : t('New entry') } const cell = describeCell(first, entry.data[first.key]) return cell.kind === 'empty' || cell.kind === 'thumbnail' || cell.kind === 'relation' ? t('Entry {0}', [entry.id]) : plainCellText(cell) }