This commit is contained in:
lubiana 2025-07-24 11:35:37 +02:00
parent ba1f43fd8e
commit 0f9fbc9375
Signed by: lubiana
SSH key fingerprint: SHA256:vW1EA0fRR3Fw+dD/sM0K+x3Il2gSry6YRYHqOeQwrfk
15 changed files with 381 additions and 297 deletions

View file

@ -1,30 +1,15 @@
import { useState, useEffect } from 'react'
import { useParams, useNavigate } from 'react-router-dom'
import { useState } from 'react'
import { useParams, Link } from 'react-router-dom'
import { useSSE } from '../hooks/useSSE'
import { useLocalStorage } from '../hooks/useLocalStorage'
import ChecklistItem from '../components/ChecklistItem'
import type { ChecklistItem as ChecklistItemType } from '../types'
export default function Checklist() {
const { uuid } = useParams<{ uuid: string }>()
const navigate = useNavigate()
const { items, isConnected, error } = useSSE(uuid || '')
const { saveChecklist, getChecklistName } = useLocalStorage()
const { items, checkListName, isConnected, error } = useSSE(uuid || '')
const [newItemContent, setNewItemContent] = useState('')
const [isAddingItem, setIsAddingItem] = useState(false)
// Save checklist to local storage if not already saved
useEffect(() => {
if (uuid && !getChecklistName(uuid)) {
// Generate a default name if not in local storage
const defaultName = `Checklist ${new Date().toLocaleDateString()}`
saveChecklist({
uuid,
name: defaultName,
createdAt: new Date().toISOString()
})
}
}, [uuid, saveChecklist, getChecklistName])
const buildItemTree = (items: ChecklistItemType[]): ChecklistItemType[] => {
const itemMap = new Map<number, ChecklistItemType>()
@ -169,33 +154,33 @@ export default function Checklist() {
)
}
const checklistName = getChecklistName(uuid) || 'Untitled Checklist'
const itemTree = buildItemTree(items)
return (
<div className="min-h-screen bg-gray-50 dark:bg-gray-900">
{/* Header */}
<div className="bg-white dark:bg-gray-800 shadow-sm border-b border-gray-200 dark:border-gray-700">
<div className="max-w-4xl mx-auto px-4 py-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-4">
<div className="max-w-4xl mx-auto p-1">
<div className="flex flex-col sm:flex-row items-center justify-between">
<div className="flex items-center gap-2">
<Link to="/">
<button
onClick={() => navigate('/')}
className="flex items-center gap-2 text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white transition-colors duration-200"
className="flex items-center gap-1 text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white transition-colors duration-200"
>
<span className="text-xl"></span>
<span className="font-medium">Back</span>
<span className="text-base"></span>
<span className="text-base">Back</span>
</button>
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">{checklistName}</h1>
</Link>
<h1 className="text-base font-bold text-gray-900 dark:text-white">{checkListName}</h1>
</div>
<div className="flex items-center gap-2">
{isConnected ? (
<span className="flex items-center gap-2 text-green-600 dark:text-green-400 bg-green-50 dark:bg-green-900/30 px-3 py-1 rounded-full text-sm font-medium">
<span className="flex items-center gap-2 text-green-600 dark:text-green-400 bg-green-50 dark:bg-green-900/30 px-2 py-0.5 rounded-full text-sm font-medium">
<span className="w-2 h-2 bg-green-500 rounded-full"></span>
Connected
</span>
) : (
<span className="flex items-center gap-2 text-red-600 dark:text-red-400 bg-red-50 dark:bg-red-900/30 px-3 py-1 rounded-full text-sm font-medium">
<span className="flex items-center gap-2 text-red-600 dark:text-red-400 bg-red-50 dark:bg-red-900/30 px-2 py-0.5 rounded-full text-sm font-medium">
<span className="w-2 h-2 bg-red-500 rounded-full"></span>
Disconnected
</span>
@ -207,8 +192,8 @@ export default function Checklist() {
{/* Error Message */}
{error && (
<div className="max-w-4xl mx-auto px-4 py-4">
<div className="bg-red-50 dark:bg-red-900/30 border border-red-200 dark:border-red-800 rounded-lg p-4">
<div className="max-w-4xl mx-auto px-2 py-2">
<div className="bg-red-50 dark:bg-red-900/30 border border-red-200 dark:border-red-800 rounded-lg p-2">
<div className="flex items-center gap-2">
<span className="text-red-600 dark:text-red-400"></span>
<span className="text-red-800 dark:text-red-200 font-medium">{error}</span>
@ -218,23 +203,23 @@ export default function Checklist() {
)}
{/* Main Content */}
<div className="max-w-4xl mx-auto px-4 py-6">
<div className="max-w-4xl mx-auto px-2 py-4">
{/* Add Item Section */}
<div className=" shadow-sm p-1 mb-1">
<div className="flex gap-3">
<div className="flex flex-col sm:flex-row gap-2">
<input
type="text"
placeholder="Add a new item..."
value={newItemContent}
onChange={(e) => setNewItemContent(e.target.value)}
onKeyPress={handleKeyPress}
onKeyDown={handleKeyPress}
disabled={isAddingItem || !isConnected}
className="flex-1 px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:bg-gray-100 dark:disabled:bg-gray-700 disabled:text-gray-500 dark:disabled:text-gray-400 bg-white dark:bg-gray-700 text-gray-900 dark:text-white placeholder-gray-500 dark:placeholder-gray-400"
className="flex-1 px-3 py-1 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:bg-gray-100 dark:disabled:bg-gray-700 disabled:text-gray-500 dark:disabled:text-gray-400 bg-white dark:bg-gray-700 text-gray-900 dark:text-white placeholder-gray-500 dark:placeholder-gray-400"
/>
<button
onClick={handleAddItem}
disabled={isAddingItem || !newItemContent.trim() || !isConnected}
className="px-6 py-2 bg-blue-600 dark:bg-blue-500 text-white font-medium rounded-lg hover:bg-blue-700 dark:hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 disabled:opacity-50 disabled:cursor-not-allowed transition-colors duration-200"
className="px-4 py-1 bg-blue-600 dark:bg-blue-500 text-white font-medium rounded-lg hover:bg-blue-700 dark:hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 disabled:opacity-50 disabled:cursor-not-allowed transition-colors duration-200"
>
{isAddingItem ? 'Adding...' : 'Add'}
</button>
@ -242,17 +227,17 @@ export default function Checklist() {
</div>
{/* Items Section */}
<div className=" p-2">
<div className="p-1">
{itemTree.length === 0 ? (
<div className="text-center py-12">
<div className="text-gray-400 dark:text-gray-500 text-6xl mb-4">📝</div>
<p className="text-gray-600 dark:text-gray-300 text-lg font-medium">No items yet</p>
<p className="text-gray-500 dark:text-gray-400 mt-2">Add your first item above to get started!</p>
<div className="text-center py-10">
<div className="text-gray-400 dark:text-gray-500 text-5xl mb-3">📝</div>
<p className="text-gray-600 dark:text-gray-300 text-base font-medium">No items yet</p>
<p className="text-gray-500 dark:text-gray-400 mt-1">Add your first item above to get started!</p>
</div>
) : (
<div className="space-y-1">
<ul>
{renderItems(itemTree)}
</div>
</ul>
)}
</div>
</div>