car_mms/app/components/users/UserForm.tsx
2025-09-11 14:22:27 +03:00

217 lines
6.5 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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