web / frontend /src /components /modals /ProfileModal.tsx
Chandima Prabhath
feat: Add chat components and modals for enhanced user interaction
1904e4c
raw
history blame
7.68 kB
import { useState, useEffect, useMemo } from "react";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Switch } from "@/components/ui/switch";
import { User, Mail, Shield, LogOut } from "lucide-react";
import { Separator } from "@/components/ui/separator";
import { toast } from "@/components/ui/sonner";
import { storage, STORAGE_KEYS } from "@/lib/storage";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from "@/components/ui/alert-dialog";
// Use the ESM build of multiavatar to generate SVG strings
import multiavatar from "@multiavatar/multiavatar/esm";
interface ProfileModalProps {
isOpen: boolean;
onClose: () => void;
}
interface ProfileSettings {
name: string;
email: string;
avatarUrl?: string; // now optional
saveHistory: boolean;
}
const PROFILE_STORAGE_KEY = STORAGE_KEYS.PROFILE_STORAGE_KEY;
export const ProfileModal = ({ isOpen, onClose }: ProfileModalProps) => {
const [signOutDialogOpen, setSignOutDialogOpen] = useState(false);
const [profileSettings, setProfileSettings] = useState<ProfileSettings>(() => {
// load, or default (no external URL here)
return (
storage.get<ProfileSettings>(PROFILE_STORAGE_KEY) || {
name: "John Doe",
email: "[email protected]",
avatarUrl: undefined,
saveHistory: true,
}
);
});
// reload from storage whenever modal re-opens
useEffect(() => {
if (isOpen) {
const saved = storage.get<ProfileSettings>(PROFILE_STORAGE_KEY);
if (saved) setProfileSettings(saved);
}
}, [isOpen]);
// memoize the SVG data URI so it only regenerates when the name changes
const defaultAvatarDataUri = useMemo(() => {
const svg = multiavatar(profileSettings.name);
return `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`;
}, [profileSettings.name]);
// pick either the custom URL or the inline SVG
const avatarSrc = profileSettings.avatarUrl || defaultAvatarDataUri;
const handleSaveChanges = () => {
storage.set(PROFILE_STORAGE_KEY, profileSettings);
toast.success("Profile settings saved successfully");
onClose();
};
const handleSignOut = () => {
toast.success("Signed out successfully");
setSignOutDialogOpen(false);
onClose();
// clear auth tokens, etc.
};
return (
<>
<Dialog open={isOpen} onOpenChange={onClose}>
<DialogContent className="sm:max-w-[500px] max-h-[85vh] overflow-y-auto">
<DialogHeader>
<DialogTitle className="text-xl flex items-center">
<User className="mr-2 h-5 w-5" />
Profile Settings
</DialogTitle>
<DialogDescription>
Manage your profile information and preferences.
</DialogDescription>
</DialogHeader>
<Separator />
<div className="flex flex-col space-y-6 py-4">
{/* Profile section */}
<div className="flex flex-col space-y-4">
<div className="flex items-center space-x-4">
<Avatar className="h-20 w-20">
<AvatarImage src={avatarSrc} alt="User avatar" />
<AvatarFallback className="bg-primary/10 text-lg">
<User className="h-8 w-8 text-primary" />
</AvatarFallback>
</Avatar>
<div className="space-y-1">
<h3 className="font-medium text-lg">{profileSettings.name}</h3>
<p className="text-sm text-muted-foreground">{profileSettings.email}</p>
<Button variant="outline" size="sm" className="mt-2">
Change picture
</Button>
</div>
</div>
<Separator />
<div className="grid gap-4">
{/* Name */}
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="name" className="text-right">
Name
</Label>
<Input
id="name"
value={profileSettings.name}
onChange={(e) =>
setProfileSettings({ ...profileSettings, name: e.target.value })
}
className="col-span-3"
/>
</div>
{/* Email */}
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="email" className="text-right">
Email
</Label>
<Input
id="email"
value={profileSettings.email}
onChange={(e) =>
setProfileSettings({ ...profileSettings, email: e.target.value })
}
className="col-span-3"
/>
</div>
</div>
</div>
{/* Privacy */}
<div className="space-y-4">
<h3 className="text-lg font-medium flex items-center gap-2">
<Shield className="h-5 w-5" />
Privacy and Data
</h3>
<Separator />
<div className="flex items-center justify-between">
<div className="space-y-0.5">
<Label htmlFor="chat-history">Save Chat History</Label>
<p className="text-sm text-muted-foreground">
Keep your chat history saved
</p>
</div>
<Switch
id="chat-history"
checked={profileSettings.saveHistory}
onCheckedChange={(saveHistory) =>
setProfileSettings({ ...profileSettings, saveHistory })
}
/>
</div>
</div>
</div>
<DialogFooter className="flex items-center justify-between mt-6">
<Button
variant="destructive"
size="sm"
onClick={() => setSignOutDialogOpen(true)}
>
<LogOut className="mr-2 h-4 w-4" />
Sign Out
</Button>
<Button onClick={handleSaveChanges}>Save changes</Button>
</DialogFooter>
</DialogContent>
</Dialog>
{/* Sign-out confirmation */}
<AlertDialog open={signOutDialogOpen} onOpenChange={setSignOutDialogOpen}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Sign Out</AlertDialogTitle>
<AlertDialogDescription>
Are you sure you want to sign out? You will need to sign in again to
access your account.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={handleSignOut}
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
>
Sign Out
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</>
);
};