mirror of
https://github.com/Motion-Games/MotionWebStudio.git
synced 2026-04-21 09:00:53 +02:00
250 lines
9.3 KiB
TypeScript
250 lines
9.3 KiB
TypeScript
import React, { useEffect, useState } from 'react';
|
|
import { useAuth } from '../context/AuthContext';
|
|
import { supabase, isSupabaseConfigured } from '../lib/supabaseClient';
|
|
import { Button } from './Button';
|
|
import { User, Save, AlertCircle, Calendar } from 'lucide-react';
|
|
|
|
export const ProfileCompleter: React.FC = () => {
|
|
const { user, refreshDemoUser } = useAuth();
|
|
const [isOpen, setIsOpen] = useState(false);
|
|
const [firstName, setFirstName] = useState('');
|
|
const [lastName, setLastName] = useState('');
|
|
const [dateOfBirth, setDateOfBirth] = useState('');
|
|
const [loading, setLoading] = useState(false);
|
|
const [checking, setChecking] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
if (!user) {
|
|
setIsOpen(false);
|
|
return;
|
|
}
|
|
|
|
const checkProfile = async () => {
|
|
setChecking(true);
|
|
|
|
// --- DEMO MODE ---
|
|
if (!isSupabaseConfigured) {
|
|
const meta = user.user_metadata || {};
|
|
if (!meta.first_name || !meta.last_name || !meta.date_of_birth) {
|
|
setIsOpen(true);
|
|
if (meta.first_name) setFirstName(meta.first_name);
|
|
if (meta.last_name) setLastName(meta.last_name);
|
|
if (meta.date_of_birth) setDateOfBirth(meta.date_of_birth);
|
|
}
|
|
setChecking(false);
|
|
return;
|
|
}
|
|
// -----------------
|
|
|
|
try {
|
|
const { data, error } = await supabase
|
|
.from('profiles')
|
|
.select('first_name, last_name, date_of_birth')
|
|
.eq('id', user.id)
|
|
.maybeSingle();
|
|
|
|
if (error) {
|
|
console.error('Error checking profile:', error.message || error);
|
|
// If DB check fails, fallback to metadata to see if we should block the user.
|
|
// This prevents locking the user out if the DB is temporarily unavailable or misconfigured,
|
|
// provided they have the data in their auth metadata.
|
|
const meta = user.user_metadata || {};
|
|
const hasMetadata = meta.first_name && meta.last_name && meta.date_of_birth;
|
|
|
|
if (!hasMetadata) {
|
|
setIsOpen(true);
|
|
if (meta.first_name) setFirstName(meta.first_name);
|
|
if (meta.last_name) setLastName(meta.last_name);
|
|
if (meta.date_of_birth) setDateOfBirth(meta.date_of_birth);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// If no profile exists, or names/dob are missing
|
|
if (!data || !data.first_name || !data.last_name || !data.date_of_birth) {
|
|
setIsOpen(true);
|
|
// Pre-fill if partial data exists
|
|
if (data?.first_name) setFirstName(data.first_name);
|
|
else if (user.user_metadata?.first_name) setFirstName(user.user_metadata.first_name);
|
|
|
|
if (data?.last_name) setLastName(data.last_name);
|
|
else if (user.user_metadata?.last_name) setLastName(user.user_metadata.last_name);
|
|
|
|
if (data?.date_of_birth) setDateOfBirth(data.date_of_birth);
|
|
else if (user.user_metadata?.date_of_birth) setDateOfBirth(user.user_metadata.date_of_birth);
|
|
}
|
|
} catch (err: any) {
|
|
console.error('Unexpected error in ProfileCompleter:', err);
|
|
} finally {
|
|
setChecking(false);
|
|
}
|
|
};
|
|
|
|
checkProfile();
|
|
}, [user]);
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
setError(null);
|
|
setLoading(true);
|
|
|
|
if (!firstName.trim() || !lastName.trim() || !dateOfBirth) {
|
|
setError('Minden mező kitöltése kötelező.');
|
|
setLoading(false);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// --- DEMO MODE UPDATE ---
|
|
if (!isSupabaseConfigured && user) {
|
|
await new Promise(resolve => setTimeout(resolve, 800)); // Fake delay
|
|
|
|
// Update local storage session
|
|
const storedSession = localStorage.getItem('demo_user_session');
|
|
if (storedSession) {
|
|
const parsed = JSON.parse(storedSession);
|
|
parsed.user.user_metadata = {
|
|
...parsed.user.user_metadata,
|
|
first_name: firstName,
|
|
last_name: lastName,
|
|
date_of_birth: dateOfBirth
|
|
};
|
|
localStorage.setItem('demo_user_session', JSON.stringify(parsed));
|
|
refreshDemoUser(); // Refresh context
|
|
}
|
|
setIsOpen(false);
|
|
return;
|
|
}
|
|
// ------------------------
|
|
|
|
if (user) {
|
|
// 1. Update Profile Table
|
|
const { error: dbError } = await supabase
|
|
.from('profiles')
|
|
.upsert({
|
|
id: user.id,
|
|
email: user.email,
|
|
first_name: firstName,
|
|
last_name: lastName,
|
|
date_of_birth: dateOfBirth,
|
|
updated_at: new Date().toISOString()
|
|
});
|
|
|
|
if (dbError) throw dbError;
|
|
|
|
// 2. Update Auth Metadata (optional, but good for consistency)
|
|
await supabase.auth.updateUser({
|
|
data: {
|
|
first_name: firstName,
|
|
last_name: lastName,
|
|
date_of_birth: dateOfBirth
|
|
}
|
|
});
|
|
|
|
setIsOpen(false);
|
|
// We do not reload here to avoid infinite loops if checks fail on reload.
|
|
// The state close is enough.
|
|
}
|
|
} catch (err: any) {
|
|
console.error('Error updating profile:', err);
|
|
setError('Hiba történt a mentés során: ' + (err.message || 'Ismeretlen hiba'));
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
if (!isOpen) return null;
|
|
|
|
return (
|
|
<div className="fixed inset-0 z-[100] flex items-center justify-center p-4 bg-gray-900/70 backdrop-blur-sm">
|
|
<div className="bg-white rounded-2xl shadow-2xl w-full max-w-md overflow-hidden border border-gray-100 animate-fade-in-up">
|
|
<div className="bg-gradient-to-r from-primary to-secondary p-6 text-white text-center">
|
|
<div className="w-16 h-16 bg-white/20 rounded-full flex items-center justify-center mx-auto mb-4 backdrop-blur-md border border-white/30">
|
|
<User className="w-8 h-8 text-white" />
|
|
</div>
|
|
<h2 className="text-2xl font-bold">Hiányzó Adatok</h2>
|
|
<p className="text-blue-100 text-sm mt-2">
|
|
Kérjük, a folytatáshoz adja meg a hiányzó adatait.
|
|
</p>
|
|
</div>
|
|
|
|
<div className="p-8">
|
|
<form onSubmit={handleSubmit} className="space-y-6">
|
|
{error && (
|
|
<div className="bg-red-50 text-red-700 p-3 rounded-lg text-sm flex items-start">
|
|
<AlertCircle className="w-5 h-5 mr-2 flex-shrink-0" />
|
|
<span>{error}</span>
|
|
</div>
|
|
)}
|
|
|
|
<div className="space-y-4">
|
|
<div>
|
|
<label htmlFor="comp_lastname" className="block text-sm font-medium text-gray-700 mb-1">
|
|
Vezetéknév
|
|
</label>
|
|
<input
|
|
id="comp_lastname"
|
|
type="text"
|
|
value={lastName}
|
|
onChange={(e) => setLastName(e.target.value)}
|
|
className="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-primary focus:border-transparent outline-none transition-all bg-white text-gray-900"
|
|
placeholder="Kovács"
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label htmlFor="comp_firstname" className="block text-sm font-medium text-gray-700 mb-1">
|
|
Keresztnév
|
|
</label>
|
|
<input
|
|
id="comp_firstname"
|
|
type="text"
|
|
value={firstName}
|
|
onChange={(e) => setFirstName(e.target.value)}
|
|
className="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-primary focus:border-transparent outline-none transition-all bg-white text-gray-900"
|
|
placeholder="János"
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label htmlFor="comp_dob" className="block text-sm font-medium text-gray-700 mb-1">
|
|
Születési dátum
|
|
</label>
|
|
<div className="relative">
|
|
<input
|
|
id="comp_dob"
|
|
type="date"
|
|
value={dateOfBirth}
|
|
onChange={(e) => setDateOfBirth(e.target.value)}
|
|
className="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-primary focus:border-transparent outline-none transition-all bg-white text-gray-900"
|
|
required
|
|
/>
|
|
<div className="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
|
|
<Calendar className="h-5 w-5 text-gray-400" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="pt-2">
|
|
<Button type="submit" fullWidth disabled={loading} className="flex justify-center items-center">
|
|
{loading ? 'Mentés...' : (
|
|
<>
|
|
Adatok Mentése <Save className="ml-2 w-4 h-4" />
|
|
</>
|
|
)}
|
|
</Button>
|
|
</div>
|
|
|
|
<p className="text-xs text-center text-gray-500">
|
|
Ezekre az adatokra a számlázáshoz és a kapcsolattartáshoz van szükségünk.
|
|
</p>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}; |