spine-framework 0.3.124 → 0.3.126
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/cli/commands/agents.ts +35 -37
- package/.framework/cli/commands/auth.ts +35 -34
- package/.framework/cli/commands/create-app.ts +104 -40
- package/.framework/cli/commands/dev.ts +104 -69
- package/.framework/cli/commands/doctor.ts +179 -69
- package/.framework/cli/commands/generate.ts +83 -143
- package/.framework/cli/commands/init.ts +72 -34
- package/.framework/cli/commands/install-app.ts +260 -125
- package/.framework/cli/commands/items.ts +33 -44
- package/.framework/cli/commands/migrate.ts +122 -40
- package/.framework/cli/commands/migrations.ts +33 -40
- package/.framework/cli/commands/pipelines.ts +31 -39
- package/.framework/cli/commands/status.ts +47 -23
- package/.framework/cli/commands/system.ts +47 -37
- package/.framework/cli/commands/test.ts +112 -68
- package/.framework/cli/commands/validate-app.ts +215 -121
- package/.framework/cli/context.ts +112 -130
- package/.framework/cli/env-loader.ts +47 -18
- package/.framework/cli/index.ts +81 -42
- package/.framework/docs/runtime-types.md +1024 -0
- package/.framework/functions/_shared/agent-runner.ts +1296 -961
- package/.framework/functions/_shared/app-manifest.ts +82 -68
- package/.framework/functions/_shared/audit.ts +135 -83
- package/.framework/functions/_shared/{cil-state.ts → cil.ts} +158 -56
- package/.framework/functions/_shared/db.ts +80 -126
- package/.framework/functions/_shared/hash-api-key.ts +63 -0
- package/.framework/functions/_shared/index.ts +224 -335
- package/.framework/functions/_shared/kb.ts +319 -0
- package/.framework/functions/_shared/llm.ts +92 -0
- package/.framework/functions/_shared/logging.ts +288 -0
- package/.framework/functions/_shared/middleware.ts +329 -365
- package/.framework/functions/_shared/outcomes.ts +36 -0
- package/.framework/functions/_shared/permissions.ts +359 -643
- package/.framework/functions/_shared/pipeline-runner.ts +243 -316
- package/.framework/functions/_shared/principal.ts +376 -468
- package/.framework/functions/_shared/resolve-credentials.ts +102 -0
- package/.framework/functions/_shared/resolve-ids.ts +188 -16
- package/.framework/functions/_shared/retry-deliveries.ts +104 -0
- package/.framework/functions/_shared/schema-utils.ts +611 -373
- package/.framework/functions/_shared/testing.ts +207 -137
- package/.framework/functions/_shared/tracing.ts +276 -0
- package/.framework/functions/_shared/trigger-engine.ts +159 -211
- package/.framework/functions/_shared/webhook-registration.ts +95 -81
- package/.framework/functions/_shared/webhook-registry.ts +153 -75
- package/.framework/functions/account-nodes.ts +31 -53
- package/.framework/functions/adapters/generic-webhook-inbound.ts +56 -0
- package/.framework/functions/adapters/generic-webhook-outbound.ts +43 -0
- package/.framework/functions/adapters/openai-outbound.ts +40 -0
- package/.framework/functions/admin-data.ts +685 -537
- package/.framework/functions/admin-funnel.ts +31 -20
- package/.framework/functions/admin-install-funnel.ts +47 -28
- package/.framework/functions/admin-signals.ts +47 -29
- package/.framework/functions/ai-agents.ts +122 -147
- package/.framework/functions/ai-interactions.ts +180 -90
- package/.framework/functions/api-keys.ts +250 -192
- package/.framework/functions/apps.ts +203 -197
- package/.framework/functions/auth.ts +98 -91
- package/.framework/functions/cil-agent-ingest.ts +773 -0
- package/.framework/functions/cil-agent.ts +592 -0
- package/.framework/functions/cil-callmap.ts +1227 -0
- package/.framework/functions/cil-config.ts +583 -0
- package/.framework/functions/cil-context.ts +445 -0
- package/.framework/functions/cil-doc.ts +1752 -0
- package/.framework/functions/cil-embeddings.ts +1202 -0
- package/.framework/functions/cil-graph.ts +585 -0
- package/.framework/functions/cil-ingest.ts +2416 -0
- package/.framework/functions/cil-pipeline.ts +318 -0
- package/.framework/functions/cil-project.ts +499 -0
- package/.framework/functions/cil-validate.ts +469 -0
- package/.framework/functions/debug-auth.ts +73 -53
- package/.framework/functions/deliveries.ts +117 -0
- package/.framework/functions/embeddings.ts +528 -516
- package/.framework/functions/eval-harness.ts +158 -106
- package/.framework/functions/integration-outbound.ts +311 -0
- package/.framework/functions/integration-routes.ts +240 -466
- package/.framework/functions/integrations.ts +132 -140
- package/.framework/functions/invites.ts +401 -201
- package/.framework/functions/item-progress.ts +113 -53
- package/.framework/functions/kb.ts +28 -0
- package/.framework/functions/logs.ts +239 -222
- package/.framework/functions/observability.ts +308 -259
- package/.framework/functions/pipeline-executions.ts +179 -235
- package/.framework/functions/pipelines.ts +163 -205
- package/.framework/functions/prompt-configs.ts +109 -156
- package/.framework/functions/roles.ts +97 -147
- package/.framework/functions/system-cron.ts +132 -218
- package/.framework/functions/system.ts +112 -52
- package/.framework/functions/tests.ts +32 -28
- package/.framework/functions/timers.ts +162 -210
- package/.framework/functions/triggers.ts +282 -346
- package/.framework/functions/types.ts +283 -400
- package/.framework/migrations/001_core_schema.sql +630 -0
- package/.framework/migrations/002_core_rls_and_functions.sql +532 -0
- package/.framework/migrations/{003_system_data.sql → 003_core_system_data.sql} +26 -153
- package/.framework/migrations/004_core_rpc_and_broker.sql +421 -0
- package/.framework/migrations/005_core_observability.sql +598 -0
- package/.framework/migrations/006_security_hardening_rls.sql +97 -0
- package/.framework/migrations/007_kb_article_list.sql +237 -0
- package/.framework/migrations/008_items_target.sql +8 -0
- package/.framework/migrations/009_roles_slug_unique.sql +11 -0
- package/.framework/public/spine.config.json +91 -0
- package/.framework/scripts/app-install-cli.ts +112 -98
- package/.framework/src/App.tsx +67 -20
- package/.framework/src/apps/admin/index.tsx +97 -27
- package/.framework/src/components/AppWrapper.tsx +50 -11
- package/.framework/src/components/CustomAppLoader.tsx +131 -22
- package/.framework/src/components/admin/AdminListPage.tsx +75 -8
- package/.framework/src/components/admin/AdminSidebar.tsx +148 -36
- package/.framework/src/components/admin/AdminStatsCard.tsx +48 -29
- package/.framework/src/components/admin/PageInstructions.tsx +49 -0
- package/.framework/src/components/admin/SortableTableHeader.tsx +49 -2
- package/.framework/src/components/app-shell/GenericAppShell.tsx +69 -11
- package/.framework/src/components/app-shell/GenericDetailPage.tsx +55 -10
- package/.framework/src/components/app-shell/GenericListPage.tsx +76 -9
- package/.framework/src/components/app-sidebar.tsx +50 -3
- package/.framework/src/components/auth/ProtectedRoute.tsx +53 -45
- package/.framework/src/components/kb/KBArticleEditor.tsx +409 -0
- package/.framework/src/components/kb/KBArticleList.tsx +557 -0
- package/.framework/src/components/layout/AppShell.tsx +54 -16
- package/.framework/src/components/layout/Header.tsx +50 -38
- package/.framework/src/components/layout/Layout.tsx +48 -32
- package/.framework/src/components/layout/Sidebar.tsx +167 -16
- package/.framework/src/components/runtime/DataDetailHeader.tsx +33 -1
- package/.framework/src/components/runtime/DataDetailPage.tsx +289 -4
- package/.framework/src/components/runtime/DataFilters.tsx +78 -1
- package/.framework/src/components/runtime/DataHeader.tsx +32 -29
- package/.framework/src/components/runtime/DataListPage.tsx +161 -2
- package/.framework/src/components/runtime/DataStats.tsx +48 -1
- package/.framework/src/components/runtime/DataTable.tsx +153 -1
- package/.framework/src/components/runtime/SchemaDetailForm.tsx +77 -46
- package/.framework/src/components/runtime/index.ts +16 -9
- package/.framework/src/components/search-form.tsx +33 -1
- package/.framework/src/components/shared/AgentView.tsx +48 -51
- package/.framework/src/components/shared/FieldRenderer.tsx +84 -67
- package/.framework/src/components/shared/InviteManager.tsx +113 -52
- package/.framework/src/components/shared/MemberList.tsx +182 -0
- package/.framework/src/components/shared/SchemaFields.tsx +96 -78
- package/.framework/src/components/ui/DataTable.tsx +86 -56
- package/.framework/src/components/ui/Form.tsx +188 -109
- package/.framework/src/components/ui/ItemCard.tsx +86 -58
- package/.framework/src/components/ui/ItemListView.tsx +88 -61
- package/.framework/src/components/ui/LoadingSpinner.tsx +52 -27
- package/.framework/src/components/ui/Modal.tsx +81 -4
- package/.framework/src/components/ui/RichTextEditor.tsx +101 -9
- package/.framework/src/components/ui/accordion.tsx +97 -5
- package/.framework/src/components/ui/alert-dialog.tsx +225 -13
- package/.framework/src/components/ui/alert.tsx +113 -6
- package/.framework/src/components/ui/aspect-ratio.tsx +49 -2
- package/.framework/src/components/ui/avatar.tsx +129 -7
- package/.framework/src/components/ui/badge.tsx +65 -3
- package/.framework/src/components/ui/breadcrumb.tsx +145 -8
- package/.framework/src/components/ui/button-group.tsx +81 -4
- package/.framework/src/components/ui/button.tsx +49 -2
- package/.framework/src/components/ui/calendar.tsx +65 -3
- package/.framework/src/components/ui/card.tsx +145 -8
- package/.framework/src/components/ui/carousel.tsx +129 -7
- package/.framework/src/components/ui/chart.tsx +161 -10
- package/.framework/src/components/ui/checkbox.tsx +49 -2
- package/.framework/src/components/ui/collapsible.tsx +81 -5
- package/.framework/src/components/ui/combobox.tsx +305 -20
- package/.framework/src/components/ui/command.tsx +177 -11
- package/.framework/src/components/ui/context-menu.tsx +273 -17
- package/.framework/src/components/ui/dialog.tsx +209 -27
- package/.framework/src/components/ui/direction.tsx +64 -4
- package/.framework/src/components/ui/drawer.tsx +193 -12
- package/.framework/src/components/ui/dropdown-menu.tsx +273 -18
- package/.framework/src/components/ui/empty.tsx +145 -9
- package/.framework/src/components/ui/field.tsx +209 -14
- package/.framework/src/components/ui/hover-card.tsx +81 -4
- package/.framework/src/components/ui/input-group.tsx +162 -10
- package/.framework/src/components/ui/input-otp.tsx +98 -5
- package/.framework/src/components/ui/input.tsx +49 -2
- package/.framework/src/components/ui/item.tsx +225 -13
- package/.framework/src/components/ui/kbd.tsx +65 -3
- package/.framework/src/components/ui/label.tsx +49 -2
- package/.framework/src/components/ui/menubar.tsx +289 -17
- package/.framework/src/components/ui/native-select.tsx +97 -5
- package/.framework/src/components/ui/navigation-menu.tsx +177 -10
- package/.framework/src/components/ui/pagination.tsx +161 -9
- package/.framework/src/components/ui/popover.tsx +145 -8
- package/.framework/src/components/ui/progress.tsx +49 -2
- package/.framework/src/components/ui/radio-group.tsx +65 -3
- package/.framework/src/components/ui/resizable.tsx +82 -4
- package/.framework/src/components/ui/scroll-area.tsx +65 -3
- package/.framework/src/components/ui/select.tsx +193 -11
- package/.framework/src/components/ui/separator.tsx +49 -2
- package/.framework/src/components/ui/sheet.tsx +193 -11
- package/.framework/src/components/ui/sidebar.tsx +481 -38
- package/.framework/src/components/ui/skeleton.tsx +49 -2
- package/.framework/src/components/ui/slider.tsx +49 -2
- package/.framework/src/components/ui/sonner.tsx +49 -2
- package/.framework/src/components/ui/spinner.tsx +49 -2
- package/.framework/src/components/ui/switch.tsx +49 -2
- package/.framework/src/components/ui/table-primitives.tsx +160 -33
- package/.framework/src/components/ui/table.tsx +161 -9
- package/.framework/src/components/ui/tabs.tsx +113 -6
- package/.framework/src/components/ui/textarea.tsx +49 -2
- package/.framework/src/components/ui/toggle-group.tsx +81 -4
- package/.framework/src/components/ui/toggle.tsx +65 -3
- package/.framework/src/components/ui/tooltip.tsx +97 -5
- package/.framework/src/contexts/AppContext.tsx +129 -45
- package/.framework/src/contexts/AuthContext.tsx +187 -144
- package/.framework/src/hooks/use-mobile.ts +48 -2
- package/.framework/src/hooks/useApi.ts +208 -159
- package/.framework/src/hooks/useAppPath.ts +32 -23
- package/.framework/src/hooks/useApps.ts +51 -20
- package/.framework/src/hooks/useEntityList.ts +32 -78
- package/.framework/src/hooks/useEntityRecord.ts +35 -96
- package/.framework/src/hooks/useForm.ts +32 -92
- package/.framework/src/hooks/useListSchema.ts +49 -78
- package/.framework/src/hooks/useSchemaRecord.ts +114 -105
- package/.framework/src/lib/api.ts +195 -116
- package/.framework/src/lib/supabase.ts +98 -64
- package/.framework/src/lib/utils.ts +287 -151
- package/.framework/src/main.tsx +49 -3
- package/.framework/src/pages/DashboardPage.tsx +52 -29
- package/.framework/src/pages/NotFoundPage.tsx +32 -10
- package/.framework/src/pages/SetupPage.tsx +32 -12
- package/.framework/src/pages/admin/AIAgentDetailPage.tsx +119 -10
- package/.framework/src/pages/admin/AIAgentsPage.tsx +48 -19
- package/.framework/src/pages/admin/APIKeyDetailPage.tsx +187 -17
- package/.framework/src/pages/admin/APIKeysPage.tsx +98 -22
- package/.framework/src/pages/admin/AlertsConfigPage.tsx +113 -26
- package/.framework/src/pages/admin/AppDetailPage.tsx +125 -8
- package/.framework/src/pages/admin/AppsPage.tsx +49 -2
- package/.framework/src/pages/admin/DeliveriesPage.tsx +196 -0
- package/.framework/src/pages/admin/DesignedPage.tsx +32 -18
- package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +215 -484
- package/.framework/src/pages/admin/EmbeddingsPage.tsx +116 -319
- package/.framework/src/pages/admin/ExtendedShadcnTestPage.tsx +34 -9
- package/.framework/src/pages/admin/IncrementalShadcnTestPage.tsx +34 -9
- package/.framework/src/pages/admin/IntegratedDashboard.tsx +32 -16
- package/.framework/src/pages/admin/IntegrationDetailPage.tsx +183 -26
- package/.framework/src/pages/admin/IntegrationsPage.tsx +48 -19
- package/.framework/src/pages/admin/InvitesPage.tsx +48 -14
- package/.framework/src/pages/admin/LogsPage.tsx +422 -75
- package/.framework/src/pages/admin/MinimalShadcnTestPage.tsx +34 -9
- package/.framework/src/pages/admin/ObservabilityDashboard.tsx +113 -24
- package/.framework/src/pages/admin/PipelineDetailPage.tsx +206 -34
- package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +50 -17
- package/.framework/src/pages/admin/PipelinesPage.tsx +160 -21
- package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +378 -88
- package/.framework/src/pages/admin/PromptConfigsPage.tsx +125 -16
- package/.framework/src/pages/admin/ProperlyDesignedPage.tsx +49 -10
- package/.framework/src/pages/admin/RoleDetailPage.tsx +130 -10
- package/.framework/src/pages/admin/RolesPage.tsx +171 -18
- package/.framework/src/pages/admin/SelectTestPage.tsx +34 -9
- package/.framework/src/pages/admin/ShadcnTestPage.tsx +49 -11
- package/.framework/src/pages/admin/SimpleDashboard.tsx +49 -10
- package/.framework/src/pages/admin/TestRunDetailPage.tsx +49 -4
- package/.framework/src/pages/admin/TestingDashboard.tsx +98 -28
- package/.framework/src/pages/admin/TimerDetailPage.tsx +143 -33
- package/.framework/src/pages/admin/TimersPage.tsx +48 -20
- package/.framework/src/pages/admin/TriggerDetailPage.tsx +118 -30
- package/.framework/src/pages/admin/TriggersPage.tsx +64 -19
- package/.framework/src/pages/admin/TypeDetailPage.tsx +138 -15
- package/.framework/src/pages/admin/TypesPage.tsx +48 -20
- package/.framework/src/pages/admin/cil/CILAgentPage.tsx +465 -0
- package/.framework/src/pages/admin/cil/CILChunkDetailPage.tsx +787 -0
- package/.framework/src/pages/admin/cil/CILDashboardPage.tsx +699 -0
- package/.framework/src/pages/admin/cil/CILFileListPage.tsx +647 -0
- package/.framework/src/pages/admin/cil/CILGraphPage.tsx +616 -0
- package/.framework/src/pages/admin/cil/CILIngestionPage.tsx +1402 -0
- package/.framework/src/pages/admin/cil/CILProjectsPage.tsx +614 -0
- package/.framework/src/pages/admin/cil/CILSearchPage.tsx +571 -0
- package/.framework/src/pages/admin/cil/FlexiblePipelinePage.tsx +731 -0
- package/.framework/src/pages/admin/kb/KBAdminPage.tsx +64 -0
- package/.framework/src/pages/auth/AuthConfirmPage.tsx +52 -51
- package/.framework/src/pages/auth/ForgotPasswordPage.tsx +31 -22
- package/.framework/src/pages/auth/LoginPage.tsx +50 -23
- package/.framework/src/pages/auth/PendingApprovalPage.tsx +31 -15
- package/.framework/src/pages/auth/RegisterPage.tsx +281 -317
- package/.framework/src/pages/spine-framework/APIPage.tsx +33 -0
- package/.framework/src/pages/spine-framework/CLIPage.tsx +33 -0
- package/.framework/src/types/auth.ts +128 -71
- package/.framework/src/types/types.ts +373 -157
- package/custom/.devin/skills/spine-workbench-discovery/SKILL.md +39 -0
- package/custom/.devin/skills/spine-workbench-idea/SKILL.md +36 -0
- package/custom/.devin/skills/spine-workbench-implement/SKILL.md +46 -0
- package/custom/.devin/skills/spine-workbench-initiative/SKILL.md +28 -0
- package/custom/.devin/skills/spine-workbench-plan/SKILL.md +38 -0
- package/docs/README.md +9 -130
- package/netlify.toml +5 -2
- package/package.json +13 -25
- package/scripts/kb-ingest-docs.ts +203 -0
- package/spine.config.json +65 -4
- package/.framework/migrations/001_schema.sql +0 -425
- package/.framework/migrations/002_rls_and_functions.sql +0 -763
- package/.framework/migrations/004_enhancements.sql +0 -43
- package/.framework/migrations/005_auth_user_trigger.sql +0 -105
- package/.framework/migrations/006_fix_current_actor_id.sql +0 -21
- package/.framework/migrations/016_invites.sql +0 -27
- package/.framework/migrations/017_fix_get_account_apps.sql +0 -78
- package/.framework/migrations/018_apps_required_roles.sql +0 -17
- package/.framework/migrations/019_triggers_unique_constraint.sql +0 -13
- package/.framework/migrations/020_get_account_apps_role_filter.sql +0 -117
- package/.framework/migrations/021_app_domains.sql +0 -112
- package/.framework/migrations/022_route_prefix_domain_unique.sql +0 -21
- package/.framework/migrations/023_match_embeddings_rpc.sql +0 -78
- package/.framework/migrations/024_messages_account_id.sql +0 -9
- package/.framework/migrations/025_retype_threads_messages.sql +0 -240
- package/.framework/migrations/026_fix_rls_policies.sql +0 -47
- package/.framework/migrations/027_embedding_lifecycle.sql +0 -176
- package/.framework/migrations/028_fix_message_data_permissions.sql +0 -49
- package/.framework/migrations/029_tighten_support_triage_prompt.sql +0 -41
- package/.framework/migrations/030_messages_updated_by.sql +0 -6
- package/.framework/migrations/031_seed_upsert_constraints.sql +0 -35
- package/docs/APPS.md +0 -232
- package/docs/ARCHITECTURE.md +0 -246
- package/docs/INSTALL.md +0 -155
- package/docs/design-schema-spec.md +0 -1837
- package/docs/dev-guides/01-create-an-app.md +0 -183
- package/docs/dev-guides/02-create-a-function.md +0 -178
- package/docs/dev-guides/03-create-a-component.md +0 -178
- package/docs/dev-guides/04-webhook-handlers.md +0 -129
- package/docs/dev-guides/05-testing.md +0 -168
- package/docs/dev-guides/README.md +0 -40
- package/docs/npm-packages-plan.md +0 -288
- package/docs/spine-core--api-reference.md +0 -822
|
@@ -1,20 +1,30 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module src/pages/admin/TestingDashboard
|
|
3
|
-
* @audience installer
|
|
4
|
-
* @layer frontend-page
|
|
5
|
-
* @stability stable
|
|
6
|
-
*
|
|
7
|
-
* Admin Testing dashboard. Shows per-suite health cards (unit, integration,
|
|
8
|
-
* api, ui) and a table of the 20 most recent test runs. Read-only — tests
|
|
9
|
-
* are triggered by the agentic IDE, not from this UI.
|
|
10
|
-
*
|
|
11
|
-
* Route: /admin/testing
|
|
12
|
-
*
|
|
13
|
-
* @seeAlso src/pages/admin/TestRunDetailPage.tsx
|
|
14
|
-
* @seeAlso functions/tests.ts (data source)
|
|
15
|
-
* @seeAlso migrations_dayzero/008_test_runs.sql
|
|
16
|
-
*/
|
|
17
1
|
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
// CIL[BEGIN chunk_id="testingdashboard_module_header_1783665503" type="module_header" identity="module_header" parent="none"]
|
|
14
|
+
// CIL[DOC-MACRO purpose="Import React, router, API hooks and define testing interfaces"]
|
|
15
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Provide React components and test data types"]
|
|
16
|
+
// CIL[DOC-CONTEXT module="TestingDashboard" layer="frontend-page" related_files="[\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\"custom/apps/portal/pages/ContentPage.tsx\",\".framework/src/pages/admin/IntegrationsPage.tsx\",\".framework/functions/_shared/principal.ts\",\".framework/functions/_shared/schema-utils.ts\",\"\\\"]"]
|
|
17
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="React components and test interfaces" errors="none" side_effects="none"]
|
|
18
|
+
// CIL[DOC-MICRO logic_summary="Imports testing dependencies and defines SuiteStat and TestRun types" notable_decisions="[]"]
|
|
19
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
20
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
21
|
+
// CIL[DOC-AUDIENCE audience="developer"]
|
|
22
|
+
// CIL[DOC-VECTOR type="context" content="Layer: frontend-page. Direct callers: 0. Direct calls: 0. Related files: custom/apps/cortex/pages/operations/OperationsDashboard.tsx, custom/apps/portal/pages/ContentPage.tsx, .framework/src/pages/admin/IntegrationsPage.tsx, .framework/functions/_shared/principal.ts, .framework/functions/_shared/schema-utils.ts, \."]
|
|
23
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
24
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
25
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
26
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
27
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
18
28
|
import React from 'react'
|
|
19
29
|
import { useNavigate } from 'react-router-dom'
|
|
20
30
|
import { useApi } from '../../hooks/useApi'
|
|
@@ -24,8 +34,6 @@ import { FlaskConical, CheckCircle, XCircle, Clock, RefreshCw } from 'lucide-rea
|
|
|
24
34
|
import { LoadingSpinner } from '../../components/ui/LoadingSpinner'
|
|
25
35
|
import { formatDateTime } from '../../lib/utils'
|
|
26
36
|
|
|
27
|
-
// ─── TYPES ───────────────────────────────────────────────────────────────────
|
|
28
|
-
|
|
29
37
|
interface SuiteStat {
|
|
30
38
|
suite: string
|
|
31
39
|
total_runs: number
|
|
@@ -49,9 +57,22 @@ interface TestRun {
|
|
|
49
57
|
skipped: number | null
|
|
50
58
|
triggered_by: string | null
|
|
51
59
|
}
|
|
52
|
-
|
|
53
|
-
//
|
|
54
|
-
|
|
60
|
+
import { PageInstructions } from '../../components/admin/PageInstructions'
|
|
61
|
+
// CIL[END chunk_id="testingdashboard_module_header_1783665503"]
|
|
62
|
+
// CIL[BEGIN chunk_id="testingdashboard_helpers_1783665503" type="module" identity="helpers" parent="none"]
|
|
63
|
+
// CIL[DOC-MACRO purpose="Provide helper constants and utility functions for testing dashboard"]
|
|
64
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Define suite labels, status badge, and duration formatting"]
|
|
65
|
+
// CIL[DOC-CONTEXT module="TestingDashboard" layer="frontend-page" related_files="[\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\"custom/apps/portal/pages/ContentPage.tsx\",\"custom/apps/cortex/functions/custom_cil-embeddings.ts\",\".framework/functions/_shared/principal.ts\",\".framework/functions/_shared/schema-utils.ts\",\"\\\"]"]
|
|
66
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="formatted UI elements" errors="none" side_effects="none"]
|
|
67
|
+
// CIL[DOC-MICRO logic_summary="Creates status badges, duration labels, and suite name mappings" notable_decisions="[]"]
|
|
68
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
69
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
70
|
+
// CIL[DOC-VECTOR type="context" content="Layer: frontend-page. Direct callers: 0. Direct calls: 0. Related files: custom/apps/cortex/pages/operations/OperationsDashboard.tsx, custom/apps/portal/pages/ContentPage.tsx, custom/apps/cortex/functions/custom_cil-embeddings.ts, .framework/functions/_shared/principal.ts, .framework/functions/_shared/schema-utils.ts, \."]
|
|
71
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
72
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
73
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
74
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
75
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
55
76
|
const SUITE_LABELS: Record<string, string> = {
|
|
56
77
|
unit: 'Unit',
|
|
57
78
|
integration: 'Integration',
|
|
@@ -78,9 +99,21 @@ function durationLabel(ms: number | null): string {
|
|
|
78
99
|
if (ms < 1000) return `${ms}ms`
|
|
79
100
|
return `${(ms / 1000).toFixed(1)}s`
|
|
80
101
|
}
|
|
81
|
-
|
|
82
|
-
//
|
|
83
|
-
|
|
102
|
+
// CIL[END chunk_id="testingdashboard_helpers_1783665503"]
|
|
103
|
+
// CIL[BEGIN chunk_id="testingdashboard_suitecard_1783665503" type="function" identity="SuiteCard" parent="none"]
|
|
104
|
+
// CIL[DOC-MACRO purpose="Render test suite health card with status and statistics"]
|
|
105
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Display suite stats in a formatted card component"]
|
|
106
|
+
// CIL[DOC-CONTEXT module="TestingDashboard" layer="frontend-page" related_files="[\"custom/apps/portal/pages/ContentPage.tsx\",\".framework/src/pages/admin/RolesPage.tsx\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\".framework/src/pages/admin/IntegrationsPage.tsx\",\"\\\"]"]
|
|
107
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="JSX card element" errors="none" side_effects="none"]
|
|
108
|
+
// CIL[DOC-MICRO logic_summary="Calculates pass rate and renders suite status with metrics" notable_decisions="[]"]
|
|
109
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
110
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
111
|
+
// CIL[DOC-VECTOR type="context" content="Layer: frontend-page. Direct callers: 0. Direct calls: 0. Related files: custom/apps/portal/pages/ContentPage.tsx, .framework/src/pages/admin/RolesPage.tsx, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, .framework/src/pages/admin/IntegrationsPage.tsx, \."]
|
|
112
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
113
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
114
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
115
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
116
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
84
117
|
function SuiteCard({ stat }: { stat: SuiteStat }) {
|
|
85
118
|
const label = SUITE_LABELS[stat.suite] ?? stat.suite
|
|
86
119
|
const passRate = stat.last_total
|
|
@@ -113,9 +146,21 @@ function SuiteCard({ stat }: { stat: SuiteStat }) {
|
|
|
113
146
|
</div>
|
|
114
147
|
)
|
|
115
148
|
}
|
|
116
|
-
|
|
117
|
-
//
|
|
118
|
-
|
|
149
|
+
// CIL[END chunk_id="testingdashboard_suitecard_1783665503"]
|
|
150
|
+
// CIL[BEGIN chunk_id="testingdashboard_testingdashboard_1783665503" type="function" identity="TestingDashboard" parent="none"]
|
|
151
|
+
// CIL[DOC-MACRO purpose="Render testing dashboard with suite health cards and recent runs table"]
|
|
152
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Display suite stats, recent runs, and navigation to test details"]
|
|
153
|
+
// CIL[DOC-CONTEXT module="TestingDashboard" layer="frontend-page" related_files="[\"custom/apps/portal/pages/ContentPage.tsx\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\".framework/src/pages/admin/RolesPage.tsx\",\".framework/src/components/ui/dialog.tsx\",\".framework/functions/_shared/principal.ts\",\"\\\"]"]
|
|
154
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="Complete dashboard UI" errors="none" side_effects="none"]
|
|
155
|
+
// CIL[DOC-MICRO logic_summary="Renders header, suite cards grid, and recent runs table with navigation" notable_decisions="[]"]
|
|
156
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
157
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
158
|
+
// CIL[DOC-VECTOR type="context" content="Layer: frontend-page. Direct callers: 0. Direct calls: 0. Related files: custom/apps/portal/pages/ContentPage.tsx, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, .framework/src/pages/admin/RolesPage.tsx, .framework/src/components/ui/dialog.tsx, .framework/functions/_shared/principal.ts, \."]
|
|
159
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
160
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
161
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
162
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
163
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
119
164
|
export default function TestingDashboard() {
|
|
120
165
|
const navigate = useNavigate()
|
|
121
166
|
|
|
@@ -136,10 +181,23 @@ export default function TestingDashboard() {
|
|
|
136
181
|
refreshRuns()
|
|
137
182
|
}
|
|
138
183
|
|
|
139
|
-
// API returns { data: { data: [...], error: null }, error: null, meta: {...} }
|
|
140
184
|
const stats: SuiteStat[] = (statsData as any)?.data?.data ?? []
|
|
141
185
|
const runs: TestRun[] = (runsData as any)?.data?.data ?? []
|
|
142
186
|
|
|
187
|
+
// CIL[BEGIN chunk_id="testingdashboard_return_1783665503" type="jsx" identity="return" parent="testingdashboard_testingdashboard_1783665503"]
|
|
188
|
+
// CIL[DOC-MACRO purpose="Render testing dashboard with suite health cards and recent runs table"]
|
|
189
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Handle return"]
|
|
190
|
+
// CIL[DOC-CONTEXT module="TestingDashboard" layer="frontend-page" related_files="[\"custom/apps/portal/pages/ContentPage.tsx\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\".framework/src/pages/admin/RolesPage.tsx\",\".framework/src/components/ui/dialog.tsx\",\".framework/functions/_shared/principal.ts\",\"\\\"]"]
|
|
191
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="Complete dashboard UI" errors="none" side_effects="none"]
|
|
192
|
+
// CIL[DOC-MICRO logic_summary="Renders header, suite cards grid, and recent runs table with navigation" notable_decisions="[]"]
|
|
193
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
194
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
195
|
+
// CIL[DOC-VECTOR type="context" content="Layer: frontend-page. Direct callers: 0. Direct calls: 0. Related files: custom/apps/portal/pages/ContentPage.tsx, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, .framework/src/pages/admin/RolesPage.tsx, .framework/src/components/ui/dialog.tsx, .framework/functions/_shared/principal.ts, \."]
|
|
196
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
197
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
198
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
199
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
200
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
143
201
|
return (
|
|
144
202
|
<div className="p-6 max-w-6xl mx-auto">
|
|
145
203
|
{/* Header */}
|
|
@@ -157,6 +215,16 @@ export default function TestingDashboard() {
|
|
|
157
215
|
</button>
|
|
158
216
|
</div>
|
|
159
217
|
|
|
218
|
+
<PageInstructions
|
|
219
|
+
description="Run and review Spine test suites."
|
|
220
|
+
sections={[
|
|
221
|
+
{ title: 'What this page is for', content: 'This page displays the health and history of the unit, integration, API, and UI test suites.' },
|
|
222
|
+
{ title: 'How to read the cards', content: 'The suite health cards show the most recent run status, pass rate, total runs, and last run time. Colors indicate passed, failed, running, or unknown states.' },
|
|
223
|
+
{ title: 'How to use this page', content: 'Click the Refresh button to reload stats and runs. Click a row in the Recent Runs table to view individual test case results. Suites are triggered from the IDE or CI pipeline.' },
|
|
224
|
+
{ title: 'Important notes', content: 'A pass rate below 100% is shown in red. Empty states mean no runs have been recorded yet for that suite.' },
|
|
225
|
+
]}
|
|
226
|
+
/>
|
|
227
|
+
|
|
160
228
|
{/* Suite health cards */}
|
|
161
229
|
{statsLoading ? (
|
|
162
230
|
<div className="flex justify-center py-8"><LoadingSpinner /></div>
|
|
@@ -254,4 +322,6 @@ export default function TestingDashboard() {
|
|
|
254
322
|
)}
|
|
255
323
|
</div>
|
|
256
324
|
)
|
|
325
|
+
// CIL[END chunk_id="testingdashboard_return_1783665503"]
|
|
257
326
|
}
|
|
327
|
+
// CIL[END chunk_id="testingdashboard_testingdashboard_1783665503"]
|
|
@@ -1,6 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
// CIL[BEGIN chunk_id="timerdetailpage_moduleheader_1783664706" type="module_header" identity="module_header" parent="none"]
|
|
6
|
+
// CIL[DOC-MACRO purpose="Import dependencies for TimerDetailPage component"]
|
|
7
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Provide all external dependencies and UI components"]
|
|
8
|
+
// CIL[DOC-CONTEXT module="TimerDetailPage" layer="frontend-page" related_files="[\".framework/src/components/ui/dialog.tsx\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\"custom/apps/portal/pages/ContentPage.tsx\",\".framework/src/pages/admin/TypeDetailPage.tsx\",\".framework/src/pages/admin/RolesPage.tsx\",\"\\\"]"]
|
|
9
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="React components and utilities" errors="none" side_effects="none"]
|
|
10
|
+
// CIL[DOC-MICRO logic_summary="Imports React hooks, navigation, API utilities, and UI components" notable_decisions="[]"]
|
|
11
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
12
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
13
|
+
// CIL[DOC-AUDIENCE audience="developer"]
|
|
14
|
+
// CIL[DOC-VECTOR type="context" content="Layer: frontend-page. Direct callers: 0. Direct calls: 0. Related files: .framework/src/components/ui/dialog.tsx, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, custom/apps/portal/pages/ContentPage.tsx, .framework/src/pages/admin/TypeDetailPage.tsx, .framework/src/pages/admin/RolesPage.tsx, \."]
|
|
15
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
16
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
17
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
18
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
19
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
4
20
|
import { useState, useEffect } from 'react'
|
|
5
21
|
import { useParams, useNavigate } from 'react-router-dom'
|
|
6
22
|
import { apiFetch } from '../../lib/api'
|
|
@@ -16,31 +32,70 @@ import { Alert, AlertDescription, AlertTitle } from '../../components/ui/alert'
|
|
|
16
32
|
import { Badge } from '../../components/ui/badge'
|
|
17
33
|
import { ArrowLeft, Clock, AlertCircle } from 'lucide-react'
|
|
18
34
|
import { formatDateTime } from '../../lib/utils'
|
|
19
|
-
|
|
35
|
+
import { PageInstructions } from '../../components/admin/PageInstructions'
|
|
36
|
+
// CIL[END chunk_id="timerdetailpage_moduleheader_1783664706"]
|
|
37
|
+
// CIL[BEGIN chunk_id="timerdetailpage_timer_1783664706" type="interface" identity="Timer" parent="none"]
|
|
38
|
+
// CIL[DOC-MACRO purpose="Define the Timer data structure for timer management"]
|
|
39
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Provide type definition for scheduled/recurring timers"]
|
|
40
|
+
// CIL[DOC-CONTEXT module="TimerDetailPage" layer="frontend-page" related_files="[\".framework/src/pages/admin/IntegrationsPage.tsx\",\".framework/functions/_shared/principal.ts\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\".framework/functions/_shared/schema-utils.ts\",\".framework/src/pages/admin/TypeDetailPage.tsx\",\"\\\"]"]
|
|
41
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="Timer interface" errors="none" side_effects="none"]
|
|
42
|
+
// CIL[DOC-MICRO logic_summary="Defines timer structure with execution tracking and target configuration" notable_decisions="[]"]
|
|
43
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
44
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
45
|
+
// CIL[DOC-VECTOR type="context" content="Layer: frontend-page. Direct callers: 0. Direct calls: 0. Related files: .framework/src/pages/admin/IntegrationsPage.tsx, .framework/functions/_shared/principal.ts, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, .framework/functions/_shared/schema-utils.ts, .framework/src/pages/admin/TypeDetailPage.tsx, \."]
|
|
46
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
47
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
48
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
49
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
50
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
20
51
|
interface Timer {
|
|
21
52
|
id: string
|
|
22
53
|
name: string
|
|
23
54
|
description?: string
|
|
24
|
-
|
|
55
|
+
app_id?: string
|
|
25
56
|
timer_type: 'interval' | 'cron' | 'once'
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
last_run_status?: 'success' | 'failed'
|
|
57
|
+
config?: Record<string, any>
|
|
58
|
+
pipeline_id?: string
|
|
59
|
+
metadata?: Record<string, any>
|
|
30
60
|
is_active: boolean
|
|
31
|
-
target_type: string
|
|
32
|
-
target_action: string
|
|
33
|
-
target_config: Record<string, any>
|
|
34
61
|
created_at: string
|
|
35
62
|
updated_at: string
|
|
36
63
|
}
|
|
37
|
-
|
|
64
|
+
// CIL[END chunk_id="timerdetailpage_timer_1783664706"]
|
|
65
|
+
// CIL[BEGIN chunk_id="timerdetailpage_timertypes_1783664706" type="constant" identity="TIMER_TYPES" parent="none"]
|
|
66
|
+
// CIL[DOC-MACRO purpose="Define timer type options for form selection"]
|
|
67
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Provide timer type configuration options"]
|
|
68
|
+
// CIL[DOC-CONTEXT module="TimerDetailPage" layer="frontend-page" related_files="[\".framework/src/pages/admin/TypeDetailPage.tsx\",\".framework/functions/_shared/schema-utils.ts\",\".framework/functions/_shared/principal.ts\",\".framework/migrations/025_retype_threads_messages.sql\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\"\\\"]"]
|
|
69
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="TIMER_TYPES constant" errors="none" side_effects="none"]
|
|
70
|
+
// CIL[DOC-MICRO logic_summary="Defines available timer types: interval, cron, and one-time" notable_decisions="[]"]
|
|
71
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
72
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
73
|
+
// CIL[DOC-VECTOR type="context" content="Layer: frontend-page. Direct callers: 0. Direct calls: 0. Related files: .framework/src/pages/admin/TypeDetailPage.tsx, .framework/functions/_shared/schema-utils.ts, .framework/functions/_shared/principal.ts, .framework/migrations/025_retype_threads_messages.sql, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, \."]
|
|
74
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
75
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
76
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
77
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
78
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
38
79
|
const TIMER_TYPES = [
|
|
39
80
|
{ value: 'interval', label: 'Interval' },
|
|
40
81
|
{ value: 'cron', label: 'Cron Expression' },
|
|
41
82
|
{ value: 'once', label: 'One-time' }
|
|
42
83
|
]
|
|
43
|
-
|
|
84
|
+
// CIL[END chunk_id="timerdetailpage_timertypes_1783664706"]
|
|
85
|
+
// CIL[BEGIN chunk_id="timerdetailpage_timerdetailpage_1783664706" type="function" identity="TimerDetailPage" parent="none"]
|
|
86
|
+
// CIL[DOC-MACRO purpose="Create/view/edit/delete page for single timer records"]
|
|
87
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Manage timer CRUD operations with form handling and validation"]
|
|
88
|
+
// CIL[DOC-CONTEXT module="TimerDetailPage" layer="frontend-page" related_files="[\".framework/src/pages/admin/TypeDetailPage.tsx\",\"custom/apps/portal/pages/ContentPage.tsx\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\".framework/src/pages/admin/RolesPage.tsx\",\"\\\"]"]
|
|
89
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="React component with timer form" errors="API failures, validation errors" side_effects="API calls, navigation"]
|
|
90
|
+
// CIL[DOC-MICRO logic_summary="Form handling with timer configuration and execution tracking" notable_decisions="[]"]
|
|
91
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
92
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
93
|
+
// CIL[DOC-VECTOR type="context" content="Layer: frontend-page. Direct callers: 0. Direct calls: 0. Related files: .framework/src/pages/admin/TypeDetailPage.tsx, custom/apps/portal/pages/ContentPage.tsx, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, .framework/src/pages/admin/RolesPage.tsx, \."]
|
|
94
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
95
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
96
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
97
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
98
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
44
99
|
export function TimerDetailPage() {
|
|
45
100
|
const { id } = useParams<{ id: string }>()
|
|
46
101
|
const navigate = useNavigate()
|
|
@@ -49,7 +104,16 @@ export function TimerDetailPage() {
|
|
|
49
104
|
const [loading, setLoading] = useState(true)
|
|
50
105
|
const [error, setError] = useState<string | null>(null)
|
|
51
106
|
const [isEditing, setIsEditing] = useState(isCreateMode)
|
|
52
|
-
const [formData, setFormData] = useState({
|
|
107
|
+
const [formData, setFormData] = useState({
|
|
108
|
+
name: '',
|
|
109
|
+
description: '',
|
|
110
|
+
app_id: '',
|
|
111
|
+
timer_type: 'interval' as const,
|
|
112
|
+
config: JSON.stringify({ schedule: '', interval_seconds: 3600 }, null, 2),
|
|
113
|
+
pipeline_id: '',
|
|
114
|
+
metadata: '{}',
|
|
115
|
+
is_active: true
|
|
116
|
+
})
|
|
53
117
|
|
|
54
118
|
useEffect(() => {
|
|
55
119
|
if (isCreateMode) {
|
|
@@ -65,9 +129,14 @@ export function TimerDetailPage() {
|
|
|
65
129
|
const data = result.data
|
|
66
130
|
setTimer(data)
|
|
67
131
|
setFormData({
|
|
68
|
-
name: data.name,
|
|
69
|
-
|
|
70
|
-
|
|
132
|
+
name: data.name,
|
|
133
|
+
description: data.description || '',
|
|
134
|
+
app_id: data.app_id || '',
|
|
135
|
+
timer_type: data.timer_type,
|
|
136
|
+
config: JSON.stringify(data.config || {}, null, 2),
|
|
137
|
+
pipeline_id: data.pipeline_id || '',
|
|
138
|
+
metadata: JSON.stringify(data.metadata || {}, null, 2),
|
|
139
|
+
is_active: data.is_active ?? true
|
|
71
140
|
})
|
|
72
141
|
} catch (err) {
|
|
73
142
|
setError(err instanceof Error ? err.message : 'Failed to load timer')
|
|
@@ -81,10 +150,22 @@ export function TimerDetailPage() {
|
|
|
81
150
|
const handleSave = async () => {
|
|
82
151
|
try {
|
|
83
152
|
let parsedConfig = {}
|
|
84
|
-
|
|
153
|
+
let parsedMetadata = {}
|
|
154
|
+
try { parsedConfig = JSON.parse(formData.config) } catch { throw new Error('Invalid config JSON') }
|
|
155
|
+
try { parsedMetadata = JSON.parse(formData.metadata) } catch { throw new Error('Invalid metadata JSON') }
|
|
85
156
|
const url = isCreateMode ? '/api/timers?action=create' : `/api/timers?action=update&id=${id}`
|
|
86
157
|
const method = isCreateMode ? 'POST' : 'PATCH'
|
|
87
|
-
const
|
|
158
|
+
const saveData = {
|
|
159
|
+
name: formData.name,
|
|
160
|
+
description: formData.description,
|
|
161
|
+
app_id: formData.app_id || null,
|
|
162
|
+
timer_type: formData.timer_type,
|
|
163
|
+
config: parsedConfig,
|
|
164
|
+
pipeline_id: formData.pipeline_id || null,
|
|
165
|
+
metadata: parsedMetadata,
|
|
166
|
+
is_active: formData.is_active
|
|
167
|
+
}
|
|
168
|
+
const response = await apiFetch(url, { method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(saveData) })
|
|
88
169
|
if (!response.ok) throw new Error('Failed to save timer')
|
|
89
170
|
if (isCreateMode) {
|
|
90
171
|
const result = await response.json()
|
|
@@ -120,32 +201,61 @@ export function TimerDetailPage() {
|
|
|
120
201
|
</div>
|
|
121
202
|
</div>
|
|
122
203
|
|
|
123
|
-
<
|
|
204
|
+
<PageInstructions
|
|
205
|
+
description="Schedule a timer to run actions at intervals, via cron, or once."
|
|
206
|
+
sections={[
|
|
207
|
+
{ title: 'What this timer controls', content: 'A timer is a scheduled job that calls a target function or pipeline on an interval, a cron schedule, or one time. It is the Spine equivalent of a recurring or one-off background task.' },
|
|
208
|
+
{ title: 'Fields explained', content: <ul className="list-disc pl-4 space-y-1">
|
|
209
|
+
<li><strong>Name</strong> — string. The display name. Missing name prevents saving.</li>
|
|
210
|
+
<li><strong>Type</strong> — select. interval, cron, or once. Controls which schedule field in Config is used and how the next run is calculated.</li>
|
|
211
|
+
<li><strong>App ID</strong> — string. Optional owning app UUID; blank means account-scoped.</li>
|
|
212
|
+
<li><strong>Pipeline ID</strong> — string. The pipeline to invoke when the timer fires.</li>
|
|
213
|
+
<li><strong>Active</strong> — checkbox. Inactive timers are skipped by the scheduler.</li>
|
|
214
|
+
<li><strong>Description</strong> — string. Optional notes.</li>
|
|
215
|
+
<li><strong>Config (JSON)</strong> — JSON. The timer schedule, e.g. { `{ "schedule": "0 */6 * * *" }` } for cron, { `{ "interval_seconds": 3600 }` } for interval, or { `{ "run_at": "2026-01-01T00:00:00Z" }` } for once.</li>
|
|
216
|
+
<li><strong>Metadata (JSON)</strong> — JSON. Optional metadata about the timer.</li>
|
|
217
|
+
</ul> },
|
|
218
|
+
{ title: 'Options and values', content: <ul className="list-disc pl-4 space-y-1">
|
|
219
|
+
<li><strong>interval</strong>: config should contain interval_seconds.</li>
|
|
220
|
+
<li><strong>cron</strong>: config should contain a schedule cron expression like 0 */6 * * *.</li>
|
|
221
|
+
<li><strong>once</strong>: config should contain a run_at ISO timestamp.</li>
|
|
222
|
+
</ul> },
|
|
223
|
+
{ title: 'Downstream impact', content: <ul className="list-disc pl-4 space-y-1">
|
|
224
|
+
<li>The scheduler uses the active flag and the timer_type/config fields to determine the next run and to enqueue the pipeline.</li>
|
|
225
|
+
<li>Pipeline ID must refer to an existing pipeline; otherwise the timer will fail with a missing handler.</li>
|
|
226
|
+
</ul> },
|
|
227
|
+
{ title: 'How to create, save, and validate', content: <ol className="list-decimal pl-4 space-y-1">
|
|
228
|
+
<li>Pick the timer type and fill the Config JSON with the matching schedule. Enter the Pipeline ID and optional Metadata JSON.</li>
|
|
229
|
+
<li>Click Create to save or Edit then Save for existing timers.</li>
|
|
230
|
+
<li>Validation checks the name, timer type, and that Config and Metadata are valid JSON.</li>
|
|
231
|
+
</ol> },
|
|
232
|
+
{ title: 'Common mistakes', content: <ul className="list-disc pl-4 space-y-1">
|
|
233
|
+
<li>Selecting cron but filling interval_seconds instead of a cron schedule.</li>
|
|
234
|
+
<li>Using an invalid cron expression, which the scheduler will not run.</li>
|
|
235
|
+
<li>Leaving Pipeline ID blank when the scheduler expects a pipeline to invoke.</li>
|
|
236
|
+
</ul> },
|
|
237
|
+
]}
|
|
238
|
+
/>
|
|
239
|
+
|
|
240
|
+
<Card><CardContent className="p-6 space-y-6">
|
|
124
241
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
125
242
|
<div className="space-y-4">
|
|
126
243
|
<h3 className="text-sm font-medium text-muted-foreground">Configuration</h3>
|
|
127
244
|
<div className="space-y-2"><Label>Name</Label>{isEditing ? <Input value={formData.name} onChange={e => setFormData({...formData, name: e.target.value})} /> : <div className="text-sm">{timer?.name}</div>}</div>
|
|
245
|
+
<div className="space-y-2"><Label>App ID</Label>{isEditing ? <Input value={formData.app_id} onChange={e => setFormData({...formData, app_id: e.target.value})} placeholder="Optional app UUID" /> : <div className="text-sm font-mono">{timer?.app_id || '—'}</div>}</div>
|
|
128
246
|
<div className="space-y-2"><Label>Type</Label>{isEditing ? <Select value={formData.timer_type} onValueChange={v => setFormData({...formData, timer_type: v as any})}><SelectTrigger><SelectValue /></SelectTrigger><SelectContent>{TIMER_TYPES.map(t => <SelectItem key={t.value} value={t.value}>{t.label}</SelectItem>)}</SelectContent></Select> : <Badge>{timer?.timer_type}</Badge>}</div>
|
|
129
|
-
{formData.timer_type === 'interval' && <div className="space-y-2"><Label>Interval (seconds)</Label>{isEditing ? <Input type="number" value={formData.interval_seconds} onChange={e => setFormData({...formData, interval_seconds: parseInt(e.target.value)})} /> : <div className="text-sm">{timer?.interval_seconds}s</div>}</div>}
|
|
130
|
-
<div className="space-y-2"><Label>Schedule/Cron</Label>{isEditing ? <Input value={formData.schedule} onChange={e => setFormData({...formData, schedule: e.target.value})} placeholder={formData.timer_type === 'interval' ? '3600' : '0 */6 * * *'} /> : <div className="text-sm font-mono">{timer?.schedule}</div>}</div>
|
|
131
247
|
<div className="space-y-2"><Label>Active</Label>{isEditing ? <div className="flex items-center gap-2"><Checkbox id="active" checked={formData.is_active} onCheckedChange={c => setFormData({...formData, is_active: c === true})} /><Label htmlFor="active">Enabled</Label></div> : <div className="text-sm">{timer?.is_active ? 'Yes' : 'No'}</div>}</div>
|
|
132
248
|
</div>
|
|
133
249
|
<div className="space-y-4">
|
|
134
250
|
<h3 className="text-sm font-medium text-muted-foreground">Execution</h3>
|
|
135
|
-
{
|
|
136
|
-
<>
|
|
137
|
-
<div className="flex justify-between"><span className="text-sm text-muted-foreground">Next Run</span><span className="text-sm">{timer?.next_run ? formatDateTime(timer.next_run) : '—'}</span></div>
|
|
138
|
-
<div className="flex justify-between"><span className="text-sm text-muted-foreground">Last Run</span><span className="text-sm">{timer?.last_run ? formatDateTime(timer.last_run) : '—'}</span></div>
|
|
139
|
-
<div className="flex justify-between"><span className="text-sm text-muted-foreground">Last Status</span><Badge variant={timer?.last_run_status === 'success' ? 'default' : 'destructive'}>{timer?.last_run_status || '—'}</Badge></div>
|
|
140
|
-
</>
|
|
141
|
-
)}
|
|
142
|
-
<div className="space-y-2"><Label>Target Type</Label>{isEditing ? <Input value={formData.target_type} onChange={e => setFormData({...formData, target_type: e.target.value})} placeholder="e.g., function, pipeline" /> : <div className="text-sm">{timer?.target_type}</div>}</div>
|
|
143
|
-
<div className="space-y-2"><Label>Target Action</Label>{isEditing ? <Input value={formData.target_action} onChange={e => setFormData({...formData, target_action: e.target.value})} placeholder="e.g., execute" /> : <div className="text-sm">{timer?.target_action}</div>}</div>
|
|
251
|
+
<div className="space-y-2"><Label>Pipeline ID</Label>{isEditing ? <Input value={formData.pipeline_id} onChange={e => setFormData({...formData, pipeline_id: e.target.value})} placeholder="Pipeline to invoke" /> : <div className="text-sm font-mono">{timer?.pipeline_id || '—'}</div>}</div>
|
|
144
252
|
</div>
|
|
145
253
|
</div>
|
|
146
254
|
<div className="mt-6 space-y-2"><Label>Description</Label>{isEditing ? <Textarea value={formData.description} onChange={e => setFormData({...formData, description: e.target.value})} rows={3} /> : <div className="text-sm">{timer?.description || '—'}</div>}</div>
|
|
147
|
-
<div className="mt-6 space-y-2"><Label>
|
|
255
|
+
<div className="mt-6 space-y-2"><Label>Config (JSON)</Label>{isEditing ? <Textarea value={formData.config} onChange={e => setFormData({...formData, config: e.target.value})} rows={6} className="font-mono text-sm" /> : <pre className="text-sm bg-muted p-3 rounded">{JSON.stringify(timer?.config || {}, null, 2)}</pre>}</div>
|
|
256
|
+
<div className="mt-6 space-y-2"><Label>Metadata (JSON)</Label>{isEditing ? <Textarea value={formData.metadata} onChange={e => setFormData({...formData, metadata: e.target.value})} rows={4} className="font-mono text-sm" /> : <pre className="text-sm bg-muted p-3 rounded">{JSON.stringify(timer?.metadata || {}, null, 2)}</pre>}</div>
|
|
148
257
|
</CardContent></Card>
|
|
149
258
|
</div>
|
|
150
259
|
)
|
|
151
260
|
}
|
|
261
|
+
// CIL[END chunk_id="timerdetailpage_timerdetailpage_1783664706"]
|
|
@@ -1,18 +1,21 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module src/pages/admin/TimersPage
|
|
3
|
-
* @audience installer
|
|
4
|
-
* @layer frontend-page
|
|
5
|
-
* @stability stable
|
|
6
|
-
*
|
|
7
|
-
* Admin list page for scheduled timers. Fetches all timers via
|
|
8
|
-
* `/api/timers?action=list`, applies client-side search, type filter
|
|
9
|
-
* (`schedule` | `delay` | `recurring` | `cron`), and sort. Renders
|
|
10
|
-
* inside `AdminListPage` with stat cards and a sortable table. Row
|
|
11
|
-
* clicks navigate to `/spine-framework/admin/configs/timers/:id`.
|
|
12
|
-
*
|
|
13
|
-
* @seeAlso src/pages/admin/TimerDetailPage.tsx
|
|
14
|
-
*/
|
|
15
1
|
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
// CIL[BEGIN chunk_id="timerspage_moduleheader_1783664706" type="module_header" identity="module_header" parent="none"]
|
|
5
|
+
// CIL[DOC-MACRO purpose="Import dependencies for TimersPage component"]
|
|
6
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Provide all external dependencies and UI components"]
|
|
7
|
+
// CIL[DOC-CONTEXT module="TimersPage" layer="frontend-page" related_files="[\".framework/src/components/layout/Sidebar.tsx\",\".framework/src/components/ui/dialog.tsx\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\"custom/apps/portal/pages/ContentPage.tsx\",\".framework/src/pages/admin/TypeDetailPage.tsx\",\"\\\"]"]
|
|
8
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="React components and utilities" errors="none" side_effects="none"]
|
|
9
|
+
// CIL[DOC-MICRO logic_summary="Imports React hooks, navigation, API utilities, icons, and UI components" notable_decisions="[]"]
|
|
10
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
11
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
12
|
+
// CIL[DOC-AUDIENCE audience="developer"]
|
|
13
|
+
// CIL[DOC-VECTOR type="context" content="Layer: frontend-page. Direct callers: 0. Direct calls: 0. Related files: .framework/src/components/layout/Sidebar.tsx, .framework/src/components/ui/dialog.tsx, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, custom/apps/portal/pages/ContentPage.tsx, .framework/src/pages/admin/TypeDetailPage.tsx, \."]
|
|
14
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
15
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
16
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
17
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
18
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
16
19
|
import React, { useState } from 'react'
|
|
17
20
|
import { useNavigate } from 'react-router-dom'
|
|
18
21
|
import { useApi } from '../../hooks/useApi'
|
|
@@ -24,7 +27,21 @@ import { Badge } from '../../components/ui/badge'
|
|
|
24
27
|
import { formatDateTime } from '../../lib/utils'
|
|
25
28
|
import { AdminListPage } from '../../components/admin/AdminListPage'
|
|
26
29
|
import { SortableTableHeader } from '../../components/admin/SortableTableHeader'
|
|
27
|
-
|
|
30
|
+
// CIL[END chunk_id="timerspage_moduleheader_1783664706"]
|
|
31
|
+
// CIL[BEGIN chunk_id="timerspage_timer_1783664706" type="interface" identity="Timer" parent="none"]
|
|
32
|
+
// CIL[DOC-MACRO purpose="Define the Timer data structure for timer management"]
|
|
33
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Provide type definition for scheduled/recurring timers"]
|
|
34
|
+
// CIL[DOC-CONTEXT module="TimersPage" layer="frontend-page" related_files="[\".framework/src/pages/admin/IntegrationsPage.tsx\",\".framework/functions/_shared/principal.ts\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\".framework/functions/_shared/schema-utils.ts\",\".framework/src/pages/admin/TypeDetailPage.tsx\",\"\\\"]"]
|
|
35
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="Timer interface" errors="none" side_effects="none"]
|
|
36
|
+
// CIL[DOC-MICRO logic_summary="Defines comprehensive timer structure with execution statistics" notable_decisions="[]"]
|
|
37
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
38
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
39
|
+
// CIL[DOC-VECTOR type="context" content="Layer: frontend-page. Direct callers: 0. Direct calls: 0. Related files: .framework/src/pages/admin/IntegrationsPage.tsx, .framework/functions/_shared/principal.ts, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, .framework/functions/_shared/schema-utils.ts, .framework/src/pages/admin/TypeDetailPage.tsx, \."]
|
|
40
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
41
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
42
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
43
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
44
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
28
45
|
interface Timer {
|
|
29
46
|
id: string
|
|
30
47
|
name: string
|
|
@@ -50,7 +67,21 @@ interface Timer {
|
|
|
50
67
|
success_count: number
|
|
51
68
|
failure_count: number
|
|
52
69
|
}
|
|
53
|
-
|
|
70
|
+
// CIL[END chunk_id="timerspage_timer_1783664706"]
|
|
71
|
+
// CIL[BEGIN chunk_id="timerspage_timerspage_1783664706" type="function" identity="TimersPage" parent="none"]
|
|
72
|
+
// CIL[DOC-MACRO purpose="Admin list page for managing scheduled and recurring timers"]
|
|
73
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Fetch, filter, sort, and display timers with navigation"]
|
|
74
|
+
// CIL[DOC-CONTEXT module="TimersPage" layer="frontend-page" related_files="[\"custom/apps/portal/pages/ContentPage.tsx\",\".framework/src/pages/admin/TypeDetailPage.tsx\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\".framework/src/pages/admin/RolesPage.tsx\",\"\\\"]"]
|
|
75
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="React component with timer list" errors="API failures" side_effects="API calls, navigation"]
|
|
76
|
+
// CIL[DOC-MICRO logic_summary="Client-side filtering, sorting, and stats calculation for timers" notable_decisions="[]"]
|
|
77
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
78
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
79
|
+
// CIL[DOC-VECTOR type="context" content="Layer: frontend-page. Direct callers: 0. Direct calls: 0. Related files: custom/apps/portal/pages/ContentPage.tsx, .framework/src/pages/admin/TypeDetailPage.tsx, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, .framework/src/pages/admin/RolesPage.tsx, \."]
|
|
80
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
81
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
82
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
83
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
84
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
54
85
|
export function TimersPage() {
|
|
55
86
|
const navigate = useNavigate()
|
|
56
87
|
const [searchTerm, setSearchTerm] = useState('')
|
|
@@ -59,7 +90,6 @@ export function TimersPage() {
|
|
|
59
90
|
const [sortKey, setSortKey] = useState('name')
|
|
60
91
|
const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('asc')
|
|
61
92
|
|
|
62
|
-
// Fetch timers from API
|
|
63
93
|
const { data: timers, loading, error, refetch } = useApi<Timer[]>(
|
|
64
94
|
async ({ signal }: { signal?: AbortSignal }) => {
|
|
65
95
|
try {
|
|
@@ -75,7 +105,6 @@ export function TimersPage() {
|
|
|
75
105
|
const result = await response.json()
|
|
76
106
|
console.log('Raw API result:', result)
|
|
77
107
|
|
|
78
|
-
// Handle both nested and direct responses
|
|
79
108
|
const timers = result.data || result
|
|
80
109
|
console.log('Timers after processing:', timers)
|
|
81
110
|
|
|
@@ -151,7 +180,6 @@ export function TimersPage() {
|
|
|
151
180
|
return Math.round((timer.success_count / timer.execution_count) * 100)
|
|
152
181
|
}
|
|
153
182
|
|
|
154
|
-
// Sorting logic
|
|
155
183
|
const sortedTimers = [...filteredTimers].sort((a, b) => {
|
|
156
184
|
const aValue = a[sortKey as keyof Timer]
|
|
157
185
|
const bValue = b[sortKey as keyof Timer]
|
|
@@ -191,7 +219,6 @@ export function TimersPage() {
|
|
|
191
219
|
navigate(`/spine-framework/admin/configs/timers/${timer.id}`)
|
|
192
220
|
}
|
|
193
221
|
|
|
194
|
-
// Stats cards
|
|
195
222
|
const statsCards = [
|
|
196
223
|
{
|
|
197
224
|
title: 'Total Timers',
|
|
@@ -374,3 +401,4 @@ export function TimersPage() {
|
|
|
374
401
|
</AdminListPage>
|
|
375
402
|
)
|
|
376
403
|
}
|
|
404
|
+
// CIL[END chunk_id="timerspage_timerspage_1783664706"]
|