Quran_Tech_Server / F_Pro /src /components /auth /CompleteProfile.tsx
aboalaa147's picture
Initial deployment
eb6a2f9
Raw
History Blame Contribute Delete
21.3 kB
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { Button } from '../ui/button';
import { Input } from '../ui/input';
import { Label } from '../ui/label';
import { Textarea } from '../ui/textarea';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../ui/card';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../ui/select';
import { Alert, AlertDescription } from '../ui/alert';
import { CheckCircle2, User, DollarSign, Award, FileText } from 'lucide-react';
import { useAuth } from '../../lib/authContext';
import { useLanguage } from '../../lib/languageContext';
import { LanguageSwitcher } from '../LanguageSwitcher';
import { getApiBaseUrl } from '../../lib/api/config';
export function CompleteProfile() {
const { user, updateProfile } = useAuth();
const { language } = useLanguage();
const navigate = useNavigate();
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [submitted, setSubmitted] = useState(false);
const parseErrorResponse = async (res: Response) => {
let errorMsg = language === 'ar' ? 'فشل الإرسال' : 'Submission failed';
try {
const json = await res.clone().json();
if (json?.message) return json.message;
if (json?.error) return json.error;
if (Array.isArray(json?.errors) && json.errors.length > 0) {
return json.errors.map((err: any) => err.defaultMessage || err.message || String(err)).join(', ');
}
if (json?.errors) return JSON.stringify(json.errors);
} catch {
// ignore invalid JSON
}
try {
const text = await res.text();
if (text) return text;
} catch {
// ignore text read errors
}
return errorMsg;
};
// Student fields
const [quranLevel, setQuranLevel] = useState('');
const [preferredLanguage, setPreferredLanguage] = useState('');
const [preferredAccent, setPreferredAccent] = useState('');
const [profilePhotoUrl, setProfilePhotoUrl] = useState('');
// Sheikh fields
const [bio, setBio] = useState('');
const [specialization, setSpecialization] = useState('');
const [yearsOfExperience, setYearsOfExperience] = useState('');
const [hourlyRate, setHourlyRate] = useState('');
const [certificateUrl, setCertificateUrl] = useState('');
const [sheikhProfilePhotoUrl, setSheikhProfilePhotoUrl] = useState('');
// const t = translations[language].auth;
const isRTL = language === 'ar';
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
setError(null);
try {
const token = localStorage.getItem('authToken');
if (!token) {
throw new Error(
language === 'ar'
? 'لا يوجد رمز مصادقة'
: 'No authentication token found'
);
}
const BASE_URL = getApiBaseUrl();
if (user?.role === 'student') {
// Validate student fields
if (!quranLevel || !preferredLanguage || !preferredAccent) {
setError(
language === 'ar'
? 'يرجى ملء جميع الحقول المطلوبة'
: 'Please fill in all required fields'
);
setLoading(false);
return;
}
const payload = {
quranLevel,
preferredLanguage,
preferredAccent,
profilePhotoUrl: profilePhotoUrl || ''
};
const res = await fetch(`${BASE_URL}/api/student/profile/complete`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify(payload),
});
if (!res.ok) {
const errorMsg = await parseErrorResponse(res);
throw new Error(errorMsg);
}
// Update context and navigate
updateProfile({ profileCompleted: true });
navigate('/student/dashboard');
} else if (user?.role === 'sheikh') {
// Validate sheikh fields
if (!bio || !specialization || !yearsOfExperience || !hourlyRate) {
setError(
language === 'ar'
? 'يرجى ملء جميع الحقول المطلوبة (السيرة الذاتية، التخصص، سنوات الخبرة، السعر بالساعة)'
: 'Please fill in all required fields (Bio, Specialization, Years of Experience, Hourly Rate)'
);
setLoading(false);
return;
}
const payload = {
bio,
specialization,
yearsOfExperience: parseInt(yearsOfExperience),
hourlyRate: parseFloat(hourlyRate),
certificateUrl: certificateUrl || '',
profilePhotoUrl: sheikhProfilePhotoUrl || ''
};
const res = await fetch(`${BASE_URL}/api/sheikh/profile/complete`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify(payload),
});
if (!res.ok) {
const errorMsg = await parseErrorResponse(res);
throw new Error(errorMsg);
}
// Show success screen for sheikh
setSubmitted(true);
}
} catch (err: any) {
console.error('Profile submission error:', err);
setError(err.message || (
language === 'ar'
? 'حدث خطأ أثناء الإرسال. حاول مرة أخرى.'
: 'Error submitting profile. Please try again.'
));
} finally {
setLoading(false);
}
};
// Success screen for sheikh
if (user?.role === 'sheikh' && submitted) {
return (
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-emerald-50 to-teal-50 p-4">
<div className="absolute top-4 right-4 z-10">
<LanguageSwitcher />
</div>
<Card className={`w-full max-w-md ${isRTL ? 'rtl' : 'ltr'}`} dir={isRTL ? 'rtl' : 'ltr'}>
<CardHeader className="text-center">
<div className="flex justify-center mb-4">
<div className="w-16 h-16 bg-emerald-600 rounded-full flex items-center justify-center">
<CheckCircle2 className="h-8 w-8 text-white" />
</div>
</div>
<CardTitle>
{language === 'ar' ? 'تم إرسال الطلب بنجاح!' : 'Application Submitted Successfully!'}
</CardTitle>
<CardDescription>
{language === 'ar'
? 'ملفك الشخصي قيد المراجعة من قبل فريق الإدارة'
: 'Your profile is under review by our admin team'}
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<Alert>
<AlertDescription className={isRTL ? 'text-right' : ''}>
{language === 'ar'
? 'تم إرسال طلبك وهو حاليًا في حالة الموافقة المعلقة. سيقوم فريق الإدارة بمراجعة بياناتك خلال 3-5 أيام عمل. سوف تتلقى إشعارًا عبر البريد الإلكتروني بمجرد الموافقة على طلبك.'
: 'Your application has been submitted and is currently in PENDING approval status. Our admin team will review your credentials within 3-5 business days. You will receive an email notification once your application is approved.'}
</AlertDescription>
</Alert>
<Button
onClick={() => {
updateProfile({ profileCompleted: true });
navigate('/sheikh/interview');
}}
className="w-full bg-emerald-600 hover:bg-emerald-700"
>
{language === 'ar' ? 'الانتقال إلى صفحة الانتظار' : 'Go to Waiting Page'}
</Button>
</CardContent>
</Card>
</div>
);
}
// Main form
return (
<div className="min-h-screen bg-gradient-to-br from-emerald-50 to-teal-50 p-4 py-8">
<div className="absolute top-4 right-4 z-10">
<LanguageSwitcher />
</div>
<Card className={`w-full max-w-2xl mx-auto ${isRTL ? 'rtl' : 'ltr'}`} dir={isRTL ? 'rtl' : 'ltr'}>
<CardHeader>
<CardTitle className={isRTL ? 'text-right' : ''}>
{language === 'ar' ? 'أكمل ملفك الشخصي' : 'Complete Your Profile'}
</CardTitle>
<CardDescription className={isRTL ? 'text-right' : ''}>
{user?.role === 'student'
? (language === 'ar'
? 'أخبرنا عن نفسك لتخصيص تجربة التعلم الخاصة بك'
: 'Tell us about yourself to personalize your learning experience')
: (language === 'ar'
? 'قدم بياناتك وقم برفع الملفات المطلوبة لبدء التدريس'
: 'Provide your credentials and upload required files to start teaching')}
</CardDescription>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit} className="space-y-6">
{/* STUDENT FIELDS */}
{user?.role === 'student' ? (
<>
{/* Quran Level */}
<div className="space-y-2">
<Label htmlFor="quranLevel" className={isRTL ? 'text-right block' : ''}>
{language === 'ar' ? 'مستوى القرآن' : 'Quran Level'}
<span className="text-red-500 ml-1">*</span>
</Label>
<Select value={quranLevel} onValueChange={setQuranLevel} required dir={isRTL ? 'rtl' : 'ltr'}>
<SelectTrigger className={isRTL ? 'text-right' : ''}>
<SelectValue placeholder={language === 'ar' ? 'اختر مستواك' : 'Select your Quran level'} />
</SelectTrigger>
<SelectContent>
<SelectItem value="beginner">
{language === 'ar' ? 'مبتدئ' : 'Beginner'}
</SelectItem>
<SelectItem value="intermediate">
{language === 'ar' ? 'متوسط' : 'Intermediate'}
</SelectItem>
<SelectItem value="advanced">
{language === 'ar' ? 'متقدم' : 'Advanced'}
</SelectItem>
</SelectContent>
</Select>
</div>
{/* Preferred Language */}
<div className="space-y-2">
<Label htmlFor="language" className={isRTL ? 'text-right block' : ''}>
{language === 'ar' ? 'اللغة المفضلة' : 'Preferred Language'}
<span className="text-red-500 ml-1">*</span>
</Label>
<Select value={preferredLanguage} onValueChange={setPreferredLanguage} required dir={isRTL ? 'rtl' : 'ltr'}>
<SelectTrigger className={isRTL ? 'text-right' : ''}>
<SelectValue placeholder={language === 'ar' ? 'اختر اللغة' : 'Select language'} />
</SelectTrigger>
<SelectContent>
<SelectItem value="arabic">{language === 'ar' ? 'العربية' : 'Arabic'}</SelectItem>
<SelectItem value="english">{language === 'ar' ? 'الإنجليزية' : 'English'}</SelectItem>
<SelectItem value="urdu">{language === 'ar' ? 'الأردية' : 'Urdu'}</SelectItem>
<SelectItem value="french">{language === 'ar' ? 'الفرنسية' : 'French'}</SelectItem>
</SelectContent>
</Select>
</div>
{/* Preferred Accent */}
<div className="space-y-2">
<Label htmlFor="accent" className={isRTL ? 'text-right block' : ''}>
{language === 'ar' ? 'اللهجة المفضلة' : 'Preferred Accent'}
<span className="text-red-500 ml-1">*</span>
</Label>
<Select value={preferredAccent} onValueChange={setPreferredAccent} required dir={isRTL ? 'rtl' : 'ltr'}>
<SelectTrigger className={isRTL ? 'text-right' : ''}>
<SelectValue placeholder={language === 'ar' ? 'اختر اللهجة' : 'Select accent'} />
</SelectTrigger>
<SelectContent>
<SelectItem value="egyptian">{language === 'ar' ? 'مصرية' : 'Egyptian'}</SelectItem>
<SelectItem value="saudi">{language === 'ar' ? 'سعودية (حجازية)' : 'Saudi (Hijazi)'}</SelectItem>
<SelectItem value="levantine">{language === 'ar' ? 'شامية' : 'Levantine'}</SelectItem>
<SelectItem value="maghrebi">{language === 'ar' ? 'مغربية' : 'Maghrebi'}</SelectItem>
</SelectContent>
</Select>
</div>
{/* Profile Photo URL */}
<div className="space-y-2">
<Label htmlFor="profilePhotoUrl" className={isRTL ? 'text-right block' : ''}>
{language === 'ar' ? 'رابط الصورة الشخصية (اختياري)' : 'Profile Photo URL (optional)'}
</Label>
<div className="relative">
<User className={`absolute top-3 h-4 w-4 text-muted-foreground ${isRTL ? 'right-3' : 'left-3'}`} />
<Input
id="profilePhotoUrl"
type="url"
placeholder="https://example.com/photo.jpg"
value={profilePhotoUrl}
onChange={(e) => setProfilePhotoUrl(e.target.value)}
className={isRTL ? 'pr-10 text-right' : 'pl-10'}
dir="ltr"
/>
</div>
</div>
</>
) : (
/* SHEIKH FIELDS */
<>
{/* Specialization */}
<div className="space-y-2">
<Label htmlFor="specialization" className={isRTL ? 'text-right block' : ''}>
{language === 'ar' ? 'التخصص' : 'Specialization'}
<span className="text-red-500 ml-1">*</span>
</Label>
<div className="relative">
<Award className={`absolute top-3 h-4 w-4 text-muted-foreground ${isRTL ? 'right-3' : 'left-3'}`} />
<Input
id="specialization"
placeholder={language === 'ar' ? 'مثال: تلاوة القرآن، التجويد، الحفظ' : 'e.g., Quranic Recitation, Tajweed, Memorization'}
value={specialization}
onChange={(e) => setSpecialization(e.target.value)}
className={isRTL ? 'pr-10 text-right' : 'pl-10'}
required
/>
</div>
</div>
{/* Years of Experience */}
<div className="space-y-2">
<Label htmlFor="yearsOfExperience" className={isRTL ? 'text-right block' : ''}>
{language === 'ar' ? 'سنوات الخبرة' : 'Years of Experience'}
<span className="text-red-500 ml-1">*</span>
</Label>
<Input
id="yearsOfExperience"
type="number"
min="0"
placeholder={language === 'ar' ? 'مثال: 10' : 'e.g., 10'}
value={yearsOfExperience}
onChange={(e) => setYearsOfExperience(e.target.value)}
className={isRTL ? 'text-right' : ''}
required
/>
</div>
{/* Hourly Rate */}
<div className="space-y-2">
<Label htmlFor="hourlyRate" className={isRTL ? 'text-right block' : ''}>
{language === 'ar' ? 'السعر بالساعة ($)' : 'Hourly Rate ($)'}
<span className="text-red-500 ml-1">*</span>
</Label>
<div className="relative">
<DollarSign className={`absolute top-3 h-4 w-4 text-muted-foreground ${isRTL ? 'right-3' : 'left-3'}`} />
<Input
id="hourlyRate"
type="number"
step="0.01"
min="0"
placeholder={language === 'ar' ? 'مثال: 25.00' : 'e.g., 25.00'}
value={hourlyRate}
onChange={(e) => setHourlyRate(e.target.value)}
className={isRTL ? 'pr-10 text-right' : 'pl-10'}
dir="ltr"
required
/>
</div>
</div>
{/* Bio */}
<div className="space-y-2">
<Label htmlFor="bio" className={isRTL ? 'text-right block' : ''}>
{language === 'ar' ? 'السيرة الذاتية / نبذة عنك' : 'Bio / About You'}
<span className="text-red-500 ml-1">*</span>
</Label>
<div className="relative">
<FileText className={`absolute top-3 h-4 w-4 text-muted-foreground ${isRTL ? 'right-3' : 'left-3'}`} />
<Textarea
id="bio"
placeholder={language === 'ar'
? 'أخبر الطلاب عن نفسك، منهجك التعليمي، ومؤهلاتك...'
: 'Tell students about yourself, your teaching approach, and qualifications...'}
value={bio}
onChange={(e) => setBio(e.target.value)}
className={`min-h-24 ${isRTL ? 'pr-10 text-right' : 'pl-10'}`}
required
/>
</div>
</div>
{/* Certificate URL */}
<div className="space-y-2">
<Label htmlFor="certificateUrl" className={isRTL ? 'text-right block' : ''}>
{language === 'ar' ? 'رابط الشهادة (اختياري)' : 'Certificate URL (optional)'}
</Label>
<Input
id="certificateUrl"
type="url"
placeholder="https://example.com/certificate.pdf"
value={certificateUrl}
onChange={(e) => setCertificateUrl(e.target.value)}
className={isRTL ? 'text-right' : ''}
dir="ltr"
/>
</div>
{/* Profile Photo URL */}
<div className="space-y-2">
<Label htmlFor="sheikhProfilePhotoUrl" className={isRTL ? 'text-right block' : ''}>
{language === 'ar' ? 'رابط الصورة الشخصية (اختياري)' : 'Profile Photo URL (optional)'}
</Label>
<div className="relative">
<User className={`absolute top-3 h-4 w-4 text-muted-foreground ${isRTL ? 'right-3' : 'left-3'}`} />
<Input
id="sheikhProfilePhotoUrl"
type="url"
placeholder="https://example.com/photo.jpg"
value={sheikhProfilePhotoUrl}
onChange={(e) => setSheikhProfilePhotoUrl(e.target.value)}
className={isRTL ? 'pr-10 text-right' : 'pl-10'}
dir="ltr"
/>
</div>
</div>
</>
)}
{/* Error Message */}
{error && (
<div className="bg-red-50 border border-red-200 rounded-lg p-3">
<p className={`text-red-800 text-sm ${isRTL ? 'text-right' : ''}`}>
{error}
</p>
</div>
)}
{/* Submit Button */}
<Button
type="submit"
className="w-full bg-emerald-600 hover:bg-emerald-700"
disabled={loading}
>
{loading ? (
<span className="flex items-center gap-2">
<span className="animate-spin"></span>
{language === 'ar' ? 'جاري الإرسال...' : 'Submitting...'}
</span>
) : (
language === 'ar' ? 'إكمال الملف الشخصي' : 'Complete Profile'
)}
</Button>
</form>
</CardContent>
</Card>
</div>
);
}