一键导入
waaseyaa-admin-spa
Use when working with the admin interface, Vue composables, schema-driven forms, SSE integration, i18n translations, or files in packages/admin/
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Use when working with the admin interface, Vue composables, schema-driven forms, SSE integration, i18n translations, or files in packages/admin/
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Use when working with AI schema generation, agent execution, pipeline orchestration, vector storage, agent tools, or files in packages/ai-schema/, packages/ai-agent/, packages/ai-pipeline/, packages/ai-vector/, packages/ai-tools/, packages/ai-observability/
Use when editing docs/specs/, CLAUDE.md orchestration, or agent rules — keep subsystem specs aligned with code, run drift checks, and follow the anchor-issue + design-first workflow (GitHub as integration surface).
Use when working with JSON:API endpoints, resource serialization, query parsing, schema presentation, route building, or files in packages/api/, packages/routing/
Use when working with entity types, entity storage, entity queries, field definitions, field type plugins, config entities, config import/export, or files in packages/entity/, packages/entity-storage/, packages/field/, packages/config/. Covers EntityInterface, EntityBase, EntityType, EntityTypeManager, SqlEntityStorage, SqlEntityQuery, SqlSchemaHandler, EntityRepository, UnitOfWork, FieldDefinition, FieldTypeManager, FieldItemBase, ConfigFactory, ConfigManager. Also applies when touching User (packages/user/) or Node (packages/node/) entity subclasses.
Use when working with service providers, domain events, cache backends, database queries, migrations, package discovery, attribute scanning, or files in packages/foundation/, packages/cache/, packages/database-legacy/, packages/plugin/
Use when working with HTTP middleware, event middleware, job middleware, pipeline compilation, middleware discovery, or files in packages/foundation/src/Middleware/, packages/routing/, packages/user/src/Middleware/, packages/access/src/Middleware/, public/index.php
| name | waaseyaa:admin-spa |
| description | Use when working with the admin interface, Vue composables, schema-driven forms, SSE integration, i18n translations, or files in packages/admin/ |
This skill covers the Nuxt 3 admin SPA at packages/admin/. Use it when:
packages/admin/app/components/packages/admin/app/composables/packages/admin/app/pages/packages/admin/app/i18n/en.jsonOut of scope: PHP backend code, entity storage, access policies (see project-level CLAUDE.md for those).
packages/admin/app/composables/useEntity.ts)interface JsonApiResource {
type: string; id: string; attributes: Record<string, any>
relationships?: Record<string, any>; links?: Record<string, string>; meta?: Record<string, any>
}
interface JsonApiDocument {
jsonapi: { version: string }; data: JsonApiResource | JsonApiResource[] | null
errors?: Array<{ status: string; title: string; detail?: string }>
}
packages/admin/app/composables/useSchema.ts)interface SchemaProperty {
type: string; description?: string; format?: string; readOnly?: boolean
enum?: string[]; minimum?: number; maximum?: number; maxLength?: number
'x-widget'?: string; 'x-label'?: string; 'x-description'?: string
'x-weight'?: number; 'x-required'?: boolean; 'x-enum-labels'?: Record<string, string>
'x-target-type'?: string; 'x-access-restricted'?: boolean
}
interface EntitySchema {
$schema: string; title: string; description: string; type: string
'x-entity-type': string; 'x-translatable': boolean; 'x-revisionable': boolean
properties: Record<string, SchemaProperty>; required?: string[]
}
packages/admin/app/composables/useRealtime.ts)interface BroadcastMessage {
channel: string; event: string; data: Record<string, unknown>; timestamp: number
}
Every widget in packages/admin/app/components/widgets/ must accept:
defineProps<{
modelValue: any
label?: string
description?: string
required?: boolean
disabled?: boolean
schema?: SchemaProperty
}>()
defineEmits<{ 'update:modelValue': [value: any] }>()
packages/admin/app/pages/. Dynamic segments use [param] syntax.app.vue -> layouts/default.vue -> LayoutAdminShell (topbar + sidebar + main content area).NavBuilder fetches entity types from GET /api/entity-types and renders sidebar links.useSchema(entityType).fetch() calls GET /api/schema/{entityType}, caches in module-level Map.SchemaForm iterates sortedProperties(true) -> SchemaField resolves widget from x-widget -> widget component renders the input.useEntity() provides list, get, create, update, remove, search against GET/POST/PATCH/DELETE /api/{type}[/{id}].useRealtime(['admin']) opens SSE to GET /api/broadcast?channels=admin. SchemaList watches messages and auto-refreshes on entity.saved / entity.deleted events.All /api/* requests are proxied to http://localhost:8080/api via Nuxt config. This is configured in packages/admin/nuxt.config.ts under both nitro.devProxy (dev) and routeRules (production).
SchemaField (packages/admin/app/components/schema/SchemaField.vue) maps x-widget to components:
| x-widget | Component resolved via |
|---|---|
text (default) | resolveComponent('WidgetsTextInput') |
email, url, password, image, file | WidgetsTextInput |
textarea | WidgetsTextArea |
richtext | WidgetsRichText |
number | WidgetsNumberInput |
boolean | WidgetsToggle |
select | WidgetsSelect |
datetime | WidgetsDateTimeInput |
entity_autocomplete | WidgetsEntityAutocomplete |
hidden | WidgetsHiddenField |
Fallback for unknown widgets: WidgetsTextInput.
Two distinct concepts in schema properties:
readOnly: true, no x-access-restricted): Fields like id, uuid. Excluded from forms entirely by sortedProperties(true).readOnly: true + x-access-restricted: true): Fields the user can view but not edit. Rendered as disabled inputs. SchemaForm guards the @update:model-value handler to prevent writes.useRealtime (packages/admin/app/composables/useRealtime.ts):
Math.min(3000 * Math.pow(2, retryCount - 1), 30000)onUnmountedreconnect() resets error state and retry counter, then reconnectsuseFetch instead of $fetchAll composables use $fetch (imperative) not useFetch (SSR-aware). The admin SPA fetches data in onMounted callbacks and event handlers, not during SSR. Using useFetch would cause hydration issues and unwanted caching behavior.
x-access-restricted guard in form handlersWhen adding new form submission logic, always check x-access-restricted before writing to formData. The pattern in SchemaForm:
@update:model-value="(val: any) => { if (!fieldSchema['x-access-restricted']) formData[fieldName] = val }"
Omitting this guard allows users to modify access-restricted fields via browser devtools.
Nuxt auto-import resolves components by directory path in PascalCase. A widget at components/widgets/TextInput.vue is referenced as WidgetsTextInput (not TextInput). When adding widgets to widgetMap in SchemaField.vue, use resolveComponent('Widgets{Name}').
useSchema uses a module-level Map for caching. If you modify schema data on the backend and the frontend still shows stale data, call invalidate() on the relevant useSchema instance. The cache is never automatically invalidated.
useRealtime replaces the array reference on each message (messages.value = [...messages.value.slice(-99), msg]). Do not push to messages.value directly -- Vue reactivity tracks the ref assignment, not array mutations.
All user-facing strings must go through useLanguage().t('key'). When adding new UI text, add the key to packages/admin/app/i18n/en.json first. The t() function falls back to the key string itself if no translation exists, which can mask missing translations.
The RichText widget emits raw innerHTML from the contenteditable div. The sanitization runs on render (via computed), not on emit. Server-side sanitization is required. Do not trust client-side sanitization for security.
EntityAutocomplete currently hardcodes labelField to 'title'. For entity types where the label field is not title, this must be updated. The schema's x-target-type is used but there is no x-label-field extension yet.
useRealtime connects to GET /api/broadcast?channels=admin. This endpoint must be wired in public/index.php. If the endpoint is not available, the composable will retry 10 times with exponential backoff and then show an error. The frontend works without SSE (graceful degradation).
No frontend test framework is configured. TypeScript correctness is verified via build:
cd packages/admin && npm run build
This catches type errors, missing imports, and component resolution failures.
php -S localhost:8080 public/index.phpcd packages/admin && npm run devhttp://localhost:3000/packages/admin/app/components/widgets/{Name}.vue implementing the widget props contractwidgetMap in packages/admin/app/components/schema/SchemaField.vueSchemaPresenter sets the corresponding x-widget value on fieldscd packages/admin && npm run build to verify compilationpackages/admin/app/composables/use{Name}.tsuse{Name} returning reactive refs and functionscomposables/ -- no explicit import needed in componentscd packages/admin && npm run build to verifypackages/admin/app/pages/ following Nuxt file-based routing conventions[param] for dynamic route segmentslayouts/default.vue)NavBuilder if the page should appear in the sidebarpackages/admin/app/i18n/en.jsonconst { t } = useLanguage() in the componentt('your_key') or t('your_key', { param: 'value' }) for replacements| File | Role |
|---|---|
packages/admin/nuxt.config.ts | Nuxt config, API proxy setup |
packages/admin/app/composables/useEntity.ts | JSON:API CRUD + search |
packages/admin/app/composables/useSchema.ts | Schema fetching, caching, field sorting |
packages/admin/app/composables/useLanguage.ts | i18n with token replacement |
packages/admin/app/composables/useRealtime.ts | SSE connection management |
packages/admin/app/components/schema/SchemaForm.vue | Schema-driven entity form |
packages/admin/app/components/schema/SchemaField.vue | Widget resolver (x-widget -> component) |
packages/admin/app/components/schema/SchemaList.vue | Entity list with SSE auto-refresh |
packages/admin/app/components/widgets/EntityAutocomplete.vue | Typeahead entity reference widget |
packages/admin/app/components/layout/AdminShell.vue | Layout shell + global CSS |
packages/admin/app/components/layout/NavBuilder.vue | Dynamic sidebar navigation |
packages/admin/app/i18n/en.json | English translations |
docs/specs/admin-spa.md -- Full specification with all interfaces, widget mapping, and component details