spine-framework 0.1.0
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/.framework/README.md +129 -0
- package/.framework/cli/bin.cjs +14 -0
- package/.framework/cli/commands/agents.ts +153 -0
- package/.framework/cli/commands/auth.ts +94 -0
- package/.framework/cli/commands/create-app.ts +185 -0
- package/.framework/cli/commands/dev.ts +295 -0
- package/.framework/cli/commands/doctor.ts +442 -0
- package/.framework/cli/commands/generate.ts +332 -0
- package/.framework/cli/commands/init.ts +272 -0
- package/.framework/cli/commands/install-app.ts +391 -0
- package/.framework/cli/commands/items.ts +253 -0
- package/.framework/cli/commands/migrations.ts +141 -0
- package/.framework/cli/commands/pipelines.ts +166 -0
- package/.framework/cli/commands/status.ts +197 -0
- package/.framework/cli/commands/system.ts +184 -0
- package/.framework/cli/commands/test.ts +227 -0
- package/.framework/cli/commands/uninstall-app.ts +166 -0
- package/.framework/cli/context.ts +268 -0
- package/.framework/cli/env-loader.ts +36 -0
- package/.framework/cli/index.ts +106 -0
- package/.framework/cli/welcome.cjs +45 -0
- package/.framework/docs/API.md +384 -0
- package/.framework/docs/STABILITY.md +52 -0
- package/.framework/docs/admin-routes.md +76 -0
- package/.framework/docs/api-docs-progress.md +38 -0
- package/.framework/docs/api-governance.md +146 -0
- package/.framework/docs/api-testing-results.md +212 -0
- package/.framework/docs/apis/admin-configs.md +567 -0
- package/.framework/docs/apis/admin-data.md +272 -0
- package/.framework/docs/apis/index.md +231 -0
- package/.framework/docs/apis/internal.md +295 -0
- package/.framework/docs/apis/runtime.md +537 -0
- package/.framework/docs/assembly-launch-guide.md +138 -0
- package/.framework/docs/audit-results.md +590 -0
- package/.framework/docs/authorization-model.md +170 -0
- package/.framework/docs/db-api-inventory.md +95 -0
- package/.framework/docs/examples/custom-app/README.md +77 -0
- package/.framework/docs/examples/custom-function/README.md +27 -0
- package/.framework/docs/examples/custom-function/handler.ts +48 -0
- package/.framework/docs/examples/custom-webhook/README.md +68 -0
- package/.framework/docs/gap-remediation-backlog.md +103 -0
- package/.framework/docs/guides/cli-guide.md +224 -0
- package/.framework/docs/guides/getting-started.md +103 -0
- package/.framework/docs/guides/import-guide.md +193 -0
- package/.framework/docs/guides/testing-guide.md +229 -0
- package/.framework/docs/permission-examples.md +326 -0
- package/.framework/docs/ui-adoption-verification.md +111 -0
- package/.framework/docs/ui-api-coverage.md +84 -0
- package/.framework/docs/v2-compatibility-audit.md +228 -0
- package/.framework/functions/.gitkeep +1 -0
- package/.framework/functions/_shared/agent-runner.ts +1097 -0
- package/.framework/functions/_shared/app-manifest.ts +184 -0
- package/.framework/functions/_shared/audit.ts +150 -0
- package/.framework/functions/_shared/db.ts +174 -0
- package/.framework/functions/_shared/index.ts +382 -0
- package/.framework/functions/_shared/middleware.ts +490 -0
- package/.framework/functions/_shared/permissions.ts +1325 -0
- package/.framework/functions/_shared/pipeline-runner.ts +731 -0
- package/.framework/functions/_shared/principal.ts +760 -0
- package/.framework/functions/_shared/schema-utils.ts +967 -0
- package/.framework/functions/_shared/testing.ts +258 -0
- package/.framework/functions/_shared/trigger-engine.ts +425 -0
- package/.framework/functions/_shared/webhook-registration.ts +168 -0
- package/.framework/functions/_shared/webhook-registry.ts +129 -0
- package/.framework/functions/account-nodes.ts +111 -0
- package/.framework/functions/admin-data.ts +606 -0
- package/.framework/functions/ai-agents.ts +323 -0
- package/.framework/functions/api-keys.ts +376 -0
- package/.framework/functions/apps.ts +483 -0
- package/.framework/functions/auth.ts +196 -0
- package/.framework/functions/debug-auth.ts +107 -0
- package/.framework/functions/embeddings.ts +556 -0
- package/.framework/functions/integration-routes.ts +523 -0
- package/.framework/functions/integrations.ts +319 -0
- package/.framework/functions/item-progress.ts +272 -0
- package/.framework/functions/logs.ts +438 -0
- package/.framework/functions/observability.ts +275 -0
- package/.framework/functions/pipeline-executions.ts +494 -0
- package/.framework/functions/pipelines.ts +485 -0
- package/.framework/functions/prompt-configs.ts +339 -0
- package/.framework/functions/roles.ts +387 -0
- package/.framework/functions/system-cron.ts +742 -0
- package/.framework/functions/system.ts +323 -0
- package/.framework/functions/tests.ts +119 -0
- package/.framework/functions/timers.ts +357 -0
- package/.framework/functions/triggers.ts +563 -0
- package/.framework/functions/types.ts +604 -0
- package/.framework/migrations/000_foundation.sql +1256 -0
- package/.framework/migrations/001_seed.sql +92 -0
- package/.framework/migrations/002_seed_constraints.sql +13 -0
- package/.framework/migrations/003_auth_user_trigger.sql +59 -0
- package/.framework/src/App.tsx +126 -0
- package/.framework/src/apps/admin/index.tsx +173 -0
- package/.framework/src/components/AppWrapper.tsx +56 -0
- package/.framework/src/components/CustomAppLoader.tsx +116 -0
- package/.framework/src/components/admin/AdminListPage.tsx +151 -0
- package/.framework/src/components/admin/AdminSidebar.tsx +166 -0
- package/.framework/src/components/admin/AdminStatsCard.tsx +62 -0
- package/.framework/src/components/admin/SortableTableHeader.tsx +42 -0
- package/.framework/src/components/app-shell/GenericAppShell.tsx +181 -0
- package/.framework/src/components/app-shell/GenericDetailPage.tsx +200 -0
- package/.framework/src/components/app-shell/GenericListPage.tsx +116 -0
- package/.framework/src/components/app-sidebar.tsx +228 -0
- package/.framework/src/components/auth/ProtectedRoute.tsx +88 -0
- package/.framework/src/components/layout/AppShell.tsx +91 -0
- package/.framework/src/components/layout/Header.tsx +88 -0
- package/.framework/src/components/layout/Layout.tsx +95 -0
- package/.framework/src/components/layout/Sidebar.tsx +329 -0
- package/.framework/src/components/runtime/DataDetailHeader.tsx +77 -0
- package/.framework/src/components/runtime/DataDetailPage.tsx +171 -0
- package/.framework/src/components/runtime/DataFilters.tsx +91 -0
- package/.framework/src/components/runtime/DataHeader.tsx +68 -0
- package/.framework/src/components/runtime/DataListPage.tsx +124 -0
- package/.framework/src/components/runtime/DataStats.tsx +70 -0
- package/.framework/src/components/runtime/DataTable.tsx +174 -0
- package/.framework/src/components/runtime/SchemaDetailForm.tsx +134 -0
- package/.framework/src/components/runtime/index.ts +18 -0
- package/.framework/src/components/search-form.tsx +29 -0
- package/.framework/src/components/shared/AgentView.tsx +213 -0
- package/.framework/src/components/shared/FieldRenderer.tsx +478 -0
- package/.framework/src/components/shared/SchemaFields.tsx +226 -0
- package/.framework/src/components/ui/DataTable.tsx +343 -0
- package/.framework/src/components/ui/Form.tsx +281 -0
- package/.framework/src/components/ui/ItemCard.tsx +296 -0
- package/.framework/src/components/ui/ItemListView.tsx +308 -0
- package/.framework/src/components/ui/LoadingSpinner.tsx +52 -0
- package/.framework/src/components/ui/Modal.tsx +61 -0
- package/.framework/src/components/ui/RichTextEditor.tsx +210 -0
- package/.framework/src/components/ui/accordion.tsx +82 -0
- package/.framework/src/components/ui/alert-dialog.tsx +197 -0
- package/.framework/src/components/ui/alert.tsx +76 -0
- package/.framework/src/components/ui/aspect-ratio.tsx +11 -0
- package/.framework/src/components/ui/avatar.tsx +110 -0
- package/.framework/src/components/ui/badge.tsx +49 -0
- package/.framework/src/components/ui/breadcrumb.tsx +122 -0
- package/.framework/src/components/ui/button-group.tsx +83 -0
- package/.framework/src/components/ui/button.tsx +65 -0
- package/.framework/src/components/ui/calendar.tsx +222 -0
- package/.framework/src/components/ui/card.tsx +100 -0
- package/.framework/src/components/ui/carousel.tsx +240 -0
- package/.framework/src/components/ui/chart.tsx +373 -0
- package/.framework/src/components/ui/checkbox.tsx +31 -0
- package/.framework/src/components/ui/collapsible.tsx +33 -0
- package/.framework/src/components/ui/combobox.tsx +299 -0
- package/.framework/src/components/ui/command.tsx +193 -0
- package/.framework/src/components/ui/context-menu.tsx +261 -0
- package/.framework/src/components/ui/dialog.tsx +165 -0
- package/.framework/src/components/ui/direction.tsx +22 -0
- package/.framework/src/components/ui/drawer.tsx +132 -0
- package/.framework/src/components/ui/dropdown-menu.tsx +269 -0
- package/.framework/src/components/ui/empty.tsx +104 -0
- package/.framework/src/components/ui/field.tsx +238 -0
- package/.framework/src/components/ui/hover-card.tsx +42 -0
- package/.framework/src/components/ui/input-group.tsx +153 -0
- package/.framework/src/components/ui/input-otp.tsx +87 -0
- package/.framework/src/components/ui/input.tsx +19 -0
- package/.framework/src/components/ui/item.tsx +196 -0
- package/.framework/src/components/ui/kbd.tsx +26 -0
- package/.framework/src/components/ui/label.tsx +22 -0
- package/.framework/src/components/ui/menubar.tsx +277 -0
- package/.framework/src/components/ui/native-select.tsx +61 -0
- package/.framework/src/components/ui/navigation-menu.tsx +164 -0
- package/.framework/src/components/ui/pagination.tsx +129 -0
- package/.framework/src/components/ui/popover.tsx +87 -0
- package/.framework/src/components/ui/progress.tsx +31 -0
- package/.framework/src/components/ui/radio-group.tsx +42 -0
- package/.framework/src/components/ui/resizable.tsx +50 -0
- package/.framework/src/components/ui/scroll-area.tsx +53 -0
- package/.framework/src/components/ui/select.tsx +195 -0
- package/.framework/src/components/ui/separator.tsx +26 -0
- package/.framework/src/components/ui/sheet.tsx +145 -0
- package/.framework/src/components/ui/sidebar.tsx +706 -0
- package/.framework/src/components/ui/skeleton.tsx +13 -0
- package/.framework/src/components/ui/slider.tsx +59 -0
- package/.framework/src/components/ui/sonner.tsx +47 -0
- package/.framework/src/components/ui/spinner.tsx +10 -0
- package/.framework/src/components/ui/switch.tsx +33 -0
- package/.framework/src/components/ui/table-primitives.tsx +141 -0
- package/.framework/src/components/ui/table.tsx +114 -0
- package/.framework/src/components/ui/tabs.tsx +90 -0
- package/.framework/src/components/ui/textarea.tsx +18 -0
- package/.framework/src/components/ui/toggle-group.tsx +89 -0
- package/.framework/src/components/ui/toggle.tsx +45 -0
- package/.framework/src/components/ui/tooltip.tsx +57 -0
- package/.framework/src/contexts/AppContext.tsx +133 -0
- package/.framework/src/contexts/AuthContext.tsx +371 -0
- package/.framework/src/hooks/use-mobile.ts +19 -0
- package/.framework/src/hooks/useApi.ts +526 -0
- package/.framework/src/hooks/useApps.ts +114 -0
- package/.framework/src/hooks/useEntityList.ts +190 -0
- package/.framework/src/hooks/useEntityRecord.ts +308 -0
- package/.framework/src/hooks/useForm.ts +307 -0
- package/.framework/src/hooks/useListSchema.ts +264 -0
- package/.framework/src/hooks/useSchemaRecord.ts +223 -0
- package/.framework/src/index.css +128 -0
- package/.framework/src/lib/api.ts +156 -0
- package/.framework/src/lib/supabase.ts +94 -0
- package/.framework/src/lib/utils.ts +317 -0
- package/.framework/src/main.tsx +27 -0
- package/.framework/src/pages/DashboardPage.tsx +181 -0
- package/.framework/src/pages/NotFoundPage.tsx +39 -0
- package/.framework/src/pages/admin/AIAgentDetailPage.tsx +161 -0
- package/.framework/src/pages/admin/AIAgentsPage.tsx +318 -0
- package/.framework/src/pages/admin/APIKeyDetailPage.tsx +199 -0
- package/.framework/src/pages/admin/APIKeysPage.tsx +303 -0
- package/.framework/src/pages/admin/AlertsConfigPage.tsx +523 -0
- package/.framework/src/pages/admin/AppDetailPage.tsx +493 -0
- package/.framework/src/pages/admin/AppsPage.tsx +355 -0
- package/.framework/src/pages/admin/DesignedPage.tsx +491 -0
- package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +534 -0
- package/.framework/src/pages/admin/EmbeddingsPage.tsx +424 -0
- package/.framework/src/pages/admin/ExtendedShadcnTestPage.tsx +176 -0
- package/.framework/src/pages/admin/IncrementalShadcnTestPage.tsx +109 -0
- package/.framework/src/pages/admin/IntegratedDashboard.tsx +402 -0
- package/.framework/src/pages/admin/IntegrationDetailPage.tsx +187 -0
- package/.framework/src/pages/admin/IntegrationsPage.tsx +301 -0
- package/.framework/src/pages/admin/LogsPage.tsx +283 -0
- package/.framework/src/pages/admin/MinimalShadcnTestPage.tsx +85 -0
- package/.framework/src/pages/admin/ObservabilityDashboard.tsx +470 -0
- package/.framework/src/pages/admin/PipelineDetailPage.tsx +183 -0
- package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +279 -0
- package/.framework/src/pages/admin/PipelinesPage.tsx +390 -0
- package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +299 -0
- package/.framework/src/pages/admin/PromptConfigsPage.tsx +292 -0
- package/.framework/src/pages/admin/ProperlyDesignedPage.tsx +434 -0
- package/.framework/src/pages/admin/RoleDetailPage.tsx +273 -0
- package/.framework/src/pages/admin/RolesPage.tsx +292 -0
- package/.framework/src/pages/admin/SelectTestPage.tsx +61 -0
- package/.framework/src/pages/admin/ShadcnTestPage.tsx +588 -0
- package/.framework/src/pages/admin/SimpleDashboard.tsx +387 -0
- package/.framework/src/pages/admin/TestRunDetailPage.tsx +172 -0
- package/.framework/src/pages/admin/TestingDashboard.tsx +257 -0
- package/.framework/src/pages/admin/TimerDetailPage.tsx +151 -0
- package/.framework/src/pages/admin/TimersPage.tsx +376 -0
- package/.framework/src/pages/admin/TriggerDetailPage.tsx +149 -0
- package/.framework/src/pages/admin/TriggersPage.tsx +381 -0
- package/.framework/src/pages/admin/TypeDetailPage.tsx +694 -0
- package/.framework/src/pages/admin/TypesPage.tsx +295 -0
- package/.framework/src/pages/auth/LoginPage.tsx +188 -0
- package/.framework/src/pages/auth/RegisterPage.tsx +163 -0
- package/.framework/src/pages/spine-framework/APIPage.tsx +17 -0
- package/.framework/src/pages/spine-framework/CLIPage.tsx +25 -0
- package/.framework/src/types/auth.ts +125 -0
- package/.framework/src/types/types.ts +407 -0
- package/STRUCTURE.md +150 -0
- package/config/components.json +25 -0
- package/config/deno.lock +108 -0
- package/config/package-lock.json +17183 -0
- package/config/postcss.config.cjs +10 -0
- package/config/tailwind.config.cjs +78 -0
- package/config/tsconfig.build.json +32 -0
- package/config/tsconfig.cli.json +18 -0
- package/config/tsconfig.json +41 -0
- package/config/tsconfig.node.json +17 -0
- package/config/tsconfig.node.tsbuildinfo +1 -0
- package/config/tsconfig.tsbuildinfo +1 -0
- package/config/typedoc.json +16 -0
- package/config/vite.config.d.ts +2 -0
- package/config/vite.config.ts +72 -0
- package/dist/cli/commands/agents.d.ts +39 -0
- package/dist/cli/commands/agents.d.ts.map +1 -0
- package/dist/cli/commands/auth.d.ts +36 -0
- package/dist/cli/commands/auth.d.ts.map +1 -0
- package/dist/cli/commands/create-app.d.ts +23 -0
- package/dist/cli/commands/create-app.d.ts.map +1 -0
- package/dist/cli/commands/dev.d.ts +39 -0
- package/dist/cli/commands/dev.d.ts.map +1 -0
- package/dist/cli/commands/doctor.d.ts +42 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/generate.d.ts +36 -0
- package/dist/cli/commands/generate.d.ts.map +1 -0
- package/dist/cli/commands/init.d.ts +30 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/install-app.d.ts +30 -0
- package/dist/cli/commands/install-app.d.ts.map +1 -0
- package/dist/cli/commands/items.d.ts +45 -0
- package/dist/cli/commands/items.d.ts.map +1 -0
- package/dist/cli/commands/migrations.d.ts +41 -0
- package/dist/cli/commands/migrations.d.ts.map +1 -0
- package/dist/cli/commands/pipelines.d.ts +40 -0
- package/dist/cli/commands/pipelines.d.ts.map +1 -0
- package/dist/cli/commands/status.d.ts +23 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/system.d.ts +29 -0
- package/dist/cli/commands/system.d.ts.map +1 -0
- package/dist/cli/commands/test.d.ts +46 -0
- package/dist/cli/commands/test.d.ts.map +1 -0
- package/dist/cli/commands/uninstall-app.d.ts +23 -0
- package/dist/cli/commands/uninstall-app.d.ts.map +1 -0
- package/dist/cli/context.d.ts +88 -0
- package/dist/cli/context.d.ts.map +1 -0
- package/dist/cli/env-loader.d.ts +14 -0
- package/dist/cli/env-loader.d.ts.map +1 -0
- package/dist/cli/index.d.ts +41 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/functions/_shared/agent-runner.d.ts +156 -0
- package/dist/functions/_shared/agent-runner.d.ts.map +1 -0
- package/dist/functions/_shared/app-manifest.d.ts +68 -0
- package/dist/functions/_shared/app-manifest.d.ts.map +1 -0
- package/dist/functions/_shared/audit.d.ts +91 -0
- package/dist/functions/_shared/audit.d.ts.map +1 -0
- package/dist/functions/_shared/db.d.ts +125 -0
- package/dist/functions/_shared/db.d.ts.map +1 -0
- package/dist/functions/_shared/index.d.ts +298 -0
- package/dist/functions/_shared/index.d.ts.map +1 -0
- package/dist/functions/_shared/middleware.d.ts +315 -0
- package/dist/functions/_shared/middleware.d.ts.map +1 -0
- package/dist/functions/_shared/permissions.d.ts +626 -0
- package/dist/functions/_shared/permissions.d.ts.map +1 -0
- package/dist/functions/_shared/pipeline-runner.d.ts +124 -0
- package/dist/functions/_shared/pipeline-runner.d.ts.map +1 -0
- package/dist/functions/_shared/principal.d.ts +284 -0
- package/dist/functions/_shared/principal.d.ts.map +1 -0
- package/dist/functions/_shared/schema-utils.d.ts +181 -0
- package/dist/functions/_shared/schema-utils.d.ts.map +1 -0
- package/dist/functions/_shared/testing.d.ts +172 -0
- package/dist/functions/_shared/testing.d.ts.map +1 -0
- package/dist/functions/_shared/trigger-engine.d.ts +140 -0
- package/dist/functions/_shared/trigger-engine.d.ts.map +1 -0
- package/dist/functions/_shared/webhook-registration.d.ts +81 -0
- package/dist/functions/_shared/webhook-registration.d.ts.map +1 -0
- package/dist/functions/_shared/webhook-registry.d.ts +57 -0
- package/dist/functions/_shared/webhook-registry.d.ts.map +1 -0
- package/dist/functions/account-nodes.d.ts +48 -0
- package/dist/functions/account-nodes.d.ts.map +1 -0
- package/dist/functions/admin-data.d.ts +178 -0
- package/dist/functions/admin-data.d.ts.map +1 -0
- package/dist/functions/ai-agents.d.ts +125 -0
- package/dist/functions/ai-agents.d.ts.map +1 -0
- package/dist/functions/api-keys.d.ts +140 -0
- package/dist/functions/api-keys.d.ts.map +1 -0
- package/dist/functions/apps.d.ts +163 -0
- package/dist/functions/apps.d.ts.map +1 -0
- package/dist/functions/auth.d.ts +74 -0
- package/dist/functions/auth.d.ts.map +1 -0
- package/dist/functions/debug-auth.d.ts +33 -0
- package/dist/functions/debug-auth.d.ts.map +1 -0
- package/dist/functions/embeddings.d.ts +205 -0
- package/dist/functions/embeddings.d.ts.map +1 -0
- package/dist/functions/integration-routes.d.ts +45 -0
- package/dist/functions/integration-routes.d.ts.map +1 -0
- package/dist/functions/integrations.d.ts +124 -0
- package/dist/functions/integrations.d.ts.map +1 -0
- package/dist/functions/item-progress.d.ts +41 -0
- package/dist/functions/item-progress.d.ts.map +1 -0
- package/dist/functions/logs.d.ts +162 -0
- package/dist/functions/logs.d.ts.map +1 -0
- package/dist/functions/observability.d.ts +123 -0
- package/dist/functions/observability.d.ts.map +1 -0
- package/dist/functions/pipeline-executions.d.ts +190 -0
- package/dist/functions/pipeline-executions.d.ts.map +1 -0
- package/dist/functions/pipelines.d.ts +171 -0
- package/dist/functions/pipelines.d.ts.map +1 -0
- package/dist/functions/prompt-configs.d.ts +125 -0
- package/dist/functions/prompt-configs.d.ts.map +1 -0
- package/dist/functions/roles.d.ts +118 -0
- package/dist/functions/roles.d.ts.map +1 -0
- package/dist/functions/system-cron.d.ts +65 -0
- package/dist/functions/system-cron.d.ts.map +1 -0
- package/dist/functions/system.d.ts +29 -0
- package/dist/functions/system.d.ts.map +1 -0
- package/dist/functions/tests.d.ts +28 -0
- package/dist/functions/tests.d.ts.map +1 -0
- package/dist/functions/timers.d.ts +139 -0
- package/dist/functions/timers.d.ts.map +1 -0
- package/dist/functions/triggers.d.ts +203 -0
- package/dist/functions/triggers.d.ts.map +1 -0
- package/dist/functions/types.d.ts +151 -0
- package/dist/functions/types.d.ts.map +1 -0
- package/dist/src/types/types.d.ts +364 -0
- package/dist/src/types/types.d.ts.map +1 -0
- package/package.json +192 -0
- package/scripts/app-install-cli.ts +286 -0
- package/scripts/assemble-frontend.sh +79 -0
- package/scripts/assemble-functions.sh +62 -0
- package/scripts/assemble.sh +35 -0
- package/scripts/boundary-check.sh +106 -0
- package/scripts/build-manifest.sh +80 -0
- package/scripts/check-core-integrity.sh +82 -0
- package/scripts/ingest-chunks.cjs +202 -0
- package/scripts/kb-chunk-parser.cjs +312 -0
- package/scripts/kb-chunk-parser.ts +330 -0
- package/scripts/load-test-app-install.ts +484 -0
- package/scripts/netlify-dev-wrapper.sh +22 -0
- package/scripts/verify-integrity.sh +69 -0
|
@@ -0,0 +1,694 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module src/pages/admin/TypeDetailPage
|
|
3
|
+
* @audience installer
|
|
4
|
+
* @layer frontend-page
|
|
5
|
+
* @stability stable
|
|
6
|
+
*
|
|
7
|
+
* Create / view / edit / delete page for a single type record.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import React, { useState, useEffect } from 'react'
|
|
11
|
+
import { useParams, useNavigate } from 'react-router-dom'
|
|
12
|
+
import { useApi } from '../../hooks/useApi'
|
|
13
|
+
import { useMutation } from '../../hooks/useApi'
|
|
14
|
+
import { apiFetch } from '../../lib/api'
|
|
15
|
+
import { Button } from '../../components/ui/button'
|
|
16
|
+
import { Modal } from '../../components/ui/Modal'
|
|
17
|
+
import { Badge } from '../../components/ui/badge'
|
|
18
|
+
import { Input } from '../../components/ui/input'
|
|
19
|
+
import { Textarea } from '../../components/ui/textarea'
|
|
20
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../../components/ui/select'
|
|
21
|
+
import { Checkbox } from '../../components/ui/checkbox'
|
|
22
|
+
import { Label } from '../../components/ui/label'
|
|
23
|
+
import { Card, CardContent, CardHeader, CardTitle } from '../../components/ui/card'
|
|
24
|
+
import { Skeleton } from '../../components/ui/skeleton'
|
|
25
|
+
import { Alert, AlertDescription, AlertTitle } from '../../components/ui/alert'
|
|
26
|
+
import { DataTable } from '../../components/ui/DataTable'
|
|
27
|
+
import { ArrowLeft, Pencil, Trash2, AlertCircle } from 'lucide-react'
|
|
28
|
+
import { formatDateTime } from '../../lib/utils'
|
|
29
|
+
|
|
30
|
+
interface Type {
|
|
31
|
+
id: string
|
|
32
|
+
name: string
|
|
33
|
+
slug: string
|
|
34
|
+
kind: string
|
|
35
|
+
description?: string
|
|
36
|
+
icon?: string
|
|
37
|
+
color?: string
|
|
38
|
+
design_schema?: {
|
|
39
|
+
fields: Record<string, any>
|
|
40
|
+
record_permissions?: Record<string, string[]>
|
|
41
|
+
}
|
|
42
|
+
ownership: string
|
|
43
|
+
is_active: boolean
|
|
44
|
+
app_id?: string
|
|
45
|
+
app?: any
|
|
46
|
+
created_at: string
|
|
47
|
+
updated_at: string
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface TypeItem {
|
|
51
|
+
id: string
|
|
52
|
+
item_type_id: string
|
|
53
|
+
data: Record<string, any>
|
|
54
|
+
metadata: Record<string, any>
|
|
55
|
+
created_at: string
|
|
56
|
+
updated_at: string
|
|
57
|
+
created_by: string
|
|
58
|
+
account_id: string
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function TypeDetailPage() {
|
|
62
|
+
const { id, kind } = useParams<{ id: string, kind: string }>()
|
|
63
|
+
const navigate = useNavigate()
|
|
64
|
+
const isCreateMode = !id || id === 'new'
|
|
65
|
+
const typeKind = kind || 'item'
|
|
66
|
+
|
|
67
|
+
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false)
|
|
68
|
+
const [isEditing, setIsEditing] = useState(isCreateMode)
|
|
69
|
+
const [editData, setEditData] = useState<Record<string, any>>({
|
|
70
|
+
name: '',
|
|
71
|
+
slug: '',
|
|
72
|
+
description: '',
|
|
73
|
+
icon: '',
|
|
74
|
+
color: '',
|
|
75
|
+
kind: typeKind,
|
|
76
|
+
schema: { fields: {} },
|
|
77
|
+
ownership: 'app',
|
|
78
|
+
app_id: '',
|
|
79
|
+
is_active: true
|
|
80
|
+
})
|
|
81
|
+
const [schemaText, setSchemaText] = useState(JSON.stringify({ fields: {} }, null, 2))
|
|
82
|
+
const [availableApps, setAvailableApps] = useState<Array<{id: string, name: string}>>([])
|
|
83
|
+
|
|
84
|
+
React.useEffect(() => {
|
|
85
|
+
const fetchApps = async () => {
|
|
86
|
+
try {
|
|
87
|
+
const response = await apiFetch('/api/apps?action=list')
|
|
88
|
+
if (response.ok) {
|
|
89
|
+
const data = await response.json()
|
|
90
|
+
setAvailableApps(data.data || [])
|
|
91
|
+
}
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error('Error fetching apps:', error)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
fetchApps()
|
|
97
|
+
}, [])
|
|
98
|
+
|
|
99
|
+
const getKindDefaults = () => {
|
|
100
|
+
switch (typeKind) {
|
|
101
|
+
case 'account':
|
|
102
|
+
return { kind: 'account', ownership: 'app', icon: 'building-office', color: 'green' }
|
|
103
|
+
case 'person':
|
|
104
|
+
return { kind: 'person', ownership: 'app', icon: 'user', color: 'purple' }
|
|
105
|
+
default:
|
|
106
|
+
return { kind: 'item', ownership: 'app', icon: 'cube', color: 'blue' }
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const { data: type, loading, error, refetch } = useApi<Type>(
|
|
111
|
+
async () => {
|
|
112
|
+
if (isCreateMode) {
|
|
113
|
+
const defaults = getKindDefaults()
|
|
114
|
+
return {
|
|
115
|
+
id: '',
|
|
116
|
+
name: '',
|
|
117
|
+
slug: '',
|
|
118
|
+
description: '',
|
|
119
|
+
icon: defaults.icon,
|
|
120
|
+
color: defaults.color,
|
|
121
|
+
kind: defaults.kind,
|
|
122
|
+
design_schema: { fields: {} },
|
|
123
|
+
ownership: defaults.ownership,
|
|
124
|
+
app_id: '',
|
|
125
|
+
is_active: true,
|
|
126
|
+
created_at: '',
|
|
127
|
+
updated_at: ''
|
|
128
|
+
}
|
|
129
|
+
} else {
|
|
130
|
+
if (!id) throw new Error('Type ID or slug is required')
|
|
131
|
+
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
|
|
132
|
+
const url = uuidRegex.test(id)
|
|
133
|
+
? `/api/types?action=get&id=${id}`
|
|
134
|
+
: `/api/types?action=get&slug=${id}`
|
|
135
|
+
const response = await apiFetch(url)
|
|
136
|
+
if (!response.ok) {
|
|
137
|
+
throw new Error(response.status === 500 ? 'Type not found' : 'Failed to fetch type')
|
|
138
|
+
}
|
|
139
|
+
const json = await response.json()
|
|
140
|
+
return json.data as Type
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
{ immediate: true }
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
React.useEffect(() => {
|
|
147
|
+
if (type) {
|
|
148
|
+
setEditData({
|
|
149
|
+
name: type.name,
|
|
150
|
+
slug: type.slug,
|
|
151
|
+
description: type.description || '',
|
|
152
|
+
icon: type.icon || '',
|
|
153
|
+
color: type.color || '',
|
|
154
|
+
kind: type.kind || 'item',
|
|
155
|
+
design_schema: type.design_schema || { fields: {} },
|
|
156
|
+
ownership: type.ownership || 'system',
|
|
157
|
+
app_id: type.app_id || '',
|
|
158
|
+
is_active: type.is_active ?? true
|
|
159
|
+
})
|
|
160
|
+
setSchemaText(JSON.stringify(type.design_schema || { fields: {} }, null, 2))
|
|
161
|
+
}
|
|
162
|
+
}, [type])
|
|
163
|
+
|
|
164
|
+
const handleSave = async () => {
|
|
165
|
+
try {
|
|
166
|
+
const { app_id } = editData
|
|
167
|
+
const ownership = (editData.ownership || '').toLowerCase()
|
|
168
|
+
if (ownership !== 'system' && !app_id) {
|
|
169
|
+
throw new Error('App selection is required when ownership is not "System"')
|
|
170
|
+
}
|
|
171
|
+
let parsedSchema = { fields: {} }
|
|
172
|
+
try {
|
|
173
|
+
parsedSchema = JSON.parse(schemaText)
|
|
174
|
+
} catch (error) {
|
|
175
|
+
console.warn('Invalid JSON in schema field, using default:', error)
|
|
176
|
+
}
|
|
177
|
+
const saveData = {
|
|
178
|
+
...editData,
|
|
179
|
+
design_schema: parsedSchema,
|
|
180
|
+
app_id: editData.app_id || null
|
|
181
|
+
}
|
|
182
|
+
const url = isCreateMode
|
|
183
|
+
? '/api/types?action=create'
|
|
184
|
+
: `/api/types?action=update&id=${id}`
|
|
185
|
+
const method = isCreateMode ? 'POST' : 'PATCH'
|
|
186
|
+
const response = await apiFetch(url, {
|
|
187
|
+
method,
|
|
188
|
+
headers: { 'Content-Type': 'application/json' },
|
|
189
|
+
body: JSON.stringify(saveData)
|
|
190
|
+
})
|
|
191
|
+
if (!response.ok) throw new Error(`Failed to ${isCreateMode ? 'create' : 'update'} type`)
|
|
192
|
+
if (isCreateMode) {
|
|
193
|
+
const result = await response.json()
|
|
194
|
+
const newId = result.data?.id || result.id
|
|
195
|
+
navigate(`/spine-framework/admin/configs/${kind || 'types'}/${newId}`)
|
|
196
|
+
} else {
|
|
197
|
+
await refetch()
|
|
198
|
+
setIsEditing(false)
|
|
199
|
+
}
|
|
200
|
+
} catch (error) {
|
|
201
|
+
console.error(`Error ${isCreateMode ? 'creating' : 'updating'} type:`, error)
|
|
202
|
+
alert(error instanceof Error ? error.message : 'Unknown error occurred')
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const handleCancel = () => {
|
|
207
|
+
if (isCreateMode) {
|
|
208
|
+
navigate('/spine-framework/admin/configs/types')
|
|
209
|
+
return
|
|
210
|
+
}
|
|
211
|
+
if (type) {
|
|
212
|
+
setEditData({
|
|
213
|
+
name: type.name,
|
|
214
|
+
slug: type.slug,
|
|
215
|
+
description: type.description || '',
|
|
216
|
+
icon: type.icon || '',
|
|
217
|
+
color: type.color || '',
|
|
218
|
+
design_schema: type.design_schema || { fields: {} },
|
|
219
|
+
ownership: type.ownership || 'system',
|
|
220
|
+
app_id: type.app_id || '',
|
|
221
|
+
is_active: type.is_active ?? true
|
|
222
|
+
})
|
|
223
|
+
setSchemaText(JSON.stringify(type.design_schema || { fields: {} }, null, 2))
|
|
224
|
+
}
|
|
225
|
+
setIsEditing(false)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const handleEdit = () => {
|
|
229
|
+
setIsEditing(true)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const { data: items, loading: itemsLoading, execute: fetchItems } = useApi<TypeItem[]>(
|
|
233
|
+
async () => {
|
|
234
|
+
if (isCreateMode || !type?.id) {
|
|
235
|
+
return []
|
|
236
|
+
}
|
|
237
|
+
const response = await apiFetch(`/api/admin-data?entity=items&type_id=${type.id}`)
|
|
238
|
+
if (!response.ok) throw new Error('Failed to fetch items')
|
|
239
|
+
const result = await response.json()
|
|
240
|
+
return result.data || []
|
|
241
|
+
},
|
|
242
|
+
{ immediate: !isCreateMode }
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
useEffect(() => {
|
|
246
|
+
if (!isCreateMode && type?.id) fetchItems()
|
|
247
|
+
}, [type?.id])
|
|
248
|
+
|
|
249
|
+
const deleteMutation = useMutation(
|
|
250
|
+
async () => {
|
|
251
|
+
const response = await apiFetch(`/api/types?action=delete&id=${id}`, {
|
|
252
|
+
method: 'DELETE'
|
|
253
|
+
})
|
|
254
|
+
if (!response.ok) throw new Error('Failed to delete type')
|
|
255
|
+
return response.json()
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
onSuccess: () => {
|
|
259
|
+
navigate(`/spine-framework/admin/configs/${kind || 'types'}`)
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
if (loading) {
|
|
265
|
+
return (
|
|
266
|
+
<div className="space-y-6">
|
|
267
|
+
<Skeleton className="h-8 w-48" />
|
|
268
|
+
<Card>
|
|
269
|
+
<CardHeader><Skeleton className="h-6 w-32" /></CardHeader>
|
|
270
|
+
<CardContent className="space-y-4">
|
|
271
|
+
<Skeleton className="h-4 w-full" />
|
|
272
|
+
<Skeleton className="h-4 w-full" />
|
|
273
|
+
<Skeleton className="h-4 w-2/3" />
|
|
274
|
+
</CardContent>
|
|
275
|
+
</Card>
|
|
276
|
+
</div>
|
|
277
|
+
)
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (error || (!type && !isCreateMode)) {
|
|
281
|
+
return (
|
|
282
|
+
<Alert variant="destructive">
|
|
283
|
+
<AlertCircle className="h-4 w-4" />
|
|
284
|
+
<AlertTitle>Failed to load type details</AlertTitle>
|
|
285
|
+
<AlertDescription>{error || 'Type not found'}</AlertDescription>
|
|
286
|
+
<Button onClick={() => refetch()} variant="outline" className="mt-4">Retry</Button>
|
|
287
|
+
</Alert>
|
|
288
|
+
)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const itemColumns = [
|
|
292
|
+
{
|
|
293
|
+
key: 'id' as keyof TypeItem,
|
|
294
|
+
title: 'ID',
|
|
295
|
+
render: (row: any) => (
|
|
296
|
+
<span className="font-mono text-xs">{row.id?.slice(0, 8)}...</span>
|
|
297
|
+
)
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
key: 'data' as keyof TypeItem,
|
|
301
|
+
title: 'Data',
|
|
302
|
+
render: (row: any) => {
|
|
303
|
+
const firstFieldKey = Object.keys(type?.design_schema?.fields || {})[0]
|
|
304
|
+
const displayValue = firstFieldKey ? row.data?.[firstFieldKey] : 'No data'
|
|
305
|
+
return (
|
|
306
|
+
<div>
|
|
307
|
+
<div className="font-medium">{displayValue}</div>
|
|
308
|
+
<div className="text-xs text-muted-foreground">
|
|
309
|
+
{Object.keys(row.data || {}).length} fields
|
|
310
|
+
</div>
|
|
311
|
+
</div>
|
|
312
|
+
)
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
key: 'created_at' as keyof TypeItem,
|
|
317
|
+
title: 'Created',
|
|
318
|
+
render: (row: any) => formatDateTime(row.created_at)
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
key: 'updated_at' as keyof TypeItem,
|
|
322
|
+
title: 'Updated',
|
|
323
|
+
render: (row: any) => formatDateTime(row.updated_at)
|
|
324
|
+
}
|
|
325
|
+
]
|
|
326
|
+
|
|
327
|
+
return (
|
|
328
|
+
<div className="space-y-6">
|
|
329
|
+
<div className="flex items-center justify-between">
|
|
330
|
+
<div className="flex items-center gap-4">
|
|
331
|
+
<Button
|
|
332
|
+
variant="ghost"
|
|
333
|
+
onClick={() => isCreateMode ? navigate('/spine-framework/admin/configs/types') : navigate(-1)}
|
|
334
|
+
>
|
|
335
|
+
<ArrowLeft className="h-5 w-5" />
|
|
336
|
+
</Button>
|
|
337
|
+
<div>
|
|
338
|
+
<h1 className="text-2xl font-bold">
|
|
339
|
+
{isCreateMode ? 'Create Type' : type?.name}
|
|
340
|
+
</h1>
|
|
341
|
+
<p className="text-sm text-muted-foreground">
|
|
342
|
+
{isCreateMode ? 'Type Configuration' : 'Type Details'}
|
|
343
|
+
</p>
|
|
344
|
+
</div>
|
|
345
|
+
</div>
|
|
346
|
+
|
|
347
|
+
<div className="flex space-x-2">
|
|
348
|
+
{isEditing ? (
|
|
349
|
+
<>
|
|
350
|
+
<Button variant="outline" onClick={handleCancel}>Cancel</Button>
|
|
351
|
+
<Button onClick={handleSave}>
|
|
352
|
+
{isCreateMode ? 'Create' : 'Save Changes'}
|
|
353
|
+
</Button>
|
|
354
|
+
</>
|
|
355
|
+
) : (
|
|
356
|
+
!isCreateMode && (
|
|
357
|
+
<>
|
|
358
|
+
<Button variant="outline" onClick={handleEdit}>
|
|
359
|
+
<Pencil className="h-4 w-4 mr-2" />
|
|
360
|
+
Edit
|
|
361
|
+
</Button>
|
|
362
|
+
<Button
|
|
363
|
+
variant="outline"
|
|
364
|
+
onClick={() => setIsDeleteModalOpen(true)}
|
|
365
|
+
disabled={type?.ownership === 'system'}
|
|
366
|
+
>
|
|
367
|
+
<Trash2 className="h-4 w-4 mr-2" />
|
|
368
|
+
Delete
|
|
369
|
+
</Button>
|
|
370
|
+
</>
|
|
371
|
+
)
|
|
372
|
+
)}
|
|
373
|
+
</div>
|
|
374
|
+
</div>
|
|
375
|
+
|
|
376
|
+
{isEditing ? (
|
|
377
|
+
<>
|
|
378
|
+
<Card>
|
|
379
|
+
<CardHeader>
|
|
380
|
+
<CardTitle>
|
|
381
|
+
{isCreateMode ? `Create New ${typeKind.charAt(0).toUpperCase() + typeKind.slice(1)}` : `Edit ${type?.name || 'Type'}`}
|
|
382
|
+
</CardTitle>
|
|
383
|
+
</CardHeader>
|
|
384
|
+
<CardContent>
|
|
385
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
|
386
|
+
<div className="space-y-4">
|
|
387
|
+
<h3 className="text-sm font-medium text-muted-foreground">Type Details</h3>
|
|
388
|
+
<div className="space-y-4">
|
|
389
|
+
<div className="space-y-2">
|
|
390
|
+
<Label>Name</Label>
|
|
391
|
+
<Input
|
|
392
|
+
value={editData.name}
|
|
393
|
+
onChange={(e) => setEditData({...editData, name: e.target.value})}
|
|
394
|
+
/>
|
|
395
|
+
</div>
|
|
396
|
+
<div className="space-y-2">
|
|
397
|
+
<Label>Slug</Label>
|
|
398
|
+
<Input
|
|
399
|
+
value={editData.slug}
|
|
400
|
+
onChange={(e) => setEditData({...editData, slug: e.target.value})}
|
|
401
|
+
/>
|
|
402
|
+
</div>
|
|
403
|
+
<div className="space-y-2">
|
|
404
|
+
<Label>Description</Label>
|
|
405
|
+
<Input
|
|
406
|
+
value={editData.description}
|
|
407
|
+
onChange={(e) => setEditData({...editData, description: e.target.value})}
|
|
408
|
+
placeholder="Enter description"
|
|
409
|
+
/>
|
|
410
|
+
</div>
|
|
411
|
+
<div className="space-y-2">
|
|
412
|
+
<Label>Kind</Label>
|
|
413
|
+
<Select value={editData.kind} onValueChange={(v) => setEditData({...editData, kind: v})}>
|
|
414
|
+
<SelectTrigger>
|
|
415
|
+
<SelectValue />
|
|
416
|
+
</SelectTrigger>
|
|
417
|
+
<SelectContent>
|
|
418
|
+
{typeKind ? (
|
|
419
|
+
<SelectItem value={typeKind}>{typeKind.charAt(0).toUpperCase() + typeKind.slice(1)}</SelectItem>
|
|
420
|
+
) : (
|
|
421
|
+
<>
|
|
422
|
+
<SelectItem value="item">Item</SelectItem>
|
|
423
|
+
<SelectItem value="account">Account</SelectItem>
|
|
424
|
+
<SelectItem value="person">Person</SelectItem>
|
|
425
|
+
<SelectItem value="thread">Thread</SelectItem>
|
|
426
|
+
<SelectItem value="message">Message</SelectItem>
|
|
427
|
+
</>
|
|
428
|
+
)}
|
|
429
|
+
</SelectContent>
|
|
430
|
+
</Select>
|
|
431
|
+
</div>
|
|
432
|
+
<div className="space-y-2">
|
|
433
|
+
<Label>Ownership</Label>
|
|
434
|
+
<Select value={editData.ownership} onValueChange={(v) => setEditData({...editData, ownership: v})}>
|
|
435
|
+
<SelectTrigger>
|
|
436
|
+
<SelectValue />
|
|
437
|
+
</SelectTrigger>
|
|
438
|
+
<SelectContent>
|
|
439
|
+
<SelectItem value="system">System</SelectItem>
|
|
440
|
+
<SelectItem value="app">App</SelectItem>
|
|
441
|
+
<SelectItem value="tenant">Tenant</SelectItem>
|
|
442
|
+
</SelectContent>
|
|
443
|
+
</Select>
|
|
444
|
+
</div>
|
|
445
|
+
<div className="flex items-center gap-2">
|
|
446
|
+
<Checkbox
|
|
447
|
+
id="is_active"
|
|
448
|
+
checked={editData.is_active}
|
|
449
|
+
onCheckedChange={(checked) => setEditData({...editData, is_active: checked === true})}
|
|
450
|
+
/>
|
|
451
|
+
<Label htmlFor="is_active">Is Active</Label>
|
|
452
|
+
</div>
|
|
453
|
+
</div>
|
|
454
|
+
</div>
|
|
455
|
+
|
|
456
|
+
<div className="space-y-4">
|
|
457
|
+
<h3 className="text-sm font-medium text-muted-foreground">Metadata</h3>
|
|
458
|
+
<div className="space-y-4">
|
|
459
|
+
<div className="space-y-2">
|
|
460
|
+
<Label>ID</Label>
|
|
461
|
+
<div className="text-sm font-mono text-muted-foreground">{type?.id || 'Auto-generated'}</div>
|
|
462
|
+
</div>
|
|
463
|
+
<div className="space-y-2">
|
|
464
|
+
<Label>App ID</Label>
|
|
465
|
+
<Select value={editData.app_id || ''} onValueChange={(v) => setEditData({...editData, app_id: v})}>
|
|
466
|
+
<SelectTrigger>
|
|
467
|
+
<SelectValue placeholder="None (System Types)" />
|
|
468
|
+
</SelectTrigger>
|
|
469
|
+
<SelectContent>
|
|
470
|
+
{availableApps.map(app => (
|
|
471
|
+
<SelectItem key={app.id} value={app.id}>{app.name}</SelectItem>
|
|
472
|
+
))}
|
|
473
|
+
</SelectContent>
|
|
474
|
+
</Select>
|
|
475
|
+
</div>
|
|
476
|
+
<div className="space-y-2">
|
|
477
|
+
<Label>Icon</Label>
|
|
478
|
+
<Input
|
|
479
|
+
value={editData.icon || ''}
|
|
480
|
+
onChange={(e) => setEditData({...editData, icon: e.target.value})}
|
|
481
|
+
placeholder="Enter icon name"
|
|
482
|
+
/>
|
|
483
|
+
</div>
|
|
484
|
+
<div className="space-y-2">
|
|
485
|
+
<Label>Color</Label>
|
|
486
|
+
<Select value={editData.color || ''} onValueChange={(v) => setEditData({...editData, color: v})}>
|
|
487
|
+
<SelectTrigger>
|
|
488
|
+
<SelectValue placeholder="Select color" />
|
|
489
|
+
</SelectTrigger>
|
|
490
|
+
<SelectContent>
|
|
491
|
+
<SelectItem value="blue">Blue</SelectItem>
|
|
492
|
+
<SelectItem value="green">Green</SelectItem>
|
|
493
|
+
<SelectItem value="red">Red</SelectItem>
|
|
494
|
+
<SelectItem value="yellow">Yellow</SelectItem>
|
|
495
|
+
<SelectItem value="purple">Purple</SelectItem>
|
|
496
|
+
<SelectItem value="pink">Pink</SelectItem>
|
|
497
|
+
<SelectItem value="indigo">Indigo</SelectItem>
|
|
498
|
+
<SelectItem value="gray">Gray</SelectItem>
|
|
499
|
+
<SelectItem value="slate">Slate</SelectItem>
|
|
500
|
+
</SelectContent>
|
|
501
|
+
</Select>
|
|
502
|
+
</div>
|
|
503
|
+
<div className="space-y-2">
|
|
504
|
+
<Label>Created</Label>
|
|
505
|
+
<div className="text-sm text-muted-foreground">{formatDateTime(type?.created_at) || 'Not yet created'}</div>
|
|
506
|
+
</div>
|
|
507
|
+
<div className="space-y-2">
|
|
508
|
+
<Label>Updated</Label>
|
|
509
|
+
<div className="text-sm text-muted-foreground">{formatDateTime(type?.updated_at) || 'Not yet updated'}</div>
|
|
510
|
+
</div>
|
|
511
|
+
</div>
|
|
512
|
+
</div>
|
|
513
|
+
</div>
|
|
514
|
+
|
|
515
|
+
<div className="mt-8 space-y-2">
|
|
516
|
+
<h3 className="text-sm font-medium text-muted-foreground">Schema Definition</h3>
|
|
517
|
+
<Label>Schema</Label>
|
|
518
|
+
<Textarea
|
|
519
|
+
value={schemaText}
|
|
520
|
+
onChange={(e) => setSchemaText(e.target.value)}
|
|
521
|
+
className="font-mono text-sm"
|
|
522
|
+
rows={8}
|
|
523
|
+
placeholder='Enter JSON schema with fields object'
|
|
524
|
+
/>
|
|
525
|
+
<p className="text-xs text-muted-foreground">JSON schema defining the fields for this type.</p>
|
|
526
|
+
</div>
|
|
527
|
+
</CardContent>
|
|
528
|
+
</Card>
|
|
529
|
+
</>
|
|
530
|
+
) : (
|
|
531
|
+
<>
|
|
532
|
+
<Card>
|
|
533
|
+
<CardHeader>
|
|
534
|
+
<CardTitle>Type Information</CardTitle>
|
|
535
|
+
</CardHeader>
|
|
536
|
+
<CardContent>
|
|
537
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
|
538
|
+
<div className="space-y-4">
|
|
539
|
+
<h3 className="text-sm font-medium text-muted-foreground">Type Details</h3>
|
|
540
|
+
<div className="space-y-4">
|
|
541
|
+
<div className="flex justify-between items-center">
|
|
542
|
+
<span className="text-sm text-muted-foreground">Name:</span>
|
|
543
|
+
<span className="text-sm">{type?.name}</span>
|
|
544
|
+
</div>
|
|
545
|
+
<div className="flex justify-between items-center">
|
|
546
|
+
<span className="text-sm text-muted-foreground">Slug:</span>
|
|
547
|
+
<span className="text-sm font-mono">{type?.slug}</span>
|
|
548
|
+
</div>
|
|
549
|
+
<div className="flex justify-between items-center">
|
|
550
|
+
<span className="text-sm text-muted-foreground">Description:</span>
|
|
551
|
+
<span className="text-sm">{type?.description || 'No description'}</span>
|
|
552
|
+
</div>
|
|
553
|
+
<div className="flex justify-between items-center">
|
|
554
|
+
<span className="text-sm text-muted-foreground">Kind:</span>
|
|
555
|
+
<Badge>{type?.kind}</Badge>
|
|
556
|
+
</div>
|
|
557
|
+
<div className="flex justify-between items-center">
|
|
558
|
+
<span className="text-sm text-muted-foreground">Ownership:</span>
|
|
559
|
+
<Badge variant="outline">{type?.ownership}</Badge>
|
|
560
|
+
</div>
|
|
561
|
+
<div className="flex justify-between items-center">
|
|
562
|
+
<span className="text-sm text-muted-foreground">Status:</span>
|
|
563
|
+
<Badge variant={type?.is_active ? 'default' : 'secondary'}>
|
|
564
|
+
{type?.is_active ? 'Active' : 'Inactive'}
|
|
565
|
+
</Badge>
|
|
566
|
+
</div>
|
|
567
|
+
</div>
|
|
568
|
+
</div>
|
|
569
|
+
|
|
570
|
+
<div className="space-y-4">
|
|
571
|
+
<h3 className="text-sm font-medium text-muted-foreground">Metadata</h3>
|
|
572
|
+
<div className="space-y-4">
|
|
573
|
+
<div className="flex justify-between items-center">
|
|
574
|
+
<span className="text-sm text-muted-foreground">ID:</span>
|
|
575
|
+
<span className="text-sm font-mono">{type?.id}</span>
|
|
576
|
+
</div>
|
|
577
|
+
<div className="flex justify-between items-center">
|
|
578
|
+
<span className="text-sm text-muted-foreground">App ID:</span>
|
|
579
|
+
<span className="text-sm font-mono">{type?.app_id || 'None'}</span>
|
|
580
|
+
</div>
|
|
581
|
+
<div className="flex justify-between items-center">
|
|
582
|
+
<span className="text-sm text-muted-foreground">Icon:</span>
|
|
583
|
+
<span className="text-sm">{type?.icon || 'None'}</span>
|
|
584
|
+
</div>
|
|
585
|
+
<div className="flex justify-between items-center">
|
|
586
|
+
<span className="text-sm text-muted-foreground">Color:</span>
|
|
587
|
+
<div className="flex items-center gap-2">
|
|
588
|
+
{type?.color && (
|
|
589
|
+
<span className="inline-block w-3 h-3 rounded-full" style={{backgroundColor: type?.color}}></span>
|
|
590
|
+
)}
|
|
591
|
+
<span className="text-sm">{type?.color || 'None'}</span>
|
|
592
|
+
</div>
|
|
593
|
+
</div>
|
|
594
|
+
<div className="flex justify-between items-center">
|
|
595
|
+
<span className="text-sm text-muted-foreground">Created:</span>
|
|
596
|
+
<span className="text-sm">{formatDateTime(type?.created_at)}</span>
|
|
597
|
+
</div>
|
|
598
|
+
<div className="flex justify-between items-center">
|
|
599
|
+
<span className="text-sm text-muted-foreground">Updated:</span>
|
|
600
|
+
<span className="text-sm">{formatDateTime(type?.updated_at)}</span>
|
|
601
|
+
</div>
|
|
602
|
+
</div>
|
|
603
|
+
</div>
|
|
604
|
+
</div>
|
|
605
|
+
|
|
606
|
+
<div className="mt-8 space-y-2">
|
|
607
|
+
<h3 className="text-sm font-medium text-muted-foreground">Schema Definition</h3>
|
|
608
|
+
<pre className="text-xs bg-muted p-3 rounded border overflow-auto max-h-48">
|
|
609
|
+
{JSON.stringify(type?.design_schema || { fields: {} }, null, 2)}
|
|
610
|
+
</pre>
|
|
611
|
+
</div>
|
|
612
|
+
</CardContent>
|
|
613
|
+
</Card>
|
|
614
|
+
|
|
615
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
616
|
+
<Card>
|
|
617
|
+
<CardContent className="p-6">
|
|
618
|
+
<div className="flex items-center gap-4">
|
|
619
|
+
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-primary/10">
|
|
620
|
+
<span className="text-primary font-semibold">{Object.keys(type?.design_schema?.fields || {}).length}</span>
|
|
621
|
+
</div>
|
|
622
|
+
<div>
|
|
623
|
+
<h3 className="text-sm font-medium">Fields</h3>
|
|
624
|
+
<p className="text-sm text-muted-foreground">Schema field definitions</p>
|
|
625
|
+
</div>
|
|
626
|
+
</div>
|
|
627
|
+
</CardContent>
|
|
628
|
+
</Card>
|
|
629
|
+
|
|
630
|
+
<Card>
|
|
631
|
+
<CardContent className="p-6">
|
|
632
|
+
<div className="flex items-center gap-4">
|
|
633
|
+
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-green-500/10">
|
|
634
|
+
<span className="text-green-600 font-semibold">{items?.length || 0}</span>
|
|
635
|
+
</div>
|
|
636
|
+
<div>
|
|
637
|
+
<h3 className="text-sm font-medium">Items</h3>
|
|
638
|
+
<p className="text-sm text-muted-foreground">Items using this type</p>
|
|
639
|
+
</div>
|
|
640
|
+
</div>
|
|
641
|
+
</CardContent>
|
|
642
|
+
</Card>
|
|
643
|
+
</div>
|
|
644
|
+
|
|
645
|
+
<Card>
|
|
646
|
+
<CardHeader>
|
|
647
|
+
<CardTitle>Items ({items?.length || 0})</CardTitle>
|
|
648
|
+
</CardHeader>
|
|
649
|
+
<CardContent>
|
|
650
|
+
{itemsLoading ? (
|
|
651
|
+
<div className="space-y-2">
|
|
652
|
+
<Skeleton className="h-8 w-full" />
|
|
653
|
+
<Skeleton className="h-8 w-full" />
|
|
654
|
+
<Skeleton className="h-8 w-full" />
|
|
655
|
+
</div>
|
|
656
|
+
) : items && items.length > 0 ? (
|
|
657
|
+
<DataTable
|
|
658
|
+
data={items}
|
|
659
|
+
columns={itemColumns as any}
|
|
660
|
+
searchable={false}
|
|
661
|
+
/>
|
|
662
|
+
) : (
|
|
663
|
+
<div className="text-center py-8 text-muted-foreground">
|
|
664
|
+
No items found for this type
|
|
665
|
+
</div>
|
|
666
|
+
)}
|
|
667
|
+
</CardContent>
|
|
668
|
+
</Card>
|
|
669
|
+
</>
|
|
670
|
+
)}
|
|
671
|
+
|
|
672
|
+
<Modal
|
|
673
|
+
isOpen={isDeleteModalOpen}
|
|
674
|
+
onClose={() => setIsDeleteModalOpen(false)}
|
|
675
|
+
title="Delete Type"
|
|
676
|
+
description="Are you sure you want to delete this type? This action cannot be undone."
|
|
677
|
+
size="sm"
|
|
678
|
+
>
|
|
679
|
+
<div className="flex justify-end space-x-3">
|
|
680
|
+
<Button variant="outline" onClick={() => setIsDeleteModalOpen(false)}>
|
|
681
|
+
Cancel
|
|
682
|
+
</Button>
|
|
683
|
+
<Button
|
|
684
|
+
variant="destructive"
|
|
685
|
+
onClick={() => deleteMutation.mutate()}
|
|
686
|
+
disabled={deleteMutation.loading}
|
|
687
|
+
>
|
|
688
|
+
{deleteMutation.loading ? 'Deleting...' : 'Delete Type'}
|
|
689
|
+
</Button>
|
|
690
|
+
</div>
|
|
691
|
+
</Modal>
|
|
692
|
+
</div>
|
|
693
|
+
)
|
|
694
|
+
}
|