144 lines
5.4 KiB
TypeScript
144 lines
5.4 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
import { supabase } from '@/integrations/supabase/client';
|
|
import { toast } from 'sonner';
|
|
import { T, translate } from '@/i18n';
|
|
import { Tables } from '@/integrations/supabase/types';
|
|
import { getUserSecrets, updateUserSecrets } from '@/components/ImageWizard/db';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
|
import { Input } from '@/components/ui/input';
|
|
import { Label } from '@/components/ui/label';
|
|
import { Textarea } from '@/components/ui/textarea';
|
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
|
import { Save } from 'lucide-react';
|
|
|
|
type Profile = Tables<'profiles'>;
|
|
|
|
interface EditUserDialogProps {
|
|
user: Profile | null;
|
|
open: boolean;
|
|
onOpenChange: (open: boolean) => void;
|
|
onUserUpdate: (updatedUser: Profile) => void;
|
|
}
|
|
|
|
export const EditUserDialog = ({ user, open, onOpenChange, onUserUpdate }: EditUserDialogProps) => {
|
|
const [profile, setProfile] = useState<Profile | null>(null);
|
|
const [secrets, setSecrets] = useState<Record<string, string>>({});
|
|
const [updating, setUpdating] = useState(false);
|
|
|
|
useEffect(() => {
|
|
if (user) {
|
|
setProfile(user);
|
|
// Fetch secrets
|
|
getUserSecrets(user.user_id).then(data => {
|
|
if (data) setSecrets(data);
|
|
else setSecrets({});
|
|
});
|
|
}
|
|
}, [user]);
|
|
|
|
const handleUpdate = async () => {
|
|
if (!profile) return;
|
|
|
|
setUpdating(true);
|
|
try {
|
|
// 1. Update profile (non-secret fields)
|
|
const { data, error } = await supabase
|
|
.from('profiles')
|
|
.update({
|
|
username: profile.username,
|
|
display_name: profile.display_name,
|
|
bio: profile.bio,
|
|
})
|
|
.eq('id', profile.id)
|
|
.select()
|
|
.single();
|
|
|
|
if (error) throw error;
|
|
|
|
// 2. Update secrets
|
|
if (profile.user_id) {
|
|
await updateUserSecrets(profile.user_id, secrets);
|
|
}
|
|
|
|
toast.success(translate('User profile updated successfully'));
|
|
onUserUpdate(data);
|
|
onOpenChange(false);
|
|
} catch (error) {
|
|
console.error('Error updating user profile:', error);
|
|
toast.error(translate('Failed to update user profile'));
|
|
} finally {
|
|
setUpdating(false);
|
|
}
|
|
};
|
|
|
|
if (!profile) return null;
|
|
|
|
return (
|
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
<DialogContent className="max-w-2xl">
|
|
<DialogHeader>
|
|
<DialogTitle>Edit User: {profile.display_name || profile.username}</DialogTitle>
|
|
</DialogHeader>
|
|
<Tabs defaultValue="general">
|
|
<TabsList>
|
|
<TabsTrigger value="general">General</TabsTrigger>
|
|
<TabsTrigger value="api-keys">API Keys</TabsTrigger>
|
|
</TabsList>
|
|
<TabsContent value="general" className="space-y-4 py-4">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="username">Username</Label>
|
|
<Input
|
|
id="username"
|
|
value={profile.username || ''}
|
|
onChange={(e) => setProfile({ ...profile, username: e.target.value })}
|
|
/>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label htmlFor="display_name">Display Name</Label>
|
|
<Input
|
|
id="display_name"
|
|
value={profile.display_name || ''}
|
|
onChange={(e) => setProfile({ ...profile, display_name: e.target.value })}
|
|
/>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label htmlFor="bio">Bio</Label>
|
|
<Textarea
|
|
id="bio"
|
|
value={profile.bio || ''}
|
|
onChange={(e) => setProfile({ ...profile, bio: e.target.value })}
|
|
/>
|
|
</div>
|
|
</TabsContent>
|
|
<TabsContent value="api-keys" className="space-y-4 py-4">
|
|
<ApiKeyInput id="google_api_key" label="Google API Key" secrets={secrets} setSecrets={setSecrets} />
|
|
<ApiKeyInput id="openai_api_key" label="OpenAI API Key" secrets={secrets} setSecrets={setSecrets} />
|
|
<ApiKeyInput id="replicate_api_key" label="Replicate API Key" secrets={secrets} setSecrets={setSecrets} />
|
|
<ApiKeyInput id="bria_api_key" label="Bria API Key" secrets={secrets} setSecrets={setSecrets} />
|
|
<ApiKeyInput id="huggingface_api_key" label="HuggingFace API Key" secrets={secrets} setSecrets={setSecrets} />
|
|
<ApiKeyInput id="aimlapi_api_key" label="AIMLAPI API Key" secrets={secrets} setSecrets={setSecrets} />
|
|
</TabsContent>
|
|
</Tabs>
|
|
<Button onClick={handleUpdate} disabled={updating}>
|
|
<Save className="mr-2 h-4 w-4" />
|
|
{updating ? 'Saving...' : 'Save Changes'}
|
|
</Button>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
};
|
|
|
|
const ApiKeyInput = ({ id, label, secrets, setSecrets }: { id: string; label: string; secrets: Record<string, string>; setSecrets: (s: Record<string, string>) => void; }) => (
|
|
<div className="space-y-2">
|
|
<Label htmlFor={id}>{label}</Label>
|
|
<Input
|
|
id={id}
|
|
type="password"
|
|
value={secrets[id] || ''}
|
|
onChange={(e) => setSecrets({ ...secrets, [id]: e.target.value })}
|
|
placeholder={`Enter ${label}`}
|
|
/>
|
|
</div>
|
|
);
|