@maykonpaulo/maestro-admin 0.1.2 → 0.2.0-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +149 -7
- package/dist/index.js +869 -134
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/MaestroAdmin.tsx","../src/client/MaestroClient.ts","../src/AdminContext.tsx","../src/hooks.ts","../src/components/Sidebar.tsx","../src/components/EntityList.tsx","../src/components/ui.tsx","../src/components/FieldValue.tsx","../src/components/EntityDetail.tsx","../src/components/EntityForm.tsx","../src/components/FieldInput.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { MaestroClient, type MaestroClientConfig } from '../client/MaestroClient.js';\nimport { AdminClientProvider } from '../AdminContext.js';\nimport { useMetadata } from '../hooks.js';\nimport { Sidebar } from './Sidebar.js';\nimport { EntityList } from './EntityList.js';\nimport { EntityDetail } from './EntityDetail.js';\nimport { EntityForm } from './EntityForm.js';\nimport { ErrorBanner, Spinner } from './ui.js';\n\nexport interface MaestroAdminProps {\n /** Base URL of the running maestro-server. Ignored when `client` is given. */\n apiUrl?: string;\n /** Per-request headers (auth). Ignored when `client` is given. */\n headers?: MaestroClientConfig['headers'];\n /** A pre-built client, for full control (custom fetch, SSR, tests). */\n client?: MaestroClient;\n /** Sidebar title. Defaults to \"Maestro Admin\". */\n title?: string;\n}\n\ntype View =\n | { kind: 'list'; entityId: string }\n | { kind: 'detail'; entityId: string; id: string }\n | { kind: 'create'; entityId: string }\n | { kind: 'edit'; entityId: string; id: string };\n\nfunction AdminShell({ title }: { title: string }) {\n const { data, error, loading } = useMetadata();\n const [view, setView] = useState<View | undefined>(undefined);\n\n const entities = data?.entities ?? [];\n const activeId = view?.entityId ?? entities[0]?.id;\n const activeEntity = entities.find((e) => e.id === activeId);\n const effectiveView: View | undefined = view ?? (activeId ? { kind: 'list', entityId: activeId } : undefined);\n\n if (loading && !data) {\n return (\n <div className=\"flex h-full items-center justify-center\">\n <Spinner label=\"Loading metadata…\" />\n </div>\n );\n }\n\n return (\n <div className=\"flex h-full min-h-screen bg-white text-slate-800\">\n <Sidebar\n entities={entities}\n activeId={activeId}\n title={title}\n onSelect={(entityId) => setView({ kind: 'list', entityId })}\n />\n <main className=\"flex-1 overflow-y-auto p-6\">\n {error && <ErrorBanner error={error} />}\n {activeEntity && effectiveView && effectiveView.kind === 'list' && (\n <EntityList\n entity={activeEntity}\n onOpen={(id) => setView({ kind: 'detail', entityId: activeEntity.id, id })}\n onCreate={() => setView({ kind: 'create', entityId: activeEntity.id })}\n onEdit={(id) => setView({ kind: 'edit', entityId: activeEntity.id, id })}\n />\n )}\n {activeEntity && effectiveView?.kind === 'detail' && (\n <EntityDetail\n entity={activeEntity}\n id={effectiveView.id}\n onBack={() => setView({ kind: 'list', entityId: activeEntity.id })}\n onEdit={() => setView({ kind: 'edit', entityId: activeEntity.id, id: effectiveView.id })}\n />\n )}\n {activeEntity && (effectiveView?.kind === 'create' || effectiveView?.kind === 'edit') && (\n <EntityForm\n entity={activeEntity}\n mode={effectiveView.kind}\n id={effectiveView.kind === 'edit' ? effectiveView.id : undefined}\n onCancel={() => setView({ kind: 'list', entityId: activeEntity.id })}\n onSaved={() => setView({ kind: 'list', entityId: activeEntity.id })}\n />\n )}\n </main>\n </div>\n );\n}\n\n/**\n * The whole metadata-driven admin in one component. Point it at a running maestro-server and it reads\n * `GET /metadata` and auto-builds navigation, tables, detail views and create/update forms for every\n * entity — respecting each entity's capabilities and the server's RBAC. No per-entity code.\n */\nexport function MaestroAdmin(props: MaestroAdminProps) {\n const client = useMemo(\n () => props.client ?? new MaestroClient({ apiUrl: props.apiUrl ?? '', headers: props.headers }),\n [props.client, props.apiUrl, props.headers],\n );\n\n return (\n <AdminClientProvider value={client}>\n <AdminShell title={props.title ?? 'Maestro Admin'} />\n </AdminClientProvider>\n );\n}\n","import type { EntityMetadata, ListQuery, ListResult, MaestroMetadata, Record_ } from './types.js';\n\nexport interface MaestroClientConfig {\n /** Base URL of the running maestro-server, e.g. `http://localhost:3000` or `/api/admin`. */\n apiUrl: string;\n /** Extra headers sent on every request — the place to inject an auth token (`Authorization`, `X-Role`, …). */\n headers?: Record<string, string> | (() => Record<string, string>);\n /** Custom fetch (SSR, tests). Defaults to the global `fetch`. */\n fetch?: typeof fetch;\n}\n\n/** Thrown for any non-2xx response, carrying the HTTP status and the parsed error body when present. */\nexport class MaestroApiError extends Error {\n constructor(\n readonly status: number,\n readonly code: string,\n message: string,\n ) {\n super(message);\n this.name = 'MaestroApiError';\n }\n}\n\nfunction buildListParams(query: ListQuery = {}): string {\n const params = new URLSearchParams();\n if (query.page !== undefined) params.set('page', String(query.page));\n if (query.pageSize !== undefined) params.set('pageSize', String(query.pageSize));\n if (query.search) {\n params.set('search', query.search);\n if (query.searchFields?.length) params.set('searchFields', query.searchFields.join(','));\n }\n for (const s of query.sort ?? []) params.append('sort', `${s.field}:${s.direction}`);\n const qs = params.toString();\n return qs ? `?${qs}` : '';\n}\n\n/**\n * A thin, framework-free client for the Maestro HTTP contract. Every admin component talks to the server\n * through this — it is the single seam where auth headers, base URL and error handling live.\n */\nexport class MaestroClient {\n private readonly base: string;\n private readonly doFetch: typeof fetch;\n\n constructor(private readonly config: MaestroClientConfig) {\n this.base = config.apiUrl.replace(/\\/$/, '');\n this.doFetch = config.fetch ?? globalThis.fetch.bind(globalThis);\n }\n\n private resolveHeaders(): Record<string, string> {\n const h = typeof this.config.headers === 'function' ? this.config.headers() : this.config.headers;\n return { ...h };\n }\n\n private async request<T>(path: string, init?: RequestInit): Promise<T> {\n const res = await this.doFetch(`${this.base}${path}`, {\n ...init,\n headers: {\n Accept: 'application/json',\n ...(init?.body ? { 'Content-Type': 'application/json' } : {}),\n ...this.resolveHeaders(),\n ...(init?.headers as Record<string, string> | undefined),\n },\n });\n\n if (res.status === 204) return undefined as T;\n\n const text = await res.text();\n const body = text ? (JSON.parse(text) as unknown) : undefined;\n\n if (!res.ok) {\n const err = body as { error?: string; message?: string } | undefined;\n throw new MaestroApiError(res.status, err?.error ?? 'ERROR', err?.message ?? res.statusText);\n }\n return body as T;\n }\n\n async getMetadata(): Promise<MaestroMetadata> {\n return this.request<MaestroMetadata>('/metadata');\n }\n\n async getEntityMetadata(entityId: string): Promise<EntityMetadata> {\n return this.request<EntityMetadata>(`/metadata/${encodeURIComponent(entityId)}`);\n }\n\n async list(entityId: string, query?: ListQuery): Promise<ListResult> {\n return this.request<ListResult>(`/entities/${encodeURIComponent(entityId)}${buildListParams(query)}`);\n }\n\n async findById(entityId: string, id: string): Promise<Record_> {\n return this.request<Record_>(`/entities/${encodeURIComponent(entityId)}/${encodeURIComponent(id)}`);\n }\n\n async create(entityId: string, data: Record_): Promise<Record_> {\n return this.request<Record_>(`/entities/${encodeURIComponent(entityId)}`, {\n method: 'POST',\n body: JSON.stringify(data),\n });\n }\n\n async update(entityId: string, id: string, data: Record_): Promise<Record_> {\n return this.request<Record_>(`/entities/${encodeURIComponent(entityId)}/${encodeURIComponent(id)}`, {\n method: 'PATCH',\n body: JSON.stringify(data),\n });\n }\n\n async remove(entityId: string, id: string): Promise<void> {\n await this.request<void>(`/entities/${encodeURIComponent(entityId)}/${encodeURIComponent(id)}`, {\n method: 'DELETE',\n });\n }\n}\n","import { createContext, useContext } from 'react';\nimport type { MaestroClient } from './client/MaestroClient.js';\n\nconst ClientContext = createContext<MaestroClient | null>(null);\n\nexport const AdminClientProvider = ClientContext.Provider;\n\n/** Returns the `MaestroClient` provided at the root of the admin. Throws if used outside the provider. */\nexport function useClient(): MaestroClient {\n const client = useContext(ClientContext);\n if (!client) {\n throw new Error('useClient must be used within a <MaestroAdmin> (no MaestroClient in context).');\n }\n return client;\n}\n","import { useCallback, useEffect, useState } from 'react';\nimport { useClient } from './AdminContext.js';\nimport type { EntityMetadata, ListQuery, ListResult, MaestroMetadata } from './client/types.js';\n\nexport interface AsyncState<T> {\n data: T | undefined;\n error: Error | undefined;\n loading: boolean;\n reload: () => void;\n}\n\n/** Runs an async producer, tracking loading/error and exposing a manual `reload`. Re-runs when `deps` change. */\nexport function useAsync<T>(producer: () => Promise<T>, deps: readonly unknown[]): AsyncState<T> {\n const [data, setData] = useState<T | undefined>(undefined);\n const [error, setError] = useState<Error | undefined>(undefined);\n const [loading, setLoading] = useState(true);\n const [tick, setTick] = useState(0);\n\n useEffect(() => {\n let cancelled = false;\n setLoading(true);\n setError(undefined);\n producer()\n .then((result) => {\n if (!cancelled) setData(result);\n })\n .catch((err: unknown) => {\n if (!cancelled) setError(err instanceof Error ? err : new Error(String(err)));\n })\n .finally(() => {\n if (!cancelled) setLoading(false);\n });\n return () => {\n cancelled = true;\n };\n }, [...deps, tick]);\n\n const reload = useCallback(() => setTick((t) => t + 1), []);\n return { data, error, loading, reload };\n}\n\n/** Loads the full metadata document (`GET /metadata`). */\nexport function useMetadata(): AsyncState<MaestroMetadata> {\n const client = useClient();\n return useAsync(() => client.getMetadata(), []);\n}\n\n/** Loads a page of records for one entity. */\nexport function useEntityList(entityId: string, query: ListQuery): AsyncState<ListResult> {\n const client = useClient();\n const key = JSON.stringify(query);\n return useAsync(() => client.list(entityId, query), [entityId, key]);\n}\n\n/** Loads a single record by id. */\nexport function useEntityRecord(\n entityId: string,\n id: string | undefined,\n): AsyncState<globalThis.Record<string, unknown>> {\n const client = useClient();\n return useAsync(async () => (id ? client.findById(entityId, id) : {}), [entityId, id]);\n}\n\n/** Fields shown in the list table, ordered, honoring per-field list visibility. */\nexport function listFields(entity: EntityMetadata) {\n return entity.fields\n .filter((f) => f.list.visible && !f.hidden)\n .sort((a, b) => (a.list.order ?? 0) - (b.list.order ?? 0));\n}\n\n/** Fields shown in the detail view, ordered. */\nexport function detailFields(entity: EntityMetadata) {\n return entity.fields\n .filter((f) => f.detail.visible)\n .sort((a, b) => (a.detail.order ?? 0) - (b.detail.order ?? 0));\n}\n\n/** Fields editable in the create/update form, ordered, per mode. */\nexport function formFields(entity: EntityMetadata, mode: 'create' | 'edit') {\n return entity.fields\n .filter((f) => (mode === 'create' ? f.form.creatable : f.form.editable))\n .sort((a, b) => (a.form.order ?? 0) - (b.form.order ?? 0));\n}\n","import type { EntityMetadata } from '../client/types.js';\n\nexport function Sidebar({\n entities,\n activeId,\n onSelect,\n title,\n}: {\n entities: EntityMetadata[];\n activeId: string | undefined;\n onSelect: (entityId: string) => void;\n title: string;\n}) {\n return (\n <aside className=\"flex w-60 shrink-0 flex-col border-r border-slate-200 bg-slate-50\">\n <div className=\"border-b border-slate-200 px-4 py-4\">\n <span className=\"text-sm font-semibold tracking-tight text-slate-900\">{title}</span>\n </div>\n <nav className=\"flex-1 overflow-y-auto p-2\">\n {entities.map((e) => (\n <button\n key={e.id}\n onClick={() => onSelect(e.id)}\n className={`flex w-full items-center justify-between rounded-md px-3 py-2 text-left text-sm transition-colors ${\n e.id === activeId ? 'bg-indigo-100 font-medium text-indigo-700' : 'text-slate-700 hover:bg-slate-100'\n }`}\n >\n <span className=\"truncate\">{e.label.plural}</span>\n </button>\n ))}\n </nav>\n <div className=\"border-t border-slate-200 px-4 py-3 text-xs text-slate-400\">\n {entities.length} entities · Maestro\n </div>\n </aside>\n );\n}\n","import { useMemo, useState } from 'react';\nimport type { EntityMetadata, ListQuery, SortInput } from '../client/types.js';\nimport { useClient } from '../AdminContext.js';\nimport { listFields, useEntityList } from '../hooks.js';\nimport { FieldValue } from './FieldValue.js';\nimport { Button, EmptyState, ErrorBanner, Spinner } from './ui.js';\n\nconst PAGE_SIZE = 20;\n\nexport function EntityList({\n entity,\n onOpen,\n onCreate,\n onEdit,\n}: {\n entity: EntityMetadata;\n onOpen: (id: string) => void;\n onCreate: () => void;\n onEdit: (id: string) => void;\n}) {\n const client = useClient();\n const columns = useMemo(() => listFields(entity), [entity]);\n const searchFields = useMemo(() => entity.fields.filter((f) => f.searchable).map((f) => f.name), [entity]);\n\n const [page, setPage] = useState(1);\n const [search, setSearch] = useState('');\n const [sort, setSort] = useState<SortInput | undefined>(undefined);\n\n const query: ListQuery = {\n page,\n pageSize: PAGE_SIZE,\n search: search || undefined,\n searchFields,\n sort: sort ? [sort] : undefined,\n };\n const { data, error, loading, reload } = useEntityList(entity.id, query);\n\n const toggleSort = (fieldName: string): void => {\n setSort((prev) =>\n prev?.field === fieldName\n ? { field: fieldName, direction: prev.direction === 'asc' ? 'desc' : 'asc' }\n : { field: fieldName, direction: 'asc' },\n );\n };\n\n const onDelete = async (id: string): Promise<void> => {\n if (!globalThis.confirm(`Delete this ${entity.label.singular.toLowerCase()}? This cannot be undone.`)) return;\n await client.remove(entity.id, id);\n reload();\n };\n\n const totalPages = data?.totalPages ?? (data ? Math.max(1, Math.ceil(data.total / PAGE_SIZE)) : 1);\n\n return (\n <section className=\"space-y-4\">\n <header className=\"flex flex-wrap items-center justify-between gap-3\">\n <div>\n <h1 className=\"text-lg font-semibold text-slate-900\">{entity.label.plural}</h1>\n {data && <p className=\"text-sm text-slate-500\">{data.total} records</p>}\n </div>\n <div className=\"flex items-center gap-2\">\n {searchFields.length > 0 && (\n <input\n type=\"search\"\n placeholder={`Search ${entity.label.plural.toLowerCase()}…`}\n value={search}\n onChange={(e) => {\n setSearch(e.target.value);\n setPage(1);\n }}\n className=\"rounded-md border border-slate-300 px-3 py-1.5 text-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500\"\n />\n )}\n {entity.capabilities.create && (\n <Button variant=\"primary\" onClick={onCreate}>\n + New\n </Button>\n )}\n </div>\n </header>\n\n {error && <ErrorBanner error={error} />}\n\n {loading && !data ? (\n <Spinner label={`Loading ${entity.label.plural.toLowerCase()}…`} />\n ) : data && data.records.length === 0 ? (\n <EmptyState title=\"No records\" hint={search ? 'Try a different search.' : undefined} />\n ) : (\n <div className=\"overflow-x-auto rounded-lg border border-slate-200\">\n <table className=\"min-w-full divide-y divide-slate-200 text-sm\">\n <thead className=\"bg-slate-50\">\n <tr>\n {columns.map((f) => (\n <th\n key={f.name}\n onClick={() => f.sortable && toggleSort(f.name)}\n className={`px-3 py-2 text-left font-medium text-slate-600 ${f.sortable ? 'cursor-pointer select-none hover:text-slate-900' : ''}`}\n >\n {f.label}\n {sort?.field === f.name && (sort.direction === 'asc' ? ' ▲' : ' ▼')}\n </th>\n ))}\n <th className=\"px-3 py-2\" />\n </tr>\n </thead>\n <tbody className=\"divide-y divide-slate-100 bg-white\">\n {data?.records.map((record, i) => {\n const id = String(record[entity.primaryKey]);\n return (\n <tr key={id || i} className=\"hover:bg-slate-50\">\n {columns.map((f) => (\n <td\n key={f.name}\n className=\"cursor-pointer px-3 py-2 align-top\"\n onClick={() => onOpen(id)}\n >\n <FieldValue field={f} value={record[f.name]} />\n </td>\n ))}\n <td className=\"whitespace-nowrap px-3 py-2 text-right\">\n {entity.capabilities.update && (\n <Button variant=\"ghost\" onClick={() => onEdit(id)}>\n Edit\n </Button>\n )}\n {entity.capabilities.delete && (\n <Button variant=\"ghost\" className=\"text-red-600\" onClick={() => onDelete(id)}>\n Delete\n </Button>\n )}\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n )}\n\n {data && totalPages > 1 && (\n <div className=\"flex items-center justify-end gap-2 text-sm\">\n <Button variant=\"secondary\" disabled={page <= 1} onClick={() => setPage((p) => p - 1)}>\n Previous\n </Button>\n <span className=\"text-slate-500\">\n Page {page} of {totalPages}\n </span>\n <Button variant=\"secondary\" disabled={page >= totalPages} onClick={() => setPage((p) => p + 1)}>\n Next\n </Button>\n </div>\n )}\n </section>\n );\n}\n","import type { ButtonHTMLAttributes, ReactNode } from 'react';\n\ntype Variant = 'primary' | 'secondary' | 'danger' | 'ghost';\n\nconst VARIANTS: Record<Variant, string> = {\n primary: 'bg-indigo-600 text-white hover:bg-indigo-500 disabled:bg-indigo-300',\n secondary: 'bg-white text-slate-700 ring-1 ring-inset ring-slate-300 hover:bg-slate-50',\n danger: 'bg-red-600 text-white hover:bg-red-500 disabled:bg-red-300',\n ghost: 'text-slate-600 hover:bg-slate-100',\n};\n\nexport function Button({\n variant = 'secondary',\n className = '',\n ...props\n}: ButtonHTMLAttributes<HTMLButtonElement> & { variant?: Variant }) {\n return (\n <button\n className={`inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-medium transition-colors disabled:cursor-not-allowed ${VARIANTS[variant]} ${className}`}\n {...props}\n />\n );\n}\n\nexport function Badge({ children, tone = 'slate' }: { children: ReactNode; tone?: 'slate' | 'green' | 'red' | 'amber' | 'indigo' }) {\n const tones: Record<string, string> = {\n slate: 'bg-slate-100 text-slate-700',\n green: 'bg-green-100 text-green-700',\n red: 'bg-red-100 text-red-700',\n amber: 'bg-amber-100 text-amber-700',\n indigo: 'bg-indigo-100 text-indigo-700',\n };\n return (\n <span className={`inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${tones[tone]}`}>\n {children}\n </span>\n );\n}\n\nexport function Spinner({ label }: { label?: string }) {\n return (\n <div className=\"flex items-center gap-2 text-sm text-slate-500\" role=\"status\">\n <span className=\"h-4 w-4 animate-spin rounded-full border-2 border-slate-300 border-t-indigo-600\" />\n {label ?? 'Loading…'}\n </div>\n );\n}\n\nexport function EmptyState({ title, hint }: { title: string; hint?: string }) {\n return (\n <div className=\"rounded-lg border border-dashed border-slate-300 p-8 text-center\">\n <p className=\"text-sm font-medium text-slate-700\">{title}</p>\n {hint && <p className=\"mt-1 text-sm text-slate-500\">{hint}</p>}\n </div>\n );\n}\n\nexport function ErrorBanner({ error }: { error: Error }) {\n return (\n <div className=\"rounded-md border border-red-200 bg-red-50 p-3 text-sm text-red-700\" role=\"alert\">\n {error.message}\n </div>\n );\n}\n","import type { FieldMetadata } from '../client/types.js';\nimport { Badge } from './ui.js';\n\nfunction enumLabel(field: FieldMetadata, value: unknown): string {\n const opt = field.enumOptions?.find((o) => o.value === value);\n return opt ? opt.label : String(value);\n}\n\n/** Renders a single record value read-only, shaped by the field's type (for tables and detail views). */\nexport function FieldValue({ field, value }: { field: FieldMetadata; value: unknown }) {\n if (value === null || value === undefined || value === '') {\n return <span className=\"text-slate-400\">—</span>;\n }\n\n if (field.sensitive) {\n return <span className=\"text-slate-400\">••••••</span>;\n }\n\n switch (field.type) {\n case 'boolean':\n return value ? <Badge tone=\"green\">true</Badge> : <Badge tone=\"slate\">false</Badge>;\n case 'enum':\n return <Badge tone=\"indigo\">{enumLabel(field, value)}</Badge>;\n case 'date':\n case 'datetime': {\n const d = new Date(String(value));\n return <span>{Number.isNaN(d.getTime()) ? String(value) : d.toLocaleString()}</span>;\n }\n case 'json':\n return (\n <code className=\"block max-w-md overflow-x-auto rounded bg-slate-50 px-1.5 py-0.5 text-xs text-slate-700\">\n {JSON.stringify(value)}\n </code>\n );\n default:\n return <span className=\"text-slate-700\">{String(value)}</span>;\n }\n}\n","import type { EntityMetadata } from '../client/types.js';\nimport { detailFields, useEntityRecord } from '../hooks.js';\nimport { FieldValue } from './FieldValue.js';\nimport { Button, ErrorBanner, Spinner } from './ui.js';\n\nexport function EntityDetail({\n entity,\n id,\n onBack,\n onEdit,\n}: {\n entity: EntityMetadata;\n id: string;\n onBack: () => void;\n onEdit: () => void;\n}) {\n const { data, error, loading } = useEntityRecord(entity.id, id);\n const fields = detailFields(entity);\n\n return (\n <section className=\"space-y-4\">\n <header className=\"flex items-center justify-between gap-3\">\n <div>\n <button onClick={onBack} className=\"text-sm text-indigo-600 hover:underline\">\n ← {entity.label.plural}\n </button>\n <h1 className=\"text-lg font-semibold text-slate-900\">\n {entity.label.singular} {data ? String(data[entity.displayField] ?? id) : ''}\n </h1>\n </div>\n {entity.capabilities.update && (\n <Button variant=\"primary\" onClick={onEdit}>\n Edit\n </Button>\n )}\n </header>\n\n {error && <ErrorBanner error={error} />}\n {loading && !data ? (\n <Spinner />\n ) : (\n data && (\n <dl className=\"divide-y divide-slate-100 rounded-lg border border-slate-200 bg-white\">\n {fields.map((f) => (\n <div key={f.name} className=\"grid grid-cols-3 gap-4 px-4 py-3\">\n <dt className=\"text-sm font-medium text-slate-500\">{f.label}</dt>\n <dd className=\"col-span-2 text-sm\">\n <FieldValue field={f} value={data[f.name]} />\n </dd>\n </div>\n ))}\n </dl>\n )\n )}\n </section>\n );\n}\n","import { useEffect, useState } from 'react';\nimport type { EntityMetadata, Record_ } from '../client/types.js';\nimport { useClient } from '../AdminContext.js';\nimport { formFields, useEntityRecord } from '../hooks.js';\nimport { FieldInput } from './FieldInput.js';\nimport { Button, ErrorBanner, Spinner } from './ui.js';\n\nexport function EntityForm({\n entity,\n mode,\n id,\n onCancel,\n onSaved,\n}: {\n entity: EntityMetadata;\n mode: 'create' | 'edit';\n id?: string;\n onCancel: () => void;\n onSaved: (record: Record_) => void;\n}) {\n const client = useClient();\n const fields = formFields(entity, mode);\n const existing = useEntityRecord(entity.id, mode === 'edit' ? id : undefined);\n\n const [values, setValues] = useState<Record_>({});\n const [saving, setSaving] = useState(false);\n const [error, setError] = useState<Error | undefined>(undefined);\n\n useEffect(() => {\n if (mode === 'edit' && existing.data) setValues(existing.data);\n }, [mode, existing.data]);\n\n const submit = async (e: React.FormEvent): Promise<void> => {\n e.preventDefault();\n setSaving(true);\n setError(undefined);\n try {\n const payload: Record_ = {};\n for (const f of fields) {\n if (values[f.name] !== undefined) payload[f.name] = values[f.name];\n }\n const saved =\n mode === 'create'\n ? await client.create(entity.id, payload)\n : await client.update(entity.id, id!, payload);\n onSaved(saved);\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)));\n } finally {\n setSaving(false);\n }\n };\n\n if (mode === 'edit' && existing.loading && !existing.data) {\n return <Spinner />;\n }\n\n return (\n <section className=\"max-w-2xl space-y-4\">\n <header>\n <button onClick={onCancel} className=\"text-sm text-indigo-600 hover:underline\">\n ← {entity.label.plural}\n </button>\n <h1 className=\"text-lg font-semibold text-slate-900\">\n {mode === 'create' ? `New ${entity.label.singular}` : `Edit ${entity.label.singular}`}\n </h1>\n </header>\n\n {error && <ErrorBanner error={error} />}\n\n <form onSubmit={submit} className=\"space-y-4 rounded-lg border border-slate-200 bg-white p-5\">\n {fields.map((f) => (\n <div key={f.name} className=\"space-y-1\">\n <label htmlFor={`field-${f.name}`} className=\"block text-sm font-medium text-slate-700\">\n {f.label}\n {f.required && <span className=\"ml-0.5 text-red-500\">*</span>}\n </label>\n <FieldInput\n field={f}\n value={values[f.name]}\n disabled={saving || f.readonly}\n onChange={(next) => setValues((v) => ({ ...v, [f.name]: next }))}\n />\n {f.form.helpText && <p className=\"text-xs text-slate-500\">{f.form.helpText}</p>}\n </div>\n ))}\n\n <div className=\"flex justify-end gap-2 border-t border-slate-100 pt-4\">\n <Button type=\"button\" variant=\"secondary\" onClick={onCancel} disabled={saving}>\n Cancel\n </Button>\n <Button type=\"submit\" variant=\"primary\" disabled={saving}>\n {saving ? 'Saving…' : mode === 'create' ? 'Create' : 'Save'}\n </Button>\n </div>\n </form>\n </section>\n );\n}\n","import type { FieldMetadata } from '../client/types.js';\n\nconst inputClass =\n 'w-full rounded-md border border-slate-300 px-3 py-1.5 text-sm text-slate-800 shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 disabled:bg-slate-100';\n\n/**\n * Renders the right form control for a field's type and reports its next value. Values are kept as strings\n * for text/enum, numbers for numeric types, booleans for checkboxes, and parsed objects for json — matching\n * what the create/update endpoints expect.\n */\nexport function FieldInput({\n field,\n value,\n onChange,\n disabled,\n}: {\n field: FieldMetadata;\n value: unknown;\n onChange: (next: unknown) => void;\n disabled?: boolean;\n}) {\n const id = `field-${field.name}`;\n\n switch (field.type) {\n case 'boolean':\n return (\n <input\n id={id}\n type=\"checkbox\"\n checked={Boolean(value)}\n disabled={disabled}\n onChange={(e) => onChange(e.target.checked)}\n className=\"h-4 w-4 rounded border-slate-300 text-indigo-600 focus:ring-indigo-500\"\n />\n );\n\n case 'integer':\n case 'number':\n case 'float':\n case 'decimal':\n case 'currency':\n return (\n <input\n id={id}\n type=\"number\"\n value={value === null || value === undefined ? '' : String(value)}\n placeholder={field.form.placeholder}\n disabled={disabled}\n step={field.type === 'integer' ? 1 : 'any'}\n onChange={(e) => onChange(e.target.value === '' ? null : Number(e.target.value))}\n className={inputClass}\n />\n );\n\n case 'enum':\n return (\n <select\n id={id}\n value={value === null || value === undefined ? '' : String(value)}\n disabled={disabled}\n onChange={(e) => onChange(e.target.value)}\n className={inputClass}\n >\n <option value=\"\">—</option>\n {field.enumOptions?.map((o) => (\n <option key={String(o.value)} value={String(o.value)}>\n {o.label}\n </option>\n ))}\n </select>\n );\n\n case 'date':\n return (\n <input\n id={id}\n type=\"date\"\n value={value ? String(value).slice(0, 10) : ''}\n disabled={disabled}\n onChange={(e) => onChange(e.target.value || null)}\n className={inputClass}\n />\n );\n\n case 'text':\n case 'json':\n return (\n <textarea\n id={id}\n rows={field.type === 'json' ? 5 : 3}\n value={\n field.type === 'json' && value !== null && typeof value === 'object'\n ? JSON.stringify(value, null, 2)\n : value === null || value === undefined\n ? ''\n : String(value)\n }\n placeholder={field.form.placeholder}\n disabled={disabled}\n onChange={(e) => {\n if (field.type === 'json') {\n try {\n onChange(e.target.value ? JSON.parse(e.target.value) : null);\n } catch {\n onChange(e.target.value);\n }\n } else {\n onChange(e.target.value);\n }\n }}\n className={`${inputClass} font-mono`}\n />\n );\n\n default:\n return (\n <input\n id={id}\n type={field.type === 'email' ? 'email' : 'text'}\n value={value === null || value === undefined ? '' : String(value)}\n placeholder={field.form.placeholder}\n disabled={disabled}\n onChange={(e) => onChange(e.target.value)}\n className={inputClass}\n />\n );\n }\n}\n"],"mappings":"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","names":["useMemo","useState","useState","jsx","jsxs","jsx","jsx","jsxs","useState","jsx","jsxs","useEffect","useState","jsx","jsxs","jsx","jsxs","useState","useEffect","jsx","jsxs","useState","useMemo"]}
|
|
1
|
+
{"version":3,"sources":["../src/components/MaestroAdmin.tsx","../src/client/MaestroClient.ts","../src/AdminContext.tsx","../src/hooks.ts","../src/components/Sidebar.tsx","../src/grouping.ts","../src/i18n.ts","../src/components/EntityList.tsx","../src/components/ui.tsx","../src/components/RelationValue.tsx","../src/components/FieldValue.tsx","../src/components/menu.tsx","../src/clipboard.ts","../src/components/EntityDetail.tsx","../src/components/EntityForm.tsx","../src/components/FieldInput.tsx","../src/labels.ts","../src/recents.ts"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { MaestroClient, type MaestroClientConfig } from '../client/MaestroClient.js';\nimport { AdminClientProvider, AdminEntitiesProvider } from '../AdminContext.js';\nimport { useMetadata } from '../hooks.js';\nimport { Sidebar } from './Sidebar.js';\nimport { EntityList } from './EntityList.js';\nimport { EntityDetail } from './EntityDetail.js';\nimport { EntityForm } from './EntityForm.js';\nimport { ErrorBanner, Spinner } from './ui.js';\nimport { I18nProvider, stringsFor, useT, type AdminLocale } from '../i18n.js';\nimport { applyLabels, type AdminLabels } from '../labels.js';\nimport { useRecents } from '../recents.js';\n\nexport interface MaestroAdminProps {\n /** Base URL of the running maestro-server. Ignored when `client` is given. */\n apiUrl?: string;\n /** Per-request headers (auth). Ignored when `client` is given. */\n headers?: MaestroClientConfig['headers'];\n /** A pre-built client, for full control (custom fetch, SSR, tests). */\n client?: MaestroClient;\n /** Sidebar title. Defaults to \"Maestro Admin\". */\n title?: string;\n /** UI-chrome language (buttons, menus, pagination). Defaults to `en`. */\n locale?: AdminLocale;\n /** Label rewrites (localization/humanization) applied over the served metadata. */\n labels?: AdminLabels;\n}\n\ntype View =\n | { kind: 'list'; entityId: string }\n | { kind: 'detail'; entityId: string; id: string }\n | { kind: 'create'; entityId: string; cloneFromId?: string }\n | { kind: 'edit'; entityId: string; id: string };\n\nfunction AdminShell({ title, labels }: { title: string; labels?: AdminLabels }) {\n const { data, error, loading } = useMetadata();\n const t = useT();\n const [view, setView] = useState<View | undefined>(undefined);\n const { recents, push: pushRecent } = useRecents();\n\n const metadata = useMemo(() => (data ? applyLabels(data, labels) : undefined), [data, labels]);\n const entities = metadata?.entities ?? [];\n const activeId = view?.entityId ?? entities[0]?.id;\n const activeEntity = entities.find((e) => e.id === activeId);\n const effectiveView: View | undefined = view ?? (activeId ? { kind: 'list', entityId: activeId } : undefined);\n\n const openEntity = (entityId: string): void => {\n pushRecent(entityId);\n setView({ kind: 'list', entityId });\n };\n\n const navigateToRecord = (entityId: string, id: string): void => {\n pushRecent(entityId);\n setView({ kind: 'detail', entityId, id });\n };\n\n if (loading && !data) {\n return (\n <div className=\"flex h-full items-center justify-center\">\n <Spinner label={t.loadingMetadata} />\n </div>\n );\n }\n\n return (\n <AdminEntitiesProvider value={entities}>\n <div className=\"flex h-screen overflow-hidden bg-white text-slate-800\">\n <Sidebar\n entities={entities}\n activeId={activeId}\n title={title}\n labels={labels}\n recents={recents}\n onSelect={openEntity}\n />\n <main className=\"flex-1 overflow-y-auto p-6\">\n {error && <ErrorBanner error={error} />}\n {activeEntity && effectiveView && effectiveView.kind === 'list' && (\n <EntityList\n entity={activeEntity}\n onOpen={(id) => setView({ kind: 'detail', entityId: activeEntity.id, id })}\n onCreate={() => setView({ kind: 'create', entityId: activeEntity.id })}\n onEdit={(id) => setView({ kind: 'edit', entityId: activeEntity.id, id })}\n onClone={(id) => setView({ kind: 'create', entityId: activeEntity.id, cloneFromId: id })}\n onNavigate={navigateToRecord}\n />\n )}\n {activeEntity && effectiveView?.kind === 'detail' && (\n <EntityDetail\n entity={activeEntity}\n id={effectiveView.id}\n onBack={() => setView({ kind: 'list', entityId: activeEntity.id })}\n onEdit={() => setView({ kind: 'edit', entityId: activeEntity.id, id: effectiveView.id })}\n onClone={() =>\n setView({ kind: 'create', entityId: activeEntity.id, cloneFromId: effectiveView.id })\n }\n onDeleted={() => setView({ kind: 'list', entityId: activeEntity.id })}\n onNavigate={navigateToRecord}\n />\n )}\n {activeEntity && (effectiveView?.kind === 'create' || effectiveView?.kind === 'edit') && (\n <EntityForm\n entity={activeEntity}\n mode={effectiveView.kind}\n id={effectiveView.kind === 'edit' ? effectiveView.id : undefined}\n cloneFromId={effectiveView.kind === 'create' ? effectiveView.cloneFromId : undefined}\n onCancel={() => setView({ kind: 'list', entityId: activeEntity.id })}\n onSaved={() => setView({ kind: 'list', entityId: activeEntity.id })}\n />\n )}\n </main>\n </div>\n </AdminEntitiesProvider>\n );\n}\n\n/**\n * The whole metadata-driven admin in one component. Point it at a running maestro-server and it reads\n * `GET /metadata` and auto-builds navigation, tables, detail views and create/update forms for every\n * entity — respecting each entity's capabilities and the server's RBAC. No per-entity code.\n */\nexport function MaestroAdmin(props: MaestroAdminProps) {\n const client = useMemo(\n () => props.client ?? new MaestroClient({ apiUrl: props.apiUrl ?? '', headers: props.headers }),\n [props.client, props.apiUrl, props.headers],\n );\n const strings = useMemo(() => stringsFor(props.locale ?? 'en'), [props.locale]);\n\n return (\n <AdminClientProvider value={client}>\n <I18nProvider value={strings}>\n <AdminShell title={props.title ?? 'Maestro Admin'} labels={props.labels} />\n </I18nProvider>\n </AdminClientProvider>\n );\n}\n","import type { EntityMetadata, ListQuery, ListResult, MaestroMetadata, Record_ } from './types.js';\n\nexport interface MaestroClientConfig {\n /** Base URL of the running maestro-server, e.g. `http://localhost:3000` or `/api/admin`. */\n apiUrl: string;\n /** Extra headers sent on every request — the place to inject an auth token (`Authorization`, `X-Role`, …). */\n headers?: Record<string, string> | (() => Record<string, string>);\n /** Custom fetch (SSR, tests). Defaults to the global `fetch`. */\n fetch?: typeof fetch;\n}\n\n/** Thrown for any non-2xx response, carrying the HTTP status and the parsed error body when present. */\nexport class MaestroApiError extends Error {\n constructor(\n readonly status: number,\n readonly code: string,\n message: string,\n ) {\n super(message);\n this.name = 'MaestroApiError';\n }\n}\n\nfunction buildListParams(query: ListQuery = {}): string {\n const params = new URLSearchParams();\n if (query.page !== undefined) params.set('page', String(query.page));\n if (query.pageSize !== undefined) params.set('pageSize', String(query.pageSize));\n if (query.search) {\n params.set('search', query.search);\n if (query.searchFields?.length) params.set('searchFields', query.searchFields.join(','));\n }\n for (const s of query.sort ?? []) params.append('sort', `${s.field}:${s.direction}`);\n for (const f of query.filters ?? []) {\n params.append('filter', f.value === undefined ? `${f.field}:${f.operator}` : `${f.field}:${f.operator}:${f.value}`);\n }\n const qs = params.toString();\n return qs ? `?${qs}` : '';\n}\n\n/**\n * A thin, framework-free client for the Maestro HTTP contract. Every admin component talks to the server\n * through this — it is the single seam where auth headers, base URL and error handling live.\n */\nexport class MaestroClient {\n private readonly base: string;\n private readonly doFetch: typeof fetch;\n\n constructor(private readonly config: MaestroClientConfig) {\n this.base = config.apiUrl.replace(/\\/$/, '');\n this.doFetch = config.fetch ?? globalThis.fetch.bind(globalThis);\n }\n\n private resolveHeaders(): Record<string, string> {\n const h = typeof this.config.headers === 'function' ? this.config.headers() : this.config.headers;\n return { ...h };\n }\n\n private async request<T>(path: string, init?: RequestInit): Promise<T> {\n const res = await this.doFetch(`${this.base}${path}`, {\n ...init,\n headers: {\n Accept: 'application/json',\n ...(init?.body ? { 'Content-Type': 'application/json' } : {}),\n ...this.resolveHeaders(),\n ...(init?.headers as Record<string, string> | undefined),\n },\n });\n\n if (res.status === 204) return undefined as T;\n\n const text = await res.text();\n const body = text ? (JSON.parse(text) as unknown) : undefined;\n\n if (!res.ok) {\n const err = body as { error?: string; message?: string } | undefined;\n throw new MaestroApiError(res.status, err?.error ?? 'ERROR', err?.message ?? res.statusText);\n }\n return body as T;\n }\n\n async getMetadata(): Promise<MaestroMetadata> {\n return this.request<MaestroMetadata>('/metadata');\n }\n\n async getEntityMetadata(entityId: string): Promise<EntityMetadata> {\n return this.request<EntityMetadata>(`/metadata/${encodeURIComponent(entityId)}`);\n }\n\n async list(entityId: string, query?: ListQuery): Promise<ListResult> {\n return this.request<ListResult>(`/entities/${encodeURIComponent(entityId)}${buildListParams(query)}`);\n }\n\n async findById(entityId: string, id: string): Promise<Record_> {\n return this.request<Record_>(`/entities/${encodeURIComponent(entityId)}/${encodeURIComponent(id)}`);\n }\n\n async create(entityId: string, data: Record_): Promise<Record_> {\n return this.request<Record_>(`/entities/${encodeURIComponent(entityId)}`, {\n method: 'POST',\n body: JSON.stringify(data),\n });\n }\n\n async update(entityId: string, id: string, data: Record_): Promise<Record_> {\n return this.request<Record_>(`/entities/${encodeURIComponent(entityId)}/${encodeURIComponent(id)}`, {\n method: 'PATCH',\n body: JSON.stringify(data),\n });\n }\n\n async remove(entityId: string, id: string): Promise<void> {\n await this.request<void>(`/entities/${encodeURIComponent(entityId)}/${encodeURIComponent(id)}`, {\n method: 'DELETE',\n });\n }\n}\n","import { createContext, useContext } from 'react';\nimport type { MaestroClient } from './client/MaestroClient.js';\nimport type { EntityMetadata } from './client/types.js';\n\nconst ClientContext = createContext<MaestroClient | null>(null);\n\nexport const AdminClientProvider = ClientContext.Provider;\n\n/** Returns the `MaestroClient` provided at the root of the admin. Throws if used outside the provider. */\nexport function useClient(): MaestroClient {\n const client = useContext(ClientContext);\n if (!client) {\n throw new Error('useClient must be used within a <MaestroAdmin> (no MaestroClient in context).');\n }\n return client;\n}\n\nconst EntitiesContext = createContext<EntityMetadata[]>([]);\n\nexport const AdminEntitiesProvider = EntitiesContext.Provider;\n\n/** All entities from the loaded metadata — lets deep components (relation chips) resolve a target entity. */\nexport function useEntities(): EntityMetadata[] {\n return useContext(EntitiesContext);\n}\n\nexport function useEntity(entityId: string | undefined): EntityMetadata | undefined {\n const entities = useEntities();\n return entityId ? entities.find((e) => e.id === entityId) : undefined;\n}\n","import { useCallback, useEffect, useState } from 'react';\nimport { useClient } from './AdminContext.js';\nimport type { EntityMetadata, FieldMetadata, ListQuery, ListResult, MaestroMetadata } from './client/types.js';\n\nexport interface AsyncState<T> {\n data: T | undefined;\n error: Error | undefined;\n loading: boolean;\n reload: () => void;\n}\n\n/** Runs an async producer, tracking loading/error and exposing a manual `reload`. Re-runs when `deps` change. */\nexport function useAsync<T>(producer: () => Promise<T>, deps: readonly unknown[]): AsyncState<T> {\n const [data, setData] = useState<T | undefined>(undefined);\n const [error, setError] = useState<Error | undefined>(undefined);\n const [loading, setLoading] = useState(true);\n const [tick, setTick] = useState(0);\n\n useEffect(() => {\n let cancelled = false;\n setLoading(true);\n setError(undefined);\n producer()\n .then((result) => {\n if (!cancelled) setData(result);\n })\n .catch((err: unknown) => {\n if (!cancelled) setError(err instanceof Error ? err : new Error(String(err)));\n })\n .finally(() => {\n if (!cancelled) setLoading(false);\n });\n return () => {\n cancelled = true;\n };\n }, [...deps, tick]);\n\n const reload = useCallback(() => setTick((t) => t + 1), []);\n return { data, error, loading, reload };\n}\n\n/** Loads the full metadata document (`GET /metadata`). */\nexport function useMetadata(): AsyncState<MaestroMetadata> {\n const client = useClient();\n return useAsync(() => client.getMetadata(), []);\n}\n\n/** Loads a page of records for one entity. */\nexport function useEntityList(entityId: string, query: ListQuery): AsyncState<ListResult> {\n const client = useClient();\n const key = JSON.stringify(query);\n return useAsync(() => client.list(entityId, query), [entityId, key]);\n}\n\n/** Loads a single record by id. */\nexport function useEntityRecord(\n entityId: string,\n id: string | undefined,\n): AsyncState<globalThis.Record<string, unknown>> {\n const client = useClient();\n return useAsync(async () => (id ? client.findById(entityId, id) : {}), [entityId, id]);\n}\n\n/** Fields shown in the list table, ordered, honoring per-field list visibility. */\nexport function listFields(entity: EntityMetadata) {\n return entity.fields\n .filter((f) => f.list.visible && !f.hidden)\n .sort((a, b) => (a.list.order ?? 0) - (b.list.order ?? 0));\n}\n\n// Types that never make a useful table column: unbounded blobs and structures.\nconst LIST_EXCLUDED_TYPES = new Set(['json', 'array', 'text']);\n\n// Lower rank ⇒ more \"principal\": identity texts first, then classification, relations, status,\n// dates, numbers; opaque values (uuid, long urls) last.\nfunction listColumnRank(field: FieldMetadata): number {\n switch (field.type) {\n case 'string':\n case 'email':\n case 'document':\n case 'phone':\n return 1;\n case 'enum':\n return 2;\n case 'relation':\n return 3;\n case 'boolean':\n return 4;\n case 'date':\n case 'datetime':\n case 'time':\n return 5;\n case 'uuid':\n return 7;\n case 'url':\n return 8;\n default:\n return 6;\n }\n}\n\n/**\n * The \"principal\" columns for an entity's table: instead of dumping every field, picks up to `max`\n * of the most identifying ones — the display field first, then searchable/required texts, enums,\n * relations, status booleans and dates — and drops blobs (`json`/`array`/`text`), sensitive values\n * and the primary key. Everything remains visible in the detail view. When any field carries an\n * explicit `list.order`/`list.width` (a curated schema), the curator's selection is honored as-is.\n */\nexport function primaryListFields(entity: EntityMetadata, max = 8): FieldMetadata[] {\n const all = listFields(entity);\n const curated = all.some((f) => f.list.order !== undefined || f.list.width !== undefined);\n if (curated || all.length <= max) return all;\n\n const scored = all\n .filter((f) => !f.sensitive && f.name !== entity.primaryKey && !LIST_EXCLUDED_TYPES.has(f.type))\n .map((field, index) => {\n let score = listColumnRank(field) * 10 + index * 0.01;\n if (field.searchable) score -= 5;\n if (field.required) score -= 2;\n if (field.name === entity.displayField) score = -100;\n return { field, score };\n })\n .sort((a, b) => a.score - b.score);\n\n return scored.slice(0, max).map((s) => s.field);\n}\n\n/** Fields shown in the detail view, ordered. */\nexport function detailFields(entity: EntityMetadata) {\n return entity.fields\n .filter((f) => f.detail.visible)\n .sort((a, b) => (a.detail.order ?? 0) - (b.detail.order ?? 0));\n}\n\n/** Fields editable in the create/update form, ordered, per mode. */\nexport function formFields(entity: EntityMetadata, mode: 'create' | 'edit') {\n return entity.fields\n .filter((f) => (mode === 'create' ? f.form.creatable : f.form.editable))\n .sort((a, b) => (a.form.order ?? 0) - (b.form.order ?? 0));\n}\n","import { useEffect, useMemo, useRef, useState, type ReactNode } from 'react';\nimport type { EntityMetadata } from '../client/types.js';\nimport type { AdminLabels } from '../labels.js';\nimport { buildMenu } from '../grouping.js';\nimport { useT } from '../i18n.js';\n\n/** Lowercases and strips diacritics, so \"usuario\" finds \"Usuários\". */\nfunction normalize(text: string): string {\n return text.toLowerCase().normalize('NFD').replace(/\\p{M}/gu, '');\n}\n\n/**\n * The label with the matched substring highlighted (accent-insensitive, first occurrence). Folding\n * diacritics changes string length (\"á\" → \"a\" + combining mark under NFD), so match positions are\n * found in a folded copy and mapped back to original-label indices character by character.\n */\nfunction Highlight({ label, query }: { label: string; query: string }): ReactNode {\n const foldedQuery = normalize(query);\n if (!foldedQuery) return label;\n\n const folded: string[] = [];\n const originalIndex: number[] = [];\n for (let i = 0; i < label.length; i += 1) {\n for (const ch of normalize(label[i])) {\n folded.push(ch);\n originalIndex.push(i);\n }\n }\n\n const idx = folded.join('').indexOf(foldedQuery);\n if (idx === -1) return label;\n const start = originalIndex[idx];\n const end = (originalIndex[idx + foldedQuery.length - 1] ?? label.length - 1) + 1;\n\n return (\n <>\n {label.slice(0, start)}\n <mark className=\"rounded-sm bg-amber-200 text-inherit\">{label.slice(start, end)}</mark>\n {label.slice(end)}\n </>\n );\n}\n\nfunction EntityButton({\n entity,\n active,\n onSelect,\n subtitle,\n query,\n}: {\n entity: EntityMetadata;\n active: boolean;\n onSelect: (entityId: string) => void;\n subtitle?: string;\n query?: string;\n}) {\n return (\n <button\n onClick={() => onSelect(entity.id)}\n className={`flex w-full flex-col rounded-md px-3 py-1.5 text-left text-sm transition-colors ${\n active ? 'bg-indigo-100 font-medium text-indigo-700' : 'text-slate-700 hover:bg-slate-100'\n }`}\n >\n <span className=\"truncate\">\n {query ? <Highlight label={entity.label.plural} query={query} /> : entity.label.plural}\n </span>\n {subtitle && <span className=\"truncate text-xs font-normal text-slate-400\">{subtitle}</span>}\n </button>\n );\n}\n\nfunction SectionTitle({ children }: { children: ReactNode }) {\n return (\n <div className=\"px-3 pb-1 pt-3 text-xs font-semibold uppercase tracking-wide text-slate-400\">{children}</div>\n );\n}\n\nexport function Sidebar({\n entities,\n activeId,\n onSelect,\n title,\n labels,\n recents = [],\n}: {\n entities: EntityMetadata[];\n activeId: string | undefined;\n onSelect: (entityId: string) => void;\n title: string;\n /** Same object given to `<MaestroAdmin labels>` — supplies folder assignments and folder names. */\n labels?: AdminLabels;\n /** Recently-visited entity ids, most recent first. */\n recents?: string[];\n}) {\n const t = useT();\n const [query, setQuery] = useState('');\n const [expanded, setExpanded] = useState<Record<string, boolean>>({});\n const searchRef = useRef<HTMLInputElement>(null);\n\n const menu = useMemo(() => buildMenu(entities, labels), [entities, labels]);\n const groupOf = useMemo(() => {\n const map = new Map<string, string>();\n for (const group of menu.groups) for (const e of group.entities) map.set(e.id, group.label);\n return map;\n }, [menu]);\n\n const recentEntities = recents\n .map((id) => entities.find((e) => e.id === id))\n .filter((e): e is EntityMetadata => Boolean(e))\n .slice(0, 6);\n\n const results = useMemo(() => {\n if (!query) return [];\n const q = normalize(query);\n return entities\n .filter((e) => normalize(`${e.label.plural} ${e.label.singular} ${e.table}`).includes(q))\n .sort((a, b) => a.label.plural.localeCompare(b.label.plural));\n }, [entities, query]);\n\n // Ctrl+K / Cmd+K focuses the menu search from anywhere.\n useEffect(() => {\n const onKey = (e: KeyboardEvent): void => {\n if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'k') {\n e.preventDefault();\n searchRef.current?.focus();\n }\n };\n document.addEventListener('keydown', onKey);\n return () => document.removeEventListener('keydown', onKey);\n }, []);\n\n const select = (entityId: string): void => {\n setQuery('');\n onSelect(entityId);\n };\n\n const isExpanded = (groupId: string, hasActive: boolean): boolean => expanded[groupId] ?? hasActive;\n\n return (\n <aside className=\"flex w-64 shrink-0 flex-col border-r border-slate-200 bg-slate-50\">\n <div className=\"border-b border-slate-200 px-4 py-4\">\n <span className=\"text-sm font-semibold tracking-tight text-slate-900\">{title}</span>\n </div>\n\n <div className=\"px-2 pt-2\">\n <input\n ref={searchRef}\n type=\"search\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Escape') setQuery('');\n if (e.key === 'Enter' && results[0]) select(results[0].id);\n }}\n placeholder={`${t.searchMenu} (Ctrl+K)`}\n className=\"w-full rounded-md border border-slate-300 bg-white px-3 py-1.5 text-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500\"\n />\n </div>\n\n <nav className=\"flex-1 overflow-y-auto p-2\">\n {query ? (\n results.length === 0 ? (\n <p className=\"px-3 py-2 text-sm text-slate-400\">{t.noMenuResults}</p>\n ) : (\n results.map((e) => (\n <EntityButton\n key={e.id}\n entity={e}\n active={e.id === activeId}\n onSelect={select}\n subtitle={groupOf.get(e.id)}\n query={query}\n />\n ))\n )\n ) : (\n <>\n {recentEntities.length > 0 && (\n <>\n <SectionTitle>{t.recents}</SectionTitle>\n {recentEntities.map((e) => (\n <EntityButton key={e.id} entity={e} active={e.id === activeId} onSelect={select} />\n ))}\n </>\n )}\n\n <SectionTitle>{t.menus}</SectionTitle>\n {menu.groups.map((group) => {\n const hasActive = group.entities.some((e) => e.id === activeId);\n const open = isExpanded(group.id, hasActive);\n return (\n <div key={group.id}>\n <button\n onClick={() => setExpanded((prev) => ({ ...prev, [group.id]: !open }))}\n className=\"flex w-full items-center gap-1.5 rounded-md px-3 py-1.5 text-left text-sm font-medium text-slate-600 hover:bg-slate-100\"\n >\n <span className=\"text-xs text-slate-400\">{open ? '▾' : '▸'}</span>\n <span className=\"truncate\">{group.label}</span>\n <span className=\"ml-auto text-xs text-slate-400\">{group.entities.length}</span>\n </button>\n {open && (\n <div className=\"ml-3 border-l border-slate-200 pl-1\">\n {group.entities.map((e) => (\n <EntityButton key={e.id} entity={e} active={e.id === activeId} onSelect={select} />\n ))}\n </div>\n )}\n </div>\n );\n })}\n {menu.ungrouped.map((e) => (\n <EntityButton key={e.id} entity={e} active={e.id === activeId} onSelect={select} />\n ))}\n </>\n )}\n </nav>\n\n <div className=\"border-t border-slate-200 px-4 py-3 text-xs text-slate-400\">\n {t.entitiesFooter(entities.length)}\n </div>\n </aside>\n );\n}\n","import type { EntityMetadata } from './client/types.js';\nimport type { AdminLabels } from './labels.js';\n\nexport interface MenuGroup {\n /** Stable key: the explicit group name, or the heuristic table-name prefix. */\n id: string;\n label: string;\n entities: EntityMetadata[];\n}\n\nexport interface SidebarMenu {\n groups: MenuGroup[];\n /** Entities that belong to no group — rendered flat after the folders. */\n ungrouped: EntityMetadata[];\n}\n\n// A heuristic cluster needs a meaningful shared prefix (≥ 4 chars keeps `ai*`-style accidents out)\n// and at least two members — a \"folder\" of one is worse than a flat item.\nconst MIN_PREFIX = 4;\n\nfunction commonPrefix(a: string, b: string): string {\n let i = 0;\n while (i < a.length && i < b.length && a[i] === b[i]) i += 1;\n return a.slice(0, i);\n}\n\nfunction humanize(prefix: string): string {\n return prefix.charAt(0).toUpperCase() + prefix.slice(1);\n}\n\n/**\n * Builds the sidebar menu structure: explicit groups first (an entity override's `group` in\n * `AdminLabels` always wins), then heuristic clustering of what's left — collections sharing a\n * table-name prefix (`recoveryactions`, `recoveryscores`, … → \"Recovery\") become a folder, since a\n * shared prefix is how schemaless databases usually encode the module a collection belongs to.\n * Heuristic folder names can be localized via `labels.groups` (prefix → display name). Everything\n * else stays flat. Groups and their entities are sorted by label.\n */\nexport function buildMenu(entities: EntityMetadata[], labels?: AdminLabels): SidebarMenu {\n const byGroup = new Map<string, MenuGroup>();\n const rest: EntityMetadata[] = [];\n\n for (const entity of entities) {\n const override = labels?.entities?.[entity.id] ?? labels?.entities?.[entity.table];\n if (override?.group) {\n const existing = byGroup.get(override.group);\n if (existing) existing.entities.push(entity);\n else byGroup.set(override.group, { id: override.group, label: override.group, entities: [entity] });\n } else {\n rest.push(entity);\n }\n }\n\n // Heuristic pass: walk tables in lexicographic order and greedily grow clusters while the common\n // prefix across every member stays meaningful.\n const sorted = [...rest].sort((a, b) => (a.table < b.table ? -1 : a.table > b.table ? 1 : 0));\n const ungrouped: EntityMetadata[] = [];\n let i = 0;\n while (i < sorted.length) {\n let prefix = i + 1 < sorted.length ? commonPrefix(sorted[i].table, sorted[i + 1].table) : '';\n if (prefix.length < MIN_PREFIX) {\n ungrouped.push(sorted[i]);\n i += 1;\n continue;\n }\n let end = i + 1;\n while (end + 1 < sorted.length) {\n const next = commonPrefix(prefix, sorted[end + 1].table);\n if (next.length < MIN_PREFIX) break;\n prefix = next;\n end += 1;\n }\n const members = sorted.slice(i, end + 1);\n const label = labels?.groups?.[prefix] ?? humanize(prefix);\n const existing = byGroup.get(prefix);\n if (existing) existing.entities.push(...members);\n else byGroup.set(prefix, { id: prefix, label, entities: members });\n i = end + 1;\n }\n\n const collator = (a: string, b: string): number => a.localeCompare(b);\n const groups = [...byGroup.values()].sort((a, b) => collator(a.label, b.label));\n for (const group of groups) {\n group.entities.sort((a, b) => collator(a.label.plural, b.label.plural));\n }\n ungrouped.sort((a, b) => collator(a.label.plural, b.label.plural));\n\n return { groups, ungrouped };\n}\n","import { createContext, useContext } from 'react';\n\nexport type AdminLocale = 'en' | 'pt-BR';\n\n/**\n * Every user-facing string the admin chrome renders. Entity/field labels are NOT here — those come\n * from the server metadata (optionally rewritten via `AdminLabels`); this dictionary covers only the\n * UI around them (buttons, menus, pagination, confirmations).\n */\nexport interface AdminStrings {\n loading: string;\n loadingMetadata: string;\n records: (total: number) => string;\n searchPlaceholder: (plural: string) => string;\n newRecord: string;\n view: string;\n edit: string;\n clone: string;\n remove: string;\n copyId: string;\n copyObject: string;\n copyValue: string;\n copied: string;\n addToFilter: string;\n openRelated: (label: string) => string;\n filters: string;\n clearFilters: string;\n actions: string;\n noRecords: string;\n tryDifferentSearch: string;\n deleteConfirm: (singular: string) => string;\n page: (page: number, total: number) => string;\n previous: string;\n next: string;\n cancel: string;\n save: string;\n saving: string;\n create: string;\n newTitle: (singular: string) => string;\n editTitle: (singular: string) => string;\n cloneTitle: (singular: string) => string;\n entitiesFooter: (count: number) => string;\n yes: string;\n no: string;\n searchMenu: string;\n recents: string;\n menus: string;\n noMenuResults: string;\n}\n\nconst EN: AdminStrings = {\n loading: 'Loading…',\n loadingMetadata: 'Loading metadata…',\n records: (total) => `${total} record${total === 1 ? '' : 's'}`,\n searchPlaceholder: (plural) => `Search ${plural.toLowerCase()}…`,\n newRecord: '+ New',\n view: 'View',\n edit: 'Edit',\n clone: 'Clone',\n remove: 'Remove',\n copyId: 'Copy ID',\n copyObject: 'Copy object',\n copyValue: 'Copy value',\n copied: 'Copied!',\n addToFilter: 'Add to filter',\n openRelated: (label) => `Open ${label}`,\n filters: 'Filters',\n clearFilters: 'Clear filters',\n actions: 'Actions',\n noRecords: 'No records',\n tryDifferentSearch: 'Try a different search.',\n deleteConfirm: (singular) => `Delete this ${singular.toLowerCase()}? This cannot be undone.`,\n page: (page, total) => `Page ${page} of ${total}`,\n previous: 'Previous',\n next: 'Next',\n cancel: 'Cancel',\n save: 'Save',\n saving: 'Saving…',\n create: 'Create',\n newTitle: (singular) => `New ${singular}`,\n editTitle: (singular) => `Edit ${singular}`,\n cloneTitle: (singular) => `Clone ${singular}`,\n entitiesFooter: (count) => `${count} entities · Maestro`,\n yes: 'true',\n no: 'false',\n searchMenu: 'Search menu…',\n recents: 'Recents',\n menus: 'Menus',\n noMenuResults: 'No matches',\n};\n\nconst PT_BR: AdminStrings = {\n loading: 'Carregando…',\n loadingMetadata: 'Carregando metadados…',\n records: (total) => `${total} registro${total === 1 ? '' : 's'}`,\n searchPlaceholder: (plural) => `Buscar ${plural.toLowerCase()}…`,\n newRecord: '+ Novo',\n view: 'Visualizar',\n edit: 'Editar',\n clone: 'Clonar',\n remove: 'Remover',\n copyId: 'Copiar ID',\n copyObject: 'Copiar objeto',\n copyValue: 'Copiar valor',\n copied: 'Copiado!',\n addToFilter: 'Adicionar ao filtro',\n openRelated: (label) => `Abrir ${label}`,\n filters: 'Filtros',\n clearFilters: 'Limpar filtros',\n actions: 'Ações',\n noRecords: 'Nenhum registro',\n tryDifferentSearch: 'Tente uma busca diferente.',\n deleteConfirm: (singular) => `Remover este registro de ${singular}? Essa ação não pode ser desfeita.`,\n page: (page, total) => `Página ${page} de ${total}`,\n previous: 'Anterior',\n next: 'Próxima',\n cancel: 'Cancelar',\n save: 'Salvar',\n saving: 'Salvando…',\n create: 'Criar',\n newTitle: (singular) => `Novo(a) ${singular}`,\n editTitle: (singular) => `Editar ${singular}`,\n cloneTitle: (singular) => `Clonar ${singular}`,\n entitiesFooter: (count) => `${count} entidades · Maestro`,\n yes: 'sim',\n no: 'não',\n searchMenu: 'Buscar no menu…',\n recents: 'Recentes',\n menus: 'Menus',\n noMenuResults: 'Nada encontrado',\n};\n\nconst LOCALES: Record<AdminLocale, AdminStrings> = { en: EN, 'pt-BR': PT_BR };\n\nexport function stringsFor(locale: AdminLocale): AdminStrings {\n return LOCALES[locale] ?? EN;\n}\n\nconst I18nContext = createContext<AdminStrings>(EN);\n\nexport const I18nProvider = I18nContext.Provider;\n\n/** The active UI-string dictionary (set by `<MaestroAdmin locale>`; defaults to English). */\nexport function useT(): AdminStrings {\n return useContext(I18nContext);\n}\n","import { useMemo, useState } from 'react';\nimport type { EntityMetadata, FieldMetadata, FilterInput, ListQuery, Record_, SortInput } from '../client/types.js';\nimport { useClient, useEntities } from '../AdminContext.js';\nimport { primaryListFields, useEntityList } from '../hooks.js';\nimport { FieldValue } from './FieldValue.js';\nimport { Button, EmptyState, ErrorBanner, Spinner } from './ui.js';\nimport { PopMenu, MenuButton, type MenuAnchor, type MenuItem } from './menu.js';\nimport { useT } from '../i18n.js';\nimport { copyText } from '../clipboard.js';\n\nconst PAGE_SIZE = 20;\n\ninterface CellMenuState {\n anchor: MenuAnchor;\n field: FieldMetadata;\n record: Record_;\n}\n\nfunction filterFor(field: FieldMetadata, value: unknown): FilterInput | undefined {\n if (value === null || value === undefined) return { field: field.name, operator: 'isNull' };\n if (field.type === 'boolean') return { field: field.name, operator: value ? 'isTrue' : 'isFalse' };\n if (field.type === 'json' || field.type === 'array') return undefined;\n return { field: field.name, operator: 'equals', value: String(value) };\n}\n\nfunction filterChipText(entity: EntityMetadata, filter: FilterInput): string {\n const label = entity.fields.find((f) => f.name === filter.field)?.label ?? filter.field;\n if (filter.operator === 'isNull') return `${label} = ∅`;\n if (filter.operator === 'isTrue') return `${label} = ✓`;\n if (filter.operator === 'isFalse') return `${label} = ✗`;\n const value = filter.value ?? '';\n return `${label} = ${value.length > 24 ? `${value.slice(0, 24)}…` : value}`;\n}\n\nexport function EntityList({\n entity,\n onOpen,\n onCreate,\n onEdit,\n onClone,\n onNavigate,\n initialFilters,\n}: {\n entity: EntityMetadata;\n onOpen: (id: string) => void;\n onCreate: () => void;\n onEdit: (id: string) => void;\n onClone: (id: string) => void;\n onNavigate: (entityId: string, id: string) => void;\n initialFilters?: FilterInput[];\n}) {\n const client = useClient();\n const t = useT();\n const entities = useEntities();\n const relatedLabel = (entityId: string): string =>\n entities.find((e) => e.id === entityId)?.label.singular ?? entityId;\n const columns = useMemo(() => primaryListFields(entity), [entity]);\n const searchFields = useMemo(() => entity.fields.filter((f) => f.searchable).map((f) => f.name), [entity]);\n\n const [page, setPage] = useState(1);\n const [search, setSearch] = useState('');\n const [sort, setSort] = useState<SortInput | undefined>(undefined);\n const [filters, setFilters] = useState<FilterInput[]>(initialFilters ?? []);\n const [cellMenu, setCellMenu] = useState<CellMenuState | undefined>(undefined);\n const [copied, setCopied] = useState(false);\n\n const query: ListQuery = {\n page,\n pageSize: PAGE_SIZE,\n search: search || undefined,\n searchFields,\n sort: sort ? [sort] : undefined,\n filters: filters.length ? filters : undefined,\n };\n const { data, error, loading, reload } = useEntityList(entity.id, query);\n\n const toggleSort = (fieldName: string): void => {\n setSort((prev) =>\n prev?.field === fieldName\n ? { field: fieldName, direction: prev.direction === 'asc' ? 'desc' : 'asc' }\n : { field: fieldName, direction: 'asc' },\n );\n };\n\n const flashCopied = (): void => {\n setCopied(true);\n setTimeout(() => setCopied(false), 1500);\n };\n\n const copy = async (text: string): Promise<void> => {\n await copyText(text);\n flashCopied();\n };\n\n const addFilter = (filter: FilterInput): void => {\n setFilters((prev) => {\n const others = prev.filter((f) => f.field !== filter.field);\n return [...others, filter];\n });\n setPage(1);\n };\n\n const onDelete = async (id: string): Promise<void> => {\n if (!globalThis.confirm(t.deleteConfirm(entity.label.singular))) return;\n await client.remove(entity.id, id);\n reload();\n };\n\n const rowMenuItems = (record: Record_): MenuItem[] => {\n const id = String(record[entity.primaryKey]);\n const items: MenuItem[] = [{ id: 'view', label: `👁 ${t.view}`, onSelect: () => onOpen(id) }];\n if (entity.capabilities.update) items.push({ id: 'edit', label: `✏️ ${t.edit}`, onSelect: () => onEdit(id) });\n if (entity.capabilities.clone && entity.capabilities.create) {\n items.push({ id: 'clone', label: `⧉ ${t.clone}`, onSelect: () => onClone(id) });\n }\n items.push(\n { id: 'copy-id', label: `⎘ ${t.copyId}`, onSelect: () => void copy(id) },\n { id: 'copy-object', label: `{} ${t.copyObject}`, onSelect: () => void copy(JSON.stringify(record, null, 2)) },\n );\n if (entity.capabilities.delete) {\n items.push({ id: 'remove', label: `🗑 ${t.remove}`, danger: true, onSelect: () => void onDelete(id) });\n }\n return items;\n };\n\n const cellMenuItems = (state: CellMenuState): MenuItem[] => {\n const { field, record } = state;\n const id = String(record[entity.primaryKey]);\n const value = record[field.name];\n const items: MenuItem[] = [];\n\n const filter = filterFor(field, value);\n if (filter) items.push({ id: 'filter', label: `▼ ${t.addToFilter}`, onSelect: () => addFilter(filter) });\n\n items.push({ id: 'view', label: `👁 ${t.view}`, onSelect: () => onOpen(id) });\n if (entity.capabilities.update) items.push({ id: 'edit', label: `✏️ ${t.edit}`, onSelect: () => onEdit(id) });\n\n if (field.type === 'relation' && field.relationEntity && value !== null && value !== undefined) {\n const targetId = field.relationEntity;\n items.push({\n id: 'open-related',\n label: `↗ ${t.openRelated(relatedLabel(targetId))}`,\n onSelect: () => onNavigate(targetId, String(value)),\n });\n }\n\n if (value !== null && value !== undefined) {\n items.push({\n id: 'copy-value',\n label: `⎘ ${t.copyValue}`,\n onSelect: () => void copy(typeof value === 'object' ? JSON.stringify(value) : String(value)),\n });\n }\n return items;\n };\n\n const totalPages = data?.totalPages ?? (data ? Math.max(1, Math.ceil(data.total / PAGE_SIZE)) : 1);\n\n return (\n <section className=\"space-y-4\">\n <header className=\"flex flex-wrap items-center justify-between gap-3\">\n <div>\n <h1 className=\"text-lg font-semibold text-slate-900\">{entity.label.plural}</h1>\n {data && <p className=\"text-sm text-slate-500\">{t.records(data.total)}</p>}\n </div>\n <div className=\"flex items-center gap-2\">\n {searchFields.length > 0 && (\n <input\n type=\"search\"\n placeholder={t.searchPlaceholder(entity.label.plural)}\n value={search}\n onChange={(e) => {\n setSearch(e.target.value);\n setPage(1);\n }}\n className=\"rounded-md border border-slate-300 px-3 py-1.5 text-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500\"\n />\n )}\n {entity.capabilities.create && (\n <Button variant=\"primary\" onClick={onCreate}>\n {t.newRecord}\n </Button>\n )}\n </div>\n </header>\n\n {filters.length > 0 && (\n <div className=\"flex flex-wrap items-center gap-2 text-sm\">\n <span className=\"text-slate-500\">{t.filters}:</span>\n {filters.map((f) => (\n <button\n key={f.field}\n onClick={() => {\n setFilters((prev) => prev.filter((p) => p.field !== f.field));\n setPage(1);\n }}\n title={f.value}\n className=\"inline-flex items-center gap-1 rounded-full bg-indigo-50 px-2.5 py-0.5 text-xs font-medium text-indigo-700 hover:bg-indigo-100\"\n >\n {filterChipText(entity, f)}\n <span aria-hidden>×</span>\n </button>\n ))}\n <button\n onClick={() => {\n setFilters([]);\n setPage(1);\n }}\n className=\"text-xs text-slate-500 underline hover:text-slate-700\"\n >\n {t.clearFilters}\n </button>\n </div>\n )}\n\n {error && <ErrorBanner error={error} />}\n\n {loading && !data ? (\n <Spinner label={t.loading} />\n ) : data && data.records.length === 0 ? (\n <EmptyState title={t.noRecords} hint={search || filters.length ? t.tryDifferentSearch : undefined} />\n ) : (\n <div className=\"overflow-x-auto rounded-lg border border-slate-200\">\n <table className=\"min-w-full divide-y divide-slate-200 text-sm\">\n <thead className=\"bg-slate-50\">\n <tr>\n {columns.map((f) => (\n <th\n key={f.name}\n onClick={() => f.sortable && toggleSort(f.name)}\n className={`px-3 py-2 text-left font-medium text-slate-600 ${f.sortable ? 'cursor-pointer select-none hover:text-slate-900' : ''}`}\n >\n {f.label}\n {sort?.field === f.name && (sort.direction === 'asc' ? ' ▲' : ' ▼')}\n </th>\n ))}\n <th className=\"px-3 py-2 text-right font-medium text-slate-600\">{t.actions}</th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-slate-100 bg-white\">\n {data?.records.map((record, i) => {\n const id = String(record[entity.primaryKey]);\n return (\n <tr key={id || i} className=\"hover:bg-slate-50\">\n {columns.map((f) => (\n <td\n key={f.name}\n className=\"cursor-pointer px-3 py-2 align-top\"\n onClick={(e) =>\n setCellMenu({ anchor: { x: e.clientX, y: e.clientY }, field: f, record })\n }\n onDoubleClick={() => onOpen(id)}\n >\n <FieldValue field={f} value={record[f.name]} compact />\n </td>\n ))}\n <td className=\"whitespace-nowrap px-3 py-2 text-right\">\n <MenuButton label={t.actions} items={rowMenuItems(record)} />\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n )}\n\n {cellMenu && (\n <PopMenu anchor={cellMenu.anchor} items={cellMenuItems(cellMenu)} onClose={() => setCellMenu(undefined)} />\n )}\n\n {copied && (\n <div className=\"fixed bottom-4 right-4 z-50 rounded-md bg-slate-900 px-3 py-1.5 text-sm text-white shadow-lg\">\n {t.copied}\n </div>\n )}\n\n {data && totalPages > 1 && (\n <div className=\"flex items-center justify-end gap-2 text-sm\">\n <Button variant=\"secondary\" disabled={page <= 1} onClick={() => setPage((p) => p - 1)}>\n {t.previous}\n </Button>\n <span className=\"text-slate-500\">{t.page(page, totalPages)}</span>\n <Button variant=\"secondary\" disabled={page >= totalPages} onClick={() => setPage((p) => p + 1)}>\n {t.next}\n </Button>\n </div>\n )}\n </section>\n );\n}\n","import type { ButtonHTMLAttributes, ReactNode } from 'react';\n\ntype Variant = 'primary' | 'secondary' | 'danger' | 'ghost';\n\nconst VARIANTS: Record<Variant, string> = {\n primary: 'bg-indigo-600 text-white hover:bg-indigo-500 disabled:bg-indigo-300',\n secondary: 'bg-white text-slate-700 ring-1 ring-inset ring-slate-300 hover:bg-slate-50',\n danger: 'bg-red-600 text-white hover:bg-red-500 disabled:bg-red-300',\n ghost: 'text-slate-600 hover:bg-slate-100',\n};\n\nexport function Button({\n variant = 'secondary',\n className = '',\n ...props\n}: ButtonHTMLAttributes<HTMLButtonElement> & { variant?: Variant }) {\n return (\n <button\n className={`inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-medium transition-colors disabled:cursor-not-allowed ${VARIANTS[variant]} ${className}`}\n {...props}\n />\n );\n}\n\nexport function Badge({ children, tone = 'slate' }: { children: ReactNode; tone?: 'slate' | 'green' | 'red' | 'amber' | 'indigo' }) {\n const tones: Record<string, string> = {\n slate: 'bg-slate-100 text-slate-700',\n green: 'bg-green-100 text-green-700',\n red: 'bg-red-100 text-red-700',\n amber: 'bg-amber-100 text-amber-700',\n indigo: 'bg-indigo-100 text-indigo-700',\n };\n return (\n <span className={`inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${tones[tone]}`}>\n {children}\n </span>\n );\n}\n\nexport function Spinner({ label }: { label?: string }) {\n return (\n <div className=\"flex items-center gap-2 text-sm text-slate-500\" role=\"status\">\n <span className=\"h-4 w-4 animate-spin rounded-full border-2 border-slate-300 border-t-indigo-600\" />\n {label ?? 'Loading…'}\n </div>\n );\n}\n\nexport function EmptyState({ title, hint }: { title: string; hint?: string }) {\n return (\n <div className=\"rounded-lg border border-dashed border-slate-300 p-8 text-center\">\n <p className=\"text-sm font-medium text-slate-700\">{title}</p>\n {hint && <p className=\"mt-1 text-sm text-slate-500\">{hint}</p>}\n </div>\n );\n}\n\nexport function ErrorBanner({ error }: { error: Error }) {\n return (\n <div className=\"rounded-md border border-red-200 bg-red-50 p-3 text-sm text-red-700\" role=\"alert\">\n {error.message}\n </div>\n );\n}\n","import { useEffect, useState } from 'react';\nimport type { FieldMetadata } from '../client/types.js';\nimport { useClient, useEntity } from '../AdminContext.js';\nimport type { MaestroClient } from '../client/MaestroClient.js';\n\n// Display values already resolved this session, keyed by `entityId/recordId`. A tiny, ever-growing\n// cache is the right trade-off here: relation columns repeat the same handful of ids page after page.\nconst displayCache = new Map<string, Promise<string | undefined>>();\n\nfunction resolveDisplay(\n client: MaestroClient,\n entityId: string,\n displayField: string,\n id: string,\n): Promise<string | undefined> {\n const key = `${entityId}/${id}`;\n let cached = displayCache.get(key);\n if (!cached) {\n cached = client\n .findById(entityId, id)\n .then((record) => {\n const value = record[displayField];\n return value === null || value === undefined || value === '' ? undefined : String(value);\n })\n .catch(() => undefined);\n displayCache.set(key, cached);\n }\n return cached;\n}\n\nfunction shortId(id: string): string {\n return id.length > 12 ? `${id.slice(0, 6)}…${id.slice(-4)}` : id;\n}\n\n/**\n * Renders a relation field's value as a chip labeled with the *related record's* display value\n * (fetched lazily and cached) instead of a bare foreign id. Falls back to a shortened id while\n * loading or when the related record is unreachable.\n */\nexport function RelationValue({ field, value }: { field: FieldMetadata; value: unknown }) {\n const client = useClient();\n const target = useEntity(field.relationEntity);\n const id = String(value);\n const [display, setDisplay] = useState<string | undefined>(undefined);\n\n useEffect(() => {\n let cancelled = false;\n setDisplay(undefined);\n if (!target || target.displayField === target.primaryKey) return;\n resolveDisplay(client, target.id, target.displayField, id).then((resolved) => {\n if (!cancelled) setDisplay(resolved);\n });\n return () => {\n cancelled = true;\n };\n }, [client, target, id]);\n\n return (\n <span\n title={id}\n className=\"inline-flex max-w-56 items-center gap-1 truncate rounded border border-slate-200 bg-slate-50 px-1.5 py-0.5 text-xs font-medium text-slate-700\"\n >\n {target && <span className=\"text-slate-400\">{target.label.singular}:</span>}\n <span className=\"truncate\">{display ?? shortId(id)}</span>\n </span>\n );\n}\n","import type { FieldMetadata } from '../client/types.js';\nimport { Badge } from './ui.js';\nimport { RelationValue } from './RelationValue.js';\nimport { useT } from '../i18n.js';\n\nfunction enumLabel(field: FieldMetadata, value: unknown): string {\n const opt = field.enumOptions?.find((o) => o.value === value);\n return opt ? opt.label : String(value);\n}\n\n/**\n * Renders a single record value read-only, shaped by the field's type (for tables and detail views).\n * `compact` is the table-cell mode: long text truncates instead of stretching the row.\n */\nexport function FieldValue({ field, value, compact = false }: { field: FieldMetadata; value: unknown; compact?: boolean }) {\n const t = useT();\n\n if (value === null || value === undefined || value === '') {\n return <span className=\"text-slate-400\">—</span>;\n }\n\n if (field.sensitive) {\n return <span className=\"text-slate-400\">••••••</span>;\n }\n\n switch (field.type) {\n case 'boolean':\n return value ? <Badge tone=\"green\">{t.yes}</Badge> : <Badge tone=\"slate\">{t.no}</Badge>;\n case 'enum':\n return <Badge tone=\"indigo\">{enumLabel(field, value)}</Badge>;\n case 'relation':\n return field.relationEntity ? (\n <RelationValue field={field} value={value} />\n ) : (\n <span className=\"text-slate-700\">{String(value)}</span>\n );\n case 'date':\n case 'datetime': {\n const d = new Date(String(value));\n return <span className=\"whitespace-nowrap\">{Number.isNaN(d.getTime()) ? String(value) : d.toLocaleString()}</span>;\n }\n case 'json':\n return (\n <code className={`block overflow-x-auto rounded bg-slate-50 px-1.5 py-0.5 text-xs text-slate-700 ${compact ? 'max-w-xs truncate' : 'max-w-md'}`}>\n {JSON.stringify(value)}\n </code>\n );\n default:\n return <span className={`text-slate-700 ${compact ? 'block max-w-64 truncate' : ''}`}>{String(value)}</span>;\n }\n}\n","import { useEffect, useLayoutEffect, useRef, useState, type ReactNode } from 'react';\nimport { createPortal } from 'react-dom';\n\nexport interface MenuItem {\n id: string;\n label: ReactNode;\n danger?: boolean;\n onSelect: () => void;\n}\n\nexport interface MenuAnchor {\n x: number;\n y: number;\n}\n\n/**\n * A floating menu rendered in a portal at a fixed viewport position — immune to the table's\n * `overflow-x` clipping. Closes on outside click, Escape, scroll or resize. Purely presentational:\n * callers decide the anchor (a button's rect, a clicked cell) and the items.\n */\nexport function PopMenu({ anchor, items, onClose }: { anchor: MenuAnchor; items: MenuItem[]; onClose: () => void }) {\n const ref = useRef<HTMLDivElement>(null);\n const [pos, setPos] = useState(anchor);\n\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el) return;\n const rect = el.getBoundingClientRect();\n setPos({\n x: Math.max(4, Math.min(anchor.x, window.innerWidth - rect.width - 8)),\n y: Math.max(4, Math.min(anchor.y, window.innerHeight - rect.height - 8)),\n });\n }, [anchor]);\n\n useEffect(() => {\n const onDown = (e: MouseEvent): void => {\n if (ref.current && !ref.current.contains(e.target as Node)) onClose();\n };\n const onKey = (e: KeyboardEvent): void => {\n if (e.key === 'Escape') onClose();\n };\n document.addEventListener('mousedown', onDown);\n document.addEventListener('keydown', onKey);\n window.addEventListener('scroll', onClose, true);\n window.addEventListener('resize', onClose);\n return () => {\n document.removeEventListener('mousedown', onDown);\n document.removeEventListener('keydown', onKey);\n window.removeEventListener('scroll', onClose, true);\n window.removeEventListener('resize', onClose);\n };\n }, [onClose]);\n\n return createPortal(\n <div\n ref={ref}\n role=\"menu\"\n style={{ position: 'fixed', left: pos.x, top: pos.y, zIndex: 50 }}\n className=\"min-w-44 rounded-md border border-slate-200 bg-white py-1 shadow-lg\"\n >\n {items.map((item) => (\n <button\n key={item.id}\n role=\"menuitem\"\n onClick={() => {\n onClose();\n item.onSelect();\n }}\n className={`block w-full px-3 py-1.5 text-left text-sm ${\n item.danger ? 'text-red-600 hover:bg-red-50' : 'text-slate-700 hover:bg-slate-100'\n }`}\n >\n {item.label}\n </button>\n ))}\n </div>,\n document.body,\n );\n}\n\n/** The row-level \"⋮\" trigger: a compact button that opens a `PopMenu` aligned under itself. */\nexport function MenuButton({ items, label }: { items: MenuItem[]; label: string }) {\n const [anchor, setAnchor] = useState<MenuAnchor | undefined>(undefined);\n\n return (\n <>\n <button\n type=\"button\"\n aria-label={label}\n title={label}\n onClick={(e) => {\n const rect = e.currentTarget.getBoundingClientRect();\n setAnchor({ x: rect.right - 176, y: rect.bottom + 4 });\n }}\n className=\"rounded-md px-2 py-1 text-slate-500 hover:bg-slate-100 hover:text-slate-900\"\n >\n ⋮\n </button>\n {anchor && <PopMenu anchor={anchor} items={items} onClose={() => setAnchor(undefined)} />}\n </>\n );\n}\n","/**\n * Copies text to the clipboard, with a hidden-textarea fallback for non-secure contexts (plain-http\n * admin deployments), where `navigator.clipboard` is unavailable.\n */\nexport async function copyText(text: string): Promise<void> {\n if (navigator.clipboard?.writeText) {\n try {\n await navigator.clipboard.writeText(text);\n return;\n } catch {\n // fall through to the textarea fallback\n }\n }\n const textarea = document.createElement('textarea');\n textarea.value = text;\n textarea.style.position = 'fixed';\n textarea.style.opacity = '0';\n document.body.appendChild(textarea);\n textarea.select();\n document.execCommand('copy');\n textarea.remove();\n}\n","import { useState } from 'react';\nimport type { EntityMetadata } from '../client/types.js';\nimport { useClient, useEntities } from '../AdminContext.js';\nimport { detailFields, useEntityRecord } from '../hooks.js';\nimport { FieldValue } from './FieldValue.js';\nimport { Button, ErrorBanner, Spinner } from './ui.js';\nimport { MenuButton, type MenuItem } from './menu.js';\nimport { useT } from '../i18n.js';\nimport { copyText } from '../clipboard.js';\n\nexport function EntityDetail({\n entity,\n id,\n onBack,\n onEdit,\n onClone,\n onDeleted,\n onNavigate,\n}: {\n entity: EntityMetadata;\n id: string;\n onBack: () => void;\n onEdit: () => void;\n onClone: () => void;\n onDeleted: () => void;\n onNavigate: (entityId: string, id: string) => void;\n}) {\n const client = useClient();\n const t = useT();\n const entities = useEntities();\n const { data, error, loading } = useEntityRecord(entity.id, id);\n const fields = detailFields(entity);\n const [copied, setCopied] = useState(false);\n\n const copy = async (text: string): Promise<void> => {\n await copyText(text);\n setCopied(true);\n setTimeout(() => setCopied(false), 1500);\n };\n\n const onDelete = async (): Promise<void> => {\n if (!globalThis.confirm(t.deleteConfirm(entity.label.singular))) return;\n await client.remove(entity.id, id);\n onDeleted();\n };\n\n const menuItems: MenuItem[] = [];\n if (entity.capabilities.clone && entity.capabilities.create) {\n menuItems.push({ id: 'clone', label: `⧉ ${t.clone}`, onSelect: onClone });\n }\n menuItems.push({ id: 'copy-id', label: `⎘ ${t.copyId}`, onSelect: () => void copy(id) });\n if (data) {\n menuItems.push({\n id: 'copy-object',\n label: `{} ${t.copyObject}`,\n onSelect: () => void copy(JSON.stringify(data, null, 2)),\n });\n }\n if (entity.capabilities.delete) {\n menuItems.push({ id: 'remove', label: `🗑 ${t.remove}`, danger: true, onSelect: () => void onDelete() });\n }\n\n return (\n <section className=\"space-y-4\">\n <header className=\"flex items-center justify-between gap-3\">\n <div>\n <button onClick={onBack} className=\"text-sm text-indigo-600 hover:underline\">\n ← {entity.label.plural}\n </button>\n <h1 className=\"text-lg font-semibold text-slate-900\">\n {entity.label.singular} {data ? String(data[entity.displayField] ?? id) : ''}\n </h1>\n </div>\n <div className=\"flex items-center gap-1\">\n {entity.capabilities.update && (\n <Button variant=\"primary\" onClick={onEdit}>\n {t.edit}\n </Button>\n )}\n <MenuButton label={t.actions} items={menuItems} />\n </div>\n </header>\n\n {error && <ErrorBanner error={error} />}\n {loading && !data ? (\n <Spinner />\n ) : (\n data && (\n <dl className=\"divide-y divide-slate-100 rounded-lg border border-slate-200 bg-white\">\n {fields.map((f) => {\n const value = data[f.name];\n const isRelation = f.type === 'relation' && f.relationEntity && value !== null && value !== undefined;\n const target = isRelation ? entities.find((e) => e.id === f.relationEntity) : undefined;\n return (\n <div key={f.name} className=\"grid grid-cols-3 gap-4 px-4 py-3\">\n <dt className=\"text-sm font-medium text-slate-500\">{f.label}</dt>\n <dd className=\"col-span-2 flex items-center gap-2 text-sm\">\n <FieldValue field={f} value={value} />\n {isRelation && (\n <button\n onClick={() => onNavigate(f.relationEntity!, String(value))}\n className=\"text-xs text-indigo-600 hover:underline\"\n >\n {t.openRelated(target?.label.singular ?? f.relationEntity!)} ↗\n </button>\n )}\n </dd>\n </div>\n );\n })}\n </dl>\n )\n )}\n\n {copied && (\n <div className=\"fixed bottom-4 right-4 z-50 rounded-md bg-slate-900 px-3 py-1.5 text-sm text-white shadow-lg\">\n {t.copied}\n </div>\n )}\n </section>\n );\n}\n","import { useEffect, useState } from 'react';\nimport type { EntityMetadata, Record_ } from '../client/types.js';\nimport { useClient } from '../AdminContext.js';\nimport { formFields, useEntityRecord } from '../hooks.js';\nimport { FieldInput } from './FieldInput.js';\nimport { Button, ErrorBanner, Spinner } from './ui.js';\nimport { useT } from '../i18n.js';\n\nexport function EntityForm({\n entity,\n mode,\n id,\n cloneFromId,\n onCancel,\n onSaved,\n}: {\n entity: EntityMetadata;\n mode: 'create' | 'edit';\n id?: string;\n /** In `create` mode, prefills the form from this record (minus the primary key and non-cloneable fields). */\n cloneFromId?: string;\n onCancel: () => void;\n onSaved: (record: Record_) => void;\n}) {\n const client = useClient();\n const t = useT();\n const fields = formFields(entity, mode);\n const sourceId = mode === 'edit' ? id : cloneFromId;\n const existing = useEntityRecord(entity.id, sourceId);\n\n const [values, setValues] = useState<Record_>({});\n const [saving, setSaving] = useState(false);\n const [error, setError] = useState<Error | undefined>(undefined);\n\n useEffect(() => {\n if (!existing.data || !sourceId) return;\n if (mode === 'edit') {\n setValues(existing.data);\n return;\n }\n // Cloning: copy only cloneable, non-readonly fields; the primary key must be newly assigned.\n const seed: Record_ = {};\n for (const f of entity.fields) {\n if (f.name === entity.primaryKey) continue;\n if (!f.form.cloneable || f.readonly) continue;\n if (existing.data[f.name] !== undefined) seed[f.name] = existing.data[f.name];\n }\n setValues(seed);\n }, [mode, sourceId, existing.data, entity]);\n\n const submit = async (e: React.FormEvent): Promise<void> => {\n e.preventDefault();\n setSaving(true);\n setError(undefined);\n try {\n const payload: Record_ = {};\n for (const f of fields) {\n if (values[f.name] !== undefined) payload[f.name] = values[f.name];\n }\n const saved =\n mode === 'create'\n ? await client.create(entity.id, payload)\n : await client.update(entity.id, id!, payload);\n onSaved(saved);\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)));\n } finally {\n setSaving(false);\n }\n };\n\n if (sourceId && existing.loading && !existing.data) {\n return <Spinner />;\n }\n\n const title =\n mode === 'edit'\n ? t.editTitle(entity.label.singular)\n : cloneFromId\n ? t.cloneTitle(entity.label.singular)\n : t.newTitle(entity.label.singular);\n\n return (\n <section className=\"max-w-2xl space-y-4\">\n <header>\n <button onClick={onCancel} className=\"text-sm text-indigo-600 hover:underline\">\n ← {entity.label.plural}\n </button>\n <h1 className=\"text-lg font-semibold text-slate-900\">{title}</h1>\n </header>\n\n {error && <ErrorBanner error={error} />}\n\n <form onSubmit={submit} className=\"space-y-4 rounded-lg border border-slate-200 bg-white p-5\">\n {fields.map((f) => (\n <div key={f.name} className=\"space-y-1\">\n <label htmlFor={`field-${f.name}`} className=\"block text-sm font-medium text-slate-700\">\n {f.label}\n {f.required && <span className=\"ml-0.5 text-red-500\">*</span>}\n </label>\n <FieldInput\n field={f}\n value={values[f.name]}\n disabled={saving || f.readonly}\n onChange={(next) => setValues((v) => ({ ...v, [f.name]: next }))}\n />\n {f.form.helpText && <p className=\"text-xs text-slate-500\">{f.form.helpText}</p>}\n </div>\n ))}\n\n <div className=\"flex justify-end gap-2 border-t border-slate-100 pt-4\">\n <Button type=\"button\" variant=\"secondary\" onClick={onCancel} disabled={saving}>\n {t.cancel}\n </Button>\n <Button type=\"submit\" variant=\"primary\" disabled={saving}>\n {saving ? t.saving : mode === 'create' ? t.create : t.save}\n </Button>\n </div>\n </form>\n </section>\n );\n}\n","import type { FieldMetadata } from '../client/types.js';\n\nconst inputClass =\n 'w-full rounded-md border border-slate-300 px-3 py-1.5 text-sm text-slate-800 shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 disabled:bg-slate-100';\n\n/**\n * Renders the right form control for a field's type and reports its next value. Values are kept as strings\n * for text/enum, numbers for numeric types, booleans for checkboxes, and parsed objects for json — matching\n * what the create/update endpoints expect.\n */\nexport function FieldInput({\n field,\n value,\n onChange,\n disabled,\n}: {\n field: FieldMetadata;\n value: unknown;\n onChange: (next: unknown) => void;\n disabled?: boolean;\n}) {\n const id = `field-${field.name}`;\n\n switch (field.type) {\n case 'boolean':\n return (\n <input\n id={id}\n type=\"checkbox\"\n checked={Boolean(value)}\n disabled={disabled}\n onChange={(e) => onChange(e.target.checked)}\n className=\"h-4 w-4 rounded border-slate-300 text-indigo-600 focus:ring-indigo-500\"\n />\n );\n\n case 'integer':\n case 'number':\n case 'float':\n case 'decimal':\n case 'currency':\n return (\n <input\n id={id}\n type=\"number\"\n value={value === null || value === undefined ? '' : String(value)}\n placeholder={field.form.placeholder}\n disabled={disabled}\n step={field.type === 'integer' ? 1 : 'any'}\n onChange={(e) => onChange(e.target.value === '' ? null : Number(e.target.value))}\n className={inputClass}\n />\n );\n\n case 'enum':\n return (\n <select\n id={id}\n value={value === null || value === undefined ? '' : String(value)}\n disabled={disabled}\n onChange={(e) => onChange(e.target.value)}\n className={inputClass}\n >\n <option value=\"\">—</option>\n {field.enumOptions?.map((o) => (\n <option key={String(o.value)} value={String(o.value)}>\n {o.label}\n </option>\n ))}\n </select>\n );\n\n case 'date':\n return (\n <input\n id={id}\n type=\"date\"\n value={value ? String(value).slice(0, 10) : ''}\n disabled={disabled}\n onChange={(e) => onChange(e.target.value || null)}\n className={inputClass}\n />\n );\n\n case 'text':\n case 'json':\n return (\n <textarea\n id={id}\n rows={field.type === 'json' ? 5 : 3}\n value={\n field.type === 'json' && value !== null && typeof value === 'object'\n ? JSON.stringify(value, null, 2)\n : value === null || value === undefined\n ? ''\n : String(value)\n }\n placeholder={field.form.placeholder}\n disabled={disabled}\n onChange={(e) => {\n if (field.type === 'json') {\n try {\n onChange(e.target.value ? JSON.parse(e.target.value) : null);\n } catch {\n onChange(e.target.value);\n }\n } else {\n onChange(e.target.value);\n }\n }}\n className={`${inputClass} font-mono`}\n />\n );\n\n default:\n return (\n <input\n id={id}\n type={field.type === 'email' ? 'email' : 'text'}\n value={value === null || value === undefined ? '' : String(value)}\n placeholder={field.form.placeholder}\n disabled={disabled}\n onChange={(e) => onChange(e.target.value)}\n className={inputClass}\n />\n );\n }\n}\n","import type { EntityMetadata, FieldMetadata, MaestroMetadata } from './client/types.js';\n\nexport interface EntityLabelOverride {\n /** Singular label (\"Usuário\"). */\n singular?: string;\n /** Plural label (\"Usuários\") — also the sidebar entry. */\n plural?: string;\n /** Sidebar folder this entity belongs to (display name). Wins over heuristic prefix grouping. */\n group?: string;\n /** Field labels for this entity only, by field name. Wins over the global `fields` map. */\n fields?: Record<string, string>;\n}\n\n/**\n * Presentation-layer label rewrites, applied on top of the metadata the server serves. This is how a\n * deployment localizes or humanizes auto-introspected names (\"recoveryactions\" → \"Ações de\n * Recuperação\") without touching the engine: keys match either the entity `id` or its source `table`.\n */\nexport interface AdminLabels {\n entities?: Record<string, EntityLabelOverride>;\n /** Global field labels by field name (`createdAt` → \"Criado em\"), for every entity. */\n fields?: Record<string, string>;\n /** Display names for heuristic sidebar folders, by shared table-name prefix (`recovery` → \"Recuperação\"). */\n groups?: Record<string, string>;\n}\n\nfunction relabelField(\n field: FieldMetadata,\n entityOverride: EntityLabelOverride | undefined,\n globalFields: Record<string, string>,\n): FieldMetadata {\n const label = entityOverride?.fields?.[field.name] ?? globalFields[field.name];\n return label ? { ...field, label } : field;\n}\n\nfunction relabelEntity(entity: EntityMetadata, labels: AdminLabels): EntityMetadata {\n const override = labels.entities?.[entity.id] ?? labels.entities?.[entity.table];\n const globalFields = labels.fields ?? {};\n return {\n ...entity,\n label: {\n singular: override?.singular ?? entity.label.singular,\n plural: override?.plural ?? override?.singular ?? entity.label.plural,\n },\n fields: entity.fields.map((f) => relabelField(f, override, globalFields)),\n };\n}\n\n/** Returns a copy of `metadata` with every label override applied. No-op when `labels` is empty. */\nexport function applyLabels(metadata: MaestroMetadata, labels: AdminLabels | undefined): MaestroMetadata {\n if (!labels || (!labels.entities && !labels.fields)) return metadata;\n return { ...metadata, entities: metadata.entities.map((e) => relabelEntity(e, labels)) };\n}\n","import { useCallback, useState } from 'react';\n\nconst STORAGE_KEY = 'maestro-admin:recents';\nconst MAX_RECENTS = 8;\n\nfunction read(key: string): string[] {\n try {\n const raw = globalThis.localStorage?.getItem(key);\n const parsed: unknown = raw ? JSON.parse(raw) : [];\n return Array.isArray(parsed) ? parsed.filter((x): x is string => typeof x === 'string') : [];\n } catch {\n return []; // storage unavailable (SSR, blocked) or corrupted — recents are best-effort\n }\n}\n\nfunction write(key: string, ids: string[]): void {\n try {\n globalThis.localStorage?.setItem(key, JSON.stringify(ids));\n } catch {\n // best-effort\n }\n}\n\n/**\n * Recently-visited entity ids, most recent first, persisted in `localStorage` so they survive\n * reloads. `push` moves (or inserts) an id to the front, capped at 8. Pass a distinct `scope` (e.g.\n * the API URL) when one browser origin hosts admins for several servers.\n */\nexport function useRecents(scope?: string): { recents: string[]; push: (entityId: string) => void } {\n const key = scope ? `${STORAGE_KEY}:${scope}` : STORAGE_KEY;\n const [recents, setRecents] = useState<string[]>(() => read(key));\n\n const push = useCallback(\n (entityId: string): void => {\n setRecents((prev) => {\n const next = [entityId, ...prev.filter((id) => id !== entityId)].slice(0, MAX_RECENTS);\n write(key, next);\n return next;\n });\n },\n [key],\n );\n\n return { recents, push };\n}\n"],"mappings":"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","names":["useMemo","useState","useEffect","useState","createContext","useContext","useState","useEffect","useMemo","useState","jsx","jsxs","useEffect","useState","jsx","jsxs","useState","useEffect","jsx","useEffect","useRef","useState","Fragment","jsx","jsxs","jsx","jsxs","useMemo","useState","useState","jsx","jsxs","useState","useEffect","useState","jsx","jsxs","jsx","jsxs","useState","useEffect","useCallback","useState","jsx","jsxs","useState","useMemo"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maykonpaulo/maestro-admin",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.0-next.1",
|
|
4
4
|
"description": "Generic, metadata-driven admin UI for @maykonpaulo/maestro-core (via @maykonpaulo/maestro-server). Reads GET /metadata and auto-builds navigation, tables, detail views and create/update forms for every entity/collection — no per-entity code. Ships as a runnable React app and as exported components (ADR 0008).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -56,6 +56,6 @@
|
|
|
56
56
|
"test:e2e": "playwright test",
|
|
57
57
|
"typecheck": "tsc --noEmit",
|
|
58
58
|
"lint": "eslint src",
|
|
59
|
-
"clean": "rimraf dist dist-app"
|
|
59
|
+
"clean": "rimraf dist dist-app dist-e2e"
|
|
60
60
|
}
|
|
61
61
|
}
|