- Replaced broken animate-spin Tailwind class with custom weekly-spinner CSS - Standardized loading indicators across Auth, Tasks, and Calendar settings - Fixed JSX syntax errors in several components
214 lines
6.5 KiB
TypeScript
214 lines
6.5 KiB
TypeScript
'use client';
|
|
|
|
import React, { useState } from 'react';
|
|
|
|
interface TaskFormData {
|
|
title: string;
|
|
description?: string;
|
|
startTime?: string;
|
|
endTime?: string;
|
|
dayOfWeek?: number;
|
|
}
|
|
|
|
interface TaskFormProps {
|
|
onSubmit: (task: TaskFormData) => void;
|
|
isLoading?: boolean;
|
|
}
|
|
|
|
const TaskForm: React.FC<TaskFormProps> = ({ onSubmit, isLoading = false }) => {
|
|
const [formData, setFormData] = useState<TaskFormData>({
|
|
title: '',
|
|
description: '',
|
|
startTime: '',
|
|
endTime: '',
|
|
dayOfWeek: 1 // Default to Monday
|
|
});
|
|
const [errors, setErrors] = useState<Record<string, string>>({});
|
|
|
|
const validateForm = (): boolean => {
|
|
const newErrors: Record<string, string> = {};
|
|
|
|
if (!formData.title.trim()) {
|
|
newErrors.title = 'Title is required';
|
|
}
|
|
|
|
// Validate time format if provided
|
|
if (formData.startTime && !isValidTime(formData.startTime)) {
|
|
newErrors.startTime = 'Invalid time format';
|
|
}
|
|
|
|
if (formData.endTime && !isValidTime(formData.endTime)) {
|
|
newErrors.endTime = 'Invalid time format';
|
|
}
|
|
|
|
// Check if start time is before end time if both are provided
|
|
if (formData.startTime && formData.endTime) {
|
|
const start = parseTime(formData.startTime);
|
|
const end = parseTime(formData.endTime);
|
|
if (start >= end) {
|
|
newErrors.endTime = 'End time must be after start time';
|
|
}
|
|
}
|
|
|
|
setErrors(newErrors);
|
|
return Object.keys(newErrors).length === 0;
|
|
};
|
|
|
|
const isValidTime = (time: string): boolean => {
|
|
const timeRegex = /^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$/;
|
|
return timeRegex.test(time);
|
|
};
|
|
|
|
const parseTime = (time: string): number => {
|
|
const [hours, minutes] = time.split(':').map(Number);
|
|
return hours * 60 + minutes;
|
|
};
|
|
|
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
|
|
const { name, value } = e.target;
|
|
setFormData({
|
|
...formData,
|
|
[name]: value
|
|
});
|
|
|
|
// Clear error when user starts typing
|
|
if (errors[name]) {
|
|
setErrors(prev => {
|
|
const newErrors = { ...prev };
|
|
delete newErrors[name];
|
|
return newErrors;
|
|
});
|
|
}
|
|
};
|
|
|
|
const handleSubmit = (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
|
|
if (validateForm()) {
|
|
onSubmit(formData);
|
|
// Reset form
|
|
setFormData({
|
|
title: '',
|
|
description: '',
|
|
startTime: '',
|
|
endTime: '',
|
|
dayOfWeek: 1
|
|
});
|
|
setErrors({});
|
|
}
|
|
};
|
|
|
|
return (
|
|
<form onSubmit={handleSubmit} className="task-form bg-white rounded-lg shadow-md p-6 mb-6">
|
|
<h3 className="text-lg font-semibold text-gray-800 mb-4">Add New Task</h3>
|
|
|
|
<div className="form-group mb-4">
|
|
<label htmlFor="title" className="block text-sm font-medium text-gray-700 mb-1">
|
|
Task Title *
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="title"
|
|
name="title"
|
|
value={formData.title}
|
|
onChange={handleChange}
|
|
className={`w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${errors.title ? 'border-red-500' : 'border-gray-300'
|
|
}`}
|
|
placeholder="What needs to be done?"
|
|
required
|
|
/>
|
|
{errors.title && <p className="mt-1 text-sm text-red-600">{errors.title}</p>}
|
|
</div>
|
|
|
|
<div className="form-group mb-4">
|
|
<label htmlFor="description" className="block text-sm font-medium text-gray-700 mb-1">
|
|
Description
|
|
</label>
|
|
<textarea
|
|
id="description"
|
|
name="description"
|
|
value={formData.description}
|
|
onChange={handleChange}
|
|
rows={3}
|
|
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
placeholder="Add details..."
|
|
/>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
|
<div className="form-group">
|
|
<label htmlFor="startTime" className="block text-sm font-medium text-gray-700 mb-1">
|
|
Start Time
|
|
</label>
|
|
<input
|
|
type="time"
|
|
id="startTime"
|
|
name="startTime"
|
|
value={formData.startTime}
|
|
onChange={handleChange}
|
|
className={`w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${errors.startTime ? 'border-red-500' : 'border-gray-300'
|
|
}`}
|
|
/>
|
|
{errors.startTime && <p className="mt-1 text-sm text-red-600">{errors.startTime}</p>}
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label htmlFor="endTime" className="block text-sm font-medium text-gray-700 mb-1">
|
|
End Time
|
|
</label>
|
|
<input
|
|
type="time"
|
|
id="endTime"
|
|
name="endTime"
|
|
value={formData.endTime}
|
|
onChange={handleChange}
|
|
className={`w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${errors.endTime ? 'border-red-500' : 'border-gray-300'
|
|
}`}
|
|
/>
|
|
{errors.endTime && <p className="mt-1 text-sm text-red-600">{errors.endTime}</p>}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="form-group mb-4">
|
|
<label htmlFor="dayOfWeek" className="block text-sm font-medium text-gray-700 mb-1">
|
|
Day of Week
|
|
</label>
|
|
<select
|
|
id="dayOfWeek"
|
|
name="dayOfWeek"
|
|
value={formData.dayOfWeek}
|
|
onChange={handleChange}
|
|
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
>
|
|
<option value={0}>Sunday</option>
|
|
<option value={1}>Monday</option>
|
|
<option value={2}>Tuesday</option>
|
|
<option value={3}>Wednesday</option>
|
|
<option value={4}>Thursday</option>
|
|
<option value={5}>Friday</option>
|
|
<option value={6}>Saturday</option>
|
|
</select>
|
|
</div>
|
|
|
|
<button
|
|
type="submit"
|
|
disabled={isLoading}
|
|
className={`w-full py-2 px-4 rounded-md text-white font-medium transition-colors ${isLoading
|
|
? 'bg-blue-400 cursor-not-allowed'
|
|
: 'bg-blue-600 hover:bg-blue-700'
|
|
}`}
|
|
>
|
|
{isLoading ? (
|
|
<span className="flex items-center justify-center">
|
|
<div className="weekly-spinner weekly-spinner-white -ml-1 mr-2"></div>
|
|
Adding Task...
|
|
</span>
|
|
) : (
|
|
'Add Task'
|
|
)}
|
|
</button>
|
|
</form >
|
|
);
|
|
};
|
|
|
|
export default TaskForm; |