wip
This commit is contained in:
parent
ba1f43fd8e
commit
0f9fbc9375
15 changed files with 381 additions and 297 deletions
|
@ -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>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue