My-Weekly-ToDo-List/src/components/TaskForm.tsx
mARTin a26f76ea1d feat(02-01): enhance user experience with advanced features
- Extended Task type with recurring, category, reminder, and priority properties
- Improved TaskCard component with new UI elements for advanced features
- Enhanced TaskForm component with inputs for advanced features
- Updated CalendarGrid component to handle improved task rendering
- Updated taskPersistence with handling for new properties

SUMMARY: .planning/phases/02-user-experience-advanced-features/02-01-SUMMARY.md
2026-01-24 14:32:45 +01:00

212 lines
8.0 KiB
TypeScript
Raw 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 React, { useState, useEffect } from 'react';
import { Task } from '../types/task';
interface TaskFormProps {
task?: Task;
onSave: (task: Task) => void;
onCancel: () => void;
}
const TaskForm: React.FC<TaskFormProps> = ({ task, onSave, onCancel }) => {
const [title, setTitle] = useState(task?.title || '');
const [description, setDescription] = useState(task?.description || '');
const [date, setDate] = useState(task?.date ? new Date(task.date).toISOString().split('T')[0] : '');
const [completed, setCompleted] = useState(task?.completed || false);
const [category, setCategory] = useState(task?.category || '');
const [priority, setPriority] = useState<Task['priority']>(task?.priority || 'medium');
const [recurring, setRecurring] = useState<Task['recurring']>(task?.recurring || undefined);
const [reminder, setReminder] = useState(task?.reminder ? new Date(task.reminder).toISOString().split('T')[0] : '');
const [tags, setTags] = useState<string[]>(task?.tags || []);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
const newTask: Task = {
id: task?.id || Date.now().toString(),
title,
description,
date: new Date(date),
completed,
category,
priority,
recurring,
reminder: reminder ? new Date(reminder) : undefined,
tags
};
onSave(newTask);
};
const handleTagAdd = (e: React.KeyboardEvent) => {
if (e.key === 'Enter' && (e.target as HTMLInputElement).value.trim() !== '') {
const tagValue = (e.target as HTMLInputElement).value.trim();
if (!tags.includes(tagValue)) {
setTags([...tags, tagValue]);
}
(e.target as HTMLInputElement).value = '';
}
};
const handleTagRemove = (index: number) => {
setTags(tags.filter((_, i) => i !== index));
};
return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50">
<div className="bg-white rounded-lg shadow-xl w-full max-w-md">
<div className="p-6">
<h2 className="text-xl font-bold text-gray-900 mb-4">
{task ? 'Edit Task' : 'Add New Task'}
</h2>
<form onSubmit={handleSubmit}>
<div className="mb-4">
<label className="block text-sm font-medium text-gray-700 mb-1">
Title *
</label>
<input
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
required
/>
</div>
<div className="mb-4">
<label className="block text-sm font-medium text-gray-700 mb-1">
Description
</label>
<textarea
value={description}
onChange={(e) => setDescription(e.target.value)}
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"
/>
</div>
<div className="grid grid-cols-2 gap-4 mb-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Date *
</label>
<input
type="date"
value={date}
onChange={(e) => setDate(e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
required
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Priority
</label>
<select
value={priority}
onChange={(e) => setPriority(e.target.value as Task['priority'])}
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="low">Low</option>
<option value="medium">Medium</option>
<option value="high">High</option>
</select>
</div>
</div>
<div className="mb-4">
<label className="block text-sm font-medium text-gray-700 mb-1">
Category
</label>
<input
type="text"
value={category}
onChange={(e) => setCategory(e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="Work, Personal, etc."
/>
</div>
<div className="mb-4">
<label className="block text-sm font-medium text-gray-700 mb-1">
Tags
</label>
<div className="flex flex-wrap gap-2 mb-2">
{tags.map((tag, index) => (
<span
key={index}
className="bg-indigo-100 text-indigo-800 text-xs px-2 py-1 rounded flex items-center"
>
{tag}
<button
type="button"
onClick={() => handleTagRemove(index)}
className="ml-1 text-indigo-600 hover:text-indigo-800"
>
×
</button>
</span>
))}
</div>
<input
type="text"
placeholder="Press Enter to add tag"
onKeyDown={handleTagAdd}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
</div>
<div className="grid grid-cols-2 gap-4 mb-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Recurring
</label>
<select
value={recurring || ''}
onChange={(e) => setRecurring(e.target.value ? e.target.value as Task['recurring'] : undefined)}
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="">None</option>
<option value="daily">Daily</option>
<option value="weekly">Weekly</option>
<option value="monthly">Monthly</option>
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Reminder Date
</label>
<input
type="date"
value={reminder}
onChange={(e) => setReminder(e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
</div>
</div>
<div className="flex justify-end space-x-3">
<button
type="button"
onClick={onCancel}
className="px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50"
>
Cancel
</button>
<button
type="submit"
className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500"
>
{task ? 'Update Task' : 'Add Task'}
</button>
</div>
</form>
</div>
</div>
</div>
);
};
export default TaskForm;