Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
469 changes: 15 additions & 454 deletions components/settings-dialog.tsx

Large diffs are not rendered by default.

69 changes: 69 additions & 0 deletions components/settings/about-section.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
"use client";

import { InfoIcon, ChevronRightIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible";

interface AboutSectionProps {
isExpanded: boolean;
onToggle: () => void;
}

export function AboutSection({ isExpanded, onToggle }: AboutSectionProps) {
return (
<Collapsible open={isExpanded} onOpenChange={onToggle}>
<CollapsibleTrigger className="flex w-full items-center justify-between rounded-lg bg-background-muted p-4 hover:bg-background-muted/80">
<div className="flex items-center gap-3">
<InfoIcon className="h-5 w-5 text-accent" />
<span className="font-semibold text-foreground">About</span>
</div>
<ChevronRightIcon
className={`h-5 w-5 text-foreground-muted transition-transform ${
isExpanded ? "rotate-90" : ""
}`}
/>
</CollapsibleTrigger>
<CollapsibleContent className="px-4 pb-4 pt-4 space-y-3">
<div className="space-y-2 text-sm">
<div className="flex justify-between">
<span className="text-foreground-muted">Version</span>
<span className="font-medium text-foreground">
{process.env.NEXT_PUBLIC_BUILD_VERSION || "3.0.1"}
</span>
</div>
<div className="flex justify-between">
<span className="text-foreground-muted">Build Date</span>
<span className="font-medium text-foreground">
{process.env.NEXT_PUBLIC_BUILD_DATE || "Oct 12, 2025"}
</span>
</div>
</div>

<div className="rounded-lg border border-border bg-background-muted/50 p-3">
<p className="text-xs text-foreground-muted mb-2">
🔒{" "}
<span className="font-semibold text-foreground">Privacy First</span>
</p>
<p className="text-xs text-foreground-muted">
All your data is stored locally in your browser. Nothing is sent to any
server. Your tasks, preferences, and settings stay on your device.
</p>
</div>

<Button
variant="subtle"
className="w-full justify-start"
onClick={() =>
window.open("https://github.com/vscarpenter/gsd-task-manager", "_blank")
}
>
View on GitHub →
</Button>
</CollapsibleContent>
</Collapsible>
);
}
91 changes: 91 additions & 0 deletions components/settings/appearance-settings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
"use client";

import { useTheme } from "next-themes";
import { PaletteIcon, EyeIcon, EyeOffIcon, ChevronRightIcon } from "lucide-react";
import { Label } from "@/components/ui/label";
import { Switch } from "@/components/ui/switch";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible";

interface AppearanceSettingsProps {
isExpanded: boolean;
onToggle: () => void;
showCompleted: boolean;
onToggleCompleted: () => void;
}

export function AppearanceSettings({
isExpanded,
onToggle,
showCompleted,
onToggleCompleted,
}: AppearanceSettingsProps) {
const { theme, setTheme } = useTheme();

return (
<Collapsible open={isExpanded} onOpenChange={onToggle}>
<CollapsibleTrigger className="flex w-full items-center justify-between rounded-lg bg-background-muted p-4 hover:bg-background-muted/80">
<div className="flex items-center gap-3">
<PaletteIcon className="h-5 w-5 text-accent" />
<span className="font-semibold text-foreground">Appearance</span>
</div>
<ChevronRightIcon
className={`h-5 w-5 text-foreground-muted transition-transform ${
isExpanded ? "rotate-90" : ""
}`}
/>
</CollapsibleTrigger>
<CollapsibleContent className="px-4 pb-4 pt-4 space-y-4">
{/* Theme Selection */}
<div className="space-y-2">
<Label htmlFor="theme-select">Theme</Label>
<Select value={theme} onValueChange={setTheme}>
<SelectTrigger id="theme-select">
<SelectValue placeholder="Select theme" />
</SelectTrigger>
<SelectContent>
<SelectItem value="system">System</SelectItem>
<SelectItem value="light">Light</SelectItem>
<SelectItem value="dark">Dark</SelectItem>
</SelectContent>
</Select>
<p className="text-xs text-foreground-muted">
Choose your preferred theme. Changes apply immediately.
</p>
</div>

{/* Show Completed Tasks */}
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
{showCompleted ? (
<EyeIcon className="h-4 w-4 text-foreground-muted" />
) : (
<EyeOffIcon className="h-4 w-4 text-foreground-muted" />
)}
<div>
<Label htmlFor="show-completed">Show completed tasks</Label>
<p className="text-xs text-foreground-muted">
Display completed tasks in the matrix view
</p>
</div>
</div>
<Switch
id="show-completed"
checked={showCompleted}
onCheckedChange={onToggleCompleted}
/>
</div>
</CollapsibleContent>
</Collapsible>
);
}
107 changes: 107 additions & 0 deletions components/settings/data-management.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
"use client";

import { DatabaseIcon, DownloadIcon, UploadIcon, ChevronRightIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible";

interface DataManagementProps {
isExpanded: boolean;
onToggle: () => void;
activeTasks: number;
completedTasks: number;
totalTasks: number;
estimatedSize: string;
onExport: () => Promise<void>;
onImportClick: () => void;
isLoading?: boolean;
}

export function DataManagement({
isExpanded,
onToggle,
activeTasks,
completedTasks,
totalTasks,
estimatedSize,
onExport,
onImportClick,
isLoading,
}: DataManagementProps) {
return (
<Collapsible open={isExpanded} onOpenChange={onToggle}>
<CollapsibleTrigger className="flex w-full items-center justify-between rounded-lg bg-background-muted p-4 hover:bg-background-muted/80">
<div className="flex items-center gap-3">
<DatabaseIcon className="h-5 w-5 text-accent" />
<span className="font-semibold text-foreground">Data & Backup</span>
</div>
<ChevronRightIcon
className={`h-5 w-5 text-foreground-muted transition-transform ${
isExpanded ? "rotate-90" : ""
}`}
/>
</CollapsibleTrigger>
<CollapsibleContent className="px-4 pb-4 pt-4 space-y-4">
{/* Storage Stats */}
<div className="rounded-lg border border-border bg-background-muted/50 p-4">
<h4 className="text-sm font-semibold text-foreground mb-2">Storage</h4>
<div className="space-y-1 text-xs text-foreground-muted">
<p>
Active tasks:{" "}
<span className="font-medium text-foreground">{activeTasks}</span>
</p>
<p>
Completed tasks:{" "}
<span className="font-medium text-foreground">{completedTasks}</span>
</p>
<p>
Total tasks:{" "}
<span className="font-medium text-foreground">{totalTasks}</span>
</p>
<p>
Estimated size:{" "}
<span className="font-medium text-foreground">{estimatedSize} KB</span>
</p>
</div>
</div>

{/* Export/Import Actions */}
<div className="space-y-2">
<Button
variant="subtle"
className="w-full justify-start"
onClick={onExport}
disabled={isLoading}
>
<DownloadIcon className="mr-2 h-4 w-4" />
Export Tasks
</Button>
<Button
variant="subtle"
className="w-full justify-start"
onClick={onImportClick}
disabled={isLoading}
>
<UploadIcon className="mr-2 h-4 w-4" />
Import Tasks
</Button>
<p className="text-xs text-foreground-muted px-2">
Export your tasks as JSON for backup or transfer. Import to restore or
merge tasks.
</p>
</div>

{/* Clear Data Warning */}
<div className="rounded-lg border border-red-200 bg-red-50 dark:border-red-900 dark:bg-red-950/20 p-3">
<p className="text-xs text-red-600 dark:text-red-400 mb-2">
⚠️ Clearing data is permanent and cannot be undone. Export your tasks
first.
</p>
</div>
</CollapsibleContent>
</Collapsible>
);
}
113 changes: 113 additions & 0 deletions components/settings/notification-settings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
"use client";

import { BellIcon, ChevronRightIcon } from "lucide-react";
import { Label } from "@/components/ui/label";
import { Switch } from "@/components/ui/switch";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible";
import type { NotificationSettings } from "@/lib/types";

interface NotificationSettingsProps {
isExpanded: boolean;
onToggle: () => void;
settings: NotificationSettings | null;
onNotificationToggle: () => Promise<void>;
onDefaultReminderChange: (value: string) => Promise<void>;
}

export function NotificationSettingsSection({
isExpanded,
onToggle,
settings,
onNotificationToggle,
onDefaultReminderChange,
}: NotificationSettingsProps) {
return (
<Collapsible open={isExpanded} onOpenChange={onToggle}>
<CollapsibleTrigger className="flex w-full items-center justify-between rounded-lg bg-background-muted p-4 hover:bg-background-muted/80">
<div className="flex items-center gap-3">
<BellIcon className="h-5 w-5 text-accent" />
<span className="font-semibold text-foreground">Notifications</span>
</div>
<ChevronRightIcon
className={`h-5 w-5 text-foreground-muted transition-transform ${
isExpanded ? "rotate-90" : ""
}`}
/>
</CollapsibleTrigger>
<CollapsibleContent className="px-4 pb-4 pt-4 space-y-4">
{settings && (
<>
{/* Enable Notifications */}
<div className="flex items-center justify-between">
<div>
<Label htmlFor="enable-notifications">
Enable browser notifications
</Label>
<p className="text-xs text-foreground-muted">
Show browser notifications for task reminders
</p>
</div>
<Switch
id="enable-notifications"
checked={settings.enabled}
onCheckedChange={onNotificationToggle}
/>
</div>

{/* Default Reminder Time */}
{settings.enabled && (
<div className="space-y-2">
<Label htmlFor="default-reminder">Default reminder time</Label>
<Select
value={settings.defaultReminder.toString()}
onValueChange={onDefaultReminderChange}
>
<SelectTrigger id="default-reminder">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="15">15 minutes before</SelectItem>
<SelectItem value="30">30 minutes before</SelectItem>
<SelectItem value="60">1 hour before</SelectItem>
<SelectItem value="120">2 hours before</SelectItem>
<SelectItem value="1440">1 day before</SelectItem>
</SelectContent>
</Select>
<p className="text-xs text-foreground-muted">
Default reminder time for new tasks with due dates
</p>
</div>
)}

{/* Permission Status */}
{"Notification" in window && (
<div className="rounded-lg border border-border bg-background-muted/50 p-3">
<p className="text-xs text-foreground-muted">
Browser permission:{" "}
<span className="font-medium text-foreground">
{Notification.permission === "granted"
? "Granted ✓"
: Notification.permission === "denied"
? "Denied"
: "Not requested"}
</span>
</p>
</div>
)}
</>
)}
</CollapsibleContent>
</Collapsible>
);
}
Loading