@knotpad/app 0.1.5 → 0.1.6
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/app/(app)/calendar/page.tsx +57 -0
- package/app/(app)/error.tsx +35 -0
- package/app/(app)/graph/page.tsx +32 -0
- package/app/(app)/guide/page.tsx +21 -0
- package/app/(app)/kanban/loading.tsx +24 -0
- package/app/(app)/kanban/page.tsx +59 -0
- package/app/(app)/layout.tsx +122 -0
- package/app/(app)/list/loading.tsx +21 -0
- package/app/(app)/list/page.tsx +137 -0
- package/app/(app)/loading.tsx +18 -0
- package/app/(app)/notes/[noteId]/page.tsx +84 -0
- package/app/(app)/notes/layout.tsx +30 -0
- package/app/(app)/notes/page.tsx +39 -0
- package/app/(app)/page.tsx +5 -0
- package/app/(app)/settings/agent-token/page.tsx +59 -0
- package/app/(app)/settings/backup/page.tsx +49 -0
- package/app/(app)/settings/billing/page.tsx +53 -0
- package/app/(app)/settings/calendar/page.tsx +41 -0
- package/app/(app)/settings/layout.test.tsx +39 -0
- package/app/(app)/settings/layout.tsx +71 -0
- package/app/(app)/settings/page.tsx +4 -0
- package/app/(app)/settings/security/page.tsx +43 -0
- package/app/(app)/settings/team/page.tsx +74 -0
- package/app/(app)/settings/workspace/page.tsx +27 -0
- package/app/(app)/tasks/[taskId]/page.tsx +79 -0
- package/app/(auth)/forgot-password/page.tsx +106 -0
- package/app/(auth)/guest/page.tsx +56 -0
- package/app/(auth)/layout.tsx +13 -0
- package/app/(auth)/login/page.tsx +14 -0
- package/app/(auth)/register/page.tsx +193 -0
- package/app/(auth)/reset-password/page.tsx +138 -0
- package/app/api/account/claim/route.tsx +135 -0
- package/app/api/admin/backfill-encryption/route.tsx +43 -0
- package/app/api/admin/license/route.tsx +42 -0
- package/app/api/auth/2fa/route.tsx +148 -0
- package/app/api/auth/[...nextauth]/route.tsx +3 -0
- package/app/api/auth/change-password/route.tsx +61 -0
- package/app/api/auth/check-2fa/route.tsx +19 -0
- package/app/api/auth/forgot-password/route.tsx +65 -0
- package/app/api/auth/reset-password/route.tsx +52 -0
- package/app/api/auth/verify-2fa/route.tsx +88 -0
- package/app/api/backup/download/db/route.ts +29 -0
- package/app/api/backup/download/notes/route.ts +25 -0
- package/app/api/backup/settings/route.ts +92 -0
- package/app/api/billing/checkout/route.tsx +81 -0
- package/app/api/billing/migrate/route.tsx +163 -0
- package/app/api/billing/portal/route.tsx +24 -0
- package/app/api/billing/setup-intent/route.tsx +55 -0
- package/app/api/billing/status/route.tsx +36 -0
- package/app/api/billing/subscribe/route.tsx +85 -0
- package/app/api/billing/webhook/route.tsx +199 -0
- package/app/api/calendar-feeds/[feedId]/route.tsx +67 -0
- package/app/api/calendar-feeds/[feedId]/sync/route.tsx +37 -0
- package/app/api/calendar-feeds/events/route.tsx +82 -0
- package/app/api/calendar-feeds/route.tsx +52 -0
- package/app/api/calendar-feeds/sync-all/route.tsx +34 -0
- package/app/api/cron/calendar-feeds/route.tsx +31 -0
- package/app/api/cron/stale-tasks/route.tsx +51 -0
- package/app/api/cron/sync/route.tsx +34 -0
- package/app/api/devices/[deviceId]/route.tsx +25 -0
- package/app/api/devices/route.tsx +41 -0
- package/app/api/export/route.tsx +40 -0
- package/app/api/feedback/route.tsx +54 -0
- package/app/api/folders/[folderId]/route.tsx +51 -0
- package/app/api/folders/route.tsx +37 -0
- package/app/api/graph/route.tsx +242 -0
- package/app/api/guest/route.tsx +58 -0
- package/app/api/health/route.tsx +10 -0
- package/app/api/holidays/countries/route.tsx +14 -0
- package/app/api/holidays/route.tsx +49 -0
- package/app/api/holidays/states/route.tsx +21 -0
- package/app/api/invites/[token]/route.tsx +131 -0
- package/app/api/invites/route.tsx +74 -0
- package/app/api/mcp/generate-token/route.tsx +55 -0
- package/app/api/mcp/revoke-token/[tokenId]/route.tsx +30 -0
- package/app/api/mcp/update-alias/[tokenId]/route.tsx +22 -0
- package/app/api/notes/[noteId]/export/route.tsx +45 -0
- package/app/api/notes/[noteId]/route.tsx +360 -0
- package/app/api/notes/route.tsx +112 -0
- package/app/api/notifications/route.tsx +44 -0
- package/app/api/register/route.tsx +67 -0
- package/app/api/restore/route.tsx +148 -0
- package/app/api/sync/conflicts/[conflictId]/route.tsx +134 -0
- package/app/api/sync/conflicts/route.tsx +48 -0
- package/app/api/sync/status/route.tsx +49 -0
- package/app/api/sync/trigger/route.tsx +15 -0
- package/app/api/tasks/[taskId]/detail/route.tsx +68 -0
- package/app/api/tasks/[taskId]/route.tsx +259 -0
- package/app/api/tasks/bulk/route.tsx +133 -0
- package/app/api/tasks/route.tsx +36 -0
- package/app/api/workspace/active/route.tsx +39 -0
- package/app/api/workspace/create-team/route.tsx +42 -0
- package/app/api/workspace/kanban-statuses/route.tsx +71 -0
- package/app/api/workspace/members/[memberId]/route.tsx +69 -0
- package/app/api/workspace/route.tsx +24 -0
- package/app/download/page.tsx +170 -0
- package/app/favicon.ico +0 -0
- package/app/generated/prisma/client.d.ts +1 -0
- package/app/generated/prisma/client.js +5 -0
- package/app/generated/prisma/default.d.ts +1 -0
- package/app/generated/prisma/default.js +5 -0
- package/app/generated/prisma/edge.d.ts +1 -0
- package/app/generated/prisma/edge.js +497 -0
- package/app/generated/prisma/index-browser.js +523 -0
- package/app/generated/prisma/index.d.ts +46376 -0
- package/app/generated/prisma/index.js +497 -0
- package/app/generated/prisma/package.json +144 -0
- package/app/generated/prisma/query_compiler_fast_bg.js +2 -0
- package/app/generated/prisma/query_compiler_fast_bg.wasm +0 -0
- package/app/generated/prisma/query_compiler_fast_bg.wasm-base64.js +2 -0
- package/app/generated/prisma/runtime/client.d.ts +3386 -0
- package/app/generated/prisma/runtime/client.js +86 -0
- package/app/generated/prisma/runtime/index-browser.d.ts +90 -0
- package/app/generated/prisma/runtime/index-browser.js +6 -0
- package/app/generated/prisma/runtime/wasm-compiler-edge.js +76 -0
- package/app/generated/prisma/schema.prisma +456 -0
- package/app/generated/prisma/wasm-edge-light-loader.mjs +5 -0
- package/app/generated/prisma/wasm-worker-loader.mjs +5 -0
- package/app/globals.css +54 -0
- package/app/invite/[token]/page.tsx +52 -0
- package/app/layout.tsx +90 -0
- package/app/mcp/route.tsx +430 -0
- package/app/opengraph-image.tsx +120 -0
- package/app/page.tsx +398 -0
- package/app/privacy/page.tsx +69 -0
- package/app/robots.tsx +25 -0
- package/app/sitemap.tsx +36 -0
- package/app/terms/page.tsx +69 -0
- package/app/upgrade/page.tsx +75 -0
- package/auth.config.ts +33 -0
- package/auth.ts +79 -0
- package/bin/brief.js +224 -0
- package/components/auth/login-form.tsx +302 -0
- package/components/auth/password-checklist.tsx +31 -0
- package/components/auth/password-input.tsx +36 -0
- package/components/auth/switch-account-button.test.tsx +22 -0
- package/components/auth/switch-account-button.tsx +19 -0
- package/components/auth/two-factor-input.tsx +116 -0
- package/components/billing/billing-dashboard.tsx +265 -0
- package/components/billing/card-form.tsx +210 -0
- package/components/billing/claim-account-form.tsx +99 -0
- package/components/branding/app-logo.test.tsx +20 -0
- package/components/branding/app-logo.tsx +25 -0
- package/components/calendar/calendar-agenda.tsx +150 -0
- package/components/calendar/calendar-drag.test.tsx +177 -0
- package/components/calendar/calendar-grid.tsx +357 -0
- package/components/calendar/calendar-hooks.test.tsx +27 -0
- package/components/calendar/calendar-hooks.ts +351 -0
- package/components/calendar/calendar-toolbar.test.tsx +68 -0
- package/components/calendar/calendar-toolbar.tsx +291 -0
- package/components/calendar/calendar-types.ts +148 -0
- package/components/calendar/calendar-view.test.tsx +295 -0
- package/components/calendar/calendar-view.tsx +307 -0
- package/components/calendar/day-detail-popover.tsx +174 -0
- package/components/calendar/task-chip.tsx +86 -0
- package/components/command/command-palette.test.tsx +33 -0
- package/components/command/command-palette.tsx +310 -0
- package/components/download-cta.tsx +87 -0
- package/components/feedback/feedback-popup.tsx +207 -0
- package/components/graph/graph-draw.ts +337 -0
- package/components/graph/graph-overlays.tsx +160 -0
- package/components/graph/graph-page.test.tsx +131 -0
- package/components/graph/graph-page.tsx +263 -0
- package/components/graph/graph-types.ts +47 -0
- package/components/graph/graph-view.tsx +322 -0
- package/components/guide/guide-view.tsx +522 -0
- package/components/kanban/kanban-board.test.tsx +128 -0
- package/components/kanban/kanban-board.tsx +361 -0
- package/components/kanban/kanban-card-menu.tsx +102 -0
- package/components/kanban/kanban-card.tsx +227 -0
- package/components/kanban/kanban-column.tsx +49 -0
- package/components/kanban/kanban-status-context.tsx +28 -0
- package/components/landing/calendar-sandbox.test.tsx +15 -0
- package/components/landing/calendar-sandbox.tsx +107 -0
- package/components/landing/graph-sandbox.test.tsx +27 -0
- package/components/landing/graph-sandbox.tsx +80 -0
- package/components/landing/kanban-sandbox.test.tsx +24 -0
- package/components/landing/kanban-sandbox.tsx +101 -0
- package/components/landing/landing-showcase.test.tsx +21 -0
- package/components/landing/landing-showcase.tsx +54 -0
- package/components/landing/list-sandbox.tsx +86 -0
- package/components/landing/mock-workspace.ts +168 -0
- package/components/landing/notes-sandbox.test.tsx +14 -0
- package/components/landing/notes-sandbox.tsx +88 -0
- package/components/layout/app-shell.tsx +83 -0
- package/components/layout/backup-scheduler.tsx +122 -0
- package/components/layout/bottom-nav.tsx +43 -0
- package/components/layout/icon-bar.test.tsx +29 -0
- package/components/layout/icon-bar.tsx +118 -0
- package/components/layout/mobile-top-bar.tsx +68 -0
- package/components/layout/notes-panel-folder.tsx +127 -0
- package/components/layout/notes-panel-note-item.tsx +140 -0
- package/components/layout/notes-panel-task-tab.tsx +63 -0
- package/components/layout/notes-panel-types.ts +44 -0
- package/components/layout/notes-panel.tsx +476 -0
- package/components/layout/notification-bell.tsx +251 -0
- package/components/layout/paywall-screen.tsx +41 -0
- package/components/layout/pro-banner.tsx +76 -0
- package/components/layout/sw-register.tsx +27 -0
- package/components/layout/workspace-switcher.tsx +90 -0
- package/components/notes/mobile-bottom-sheet.tsx +99 -0
- package/components/notes/note-editor-context-menu.tsx +47 -0
- package/components/notes/note-editor-dom.ts +33 -0
- package/components/notes/note-editor-dropdowns.tsx +484 -0
- package/components/notes/note-editor-hooks.ts +692 -0
- package/components/notes/note-editor-keyboard.ts +305 -0
- package/components/notes/note-editor-overlay.tsx +90 -0
- package/components/notes/note-editor.test.tsx +372 -0
- package/components/notes/note-editor.tsx +662 -0
- package/components/notes/note-preview-pane.tsx +156 -0
- package/components/notes/note-tabs.tsx +120 -0
- package/components/notes/note-types.tsx +157 -0
- package/components/settings/accept-invite.tsx +108 -0
- package/components/settings/agent-token-settings.tsx +369 -0
- package/components/settings/backup-restore-settings.test.tsx +25 -0
- package/components/settings/backup-restore-settings.tsx +327 -0
- package/components/settings/calendar-feeds-settings.tsx +489 -0
- package/components/settings/calendar-general-settings.tsx +174 -0
- package/components/settings/confirm-danger-action.test.tsx +215 -0
- package/components/settings/confirm-danger-action.tsx +65 -0
- package/components/settings/security-settings.tsx +252 -0
- package/components/settings/settings-guidance.test.tsx +98 -0
- package/components/settings/team-settings.tsx +319 -0
- package/components/settings/two-factor-auth.tsx +296 -0
- package/components/settings/workspace-settings-client.tsx +363 -0
- package/components/settings/workspace-settings-form.tsx +73 -0
- package/components/sync/conflict-viewer.tsx +247 -0
- package/components/sync/sync-indicator.tsx +171 -0
- package/components/tasks/snippet-thread.tsx +119 -0
- package/components/tasks/status-dot.tsx +47 -0
- package/components/tasks/task-badge.tsx +43 -0
- package/components/tasks/task-detail.test.tsx +187 -0
- package/components/tasks/task-detail.tsx +458 -0
- package/components/tasks/task-list-filters.test.tsx +75 -0
- package/components/tasks/task-list-filters.tsx +163 -0
- package/components/tasks/task-list-types.ts +20 -0
- package/components/tasks/task-list.test.tsx +175 -0
- package/components/tasks/task-list.tsx +481 -0
- package/components/tasks/task-row.tsx +85 -0
- package/components/tasks/task-table-row.tsx +259 -0
- package/components/ui/skeleton.tsx +3 -0
- package/components/ui/toast.test.tsx +42 -0
- package/components/ui/toast.tsx +70 -0
- package/instrumentation.tsx +23 -0
- package/lib/api-error.ts +50 -0
- package/lib/backup/backup-runner.test.ts +32 -0
- package/lib/backup/backup-runner.ts +19 -0
- package/lib/backup/backup-schedule.test.ts +23 -0
- package/lib/backup/backup-schedule.ts +55 -0
- package/lib/backup/backup-settings.test.ts +30 -0
- package/lib/backup/backup-settings.ts +27 -0
- package/lib/backup/export-notes-zip.test.ts +26 -0
- package/lib/backup/export-notes-zip.ts +82 -0
- package/lib/backup/export-workspace-backup.test.ts +17 -0
- package/lib/backup/export-workspace-backup.ts +77 -0
- package/lib/backup/restore-workspace-from-export.test.ts +18 -0
- package/lib/backup/restore-workspace-from-export.ts +183 -0
- package/lib/backup/types.ts +14 -0
- package/lib/brand-icons.ts +1 -0
- package/lib/calendar-feed-crypto.ts +38 -0
- package/lib/calendar-feed.ts +239 -0
- package/lib/client/online-status.ts +47 -0
- package/lib/conflict-resolver.test.ts +57 -0
- package/lib/conflict-resolver.ts +240 -0
- package/lib/db-init.ts +79 -0
- package/lib/email.ts +159 -0
- package/lib/encryption.test.ts +41 -0
- package/lib/encryption.ts +98 -0
- package/lib/extract-snippet.test.ts +123 -0
- package/lib/extract-snippet.ts +69 -0
- package/lib/kanban-status.ts +55 -0
- package/lib/license.ts +21 -0
- package/lib/limits.ts +31 -0
- package/lib/mcp-auth.test.ts +58 -0
- package/lib/mcp-auth.ts +65 -0
- package/lib/mcp-contract.test.ts +25 -0
- package/lib/mcp-contract.ts +210 -0
- package/lib/mcp-handler.ts +31 -0
- package/lib/mcp-url.test.ts +12 -0
- package/lib/mcp-url.ts +7 -0
- package/lib/mentions.test.ts +45 -0
- package/lib/mentions.ts +73 -0
- package/lib/note-crypto.ts +108 -0
- package/lib/note-sync.ts +201 -0
- package/lib/note-title.ts +93 -0
- package/lib/prisma.ts +193 -0
- package/lib/pro-flush.ts +292 -0
- package/lib/rate-limit.ts +57 -0
- package/lib/stripe.ts +38 -0
- package/lib/sync-worker.ts +388 -0
- package/lib/task-parser.test.ts +91 -0
- package/lib/task-parser.ts +81 -0
- package/lib/task-utils.ts +52 -0
- package/lib/use-is-electron.ts +19 -0
- package/lib/use-is-mobile.ts +22 -0
- package/lib/validation/calendar-feed.ts +31 -0
- package/lib/validation/note.ts +27 -0
- package/lib/validation/task.ts +26 -0
- package/lib/view-preferences.test.ts +54 -0
- package/lib/view-preferences.ts +28 -0
- package/lib/workspace.ts +66 -0
- package/next.config.ts +21 -0
- package/package.json +49 -3
- package/postcss.config.mjs +7 -0
- package/prisma/migrations/20260519021916_init/migration.sql +388 -0
- package/prisma/migrations/20260519061113_drop_sync_password/migration.sql +8 -0
- package/prisma/migrations/20260520065016_add_task_start_date/migration.sql +2 -0
- package/prisma/migrations/20260529010600_remove_encryption_fields/migration.sql +12 -0
- package/prisma/migrations/20260529020000_restore_encryption_salt/migration.sql +3 -0
- package/prisma/migrations/20260529030000_add_folders/migration.sql +17 -0
- package/prisma/migrations/20260605000000_deferred_fixes/migration.sql +31 -0
- package/prisma/migrations/20260605020806_add_pending_sync_to_note_and_task/migration.sql +5 -0
- package/prisma/migrations/20260605063634_add_stripe_webhook_event_sync_lock/migration.sql +14 -0
- package/prisma/migrations/20260605100000_add_prod_indexes/migration.sql +26 -0
- package/prisma/migrations/20260608081404_add_kanban_statuses/migration.sql +23 -0
- package/prisma/migrations/20260611032723_add_calendar_feeds/migration.sql +43 -0
- package/prisma/migrations/20260611040000_add_calendar_feed_color/migration.sql +2 -0
- package/prisma/migrations/20260611050000_add_task_priority/migration.sql +14 -0
- package/prisma/migrations/20260612060000_add_critical_priority/migration.sql +2 -0
- package/prisma/migrations/20260613090000_add_backup_settings/migration.sql +25 -0
- package/prisma/migrations/20260614160000_add_feedback/migration.sql +20 -0
- package/prisma/migrations/20260614210000_add_2fa/migration.sql +4 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +457 -0
- package/public/Logo_icon.svg +1 -0
- package/public/file.svg +1 -0
- package/public/globe.svg +1 -0
- package/public/icon-192.png +0 -0
- package/public/icon-512.png +0 -0
- package/public/icon.svg +4 -0
- package/public/icon_dark.svg +1 -0
- package/public/knotpad_icon.svg +1 -0
- package/public/knotpad_logo_full.svg +1 -0
- package/public/manifest.json +14 -0
- package/public/next.svg +1 -0
- package/public/sw.js +137 -0
- package/public/vercel.svg +1 -0
- package/public/window.svg +1 -0
- package/tsconfig.json +35 -0
- package/brief.js +0 -311
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import {
|
|
5
|
+
FileText,
|
|
6
|
+
Kanban,
|
|
7
|
+
List,
|
|
8
|
+
Calendar,
|
|
9
|
+
Network,
|
|
10
|
+
Settings,
|
|
11
|
+
Bell,
|
|
12
|
+
Search,
|
|
13
|
+
Users,
|
|
14
|
+
Shield,
|
|
15
|
+
Zap,
|
|
16
|
+
BookOpen,
|
|
17
|
+
} from "lucide-react";
|
|
18
|
+
|
|
19
|
+
type Section = {
|
|
20
|
+
id: string;
|
|
21
|
+
icon: typeof FileText;
|
|
22
|
+
title: string;
|
|
23
|
+
content: React.ReactNode;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const sections: Section[] = [
|
|
27
|
+
{
|
|
28
|
+
id: "overview",
|
|
29
|
+
icon: BookOpen,
|
|
30
|
+
title: "Overview",
|
|
31
|
+
content: (
|
|
32
|
+
<div className="space-y-4">
|
|
33
|
+
<p className="text-sm text-zinc-300 leading-relaxed">
|
|
34
|
+
<strong className="text-zinc-100">Knotpad</strong> is a note-first project management tool designed for
|
|
35
|
+
individuals and teams who want to organize their work around written context rather than just task lists.
|
|
36
|
+
</p>
|
|
37
|
+
<p className="text-sm text-zinc-300 leading-relaxed">
|
|
38
|
+
Unlike traditional project management tools that start with tasks, Knotpad starts with <strong className="text-zinc-100">notes</strong>.
|
|
39
|
+
Each note can contain multiple tasks, and tasks can be viewed across different perspectives: Kanban boards,
|
|
40
|
+
list views, calendar timelines, and relationship graphs.
|
|
41
|
+
</p>
|
|
42
|
+
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-4">
|
|
43
|
+
<h4 className="text-xs font-semibold text-zinc-400 uppercase tracking-wider mb-2">Core Concepts</h4>
|
|
44
|
+
<ul className="space-y-2 text-sm text-zinc-300">
|
|
45
|
+
<li className="flex items-start gap-2">
|
|
46
|
+
<span className="text-zinc-500">•</span>
|
|
47
|
+
<span><strong>Notes:</strong> Your primary documents - project specs, meeting notes, ideas, or any written context</span>
|
|
48
|
+
</li>
|
|
49
|
+
<li className="flex items-start gap-2">
|
|
50
|
+
<span className="text-zinc-500">•</span>
|
|
51
|
+
<span><strong>Tasks:</strong> Action items extracted from notes with status, priority, and assignment</span>
|
|
52
|
+
</li>
|
|
53
|
+
<li className="flex items-start gap-2">
|
|
54
|
+
<span className="text-zinc-500">•</span>
|
|
55
|
+
<span><strong>Folders:</strong> Organize notes into collections for better navigation</span>
|
|
56
|
+
</li>
|
|
57
|
+
<li className="flex items-start gap-2">
|
|
58
|
+
<span className="text-zinc-500">•</span>
|
|
59
|
+
<span><strong>Workspaces:</strong> Separate environments for different teams or projects</span>
|
|
60
|
+
</li>
|
|
61
|
+
</ul>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
),
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: "notes",
|
|
68
|
+
icon: FileText,
|
|
69
|
+
title: "Notes & Editor",
|
|
70
|
+
content: (
|
|
71
|
+
<div className="space-y-4">
|
|
72
|
+
<p className="text-sm text-zinc-300 leading-relaxed">
|
|
73
|
+
The <strong className="text-zinc-100">Notes</strong> module is your main workspace for writing and editing.
|
|
74
|
+
Notes support rich formatting and can contain tasks that appear across all views.
|
|
75
|
+
</p>
|
|
76
|
+
<div className="grid gap-3">
|
|
77
|
+
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-3">
|
|
78
|
+
<h4 className="text-xs font-medium text-zinc-200 mb-1">Creating Notes</h4>
|
|
79
|
+
<p className="text-xs text-zinc-400">
|
|
80
|
+
Click the "+" button in the notes panel or press ⌘+N (Ctrl+N on Windows) to create a new note.
|
|
81
|
+
Notes are auto-saved as you type.
|
|
82
|
+
</p>
|
|
83
|
+
</div>
|
|
84
|
+
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-3">
|
|
85
|
+
<h4 className="text-xs font-medium text-zinc-200 mb-1">Task Extraction</h4>
|
|
86
|
+
<p className="text-xs text-zinc-400">
|
|
87
|
+
Type "- [ ]" or "1. [ ]" at the start of a line to create a task checkbox.
|
|
88
|
+
These tasks automatically appear in your Kanban, List, and Calendar views.
|
|
89
|
+
</p>
|
|
90
|
+
</div>
|
|
91
|
+
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-3">
|
|
92
|
+
<h4 className="text-xs font-medium text-zinc-200 mb-1">Folders</h4>
|
|
93
|
+
<p className="text-xs text-zinc-400">
|
|
94
|
+
Organize notes by dragging them into folders. Create folders from the sidebar menu.
|
|
95
|
+
Folders help group related projects or contexts together.
|
|
96
|
+
</p>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
),
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
id: "kanban",
|
|
104
|
+
icon: Kanban,
|
|
105
|
+
title: "Kanban Board",
|
|
106
|
+
content: (
|
|
107
|
+
<div className="space-y-4">
|
|
108
|
+
<p className="text-sm text-zinc-300 leading-relaxed">
|
|
109
|
+
The <strong className="text-zinc-100">Kanban</strong> view displays tasks as cards organized by status columns.
|
|
110
|
+
Drag and drop tasks between columns to update their status.
|
|
111
|
+
</p>
|
|
112
|
+
<div className="grid gap-3">
|
|
113
|
+
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-3">
|
|
114
|
+
<h4 className="text-xs font-medium text-zinc-200 mb-1">Status Columns</h4>
|
|
115
|
+
<p className="text-xs text-zinc-400">
|
|
116
|
+
Default columns: Open, Claimed, In Progress, Review, Done.
|
|
117
|
+
Admins can customize columns in Settings → Kanban Statuses.
|
|
118
|
+
</p>
|
|
119
|
+
</div>
|
|
120
|
+
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-3">
|
|
121
|
+
<h4 className="text-xs font-medium text-zinc-200 mb-1">Task Cards</h4>
|
|
122
|
+
<p className="text-xs text-zinc-400">
|
|
123
|
+
Each card shows the task title, priority indicator, assignee, and linked note.
|
|
124
|
+
Click any card to open its note in the editor.
|
|
125
|
+
</p>
|
|
126
|
+
</div>
|
|
127
|
+
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-3">
|
|
128
|
+
<h4 className="text-xs font-medium text-zinc-200 mb-1">Claiming Tasks</h4>
|
|
129
|
+
<p className="text-xs text-zinc-400">
|
|
130
|
+
Click "Claim" on a task to take ownership and start working on it.
|
|
131
|
+
Tasks auto-release if you go offline for too long, allowing others to pick them up.
|
|
132
|
+
</p>
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
),
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
id: "list",
|
|
140
|
+
icon: List,
|
|
141
|
+
title: "List View",
|
|
142
|
+
content: (
|
|
143
|
+
<div className="space-y-4">
|
|
144
|
+
<p className="text-sm text-zinc-300 leading-relaxed">
|
|
145
|
+
The <strong className="text-zinc-100">List</strong> view provides a compact, filterable table of all tasks.
|
|
146
|
+
Great for quick scanning and bulk operations.
|
|
147
|
+
</p>
|
|
148
|
+
<div className="grid gap-3">
|
|
149
|
+
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-3">
|
|
150
|
+
<h4 className="text-xs font-medium text-zinc-200 mb-1">Sorting & Filtering</h4>
|
|
151
|
+
<p className="text-xs text-zinc-400">
|
|
152
|
+
Click column headers to sort. Use the search bar to filter by task title,
|
|
153
|
+
note name, or assignee. Filter by status using the dropdown.
|
|
154
|
+
</p>
|
|
155
|
+
</div>
|
|
156
|
+
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-3">
|
|
157
|
+
<h4 className="text-xs font-medium text-zinc-200 mb-1">Inline Editing</h4>
|
|
158
|
+
<p className="text-xs text-zinc-400">
|
|
159
|
+
Click any task title to edit it inline. Status and priority can be changed
|
|
160
|
+
via dropdown menus on each row.
|
|
161
|
+
</p>
|
|
162
|
+
</div>
|
|
163
|
+
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-3">
|
|
164
|
+
<h4 className="text-xs font-medium text-zinc-200 mb-1">Bulk Selection</h4>
|
|
165
|
+
<p className="text-xs text-zinc-400">
|
|
166
|
+
Select multiple tasks using checkboxes to perform bulk actions like
|
|
167
|
+
changing status or priority for several tasks at once.
|
|
168
|
+
</p>
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
),
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
id: "calendar",
|
|
176
|
+
icon: Calendar,
|
|
177
|
+
title: "Calendar",
|
|
178
|
+
content: (
|
|
179
|
+
<div className="space-y-4">
|
|
180
|
+
<p className="text-sm text-zinc-300 leading-relaxed">
|
|
181
|
+
The <strong className="text-zinc-100">Calendar</strong> view shows tasks with due dates across a monthly timeline.
|
|
182
|
+
External calendar feeds (Google/Apple) can be overlaid for context.
|
|
183
|
+
</p>
|
|
184
|
+
<div className="grid gap-3">
|
|
185
|
+
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-3">
|
|
186
|
+
<h4 className="text-xs font-medium text-zinc-200 mb-1">Setting Due Dates</h4>
|
|
187
|
+
<p className="text-xs text-zinc-400">
|
|
188
|
+
Click the "+" button next to a task or use the date picker in task details
|
|
189
|
+
to set start and due dates. Tasks with dates appear on the calendar.
|
|
190
|
+
</p>
|
|
191
|
+
</div>
|
|
192
|
+
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-3">
|
|
193
|
+
<h4 className="text-xs font-medium text-zinc-200 mb-1">Calendar Feeds</h4>
|
|
194
|
+
<p className="text-xs text-zinc-400">
|
|
195
|
+
Add external calendars in Settings → Calendar Feeds. Paste your Google/Apple
|
|
196
|
+
calendar's "secret iCal URL" to overlay meetings alongside tasks.
|
|
197
|
+
</p>
|
|
198
|
+
</div>
|
|
199
|
+
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-3">
|
|
200
|
+
<h4 className="text-xs font-medium text-zinc-200 mb-1">Day Detail</h4>
|
|
201
|
+
<p className="text-xs text-zinc-400">
|
|
202
|
+
Click any date to see all tasks and events for that day.
|
|
203
|
+
Drag tasks between days to reschedule their due dates.
|
|
204
|
+
</p>
|
|
205
|
+
</div>
|
|
206
|
+
</div>
|
|
207
|
+
</div>
|
|
208
|
+
),
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
id: "graph",
|
|
212
|
+
icon: Network,
|
|
213
|
+
title: "Graph View",
|
|
214
|
+
content: (
|
|
215
|
+
<div className="space-y-4">
|
|
216
|
+
<p className="text-sm text-zinc-300 leading-relaxed">
|
|
217
|
+
The <strong className="text-zinc-100">Graph</strong> view visualizes relationships between notes and tasks
|
|
218
|
+
as a network diagram. Useful for understanding connections in complex projects.
|
|
219
|
+
</p>
|
|
220
|
+
<div className="grid gap-3">
|
|
221
|
+
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-3">
|
|
222
|
+
<h4 className="text-xs font-medium text-zinc-200 mb-1">Node Types</h4>
|
|
223
|
+
<p className="text-xs text-zinc-400">
|
|
224
|
+
Notes appear as larger nodes, tasks as smaller nodes.
|
|
225
|
+
Lines show relationships between notes and their tasks.
|
|
226
|
+
</p>
|
|
227
|
+
</div>
|
|
228
|
+
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-3">
|
|
229
|
+
<h4 className="text-xs font-medium text-zinc-200 mb-1">Navigation</h4>
|
|
230
|
+
<p className="text-xs text-zinc-400">
|
|
231
|
+
Click and drag to pan, scroll to zoom. Click a node to focus on it
|
|
232
|
+
and see only its connected items. Double-click to open the note.
|
|
233
|
+
</p>
|
|
234
|
+
</div>
|
|
235
|
+
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-3">
|
|
236
|
+
<h4 className="text-xs font-medium text-zinc-200 mb-1">Task References</h4>
|
|
237
|
+
<p className="text-xs text-zinc-400">
|
|
238
|
+
When you mention a task ID in another note, a reference link is created.
|
|
239
|
+
These references appear as connections in the graph view.
|
|
240
|
+
</p>
|
|
241
|
+
</div>
|
|
242
|
+
</div>
|
|
243
|
+
</div>
|
|
244
|
+
),
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
id: "search",
|
|
248
|
+
icon: Search,
|
|
249
|
+
title: "Search & Command",
|
|
250
|
+
content: (
|
|
251
|
+
<div className="space-y-4">
|
|
252
|
+
<p className="text-sm text-zinc-300 leading-relaxed">
|
|
253
|
+
<strong className="text-zinc-100">Command Palette</strong> (⌘K) is your quick access tool
|
|
254
|
+
for finding notes, tasks, and actions without leaving the keyboard.
|
|
255
|
+
</p>
|
|
256
|
+
<div className="grid gap-3">
|
|
257
|
+
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-3">
|
|
258
|
+
<h4 className="text-xs font-medium text-zinc-200 mb-1">Opening the Palette</h4>
|
|
259
|
+
<p className="text-xs text-zinc-400">
|
|
260
|
+
Press ⌘+K (Ctrl+K) or click the search icon in the sidebar.
|
|
261
|
+
Type to filter results instantly across notes and tasks.
|
|
262
|
+
</p>
|
|
263
|
+
</div>
|
|
264
|
+
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-3">
|
|
265
|
+
<h4 className="text-xs font-medium text-zinc-200 mb-1">Quick Actions</h4>
|
|
266
|
+
<p className="text-xs text-zinc-400">
|
|
267
|
+
Type "/" to see available commands like "Create note", "Go to Kanban",
|
|
268
|
+
or "Toggle theme". Navigate with arrow keys, confirm with Enter.
|
|
269
|
+
</p>
|
|
270
|
+
</div>
|
|
271
|
+
</div>
|
|
272
|
+
</div>
|
|
273
|
+
),
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
id: "notifications",
|
|
277
|
+
icon: Bell,
|
|
278
|
+
title: "Notifications",
|
|
279
|
+
content: (
|
|
280
|
+
<div className="space-y-4">
|
|
281
|
+
<p className="text-sm text-zinc-300 leading-relaxed">
|
|
282
|
+
Stay informed about task assignments, status changes, and team activity
|
|
283
|
+
through the notification system.
|
|
284
|
+
</p>
|
|
285
|
+
<div className="grid gap-3">
|
|
286
|
+
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-3">
|
|
287
|
+
<h4 className="text-xs font-medium text-zinc-200 mb-1">Notification Types</h4>
|
|
288
|
+
<p className="text-xs text-zinc-400">
|
|
289
|
+
• <strong>Task Assigned:</strong> Someone assigned you a task<br />
|
|
290
|
+
• <strong>Status Change:</strong> A task you own changed status<br />
|
|
291
|
+
• <strong>Invite Accepted:</strong> Someone joined your workspace<br />
|
|
292
|
+
• <strong>Takeover:</strong> Your stale claim was released
|
|
293
|
+
</p>
|
|
294
|
+
</div>
|
|
295
|
+
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-3">
|
|
296
|
+
<h4 className="text-xs font-medium text-zinc-200 mb-1">Managing Notifications</h4>
|
|
297
|
+
<p className="text-xs text-zinc-400">
|
|
298
|
+
Click the bell icon to open the notification panel.
|
|
299
|
+
Mark individual items read or "Mark all read" to clear the list.
|
|
300
|
+
</p>
|
|
301
|
+
</div>
|
|
302
|
+
</div>
|
|
303
|
+
</div>
|
|
304
|
+
),
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
id: "team",
|
|
308
|
+
icon: Users,
|
|
309
|
+
title: "Team & Collaboration",
|
|
310
|
+
content: (
|
|
311
|
+
<div className="space-y-4">
|
|
312
|
+
<p className="text-sm text-zinc-300 leading-relaxed">
|
|
313
|
+
<strong className="text-zinc-100">Workspaces</strong> are collaborative environments where teams
|
|
314
|
+
share notes and tasks. Personal workspaces are private by default.
|
|
315
|
+
</p>
|
|
316
|
+
<div className="grid gap-3">
|
|
317
|
+
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-3">
|
|
318
|
+
<h4 className="text-xs font-medium text-zinc-200 mb-1">Inviting Members</h4>
|
|
319
|
+
<p className="text-xs text-zinc-400">
|
|
320
|
+
Go to Settings → Team Members. Enter email addresses to send invites.
|
|
321
|
+
New members can be Admins (full access) or Members (can edit, can't manage).
|
|
322
|
+
</p>
|
|
323
|
+
</div>
|
|
324
|
+
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-3">
|
|
325
|
+
<h4 className="text-xs font-medium text-zinc-200 mb-1">Task Assignment</h4>
|
|
326
|
+
<p className="text-xs text-zinc-400">
|
|
327
|
+
Click the assignee area on any task to assign it to a team member.
|
|
328
|
+
Assigned tasks appear in their personal views and notifications.
|
|
329
|
+
</p>
|
|
330
|
+
</div>
|
|
331
|
+
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-3">
|
|
332
|
+
<h4 className="text-xs font-medium text-zinc-200 mb-1">Workspace Switcher</h4>
|
|
333
|
+
<p className="text-xs text-zinc-400">
|
|
334
|
+
Use the workspace dropdown in the sidebar to switch between personal
|
|
335
|
+
and team workspaces. Each workspace has isolated notes and tasks.
|
|
336
|
+
</p>
|
|
337
|
+
</div>
|
|
338
|
+
</div>
|
|
339
|
+
</div>
|
|
340
|
+
),
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
id: "settings",
|
|
344
|
+
icon: Settings,
|
|
345
|
+
title: "Settings",
|
|
346
|
+
content: (
|
|
347
|
+
<div className="space-y-4">
|
|
348
|
+
<p className="text-sm text-zinc-300 leading-relaxed">
|
|
349
|
+
Customize your Knotpad experience through the various settings panels.
|
|
350
|
+
</p>
|
|
351
|
+
<div className="grid gap-3">
|
|
352
|
+
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-3">
|
|
353
|
+
<h4 className="text-xs font-medium text-zinc-200 mb-1">Workspace Settings</h4>
|
|
354
|
+
<p className="text-xs text-zinc-400">
|
|
355
|
+
Rename workspace, update slug (URL identifier), manage cloud sync preferences.
|
|
356
|
+
Only owners and admins can change workspace settings.
|
|
357
|
+
</p>
|
|
358
|
+
</div>
|
|
359
|
+
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-3">
|
|
360
|
+
<h4 className="text-xs font-medium text-zinc-200 mb-1">Kanban Statuses</h4>
|
|
361
|
+
<p className="text-xs text-zinc-400">
|
|
362
|
+
Customize the columns in your Kanban board. Add, rename, reorder, or hide statuses.
|
|
363
|
+
Each status has a color for visual organization.
|
|
364
|
+
</p>
|
|
365
|
+
</div>
|
|
366
|
+
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-3">
|
|
367
|
+
<h4 className="text-xs font-medium text-zinc-200 mb-1">Calendar Feeds</h4>
|
|
368
|
+
<p className="text-xs text-zinc-400">
|
|
369
|
+
Add external calendar URLs (Google, Apple, Outlook) to overlay on your task calendar.
|
|
370
|
+
Feeds are personal - other members don't see your calendar events.
|
|
371
|
+
</p>
|
|
372
|
+
</div>
|
|
373
|
+
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-3">
|
|
374
|
+
<h4 className="text-xs font-medium text-zinc-200 mb-1">Backup & Export</h4>
|
|
375
|
+
<p className="text-xs text-zinc-400">
|
|
376
|
+
Schedule automatic backups or export your workspace data as JSON or Markdown ZIP.
|
|
377
|
+
Backups include all notes, tasks, and folder structure.
|
|
378
|
+
</p>
|
|
379
|
+
</div>
|
|
380
|
+
</div>
|
|
381
|
+
</div>
|
|
382
|
+
),
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
id: "security",
|
|
386
|
+
icon: Shield,
|
|
387
|
+
title: "Security & Privacy",
|
|
388
|
+
content: (
|
|
389
|
+
<div className="space-y-4">
|
|
390
|
+
<p className="text-sm text-zinc-300 leading-relaxed">
|
|
391
|
+
Knotpad is designed with security in mind for both personal and team use.
|
|
392
|
+
</p>
|
|
393
|
+
<div className="grid gap-3">
|
|
394
|
+
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-3">
|
|
395
|
+
<h4 className="text-xs font-medium text-zinc-200 mb-1">Authentication</h4>
|
|
396
|
+
<p className="text-xs text-zinc-400">
|
|
397
|
+
Secure password-based login with session management.
|
|
398
|
+
All passwords are hashed using bcrypt before storage.
|
|
399
|
+
</p>
|
|
400
|
+
</div>
|
|
401
|
+
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-3">
|
|
402
|
+
<h4 className="text-xs font-medium text-zinc-200 mb-1">Data Encryption</h4>
|
|
403
|
+
<p className="text-xs text-zinc-400">
|
|
404
|
+
Calendar feed URLs are encrypted at rest using per-row salts.
|
|
405
|
+
All data is served over HTTPS. Notes are stored securely with workspace-level isolation.
|
|
406
|
+
</p>
|
|
407
|
+
</div>
|
|
408
|
+
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-3">
|
|
409
|
+
<h4 className="text-xs font-medium text-zinc-200 mb-1">Device Sessions</h4>
|
|
410
|
+
<p className="text-xs text-zinc-400">
|
|
411
|
+
View and manage active logins from Settings → Security.
|
|
412
|
+
Revoke access from devices you no longer use.
|
|
413
|
+
</p>
|
|
414
|
+
</div>
|
|
415
|
+
</div>
|
|
416
|
+
</div>
|
|
417
|
+
),
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
id: "pro",
|
|
421
|
+
icon: Zap,
|
|
422
|
+
title: "Pro Features",
|
|
423
|
+
content: (
|
|
424
|
+
<div className="space-y-4">
|
|
425
|
+
<p className="text-sm text-zinc-300 leading-relaxed">
|
|
426
|
+
Upgrade to Pro to unlock additional capabilities for power users and teams.
|
|
427
|
+
</p>
|
|
428
|
+
<div className="grid gap-3">
|
|
429
|
+
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-3">
|
|
430
|
+
<h4 className="text-xs font-medium text-zinc-200 mb-1">Cloud Sync</h4>
|
|
431
|
+
<p className="text-xs text-zinc-400">
|
|
432
|
+
Pro workspaces sync across all your devices in real-time.
|
|
433
|
+
Access your notes and tasks from desktop, web, and mobile apps.
|
|
434
|
+
</p>
|
|
435
|
+
</div>
|
|
436
|
+
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-3">
|
|
437
|
+
<h4 className="text-xs font-medium text-zinc-200 mb-1">Team Workspaces</h4>
|
|
438
|
+
<p className="text-xs text-zinc-400">
|
|
439
|
+
Create unlimited team workspaces with multiple members.
|
|
440
|
+
Perfect for agencies, departments, or project teams.
|
|
441
|
+
</p>
|
|
442
|
+
</div>
|
|
443
|
+
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-3">
|
|
444
|
+
<h4 className="text-xs font-medium text-zinc-200 mb-1">Priority Support</h4>
|
|
445
|
+
<p className="text-xs text-zinc-400">
|
|
446
|
+
Pro users get priority response on feedback and support requests.
|
|
447
|
+
Direct access to the product team for feature requests.
|
|
448
|
+
</p>
|
|
449
|
+
</div>
|
|
450
|
+
</div>
|
|
451
|
+
</div>
|
|
452
|
+
),
|
|
453
|
+
},
|
|
454
|
+
];
|
|
455
|
+
|
|
456
|
+
export function GuideView() {
|
|
457
|
+
const [activeSection, setActiveSection] = useState<string>("overview");
|
|
458
|
+
const [sidebarOpen, setSidebarOpen] = useState(false);
|
|
459
|
+
|
|
460
|
+
const activeContent = sections.find((s) => s.id === activeSection);
|
|
461
|
+
|
|
462
|
+
return (
|
|
463
|
+
<div className="flex flex-1 overflow-hidden relative">
|
|
464
|
+
{/* Mobile sidebar toggle */}
|
|
465
|
+
<button
|
|
466
|
+
onClick={() => setSidebarOpen(!sidebarOpen)}
|
|
467
|
+
className="lg:hidden absolute top-4 left-4 z-20 p-2 rounded-md bg-zinc-900 border border-zinc-800 text-zinc-400 hover:text-zinc-200"
|
|
468
|
+
aria-label="Toggle menu"
|
|
469
|
+
>
|
|
470
|
+
<BookOpen size={16} />
|
|
471
|
+
</button>
|
|
472
|
+
|
|
473
|
+
{/* Sidebar */}
|
|
474
|
+
<aside className={`fixed inset-y-0 left-0 z-10 w-64 border-r border-zinc-800 bg-zinc-950 overflow-y-auto transform transition-transform duration-200 lg:relative lg:transform-none ${sidebarOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0'}`}>
|
|
475
|
+
<nav className="p-2 space-y-0.5">
|
|
476
|
+
{sections.map((section) => {
|
|
477
|
+
const Icon = section.icon;
|
|
478
|
+
const isActive = section.id === activeSection;
|
|
479
|
+
return (
|
|
480
|
+
<button
|
|
481
|
+
key={section.id}
|
|
482
|
+
onClick={() => {
|
|
483
|
+
setActiveSection(section.id);
|
|
484
|
+
setSidebarOpen(false);
|
|
485
|
+
}}
|
|
486
|
+
className={`w-full flex items-center gap-2.5 rounded-md px-3 py-2 text-left text-xs transition-colors ${
|
|
487
|
+
isActive
|
|
488
|
+
? "bg-zinc-800 text-zinc-200"
|
|
489
|
+
: "text-zinc-400 hover:bg-zinc-800/50 hover:text-zinc-300"
|
|
490
|
+
}`}
|
|
491
|
+
>
|
|
492
|
+
<Icon size={14} className={isActive ? "text-zinc-200" : "text-zinc-500"} />
|
|
493
|
+
<span className="font-medium">{section.title}</span>
|
|
494
|
+
</button>
|
|
495
|
+
);
|
|
496
|
+
})}
|
|
497
|
+
</nav>
|
|
498
|
+
</aside>
|
|
499
|
+
|
|
500
|
+
{/* Mobile overlay */}
|
|
501
|
+
{sidebarOpen && (
|
|
502
|
+
<div
|
|
503
|
+
className="fixed inset-0 bg-black/50 z-0 lg:hidden"
|
|
504
|
+
onClick={() => setSidebarOpen(false)}
|
|
505
|
+
/>
|
|
506
|
+
)}
|
|
507
|
+
|
|
508
|
+
{/* Content */}
|
|
509
|
+
<main className="flex-1 overflow-y-auto p-4 lg:p-6 lg:ml-0">
|
|
510
|
+
{activeContent && (
|
|
511
|
+
<div className="max-w-2xl mt-12 lg:mt-0">
|
|
512
|
+
<div className="flex items-center gap-3 mb-6">
|
|
513
|
+
<activeContent.icon size={20} className="text-zinc-400" />
|
|
514
|
+
<h2 className="text-lg font-semibold text-zinc-100">{activeContent.title}</h2>
|
|
515
|
+
</div>
|
|
516
|
+
{activeContent.content}
|
|
517
|
+
</div>
|
|
518
|
+
)}
|
|
519
|
+
</main>
|
|
520
|
+
</div>
|
|
521
|
+
);
|
|
522
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import { cleanup, render, screen } from "@testing-library/react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
import { KanbanBoard } from "@/components/kanban/kanban-board";
|
|
6
|
+
import type { KanbanTask } from "@/components/kanban/kanban-board";
|
|
7
|
+
|
|
8
|
+
vi.mock("next/navigation", () => ({
|
|
9
|
+
useRouter: () => ({ push: vi.fn(), refresh: vi.fn() }),
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
vi.mock("@/components/kanban/kanban-status-context", () => ({
|
|
13
|
+
useKanbanStatuses: () => [
|
|
14
|
+
{ key: "OPEN", label: "Open", color: "border-zinc-700", isVisible: true },
|
|
15
|
+
{ key: "DONE", label: "Done", color: "border-zinc-700", isVisible: true },
|
|
16
|
+
],
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
vi.mock("@/components/kanban/kanban-card", () => ({
|
|
20
|
+
KanbanCard: ({ task }: { task: KanbanTask }) => <div data-testid="kanban-card">{task.title}</div>,
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
function makeTask(overrides: Partial<KanbanTask> = {}): KanbanTask {
|
|
24
|
+
return {
|
|
25
|
+
id: overrides.id ?? "t1",
|
|
26
|
+
title: overrides.title ?? "Ship notes polish",
|
|
27
|
+
status: overrides.status ?? "OPEN",
|
|
28
|
+
claimedBy: overrides.claimedBy ?? null,
|
|
29
|
+
claimedByAlias: overrides.claimedByAlias ?? null,
|
|
30
|
+
lastHeartbeat: overrides.lastHeartbeat ?? null,
|
|
31
|
+
assigneeType: overrides.assigneeType ?? "HUMAN",
|
|
32
|
+
assignee: overrides.assignee ?? null,
|
|
33
|
+
note: overrides.note ?? { id: "n1", title: "Launch" },
|
|
34
|
+
dueDate: overrides.dueDate ?? null,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function getColumnBody(label: string) {
|
|
39
|
+
const header = screen.getByText(label).closest("header");
|
|
40
|
+
|
|
41
|
+
if (!(header instanceof HTMLElement) || !(header.nextElementSibling instanceof HTMLElement)) {
|
|
42
|
+
throw new Error(`Could not find column body for ${label}`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return { header, body: header.nextElementSibling };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
beforeEach(() => {
|
|
49
|
+
window.localStorage.clear();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
afterEach(() => {
|
|
53
|
+
cleanup();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe("KanbanBoard", () => {
|
|
57
|
+
it("shows density controls, makes headers sticky, and saves compact mode", async () => {
|
|
58
|
+
const user = userEvent.setup();
|
|
59
|
+
|
|
60
|
+
render(<KanbanBoard tasks={[makeTask()]} />);
|
|
61
|
+
|
|
62
|
+
expect(screen.getByRole("button", { name: "Comfortable" })).toBeInTheDocument();
|
|
63
|
+
expect(screen.getByRole("button", { name: "Compact" })).toBeInTheDocument();
|
|
64
|
+
|
|
65
|
+
const { header, body } = getColumnBody("Open");
|
|
66
|
+
expect(header.className).toContain("sticky");
|
|
67
|
+
expect(header.className).toContain("top-0");
|
|
68
|
+
expect(body.className).toContain("gap-3");
|
|
69
|
+
expect(body.className).toContain("p-3");
|
|
70
|
+
|
|
71
|
+
await user.click(screen.getByRole("button", { name: "Compact" }));
|
|
72
|
+
|
|
73
|
+
expect(body.className).toContain("gap-2");
|
|
74
|
+
expect(body.className).toContain("p-2");
|
|
75
|
+
expect(window.localStorage.getItem("brief:kanban-density")).toBe(JSON.stringify("compact"));
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("loads the saved density preference on first render", () => {
|
|
79
|
+
window.localStorage.setItem("brief:kanban-density", JSON.stringify("compact"));
|
|
80
|
+
|
|
81
|
+
render(<KanbanBoard tasks={[makeTask()]} />);
|
|
82
|
+
|
|
83
|
+
const { body } = getColumnBody("Open");
|
|
84
|
+
expect(body.className).toContain("gap-2");
|
|
85
|
+
expect(body.className).toContain("p-2");
|
|
86
|
+
expect(screen.getByRole("button", { name: "Compact" })).toHaveAttribute("aria-pressed", "true");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("filters the board down to overdue tasks", async () => {
|
|
90
|
+
const user = userEvent.setup();
|
|
91
|
+
const yesterday = new Date();
|
|
92
|
+
yesterday.setDate(yesterday.getDate() - 1);
|
|
93
|
+
const tomorrow = new Date();
|
|
94
|
+
tomorrow.setDate(tomorrow.getDate() + 1);
|
|
95
|
+
|
|
96
|
+
const overdueTask = makeTask({
|
|
97
|
+
id: "overdue-task",
|
|
98
|
+
title: "Past due follow-up",
|
|
99
|
+
dueDate: yesterday.toISOString(),
|
|
100
|
+
});
|
|
101
|
+
const futureTask = makeTask({
|
|
102
|
+
id: "future-task",
|
|
103
|
+
title: "Upcoming review",
|
|
104
|
+
dueDate: tomorrow.toISOString(),
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
render(<KanbanBoard tasks={[overdueTask, futureTask]} />);
|
|
108
|
+
|
|
109
|
+
await user.click(screen.getByRole("button", { name: "Overdue" }));
|
|
110
|
+
|
|
111
|
+
expect(screen.getByText(overdueTask.title)).toBeInTheDocument();
|
|
112
|
+
expect(screen.queryByText(futureTask.title)).not.toBeInTheDocument();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("shows filtered-empty column messaging when quick filters hide cards", async () => {
|
|
116
|
+
const user = userEvent.setup();
|
|
117
|
+
const futureHumanTask = makeTask({
|
|
118
|
+
id: "future-human-task",
|
|
119
|
+
title: "Upcoming human review",
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
render(<KanbanBoard tasks={[futureHumanTask]} />);
|
|
123
|
+
|
|
124
|
+
await user.click(screen.getByRole("button", { name: "Agent" }));
|
|
125
|
+
|
|
126
|
+
expect(screen.getAllByText("No matching tasks in this column")).toHaveLength(2);
|
|
127
|
+
});
|
|
128
|
+
});
|