mono/packages/ui/src/components/admin/EditUserDialog.tsx
2026-01-20 10:34:09 +01:00

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>
);