mirror of
https://github.com/pezkuwichain/pwap.git
synced 2026-04-27 15:07:56 +00:00
Initial commit - PezkuwiChain Web Governance App
This commit is contained in:
@@ -0,0 +1,378 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useAuth } from '@/contexts/AuthContext';
|
||||
import { supabase } from '@/lib/supabase';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
import { Loader2, User, Mail, Shield, Bell, Palette, Globe } from 'lucide-react';
|
||||
import { TwoFactorSetup } from '@/components/auth/TwoFactorSetup';
|
||||
export default function ProfileSettings() {
|
||||
const { user } = useAuth();
|
||||
const { toast } = useToast();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [profile, setProfile] = useState({
|
||||
username: '',
|
||||
full_name: '',
|
||||
bio: '',
|
||||
phone: '',
|
||||
language: 'en',
|
||||
theme: 'light',
|
||||
notifications_email: true,
|
||||
notifications_push: false,
|
||||
notifications_sms: false,
|
||||
two_factor_enabled: false
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (user) {
|
||||
loadProfile();
|
||||
}
|
||||
}, [user]);
|
||||
|
||||
const loadProfile = async () => {
|
||||
try {
|
||||
const { data } = await supabase
|
||||
.from('profiles')
|
||||
.select('*')
|
||||
.eq('id', user?.id)
|
||||
.single();
|
||||
|
||||
if (data) {
|
||||
setProfile({
|
||||
username: data.username || '',
|
||||
full_name: data.full_name || '',
|
||||
bio: data.bio || '',
|
||||
phone: data.phone || '',
|
||||
language: data.language || 'en',
|
||||
theme: data.theme || 'light',
|
||||
notifications_email: data.notifications_email ?? true,
|
||||
notifications_push: data.notifications_push ?? false,
|
||||
notifications_sms: data.notifications_sms ?? false,
|
||||
two_factor_enabled: data.two_factor_enabled ?? false
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading profile:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const updateProfile = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const { error } = await supabase
|
||||
.from('profiles')
|
||||
.update({
|
||||
username: profile.username,
|
||||
full_name: profile.full_name,
|
||||
bio: profile.bio,
|
||||
phone: profile.phone,
|
||||
language: profile.language,
|
||||
theme: profile.theme,
|
||||
updated_at: new Date().toISOString()
|
||||
})
|
||||
.eq('id', user?.id);
|
||||
|
||||
if (error) throw error;
|
||||
|
||||
toast({
|
||||
title: 'Success',
|
||||
description: 'Profile updated successfully',
|
||||
});
|
||||
} catch (error) {
|
||||
toast({
|
||||
title: 'Error',
|
||||
description: 'Failed to update profile',
|
||||
variant: 'destructive',
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const updateNotificationSettings = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const { error } = await supabase
|
||||
.from('profiles')
|
||||
.update({
|
||||
notifications_email: profile.notifications_email,
|
||||
notifications_push: profile.notifications_push,
|
||||
notifications_sms: profile.notifications_sms,
|
||||
updated_at: new Date().toISOString()
|
||||
})
|
||||
.eq('id', user?.id);
|
||||
|
||||
if (error) throw error;
|
||||
|
||||
toast({
|
||||
title: 'Success',
|
||||
description: 'Notification settings updated',
|
||||
});
|
||||
} catch (error) {
|
||||
toast({
|
||||
title: 'Error',
|
||||
description: 'Failed to update notification settings',
|
||||
variant: 'destructive',
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const updateSecuritySettings = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const { error } = await supabase
|
||||
.from('profiles')
|
||||
.update({
|
||||
two_factor_enabled: profile.two_factor_enabled,
|
||||
updated_at: new Date().toISOString()
|
||||
})
|
||||
.eq('id', user?.id);
|
||||
|
||||
if (error) throw error;
|
||||
|
||||
toast({
|
||||
title: 'Success',
|
||||
description: 'Security settings updated',
|
||||
});
|
||||
} catch (error) {
|
||||
toast({
|
||||
title: 'Error',
|
||||
description: 'Failed to update security settings',
|
||||
variant: 'destructive',
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const changePassword = async () => {
|
||||
const newPassword = prompt('Enter new password:');
|
||||
if (!newPassword) return;
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
const { error } = await supabase.auth.updateUser({
|
||||
password: newPassword
|
||||
});
|
||||
|
||||
if (error) throw error;
|
||||
|
||||
toast({
|
||||
title: 'Success',
|
||||
description: 'Password changed successfully',
|
||||
});
|
||||
} catch (error) {
|
||||
toast({
|
||||
title: 'Error',
|
||||
description: 'Failed to change password',
|
||||
variant: 'destructive',
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container mx-auto py-8 max-w-4xl">
|
||||
<h1 className="text-3xl font-bold mb-8">Profile Settings</h1>
|
||||
|
||||
<Tabs defaultValue="profile" className="space-y-4">
|
||||
<TabsList className="grid w-full grid-cols-4">
|
||||
<TabsTrigger value="profile">
|
||||
<User className="mr-2 h-4 w-4" />
|
||||
Profile
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="notifications">
|
||||
<Bell className="mr-2 h-4 w-4" />
|
||||
Notifications
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="security">
|
||||
<Shield className="mr-2 h-4 w-4" />
|
||||
Security
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="preferences">
|
||||
<Palette className="mr-2 h-4 w-4" />
|
||||
Preferences
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="profile">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Profile Information</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div>
|
||||
<Label>Username</Label>
|
||||
<Input
|
||||
value={profile.username}
|
||||
onChange={(e) => setProfile({ ...profile, username: e.target.value })}
|
||||
placeholder="Enter username"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label>Full Name</Label>
|
||||
<Input
|
||||
value={profile.full_name}
|
||||
onChange={(e) => setProfile({ ...profile, full_name: e.target.value })}
|
||||
placeholder="Enter full name"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label>Bio</Label>
|
||||
<Textarea
|
||||
value={profile.bio}
|
||||
onChange={(e) => setProfile({ ...profile, bio: e.target.value })}
|
||||
placeholder="Tell us about yourself"
|
||||
rows={4}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label>Phone Number</Label>
|
||||
<Input
|
||||
value={profile.phone}
|
||||
onChange={(e) => setProfile({ ...profile, phone: e.target.value })}
|
||||
placeholder="+1234567890"
|
||||
/>
|
||||
</div>
|
||||
<Button onClick={updateProfile} disabled={loading}>
|
||||
Save Changes
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="notifications">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Notification Preferences</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<Label>Email Notifications</Label>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Receive notifications via email
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={profile.notifications_email}
|
||||
onCheckedChange={(checked) =>
|
||||
setProfile({ ...profile, notifications_email: checked })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<Label>Push Notifications</Label>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Receive push notifications in browser
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={profile.notifications_push}
|
||||
onCheckedChange={(checked) =>
|
||||
setProfile({ ...profile, notifications_push: checked })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<Label>SMS Notifications</Label>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Receive notifications via SMS
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={profile.notifications_sms}
|
||||
onCheckedChange={(checked) =>
|
||||
setProfile({ ...profile, notifications_sms: checked })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<Button onClick={updateNotificationSettings} disabled={loading}>
|
||||
Save Preferences
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
<TabsContent value="security">
|
||||
<div className="space-y-4">
|
||||
<TwoFactorSetup />
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Password Security</CardTitle>
|
||||
<CardDescription>
|
||||
Manage your password settings
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<Button variant="outline" onClick={changePassword}>
|
||||
Change Password
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="preferences">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>App Preferences</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div>
|
||||
<Label>Language</Label>
|
||||
<Select
|
||||
value={profile.language}
|
||||
onValueChange={(value) => setProfile({ ...profile, language: value })}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="en">English</SelectItem>
|
||||
<SelectItem value="tr">Türkçe</SelectItem>
|
||||
<SelectItem value="ar">العربية</SelectItem>
|
||||
<SelectItem value="kmr">Kurdî</SelectItem>
|
||||
<SelectItem value="ckb">کوردی</SelectItem>
|
||||
<SelectItem value="fa">فارسی</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div>
|
||||
<Label>Theme</Label>
|
||||
<Select
|
||||
value={profile.theme}
|
||||
onValueChange={(value) => setProfile({ ...profile, theme: value })}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="light">Light</SelectItem>
|
||||
<SelectItem value="dark">Dark</SelectItem>
|
||||
<SelectItem value="system">System</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<Button onClick={updateProfile} disabled={loading}>
|
||||
Save Preferences
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user