From c2eaea29429297accc9ac4b4bf6731b5e51a39e6 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 1 Mar 2026 01:42:18 +0000 Subject: [PATCH 1/2] Add admin UI setup step to migration assistant Adds a new `setup-admin-ui` skill and integrates it into the migration assistant as Phase 5. After completing the core migration steps, the assistant now asks users whether they want the Admin UI set up and at what path, then delegates to the skill to install @opensaas/stack-ui and create the catch-all admin route page (with or without auth). https://claude.ai/code/session_019hziN9HYyNwoTgyHVxRyUC --- .../agents/migration-assistant.md | 32 +++- .../skills/setup-admin-ui/SKILL.md | 174 ++++++++++++++++++ 2 files changed, 203 insertions(+), 3 deletions(-) create mode 100644 claude-plugins/opensaas-migration/skills/setup-admin-ui/SKILL.md diff --git a/claude-plugins/opensaas-migration/agents/migration-assistant.md b/claude-plugins/opensaas-migration/agents/migration-assistant.md index 8be9a1af..a84e98d3 100644 --- a/claude-plugins/opensaas-migration/agents/migration-assistant.md +++ b/claude-plugins/opensaas-migration/agents/migration-assistant.md @@ -382,7 +382,22 @@ Change `session.data.id` → `session.userId` in access control functions. The subagent will search the project, edit all files, and report what it changed. -### Step 8: Run generation and validate +### Step 10: Set up Admin UI — ask the user, then delegate to subagent + +**Do not do this yourself.** Ask the user two questions: + +1. "Would you like to set up the OpenSaaS Stack Admin UI in this project? It provides a full CRUD interface for all your lists out of the box." +2. "What path should the admin UI be mounted at? (default: `/admin`)" + +If the user wants the admin UI: + +> Invoke `setup-admin-ui` with: "Project root: /path/to/project. Admin path: /admin (or whatever they chose). Auth enabled: yes/no (based on whether authPlugin was detected in their config)." + +The subagent will install `@opensaas/stack-ui`, create `app/{path}/[[...{segment}]]/page.tsx`, and report next steps. + +If the user declines, skip this step and proceed to validation. + +### Step 11: Run generation and validate ```bash pnpm opensaas generate # Generates prisma schema @@ -410,6 +425,7 @@ Your job is to plan and coordinate the migration, not to do all the editing your - context.graphql/context.query migration → `migrate-context-calls` skill - Image/file field migration (config + SQL) → `migrate-image-fields` skill - Document field migration (→ tiptap) → `migrate-document-fields` skill +- Admin UI setup (ask first, then delegate) → `setup-admin-ui` skill ### When the user says "help me migrate" or similar: @@ -446,7 +462,16 @@ After assessing, show the user a numbered list of exactly what will change and w - **If virtual fields**: invoke `migrate-virtual-fields` with config path and virtual field code - **If context.graphql/context.query**: invoke `migrate-context-calls` with project root path -**Phase 5 — Validate:** +**Phase 5 — Admin UI:** + +Ask the user two questions (both required before delegating): +1. "Would you like to set up the OpenSaaS Stack Admin UI? It gives you a full CRUD interface for all your lists out of the box, at a path you choose." +2. "What path should it be mounted at?" (default: `/admin`) + +If yes → **invoke `setup-admin-ui`** with: project root, chosen admin path, and whether `authPlugin` is in their config. +If no → skip and go to Phase 6. + +**Phase 6 — Validate:** - Run `pnpm opensaas generate` and report any errors - If image/file fields were found, remind the user to run the SQL migration script BEFORE `prisma db push` @@ -515,4 +540,5 @@ Guide them through: 2. Run `pnpm opensaas generate` 3. Run `npx prisma db push` 4. Start dev server: `pnpm dev` -5. Visit the admin UI at `http://localhost:3000/admin` +5. If Admin UI was set up: visit `http://localhost:3000/{adminPath}` (e.g. `http://localhost:3000/admin`) +6. If Admin UI was skipped: mention that they can set it up any time — see https://stack.opensaas.au/admin-ui diff --git a/claude-plugins/opensaas-migration/skills/setup-admin-ui/SKILL.md b/claude-plugins/opensaas-migration/skills/setup-admin-ui/SKILL.md new file mode 100644 index 00000000..a60b7e69 --- /dev/null +++ b/claude-plugins/opensaas-migration/skills/setup-admin-ui/SKILL.md @@ -0,0 +1,174 @@ +--- +name: setup-admin-ui +description: Set up the OpenSaaS Stack Admin UI in an existing Next.js App Router project. Invoke as a forked subagent after migration is complete, passing the project root, desired admin path, and whether auth is enabled. +context: fork +agent: general-purpose +--- + +Set up the OpenSaaS Stack Admin UI in the Next.js project described below. + +$ARGUMENTS + +## What This Skill Does + +1. Installs `@opensaas/stack-ui` if not already a dependency +2. Creates the catch-all admin route page at `app/{adminPath}/[[...{segmentName}]]/page.tsx` +3. Validates the generated file works with the project structure + +## Step 1 — Check if `@opensaas/stack-ui` Is Already Installed + +Read `package.json` in the project root. If `@opensaas/stack-ui` is **not** in `dependencies`, install it: + +```bash +# Detect package manager +# - If pnpm-lock.yaml exists → pnpm add @opensaas/stack-ui +# - If yarn.lock exists → yarn add @opensaas/stack-ui +# - Otherwise → npm install @opensaas/stack-ui +``` + +Check existing versions of `@opensaas/stack-core` in `package.json` and install `@opensaas/stack-ui` at the **same version** to avoid mismatches. + +## Step 2 — Determine the Admin Path + +The admin path comes from `$ARGUMENTS` (e.g. `/admin`, `/dashboard/admin`). + +- **Route path**: `app/{adminPath}/[[...{segmentName}]]/page.tsx` + - For `/admin` → `app/admin/[[...admin]]/page.tsx` + - For `/dashboard/admin` → `app/dashboard/admin/[[...admin]]/page.tsx` + - For `/cms` → `app/cms/[[...cms]]/page.tsx` +- **Segment name** (used in both the directory name and the `params.{segmentName}` reference): the last path segment (e.g. `admin`, `cms`) +- **`basePath`** prop on ``: the full admin path (e.g. `/admin`, `/dashboard/admin`) + +Create all intermediate directories as needed. + +## Step 3 — Determine if Auth Is Enabled + +Check whether auth is configured: + +1. Look in `opensaas.config.ts` for `authPlugin` usage +2. Check for a `lib/auth.ts` or `lib/auth/index.ts` that exports `getSession` + +This determines which page template to use. + +## Step 4 — Create the Admin Page + +### Template A — Without Auth + +Use this when auth is NOT configured: + +```typescript +import { AdminUI } from '@opensaas/stack-ui' +import type { ServerActionInput } from '@opensaas/stack-ui/server' +import { getContext, config } from '@/.opensaas/context' + +async function serverAction(props: ServerActionInput) { + 'use server' + const context = await getContext() + return await context.serverAction(props) +} + +interface AdminPageProps { + params: Promise<{ {SEGMENT_NAME}?: string[] }> + searchParams: Promise<{ [key: string]: string | string[] | undefined }> +} + +export default async function AdminPage({ params, searchParams }: AdminPageProps) { + const resolvedParams = await params + const resolvedSearchParams = await searchParams + return ( + + ) +} +``` + +### Template B — With Auth + +Use this when `authPlugin` is detected and `getSession` is available: + +```typescript +import { AdminUI } from '@opensaas/stack-ui' +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 context = await getContext({ session: await getSession() }) + return await context.serverAction(props) +} + +interface AdminPageProps { + params: Promise<{ {SEGMENT_NAME}?: 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 ( +
+
+

Access Denied

+

You must be logged in to access the admin interface.

+
+
+ ) + } + return ( + + ) +} +``` + +Replace `{SEGMENT_NAME}` with the last segment of the admin path (e.g. `admin`) and `{ADMIN_PATH}` with the full path (e.g. `/admin`). + +**Important**: If `getSession` is not at `@/lib/auth`, check for it at its actual location (e.g. `@/lib/auth/index`, `@/app/lib/auth`) and use the correct import path. + +## Step 5 — Check for Missing `.opensaas/context` + +The admin page imports from `@/.opensaas/context`. This file is generated by `pnpm opensaas generate` (or `pnpm generate`). If it doesn't exist yet: + +- **Do not create it manually** — it's generated from `opensaas.config.ts` +- Remind the user to run `pnpm generate` (or `pnpm opensaas generate`) before starting the dev server + +## Step 6 — Report What Was Done + +Report to the user: + +``` +✓ Admin UI set up at: {adminPath} +✓ File created: app/{adminPath}/[[...{segmentName}]]/page.tsx +✓ Auth-aware: yes/no +✓ @opensaas/stack-ui: already installed / installed at version X.Y.Z + +Next steps: +1. Run `pnpm generate` to generate the .opensaas/context.ts file (if not already done) +2. Run `pnpm dev` to start the dev server +3. Visit http://localhost:3000{adminPath} to access the admin UI + +Docs: https://stack.opensaas.au/admin-ui +``` + +## Notes + +- The `[[...{segmentName}]]` catch-all segment handles all admin routes: the list view, item detail, create, and edit pages — all from one file. +- The `basePath` prop must match exactly the URL path where the admin is mounted. +- The `serverAction` wrapper function is required — it provides the server action bridge between the client-side admin UI components and the database context. +- If the user is using a custom `getSession` approach (not from `@opensaas/stack-auth`), the auth template still works — just update the import path and the session shape passed to `getContext`. +- If Tailwind CSS is not configured in the project, mention that `@opensaas/stack-ui` uses Tailwind for styling and link to the docs for setup: https://stack.opensaas.au/admin-ui#tailwind From 7ed51af940f7711c34a22bd93c80d766679da6f9 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 1 Mar 2026 01:50:53 +0000 Subject: [PATCH 2/2] Fix prettier formatting in migration-assistant.md https://claude.ai/code/session_019hziN9HYyNwoTgyHVxRyUC --- claude-plugins/opensaas-migration/agents/migration-assistant.md | 1 + 1 file changed, 1 insertion(+) diff --git a/claude-plugins/opensaas-migration/agents/migration-assistant.md b/claude-plugins/opensaas-migration/agents/migration-assistant.md index a84e98d3..36855e9f 100644 --- a/claude-plugins/opensaas-migration/agents/migration-assistant.md +++ b/claude-plugins/opensaas-migration/agents/migration-assistant.md @@ -465,6 +465,7 @@ After assessing, show the user a numbered list of exactly what will change and w **Phase 5 — Admin UI:** Ask the user two questions (both required before delegating): + 1. "Would you like to set up the OpenSaaS Stack Admin UI? It gives you a full CRUD interface for all your lists out of the box, at a path you choose." 2. "What path should it be mounted at?" (default: `/admin`)