217 lines
6.5 KiB
TypeScript
217 lines
6.5 KiB
TypeScript
import { useState, useEffect } from 'react';
|
||
import { Form } from '@remix-run/react';
|
||
import { Input, Button, Select, Text } from '~/components/ui';
|
||
import { validateUser } from '~/lib/validation';
|
||
import { AUTH_LEVELS, USER_STATUS } from '~/types/auth';
|
||
import type { UserWithoutPassword } from '~/types/database';
|
||
|
||
interface UserFormProps {
|
||
user?: UserWithoutPassword;
|
||
onSubmit: (data: FormData) => void;
|
||
onCancel: () => void;
|
||
loading?: boolean;
|
||
currentUserAuthLevel: number;
|
||
}
|
||
|
||
export function UserForm({
|
||
user,
|
||
onSubmit,
|
||
onCancel,
|
||
loading = false,
|
||
currentUserAuthLevel,
|
||
}: UserFormProps) {
|
||
const [formData, setFormData] = useState({
|
||
name: user?.name || '',
|
||
username: user?.username || '',
|
||
email: user?.email || '',
|
||
password: '',
|
||
confirmPassword: '',
|
||
authLevel: user?.authLevel || AUTH_LEVELS.USER,
|
||
status: user?.status || USER_STATUS.ACTIVE,
|
||
});
|
||
|
||
const [errors, setErrors] = useState<Record<string, string>>({});
|
||
const [touched, setTouched] = useState<Record<string, boolean>>({});
|
||
|
||
const isEditing = !!user;
|
||
|
||
// Validate form data
|
||
useEffect(() => {
|
||
const validationData = {
|
||
name: formData.name,
|
||
username: formData.username,
|
||
email: formData.email,
|
||
authLevel: formData.authLevel,
|
||
status: formData.status,
|
||
};
|
||
|
||
// Only validate password for new users or when password is provided
|
||
if (!isEditing || formData.password) {
|
||
validationData.password = formData.password;
|
||
}
|
||
|
||
const validation = validateUser(validationData);
|
||
|
||
// Add confirm password validation
|
||
const newErrors = { ...validation.errors };
|
||
if (!isEditing || formData.password) {
|
||
if (formData.password !== formData.confirmPassword) {
|
||
newErrors.confirmPassword = 'كلمات المرور غير متطابقة';
|
||
}
|
||
}
|
||
|
||
setErrors(newErrors);
|
||
}, [formData, isEditing]);
|
||
|
||
const handleInputChange = (field: string, value: string | number) => {
|
||
setFormData(prev => ({ ...prev, [field]: value }));
|
||
setTouched(prev => ({ ...prev, [field]: true }));
|
||
};
|
||
|
||
const handleSubmit = (e: React.FormEvent) => {
|
||
e.preventDefault();
|
||
|
||
// Mark all fields as touched
|
||
const allFields = Object.keys(formData);
|
||
setTouched(allFields.reduce((acc, field) => ({ ...acc, [field]: true }), {}));
|
||
|
||
// Check if form is valid
|
||
if (Object.keys(errors).length > 0) {
|
||
return;
|
||
}
|
||
|
||
// Create FormData
|
||
const submitData = new FormData();
|
||
submitData.append('name', formData.name);
|
||
submitData.append('username', formData.username);
|
||
submitData.append('email', formData.email);
|
||
submitData.append('authLevel', formData.authLevel.toString());
|
||
submitData.append('status', formData.status);
|
||
|
||
if (!isEditing || formData.password) {
|
||
submitData.append('password', formData.password);
|
||
}
|
||
|
||
if (isEditing) {
|
||
submitData.append('userId', user.id.toString());
|
||
submitData.append('_action', 'update');
|
||
} else {
|
||
submitData.append('_action', 'create');
|
||
}
|
||
|
||
onSubmit(submitData);
|
||
};
|
||
|
||
// Get available auth levels based on current user's level
|
||
const getAuthLevelOptions = () => {
|
||
const options = [];
|
||
|
||
if (currentUserAuthLevel === AUTH_LEVELS.SUPERADMIN) {
|
||
options.push({ value: AUTH_LEVELS.SUPERADMIN, label: 'مدير عام' });
|
||
}
|
||
|
||
if (currentUserAuthLevel <= AUTH_LEVELS.ADMIN) {
|
||
options.push({ value: AUTH_LEVELS.ADMIN, label: 'مدير' });
|
||
}
|
||
|
||
options.push({ value: AUTH_LEVELS.USER, label: 'مستخدم' });
|
||
|
||
return options;
|
||
};
|
||
|
||
const statusOptions = [
|
||
{ value: USER_STATUS.ACTIVE, label: 'نشط' },
|
||
{ value: USER_STATUS.INACTIVE, label: 'غير نشط' },
|
||
];
|
||
|
||
return (
|
||
<Form onSubmit={handleSubmit} className="space-y-4">
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
<Input
|
||
label="الاسم"
|
||
value={formData.name}
|
||
onChange={(e) => handleInputChange('name', e.target.value)}
|
||
error={touched.name ? errors.name : undefined}
|
||
required
|
||
/>
|
||
|
||
<Input
|
||
label="اسم المستخدم"
|
||
value={formData.username}
|
||
onChange={(e) => handleInputChange('username', e.target.value)}
|
||
error={touched.username ? errors.username : undefined}
|
||
required
|
||
/>
|
||
</div>
|
||
|
||
<Input
|
||
label="البريد الإلكتروني"
|
||
type="email"
|
||
value={formData.email}
|
||
onChange={(e) => handleInputChange('email', e.target.value)}
|
||
error={touched.email ? errors.email : undefined}
|
||
required
|
||
/>
|
||
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
<Input
|
||
label={isEditing ? "كلمة المرور الجديدة (اختياري)" : "كلمة المرور"}
|
||
type="password"
|
||
value={formData.password}
|
||
onChange={(e) => handleInputChange('password', e.target.value)}
|
||
error={touched.password ? errors.password : undefined}
|
||
required={!isEditing}
|
||
/>
|
||
|
||
<Input
|
||
label="تأكيد كلمة المرور"
|
||
type="password"
|
||
value={formData.confirmPassword}
|
||
onChange={(e) => handleInputChange('confirmPassword', e.target.value)}
|
||
error={touched.confirmPassword ? errors.confirmPassword : undefined}
|
||
required={!isEditing || !!formData.password}
|
||
/>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
<Select
|
||
label="مستوى الصلاحية"
|
||
value={formData.authLevel}
|
||
onChange={(e) => handleInputChange('authLevel', parseInt(e.target.value))}
|
||
options={getAuthLevelOptions()}
|
||
error={touched.authLevel ? errors.authLevel : undefined}
|
||
required
|
||
/>
|
||
|
||
<Select
|
||
label="الحالة"
|
||
value={formData.status}
|
||
onChange={(e) => handleInputChange('status', e.target.value)}
|
||
options={statusOptions}
|
||
error={touched.status ? errors.status : undefined}
|
||
required
|
||
/>
|
||
</div>
|
||
|
||
<div className="flex justify-start space-x-3 space-x-reverse pt-4">
|
||
|
||
<Button
|
||
type="submit"
|
||
loading={loading}
|
||
disabled={Object.keys(errors).length > 0}
|
||
>
|
||
{isEditing ? 'تحديث المستخدم' : 'إنشاء المستخدم'}
|
||
</Button>
|
||
|
||
<Button
|
||
type="button"
|
||
variant="outline"
|
||
onClick={onCancel}
|
||
disabled={loading}
|
||
>
|
||
إلغاء
|
||
</Button>
|
||
</div>
|
||
</Form>
|
||
);
|
||
} |