Skip to content

Commit ce6cb60

Browse files
vscarpenterclaude
andauthored
refactor: split write-ops and settings-dialog into modular components (Phase 3, Tasks 2-3) (#52)
* refactor: split mcp-server write-ops.ts into modular components (Phase 3, Task 2) Split packages/mcp-server/src/write-ops.ts from 494 lines into focused modules: - write-ops/types.ts (58 lines) - Type definitions and interfaces - write-ops/helpers.ts (124 lines) - Helper functions (ID generation, encryption, sync) - write-ops/task-operations.ts (183 lines) - Individual task CRUD operations - write-ops/bulk-operations.ts (151 lines) - Bulk update operations - write-ops.ts (39 lines) - Re-export layer for backward compatibility Benefits: - All files now under 200-line target (well below 300-line limit) - Single responsibility per module - Improved maintainability and testability - Zero breaking changes (100% backward compatible) Verification: - MCP server build: Success (tsc compilation passed) - All existing imports work unchanged - Module boundaries clear and logical Related: Phase 3 of coding standards compliance (see next.md) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: split settings-dialog.tsx into modular components (Phase 3, Task 3) Split components/settings-dialog.tsx from 454 lines into focused modules: - settings/about-section.tsx (69 lines) - Version and app information - settings/appearance-settings.tsx (91 lines) - Theme and display preferences - settings/data-management.tsx (107 lines) - Import/export and storage stats - settings/notification-settings.tsx (113 lines) - Notification configuration - settings/settings-dialog.tsx (169 lines) - Main dialog wrapper with state - settings-dialog.tsx (15 lines) - Re-export layer for backward compatibility Benefits: - All files now under 170 lines (well below 300-line limit) - Single responsibility per section component - Improved maintainability and testability - Zero breaking changes (100% backward compatible) Verification: - TypeScript compilation: No new errors - All existing imports work unchanged - Each section independently testable Related: Phase 3 of coding standards compliance (see next.md) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent b580e36 commit ce6cb60

File tree

11 files changed

+1118
-947
lines changed

11 files changed

+1118
-947
lines changed

components/settings-dialog.tsx

Lines changed: 15 additions & 454 deletions
Large diffs are not rendered by default.
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
"use client";
2+
3+
import { InfoIcon, ChevronRightIcon } from "lucide-react";
4+
import { Button } from "@/components/ui/button";
5+
import {
6+
Collapsible,
7+
CollapsibleContent,
8+
CollapsibleTrigger,
9+
} from "@/components/ui/collapsible";
10+
11+
interface AboutSectionProps {
12+
isExpanded: boolean;
13+
onToggle: () => void;
14+
}
15+
16+
export function AboutSection({ isExpanded, onToggle }: AboutSectionProps) {
17+
return (
18+
<Collapsible open={isExpanded} onOpenChange={onToggle}>
19+
<CollapsibleTrigger className="flex w-full items-center justify-between rounded-lg bg-background-muted p-4 hover:bg-background-muted/80">
20+
<div className="flex items-center gap-3">
21+
<InfoIcon className="h-5 w-5 text-accent" />
22+
<span className="font-semibold text-foreground">About</span>
23+
</div>
24+
<ChevronRightIcon
25+
className={`h-5 w-5 text-foreground-muted transition-transform ${
26+
isExpanded ? "rotate-90" : ""
27+
}`}
28+
/>
29+
</CollapsibleTrigger>
30+
<CollapsibleContent className="px-4 pb-4 pt-4 space-y-3">
31+
<div className="space-y-2 text-sm">
32+
<div className="flex justify-between">
33+
<span className="text-foreground-muted">Version</span>
34+
<span className="font-medium text-foreground">
35+
{process.env.NEXT_PUBLIC_BUILD_VERSION || "3.0.1"}
36+
</span>
37+
</div>
38+
<div className="flex justify-between">
39+
<span className="text-foreground-muted">Build Date</span>
40+
<span className="font-medium text-foreground">
41+
{process.env.NEXT_PUBLIC_BUILD_DATE || "Oct 12, 2025"}
42+
</span>
43+
</div>
44+
</div>
45+
46+
<div className="rounded-lg border border-border bg-background-muted/50 p-3">
47+
<p className="text-xs text-foreground-muted mb-2">
48+
🔒{" "}
49+
<span className="font-semibold text-foreground">Privacy First</span>
50+
</p>
51+
<p className="text-xs text-foreground-muted">
52+
All your data is stored locally in your browser. Nothing is sent to any
53+
server. Your tasks, preferences, and settings stay on your device.
54+
</p>
55+
</div>
56+
57+
<Button
58+
variant="subtle"
59+
className="w-full justify-start"
60+
onClick={() =>
61+
window.open("https://github.com/vscarpenter/gsd-task-manager", "_blank")
62+
}
63+
>
64+
View on GitHub →
65+
</Button>
66+
</CollapsibleContent>
67+
</Collapsible>
68+
);
69+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
"use client";
2+
3+
import { useTheme } from "next-themes";
4+
import { PaletteIcon, EyeIcon, EyeOffIcon, ChevronRightIcon } from "lucide-react";
5+
import { Label } from "@/components/ui/label";
6+
import { Switch } from "@/components/ui/switch";
7+
import {
8+
Select,
9+
SelectContent,
10+
SelectItem,
11+
SelectTrigger,
12+
SelectValue,
13+
} from "@/components/ui/select";
14+
import {
15+
Collapsible,
16+
CollapsibleContent,
17+
CollapsibleTrigger,
18+
} from "@/components/ui/collapsible";
19+
20+
interface AppearanceSettingsProps {
21+
isExpanded: boolean;
22+
onToggle: () => void;
23+
showCompleted: boolean;
24+
onToggleCompleted: () => void;
25+
}
26+
27+
export function AppearanceSettings({
28+
isExpanded,
29+
onToggle,
30+
showCompleted,
31+
onToggleCompleted,
32+
}: AppearanceSettingsProps) {
33+
const { theme, setTheme } = useTheme();
34+
35+
return (
36+
<Collapsible open={isExpanded} onOpenChange={onToggle}>
37+
<CollapsibleTrigger className="flex w-full items-center justify-between rounded-lg bg-background-muted p-4 hover:bg-background-muted/80">
38+
<div className="flex items-center gap-3">
39+
<PaletteIcon className="h-5 w-5 text-accent" />
40+
<span className="font-semibold text-foreground">Appearance</span>
41+
</div>
42+
<ChevronRightIcon
43+
className={`h-5 w-5 text-foreground-muted transition-transform ${
44+
isExpanded ? "rotate-90" : ""
45+
}`}
46+
/>
47+
</CollapsibleTrigger>
48+
<CollapsibleContent className="px-4 pb-4 pt-4 space-y-4">
49+
{/* Theme Selection */}
50+
<div className="space-y-2">
51+
<Label htmlFor="theme-select">Theme</Label>
52+
<Select value={theme} onValueChange={setTheme}>
53+
<SelectTrigger id="theme-select">
54+
<SelectValue placeholder="Select theme" />
55+
</SelectTrigger>
56+
<SelectContent>
57+
<SelectItem value="system">System</SelectItem>
58+
<SelectItem value="light">Light</SelectItem>
59+
<SelectItem value="dark">Dark</SelectItem>
60+
</SelectContent>
61+
</Select>
62+
<p className="text-xs text-foreground-muted">
63+
Choose your preferred theme. Changes apply immediately.
64+
</p>
65+
</div>
66+
67+
{/* Show Completed Tasks */}
68+
<div className="flex items-center justify-between">
69+
<div className="flex items-center gap-3">
70+
{showCompleted ? (
71+
<EyeIcon className="h-4 w-4 text-foreground-muted" />
72+
) : (
73+
<EyeOffIcon className="h-4 w-4 text-foreground-muted" />
74+
)}
75+
<div>
76+
<Label htmlFor="show-completed">Show completed tasks</Label>
77+
<p className="text-xs text-foreground-muted">
78+
Display completed tasks in the matrix view
79+
</p>
80+
</div>
81+
</div>
82+
<Switch
83+
id="show-completed"
84+
checked={showCompleted}
85+
onCheckedChange={onToggleCompleted}
86+
/>
87+
</div>
88+
</CollapsibleContent>
89+
</Collapsible>
90+
);
91+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
"use client";
2+
3+
import { DatabaseIcon, DownloadIcon, UploadIcon, ChevronRightIcon } from "lucide-react";
4+
import { Button } from "@/components/ui/button";
5+
import {
6+
Collapsible,
7+
CollapsibleContent,
8+
CollapsibleTrigger,
9+
} from "@/components/ui/collapsible";
10+
11+
interface DataManagementProps {
12+
isExpanded: boolean;
13+
onToggle: () => void;
14+
activeTasks: number;
15+
completedTasks: number;
16+
totalTasks: number;
17+
estimatedSize: string;
18+
onExport: () => Promise<void>;
19+
onImportClick: () => void;
20+
isLoading?: boolean;
21+
}
22+
23+
export function DataManagement({
24+
isExpanded,
25+
onToggle,
26+
activeTasks,
27+
completedTasks,
28+
totalTasks,
29+
estimatedSize,
30+
onExport,
31+
onImportClick,
32+
isLoading,
33+
}: DataManagementProps) {
34+
return (
35+
<Collapsible open={isExpanded} onOpenChange={onToggle}>
36+
<CollapsibleTrigger className="flex w-full items-center justify-between rounded-lg bg-background-muted p-4 hover:bg-background-muted/80">
37+
<div className="flex items-center gap-3">
38+
<DatabaseIcon className="h-5 w-5 text-accent" />
39+
<span className="font-semibold text-foreground">Data & Backup</span>
40+
</div>
41+
<ChevronRightIcon
42+
className={`h-5 w-5 text-foreground-muted transition-transform ${
43+
isExpanded ? "rotate-90" : ""
44+
}`}
45+
/>
46+
</CollapsibleTrigger>
47+
<CollapsibleContent className="px-4 pb-4 pt-4 space-y-4">
48+
{/* Storage Stats */}
49+
<div className="rounded-lg border border-border bg-background-muted/50 p-4">
50+
<h4 className="text-sm font-semibold text-foreground mb-2">Storage</h4>
51+
<div className="space-y-1 text-xs text-foreground-muted">
52+
<p>
53+
Active tasks:{" "}
54+
<span className="font-medium text-foreground">{activeTasks}</span>
55+
</p>
56+
<p>
57+
Completed tasks:{" "}
58+
<span className="font-medium text-foreground">{completedTasks}</span>
59+
</p>
60+
<p>
61+
Total tasks:{" "}
62+
<span className="font-medium text-foreground">{totalTasks}</span>
63+
</p>
64+
<p>
65+
Estimated size:{" "}
66+
<span className="font-medium text-foreground">{estimatedSize} KB</span>
67+
</p>
68+
</div>
69+
</div>
70+
71+
{/* Export/Import Actions */}
72+
<div className="space-y-2">
73+
<Button
74+
variant="subtle"
75+
className="w-full justify-start"
76+
onClick={onExport}
77+
disabled={isLoading}
78+
>
79+
<DownloadIcon className="mr-2 h-4 w-4" />
80+
Export Tasks
81+
</Button>
82+
<Button
83+
variant="subtle"
84+
className="w-full justify-start"
85+
onClick={onImportClick}
86+
disabled={isLoading}
87+
>
88+
<UploadIcon className="mr-2 h-4 w-4" />
89+
Import Tasks
90+
</Button>
91+
<p className="text-xs text-foreground-muted px-2">
92+
Export your tasks as JSON for backup or transfer. Import to restore or
93+
merge tasks.
94+
</p>
95+
</div>
96+
97+
{/* Clear Data Warning */}
98+
<div className="rounded-lg border border-red-200 bg-red-50 dark:border-red-900 dark:bg-red-950/20 p-3">
99+
<p className="text-xs text-red-600 dark:text-red-400 mb-2">
100+
⚠️ Clearing data is permanent and cannot be undone. Export your tasks
101+
first.
102+
</p>
103+
</div>
104+
</CollapsibleContent>
105+
</Collapsible>
106+
);
107+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
"use client";
2+
3+
import { BellIcon, ChevronRightIcon } from "lucide-react";
4+
import { Label } from "@/components/ui/label";
5+
import { Switch } from "@/components/ui/switch";
6+
import {
7+
Select,
8+
SelectContent,
9+
SelectItem,
10+
SelectTrigger,
11+
SelectValue,
12+
} from "@/components/ui/select";
13+
import {
14+
Collapsible,
15+
CollapsibleContent,
16+
CollapsibleTrigger,
17+
} from "@/components/ui/collapsible";
18+
import type { NotificationSettings } from "@/lib/types";
19+
20+
interface NotificationSettingsProps {
21+
isExpanded: boolean;
22+
onToggle: () => void;
23+
settings: NotificationSettings | null;
24+
onNotificationToggle: () => Promise<void>;
25+
onDefaultReminderChange: (value: string) => Promise<void>;
26+
}
27+
28+
export function NotificationSettingsSection({
29+
isExpanded,
30+
onToggle,
31+
settings,
32+
onNotificationToggle,
33+
onDefaultReminderChange,
34+
}: NotificationSettingsProps) {
35+
return (
36+
<Collapsible open={isExpanded} onOpenChange={onToggle}>
37+
<CollapsibleTrigger className="flex w-full items-center justify-between rounded-lg bg-background-muted p-4 hover:bg-background-muted/80">
38+
<div className="flex items-center gap-3">
39+
<BellIcon className="h-5 w-5 text-accent" />
40+
<span className="font-semibold text-foreground">Notifications</span>
41+
</div>
42+
<ChevronRightIcon
43+
className={`h-5 w-5 text-foreground-muted transition-transform ${
44+
isExpanded ? "rotate-90" : ""
45+
}`}
46+
/>
47+
</CollapsibleTrigger>
48+
<CollapsibleContent className="px-4 pb-4 pt-4 space-y-4">
49+
{settings && (
50+
<>
51+
{/* Enable Notifications */}
52+
<div className="flex items-center justify-between">
53+
<div>
54+
<Label htmlFor="enable-notifications">
55+
Enable browser notifications
56+
</Label>
57+
<p className="text-xs text-foreground-muted">
58+
Show browser notifications for task reminders
59+
</p>
60+
</div>
61+
<Switch
62+
id="enable-notifications"
63+
checked={settings.enabled}
64+
onCheckedChange={onNotificationToggle}
65+
/>
66+
</div>
67+
68+
{/* Default Reminder Time */}
69+
{settings.enabled && (
70+
<div className="space-y-2">
71+
<Label htmlFor="default-reminder">Default reminder time</Label>
72+
<Select
73+
value={settings.defaultReminder.toString()}
74+
onValueChange={onDefaultReminderChange}
75+
>
76+
<SelectTrigger id="default-reminder">
77+
<SelectValue />
78+
</SelectTrigger>
79+
<SelectContent>
80+
<SelectItem value="15">15 minutes before</SelectItem>
81+
<SelectItem value="30">30 minutes before</SelectItem>
82+
<SelectItem value="60">1 hour before</SelectItem>
83+
<SelectItem value="120">2 hours before</SelectItem>
84+
<SelectItem value="1440">1 day before</SelectItem>
85+
</SelectContent>
86+
</Select>
87+
<p className="text-xs text-foreground-muted">
88+
Default reminder time for new tasks with due dates
89+
</p>
90+
</div>
91+
)}
92+
93+
{/* Permission Status */}
94+
{"Notification" in window && (
95+
<div className="rounded-lg border border-border bg-background-muted/50 p-3">
96+
<p className="text-xs text-foreground-muted">
97+
Browser permission:{" "}
98+
<span className="font-medium text-foreground">
99+
{Notification.permission === "granted"
100+
? "Granted ✓"
101+
: Notification.permission === "denied"
102+
? "Denied"
103+
: "Not requested"}
104+
</span>
105+
</p>
106+
</div>
107+
)}
108+
</>
109+
)}
110+
</CollapsibleContent>
111+
</Collapsible>
112+
);
113+
}

0 commit comments

Comments
 (0)