diff --git a/app/settings/page.tsx b/app/settings/page.tsx new file mode 100644 index 0000000..89e85cb --- /dev/null +++ b/app/settings/page.tsx @@ -0,0 +1,11 @@ +import type { Metadata } from 'next' +import { SettingsClient } from '@/app/settings/settings-client' + +export const metadata: Metadata = { + title: 'Settings | TaskChain', + description: 'Manage your profile, preferences, wallet, theme, and security settings.', +} + +export default function SettingsPage() { + return +} diff --git a/app/settings/settings-client.tsx b/app/settings/settings-client.tsx new file mode 100644 index 0000000..d419525 --- /dev/null +++ b/app/settings/settings-client.tsx @@ -0,0 +1,521 @@ +'use client' + +import { useMemo, useState } from 'react' +import Link from 'next/link' +import { useRouter } from 'next/navigation' +import { useTheme } from 'next-themes' +import { + Loader2, + ArrowLeft, + Bell, + ShieldCheck, + Wallet, + Palette, + User, + Copy, + Check, + MoonStar, + SunMedium, + Monitor, + LogOut, + KeyRound, +} from 'lucide-react' +import { Button } from '@/components/ui/button' +import { Card, CardContent, CardFooter, CardHeader } from '@/components/ui/card' +import { Input } from '@/components/ui/input' +import { Label } from '@/components/ui/label' +import { Textarea } from '@/components/ui/textarea' +import { Badge } from '@/components/ui/badge' +import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog' +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' +import { ThemeToggle } from '@/components/ui/ThemeToggle' +import { useStellarWallet, truncateStellarAddress, networkLabel, REQUIRED_NETWORK } from '@/components/wallet-provider' +import { cn } from '@/lib/utils' + +type NotificationPrefs = { + email: boolean + push: boolean + inApp: boolean + digest: 'instant' | 'daily' | 'weekly' +} + +type ProfileDraft = { + displayName: string + email: string + phone: string + avatarUrl: string + bio: string +} + +const PROFILE_STORAGE_KEY = 'taskchain_settings_profile' +const NOTIFICATION_STORAGE_KEY = 'taskchain_settings_notifications' + +const defaultProfile: ProfileDraft = { + displayName: '', + email: '', + phone: '', + avatarUrl: '', + bio: '', +} + +const defaultNotifications: NotificationPrefs = { + email: true, + push: true, + inApp: true, + digest: 'daily', +} + +function readStoredJSON(key: string, fallback: T): T { + if (typeof window === 'undefined') return fallback + try { + const raw = localStorage.getItem(key) + if (!raw) return fallback + return { ...fallback, ...JSON.parse(raw) } + } catch { + return fallback + } +} + +function SectionHeading({ + icon: Icon, + title, + description, +}: { + icon: React.ComponentType<{ className?: string }> + title: string + description: string +}) { + return ( +
+
+ +
+
+

{title}

+

{description}

+
+
+ ) +} + +export function SettingsClient() { + const router = useRouter() + const { theme, setTheme } = useTheme() + const { address, isConnected, network } = useStellarWallet() + const [profile, setProfile] = useState(() => readStoredJSON(PROFILE_STORAGE_KEY, defaultProfile)) + const [notifications, setNotifications] = useState(() => readStoredJSON(NOTIFICATION_STORAGE_KEY, defaultNotifications)) + const [profileSaved, setProfileSaved] = useState(false) + const [notificationsSaved, setNotificationsSaved] = useState(false) + const [copyState, setCopyState] = useState<'idle' | 'copied'>('idle') + const [securityDialogOpen, setSecurityDialogOpen] = useState(false) + + const maskedWallet = useMemo(() => { + if (!address) return 'No wallet connected' + return truncateStellarAddress(address) + }, [address]) + + const handleProfileSave = () => { + localStorage.setItem(PROFILE_STORAGE_KEY, JSON.stringify(profile)) + setProfileSaved(true) + window.setTimeout(() => setProfileSaved(false), 1800) + } + + const handleNotificationsSave = () => { + localStorage.setItem(NOTIFICATION_STORAGE_KEY, JSON.stringify(notifications)) + setNotificationsSaved(true) + window.setTimeout(() => setNotificationsSaved(false), 1800) + } + + const handleCopyWallet = async () => { + if (!address) return + await navigator.clipboard.writeText(address) + setCopyState('copied') + window.setTimeout(() => setCopyState('idle'), 1600) + } + + const handleSignOutDevice = () => { + localStorage.removeItem('tc_dev_access_token') + localStorage.removeItem('stellar_wallet_address') + router.push('/login') + } + + return ( +
+
+
+
+ +
+

Settings & Preferences

+

+ Manage your profile, notification preferences, wallet details, theme, and security controls from one place. +

+
+
+
+ + {isConnected ? 'Wallet connected' : 'Wallet disconnected'} + + {address && ( + + {networkLabel(network)} + + )} +
+
+ +
+
+ + + + + +
+
+ + setProfile((prev) => ({ ...prev, displayName: e.target.value }))} + placeholder="Your name" + /> +
+
+ + setProfile((prev) => ({ ...prev, email: e.target.value }))} + placeholder="you@example.com" + /> +
+
+
+
+ + setProfile((prev) => ({ ...prev, phone: e.target.value }))} + placeholder="+234..." + /> +
+
+ + setProfile((prev) => ({ ...prev, avatarUrl: e.target.value }))} + placeholder="https://..." + /> +
+
+
+ +