NAM-APJATEL/src/pages/users/UserFormPage.tsx

206 lines
6.6 KiB
TypeScript

import { useEffect, useState } from 'react';
import { useNavigate, useParams } from 'react-router';
import { useCreateUser, useUpdateUser, useUser, UserFormData } from '../../features/users/api/users';
import { extractApiErrors } from '../../lib/api-client';
import axios from 'axios';
const UserFormPage = () => {
const { id } = useParams();
const navigate = useNavigate();
const isEditing = !!id;
// Query dan mutations
const { data: user, isLoading: isLoadingUser } = useUser(id || '');
const createUser = useCreateUser();
const updateUser = useUpdateUser();
// Form state
const [formData, setFormData] = useState<UserFormData>({
name: '',
email: '',
password: '',
role: 'user', // Default role
});
const [errors, setErrors] = useState<Record<string, string>>({});
// Isi form dengan data user jika mode edit
useEffect(() => {
if (isEditing && user) {
setFormData({
name: user.name,
email: user.email,
role: user.role,
// Password kosong saat edit
});
}
}, [isEditing, user]);
const handleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>
) => {
const { name, value } = e.target;
setFormData((prev) => ({
...prev,
[name]: value,
}));
// Hapus error untuk field yang sedang diubah
if (errors[name]) {
setErrors((prev) => {
const newErrors = { ...prev };
delete newErrors[name];
return newErrors;
});
}
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
try {
if (isEditing && id) {
// Jika password kosong, hapus dari data yang dikirim
const dataToSubmit = { ...formData };
if (!dataToSubmit.password) {
delete dataToSubmit.password;
}
await updateUser.mutateAsync({ id, data: dataToSubmit });
} else {
await createUser.mutateAsync(formData);
}
navigate('/users');
} catch (error) {
if (axios.isAxiosError(error) && error.response?.status === 400) {
const apiErrors = error.response.data.status.errors;
setErrors(apiErrors || {});
} else {
const extractedErrors = extractApiErrors(error);
setErrors(extractedErrors);
}
}
};
if (isEditing && isLoadingUser) {
return (
<div className="flex justify-center items-center h-64">
<span className="loading loading-spinner loading-lg"></span>
</div>
);
}
return (
<div className="container mx-auto p-4">
<div className="card bg-base-100 shadow-xl">
<div className="card-body">
<h2 className="card-title text-2xl mb-6">
{isEditing ? 'Edit Pengguna' : 'Tambah Pengguna'}
</h2>
<form onSubmit={handleSubmit}>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="form-control">
<label className="label">
<span className="label-text">Nama</span>
</label>
<input
type="text"
name="name"
value={formData.name}
onChange={handleChange}
className={`input input-bordered ${errors.name ? 'input-error' : ''}`}
/>
{errors.name && (
<label className="label">
<span className="label-text-alt text-error">{errors.name}</span>
</label>
)}
</div>
<div className="form-control">
<label className="label">
<span className="label-text">Email</span>
</label>
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
className={`input input-bordered ${errors.email ? 'input-error' : ''}`}
/>
{errors.email && (
<label className="label">
<span className="label-text-alt text-error">{errors.email}</span>
</label>
)}
</div>
<div className="form-control">
<label className="label">
<span className="label-text">Password {isEditing && '(kosongkan jika tidak ingin mengubah)'}</span>
</label>
<input
type="password"
name="password"
value={formData.password || ''}
onChange={handleChange}
className={`input input-bordered ${errors.password ? 'input-error' : ''}`}
/>
{errors.password && (
<label className="label">
<span className="label-text-alt text-error">{errors.password}</span>
</label>
)}
</div>
<div className="form-control">
<label className="label">
<span className="label-text">Role</span>
</label>
<select
name="role"
value={formData.role}
onChange={handleChange}
className={`select select-bordered ${errors.role ? 'select-error' : ''}`}
>
<option value="user">User</option>
<option value="admin">Admin</option>
</select>
{errors.role && (
<label className="label">
<span className="label-text-alt text-error">{errors.role}</span>
</label>
)}
</div>
</div>
<div className="card-actions justify-end mt-6">
<button
type="button"
className="btn btn-outline"
onClick={() => navigate('/users')}
>
Batal
</button>
<button
type="submit"
className="btn btn-primary"
disabled={createUser.isPending || updateUser.isPending}
>
{(createUser.isPending || updateUser.isPending) ? (
<span className="loading loading-spinner loading-sm"></span>
) : (
isEditing ? 'Update' : 'Simpan'
)}
</button>
</div>
</form>
</div>
</div>
</div>
);
};
export default UserFormPage;