diff --git a/frontend/src/components/EditChoreModal.tsx b/frontend/src/components/EditChoreModal.tsx new file mode 100644 index 0000000..b4f97a7 --- /dev/null +++ b/frontend/src/components/EditChoreModal.tsx @@ -0,0 +1,303 @@ +import React, { useState, useEffect } from 'react'; +import { choreService, Chore, UpdateChoreRequest } from '../api/chores'; +import api from '../api/axios'; + +interface User { + id: number; + username: string; + full_name: string; + is_active: boolean; +} + +interface EditChoreModalProps { + choreId: number; + onClose: () => void; + onSuccess: () => void; +} + +const EditChoreModal: React.FC = ({ choreId, onClose, onSuccess }) => { + const [chore, setChore] = useState(null); + const [formData, setFormData] = useState({}); + const [users, setUsers] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [isSaving, setIsSaving] = useState(false); + const [error, setError] = useState(''); + + useEffect(() => { + loadChoreAndUsers(); + }, [choreId]); + + const loadChoreAndUsers = async () => { + try { + const [choreData, usersData] = await Promise.all([ + choreService.getChore(choreId), + api.get('/api/v1/users') + ]); + + setChore(choreData); + setUsers(usersData.data.filter(u => u.is_active)); + + // Initialize form with current chore data + setFormData({ + title: choreData.title, + description: choreData.description || '', + room: choreData.room, + frequency: choreData.frequency, + points: choreData.points, + assigned_user_ids: choreData.assigned_users.map(u => u.id), + due_date: choreData.due_date ? choreData.due_date.split('T')[0] : '', + }); + } catch (error) { + console.error('Failed to load chore:', error); + setError('Failed to load chore details'); + } finally { + setIsLoading(false); + } + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setError(''); + setIsSaving(true); + + try { + const submitData = { ...formData }; + if (submitData.due_date) { + submitData.due_date = `${submitData.due_date}T23:59:59`; + } + + await choreService.updateChore(choreId, submitData); + onSuccess(); + } catch (err: any) { + let errorMessage = 'Failed to update chore'; + + if (err.response?.data) { + const errorData = err.response.data; + if (Array.isArray(errorData.detail)) { + errorMessage = errorData.detail + .map((e: any) => `${e.loc?.join('.')}: ${e.msg}`) + .join(', '); + } else if (typeof errorData.detail === 'string') { + errorMessage = errorData.detail; + } else if (typeof errorData === 'string') { + errorMessage = errorData; + } + } + + setError(errorMessage); + } finally { + setIsSaving(false); + } + }; + + const handleChange = (e: React.ChangeEvent) => { + const { name, value } = e.target; + setFormData(prev => ({ + ...prev, + [name]: name === 'points' ? parseInt(value) || 0 : value, + })); + }; + + const toggleUserAssignment = (userId: number) => { + setFormData(prev => { + const currentIds = prev.assigned_user_ids || []; + const isAssigned = currentIds.includes(userId); + + return { + ...prev, + assigned_user_ids: isAssigned + ? currentIds.filter(id => id !== userId) + : [...currentIds, userId] + }; + }); + }; + + if (isLoading) { + return ( +
+
+
+

Loading chore...

+
+
+ ); + } + + if (!chore) { + return null; + } + + return ( +
+
+
+
+

Edit Chore

+ +
+ +
+ {error && ( +
+ {error} +
+ )} + +
+
+ + +
+ +
+ +