@jiggai/kitchen 0.3.1 → 0.3.3
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/.next/BUILD_ID +1 -1
- package/.next/build-manifest.json +2 -2
- package/.next/server/app/_global-error.html +2 -2
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +1 -1
- package/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/channels.html +2 -2
- package/.next/server/app/channels.rsc +1 -1
- package/.next/server/app/channels.segments/_full.segment.rsc +1 -1
- package/.next/server/app/channels.segments/_head.segment.rsc +1 -1
- package/.next/server/app/channels.segments/_index.segment.rsc +1 -1
- package/.next/server/app/channels.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/channels.segments/channels/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/channels.segments/channels.segment.rsc +1 -1
- package/.next/server/app/cron-jobs.html +1 -1
- package/.next/server/app/cron-jobs.rsc +1 -1
- package/.next/server/app/cron-jobs.segments/_full.segment.rsc +1 -1
- package/.next/server/app/cron-jobs.segments/_head.segment.rsc +1 -1
- package/.next/server/app/cron-jobs.segments/_index.segment.rsc +1 -1
- package/.next/server/app/cron-jobs.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/cron-jobs.segments/cron-jobs/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/cron-jobs.segments/cron-jobs.segment.rsc +1 -1
- package/.next/server/app/goals/new.html +2 -2
- package/.next/server/app/goals/new.rsc +1 -1
- package/.next/server/app/goals/new.segments/_full.segment.rsc +1 -1
- package/.next/server/app/goals/new.segments/_head.segment.rsc +1 -1
- package/.next/server/app/goals/new.segments/_index.segment.rsc +1 -1
- package/.next/server/app/goals/new.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/goals/new.segments/goals/new/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/goals/new.segments/goals/new.segment.rsc +1 -1
- package/.next/server/app/goals/new.segments/goals.segment.rsc +1 -1
- package/.next/server/app/goals.html +1 -1
- package/.next/server/app/goals.rsc +1 -1
- package/.next/server/app/goals.segments/_full.segment.rsc +1 -1
- package/.next/server/app/goals.segments/_head.segment.rsc +1 -1
- package/.next/server/app/goals.segments/_index.segment.rsc +1 -1
- package/.next/server/app/goals.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/goals.segments/goals/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/goals.segments/goals.segment.rsc +1 -1
- package/.next/server/app/settings.html +1 -1
- package/.next/server/app/settings.rsc +1 -1
- package/.next/server/app/settings.segments/_full.segment.rsc +1 -1
- package/.next/server/app/settings.segments/_head.segment.rsc +1 -1
- package/.next/server/app/settings.segments/_index.segment.rsc +1 -1
- package/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/settings.segments/settings.segment.rsc +1 -1
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +2 -2
- package/package.json +1 -2
- package/src/app/HomeClient.tsx +0 -207
- package/src/app/agents/[agentId]/agent-editor-tabs.tsx +0 -298
- package/src/app/agents/[agentId]/agent-editor.tsx +0 -468
- package/src/app/agents/[agentId]/page.tsx +0 -32
- package/src/app/api/__tests__/agents-add-route.test.ts +0 -143
- package/src/app/api/__tests__/agents-file-route.test.ts +0 -117
- package/src/app/api/__tests__/agents-files-route.test.ts +0 -61
- package/src/app/api/__tests__/agents-id-route.test.ts +0 -104
- package/src/app/api/__tests__/agents-identity-route.test.ts +0 -92
- package/src/app/api/__tests__/agents-route.test.ts +0 -54
- package/src/app/api/__tests__/agents-skills-install-route.test.ts +0 -131
- package/src/app/api/__tests__/agents-skills-route.test.ts +0 -64
- package/src/app/api/__tests__/agents-update-route.test.ts +0 -95
- package/src/app/api/__tests__/channels-bindings-route.test.ts +0 -143
- package/src/app/api/__tests__/cron-delete-route.test.ts +0 -93
- package/src/app/api/__tests__/cron-job-route.test.ts +0 -78
- package/src/app/api/__tests__/cron-jobs-route.test.ts +0 -116
- package/src/app/api/__tests__/cron-recipe-installed-route.test.ts +0 -114
- package/src/app/api/__tests__/gateway-restart-route.test.ts +0 -36
- package/src/app/api/__tests__/goals-promote-route.test.ts +0 -200
- package/src/app/api/__tests__/goals-route.test.ts +0 -184
- package/src/app/api/__tests__/ids-check-route.test.ts +0 -188
- package/src/app/api/__tests__/marketplace-recipes-route.test.ts +0 -123
- package/src/app/api/__tests__/recipes-clone-route.test.ts +0 -221
- package/src/app/api/__tests__/recipes-delete-route.test.ts +0 -248
- package/src/app/api/__tests__/recipes-id-route.test.ts +0 -166
- package/src/app/api/__tests__/recipes-route.test.ts +0 -57
- package/src/app/api/__tests__/recipes-team-agents-route.test.ts +0 -135
- package/src/app/api/__tests__/scaffold-route.test.ts +0 -173
- package/src/app/api/__tests__/settings-cron-installation-route.test.ts +0 -82
- package/src/app/api/__tests__/skills-available-route.test.ts +0 -47
- package/src/app/api/__tests__/swarms-start-route.test.ts +0 -79
- package/src/app/api/__tests__/swarms-status-route.test.ts +0 -42
- package/src/app/api/__tests__/teams-file-route.test.ts +0 -129
- package/src/app/api/__tests__/teams-files-route.test.ts +0 -57
- package/src/app/api/__tests__/teams-meta-route.test.ts +0 -113
- package/src/app/api/__tests__/teams-orchestrator-install-route.test.ts +0 -66
- package/src/app/api/__tests__/teams-orchestrator-route.test.ts +0 -59
- package/src/app/api/__tests__/teams-remove-team-route.test.ts +0 -122
- package/src/app/api/__tests__/teams-skills-install-route.test.ts +0 -78
- package/src/app/api/__tests__/teams-skills-route.test.ts +0 -73
- package/src/app/api/__tests__/teams-workflow-runs-route.test.ts +0 -85
- package/src/app/api/__tests__/teams-workflows-route.test.ts +0 -110
- package/src/app/api/__tests__/tickets-move-route.test.ts +0 -60
- package/src/app/api/agents/[id]/route.ts +0 -83
- package/src/app/api/agents/add/route.ts +0 -114
- package/src/app/api/agents/file/route.ts +0 -45
- package/src/app/api/agents/files/route.ts +0 -23
- package/src/app/api/agents/identity/route.ts +0 -41
- package/src/app/api/agents/route.ts +0 -22
- package/src/app/api/agents/skills/install/route.ts +0 -34
- package/src/app/api/agents/skills/route.ts +0 -39
- package/src/app/api/agents/update/route.ts +0 -52
- package/src/app/api/channels/bindings/route.ts +0 -63
- package/src/app/api/cron/__tests__/helpers.test.ts +0 -164
- package/src/app/api/cron/delete/route.ts +0 -23
- package/src/app/api/cron/helpers.ts +0 -172
- package/src/app/api/cron/job/route.ts +0 -22
- package/src/app/api/cron/jobs/route.ts +0 -52
- package/src/app/api/cron/recipe-installed/route.ts +0 -65
- package/src/app/api/gateway/restart/route.ts +0 -20
- package/src/app/api/goals/[id]/promote/route.ts +0 -119
- package/src/app/api/goals/[id]/route.ts +0 -54
- package/src/app/api/goals/route.ts +0 -44
- package/src/app/api/ids/check/route.ts +0 -113
- package/src/app/api/marketplace/recipes/[slug]/route.ts +0 -16
- package/src/app/api/marketplace/recipes/route.ts +0 -22
- package/src/app/api/recipes/[id]/route.ts +0 -62
- package/src/app/api/recipes/clone/route.ts +0 -106
- package/src/app/api/recipes/custom-team/route.ts +0 -193
- package/src/app/api/recipes/delete/helpers.ts +0 -65
- package/src/app/api/recipes/delete/route.ts +0 -73
- package/src/app/api/recipes/route.ts +0 -21
- package/src/app/api/recipes/team-agents/__tests__/helpers.test.ts +0 -156
- package/src/app/api/recipes/team-agents/helpers.ts +0 -151
- package/src/app/api/recipes/team-agents/route.ts +0 -80
- package/src/app/api/scaffold/__tests__/helpers.test.ts +0 -186
- package/src/app/api/scaffold/helpers.ts +0 -214
- package/src/app/api/scaffold/route.ts +0 -95
- package/src/app/api/settings/cron-installation/route.ts +0 -58
- package/src/app/api/skills/available/route.ts +0 -23
- package/src/app/api/swarms/start/route.ts +0 -100
- package/src/app/api/swarms/status/route.ts +0 -31
- package/src/app/api/teams/[teamId]/tickets/assign/route.ts +0 -105
- package/src/app/api/teams/[teamId]/tickets/assignees/route.ts +0 -27
- package/src/app/api/teams/[teamId]/tickets/delete/route.ts +0 -55
- package/src/app/api/teams/[teamId]/tickets/move/route.ts +0 -70
- package/src/app/api/teams/[teamId]/tickets/move-to-goals/route.ts +0 -56
- package/src/app/api/teams/file/route.ts +0 -46
- package/src/app/api/teams/files/route.ts +0 -63
- package/src/app/api/teams/memory/route.ts +0 -250
- package/src/app/api/teams/meta/route.ts +0 -43
- package/src/app/api/teams/orchestrator/install/route.ts +0 -129
- package/src/app/api/teams/orchestrator/route.ts +0 -216
- package/src/app/api/teams/remove-team/route.ts +0 -37
- package/src/app/api/teams/skills/install/route.ts +0 -18
- package/src/app/api/teams/skills/route.ts +0 -25
- package/src/app/api/teams/workflow-runs/route.ts +0 -534
- package/src/app/api/teams/workflow-templates/route.ts +0 -71
- package/src/app/api/teams/workflows/route.ts +0 -55
- package/src/app/api/tickets/assign/route.ts +0 -94
- package/src/app/api/tickets/assignees/route.ts +0 -24
- package/src/app/api/tickets/move/route.ts +0 -69
- package/src/app/channels/channels-client.tsx +0 -271
- package/src/app/channels/page.tsx +0 -5
- package/src/app/cron-jobs/cron-jobs-client.tsx +0 -243
- package/src/app/cron-jobs/page.tsx +0 -34
- package/src/app/favicon.ico +0 -0
- package/src/app/global-error.tsx +0 -50
- package/src/app/globals.css +0 -153
- package/src/app/goals/[id]/goal-editor.tsx +0 -162
- package/src/app/goals/[id]/page.tsx +0 -6
- package/src/app/goals/goals-client.tsx +0 -201
- package/src/app/goals/new/page.tsx +0 -81
- package/src/app/goals/page.tsx +0 -10
- package/src/app/layout.tsx +0 -53
- package/src/app/manifest.ts +0 -15
- package/src/app/not-found.tsx +0 -8
- package/src/app/page.tsx +0 -33
- package/src/app/recipes/CreateAgentModal.tsx +0 -156
- package/src/app/recipes/CreateCustomTeamModal.tsx +0 -375
- package/src/app/recipes/CreateModalShell.tsx +0 -55
- package/src/app/recipes/CreateTeamModal.tsx +0 -91
- package/src/app/recipes/[id]/RecipeEditor/RecipeEditorCreateModal.tsx +0 -72
- package/src/app/recipes/[id]/RecipeEditor/RecipeEditorPanel.tsx +0 -216
- package/src/app/recipes/[id]/RecipeEditor/index.tsx +0 -271
- package/src/app/recipes/[id]/RecipeEditor/recipe-editor-utils.ts +0 -46
- package/src/app/recipes/[id]/RecipeEditor/types.ts +0 -52
- package/src/app/recipes/[id]/page.tsx +0 -37
- package/src/app/recipes/page.tsx +0 -101
- package/src/app/recipes/recipes-client.tsx +0 -620
- package/src/app/settings/page.tsx +0 -26
- package/src/app/settings/settings-client.tsx +0 -91
- package/src/app/teams/[teamId]/CloneTeamModal.tsx +0 -116
- package/src/app/teams/[teamId]/OrchestratorPanel.tsx +0 -255
- package/src/app/teams/[teamId]/OrchestratorSetupModal.tsx +0 -184
- package/src/app/teams/[teamId]/PublishChangesModal.tsx +0 -43
- package/src/app/teams/[teamId]/page.tsx +0 -49
- package/src/app/teams/[teamId]/team-editor/TeamAgentsTab.tsx +0 -145
- package/src/app/teams/[teamId]/team-editor/TeamCronTab.tsx +0 -72
- package/src/app/teams/[teamId]/team-editor/TeamFilesTab.tsx +0 -74
- package/src/app/teams/[teamId]/team-editor/TeamMemoryTab.tsx +0 -349
- package/src/app/teams/[teamId]/team-editor/TeamRecipeTab.tsx +0 -151
- package/src/app/teams/[teamId]/team-editor/TeamSkillsTab.tsx +0 -68
- package/src/app/teams/[teamId]/team-editor/index.tsx +0 -558
- package/src/app/teams/[teamId]/team-editor/team-editor-data.ts +0 -255
- package/src/app/teams/[teamId]/team-editor/team-editor-utils.ts +0 -78
- package/src/app/teams/[teamId]/team-editor/types.ts +0 -34
- package/src/app/teams/[teamId]/tickets/[ticket]/page.tsx +0 -35
- package/src/app/teams/[teamId]/tickets/page.tsx +0 -15
- package/src/app/teams/[teamId]/workflows/[workflowId]/WorkflowCanvas.tsx +0 -111
- package/src/app/teams/[teamId]/workflows/[workflowId]/page.tsx +0 -27
- package/src/app/teams/[teamId]/workflows/[workflowId]/workflows-editor-client.tsx +0 -1608
- package/src/app/teams/[teamId]/workflows/page.tsx +0 -40
- package/src/app/teams/[teamId]/workflows/workflows-client.tsx +0 -494
- package/src/app/tickets/TicketDetailClient.tsx +0 -147
- package/src/app/tickets/TicketsBoardClient.tsx +0 -200
- package/src/app/tickets/[ticket]/TicketAssignControl.tsx +0 -112
- package/src/app/tickets/[ticket]/page.tsx +0 -36
- package/src/app/tickets/page.tsx +0 -10
- package/src/components/AppShell.tsx +0 -286
- package/src/components/ConfirmationModal.tsx +0 -81
- package/src/components/DeleteEntityModal.tsx +0 -41
- package/src/components/ErrorBoundary.tsx +0 -70
- package/src/components/FileListWithOptionalToggle.tsx +0 -86
- package/src/components/GoalFormFields.tsx +0 -163
- package/src/components/ScaffoldOverlay.tsx +0 -78
- package/src/components/ThemeToggle.tsx +0 -53
- package/src/components/ToastProvider.tsx +0 -163
- package/src/components/__tests__/ConfirmationModal.test.tsx +0 -109
- package/src/components/__tests__/ErrorBoundary.test.tsx +0 -39
- package/src/components/__tests__/FileListWithOptionalToggle.test.tsx +0 -109
- package/src/components/__tests__/GoalFormFields.test.tsx +0 -117
- package/src/components/delete-modals.tsx +0 -59
- package/src/components/icons.tsx +0 -48
- package/src/lib/__tests__/agent-workspace.test.ts +0 -44
- package/src/lib/__tests__/agents.test.ts +0 -36
- package/src/lib/__tests__/api-route-helpers.test.ts +0 -188
- package/src/lib/__tests__/cron.test.ts +0 -45
- package/src/lib/__tests__/editor-utils.test.ts +0 -38
- package/src/lib/__tests__/errors.test.ts +0 -15
- package/src/lib/__tests__/exec.test.ts +0 -13
- package/src/lib/__tests__/fetch-json.test.ts +0 -118
- package/src/lib/__tests__/gateway.test.ts +0 -234
- package/src/lib/__tests__/goal-promote.test.ts +0 -39
- package/src/lib/__tests__/goals-client.test.ts +0 -26
- package/src/lib/__tests__/goals.test.ts +0 -275
- package/src/lib/__tests__/json.test.ts +0 -15
- package/src/lib/__tests__/kitchen-api.test.ts +0 -32
- package/src/lib/__tests__/marketplace.test.ts +0 -116
- package/src/lib/__tests__/openclaw.test.ts +0 -129
- package/src/lib/__tests__/paths.test.ts +0 -136
- package/src/lib/__tests__/poll.test.ts +0 -26
- package/src/lib/__tests__/recipe-clone.test.ts +0 -85
- package/src/lib/__tests__/recipe-team-agents.test.ts +0 -70
- package/src/lib/__tests__/recipes.test.ts +0 -199
- package/src/lib/__tests__/scaffold-client.test.ts +0 -106
- package/src/lib/__tests__/scaffold.test.ts +0 -64
- package/src/lib/__tests__/slugify.test.ts +0 -23
- package/src/lib/__tests__/tickets.test.ts +0 -158
- package/src/lib/__tests__/type-guards.test.ts +0 -18
- package/src/lib/__tests__/use-slugified-id.test.tsx +0 -120
- package/src/lib/agent-workspace.ts +0 -14
- package/src/lib/agents.ts +0 -17
- package/src/lib/api-route-helpers.ts +0 -157
- package/src/lib/cron.ts +0 -40
- package/src/lib/editor-utils.ts +0 -18
- package/src/lib/errors.ts +0 -7
- package/src/lib/exec.ts +0 -4
- package/src/lib/fetch-json.ts +0 -29
- package/src/lib/gateway.ts +0 -100
- package/src/lib/goal-promote.ts +0 -27
- package/src/lib/goals-client.ts +0 -69
- package/src/lib/goals.ts +0 -171
- package/src/lib/json.ts +0 -10
- package/src/lib/kitchen-api.ts +0 -19
- package/src/lib/marketplace.ts +0 -46
- package/src/lib/openclaw.ts +0 -59
- package/src/lib/paths.ts +0 -69
- package/src/lib/poll.ts +0 -18
- package/src/lib/recipe-clone.ts +0 -42
- package/src/lib/recipe-team-agents.ts +0 -30
- package/src/lib/recipes.ts +0 -95
- package/src/lib/scaffold-client.ts +0 -31
- package/src/lib/scaffold.ts +0 -37
- package/src/lib/slugify.ts +0 -25
- package/src/lib/swarms.ts +0 -25
- package/src/lib/tickets.ts +0 -192
- package/src/lib/type-guards.ts +0 -3
- package/src/lib/use-slugified-id.ts +0 -35
- package/src/lib/workflows/README.md +0 -11
- package/src/lib/workflows/__tests__/storage.test.ts +0 -129
- package/src/lib/workflows/__tests__/validate.test.ts +0 -92
- package/src/lib/workflows/api-handlers.ts +0 -35
- package/src/lib/workflows/readdir.ts +0 -23
- package/src/lib/workflows/runs-storage.ts +0 -59
- package/src/lib/workflows/runs-types.ts +0 -42
- package/src/lib/workflows/storage.ts +0 -70
- package/src/lib/workflows/templates/index.ts +0 -1
- package/src/lib/workflows/templates/marketing-cadence-v1.ts +0 -142
- package/src/lib/workflows/types.ts +0 -48
- package/src/lib/workflows/validate.ts +0 -92
- package/src/proxy.ts +0 -28
- /package/.next/static/{z86RoqzzXXrWnpi229zP6 → Jrrrm9HH5bKkSrQhe1j93}/_buildManifest.js +0 -0
- /package/.next/static/{z86RoqzzXXrWnpi229zP6 → Jrrrm9HH5bKkSrQhe1j93}/_clientMiddlewareManifest.json +0 -0
- /package/.next/static/{z86RoqzzXXrWnpi229zP6 → Jrrrm9HH5bKkSrQhe1j93}/_ssgManifest.js +0 -0
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import type { ReactNode } from "react";
|
|
4
|
-
import { useId } from "react";
|
|
5
|
-
import type { GoalFormState, GoalStatus } from "@/lib/goals-client";
|
|
6
|
-
|
|
7
|
-
export type { GoalFormState };
|
|
8
|
-
|
|
9
|
-
/** Wraps goal form content with error display and action buttons. */
|
|
10
|
-
export function GoalFormCard({
|
|
11
|
-
children,
|
|
12
|
-
error,
|
|
13
|
-
actions,
|
|
14
|
-
}: {
|
|
15
|
-
children: ReactNode;
|
|
16
|
-
error: string | null;
|
|
17
|
-
actions: ReactNode;
|
|
18
|
-
}) {
|
|
19
|
-
return (
|
|
20
|
-
<div className="ck-glass p-6 space-y-4">
|
|
21
|
-
{children}
|
|
22
|
-
{error ? <div className="text-sm text-red-300">{error}</div> : null}
|
|
23
|
-
{actions}
|
|
24
|
-
</div>
|
|
25
|
-
);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const inputClass =
|
|
29
|
-
"mt-1 w-full rounded-[var(--ck-radius-sm)] border border-[color:var(--ck-border-subtle)] bg-transparent px-3 py-2 text-sm";
|
|
30
|
-
|
|
31
|
-
type Props = {
|
|
32
|
-
formState: GoalFormState;
|
|
33
|
-
/** When provided, renders the ID field (for create flow). */
|
|
34
|
-
idField?: { id: string; setId: (v: string) => void; suggestedId?: string };
|
|
35
|
-
/** When provided, shows updated timestamp next to body label. */
|
|
36
|
-
updatedAt?: string | null;
|
|
37
|
-
/** Body textarea height override (e.g. 260 for new page). */
|
|
38
|
-
bodyHeight?: string;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
export function GoalFormFields({
|
|
42
|
-
formState,
|
|
43
|
-
idField,
|
|
44
|
-
updatedAt,
|
|
45
|
-
bodyHeight = "h-[320px]",
|
|
46
|
-
}: Props) {
|
|
47
|
-
const { title, setTitle, status, setStatus, tagsRaw, setTagsRaw, teamsRaw, setTeamsRaw, body, setBody } = formState;
|
|
48
|
-
const baseId = useId();
|
|
49
|
-
const idInputId = `${baseId}-id`;
|
|
50
|
-
const titleInputId = `${baseId}-title`;
|
|
51
|
-
const statusSelectId = `${baseId}-status`;
|
|
52
|
-
const teamsInputId = `${baseId}-teams`;
|
|
53
|
-
const tagsInputId = `${baseId}-tags`;
|
|
54
|
-
const bodyTextareaId = `${baseId}-body`;
|
|
55
|
-
return (
|
|
56
|
-
<>
|
|
57
|
-
{idField ? (
|
|
58
|
-
<div>
|
|
59
|
-
<label htmlFor={idInputId} className="text-xs text-[color:var(--ck-text-tertiary)]">
|
|
60
|
-
ID
|
|
61
|
-
</label>
|
|
62
|
-
<input
|
|
63
|
-
id={idInputId}
|
|
64
|
-
className={`${inputClass} font-mono`}
|
|
65
|
-
value={idField.id}
|
|
66
|
-
onChange={(e) => idField.setId(e.target.value)}
|
|
67
|
-
placeholder="increase-trial-activation"
|
|
68
|
-
aria-describedby={`${baseId}-id-hint`}
|
|
69
|
-
/>
|
|
70
|
-
<div id={`${baseId}-id-hint`} className="mt-1 text-xs text-[color:var(--ck-text-tertiary)]">
|
|
71
|
-
Lowercase letters, numbers, hyphens. Stored as <code className="font-mono">{idField.id || "<id>"}.md</code>
|
|
72
|
-
.
|
|
73
|
-
{idField.suggestedId && !idField.id.trim() ? (
|
|
74
|
-
<>
|
|
75
|
-
{" "}
|
|
76
|
-
Suggested:{" "}
|
|
77
|
-
<button type="button" className="underline" onClick={() => idField.setId(idField.suggestedId ?? "")}>
|
|
78
|
-
{idField.suggestedId}
|
|
79
|
-
</button>
|
|
80
|
-
</>
|
|
81
|
-
) : null}
|
|
82
|
-
</div>
|
|
83
|
-
</div>
|
|
84
|
-
) : null}
|
|
85
|
-
|
|
86
|
-
<div>
|
|
87
|
-
<label htmlFor={titleInputId} className="text-xs text-[color:var(--ck-text-tertiary)]">
|
|
88
|
-
Title
|
|
89
|
-
</label>
|
|
90
|
-
<input
|
|
91
|
-
id={titleInputId}
|
|
92
|
-
className={inputClass}
|
|
93
|
-
value={title}
|
|
94
|
-
onChange={(e) => setTitle(e.target.value)}
|
|
95
|
-
placeholder={idField ? "Increase trial activation" : "Goal title"}
|
|
96
|
-
/>
|
|
97
|
-
</div>
|
|
98
|
-
|
|
99
|
-
<div className="grid grid-cols-1 gap-4 sm:grid-cols-3">
|
|
100
|
-
<div>
|
|
101
|
-
<label htmlFor={statusSelectId} className="text-xs text-[color:var(--ck-text-tertiary)]">
|
|
102
|
-
Status
|
|
103
|
-
</label>
|
|
104
|
-
<select
|
|
105
|
-
id={statusSelectId}
|
|
106
|
-
className={inputClass}
|
|
107
|
-
value={status}
|
|
108
|
-
onChange={(e) => setStatus(e.target.value as GoalStatus)}
|
|
109
|
-
aria-label="Goal status"
|
|
110
|
-
>
|
|
111
|
-
<option value="planned">Planned</option>
|
|
112
|
-
<option value="active">Active</option>
|
|
113
|
-
<option value="done">Done</option>
|
|
114
|
-
</select>
|
|
115
|
-
</div>
|
|
116
|
-
<div>
|
|
117
|
-
<label htmlFor={teamsInputId} className="text-xs text-[color:var(--ck-text-tertiary)]">
|
|
118
|
-
Teams (comma-separated)
|
|
119
|
-
</label>
|
|
120
|
-
<input
|
|
121
|
-
id={teamsInputId}
|
|
122
|
-
className={inputClass}
|
|
123
|
-
value={teamsRaw}
|
|
124
|
-
onChange={(e) => setTeamsRaw(e.target.value)}
|
|
125
|
-
placeholder="development-team, marketing-team"
|
|
126
|
-
/>
|
|
127
|
-
</div>
|
|
128
|
-
<div>
|
|
129
|
-
<label htmlFor={tagsInputId} className="text-xs text-[color:var(--ck-text-tertiary)]">
|
|
130
|
-
Tags (comma-separated)
|
|
131
|
-
</label>
|
|
132
|
-
<input
|
|
133
|
-
id={tagsInputId}
|
|
134
|
-
className={inputClass}
|
|
135
|
-
value={tagsRaw}
|
|
136
|
-
onChange={(e) => setTagsRaw(e.target.value)}
|
|
137
|
-
placeholder="onboarding, growth"
|
|
138
|
-
/>
|
|
139
|
-
</div>
|
|
140
|
-
</div>
|
|
141
|
-
|
|
142
|
-
<div>
|
|
143
|
-
<div className="flex items-center justify-between">
|
|
144
|
-
<label htmlFor={bodyTextareaId} className="text-xs text-[color:var(--ck-text-tertiary)]">
|
|
145
|
-
Body (markdown)
|
|
146
|
-
</label>
|
|
147
|
-
{updatedAt != null ? (
|
|
148
|
-
<div className="text-xs text-[color:var(--ck-text-tertiary)]">
|
|
149
|
-
{updatedAt ? `updated ${new Date(updatedAt).toLocaleString()}` : ""}
|
|
150
|
-
</div>
|
|
151
|
-
) : null}
|
|
152
|
-
</div>
|
|
153
|
-
<textarea
|
|
154
|
-
id={bodyTextareaId}
|
|
155
|
-
className={`mt-1 ${bodyHeight} w-full rounded-[var(--ck-radius-sm)] border border-[color:var(--ck-border-subtle)] bg-transparent px-3 py-2 font-mono text-sm`}
|
|
156
|
-
value={body}
|
|
157
|
-
onChange={(e) => setBody(e.target.value)}
|
|
158
|
-
placeholder="Write the goal here…"
|
|
159
|
-
/>
|
|
160
|
-
</div>
|
|
161
|
-
</>
|
|
162
|
-
);
|
|
163
|
-
}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { createPortal } from "react-dom";
|
|
4
|
-
|
|
5
|
-
export type ScaffoldOverlayStep = 1 | 2 | 3;
|
|
6
|
-
|
|
7
|
-
const stepLabel: Record<ScaffoldOverlayStep, string> = {
|
|
8
|
-
1: "Ordering team…",
|
|
9
|
-
2: "Cooking up agents…",
|
|
10
|
-
3: "Serving them up hot…",
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
function getStepCircleClass(done: boolean, active: boolean): string {
|
|
14
|
-
if (done) return "bg-emerald-400";
|
|
15
|
-
if (active) return "bg-[var(--ck-accent-red)] animate-pulse";
|
|
16
|
-
return "bg-white/20";
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function ScaffoldOverlay({
|
|
20
|
-
open,
|
|
21
|
-
step,
|
|
22
|
-
onDismiss,
|
|
23
|
-
}: {
|
|
24
|
-
open: boolean;
|
|
25
|
-
step: ScaffoldOverlayStep;
|
|
26
|
-
onDismiss?: () => void;
|
|
27
|
-
}) {
|
|
28
|
-
if (!open) return null;
|
|
29
|
-
|
|
30
|
-
return createPortal(
|
|
31
|
-
<div className="fixed inset-0 z-[9999]">
|
|
32
|
-
{/* Slightly transparent full-screen overlay so we hide the app while still showing a hint of context. */}
|
|
33
|
-
<div className="fixed inset-0 bg-white/90 dark:bg-black/90 pointer-events-none" />
|
|
34
|
-
|
|
35
|
-
{onDismiss ? (
|
|
36
|
-
<button
|
|
37
|
-
type="button"
|
|
38
|
-
onClick={(e) => {
|
|
39
|
-
e.preventDefault();
|
|
40
|
-
e.stopPropagation();
|
|
41
|
-
onDismiss();
|
|
42
|
-
}}
|
|
43
|
-
className="fixed right-4 top-4 z-[10000] rounded-full border border-white/10 bg-[color:var(--ck-bg-glass)] px-3 py-2 text-sm font-medium text-[color:var(--ck-text-primary)] shadow-[var(--ck-shadow-1)] hover:bg-[color:var(--ck-bg-glass-strong)]"
|
|
44
|
-
aria-label="Dismiss loading overlay"
|
|
45
|
-
title="Dismiss"
|
|
46
|
-
>
|
|
47
|
-
Esc
|
|
48
|
-
</button>
|
|
49
|
-
) : null}
|
|
50
|
-
|
|
51
|
-
<div className="fixed inset-0 flex items-center justify-center p-6 sm:p-10">
|
|
52
|
-
<div className="w-full max-w-2xl rounded-2xl border border-white/10 bg-[color:var(--ck-bg-glass-strong)] p-8 sm:p-10 shadow-[var(--ck-shadow-2)]">
|
|
53
|
-
<div className="text-2xl font-semibold text-[color:var(--ck-text-primary)]">Claw Kitchen</div>
|
|
54
|
-
<div className="mt-3 text-base text-[color:var(--ck-text-secondary)]">Hang tight — we’re updating your OpenClaw install.</div>
|
|
55
|
-
|
|
56
|
-
<div className="mt-8 space-y-4 text-base">
|
|
57
|
-
{[1, 2, 3].map((n) => {
|
|
58
|
-
const s = n as ScaffoldOverlayStep;
|
|
59
|
-
const active = s === step;
|
|
60
|
-
const done = s < step;
|
|
61
|
-
return (
|
|
62
|
-
<div key={s} className="flex items-center gap-4">
|
|
63
|
-
<div className={"h-4 w-4 rounded-full " + getStepCircleClass(done, active)} />
|
|
64
|
-
<div className={done ? "text-[color:var(--ck-text-secondary)] line-through" : "text-[color:var(--ck-text-primary)]"}>
|
|
65
|
-
{stepLabel[s]}
|
|
66
|
-
</div>
|
|
67
|
-
</div>
|
|
68
|
-
);
|
|
69
|
-
})}
|
|
70
|
-
</div>
|
|
71
|
-
|
|
72
|
-
{/* Details removed — avoid noisy/scrolling stderr in the primary UX. */}
|
|
73
|
-
</div>
|
|
74
|
-
</div>
|
|
75
|
-
</div>,
|
|
76
|
-
document.body,
|
|
77
|
-
);
|
|
78
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { useEffect, useState } from "react";
|
|
4
|
-
import { MoonIcon, SunIcon } from "@/components/icons";
|
|
5
|
-
|
|
6
|
-
export type Theme = "light" | "dark";
|
|
7
|
-
|
|
8
|
-
const STORAGE_KEY = "ck-theme";
|
|
9
|
-
|
|
10
|
-
function readInitialTheme(): Theme {
|
|
11
|
-
// Default: dark mode.
|
|
12
|
-
if (typeof window === "undefined") return "dark";
|
|
13
|
-
const saved = window.localStorage.getItem(STORAGE_KEY);
|
|
14
|
-
return saved === "light" ? "light" : "dark";
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function applyTheme(theme: Theme) {
|
|
18
|
-
document.documentElement.dataset.theme = theme;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function ThemeToggle() {
|
|
22
|
-
const [theme, setTheme] = useState<Theme>(readInitialTheme);
|
|
23
|
-
|
|
24
|
-
useEffect(() => {
|
|
25
|
-
applyTheme(theme);
|
|
26
|
-
window.localStorage.setItem(STORAGE_KEY, theme);
|
|
27
|
-
}, [theme]);
|
|
28
|
-
|
|
29
|
-
function toggle() {
|
|
30
|
-
setTheme((t) => (t === "dark" ? "light" : "dark"));
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const label = theme === "dark" ? "Switch to light mode" : "Switch to dark mode";
|
|
34
|
-
|
|
35
|
-
return (
|
|
36
|
-
<button
|
|
37
|
-
type="button"
|
|
38
|
-
onClick={toggle}
|
|
39
|
-
className="grid h-9 w-9 place-items-center rounded-full border border-[color:var(--ck-border-subtle)] bg-[color:var(--ck-bg-glass)] text-[color:var(--ck-text-primary)] shadow-[var(--ck-shadow-1)] transition-colors hover:bg-[color:var(--ck-bg-glass-strong)]"
|
|
40
|
-
aria-label={label}
|
|
41
|
-
title={label}
|
|
42
|
-
>
|
|
43
|
-
<span className="sr-only" suppressHydrationWarning>
|
|
44
|
-
{label}
|
|
45
|
-
</span>
|
|
46
|
-
{theme === "dark" ? (
|
|
47
|
-
<MoonIcon className="h-4.5 w-4.5" />
|
|
48
|
-
) : (
|
|
49
|
-
<SunIcon className="h-4.5 w-4.5" />
|
|
50
|
-
)}
|
|
51
|
-
</button>
|
|
52
|
-
);
|
|
53
|
-
}
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { createContext, useCallback, useContext, useMemo, useState } from "react";
|
|
4
|
-
|
|
5
|
-
export type ToastKind = "success" | "error" | "info";
|
|
6
|
-
|
|
7
|
-
type ToastInternal = Toast & { state: "enter" | "show" | "leave" };
|
|
8
|
-
export type Toast = {
|
|
9
|
-
id: string;
|
|
10
|
-
kind: ToastKind;
|
|
11
|
-
title?: string;
|
|
12
|
-
message: string;
|
|
13
|
-
timeoutMs?: number;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
type ToastContextValue = {
|
|
17
|
-
push: (t: Omit<Toast, "id">) => void;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const ToastContext = createContext<ToastContextValue | null>(null);
|
|
21
|
-
|
|
22
|
-
function randomHex(bytes: number): string {
|
|
23
|
-
const arr = new Uint8Array(bytes);
|
|
24
|
-
crypto.getRandomValues(arr);
|
|
25
|
-
return Array.from(arr, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function getToastMotionClass(state: "enter" | "show" | "leave"): string {
|
|
29
|
-
if (state === "enter") return "translate-x-[-16px] opacity-0";
|
|
30
|
-
if (state === "leave") return "translate-x-[-16px] opacity-0";
|
|
31
|
-
return "translate-x-0 opacity-100";
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function ToastIcon({ kind }: { kind: ToastKind }) {
|
|
35
|
-
if (kind === "success") {
|
|
36
|
-
return (
|
|
37
|
-
<svg viewBox="0 0 20 20" fill="currentColor" className="h-5 w-5 text-emerald-400">
|
|
38
|
-
<path
|
|
39
|
-
fillRule="evenodd"
|
|
40
|
-
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.707a1 1 0 00-1.414-1.414L9 10.172 7.707 8.879a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
|
|
41
|
-
clipRule="evenodd"
|
|
42
|
-
/>
|
|
43
|
-
</svg>
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
if (kind === "error") {
|
|
47
|
-
return (
|
|
48
|
-
<svg viewBox="0 0 20 20" fill="currentColor" className="h-5 w-5 text-red-400">
|
|
49
|
-
<path
|
|
50
|
-
fillRule="evenodd"
|
|
51
|
-
d="M10 18a8 8 0 100-16 8 8 0 000 16zm-1-5a1 1 0 112 0 1 1 0 01-2 0zm0-8a1 1 0 012 0v6a1 1 0 11-2 0V5z"
|
|
52
|
-
clipRule="evenodd"
|
|
53
|
-
/>
|
|
54
|
-
</svg>
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
return (
|
|
58
|
-
<svg viewBox="0 0 20 20" fill="currentColor" className="h-5 w-5 text-slate-300">
|
|
59
|
-
<path
|
|
60
|
-
fillRule="evenodd"
|
|
61
|
-
d="M18 10A8 8 0 11 2 10a8 8 0 0116 0zM9 9a1 1 0 112 0v5a1 1 0 11-2 0V9zm0-3a1 1 0 112 0 1 1 0 01-2 0z"
|
|
62
|
-
clipRule="evenodd"
|
|
63
|
-
/>
|
|
64
|
-
</svg>
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export function ToastProvider({ children }: { children: React.ReactNode }) {
|
|
69
|
-
const [toasts, setToasts] = useState<ToastInternal[]>([]);
|
|
70
|
-
|
|
71
|
-
const removeNow = useCallback((id: string) => {
|
|
72
|
-
setToasts((prev) => prev.filter((t) => t.id !== id));
|
|
73
|
-
}, []);
|
|
74
|
-
|
|
75
|
-
const dismiss = useCallback(
|
|
76
|
-
(id: string) => {
|
|
77
|
-
// Start leave animation, then remove.
|
|
78
|
-
setToasts((prev) => prev.map((t) => (t.id === id ? { ...t, state: "leave" } : t)));
|
|
79
|
-
window.setTimeout(() => removeNow(id), 250);
|
|
80
|
-
},
|
|
81
|
-
[removeNow],
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
const promoteToShow = useCallback((id: string) => {
|
|
85
|
-
setToasts((prev) => prev.map((x) => (x.id === id ? { ...x, state: "show" } : x)));
|
|
86
|
-
}, []);
|
|
87
|
-
|
|
88
|
-
const push = useCallback(
|
|
89
|
-
(t: Omit<Toast, "id">) => {
|
|
90
|
-
const id = `${Date.now()}-${randomHex(8)}`;
|
|
91
|
-
const toast: ToastInternal = { id, timeoutMs: 5000, state: "enter", ...t };
|
|
92
|
-
setToasts((prev) => [toast, ...prev].slice(0, 4));
|
|
93
|
-
|
|
94
|
-
window.setTimeout(() => promoteToShow(id), 10);
|
|
95
|
-
|
|
96
|
-
const ms = toast.timeoutMs;
|
|
97
|
-
if (ms && ms > 0) {
|
|
98
|
-
window.setTimeout(() => dismiss(id), ms);
|
|
99
|
-
}
|
|
100
|
-
},
|
|
101
|
-
[dismiss, promoteToShow],
|
|
102
|
-
);
|
|
103
|
-
|
|
104
|
-
const value = useMemo(() => ({ push }), [push]);
|
|
105
|
-
|
|
106
|
-
return (
|
|
107
|
-
<ToastContext.Provider value={value}>
|
|
108
|
-
{children}
|
|
109
|
-
|
|
110
|
-
{/* Tailwind UI-inspired notifications */}
|
|
111
|
-
<div
|
|
112
|
-
aria-live="assertive"
|
|
113
|
-
className="pointer-events-none fixed inset-0 z-[100] flex items-end px-4 py-6 sm:p-6"
|
|
114
|
-
>
|
|
115
|
-
<div className="flex w-full flex-col items-start space-y-4">
|
|
116
|
-
{toasts.map((t) => (
|
|
117
|
-
<div
|
|
118
|
-
key={t.id}
|
|
119
|
-
className={
|
|
120
|
-
"pointer-events-auto w-full max-w-sm overflow-hidden rounded-lg border bg-[color:var(--ck-toast-bg)] shadow-[var(--ck-shadow-2)] transition-all duration-200 ease-out border-[color:var(--ck-toast-border)] " +
|
|
121
|
-
getToastMotionClass(t.state)
|
|
122
|
-
}
|
|
123
|
-
>
|
|
124
|
-
<div className="p-4">
|
|
125
|
-
<div className="flex items-start">
|
|
126
|
-
<div className="shrink-0">
|
|
127
|
-
<ToastIcon kind={t.kind} />
|
|
128
|
-
</div>
|
|
129
|
-
<div className="ml-3 w-0 flex-1">
|
|
130
|
-
{t.title ? (
|
|
131
|
-
<p className="text-sm font-medium text-[color:var(--ck-text-primary)]">{t.title}</p>
|
|
132
|
-
) : null}
|
|
133
|
-
<p className="whitespace-pre-wrap text-sm text-[color:var(--ck-text-primary)]">{t.message}</p>
|
|
134
|
-
</div>
|
|
135
|
-
<div className="ml-4 flex shrink-0">
|
|
136
|
-
<button
|
|
137
|
-
type="button"
|
|
138
|
-
onClick={() => dismiss(t.id)}
|
|
139
|
-
className="inline-flex rounded-md bg-transparent text-[color:var(--ck-text-tertiary)] hover:text-[color:var(--ck-text-primary)]"
|
|
140
|
-
aria-label="Dismiss"
|
|
141
|
-
title="Dismiss"
|
|
142
|
-
>
|
|
143
|
-
<span className="sr-only">Close</span>
|
|
144
|
-
<svg viewBox="0 0 20 20" fill="currentColor" className="h-5 w-5">
|
|
145
|
-
<path d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 10-1.06-1.06L10 8.94 6.28 5.22z" />
|
|
146
|
-
</svg>
|
|
147
|
-
</button>
|
|
148
|
-
</div>
|
|
149
|
-
</div>
|
|
150
|
-
</div>
|
|
151
|
-
</div>
|
|
152
|
-
))}
|
|
153
|
-
</div>
|
|
154
|
-
</div>
|
|
155
|
-
</ToastContext.Provider>
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
export function useToast() {
|
|
160
|
-
const ctx = useContext(ToastContext);
|
|
161
|
-
if (!ctx) throw new Error("useToast must be used within ToastProvider");
|
|
162
|
-
return ctx;
|
|
163
|
-
}
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi, afterEach } from "vitest";
|
|
2
|
-
import { render, screen, fireEvent, cleanup, within } from "@testing-library/react";
|
|
3
|
-
import { ConfirmationModal } from "../ConfirmationModal";
|
|
4
|
-
|
|
5
|
-
afterEach(cleanup);
|
|
6
|
-
|
|
7
|
-
describe("ConfirmationModal", () => {
|
|
8
|
-
it("returns null when open is false", () => {
|
|
9
|
-
render(
|
|
10
|
-
<ConfirmationModal
|
|
11
|
-
open={false}
|
|
12
|
-
onClose={vi.fn()}
|
|
13
|
-
title="Confirm"
|
|
14
|
-
confirmLabel="Confirm"
|
|
15
|
-
onConfirm={vi.fn()}
|
|
16
|
-
>
|
|
17
|
-
<p>Body content</p>
|
|
18
|
-
</ConfirmationModal>
|
|
19
|
-
);
|
|
20
|
-
expect(screen.queryByText("Confirm")).toBeNull();
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it("renders title, body, Cancel and Confirm when open", () => {
|
|
24
|
-
render(
|
|
25
|
-
<ConfirmationModal
|
|
26
|
-
open={true}
|
|
27
|
-
onClose={vi.fn()}
|
|
28
|
-
title="Delete item?"
|
|
29
|
-
confirmLabel="Delete"
|
|
30
|
-
onConfirm={vi.fn()}
|
|
31
|
-
>
|
|
32
|
-
<p>Are you sure?</p>
|
|
33
|
-
</ConfirmationModal>
|
|
34
|
-
);
|
|
35
|
-
expect(screen.getByText("Delete item?")).toBeTruthy();
|
|
36
|
-
expect(screen.getByText("Are you sure?")).toBeTruthy();
|
|
37
|
-
expect(screen.getByRole("button", { name: "Cancel" })).toBeTruthy();
|
|
38
|
-
expect(screen.getByRole("button", { name: "Delete" })).toBeTruthy();
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it("calls onClose when Cancel clicked", () => {
|
|
42
|
-
const onClose = vi.fn();
|
|
43
|
-
render(
|
|
44
|
-
<ConfirmationModal
|
|
45
|
-
open={true}
|
|
46
|
-
onClose={onClose}
|
|
47
|
-
title="Confirm Close"
|
|
48
|
-
confirmLabel="OK"
|
|
49
|
-
onConfirm={vi.fn()}
|
|
50
|
-
>
|
|
51
|
-
<p>Body</p>
|
|
52
|
-
</ConfirmationModal>
|
|
53
|
-
);
|
|
54
|
-
const modal = screen.getByText("Confirm Close").closest(".rounded-2xl");
|
|
55
|
-
fireEvent.click(within(modal!).getByRole("button", { name: "Cancel" }));
|
|
56
|
-
expect(onClose).toHaveBeenCalled();
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it("calls onConfirm when Confirm clicked", () => {
|
|
60
|
-
const onConfirm = vi.fn();
|
|
61
|
-
render(
|
|
62
|
-
<ConfirmationModal
|
|
63
|
-
open={true}
|
|
64
|
-
onClose={vi.fn()}
|
|
65
|
-
title="Confirm Submit"
|
|
66
|
-
confirmLabel="Submit"
|
|
67
|
-
onConfirm={onConfirm}
|
|
68
|
-
>
|
|
69
|
-
<p>Body</p>
|
|
70
|
-
</ConfirmationModal>
|
|
71
|
-
);
|
|
72
|
-
const modal = screen.getByText("Confirm Submit").closest(".rounded-2xl");
|
|
73
|
-
fireEvent.click(within(modal!).getByRole("button", { name: "Submit" }));
|
|
74
|
-
expect(onConfirm).toHaveBeenCalled();
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it("shows error when provided", () => {
|
|
78
|
-
render(
|
|
79
|
-
<ConfirmationModal
|
|
80
|
-
open={true}
|
|
81
|
-
onClose={vi.fn()}
|
|
82
|
-
title="Confirm"
|
|
83
|
-
confirmLabel="Confirm"
|
|
84
|
-
onConfirm={vi.fn()}
|
|
85
|
-
error="Something failed"
|
|
86
|
-
>
|
|
87
|
-
<p>Body</p>
|
|
88
|
-
</ConfirmationModal>
|
|
89
|
-
);
|
|
90
|
-
expect(screen.getByText("Something failed")).toBeTruthy();
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it("shows confirmBusyLabel when busy", () => {
|
|
94
|
-
render(
|
|
95
|
-
<ConfirmationModal
|
|
96
|
-
open={true}
|
|
97
|
-
onClose={vi.fn()}
|
|
98
|
-
title="Confirm"
|
|
99
|
-
confirmLabel="Confirm"
|
|
100
|
-
confirmBusyLabel="Confirming…"
|
|
101
|
-
onConfirm={vi.fn()}
|
|
102
|
-
busy={true}
|
|
103
|
-
>
|
|
104
|
-
<p>Body</p>
|
|
105
|
-
</ConfirmationModal>
|
|
106
|
-
);
|
|
107
|
-
expect(screen.getByRole("button", { name: "Confirming…" })).toBeTruthy();
|
|
108
|
-
});
|
|
109
|
-
});
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { render, screen } from "@testing-library/react";
|
|
3
|
-
import { ErrorBoundary } from "../ErrorBoundary";
|
|
4
|
-
|
|
5
|
-
function ThrowError() {
|
|
6
|
-
throw new Error("Test error");
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
describe("ErrorBoundary", () => {
|
|
10
|
-
it("renders children when no error", () => {
|
|
11
|
-
render(
|
|
12
|
-
<ErrorBoundary>
|
|
13
|
-
<div data-testid="child">Hello</div>
|
|
14
|
-
</ErrorBoundary>
|
|
15
|
-
);
|
|
16
|
-
expect(screen.getByTestId("child").textContent).toBe("Hello");
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it("renders fallback UI when child throws", () => {
|
|
20
|
-
render(
|
|
21
|
-
<ErrorBoundary>
|
|
22
|
-
<ThrowError />
|
|
23
|
-
</ErrorBoundary>
|
|
24
|
-
);
|
|
25
|
-
expect(screen.getByText("Something went wrong")).toBeTruthy();
|
|
26
|
-
expect(screen.getByText("Test error")).toBeTruthy();
|
|
27
|
-
expect(screen.getByRole("button", { name: /refresh page/i })).toBeTruthy();
|
|
28
|
-
expect(screen.getByRole("link", { name: /go home/i })).toBeTruthy();
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it("renders custom fallback when provided", () => {
|
|
32
|
-
render(
|
|
33
|
-
<ErrorBoundary fallback={<div data-testid="custom">Custom fallback</div>}>
|
|
34
|
-
<ThrowError />
|
|
35
|
-
</ErrorBoundary>
|
|
36
|
-
);
|
|
37
|
-
expect(screen.getByTestId("custom").textContent).toBe("Custom fallback");
|
|
38
|
-
});
|
|
39
|
-
});
|