Add frontend input validation for User username and Product name

This commit is contained in:
continuist 2025-06-24 23:21:16 -04:00
parent 9de4f0b64c
commit 9956c22cce
2 changed files with 99 additions and 10 deletions

View file

@ -40,6 +40,10 @@ export default function ProductsPage() {
name: '',
description: '',
});
const [errors, setErrors] = useState({
name: '',
description: '',
});
useEffect(() => {
loadProducts();
@ -54,8 +58,29 @@ export default function ProductsPage() {
}
};
const validateForm = () => {
const newErrors = {
name: '',
description: '',
};
// Product name cannot be empty
if (!formData.name.trim()) {
newErrors.name = 'Product name is required';
}
// Description can be empty, no validation needed
setErrors(newErrors);
return !newErrors.name;
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!validateForm()) {
return;
}
try {
if (editingProduct) {
await productApi.update(editingProduct.id, formData);
@ -65,6 +90,7 @@ export default function ProductsPage() {
setIsDialogOpen(false);
setEditingProduct(null);
setFormData({ name: '', description: '' });
setErrors({ name: '', description: '' });
loadProducts();
} catch (error) {
console.error('Error saving product:', error);
@ -77,6 +103,7 @@ export default function ProductsPage() {
name: product.name,
description: product.description,
});
setErrors({ name: '', description: '' });
setIsDialogOpen(true);
};
@ -91,6 +118,14 @@ export default function ProductsPage() {
}
};
const handleInputChange = (field: 'name' | 'description', value: string) => {
setFormData({ ...formData, [field]: value });
// Clear error when user starts typing
if (errors[field]) {
setErrors({ ...errors, [field]: '' });
}
};
return (
<div className="space-y-4">
<div className="flex justify-between items-center">
@ -100,6 +135,7 @@ export default function ProductsPage() {
<Button onClick={() => {
setEditingProduct(null);
setFormData({ name: '', description: '' });
setErrors({ name: '', description: '' });
}}>
Add Product
</Button>
@ -110,21 +146,23 @@ export default function ProductsPage() {
</DialogHeader>
<form onSubmit={handleSubmit} className="space-y-4">
<div className="space-y-2">
<Label htmlFor="name">Name</Label>
<Label htmlFor="name">Name *</Label>
<Input
id="name"
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
required
onChange={(e) => handleInputChange('name', e.target.value)}
className={errors.name ? 'border-red-500' : ''}
/>
{errors.name && (
<p className="text-red-500 text-sm">{errors.name}</p>
)}
</div>
<div className="space-y-2">
<Label htmlFor="description">Description</Label>
<Input
id="description"
value={formData.description}
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
required
onChange={(e) => handleInputChange('description', e.target.value)}
/>
</div>
<Button type="submit">{editingProduct ? 'Update' : 'Create'}</Button>

View file

@ -40,6 +40,10 @@ export default function UsersPage() {
username: '',
email: '',
});
const [errors, setErrors] = useState({
username: '',
email: '',
});
useEffect(() => {
loadUsers();
@ -54,8 +58,38 @@ export default function UsersPage() {
}
};
const validateForm = () => {
const newErrors = {
username: '',
email: '',
};
// Username cannot be empty
if (!formData.username.trim()) {
newErrors.username = 'Username is required';
}
// Email can be empty, but if provided, should be valid
if (formData.email.trim() && !isValidEmail(formData.email)) {
newErrors.email = 'Please enter a valid email address';
}
setErrors(newErrors);
return !newErrors.username && !newErrors.email;
};
const isValidEmail = (email: string) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!validateForm()) {
return;
}
try {
if (editingUser) {
await userApi.update(editingUser.id, formData);
@ -65,6 +99,7 @@ export default function UsersPage() {
setIsDialogOpen(false);
setEditingUser(null);
setFormData({ username: '', email: '' });
setErrors({ username: '', email: '' });
loadUsers();
} catch (error) {
console.error('Error saving user:', error);
@ -74,6 +109,7 @@ export default function UsersPage() {
const handleEdit = (user: User) => {
setEditingUser(user);
setFormData({ username: user.username, email: user.email });
setErrors({ username: '', email: '' });
setIsDialogOpen(true);
};
@ -88,6 +124,14 @@ export default function UsersPage() {
}
};
const handleInputChange = (field: 'username' | 'email', value: string) => {
setFormData({ ...formData, [field]: value });
// Clear error when user starts typing
if (errors[field]) {
setErrors({ ...errors, [field]: '' });
}
};
return (
<div className="space-y-4">
<div className="flex justify-between items-center">
@ -97,6 +141,7 @@ export default function UsersPage() {
<Button onClick={() => {
setEditingUser(null);
setFormData({ username: '', email: '' });
setErrors({ username: '', email: '' });
}}>
Add User
</Button>
@ -107,13 +152,16 @@ export default function UsersPage() {
</DialogHeader>
<form onSubmit={handleSubmit} className="space-y-4">
<div className="space-y-2">
<Label htmlFor="username">Username</Label>
<Label htmlFor="username">Username *</Label>
<Input
id="username"
value={formData.username}
onChange={(e) => setFormData({ ...formData, username: e.target.value })}
required
onChange={(e) => handleInputChange('username', e.target.value)}
className={errors.username ? 'border-red-500' : ''}
/>
{errors.username && (
<p className="text-red-500 text-sm">{errors.username}</p>
)}
</div>
<div className="space-y-2">
<Label htmlFor="email">Email</Label>
@ -121,9 +169,12 @@ export default function UsersPage() {
id="email"
type="email"
value={formData.email}
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
required
onChange={(e) => handleInputChange('email', e.target.value)}
className={errors.email ? 'border-red-500' : ''}
/>
{errors.email && (
<p className="text-red-500 text-sm">{errors.email}</p>
)}
</div>
<Button type="submit">{editingUser ? 'Update' : 'Create'}</Button>
</form>