diff --git a/docs/content/packages/ui.md b/docs/content/packages/ui.md index d12b0f3c..3a5d5e89 100644 --- a/docs/content/packages/ui.md +++ b/docs/content/packages/ui.md @@ -28,13 +28,46 @@ The simplest way to add an admin interface to your Next.js app: ```typescript // app/admin/[[...admin]]/page.tsx import { AdminUI } from '@opensaas/stack-ui' -import { getAdminContext } from '@opensaas/stack-ui/server' -import config from '@/opensaas.config' +import type { ServerActionInput } from '@opensaas/stack-ui/server' +import { getContext, config } from '@/.opensaas/context' +import { getSession } from '@/lib/auth' +import { redirect } from 'next/navigation' +import { getUrlKey } from '@opensaas/stack-core' import '@opensaas/stack-ui/styles' -export default async function AdminPage() { - const context = await getAdminContext() - return +async function serverAction(props: ServerActionInput) { + 'use server' + const session = await getSession() + const context = await getContext(session ?? undefined) + const result = await context.serverAction(props) + if (result && typeof result === 'object' && 'success' in result && result.success) { + redirect(`/admin/${getUrlKey(props.listKey)}`) + } + return result +} + +interface AdminPageProps { + params: Promise<{ admin?: string[] }> + searchParams: Promise<{ [key: string]: string | string[] | undefined }> +} + +export default async function AdminPage({ params, searchParams }: AdminPageProps) { + const resolvedParams = await params + const resolvedSearchParams = await searchParams + const session = await getSession() + if (!session) { + return
You must be signed in to access the admin interface.
+ } + return ( + + ) } ``` @@ -65,8 +98,8 @@ import { TextField, SelectField, RelationshipField } from '@opensaas/stack-ui/fi // Standalone components - Composable CRUD components import { ItemCreateForm, ItemEditForm, ListTable } from '@opensaas/stack-ui/standalone' -// Server utilities - Context and session management -import { getAdminContext } from '@opensaas/stack-ui/server' +// Server utilities - Type imports for server actions +import type { ServerActionInput } from '@opensaas/stack-ui/server' // Styles - Tailwind CSS import '@opensaas/stack-ui/styles' @@ -80,19 +113,34 @@ The `AdminUI` component provides a complete admin interface with routing, naviga ```typescript import { AdminUI } from '@opensaas/stack-ui' -import { getAdminContext } from '@opensaas/stack-ui/server' -import config from '@/opensaas.config' +import type { ServerActionInput } from '@opensaas/stack-ui/server' +import { getContext, config } from '@/.opensaas/context' +import { getSession } from '@/lib/auth' + +async function serverAction(props: ServerActionInput) { + 'use server' + const session = await getSession() + const context = await getContext(session ?? undefined) + return context.serverAction(props) +} export default async function AdminPage() { - const context = await getAdminContext() - return + const session = await getSession() + return ( + + ) } ``` **Props:** -- `context` - Admin context with session and database access +- `context` - Access-controlled context from `.opensaas/context` - `config` - OpenSaas configuration object +- `serverAction` - `'use server'` wrapper around `context.serverAction` for mutations **Features:** @@ -156,10 +204,12 @@ A standalone form for creating new items. ```typescript import { ItemCreateForm } from '@opensaas/stack-ui/standalone' -import { getAdminContext } from '@opensaas/stack-ui/server' +import { getContext } from '@/.opensaas/context' +import { getSession } from '@/lib/auth' export default async function CreatePostPage() { - const context = await getAdminContext() + const session = await getSession() + const context = await getContext(session ?? undefined) return ( @@ -488,20 +542,27 @@ The `FieldRenderer` resolves components in the following order: ## Server Utilities -### getAdminContext +The `@opensaas/stack-ui/server` export provides TypeScript types for server action integration. -Get an admin context with session from request headers. +### ServerActionInput -```typescript -import { getAdminContext } from '@opensaas/stack-ui/server' +`ServerActionInput` is the type for the props passed to your server action wrapper. Import it to type the `serverAction` function you pass to ``. -export default async function AdminPage() { - const context = await getAdminContext() - // context includes session if using @opensaas/stack-auth - return +```typescript +import type { ServerActionInput } from '@opensaas/stack-ui/server' +import { getContext } from '@/.opensaas/context' +import { getSession } from '@/lib/auth' + +async function serverAction(props: ServerActionInput) { + 'use server' + const session = await getSession() + const context = await getContext(session ?? undefined) + return context.serverAction(props) } ``` +There is no `getAdminContext` helper in this package. The host application is responsible for resolving the session and constructing the context using `getContext` from the generated `.opensaas/context` module. + ## Styling The UI package includes Tailwind CSS v4 styles. Import them in your root layout: @@ -553,7 +614,7 @@ All components are fully typed with TypeScript: The UI package is optimized for performance: -- Server components by default (AdminUI, getAdminContext) +- Server components by default (AdminUI, standalone components) - Client components only where needed (forms, interactive elements) - Minimal client-side JavaScript - Data fetching on the server reduces bundle size @@ -567,10 +628,12 @@ Build a custom dashboard using standalone components: ```typescript import { ListTable, ItemCreateForm } from '@opensaas/stack-ui/standalone' import { Card, CardHeader, CardContent } from '@opensaas/stack-ui/primitives' -import { getAdminContext } from '@opensaas/stack-ui/server' +import { getContext } from '@/.opensaas/context' +import { getSession } from '@/lib/auth' export default async function CustomDashboard() { - const context = await getAdminContext() + const session = await getSession() + const context = await getContext(session ?? undefined) return (