@jskit-ai/agent-docs 0.1.148 → 0.1.150
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/guide/agent/app-setup/existing-application-migration.md +8 -6
- package/guide/agent/app-setup/initial-scaffolding.md +26 -7
- package/guide/agent/app-setup/quickstart.md +9 -6
- package/guide/agent/framework/application-operations.md +91 -0
- package/guide/agent/framework/crud-operations.md +83 -0
- package/guide/agent/framework/material-3.md +112 -0
- package/guide/agent/framework/ui-operations.md +78 -0
- package/guide/agent/index.md +13 -3
- package/package.json +1 -1
- package/patterns/feature-package/example/booking-engine/package.json +1 -1
- package/patterns/minimal-foundation/PATTERN.md +5 -0
- package/patterns/minimal-foundation/example/AGENTS.md +8 -8
- package/patterns/minimal-foundation/example/package.json +4 -5
- package/patterns/minimal-foundation/example/vite.config.mjs +0 -1
- package/patterns/shell-foundation/PATTERN.md +5 -0
- package/patterns/shell-foundation/example/AGENTS.md +8 -8
- package/patterns/shell-foundation/example/package.json +5 -6
- package/patterns/shell-foundation/example/packages/main/package.json +1 -1
- package/patterns/shell-foundation/example/vite.config.mjs +0 -1
- package/reference/autogen/PATTERN_INDEX.md +61 -61
- package/reference/autogen/tooling/testUtils.md +1 -1
- package/skills/jskit/SKILL.md +8 -5
- package/skills/jskit/references/app-operations.md +12 -11
- package/skills/jskit/references/crud-operations.md +10 -6
- package/skills/jskit/references/existing-application-migration.md +9 -12
- package/skills/jskit/references/material-3.md +2 -0
- package/skills/jskit/references/pattern-index.md +61 -61
- package/skills/jskit/references/patterns/app/minimal-foundation/PATTERN.md +105 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/.nvmrc +1 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/AGENTS.md +17 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/Procfile +2 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/app.json +14 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/bin/develop.js +71 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/bin/server.js +8 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/config/public.js +40 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/config/server.js +1 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/config/surfaceAccessPolicies.js +3 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/eslint.config.mjs +19 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/favicon.svg +7 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/gitignore +9 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/index.html +13 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/jsconfig.json +8 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/package.json +62 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/packages/main/package.json +42 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/packages/main/src/shared/index.js +10 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/packages/main/src/shared/schemas/index.js +22 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/playwright.config.mjs +31 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/server/lib/runtimeEnv.js +45 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/server/lib/surfaceRuntime.js +10 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/server.js +195 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/src/App.vue +13 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/src/main.js +85 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/src/pages/home/index.vue +48 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/src/pages/home.vue +13 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/src/views/NotFound.vue +13 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/tests/client/smoke.vitest.js +7 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/tests/e2e/base-shell.spec.ts +23 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/tests/server/smoke.test.js +16 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/vite.config.mjs +80 -0
- package/skills/jskit/references/patterns/app/shell-foundation/PATTERN.md +111 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/.nvmrc +1 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/AGENTS.md +17 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/Procfile +2 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/app.json +14 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/bin/develop.js +71 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/bin/server.js +8 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/config/public.js +40 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/config/server.js +1 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/config/surfaceAccessPolicies.js +3 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/eslint.config.mjs +19 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/favicon.svg +7 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/gitignore +9 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/index.html +13 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/jsconfig.json +8 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/package.json +64 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/packages/main/package.json +56 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/packages/main/src/client/index.js +9 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/packages/main/src/client/providers/MainClientProvider.js +18 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/packages/main/src/shared/index.js +10 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/packages/main/src/shared/schemas/index.js +22 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/playwright.config.mjs +31 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/server/lib/runtimeEnv.js +45 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/server/lib/surfaceRuntime.js +10 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/server.js +195 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/src/App.vue +11 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/src/components/ShellLayout.vue +12 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/src/components/menus/MenuLinkItem.vue +30 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/src/components/menus/SurfaceAwareMenuLinkItem.vue +42 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/src/components/menus/TabLinkItem.vue +42 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/src/error.js +19 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/src/main.js +85 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/src/pages/home/index.vue +116 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/src/pages/home/settings/general/index.vue +40 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/src/pages/home/settings/index.vue +7 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/src/pages/home/settings.vue +109 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/src/pages/home.vue +20 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/src/placement.js +56 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/src/placementTopology.js +149 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/src/views/NotFound.vue +13 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/tests/client/smoke.vitest.js +7 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/tests/e2e/adaptive-shell.spec.ts +10 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/tests/e2e/base-shell.spec.ts +23 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/tests/server/smoke.test.js +16 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/vite.config.mjs +80 -0
- package/skills/jskit/references/patterns/assistant/assistant-surface/PATTERN.md +77 -0
- package/skills/jskit/references/patterns/assistant/assistant-surface/example/config/public.js +19 -0
- package/skills/jskit/references/patterns/assistant/assistant-surface/example/config/server.js +9 -0
- package/skills/jskit/references/patterns/assistant/assistant-surface/example/src/pages/admin/assistant/index.vue +7 -0
- package/skills/jskit/references/patterns/assistant/assistant-surface/example/src/pages/admin/settings/assistant/index.vue +7 -0
- package/skills/jskit/references/patterns/assistant/assistant-surface/example/src/placement.js +38 -0
- package/skills/jskit/references/patterns/auth/auth-surface/PATTERN.md +90 -0
- package/skills/jskit/references/patterns/auth/auth-surface/example/src/pages/auth/login.vue +17 -0
- package/skills/jskit/references/patterns/auth/auth-surface/example/src/pages/auth/reset-password.vue +17 -0
- package/skills/jskit/references/patterns/auth/auth-surface/example/src/pages/auth/signout.vue +17 -0
- package/skills/jskit/references/patterns/auth/auth-surface/example/src/runtime/authGuardRuntime.js +7 -0
- package/skills/jskit/references/patterns/auth/auth-surface/example/src/runtime/authHttpClient.js +1 -0
- package/skills/jskit/references/patterns/auth/auth-surface/example/src/runtime/useSignOut.js +1 -0
- package/skills/jskit/references/patterns/auth/auth-surface/example/src/views/auth/LoginView.vue +7 -0
- package/skills/jskit/references/patterns/auth/auth-surface/example/src/views/auth/ResetPasswordView.vue +7 -0
- package/skills/jskit/references/patterns/auth/auth-surface/example/src/views/auth/SignOutView.vue +7 -0
- package/skills/jskit/references/patterns/auth/auth-surface/example/tools/preview-identity +5 -0
- package/skills/jskit/references/patterns/auth/supabase-auth/PATTERN.md +67 -0
- package/skills/jskit/references/patterns/auth/supabase-auth/example/.env.example +4 -0
- package/skills/jskit/references/patterns/auth/supabase-auth/example/config/server.js +11 -0
- package/skills/jskit/references/patterns/auth/supabase-auth/example/package.json +7 -0
- package/skills/jskit/references/patterns/console/console-surface/PATTERN.md +58 -0
- package/skills/jskit/references/patterns/console/console-surface/example/src/pages/console/index.vue +62 -0
- package/skills/jskit/references/patterns/console/console-surface/example/src/pages/console/settings/index.vue +17 -0
- package/skills/jskit/references/patterns/console/console-surface/example/src/pages/console/settings.vue +95 -0
- package/skills/jskit/references/patterns/console/console-surface/example/src/pages/console.vue +20 -0
- package/skills/jskit/references/patterns/crud/crud-screen-set/PATTERN.md +88 -0
- package/skills/jskit/references/patterns/crud/crud-screen-set/example/books/BookEditPage.vue +54 -0
- package/skills/jskit/references/patterns/crud/crud-screen-set/example/books/BookFormFields.vue +52 -0
- package/skills/jskit/references/patterns/crud/crud-screen-set/example/books/BookListPage.vue +54 -0
- package/skills/jskit/references/patterns/crud/crud-screen-set/example/books/BookNewPage.vue +45 -0
- package/skills/jskit/references/patterns/crud/crud-screen-set/example/books/BookViewPage.vue +50 -0
- package/skills/jskit/references/patterns/crud/crud-screen-set/example/books/formFields.js +31 -0
- package/skills/jskit/references/patterns/crud/crud-screen-set/example/books/listExtensions.js +9 -0
- package/skills/jskit/references/patterns/crud/json-api-resource-package/PATTERN.md +160 -0
- package/skills/jskit/references/patterns/crud/json-api-resource-package/example/migrations/20260815000000_books.cjs +22 -0
- package/skills/jskit/references/patterns/crud/json-api-resource-package/example/packages/books/package.json +41 -0
- package/skills/jskit/references/patterns/crud/json-api-resource-package/example/packages/books/src/server/BooksFeature.js +13 -0
- package/skills/jskit/references/patterns/crud/json-api-resource-package/example/packages/books/src/shared/bookResource.js +83 -0
- package/skills/jskit/references/patterns/crud/json-api-resource-package/example/packages/books/src/shared/index.js +1 -0
- package/skills/jskit/references/patterns/crud/resource-contract/PATTERN.md +102 -0
- package/skills/jskit/references/patterns/crud/resource-contract/example/bookResource.js +87 -0
- package/skills/jskit/references/patterns/database/mysql-application/PATTERN.md +80 -0
- package/skills/jskit/references/patterns/database/mysql-application/example/.env.example +5 -0
- package/skills/jskit/references/patterns/database/mysql-application/example/.github/workflows/verify.yml +39 -0
- package/skills/jskit/references/patterns/database/mysql-application/example/knexfile.js +11 -0
- package/skills/jskit/references/patterns/database/mysql-application/example/package.json +13 -0
- package/skills/jskit/references/patterns/database/mysql-application/example/scripts/prepare-database.js +3 -0
- package/skills/jskit/references/patterns/database/postgres-application/PATTERN.md +80 -0
- package/skills/jskit/references/patterns/database/postgres-application/example/.env.example +5 -0
- package/skills/jskit/references/patterns/database/postgres-application/example/.github/workflows/verify.yml +38 -0
- package/skills/jskit/references/patterns/database/postgres-application/example/knexfile.js +11 -0
- package/skills/jskit/references/patterns/database/postgres-application/example/package.json +13 -0
- package/skills/jskit/references/patterns/database/postgres-application/example/scripts/prepare-database.js +3 -0
- package/skills/jskit/references/patterns/mobile/android-application/PATTERN.md +71 -0
- package/skills/jskit/references/patterns/mobile/android-application/example/capacitor.config.json +10 -0
- package/skills/jskit/references/patterns/realtime/realtime-application/PATTERN.md +69 -0
- package/skills/jskit/references/patterns/realtime/realtime-application/example/.env.example +1 -0
- package/skills/jskit/references/patterns/realtime/realtime-application/example/package.json +8 -0
- package/skills/jskit/references/patterns/realtime/realtime-application/example/src/placement.js +17 -0
- package/skills/jskit/references/patterns/server/feature-package/PATTERN.md +108 -0
- package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/package.json +48 -0
- package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/src/server/BookingEngineProvider.js +33 -0
- package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/src/server/actions.js +26 -0
- package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/src/server/inputSchemas.js +19 -0
- package/skills/jskit/references/patterns/server/feature-package/example/variations/AvailabilityEngineProvider.js +31 -0
- package/skills/jskit/references/patterns/server/feature-package/example/variations/InvoiceRollupProvider.js +36 -0
- package/skills/jskit/references/patterns/server/feature-package/example/variations/customKnexRepository.js +34 -0
- package/skills/jskit/references/patterns/server/feature-package/example/variations/orchestratorService.js +23 -0
- package/skills/jskit/references/patterns/shell/application-shell/PATTERN.md +66 -0
- package/skills/jskit/references/patterns/shell/application-shell/example/expected-existing/src/App.vue +13 -0
- package/skills/jskit/references/patterns/shell/application-shell/example/expected-existing/src/pages/home/index.vue +48 -0
- package/skills/jskit/references/patterns/shell/application-shell/example/expected-existing/src/pages/home.vue +13 -0
- package/skills/jskit/references/patterns/shell/application-shell/example/src/App.vue +11 -0
- package/skills/jskit/references/patterns/shell/application-shell/example/src/components/ShellLayout.vue +13 -0
- package/skills/jskit/references/patterns/shell/application-shell/example/src/components/menus/MenuLinkItem.vue +30 -0
- package/skills/jskit/references/patterns/shell/application-shell/example/src/components/menus/SurfaceAwareMenuLinkItem.vue +42 -0
- package/skills/jskit/references/patterns/shell/application-shell/example/src/components/menus/TabLinkItem.vue +42 -0
- package/skills/jskit/references/patterns/shell/application-shell/example/src/error.js +19 -0
- package/skills/jskit/references/patterns/shell/application-shell/example/src/pages/home/index.vue +106 -0
- package/skills/jskit/references/patterns/shell/application-shell/example/src/pages/home/settings/general/index.vue +40 -0
- package/skills/jskit/references/patterns/shell/application-shell/example/src/pages/home/settings/index.vue +7 -0
- package/skills/jskit/references/patterns/shell/application-shell/example/src/pages/home/settings.vue +109 -0
- package/skills/jskit/references/patterns/shell/application-shell/example/src/pages/home.vue +20 -0
- package/skills/jskit/references/patterns/shell/application-shell/example/src/placement.js +56 -0
- package/skills/jskit/references/patterns/shell/application-shell/example/src/placementTopology.js +149 -0
- package/skills/jskit/references/patterns/shell/application-shell/example/tests/e2e/adaptive-shell.spec.ts +10 -0
- package/skills/jskit/references/patterns/ui/page-and-placement/PATTERN.md +76 -0
- package/skills/jskit/references/patterns/ui/page-and-placement/example/packages/main/src/client/providers/MainClientProvider.js +14 -0
- package/skills/jskit/references/patterns/ui/page-and-placement/example/src/components/SyncStatusElement.vue +16 -0
- package/skills/jskit/references/patterns/ui/page-and-placement/example/src/pages/home/reports/activity.vue +8 -0
- package/skills/jskit/references/patterns/ui/page-and-placement/example/src/pages/home/reports/index.vue +7 -0
- package/skills/jskit/references/patterns/ui/page-and-placement/example/src/pages/home/reports/overview.vue +8 -0
- package/skills/jskit/references/patterns/ui/page-and-placement/example/src/pages/home/reports.vue +33 -0
- package/skills/jskit/references/patterns/ui/page-and-placement/example/src/placement.js +52 -0
- package/skills/jskit/references/patterns/ui/page-and-placement/example/src/placementTopology.js +29 -0
- package/skills/jskit/references/patterns/users/account-settings/PATTERN.md +61 -0
- package/skills/jskit/references/patterns/users/account-settings/example/src/components/account/settings/AccountSettingsNotificationsSection.vue +81 -0
- package/skills/jskit/references/patterns/users/account-settings/example/src/components/account/settings/AccountSettingsPreferencesSection.vue +151 -0
- package/skills/jskit/references/patterns/users/account-settings/example/src/components/account/settings/AccountSettingsProfileSection.vue +120 -0
- package/skills/jskit/references/patterns/users/account-settings/example/src/pages/account/index.vue +17 -0
- package/skills/jskit/references/patterns/users/user-administration-server/PATTERN.md +64 -0
- package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users/package.json +42 -0
- package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users/src/server/UsersFeature.js +13 -0
- package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users/src/shared/index.js +1 -0
- package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users/src/shared/userResource.js +69 -0
- package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users-workspace/package.json +43 -0
- package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users-workspace/src/server/UsersWorkspaceFeature.js +22 -0
- package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users-workspace/src/shared/index.js +1 -0
- package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users-workspace/src/shared/userResource.js +56 -0
- package/skills/jskit/references/patterns/workspaces/workspace-server/PATTERN.md +60 -0
- package/skills/jskit/references/patterns/workspaces/workspace-server/example/config/roles.js +27 -0
- package/skills/jskit/references/patterns/workspaces/workspace-server/example/packages/main/src/server/email/workspaceInviteEmail.js +23 -0
- package/skills/jskit/references/patterns/workspaces/workspace-surfaces/PATTERN.md +63 -0
- package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/packages/main/src/client/components/AccountPendingInvitesCue.vue +106 -0
- package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/packages/main/src/client/components/AccountSettingsInvitesSection.vue +7 -0
- package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/components/WorkspaceNotFoundCard.vue +53 -0
- package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/composables/useWorkspaceNotFoundState.js +41 -0
- package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/pages/admin/members/index.vue +7 -0
- package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/pages/admin/workspace/settings/index.vue +23 -0
- package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/pages/admin/workspace/settings.vue +95 -0
- package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/pages/invite/[token].vue +7 -0
- package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/surfaces/admin/index.vue +85 -0
- package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/surfaces/admin/root.vue +20 -0
- package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/surfaces/app/index.vue +46 -0
- package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/surfaces/app/root.vue +20 -0
- package/skills/jskit/references/ui-operations.md +5 -3
- package/templates/app/AGENTS.md +8 -8
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
import { useRoute } from "vue-router";
|
|
4
|
+
import CrudAddEditScreen from "@jskit-ai/http-web/client/components/CrudAddEditScreen";
|
|
5
|
+
import { useCrudAddEditScreen } from "@jskit-ai/http-web/client/composables/useCrudAddEditScreen";
|
|
6
|
+
import { bookResource } from "@local/books/shared";
|
|
7
|
+
import BookFormFields from "./BookFormFields.vue";
|
|
8
|
+
import { bookFormFields } from "./formFields.js";
|
|
9
|
+
|
|
10
|
+
const route = useRoute();
|
|
11
|
+
const routeRecordId = computed(() => String(route.params.bookId || "").trim());
|
|
12
|
+
|
|
13
|
+
const screen = useCrudAddEditScreen({
|
|
14
|
+
mode: "edit",
|
|
15
|
+
title: "Edit book",
|
|
16
|
+
subtitle: "Update this catalogue entry.",
|
|
17
|
+
saveLabel: "Save changes",
|
|
18
|
+
cancelTo: "/books/:bookId",
|
|
19
|
+
preserveCancelQuery: true,
|
|
20
|
+
resource: bookResource,
|
|
21
|
+
operationName: "patch",
|
|
22
|
+
formFields: bookFormFields,
|
|
23
|
+
addEditOptions: {
|
|
24
|
+
apiUrlTemplate: "/books/:bookId",
|
|
25
|
+
placementSource: "crud.books.edit",
|
|
26
|
+
writeMethod: "PATCH",
|
|
27
|
+
requestRecoveryLabel: "Book",
|
|
28
|
+
fallbackLoadError: "Unable to load this book.",
|
|
29
|
+
fallbackSaveError: "Unable to save this book.",
|
|
30
|
+
recordIdParam: "bookId",
|
|
31
|
+
routeRecordId,
|
|
32
|
+
viewUrlTemplate: "/books/:bookId",
|
|
33
|
+
listUrlTemplate: "/books"
|
|
34
|
+
},
|
|
35
|
+
saveSuccess: {
|
|
36
|
+
invalidateQueryKey: ["crud", "books"],
|
|
37
|
+
listUrlTemplate: "/books"
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<template>
|
|
43
|
+
<CrudAddEditScreen :screen="screen">
|
|
44
|
+
<template #fields="fieldRuntime">
|
|
45
|
+
<BookFormFields
|
|
46
|
+
v-model:title="fieldRuntime.formState.title"
|
|
47
|
+
v-model:author="fieldRuntime.formState.author"
|
|
48
|
+
v-model:notes="fieldRuntime.formState.notes"
|
|
49
|
+
:readonly="fieldRuntime.addEdit.isFieldLocked"
|
|
50
|
+
:resolve-field-errors="fieldRuntime.resolveFieldErrors"
|
|
51
|
+
/>
|
|
52
|
+
</template>
|
|
53
|
+
</CrudAddEditScreen>
|
|
54
|
+
</template>
|
package/skills/jskit/references/patterns/crud/crud-screen-set/example/books/BookFormFields.vue
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
const title = defineModel("title", { type: String, default: "" });
|
|
3
|
+
const author = defineModel("author", { type: String, default: "" });
|
|
4
|
+
const notes = defineModel("notes", { type: String, default: "" });
|
|
5
|
+
|
|
6
|
+
defineProps({
|
|
7
|
+
readonly: {
|
|
8
|
+
type: Boolean,
|
|
9
|
+
default: false
|
|
10
|
+
},
|
|
11
|
+
resolveFieldErrors: {
|
|
12
|
+
type: Function,
|
|
13
|
+
required: true
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<template>
|
|
19
|
+
<v-col cols="12" md="6">
|
|
20
|
+
<v-text-field
|
|
21
|
+
v-model="title"
|
|
22
|
+
label="Title"
|
|
23
|
+
variant="outlined"
|
|
24
|
+
density="comfortable"
|
|
25
|
+
maxlength="255"
|
|
26
|
+
:readonly="readonly"
|
|
27
|
+
:error-messages="resolveFieldErrors('title')"
|
|
28
|
+
/>
|
|
29
|
+
</v-col>
|
|
30
|
+
<v-col cols="12" md="6">
|
|
31
|
+
<v-text-field
|
|
32
|
+
v-model="author"
|
|
33
|
+
label="Author"
|
|
34
|
+
variant="outlined"
|
|
35
|
+
density="comfortable"
|
|
36
|
+
maxlength="255"
|
|
37
|
+
:readonly="readonly"
|
|
38
|
+
:error-messages="resolveFieldErrors('author')"
|
|
39
|
+
/>
|
|
40
|
+
</v-col>
|
|
41
|
+
<v-col cols="12">
|
|
42
|
+
<v-textarea
|
|
43
|
+
v-model="notes"
|
|
44
|
+
label="Notes"
|
|
45
|
+
variant="outlined"
|
|
46
|
+
density="comfortable"
|
|
47
|
+
auto-grow
|
|
48
|
+
:readonly="readonly"
|
|
49
|
+
:error-messages="resolveFieldErrors('notes')"
|
|
50
|
+
/>
|
|
51
|
+
</v-col>
|
|
52
|
+
</template>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import CrudListScreen from "@jskit-ai/http-web/client/components/CrudListScreen";
|
|
3
|
+
import { useCrudListScreen } from "@jskit-ai/http-web/client/composables/useCrudListScreen";
|
|
4
|
+
import { bookResource } from "@local/books/shared";
|
|
5
|
+
import { listBulkActions, listFilters, listRowActions } from "./listExtensions.js";
|
|
6
|
+
|
|
7
|
+
const screen = useCrudListScreen({
|
|
8
|
+
resource: bookResource,
|
|
9
|
+
resourceNamespace: "books",
|
|
10
|
+
apiSuffix: "/books",
|
|
11
|
+
recordIdParam: "bookId",
|
|
12
|
+
recordIdSelector: (book = {}) => book.id,
|
|
13
|
+
titleFallbackFieldKey: "title",
|
|
14
|
+
viewUrlTemplate: "/books/:bookId",
|
|
15
|
+
editUrlTemplate: "/books/:bookId/edit",
|
|
16
|
+
newUrlTemplate: "/books/new",
|
|
17
|
+
listFilters,
|
|
18
|
+
listBulkActions,
|
|
19
|
+
listRowActions,
|
|
20
|
+
requestRecoveryLabel: "Books",
|
|
21
|
+
fallbackLoadError: "Unable to load books."
|
|
22
|
+
});
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<template>
|
|
26
|
+
<CrudListScreen
|
|
27
|
+
:screen="screen"
|
|
28
|
+
title-label="Books"
|
|
29
|
+
heading-title="My books"
|
|
30
|
+
subtitle="Search and maintain your catalogue."
|
|
31
|
+
create-label="Add book"
|
|
32
|
+
empty-title="No books yet"
|
|
33
|
+
empty-body="Add your first book to begin the catalogue."
|
|
34
|
+
>
|
|
35
|
+
<template #card-fields="{ record, formatListCardValue }">
|
|
36
|
+
<dl class="d-grid ga-2 mb-0">
|
|
37
|
+
<div>
|
|
38
|
+
<dt class="text-caption text-medium-emphasis">Author</dt>
|
|
39
|
+
<dd class="text-body-2 mb-0">{{ formatListCardValue(record.author) }}</dd>
|
|
40
|
+
</div>
|
|
41
|
+
</dl>
|
|
42
|
+
</template>
|
|
43
|
+
|
|
44
|
+
<template #table-header>
|
|
45
|
+
<th>Title</th>
|
|
46
|
+
<th>Author</th>
|
|
47
|
+
</template>
|
|
48
|
+
|
|
49
|
+
<template #table-row="{ record }">
|
|
50
|
+
<td>{{ record.title }}</td>
|
|
51
|
+
<td>{{ record.author }}</td>
|
|
52
|
+
</template>
|
|
53
|
+
</CrudListScreen>
|
|
54
|
+
</template>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import CrudAddEditScreen from "@jskit-ai/http-web/client/components/CrudAddEditScreen";
|
|
3
|
+
import { useCrudAddEditScreen } from "@jskit-ai/http-web/client/composables/useCrudAddEditScreen";
|
|
4
|
+
import { bookResource } from "@local/books/shared";
|
|
5
|
+
import BookFormFields from "./BookFormFields.vue";
|
|
6
|
+
import { bookFormFields } from "./formFields.js";
|
|
7
|
+
|
|
8
|
+
const screen = useCrudAddEditScreen({
|
|
9
|
+
mode: "new",
|
|
10
|
+
title: "Add book",
|
|
11
|
+
subtitle: "Add a book to your catalogue.",
|
|
12
|
+
saveLabel: "Save book",
|
|
13
|
+
cancelTo: "/books",
|
|
14
|
+
resource: bookResource,
|
|
15
|
+
operationName: "create",
|
|
16
|
+
formFields: bookFormFields,
|
|
17
|
+
addEditOptions: {
|
|
18
|
+
apiSuffix: "/books",
|
|
19
|
+
placementSource: "crud.books.new",
|
|
20
|
+
readEnabled: false,
|
|
21
|
+
writeMethod: "POST",
|
|
22
|
+
recordIdParam: "bookId",
|
|
23
|
+
viewUrlTemplate: "/books/:bookId",
|
|
24
|
+
listUrlTemplate: "/books"
|
|
25
|
+
},
|
|
26
|
+
saveSuccess: {
|
|
27
|
+
invalidateQueryKey: ["crud", "books"],
|
|
28
|
+
listUrlTemplate: "/books"
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<template>
|
|
34
|
+
<CrudAddEditScreen :screen="screen">
|
|
35
|
+
<template #fields="fieldRuntime">
|
|
36
|
+
<BookFormFields
|
|
37
|
+
v-model:title="fieldRuntime.formState.title"
|
|
38
|
+
v-model:author="fieldRuntime.formState.author"
|
|
39
|
+
v-model:notes="fieldRuntime.formState.notes"
|
|
40
|
+
:readonly="fieldRuntime.addEdit.isFieldLocked"
|
|
41
|
+
:resolve-field-errors="fieldRuntime.resolveFieldErrors"
|
|
42
|
+
/>
|
|
43
|
+
</template>
|
|
44
|
+
</CrudAddEditScreen>
|
|
45
|
+
</template>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import CrudDeleteAction from "@jskit-ai/http-web/client/components/CrudDeleteAction";
|
|
3
|
+
import CrudViewScreen from "@jskit-ai/http-web/client/components/CrudViewScreen";
|
|
4
|
+
import { useCrudDeleteAction } from "@jskit-ai/http-web/client/composables/useCrudDeleteAction";
|
|
5
|
+
import { useCrudViewScreen } from "@jskit-ai/http-web/client/composables/useCrudViewScreen";
|
|
6
|
+
import { bookResource } from "@local/books/shared";
|
|
7
|
+
|
|
8
|
+
const screen = useCrudViewScreen({
|
|
9
|
+
resource: bookResource,
|
|
10
|
+
resourceNamespace: "books",
|
|
11
|
+
apiUrlTemplate: "/books/:bookId",
|
|
12
|
+
recordIdParam: "bookId",
|
|
13
|
+
titleFallbackFieldKey: "title",
|
|
14
|
+
listUrlTemplate: "/books",
|
|
15
|
+
editUrlTemplate: "/books/:bookId/edit",
|
|
16
|
+
requestRecoveryLabel: "Book",
|
|
17
|
+
fallbackLoadError: "Unable to load this book."
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const deleteAction = useCrudDeleteAction({
|
|
21
|
+
screen,
|
|
22
|
+
resource: bookResource,
|
|
23
|
+
resourceNamespace: "books",
|
|
24
|
+
apiUrlTemplate: "/books/:bookId"
|
|
25
|
+
});
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<template>
|
|
29
|
+
<CrudViewScreen
|
|
30
|
+
:screen="screen"
|
|
31
|
+
resource-singular-title="Book"
|
|
32
|
+
resource-plural-title="Books"
|
|
33
|
+
description="Review this catalogue entry."
|
|
34
|
+
>
|
|
35
|
+
<template #actions>
|
|
36
|
+
<CrudDeleteAction :action="deleteAction" resource-singular-title="Book" />
|
|
37
|
+
</template>
|
|
38
|
+
|
|
39
|
+
<template #fields="{ view }">
|
|
40
|
+
<v-col cols="12" md="6">
|
|
41
|
+
<div class="text-caption text-medium-emphasis">Author</div>
|
|
42
|
+
<div class="text-body-1">{{ view.record?.author }}</div>
|
|
43
|
+
</v-col>
|
|
44
|
+
<v-col cols="12">
|
|
45
|
+
<div class="text-caption text-medium-emphasis">Notes</div>
|
|
46
|
+
<div class="text-body-1 text-pre-wrap">{{ view.record?.notes || "No notes" }}</div>
|
|
47
|
+
</v-col>
|
|
48
|
+
</template>
|
|
49
|
+
</CrudViewScreen>
|
|
50
|
+
</template>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const bookFormFields = Object.freeze([
|
|
2
|
+
Object.freeze({
|
|
3
|
+
key: "title",
|
|
4
|
+
label: "Title",
|
|
5
|
+
type: "string",
|
|
6
|
+
nullable: false,
|
|
7
|
+
inputType: "text",
|
|
8
|
+
component: "text",
|
|
9
|
+
maxLength: 255
|
|
10
|
+
}),
|
|
11
|
+
Object.freeze({
|
|
12
|
+
key: "author",
|
|
13
|
+
label: "Author",
|
|
14
|
+
type: "string",
|
|
15
|
+
nullable: false,
|
|
16
|
+
inputType: "text",
|
|
17
|
+
component: "text",
|
|
18
|
+
maxLength: 255
|
|
19
|
+
}),
|
|
20
|
+
Object.freeze({
|
|
21
|
+
key: "notes",
|
|
22
|
+
label: "Notes",
|
|
23
|
+
type: "string",
|
|
24
|
+
nullable: true,
|
|
25
|
+
inputType: "textarea",
|
|
26
|
+
component: "textarea",
|
|
27
|
+
maxLength: 65535
|
|
28
|
+
})
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
export { bookFormFields };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { defineCrudListBulkActions } from "@jskit-ai/http-web/client/bulkActions";
|
|
2
|
+
import { defineCrudListFilters } from "@jskit-ai/http-web/client/filters";
|
|
3
|
+
import { defineCrudListRowActions } from "@jskit-ai/http-web/client/rowActions";
|
|
4
|
+
|
|
5
|
+
const listFilters = defineCrudListFilters({});
|
|
6
|
+
const listBulkActions = defineCrudListBulkActions([]);
|
|
7
|
+
const listRowActions = defineCrudListRowActions([]);
|
|
8
|
+
|
|
9
|
+
export { listBulkActions, listFilters, listRowActions };
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: crud/json-api-resource-package
|
|
3
|
+
title: JSON API CRUD resource package
|
|
4
|
+
summary: Build a complete application-owned CRUD server package from a migration, resource contract, and one framework module declaration.
|
|
5
|
+
keywords: actions, crud, database, json-api, migration, permissions, provider, repository, routes, service
|
|
6
|
+
requires: @jskit-ai/crud-core, @jskit-ai/resource-crud-core
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# JSON API CRUD resource package
|
|
10
|
+
|
|
11
|
+
## Use when
|
|
12
|
+
|
|
13
|
+
Use this pattern when a product entity needs conventional JSON API list, view,
|
|
14
|
+
create, patch, and delete behavior. JSKIT owns the repeated repository, service,
|
|
15
|
+
action, route, and provider mechanics. The application owns the migration,
|
|
16
|
+
resource contract, access surface, ownership policy, and package composition.
|
|
17
|
+
|
|
18
|
+
## Do not use when
|
|
19
|
+
|
|
20
|
+
Do not use this pattern for a command-oriented workflow, aggregate, import job,
|
|
21
|
+
multi-resource transaction, or domain with materially different operations.
|
|
22
|
+
Use a feature package and explicit services for those cases.
|
|
23
|
+
|
|
24
|
+
## Product decisions
|
|
25
|
+
|
|
26
|
+
Decide the entity fields, validation, search, sorting, access surface, ownership,
|
|
27
|
+
enabled operations, route name, permissions, and user-facing messages. Author
|
|
28
|
+
those decisions in normal migration and resource source. Do not derive them
|
|
29
|
+
from a generator questionnaire or treat a live table as the sole contract.
|
|
30
|
+
|
|
31
|
+
## Invariants
|
|
32
|
+
|
|
33
|
+
- Migration and resource storage metadata describe the same table.
|
|
34
|
+
- `apiAccess`, surface access, and `autofilter` agree.
|
|
35
|
+
- Public routes use public ownership only.
|
|
36
|
+
- Workspace routes receive explicit scope validators and route-to-action input.
|
|
37
|
+
- Application source contains one module declaration, not copied generic CRUD
|
|
38
|
+
repository, service, action, route, and provider files.
|
|
39
|
+
- Current migration/resource/source state is authoritative; there is no receipt,
|
|
40
|
+
provenance marker, generator identity, or field-patching lane.
|
|
41
|
+
|
|
42
|
+
## Framework APIs
|
|
43
|
+
|
|
44
|
+
Use `defineCrudResource()` from `@jskit-ai/resource-crud-core` and
|
|
45
|
+
`defineCrudJsonApiFeature()` from `@jskit-ai/crud-core`. The latter composes the
|
|
46
|
+
JSON REST resource, repository, service events, standard CRUD actions, route
|
|
47
|
+
contracts, access policy, and HTTP routes.
|
|
48
|
+
|
|
49
|
+
## Example files
|
|
50
|
+
|
|
51
|
+
`example/` contains a complete books package plus a source-controlled migration.
|
|
52
|
+
`BooksFeature.js` is deliberately tiny: it binds the readable resource contract
|
|
53
|
+
to the framework module and exports the resulting provider.
|
|
54
|
+
|
|
55
|
+
## Variation points
|
|
56
|
+
|
|
57
|
+
Change the package name, resource fields, table, surface, ownership filter,
|
|
58
|
+
relative route, messages, and migration. Supply explicit permissions when the
|
|
59
|
+
surface uses named policy. For workspace ownership, add the workspace route and
|
|
60
|
+
action validators through the module's `scope` option. The feature exposes the
|
|
61
|
+
operations declared by the resource. Use `operations` only to narrow that
|
|
62
|
+
surface further; do not register unused mutation actions and routes. Use
|
|
63
|
+
`routes: false` only for an internal
|
|
64
|
+
capability/action surface that deliberately has no HTTP API.
|
|
65
|
+
|
|
66
|
+
When otherwise-standard CRUD earns product behavior, name the exact
|
|
67
|
+
capabilities in `requires` and provide `decorateService({ service, repository,
|
|
68
|
+
resource, ...dependencies })`. Retain the standard methods, override only the
|
|
69
|
+
methods whose CRUD behavior changes, and add resource-related domain methods
|
|
70
|
+
such as `confirmBooking()` or `sendReminder()` directly to that service. Those
|
|
71
|
+
additional methods remain available through the resource capability. When the
|
|
72
|
+
product needs only a few additional queries or locking operations, provide
|
|
73
|
+
`decorateRepository({ repository, database, http, jsonRestApi, resource,
|
|
74
|
+
...dependencies })`; return the standard methods plus only those unique
|
|
75
|
+
persistence operations instead of copying the CRUD repository.
|
|
76
|
+
|
|
77
|
+
Use `operationLifecycle` for behavior surrounding a standard operation. Its
|
|
78
|
+
per-operation `before`, `execute`, and `after` phases share one repository
|
|
79
|
+
transaction for create, update, and delete. An `execute` hook receives
|
|
80
|
+
`standard(nextInput)` so it can retain the framework write while adjusting its
|
|
81
|
+
input or coordinating other repository work. `afterCommit` runs only after the
|
|
82
|
+
mutation transaction succeeds. Put durable external delivery behind an outbox
|
|
83
|
+
written inside the transaction; do not make an email or webhook the arbiter of
|
|
84
|
+
whether an already-committed request appears successful.
|
|
85
|
+
|
|
86
|
+
Declare resource-related non-CRUD commands in `actions`. Each named action has
|
|
87
|
+
a normal JSKIT input, permission, audit, events, and `execute({ input, context,
|
|
88
|
+
service, repository, ...dependencies })` contract. Its optional `route`
|
|
89
|
+
declares an HTTP method, path below the resource base, explicit route contract,
|
|
90
|
+
status code, and optional request-to-action input mapper. This is the normal
|
|
91
|
+
home for `confirm`, `publish`, `cancel`, `reorder`, or `sendReminder`. Move to a
|
|
92
|
+
separate explicit feature only when the operation belongs to another domain or
|
|
93
|
+
the resource is no longer principally CRUD.
|
|
94
|
+
|
|
95
|
+
If a product-specific list filter needs additional transport validation and
|
|
96
|
+
JSON API search mapping, pass its validator as `listFilterQueryValidator` and
|
|
97
|
+
its storage mapping as `searchSchema`; keep that contract next to the feature.
|
|
98
|
+
Use `operationInputs.create` or `operationInputs.update` when a standard CRUD
|
|
99
|
+
operation intentionally accepts a small virtual product field that is handled
|
|
100
|
+
by the service decorator rather than stored on the resource row.
|
|
101
|
+
|
|
102
|
+
```js
|
|
103
|
+
const BookingsFeature = defineCrudJsonApiFeature({
|
|
104
|
+
resource: bookingResource,
|
|
105
|
+
surface: "app",
|
|
106
|
+
decorateService({ service, repository }) {
|
|
107
|
+
return {
|
|
108
|
+
...service,
|
|
109
|
+
async confirmBooking(recordId, options) {
|
|
110
|
+
return repository.confirmBooking(recordId, options);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
},
|
|
114
|
+
operationLifecycle: {
|
|
115
|
+
update: {
|
|
116
|
+
async before({ input, service, trx, context }) {
|
|
117
|
+
await service.assertUpdateAllowed(input.recordId, { trx, context });
|
|
118
|
+
},
|
|
119
|
+
async afterCommit({ result, service, context }) {
|
|
120
|
+
await service.recordUpdateCompleted(result.data.id, { context });
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
actions: {
|
|
125
|
+
confirm: {
|
|
126
|
+
input: confirmBookingInput,
|
|
127
|
+
permission: { require: "all", permissions: ["bookings.confirm"] },
|
|
128
|
+
execute({ input, context, service }) {
|
|
129
|
+
return service.confirmBooking(input.recordId, { context });
|
|
130
|
+
},
|
|
131
|
+
route: {
|
|
132
|
+
method: "POST",
|
|
133
|
+
path: "/:recordId/confirm",
|
|
134
|
+
contract: confirmBookingRouteContract,
|
|
135
|
+
statusCode: 200
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Verification
|
|
143
|
+
|
|
144
|
+
- Rebuild the table from migrations in disposable MySQL and PostgreSQL databases
|
|
145
|
+
when both drivers are supported by the application.
|
|
146
|
+
- Validate representative accepted and rejected resource values.
|
|
147
|
+
- Exercise every enabled route and JSON API document shape.
|
|
148
|
+
- Prove positive owner access and negative cross-owner access.
|
|
149
|
+
- Verify public routes require neither identity nor CSRF only when explicitly
|
|
150
|
+
declared public.
|
|
151
|
+
- Run the package tests and application verifier.
|
|
152
|
+
|
|
153
|
+
## Avoid
|
|
154
|
+
|
|
155
|
+
- copied generic repository/service/action/route/provider boilerplate
|
|
156
|
+
- generator subcommands for adding fields
|
|
157
|
+
- editing an applied baseline migration instead of adding a new migration
|
|
158
|
+
- public access combined with user or workspace ownership
|
|
159
|
+
- implicit workspace route parameters or permissions
|
|
160
|
+
- receipt, replay, provenance, ownership-marker, or scaffold-shape files
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const TABLE_NAME = "books";
|
|
2
|
+
|
|
3
|
+
exports.up = async function up(knex) {
|
|
4
|
+
if (await knex.schema.hasTable(TABLE_NAME)) {
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
await knex.schema.createTable(TABLE_NAME, (table) => {
|
|
9
|
+
table.bigIncrements("id").primary();
|
|
10
|
+
table.bigInteger("user_id").unsigned().notNullable().index()
|
|
11
|
+
.references("id").inTable("users").onDelete("CASCADE");
|
|
12
|
+
table.string("title", 255).notNullable();
|
|
13
|
+
table.string("author", 255).notNullable();
|
|
14
|
+
table.text("notes").nullable();
|
|
15
|
+
table.timestamp("created_at", { useTz: false }).notNullable().defaultTo(knex.fn.now());
|
|
16
|
+
table.timestamp("updated_at", { useTz: false }).notNullable().defaultTo(knex.fn.now());
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
exports.down = async function down(knex) {
|
|
21
|
+
await knex.schema.dropTableIfExists(TABLE_NAME);
|
|
22
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@app/books",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
"./server/BooksFeature": "./src/server/BooksFeature.js",
|
|
8
|
+
"./shared": "./src/shared/index.js"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@jskit-ai/crud-core": "0.1.189",
|
|
12
|
+
"@jskit-ai/resource-crud-core": "0.1.120"
|
|
13
|
+
},
|
|
14
|
+
"jskit": {
|
|
15
|
+
"kind": "runtime",
|
|
16
|
+
"capabilities": {
|
|
17
|
+
"provides": [
|
|
18
|
+
"app.books"
|
|
19
|
+
],
|
|
20
|
+
"requires": [
|
|
21
|
+
"runtime.http",
|
|
22
|
+
"runtime.json-rest-api",
|
|
23
|
+
"runtime.actions",
|
|
24
|
+
"runtime.database"
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
"runtime": {
|
|
28
|
+
"server": {
|
|
29
|
+
"providers": [
|
|
30
|
+
{
|
|
31
|
+
"entrypoint": "src/server/BooksFeature.js",
|
|
32
|
+
"export": "BooksFeature"
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
"client": {
|
|
37
|
+
"providers": []
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { defineCrudJsonApiFeature } from "@jskit-ai/crud-core/server/defineCrudJsonApiFeature";
|
|
2
|
+
import { bookResource } from "../shared/bookResource.js";
|
|
3
|
+
|
|
4
|
+
const BooksFeature = defineCrudJsonApiFeature({
|
|
5
|
+
resource: bookResource,
|
|
6
|
+
id: "app.books",
|
|
7
|
+
capability: "app.books",
|
|
8
|
+
surface: "app",
|
|
9
|
+
ownershipFilter: "user",
|
|
10
|
+
relativePath: "/books"
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export { BooksFeature };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { defineCrudResource } from "@jskit-ai/resource-crud-core/shared/crudResource";
|
|
2
|
+
|
|
3
|
+
const bookResource = defineCrudResource({
|
|
4
|
+
namespace: "books",
|
|
5
|
+
tableName: "books",
|
|
6
|
+
apiAccess: "authenticated",
|
|
7
|
+
autofilter: "user",
|
|
8
|
+
schema: {
|
|
9
|
+
userId: {
|
|
10
|
+
type: "id",
|
|
11
|
+
required: true,
|
|
12
|
+
hidden: true,
|
|
13
|
+
operations: {}
|
|
14
|
+
},
|
|
15
|
+
title: {
|
|
16
|
+
type: "string",
|
|
17
|
+
maxLength: 255,
|
|
18
|
+
required: true,
|
|
19
|
+
search: true,
|
|
20
|
+
operations: {
|
|
21
|
+
output: { required: true },
|
|
22
|
+
create: { required: true },
|
|
23
|
+
patch: { required: false }
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
author: {
|
|
27
|
+
type: "string",
|
|
28
|
+
maxLength: 255,
|
|
29
|
+
required: true,
|
|
30
|
+
search: true,
|
|
31
|
+
operations: {
|
|
32
|
+
output: { required: true },
|
|
33
|
+
create: { required: true },
|
|
34
|
+
patch: { required: false }
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
notes: {
|
|
38
|
+
type: "string",
|
|
39
|
+
maxLength: 65535,
|
|
40
|
+
nullable: true,
|
|
41
|
+
search: true,
|
|
42
|
+
operations: {
|
|
43
|
+
output: { required: true },
|
|
44
|
+
create: { required: false },
|
|
45
|
+
patch: { required: false }
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
createdAt: {
|
|
49
|
+
type: "dateTime",
|
|
50
|
+
temporalPrecision: 0,
|
|
51
|
+
default: "now()",
|
|
52
|
+
storage: { writeSerializer: "datetime-utc" },
|
|
53
|
+
operations: { output: { required: true } }
|
|
54
|
+
},
|
|
55
|
+
updatedAt: {
|
|
56
|
+
type: "dateTime",
|
|
57
|
+
temporalPrecision: 0,
|
|
58
|
+
default: "now()",
|
|
59
|
+
storage: { writeSerializer: "datetime-utc" },
|
|
60
|
+
operations: { output: { required: true } }
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
searchSchema: {
|
|
64
|
+
id: { type: "id", actualField: "id" },
|
|
65
|
+
q: {
|
|
66
|
+
type: "string",
|
|
67
|
+
oneOf: ["title", "author", "notes"],
|
|
68
|
+
filterOperator: "like",
|
|
69
|
+
splitBy: " ",
|
|
70
|
+
matchAll: true
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
defaultSort: ["-createdAt"],
|
|
74
|
+
messages: {
|
|
75
|
+
validation: "Fix invalid values and try again.",
|
|
76
|
+
saveSuccess: "Book saved.",
|
|
77
|
+
saveError: "Unable to save this book.",
|
|
78
|
+
deleteSuccess: "Book deleted.",
|
|
79
|
+
deleteError: "Unable to delete this book."
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
export { bookResource };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { bookResource } from "./bookResource.js";
|