@rsc-kit/press 0.30.51 → 0.30.53
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/README.md +1 -1
- package/dist/admin/panel.js +4 -1
- package/dist/admin/panel.js.map +1 -1
- package/dist/conditions.d.ts +32 -0
- package/dist/conditions.js +25 -0
- package/dist/conditions.js.map +1 -0
- package/dist/forms/actions.d.ts +6 -5
- package/dist/forms/actions.js +13 -8
- package/dist/forms/actions.js.map +1 -1
- package/dist/forms/blueprint.d.ts +99 -0
- package/dist/forms/blueprint.js +141 -0
- package/dist/forms/blueprint.js.map +1 -0
- package/dist/forms/brand.d.ts +26 -0
- package/dist/forms/brand.js +9 -0
- package/dist/forms/brand.js.map +1 -0
- package/dist/forms/host.d.ts +1 -1
- package/dist/forms/host.js.map +1 -1
- package/dist/forms/index.d.ts +3 -3
- package/dist/forms/index.js +1 -1
- package/dist/forms/index.js.map +1 -1
- package/dist/forms/registry.d.ts +6 -1
- package/dist/forms/registry.js +1 -1
- package/dist/forms/registry.js.map +1 -1
- package/dist/forms/theme.d.ts +10 -7
- package/dist/forms/theme.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/resource.d.ts +23 -1
- package/dist/resource.js +80 -16
- package/dist/resource.js.map +1 -1
- package/dist/shadcn/blueprint-form.d.ts +8 -0
- package/dist/shadcn/{public-form.js → blueprint-form.js} +7 -6
- package/dist/shadcn/blueprint-form.js.map +1 -0
- package/dist/shadcn/fields.d.ts +11 -1
- package/dist/shadcn/fields.js +72 -10
- package/dist/shadcn/fields.js.map +1 -1
- package/dist/shadcn/index.d.ts +1 -1
- package/dist/shadcn/index.js +3 -3
- package/dist/shadcn/index.js.map +1 -1
- package/dist/shadcn/resource-form.js +3 -2
- package/dist/shadcn/resource-form.js.map +1 -1
- package/package.json +2 -2
- package/dist/forms/form.d.ts +0 -61
- package/dist/forms/form.js +0 -66
- package/dist/forms/form.js.map +0 -1
- package/dist/shadcn/public-form.d.ts +0 -8
- package/dist/shadcn/public-form.js.map +0 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Admin panels, public forms and pages built from blocks for [rsc-kit](https://rsc
|
|
|
6
6
|
| --- | --- |
|
|
7
7
|
| `@rsc-kit/press` | `defineResource`, `memorySource`, `perTenant`, `definePlugin`, `defineBlock`, `Blocks`, and the `Storage` an upload field writes through. Nothing here draws. |
|
|
8
8
|
| `@rsc-kit/press/admin` | `definePanel`, the url schemas its routes export, and the `AdminTheme` contract |
|
|
9
|
-
| `@rsc-kit/press/forms` | `
|
|
9
|
+
| `@rsc-kit/press/forms` | `defineBlueprint`: a form defined once from a Standard Schema or a backend's actions - a contact form, a sign-up, an edit page - drawn in any page and as a resource's form, and the `FormsTheme` contract |
|
|
10
10
|
| `@rsc-kit/press/pages` | `definePages`: landing pages an editor builds in the panel from the app's blocks, served by one catch-all route |
|
|
11
11
|
| `@rsc-kit/press/shadcn` | `shadcn`, the theme that draws them |
|
|
12
12
|
| `@rsc-kit/press/signing` | `verifyPluginRequest`, for an installable plugin's server |
|
package/dist/admin/panel.js
CHANGED
|
@@ -8,6 +8,7 @@ import { digestOf, fetchManifest, RESERVED, scopeWords } from '../installable.js
|
|
|
8
8
|
import { allowOf, contextOf, registerPanel, resourcesFor } from './registry.js';
|
|
9
9
|
import { humanize } from '../resource.js';
|
|
10
10
|
import { cursorHref } from './url.js';
|
|
11
|
+
import { valuesFor } from '../forms/blueprint.js';
|
|
11
12
|
export { listSearch, pluginsSearch, recordParams, resourceParams } from './url.js';
|
|
12
13
|
/**
|
|
13
14
|
* A panel: its resources, and the pages that show them, already bound to it.
|
|
@@ -285,7 +286,9 @@ export function definePanel(definition) {
|
|
|
285
286
|
const record = resource.readOnly ? null : await resource.source.find(id, context);
|
|
286
287
|
if (!record)
|
|
287
288
|
notFound();
|
|
288
|
-
|
|
289
|
+
// Only the form's fields reach the browser, not every column the record has.
|
|
290
|
+
const editFields = await fieldsFor(resource, context, true);
|
|
291
|
+
return (_jsx(theme.Form, { title: String(record[resource.title]), description: `Edit this ${resource.singular.toLowerCase()}.`, action: saveRecord.bind(null, path, resource.name, id), fields: editFields, values: valuesFor(editFields, record), submit: "Save changes", cancel: to(resource.name) }));
|
|
289
292
|
}
|
|
290
293
|
/** The installed plugins, a manifest under review, and the form to start one. */
|
|
291
294
|
async function PluginsBody({ searchParams }) {
|
package/dist/admin/panel.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"panel.js","sourceRoot":"","sources":["../../src/admin/panel.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Suspense } from 'react'\nimport type { ComponentType, ReactNode } from 'react'\nimport { notFound } from '@rsc-kit/core/not-found'\nimport { connection } from '@rsc-kit/core/request'\nimport { section } from '@rsc-kit/core/section'\nimport {\n bulkRecords,\n deleteRecord,\n installPlugin,\n reviewPlugin,\n saveRecord,\n secretCopied,\n signOut,\n switchTenant,\n uninstallPlugin,\n} from './actions.js'\nimport { digestOf, fetchManifest, RESERVED, scopeWords } from '../installable.js'\nimport type { Installs } from '../installable.js'\nimport type { PanelPlugin } from '../plugin.js'\nimport { allowOf, contextOf, registerPanel, resourcesFor } from './registry.js'\nimport type { PanelHandle, Tenancy } from './registry.js'\nimport { humanize } from '../resource.js'\nimport type { Column, Field, Option, Resource, SourceContext } from '../resource.js'\nimport type { AdminTheme, NavItem, PanelUser, PluginsProps, TableRowData } from './theme.js'\nimport type { ListSearch, PluginsSearch } from './url.js'\n\nimport { cursorHref } from './url.js'\nexport { listSearch, pluginsSearch, recordParams, resourceParams } from './url.js'\n\nexport interface PanelDefinition {\n /** Where the panel's routes are: the folder under src/app it is mounted in. */\n path?: string\n title?: string\n /** The sidebar's mark beside the title, in a panel without tenants. */\n logo?: ComponentType<{ className?: string }>\n resources: Resource[]\n /**\n * What draws the panel: import { shadcn } from '@rsc-kit/press/shadcn', or\n * any object that fulfils AdminTheme. The panel decides what is shown and\n * who may see it; the theme only draws.\n */\n theme: AdminTheme\n /**\n * Whether this request may use the panel. Runs on every page and on every\n * save, delete and switch - an action is reachable without the page that\n * rendered it, so a guard on the pages alone would not be one. A refusal is\n * a 404. The panel has no login of its own: this asks the app's.\n */\n authorize?: () => boolean | Promise<boolean>\n /** Who is signed in, for the sidebar's footer. */\n user?: () => PanelUser | null | Promise<PanelUser | null>\n /** Sign the visitor out. Runs in a server action; the visitor lands on /. */\n signOut?: () => void | Promise<void>\n /** Whose records the panel shows: a switcher in the sidebar, and a tenant on every data source call. */\n tenancy?: Tenancy\n /** Packages that add resources, dashboard widgets and reactions to changes. */\n plugins?: PanelPlugin[]\n /**\n * Plugins a person installs from the panel while it runs, with no rebuild:\n * a manifest url, reviewed, then installed for the current tenant. Mounted\n * as src/app/admin/plugins/page.tsx. See installable.ts for what they can\n * and cannot reach.\n */\n installs?: Installs\n /** The light/dark switch in the header. On unless the app has its own. */\n modeToggle?: boolean\n}\n\n/** A type, not an interface: section() takes props that are a record, and an interface has no index signature. */\ntype Props<P, S = never> = {\n params: Promise<P>\n searchParams: Promise<S>\n}\n\n/**\n * A panel: its resources, and the pages that show them, already bound to it.\n * The app mounts each in a route file of its own, because the engine finds\n * routes, url schemas and sections by their files:\n *\n * src/app/admin/middleware.ts export default panel.middleware\n * src/app/admin/layout.tsx export default panel.Layout\n * src/app/admin/page.tsx export default panel.Dashboard\n * src/app/admin/[resource]/page.tsx export const params = resourceParams\n * export const searchParams = listSearch\n * export default panel.Index\n * src/app/admin/[resource]/admin-table.section.tsx export default panel.Table\n * src/app/admin/[resource]/new/page.tsx export const params = resourceParams\n * export default panel.Create\n * src/app/admin/[resource]/[id]/page.tsx export const params = recordParams\n * export default panel.Edit\n */\nexport function definePanel(definition: PanelDefinition) {\n const path = definition.path ?? '/admin'\n const title = definition.title ?? 'Admin'\n const plugins = definition.plugins ?? []\n const resources = [...definition.resources, ...plugins.flatMap((plugin) => plugin.resources ?? [])]\n const authorize = definition.authorize ?? (() => true)\n const tenancy = definition.tenancy ?? null\n const installs = definition.installs ?? null\n const theme = definition.theme\n /** The panel's own resources and its plugins'; any other is one a tenant installed. */\n const own = new Set(resources.map((resource) => resource.name))\n\n if (!/^(\\/[a-z0-9-]+)+$/.test(path)) {\n throw new Error(`[admin] a panel's path is where its routes are, with no trailing slash: ${JSON.stringify(path)}`)\n }\n\n const names = resources.map((resource) => resource.name)\n const twice = names.find((name, index) => names.indexOf(name) !== index)\n\n if (twice) throw new Error(`[admin] two resources are named ${twice}; a name is a url segment`)\n\n const reserved = installs ? names.find((name) => RESERVED.includes(name)) : undefined\n\n if (reserved) throw new Error(`[admin] a panel that takes installs has a plugins page, so no resource may be named ${reserved}`)\n\n const pluginNames = plugins.map((plugin) => plugin.name)\n const pluginTwice = pluginNames.find((name, index) => pluginNames.indexOf(name) !== index)\n\n if (pluginTwice) throw new Error(`[admin] the plugin ${pluginTwice} is installed twice`)\n\n // A relation names a resource by its url segment. Checked here, once every\n // resource is known, so a misspelt one fails at startup and not on the\n // first form that needs its choices.\n for (const resource of resources) {\n const named = [...resource.relations, ...resource.columns.map((column) => column.relation)]\n\n for (const relation of named) {\n if (relation !== undefined && !names.includes(relation)) {\n throw new Error(`[admin] ${resource.name} relates to ${relation}, which is not a resource of this panel`)\n }\n }\n }\n\n const handle: PanelHandle = { path, resources, authorize, plugins, tenancy, signOut: definition.signOut ?? null, installs }\n\n registerPanel(handle)\n\n /** A url of the panel. Typed routes are the app's; the panel's are its own. */\n const to = (...parts: string[]) => [path, ...parts].join('/')\n\n /**\n * A related resource's rows as choices: its key saved, its title shown.\n * Every row, scoped to the visitor's tenant like any list - so a belongsTo\n * is for a resource with a select's worth of rows: categories, teams,\n * plans. Memoised per render, so a field and a column naming the same\n * resource list it once.\n */\n function choices(context: SourceContext, resources: Resource[]) {\n const listed = new Map<string, Promise<Option[]>>()\n\n return (name: string): Promise<Option[]> => {\n let found = listed.get(name)\n\n if (!found) {\n const related = resources.find((resource) => resource.name === name)!\n\n found = related.source\n .list({ dir: 'asc', page: 1, perPage: null, filters: {}, searchable: [] }, context)\n .then(({ rows }) => rows.map((row) => ({ value: String(row[related.key]), label: String(row[related.title]) })))\n listed.set(name, found)\n }\n\n return found\n }\n }\n\n /** The form's fields, each relation given its choices: the create form's, or the edit form's. */\n async function fieldsFor(resource: Resource, context: SourceContext, editing = false): Promise<Field[]> {\n const of = choices(context, await resourcesFor(handle, context))\n\n return Promise.all((editing ? resource.editFields : resource.fields).map(async (field) => (field.relation ? { ...field, options: await of(field.relation) } : field)))\n }\n\n /**\n * The panel's door: a refusal is a 404, so the panel's existence is not\n * shown. Per request, always - authorize is about this visitor and the rows\n * are live - so the build stores the skeleton and never runs either.\n */\n async function enter(): Promise<SourceContext>\n async function enter(name: string): Promise<{ resource: Resource; context: SourceContext }>\n async function enter(name?: string) {\n await connection()\n\n if (!(await authorize())) notFound()\n\n const context = (await contextOf(handle)) ?? notFound()\n\n if (name === undefined) return context\n\n return { resource: (await resourcesFor(handle, context)).find((resource) => resource.name === name) ?? notFound(), context }\n }\n\n /**\n * The panel's guard, mounted as src/app/admin/middleware.ts. It runs before\n * anything is sent, so its refusals are real 404s: a visitor authorize turns\n * away, no tenant or another's, a resource the panel does not have, a record\n * that is not there - or not this tenant's. The pages check again, inside\n * their boundaries, where a refusal can no longer change the status.\n */\n async function middleware(params: Record<string, string | undefined> = {}) {\n if (!(await authorize())) notFound()\n\n const context = (await contextOf(handle)) ?? notFound()\n\n if (params.resource === undefined) return\n\n const resource = (await resourcesFor(handle, context)).find((candidate) => candidate.name === params.resource) ?? notFound()\n\n // An edit page changes a record. A read-only resource's new page cannot\n // be told apart here - it has the list's params - so it refuses itself.\n if (resource.readOnly && params.id !== undefined) notFound()\n\n if (params.id !== undefined && !(await resource.source.find(params.id, context))) notFound()\n }\n\n /** The sidebar's header: the tenant switcher, or the panel's name. */\n async function Tenants() {\n await connection()\n\n const context = (await authorize()) && tenancy ? await contextOf(handle) : null\n\n if (!tenancy || !context?.tenant) return brand()\n\n return (\n <theme.TenantSwitcher\n tenants={await tenancy.tenants()}\n current={context.tenant}\n label={tenancy.label ?? 'Teams'}\n choose={switchTenant.bind(null, path)}\n />\n )\n }\n\n function brand() {\n const Logo = definition.logo\n\n return <theme.Brand href={path} title={title} {...(Logo ? { logo: <Logo /> } : {})} />\n }\n\n async function User() {\n await connection()\n\n const user = (await authorize()) ? await definition.user?.() : null\n\n if (!user) return null\n\n return <theme.UserMenu user={user} {...(definition.signOut ? { signOut: signOut.bind(null, path) } : {})} />\n }\n\n /** A resource's own icon, drawn; the theme draws a default for one without. */\n function iconOf(resource: Resource) {\n const Icon = resource.icon\n\n return Icon ? { icon: <Icon /> } : {}\n }\n\n /** The sidebar's links: the panel's resources, then the tenant's installed ones, then Plugins. */\n function navOf(shown: Resource[]): NavItem[] {\n return [\n { href: path, label: 'Dashboard', kind: 'dashboard', exact: true },\n ...shown.map((resource): NavItem => ({\n href: to(resource.name),\n label: resource.label,\n kind: own.has(resource.name) ? 'resource' : 'installed',\n ...iconOf(resource),\n })),\n ...(installs ? [{ href: to('plugins'), label: 'Plugins', kind: 'plugins' } as const] : []),\n ]\n }\n\n /** Installed resources are this tenant's and this request's, so the nav waits for them; the panel's own show meanwhile. */\n async function InstalledNav() {\n await connection()\n\n const context = (await authorize()) ? await contextOf(handle) : null\n\n return <theme.Nav items={navOf(context ? await resourcesFor(handle, context) : resources)} />\n }\n\n function crumbsOf(shown: Resource[]) {\n return (\n <theme.Breadcrumbs\n root={path}\n title={title}\n labels={{ ...(installs ? { plugins: 'Plugins' } : {}), ...Object.fromEntries(shown.map((resource) => [resource.name, resource.label])) }}\n />\n )\n }\n\n async function InstalledCrumbs() {\n await connection()\n\n const context = (await authorize()) ? await contextOf(handle) : null\n\n return crumbsOf(context ? await resourcesFor(handle, context) : resources)\n }\n\n function Layout({ children }: { children: ReactNode }) {\n return (\n <theme.Shell\n title={title}\n modeToggle={definition.modeToggle !== false}\n brand={\n tenancy ? (\n <Suspense fallback={<theme.Loading kind=\"menu\" />}>\n <Tenants />\n </Suspense>\n ) : (\n brand()\n )\n }\n nav={\n installs ? (\n <Suspense fallback={<theme.Nav items={navOf(resources)} />}>\n <InstalledNav />\n </Suspense>\n ) : (\n <theme.Nav items={navOf(resources)} />\n )\n }\n user={\n definition.user ? (\n <Suspense fallback={<theme.Loading kind=\"menu\" />}>\n <User />\n </Suspense>\n ) : null\n }\n crumbs={\n installs ? (\n <Suspense fallback={crumbsOf(resources)}>\n <InstalledCrumbs />\n </Suspense>\n ) : (\n crumbsOf(resources)\n )\n }\n >\n {children}\n </theme.Shell>\n )\n }\n\n async function DashboardBody() {\n const context = await enter()\n const counts = await Promise.all(\n (await resourcesFor(handle, context)).map(async (resource) => {\n // An installed plugin is somebody else's server: one that is down\n // leaves its card blank, not the dashboard broken.\n const total = await resource.source\n .list({ dir: 'asc', page: 1, perPage: 1, filters: {}, searchable: [] }, context)\n .then(({ total: counted }) => counted as number | null)\n .catch((error: unknown) => {\n console.error(`[admin] counting ${resource.name} failed`, error)\n\n return null\n })\n\n return { name: resource.name, label: resource.label, href: to(resource.name), total, ...iconOf(resource) }\n }),\n )\n\n return (\n <theme.Dashboard\n description={context.tenant ? `Everything in ${context.tenant.name}.` : 'Everything in the panel, at a glance.'}\n counts={counts}\n widgets={plugins.flatMap((plugin) =>\n (plugin.widgets ?? []).map(({ title: widgetTitle, description, component: Widget }, index) => ({\n key: `${plugin.name}:${index}`,\n title: widgetTitle,\n ...(description ? { description } : {}),\n content: <Widget tenant={context.tenant} />,\n })),\n )}\n />\n )\n }\n\n /**\n * The table, as a section: a delete refreshes it and nothing else. A\n * refresh renders it with the page's params and searchParams, and nothing\n * else - which is why it is bound to the panel here rather than handed one\n * as a prop.\n */\n async function TableBody({ params, searchParams }: Props<{ resource: string }, ListSearch>) {\n const { resource, context } = await enter((await params).resource)\n const search = await searchParams\n const { q, sort, dir, page, cursor, ...rest } = search\n const filters: Record<string, string> = Object.fromEntries(\n Object.entries(rest).flatMap(([key, value]) =>\n typeof value === 'string' && value !== '' && resource.filters.some((filter) => filter.key === key) ? [[key, value]] : [],\n ),\n )\n const sortable = resource.columns.some((column) => column.sortable && column.key === sort) ? sort : undefined\n const server = resource.mode === 'server'\n const searchable = resource.columns.filter((column) => column.searchable).map((column) => column.key)\n const listed = await resource.source.list(\n server\n ? {\n ...(q ? { q } : {}),\n ...(sortable ? { sort: sortable } : {}),\n dir,\n page,\n ...(cursor ? { cursor } : {}),\n perPage: resource.perPage,\n filters,\n searchable,\n }\n : { dir: 'asc', page: 1, perPage: null, filters: {}, searchable },\n context,\n )\n const { rows } = listed\n const total = typeof listed.total === 'number' ? listed.total : null\n // A page of a list paged by cursor: the pager asks for the cursor it was\n // handed, with the search, filters and sort the visitor is on.\n const cursorUrl = (at: string | null | undefined) =>\n at ? cursorHref(to(resource.name), { ...(q ? { q } : {}), ...(sortable ? { sort: sortable } : {}), dir, filters }, at) : null\n const cursors = server && 'next' in listed ? { prev: cursorUrl(listed.prev), next: cursorUrl(listed.next) } : undefined\n\n // A relation column shows the related row's title, and is searched and\n // sorted by it in the browser too: what the visitor reads is what they type.\n const of = choices(context, await resourcesFor(handle, context))\n const titles = new Map(\n await Promise.all(\n resource.columns\n .filter((column) => column.relation !== undefined)\n .map(async (column) => [column.key, new Map((await of(column.relation!)).map((option) => [option.value, option.label]))] as const),\n ),\n )\n const shown = (column: Column, value: unknown) => {\n const title = value === null || value === undefined ? undefined : titles.get(column.key)?.get(String(value))\n\n return title ?? value\n }\n\n const drawn: TableRowData[] = await Promise.all(\n rows.map(async (row) => {\n const id = String(row[resource.key])\n\n return {\n id,\n cells: Object.fromEntries(\n await Promise.all(resource.columns.map(async (column) => [column.key, await draw(theme, column, shown(column, row[column.key]), row)] as const)),\n ),\n values: Object.fromEntries(resource.columns.map((column) => [column.key, plain(shown(column, row[column.key]))])),\n ...(resource.readOnly\n ? {}\n : { edit: to(resource.name, encodeURIComponent(id)), remove: deleteRecord.bind(null, path, resource.name, id) }),\n }\n }),\n )\n\n return (\n <theme.Table\n mode={resource.mode}\n href={to(resource.name)}\n columns={resource.columns.map(({ key, label, sortable: canSort, searchable: canSearch, visible }) => ({\n key,\n label,\n sortable: canSort,\n searchable: canSearch,\n visible,\n }))}\n rows={drawn}\n total={total}\n {...(cursors ? { cursors } : {})}\n perPage={resource.perPage}\n query={{ q: q ?? '', ...(sortable ? { sort: sortable } : {}), dir, page, filters }}\n filters={resource.filters}\n noun={{ one: resource.singular, many: resource.label }}\n {...(resource.readOnly\n ? {}\n : {\n bulk: {\n actions: [\n ...resource.bulkActions.map(({ name, label, destructive }) => ({ name, label, destructive: destructive ?? false })),\n { name: 'delete', label: 'Delete', destructive: true },\n ],\n run: bulkRecords.bind(null, path, resource.name),\n },\n })}\n />\n )\n }\n\n const Table = section('admin-table', TableBody)\n\n async function IndexBody({ params, searchParams }: Props<{ resource: string }, ListSearch>) {\n const { resource } = await enter((await params).resource)\n\n return (\n <theme.Index\n title={resource.label}\n {...(resource.description ? { description: resource.description } : {})}\n {...(resource.readOnly ? {} : { create: { href: to(resource.name, 'new'), label: `New ${resource.singular.toLowerCase()}` } })}\n table={<Table params={params} searchParams={searchParams} />}\n />\n )\n }\n\n async function CreateBody({ params }: Props<{ resource: string }>) {\n const { resource, context } = await enter((await params).resource)\n\n if (resource.readOnly) notFound()\n\n return (\n <theme.Form\n title={`New ${resource.singular.toLowerCase()}`}\n description={`Add a ${resource.singular.toLowerCase()} to ${resource.label.toLowerCase()}.`}\n action={saveRecord.bind(null, path, resource.name, null)}\n fields={await fieldsFor(resource, context)}\n values={{}}\n submit={`Create ${resource.singular.toLowerCase()}`}\n cancel={to(resource.name)}\n />\n )\n }\n\n async function EditBody({ params }: Props<{ resource: string; id: string }>) {\n const { resource: name, id } = await params\n const { resource, context } = await enter(name)\n const record = resource.readOnly ? null : await resource.source.find(id, context)\n\n if (!record) notFound()\n\n return (\n <theme.Form\n title={String(record[resource.title])}\n description={`Edit this ${resource.singular.toLowerCase()}.`}\n action={saveRecord.bind(null, path, resource.name, id)}\n fields={await fieldsFor(resource, context, true)}\n values={record}\n submit=\"Save changes\"\n cancel={to(resource.name)}\n />\n )\n }\n\n /** The installed plugins, a manifest under review, and the form to start one. */\n async function PluginsBody({ searchParams }: { searchParams: Promise<PluginsSearch> }) {\n const context = await enter()\n\n if (!installs) notFound()\n\n const allowed = !installs.manage || (await installs.manage())\n const { review } = await searchParams\n const installed = await installs.store.list(context.tenant)\n const labelOf = (name: string) => resources.find((resource) => resource.name === name)?.label ?? name\n let reviewing: PluginsProps['reviewing'] = null\n\n // Once installed, the review is over: the install re-renders this url in place, ?review= and all.\n if (review && allowed && !installed.some((installation) => installation.manifestUrl === review)) {\n try {\n const manifest = await fetchManifest(review, allowOf(handle), resources.map((resource) => resource.name))\n\n reviewing = {\n label: manifest.label,\n ...(manifest.version ? { version: manifest.version } : {}),\n description: manifest.description ?? new URL(manifest.endpoint).host,\n adds: manifest.resources.map((resource) => resource.label ?? humanize(resource.name)),\n can: manifest.scopes.length === 0 ? [] : scopeWords(manifest, labelOf),\n endpoint: manifest.endpoint,\n install: installPlugin.bind(null, path, review, await digestOf(manifest)),\n cancel: to('plugins'),\n }\n } catch (error) {\n reviewing = { problem: error instanceof Error ? error.message : 'The manifest could not be read.' }\n }\n }\n\n return (\n <theme.Plugins\n description={`Installed for ${context.tenant ? context.tenant.name : 'this panel'}. A plugin runs on its own server; it sees what you grant it here and nothing else.`}\n allowed={allowed}\n review={reviewPlugin.bind(null, path)}\n reviewing={reviewing}\n installed={installed.map((installation) => ({\n id: installation.id,\n label: installation.manifest.label,\n description: installation.manifest.description ?? new URL(installation.manifest.endpoint).host,\n resources: installation.manifest.resources.map((resource) => resource.label ?? humanize(resource.name)),\n scopes: installation.manifest.scopes,\n ...(allowed ? { uninstall: uninstallPlugin.bind(null, path, installation.id) } : {}),\n ...(allowed && !installation.secretSeen\n ? { secret: { installation: installation.id, secret: installation.secret, copied: secretCopied.bind(null, path, installation.id) } }\n : {}),\n }))}\n />\n )\n }\n\n // The pages read their url above everything they show, so each waits inside\n // a boundary of its own: the build stores the theme's Loading as the page's\n // shell, and a page that awaited params above every boundary would fail it.\n // A panel cannot put a loading.tsx beside the app's route files; this is that.\n function Dashboard() {\n return (\n <Suspense fallback={<theme.Loading kind=\"dashboard\" />}>\n <DashboardBody />\n </Suspense>\n )\n }\n\n function Index(props: Props<{ resource: string }, ListSearch>) {\n return (\n <Suspense fallback={<theme.Loading kind=\"table\" />}>\n <IndexBody {...props} />\n </Suspense>\n )\n }\n\n function Create(props: Props<{ resource: string }>) {\n return (\n <Suspense fallback={<theme.Loading kind=\"form\" />}>\n <CreateBody {...props} />\n </Suspense>\n )\n }\n\n function Edit(props: Props<{ resource: string; id: string }>) {\n return (\n <Suspense fallback={<theme.Loading kind=\"form\" />}>\n <EditBody {...props} />\n </Suspense>\n )\n }\n\n function Plugins(props: { searchParams: Promise<PluginsSearch> }) {\n return (\n <Suspense fallback={<theme.Loading kind=\"form\" />}>\n <PluginsBody {...props} />\n </Suspense>\n )\n }\n\n return { path, resources, middleware, Layout, Dashboard, Index, Table, Create, Edit, Plugins }\n}\n\nexport type Panel = ReturnType<typeof definePanel>\n\n/** A cell: the column's own cell(), the theme's drawing of its format, or the value as text. */\nasync function draw(theme: AdminTheme, column: Column, value: unknown, row: unknown): Promise<ReactNode> {\n if (column.cell) return column.cell(value, row)\n if (column.format === 'text' || column.format === 'relation') return String(value ?? '')\n\n return <theme.Cell format={column.format} value={value} />\n}\n\n/** A value the browser can sort and search by. */\nfunction plain(value: unknown): string | number | boolean | null {\n if (value === null || value === undefined) return null\n if (value instanceof Date) return value.toISOString()\n if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'string') return value\n\n return String(value)\n}\n"]}
|
|
1
|
+
{"version":3,"file":"panel.js","sourceRoot":"","sources":["../../src/admin/panel.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Suspense } from 'react'\nimport type { ComponentType, ReactNode } from 'react'\nimport { notFound } from '@rsc-kit/core/not-found'\nimport { connection } from '@rsc-kit/core/request'\nimport { section } from '@rsc-kit/core/section'\nimport {\n bulkRecords,\n deleteRecord,\n installPlugin,\n reviewPlugin,\n saveRecord,\n secretCopied,\n signOut,\n switchTenant,\n uninstallPlugin,\n} from './actions.js'\nimport { digestOf, fetchManifest, RESERVED, scopeWords } from '../installable.js'\nimport type { Installs } from '../installable.js'\nimport type { PanelPlugin } from '../plugin.js'\nimport { allowOf, contextOf, registerPanel, resourcesFor } from './registry.js'\nimport type { PanelHandle, Tenancy } from './registry.js'\nimport { humanize } from '../resource.js'\nimport type { Column, Field, Option, Resource, SourceContext } from '../resource.js'\nimport type { AdminTheme, NavItem, PanelUser, PluginsProps, TableRowData } from './theme.js'\nimport type { ListSearch, PluginsSearch } from './url.js'\n\nimport { cursorHref } from './url.js'\nimport { valuesFor } from '../forms/blueprint.js'\nexport { listSearch, pluginsSearch, recordParams, resourceParams } from './url.js'\n\nexport interface PanelDefinition {\n /** Where the panel's routes are: the folder under src/app it is mounted in. */\n path?: string\n title?: string\n /** The sidebar's mark beside the title, in a panel without tenants. */\n logo?: ComponentType<{ className?: string }>\n resources: Resource[]\n /**\n * What draws the panel: import { shadcn } from '@rsc-kit/press/shadcn', or\n * any object that fulfils AdminTheme. The panel decides what is shown and\n * who may see it; the theme only draws.\n */\n theme: AdminTheme\n /**\n * Whether this request may use the panel. Runs on every page and on every\n * save, delete and switch - an action is reachable without the page that\n * rendered it, so a guard on the pages alone would not be one. A refusal is\n * a 404. The panel has no login of its own: this asks the app's.\n */\n authorize?: () => boolean | Promise<boolean>\n /** Who is signed in, for the sidebar's footer. */\n user?: () => PanelUser | null | Promise<PanelUser | null>\n /** Sign the visitor out. Runs in a server action; the visitor lands on /. */\n signOut?: () => void | Promise<void>\n /** Whose records the panel shows: a switcher in the sidebar, and a tenant on every data source call. */\n tenancy?: Tenancy\n /** Packages that add resources, dashboard widgets and reactions to changes. */\n plugins?: PanelPlugin[]\n /**\n * Plugins a person installs from the panel while it runs, with no rebuild:\n * a manifest url, reviewed, then installed for the current tenant. Mounted\n * as src/app/admin/plugins/page.tsx. See installable.ts for what they can\n * and cannot reach.\n */\n installs?: Installs\n /** The light/dark switch in the header. On unless the app has its own. */\n modeToggle?: boolean\n}\n\n/** A type, not an interface: section() takes props that are a record, and an interface has no index signature. */\ntype Props<P, S = never> = {\n params: Promise<P>\n searchParams: Promise<S>\n}\n\n/**\n * A panel: its resources, and the pages that show them, already bound to it.\n * The app mounts each in a route file of its own, because the engine finds\n * routes, url schemas and sections by their files:\n *\n * src/app/admin/middleware.ts export default panel.middleware\n * src/app/admin/layout.tsx export default panel.Layout\n * src/app/admin/page.tsx export default panel.Dashboard\n * src/app/admin/[resource]/page.tsx export const params = resourceParams\n * export const searchParams = listSearch\n * export default panel.Index\n * src/app/admin/[resource]/admin-table.section.tsx export default panel.Table\n * src/app/admin/[resource]/new/page.tsx export const params = resourceParams\n * export default panel.Create\n * src/app/admin/[resource]/[id]/page.tsx export const params = recordParams\n * export default panel.Edit\n */\nexport function definePanel(definition: PanelDefinition) {\n const path = definition.path ?? '/admin'\n const title = definition.title ?? 'Admin'\n const plugins = definition.plugins ?? []\n const resources = [...definition.resources, ...plugins.flatMap((plugin) => plugin.resources ?? [])]\n const authorize = definition.authorize ?? (() => true)\n const tenancy = definition.tenancy ?? null\n const installs = definition.installs ?? null\n const theme = definition.theme\n /** The panel's own resources and its plugins'; any other is one a tenant installed. */\n const own = new Set(resources.map((resource) => resource.name))\n\n if (!/^(\\/[a-z0-9-]+)+$/.test(path)) {\n throw new Error(`[admin] a panel's path is where its routes are, with no trailing slash: ${JSON.stringify(path)}`)\n }\n\n const names = resources.map((resource) => resource.name)\n const twice = names.find((name, index) => names.indexOf(name) !== index)\n\n if (twice) throw new Error(`[admin] two resources are named ${twice}; a name is a url segment`)\n\n const reserved = installs ? names.find((name) => RESERVED.includes(name)) : undefined\n\n if (reserved) throw new Error(`[admin] a panel that takes installs has a plugins page, so no resource may be named ${reserved}`)\n\n const pluginNames = plugins.map((plugin) => plugin.name)\n const pluginTwice = pluginNames.find((name, index) => pluginNames.indexOf(name) !== index)\n\n if (pluginTwice) throw new Error(`[admin] the plugin ${pluginTwice} is installed twice`)\n\n // A relation names a resource by its url segment. Checked here, once every\n // resource is known, so a misspelt one fails at startup and not on the\n // first form that needs its choices.\n for (const resource of resources) {\n const named = [...resource.relations, ...resource.columns.map((column) => column.relation)]\n\n for (const relation of named) {\n if (relation !== undefined && !names.includes(relation)) {\n throw new Error(`[admin] ${resource.name} relates to ${relation}, which is not a resource of this panel`)\n }\n }\n }\n\n const handle: PanelHandle = { path, resources, authorize, plugins, tenancy, signOut: definition.signOut ?? null, installs }\n\n registerPanel(handle)\n\n /** A url of the panel. Typed routes are the app's; the panel's are its own. */\n const to = (...parts: string[]) => [path, ...parts].join('/')\n\n /**\n * A related resource's rows as choices: its key saved, its title shown.\n * Every row, scoped to the visitor's tenant like any list - so a belongsTo\n * is for a resource with a select's worth of rows: categories, teams,\n * plans. Memoised per render, so a field and a column naming the same\n * resource list it once.\n */\n function choices(context: SourceContext, resources: Resource[]) {\n const listed = new Map<string, Promise<Option[]>>()\n\n return (name: string): Promise<Option[]> => {\n let found = listed.get(name)\n\n if (!found) {\n const related = resources.find((resource) => resource.name === name)!\n\n found = related.source\n .list({ dir: 'asc', page: 1, perPage: null, filters: {}, searchable: [] }, context)\n .then(({ rows }) => rows.map((row) => ({ value: String(row[related.key]), label: String(row[related.title]) })))\n listed.set(name, found)\n }\n\n return found\n }\n }\n\n /** The form's fields, each relation given its choices: the create form's, or the edit form's. */\n async function fieldsFor(resource: Resource, context: SourceContext, editing = false): Promise<Field[]> {\n const of = choices(context, await resourcesFor(handle, context))\n\n return Promise.all((editing ? resource.editFields : resource.fields).map(async (field) => (field.relation ? { ...field, options: await of(field.relation) } : field)))\n }\n\n /**\n * The panel's door: a refusal is a 404, so the panel's existence is not\n * shown. Per request, always - authorize is about this visitor and the rows\n * are live - so the build stores the skeleton and never runs either.\n */\n async function enter(): Promise<SourceContext>\n async function enter(name: string): Promise<{ resource: Resource; context: SourceContext }>\n async function enter(name?: string) {\n await connection()\n\n if (!(await authorize())) notFound()\n\n const context = (await contextOf(handle)) ?? notFound()\n\n if (name === undefined) return context\n\n return { resource: (await resourcesFor(handle, context)).find((resource) => resource.name === name) ?? notFound(), context }\n }\n\n /**\n * The panel's guard, mounted as src/app/admin/middleware.ts. It runs before\n * anything is sent, so its refusals are real 404s: a visitor authorize turns\n * away, no tenant or another's, a resource the panel does not have, a record\n * that is not there - or not this tenant's. The pages check again, inside\n * their boundaries, where a refusal can no longer change the status.\n */\n async function middleware(params: Record<string, string | undefined> = {}) {\n if (!(await authorize())) notFound()\n\n const context = (await contextOf(handle)) ?? notFound()\n\n if (params.resource === undefined) return\n\n const resource = (await resourcesFor(handle, context)).find((candidate) => candidate.name === params.resource) ?? notFound()\n\n // An edit page changes a record. A read-only resource's new page cannot\n // be told apart here - it has the list's params - so it refuses itself.\n if (resource.readOnly && params.id !== undefined) notFound()\n\n if (params.id !== undefined && !(await resource.source.find(params.id, context))) notFound()\n }\n\n /** The sidebar's header: the tenant switcher, or the panel's name. */\n async function Tenants() {\n await connection()\n\n const context = (await authorize()) && tenancy ? await contextOf(handle) : null\n\n if (!tenancy || !context?.tenant) return brand()\n\n return (\n <theme.TenantSwitcher\n tenants={await tenancy.tenants()}\n current={context.tenant}\n label={tenancy.label ?? 'Teams'}\n choose={switchTenant.bind(null, path)}\n />\n )\n }\n\n function brand() {\n const Logo = definition.logo\n\n return <theme.Brand href={path} title={title} {...(Logo ? { logo: <Logo /> } : {})} />\n }\n\n async function User() {\n await connection()\n\n const user = (await authorize()) ? await definition.user?.() : null\n\n if (!user) return null\n\n return <theme.UserMenu user={user} {...(definition.signOut ? { signOut: signOut.bind(null, path) } : {})} />\n }\n\n /** A resource's own icon, drawn; the theme draws a default for one without. */\n function iconOf(resource: Resource) {\n const Icon = resource.icon\n\n return Icon ? { icon: <Icon /> } : {}\n }\n\n /** The sidebar's links: the panel's resources, then the tenant's installed ones, then Plugins. */\n function navOf(shown: Resource[]): NavItem[] {\n return [\n { href: path, label: 'Dashboard', kind: 'dashboard', exact: true },\n ...shown.map((resource): NavItem => ({\n href: to(resource.name),\n label: resource.label,\n kind: own.has(resource.name) ? 'resource' : 'installed',\n ...iconOf(resource),\n })),\n ...(installs ? [{ href: to('plugins'), label: 'Plugins', kind: 'plugins' } as const] : []),\n ]\n }\n\n /** Installed resources are this tenant's and this request's, so the nav waits for them; the panel's own show meanwhile. */\n async function InstalledNav() {\n await connection()\n\n const context = (await authorize()) ? await contextOf(handle) : null\n\n return <theme.Nav items={navOf(context ? await resourcesFor(handle, context) : resources)} />\n }\n\n function crumbsOf(shown: Resource[]) {\n return (\n <theme.Breadcrumbs\n root={path}\n title={title}\n labels={{ ...(installs ? { plugins: 'Plugins' } : {}), ...Object.fromEntries(shown.map((resource) => [resource.name, resource.label])) }}\n />\n )\n }\n\n async function InstalledCrumbs() {\n await connection()\n\n const context = (await authorize()) ? await contextOf(handle) : null\n\n return crumbsOf(context ? await resourcesFor(handle, context) : resources)\n }\n\n function Layout({ children }: { children: ReactNode }) {\n return (\n <theme.Shell\n title={title}\n modeToggle={definition.modeToggle !== false}\n brand={\n tenancy ? (\n <Suspense fallback={<theme.Loading kind=\"menu\" />}>\n <Tenants />\n </Suspense>\n ) : (\n brand()\n )\n }\n nav={\n installs ? (\n <Suspense fallback={<theme.Nav items={navOf(resources)} />}>\n <InstalledNav />\n </Suspense>\n ) : (\n <theme.Nav items={navOf(resources)} />\n )\n }\n user={\n definition.user ? (\n <Suspense fallback={<theme.Loading kind=\"menu\" />}>\n <User />\n </Suspense>\n ) : null\n }\n crumbs={\n installs ? (\n <Suspense fallback={crumbsOf(resources)}>\n <InstalledCrumbs />\n </Suspense>\n ) : (\n crumbsOf(resources)\n )\n }\n >\n {children}\n </theme.Shell>\n )\n }\n\n async function DashboardBody() {\n const context = await enter()\n const counts = await Promise.all(\n (await resourcesFor(handle, context)).map(async (resource) => {\n // An installed plugin is somebody else's server: one that is down\n // leaves its card blank, not the dashboard broken.\n const total = await resource.source\n .list({ dir: 'asc', page: 1, perPage: 1, filters: {}, searchable: [] }, context)\n .then(({ total: counted }) => counted as number | null)\n .catch((error: unknown) => {\n console.error(`[admin] counting ${resource.name} failed`, error)\n\n return null\n })\n\n return { name: resource.name, label: resource.label, href: to(resource.name), total, ...iconOf(resource) }\n }),\n )\n\n return (\n <theme.Dashboard\n description={context.tenant ? `Everything in ${context.tenant.name}.` : 'Everything in the panel, at a glance.'}\n counts={counts}\n widgets={plugins.flatMap((plugin) =>\n (plugin.widgets ?? []).map(({ title: widgetTitle, description, component: Widget }, index) => ({\n key: `${plugin.name}:${index}`,\n title: widgetTitle,\n ...(description ? { description } : {}),\n content: <Widget tenant={context.tenant} />,\n })),\n )}\n />\n )\n }\n\n /**\n * The table, as a section: a delete refreshes it and nothing else. A\n * refresh renders it with the page's params and searchParams, and nothing\n * else - which is why it is bound to the panel here rather than handed one\n * as a prop.\n */\n async function TableBody({ params, searchParams }: Props<{ resource: string }, ListSearch>) {\n const { resource, context } = await enter((await params).resource)\n const search = await searchParams\n const { q, sort, dir, page, cursor, ...rest } = search\n const filters: Record<string, string> = Object.fromEntries(\n Object.entries(rest).flatMap(([key, value]) =>\n typeof value === 'string' && value !== '' && resource.filters.some((filter) => filter.key === key) ? [[key, value]] : [],\n ),\n )\n const sortable = resource.columns.some((column) => column.sortable && column.key === sort) ? sort : undefined\n const server = resource.mode === 'server'\n const searchable = resource.columns.filter((column) => column.searchable).map((column) => column.key)\n const listed = await resource.source.list(\n server\n ? {\n ...(q ? { q } : {}),\n ...(sortable ? { sort: sortable } : {}),\n dir,\n page,\n ...(cursor ? { cursor } : {}),\n perPage: resource.perPage,\n filters,\n searchable,\n }\n : { dir: 'asc', page: 1, perPage: null, filters: {}, searchable },\n context,\n )\n const { rows } = listed\n const total = typeof listed.total === 'number' ? listed.total : null\n // A page of a list paged by cursor: the pager asks for the cursor it was\n // handed, with the search, filters and sort the visitor is on.\n const cursorUrl = (at: string | null | undefined) =>\n at ? cursorHref(to(resource.name), { ...(q ? { q } : {}), ...(sortable ? { sort: sortable } : {}), dir, filters }, at) : null\n const cursors = server && 'next' in listed ? { prev: cursorUrl(listed.prev), next: cursorUrl(listed.next) } : undefined\n\n // A relation column shows the related row's title, and is searched and\n // sorted by it in the browser too: what the visitor reads is what they type.\n const of = choices(context, await resourcesFor(handle, context))\n const titles = new Map(\n await Promise.all(\n resource.columns\n .filter((column) => column.relation !== undefined)\n .map(async (column) => [column.key, new Map((await of(column.relation!)).map((option) => [option.value, option.label]))] as const),\n ),\n )\n const shown = (column: Column, value: unknown) => {\n const title = value === null || value === undefined ? undefined : titles.get(column.key)?.get(String(value))\n\n return title ?? value\n }\n\n const drawn: TableRowData[] = await Promise.all(\n rows.map(async (row) => {\n const id = String(row[resource.key])\n\n return {\n id,\n cells: Object.fromEntries(\n await Promise.all(resource.columns.map(async (column) => [column.key, await draw(theme, column, shown(column, row[column.key]), row)] as const)),\n ),\n values: Object.fromEntries(resource.columns.map((column) => [column.key, plain(shown(column, row[column.key]))])),\n ...(resource.readOnly\n ? {}\n : { edit: to(resource.name, encodeURIComponent(id)), remove: deleteRecord.bind(null, path, resource.name, id) }),\n }\n }),\n )\n\n return (\n <theme.Table\n mode={resource.mode}\n href={to(resource.name)}\n columns={resource.columns.map(({ key, label, sortable: canSort, searchable: canSearch, visible }) => ({\n key,\n label,\n sortable: canSort,\n searchable: canSearch,\n visible,\n }))}\n rows={drawn}\n total={total}\n {...(cursors ? { cursors } : {})}\n perPage={resource.perPage}\n query={{ q: q ?? '', ...(sortable ? { sort: sortable } : {}), dir, page, filters }}\n filters={resource.filters}\n noun={{ one: resource.singular, many: resource.label }}\n {...(resource.readOnly\n ? {}\n : {\n bulk: {\n actions: [\n ...resource.bulkActions.map(({ name, label, destructive }) => ({ name, label, destructive: destructive ?? false })),\n { name: 'delete', label: 'Delete', destructive: true },\n ],\n run: bulkRecords.bind(null, path, resource.name),\n },\n })}\n />\n )\n }\n\n const Table = section('admin-table', TableBody)\n\n async function IndexBody({ params, searchParams }: Props<{ resource: string }, ListSearch>) {\n const { resource } = await enter((await params).resource)\n\n return (\n <theme.Index\n title={resource.label}\n {...(resource.description ? { description: resource.description } : {})}\n {...(resource.readOnly ? {} : { create: { href: to(resource.name, 'new'), label: `New ${resource.singular.toLowerCase()}` } })}\n table={<Table params={params} searchParams={searchParams} />}\n />\n )\n }\n\n async function CreateBody({ params }: Props<{ resource: string }>) {\n const { resource, context } = await enter((await params).resource)\n\n if (resource.readOnly) notFound()\n\n return (\n <theme.Form\n title={`New ${resource.singular.toLowerCase()}`}\n description={`Add a ${resource.singular.toLowerCase()} to ${resource.label.toLowerCase()}.`}\n action={saveRecord.bind(null, path, resource.name, null)}\n fields={await fieldsFor(resource, context)}\n values={{}}\n submit={`Create ${resource.singular.toLowerCase()}`}\n cancel={to(resource.name)}\n />\n )\n }\n\n async function EditBody({ params }: Props<{ resource: string; id: string }>) {\n const { resource: name, id } = await params\n const { resource, context } = await enter(name)\n const record = resource.readOnly ? null : await resource.source.find(id, context)\n\n if (!record) notFound()\n\n // Only the form's fields reach the browser, not every column the record has.\n const editFields = await fieldsFor(resource, context, true)\n\n return (\n <theme.Form\n title={String(record[resource.title])}\n description={`Edit this ${resource.singular.toLowerCase()}.`}\n action={saveRecord.bind(null, path, resource.name, id)}\n fields={editFields}\n values={valuesFor(editFields, record)}\n submit=\"Save changes\"\n cancel={to(resource.name)}\n />\n )\n }\n\n /** The installed plugins, a manifest under review, and the form to start one. */\n async function PluginsBody({ searchParams }: { searchParams: Promise<PluginsSearch> }) {\n const context = await enter()\n\n if (!installs) notFound()\n\n const allowed = !installs.manage || (await installs.manage())\n const { review } = await searchParams\n const installed = await installs.store.list(context.tenant)\n const labelOf = (name: string) => resources.find((resource) => resource.name === name)?.label ?? name\n let reviewing: PluginsProps['reviewing'] = null\n\n // Once installed, the review is over: the install re-renders this url in place, ?review= and all.\n if (review && allowed && !installed.some((installation) => installation.manifestUrl === review)) {\n try {\n const manifest = await fetchManifest(review, allowOf(handle), resources.map((resource) => resource.name))\n\n reviewing = {\n label: manifest.label,\n ...(manifest.version ? { version: manifest.version } : {}),\n description: manifest.description ?? new URL(manifest.endpoint).host,\n adds: manifest.resources.map((resource) => resource.label ?? humanize(resource.name)),\n can: manifest.scopes.length === 0 ? [] : scopeWords(manifest, labelOf),\n endpoint: manifest.endpoint,\n install: installPlugin.bind(null, path, review, await digestOf(manifest)),\n cancel: to('plugins'),\n }\n } catch (error) {\n reviewing = { problem: error instanceof Error ? error.message : 'The manifest could not be read.' }\n }\n }\n\n return (\n <theme.Plugins\n description={`Installed for ${context.tenant ? context.tenant.name : 'this panel'}. A plugin runs on its own server; it sees what you grant it here and nothing else.`}\n allowed={allowed}\n review={reviewPlugin.bind(null, path)}\n reviewing={reviewing}\n installed={installed.map((installation) => ({\n id: installation.id,\n label: installation.manifest.label,\n description: installation.manifest.description ?? new URL(installation.manifest.endpoint).host,\n resources: installation.manifest.resources.map((resource) => resource.label ?? humanize(resource.name)),\n scopes: installation.manifest.scopes,\n ...(allowed ? { uninstall: uninstallPlugin.bind(null, path, installation.id) } : {}),\n ...(allowed && !installation.secretSeen\n ? { secret: { installation: installation.id, secret: installation.secret, copied: secretCopied.bind(null, path, installation.id) } }\n : {}),\n }))}\n />\n )\n }\n\n // The pages read their url above everything they show, so each waits inside\n // a boundary of its own: the build stores the theme's Loading as the page's\n // shell, and a page that awaited params above every boundary would fail it.\n // A panel cannot put a loading.tsx beside the app's route files; this is that.\n function Dashboard() {\n return (\n <Suspense fallback={<theme.Loading kind=\"dashboard\" />}>\n <DashboardBody />\n </Suspense>\n )\n }\n\n function Index(props: Props<{ resource: string }, ListSearch>) {\n return (\n <Suspense fallback={<theme.Loading kind=\"table\" />}>\n <IndexBody {...props} />\n </Suspense>\n )\n }\n\n function Create(props: Props<{ resource: string }>) {\n return (\n <Suspense fallback={<theme.Loading kind=\"form\" />}>\n <CreateBody {...props} />\n </Suspense>\n )\n }\n\n function Edit(props: Props<{ resource: string; id: string }>) {\n return (\n <Suspense fallback={<theme.Loading kind=\"form\" />}>\n <EditBody {...props} />\n </Suspense>\n )\n }\n\n function Plugins(props: { searchParams: Promise<PluginsSearch> }) {\n return (\n <Suspense fallback={<theme.Loading kind=\"form\" />}>\n <PluginsBody {...props} />\n </Suspense>\n )\n }\n\n return { path, resources, middleware, Layout, Dashboard, Index, Table, Create, Edit, Plugins }\n}\n\nexport type Panel = ReturnType<typeof definePanel>\n\n/** A cell: the column's own cell(), the theme's drawing of its format, or the value as text. */\nasync function draw(theme: AdminTheme, column: Column, value: unknown, row: unknown): Promise<ReactNode> {\n if (column.cell) return column.cell(value, row)\n if (column.format === 'text' || column.format === 'relation') return String(value ?? '')\n\n return <theme.Cell format={column.format} value={value} />\n}\n\n/** A value the browser can sort and search by. */\nfunction plain(value: unknown): string | number | boolean | null {\n if (value === null || value === undefined) return null\n if (value instanceof Date) return value.toISOString()\n if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'string') return value\n\n return String(value)\n}\n"]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What makes a field depend on another, as plain data: the browser applies it
|
|
3
|
+
* while the visitor types, and the server applies it again to what was sent,
|
|
4
|
+
* so the two cannot disagree. No functions - a field crosses to the browser.
|
|
5
|
+
*/
|
|
6
|
+
type Scalar = string | number | boolean;
|
|
7
|
+
/**
|
|
8
|
+
* When a field is shown: `{ when: 'type', is: 'business' }`, `is` one value
|
|
9
|
+
* or several. `not` turns it round. With neither, the field shows once `when`
|
|
10
|
+
* is filled in - or switched on.
|
|
11
|
+
*/
|
|
12
|
+
export interface Visibility {
|
|
13
|
+
when: string;
|
|
14
|
+
is?: Scalar | Scalar[];
|
|
15
|
+
not?: Scalar | Scalar[];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* A value made from another field's: `{ field: 'title' }` fills a slug while
|
|
19
|
+
* the title is typed, until someone edits the slug by hand. Left empty, the
|
|
20
|
+
* server fills it the same way.
|
|
21
|
+
*/
|
|
22
|
+
export interface Derivation {
|
|
23
|
+
field: string;
|
|
24
|
+
/** What is made of it. A slug: lowercase letters, digits and dashes. */
|
|
25
|
+
as?: 'slug';
|
|
26
|
+
}
|
|
27
|
+
/** A field's value as a condition reads it: a toggle is on or off, anything else its text. */
|
|
28
|
+
export type Read = (name: string) => string | boolean;
|
|
29
|
+
export declare function shown(visible: Visibility, read: Read): boolean;
|
|
30
|
+
/** "Café & Bar - Downtown" is "cafe-bar-downtown". */
|
|
31
|
+
export declare function slugify(text: string): string;
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What makes a field depend on another, as plain data: the browser applies it
|
|
3
|
+
* while the visitor types, and the server applies it again to what was sent,
|
|
4
|
+
* so the two cannot disagree. No functions - a field crosses to the browser.
|
|
5
|
+
*/
|
|
6
|
+
export function shown(visible, read) {
|
|
7
|
+
const actual = read(visible.when);
|
|
8
|
+
const text = typeof actual === 'boolean' ? String(actual) : actual;
|
|
9
|
+
const listed = (values) => [].concat(values).map(String).includes(text);
|
|
10
|
+
if (visible.is !== undefined)
|
|
11
|
+
return listed(visible.is);
|
|
12
|
+
if (visible.not !== undefined)
|
|
13
|
+
return !listed(visible.not);
|
|
14
|
+
return actual === true || (typeof actual === 'string' && actual !== '');
|
|
15
|
+
}
|
|
16
|
+
/** "Café & Bar - Downtown" is "cafe-bar-downtown". */
|
|
17
|
+
export function slugify(text) {
|
|
18
|
+
return text
|
|
19
|
+
.normalize('NFKD')
|
|
20
|
+
.replace(/[\u0300-\u036f]/g, '')
|
|
21
|
+
.toLowerCase()
|
|
22
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
23
|
+
.replace(/^-+|-+$/g, '');
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=conditions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conditions.js","sourceRoot":"","sources":["../src/conditions.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA6BH,MAAM,UAAU,KAAK,CAAC,OAAmB,EAAE,IAAU;IACnD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;IACjC,MAAM,IAAI,GAAG,OAAO,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAA;IAClE,MAAM,MAAM,GAAG,CAAC,MAAyB,EAAE,EAAE,CAAE,EAAe,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;IAExG,IAAI,OAAO,CAAC,EAAE,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IACvD,IAAI,OAAO,CAAC,GAAG,KAAK,SAAS;QAAE,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IAE1D,OAAO,MAAM,KAAK,IAAI,IAAI,CAAC,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,EAAE,CAAC,CAAA;AACzE,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,OAAO,CAAC,IAAY;IAClC,OAAO,IAAI;SACR,SAAS,CAAC,MAAM,CAAC;SACjB,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC;SAC/B,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAA;AAC5B,CAAC","sourcesContent":["/**\n * What makes a field depend on another, as plain data: the browser applies it\n * while the visitor types, and the server applies it again to what was sent,\n * so the two cannot disagree. No functions - a field crosses to the browser.\n */\n\ntype Scalar = string | number | boolean\n\n/**\n * When a field is shown: `{ when: 'type', is: 'business' }`, `is` one value\n * or several. `not` turns it round. With neither, the field shows once `when`\n * is filled in - or switched on.\n */\nexport interface Visibility {\n when: string\n is?: Scalar | Scalar[]\n not?: Scalar | Scalar[]\n}\n\n/**\n * A value made from another field's: `{ field: 'title' }` fills a slug while\n * the title is typed, until someone edits the slug by hand. Left empty, the\n * server fills it the same way.\n */\nexport interface Derivation {\n field: string\n /** What is made of it. A slug: lowercase letters, digits and dashes. */\n as?: 'slug'\n}\n\n/** A field's value as a condition reads it: a toggle is on or off, anything else its text. */\nexport type Read = (name: string) => string | boolean\n\nexport function shown(visible: Visibility, read: Read): boolean {\n const actual = read(visible.when)\n const text = typeof actual === 'boolean' ? String(actual) : actual\n const listed = (values: Scalar | Scalar[]) => ([] as Scalar[]).concat(values).map(String).includes(text)\n\n if (visible.is !== undefined) return listed(visible.is)\n if (visible.not !== undefined) return !listed(visible.not)\n\n return actual === true || (typeof actual === 'string' && actual !== '')\n}\n\n/** \"Café & Bar - Downtown\" is \"cafe-bar-downtown\". */\nexport function slugify(text: string): string {\n return text\n .normalize('NFKD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n}\n\n"]}
|
package/dist/forms/actions.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* One submit for every form, bound by its component:
|
|
3
|
-
* submitForm.bind(null, 'contact')
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* One submit for every blueprint's form, bound by its component:
|
|
3
|
+
* submitForm.bind(null, 'contact', null) to create, or with the record's key
|
|
4
|
+
* to update it. The blueprint's own schema - or the backend - is the check,
|
|
5
|
+
* on the server, every time: the browser's checks are a courtesy to the
|
|
6
|
+
* visitor, and anyone can post without them, with any key.
|
|
6
7
|
*/
|
|
7
|
-
export declare function submitForm(name: string, form: FormData): Promise<undefined>;
|
|
8
|
+
export declare function submitForm(name: string, key: string | null, form: FormData): Promise<undefined>;
|
package/dist/forms/actions.js
CHANGED
|
@@ -5,21 +5,26 @@ import { parse } from '../resource.js';
|
|
|
5
5
|
import { formNamed } from './registry.js';
|
|
6
6
|
import { HONEYPOT } from './honeypot.js';
|
|
7
7
|
/**
|
|
8
|
-
* One submit for every form, bound by its component:
|
|
9
|
-
* submitForm.bind(null, 'contact')
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* One submit for every blueprint's form, bound by its component:
|
|
9
|
+
* submitForm.bind(null, 'contact', null) to create, or with the record's key
|
|
10
|
+
* to update it. The blueprint's own schema - or the backend - is the check,
|
|
11
|
+
* on the server, every time: the browser's checks are a courtesy to the
|
|
12
|
+
* visitor, and anyone can post without them, with any key.
|
|
12
13
|
*/
|
|
13
|
-
export async function submitForm(name, form) {
|
|
14
|
+
export async function submitForm(name, key, form) {
|
|
14
15
|
const definition = formNamed(name);
|
|
15
16
|
// Filled in, so not a person. Answered as a success, so a bot learns
|
|
16
17
|
// nothing from the reply, and dropped.
|
|
17
|
-
if (String(form.get(HONEYPOT) ?? '') !== '')
|
|
18
|
+
if (definition.honeypot && String(form.get(HONEYPOT) ?? '') !== '')
|
|
18
19
|
return;
|
|
19
|
-
const
|
|
20
|
+
const save = key === null ? definition.create : definition.update;
|
|
21
|
+
if (!save)
|
|
22
|
+
throw new Error(`[forms] the blueprint ${name} has no ${key === null ? 'create' : 'update'}`);
|
|
23
|
+
const target = key === null ? definition : { schema: definition.editSchema, fields: definition.editFields };
|
|
24
|
+
const parsed = await parse(target, form);
|
|
20
25
|
if (!parsed.ok)
|
|
21
26
|
return fieldErrors(parsed.errors);
|
|
22
|
-
await
|
|
27
|
+
await save(parsed.values, key);
|
|
23
28
|
if (definition.redirect)
|
|
24
29
|
redirect(definition.redirect);
|
|
25
30
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actions.js","sourceRoot":"","sources":["../../src/forms/actions.ts"],"names":[],"mappings":"AAAA,YAAY,CAAA;AAEZ,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AACjD,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAExC
|
|
1
|
+
{"version":3,"file":"actions.js","sourceRoot":"","sources":["../../src/forms/actions.ts"],"names":[],"mappings":"AAAA,YAAY,CAAA;AAEZ,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AACjD,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAExC;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAY,EAAE,GAAkB,EAAE,IAAc;IAC/E,MAAM,UAAU,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;IAElC,qEAAqE;IACrE,uCAAuC;IACvC,IAAI,UAAU,CAAC,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE;QAAE,OAAM;IAE1E,MAAM,IAAI,GAAG,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAA;IAEjE,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,IAAI,WAAW,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAA;IAExG,MAAM,MAAM,GAAG,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,CAAC,UAAU,EAAE,CAAA;IAC3G,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IAExC,IAAI,CAAC,MAAM,CAAC,EAAE;QAAE,OAAO,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IAEjD,MAAM,IAAI,CAAC,MAAM,CAAC,MAAiC,EAAE,GAAI,CAAC,CAAA;IAE1D,IAAI,UAAU,CAAC,QAAQ;QAAE,QAAQ,CAAC,UAAU,CAAC,QAAiB,CAAC,CAAA;AACjE,CAAC","sourcesContent":["'use server'\n\nimport { fieldErrors } from '@rsc-kit/core/action'\nimport { redirect } from '@rsc-kit/core/redirect'\nimport { parse } from '../resource.js'\nimport { formNamed } from './registry.js'\nimport { HONEYPOT } from './honeypot.js'\n\n/**\n * One submit for every blueprint's form, bound by its component:\n * submitForm.bind(null, 'contact', null) to create, or with the record's key\n * to update it. The blueprint's own schema - or the backend - is the check,\n * on the server, every time: the browser's checks are a courtesy to the\n * visitor, and anyone can post without them, with any key.\n */\nexport async function submitForm(name: string, key: string | null, form: FormData) {\n const definition = formNamed(name)\n\n // Filled in, so not a person. Answered as a success, so a bot learns\n // nothing from the reply, and dropped.\n if (definition.honeypot && String(form.get(HONEYPOT) ?? '') !== '') return\n\n const save = key === null ? definition.create : definition.update\n\n if (!save) throw new Error(`[forms] the blueprint ${name} has no ${key === null ? 'create' : 'update'}`)\n\n const target = key === null ? definition : { schema: definition.editSchema, fields: definition.editFields }\n const parsed = await parse(target, form)\n\n if (!parsed.ok) return fieldErrors(parsed.errors)\n\n await save(parsed.values as Record<string, unknown>, key!)\n\n if (definition.redirect) redirect(definition.redirect as never)\n}\n"]}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { Field, FieldOverride, FormSchema, ValuesOf } from '../resource.js';
|
|
3
|
+
import type { BlueprintParts } from './brand.js';
|
|
4
|
+
import type { HostAction, HostFields } from './host.js';
|
|
5
|
+
import type { FormsTheme } from './theme.js';
|
|
6
|
+
interface BlueprintOptions {
|
|
7
|
+
/** Unique in the app: the form is found by it when it is sent. Lowercase letters, digits and dashes. */
|
|
8
|
+
name: string;
|
|
9
|
+
/** What draws `<blueprint.Form />`: import { shadcn } from '@rsc-kit/press/shadcn'. A blueprint only a panel draws needs none. */
|
|
10
|
+
theme?: FormsTheme;
|
|
11
|
+
/** The submit button's words. Send to create and Save to update, unless said. */
|
|
12
|
+
submit?: string;
|
|
13
|
+
/** What the visitor reads in place of the form once it is sent. */
|
|
14
|
+
done?: string;
|
|
15
|
+
/** Where the visitor goes once it is sent, instead of `done`. */
|
|
16
|
+
redirect?: string;
|
|
17
|
+
/**
|
|
18
|
+
* A field people never see and bots fill in, for a form anyone may send.
|
|
19
|
+
* On unless said; a form only signed-in people reach can turn it off.
|
|
20
|
+
*/
|
|
21
|
+
honeypot?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface SchemaBlueprintDefinition<Schema extends FormSchema> extends BlueprintOptions {
|
|
24
|
+
/** Any Standard Schema of an object: z.object(), v.object(), type({...}). It checks every submission, on the server. */
|
|
25
|
+
form: Schema;
|
|
26
|
+
/** Labels, descriptions and controls the schema cannot say - or every field, for a schema that does not describe itself as JSON Schema. */
|
|
27
|
+
fields?: Partial<Record<keyof ValuesOf<Schema> & string, FieldOverride>>;
|
|
28
|
+
/**
|
|
29
|
+
* What a new submission that passed the schema does: store it, send it,
|
|
30
|
+
* call a backend. Throw an ActionValidationError - or let a backend's 422
|
|
31
|
+
* through - and its messages land under the fields.
|
|
32
|
+
*/
|
|
33
|
+
handle?: (values: ValuesOf<Schema>) => unknown;
|
|
34
|
+
/** What a submission of `<blueprint.Form id={...} />` does, with that key. */
|
|
35
|
+
update?: (values: ValuesOf<Schema>, key: string) => unknown;
|
|
36
|
+
}
|
|
37
|
+
export interface HostBlueprintDefinition<Create extends HostAction, Update extends HostAction> extends BlueprintOptions {
|
|
38
|
+
/**
|
|
39
|
+
* The backend actions it saves with, as the build generated them:
|
|
40
|
+
* `import { productsStore, productsUpdate } from '../server-actions.generated'`.
|
|
41
|
+
* The create form is drawn from what `create` takes and the edit form from
|
|
42
|
+
* what `update` takes, and they do all the checking: a Laravel FormRequest,
|
|
43
|
+
* a Go struct. `update` is called with the key after the values.
|
|
44
|
+
*/
|
|
45
|
+
actions: {
|
|
46
|
+
create?: Create;
|
|
47
|
+
update?: Update;
|
|
48
|
+
};
|
|
49
|
+
/** Labels, descriptions and controls the backend's types cannot say. */
|
|
50
|
+
fields?: Partial<Record<HostFields<Create> | HostFields<Update>, FieldOverride>>;
|
|
51
|
+
}
|
|
52
|
+
export interface BlueprintFormProps {
|
|
53
|
+
/** The record's current values, for an edit. Only the blueprint's fields reach the browser. */
|
|
54
|
+
values?: object;
|
|
55
|
+
/** The record's key: the form then updates it rather than creating one. */
|
|
56
|
+
id?: string | number;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* A form's fields and what saving it does, defined once and drawn anywhere:
|
|
60
|
+
* as `<product.Form />` in a page, and as a panel's create and edit pages
|
|
61
|
+
* with `defineResource({ form: product })`.
|
|
62
|
+
*/
|
|
63
|
+
export interface Blueprint extends BlueprintParts {
|
|
64
|
+
/**
|
|
65
|
+
* The form, in any page. Without `id` it creates; with it, it updates that
|
|
66
|
+
* record, starting from `values`:
|
|
67
|
+
*
|
|
68
|
+
* <profile.Form id={user.id} values={user} />
|
|
69
|
+
*
|
|
70
|
+
* The key comes back from the browser like any field, so the update must
|
|
71
|
+
* check it may touch that record: a FormRequest's authorize(), a Go
|
|
72
|
+
* handler's own check.
|
|
73
|
+
*/
|
|
74
|
+
Form: (props: BlueprintFormProps) => ReactNode;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* A form defined once - its fields, its checks, what saving it does - and
|
|
78
|
+
* drawn wherever it is needed: a page, a panel, or both. Either from a
|
|
79
|
+
* Standard Schema with `handle` and `update`, or from the backend's own
|
|
80
|
+
* actions, which draw the fields and do the checking:
|
|
81
|
+
*
|
|
82
|
+
* export const product = defineBlueprint({
|
|
83
|
+
* name: 'product',
|
|
84
|
+
* theme: shadcn,
|
|
85
|
+
* actions: { create: productsStore, update: productsUpdate },
|
|
86
|
+
* })
|
|
87
|
+
*
|
|
88
|
+
* <product.Form /> // creates
|
|
89
|
+
* <product.Form id={p.id} values={p} /> // updates
|
|
90
|
+
* defineResource<Product>()({ ..., form: product })
|
|
91
|
+
*/
|
|
92
|
+
export declare function defineBlueprint<Schema extends FormSchema>(definition: SchemaBlueprintDefinition<Schema>): Blueprint;
|
|
93
|
+
export declare function defineBlueprint<Create extends HostAction, Update extends HostAction>(definition: HostBlueprintDefinition<Create, Update>): Blueprint;
|
|
94
|
+
/**
|
|
95
|
+
* Only the form's own fields go to the browser: a record handed over whole
|
|
96
|
+
* would carry every column it has - a password's hash, a token - into the page.
|
|
97
|
+
*/
|
|
98
|
+
export declare function valuesFor(fields: Field[], values: object | undefined): Record<string, unknown>;
|
|
99
|
+
export {};
|