Add ChoreCard component
This commit is contained in:
90
frontend/src/components/ChoreCard.tsx
Normal file
90
frontend/src/components/ChoreCard.tsx
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Chore } from '../api/chores';
|
||||||
|
|
||||||
|
interface ChoreCardProps {
|
||||||
|
chore: Chore;
|
||||||
|
onComplete: (id: number) => void;
|
||||||
|
onDelete: (id: number) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ChoreCard: React.FC<ChoreCardProps> = ({ chore, onComplete, onDelete }) => {
|
||||||
|
const statusColors = {
|
||||||
|
pending: 'bg-yellow-100 text-yellow-800',
|
||||||
|
in_progress: 'bg-blue-100 text-blue-800',
|
||||||
|
completed: 'bg-green-100 text-green-800',
|
||||||
|
skipped: 'bg-gray-100 text-gray-800',
|
||||||
|
};
|
||||||
|
|
||||||
|
const frequencyIcons = {
|
||||||
|
daily: '📅',
|
||||||
|
weekly: '📆',
|
||||||
|
monthly: '🗓️',
|
||||||
|
once: '⏱️',
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-white rounded-lg shadow-md p-4 hover:shadow-lg transition-shadow">
|
||||||
|
<div className="flex justify-between items-start mb-3">
|
||||||
|
<h3 className="text-lg font-semibold text-gray-900">{chore.title}</h3>
|
||||||
|
<span className={`px-2 py-1 rounded-full text-xs font-medium ${statusColors[chore.status]}`}>
|
||||||
|
{chore.status.replace('_', ' ')}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{chore.description && (
|
||||||
|
<p className="text-sm text-gray-600 mb-3">{chore.description}</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="space-y-2 mb-4">
|
||||||
|
<div className="flex items-center text-sm text-gray-500">
|
||||||
|
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
||||||
|
</svg>
|
||||||
|
<span className="font-medium">{chore.room}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center text-sm text-gray-500">
|
||||||
|
<span className="mr-2">{frequencyIcons[chore.frequency]}</span>
|
||||||
|
<span className="capitalize">{chore.frequency}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{chore.assigned_user && (
|
||||||
|
<div className="flex items-center text-sm text-gray-500">
|
||||||
|
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
||||||
|
</svg>
|
||||||
|
<span>{chore.assigned_user.full_name}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{chore.due_date && (
|
||||||
|
<div className="flex items-center text-sm text-gray-500">
|
||||||
|
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
||||||
|
</svg>
|
||||||
|
<span>{new Date(chore.due_date).toLocaleDateString()}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex gap-2">
|
||||||
|
{chore.status !== 'completed' && (
|
||||||
|
<button
|
||||||
|
onClick={() => onComplete(chore.id)}
|
||||||
|
className="flex-1 px-3 py-2 bg-green-600 text-white text-sm rounded-lg hover:bg-green-700 transition-colors"
|
||||||
|
>
|
||||||
|
Complete
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
onClick={() => onDelete(chore.id)}
|
||||||
|
className="px-3 py-2 bg-red-100 text-red-700 text-sm rounded-lg hover:bg-red-200 transition-colors"
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ChoreCard;
|
||||||
Reference in New Issue
Block a user