150 lines
4.1 KiB
TypeScript
150 lines
4.1 KiB
TypeScript
'use client'
|
|
|
|
import { useState, useMemo } from 'react'
|
|
import { Share2 } from 'lucide-react'
|
|
import ItemCard from './ItemCard'
|
|
import EmptyState from './EmptyState'
|
|
import ShareListModal from './ShareListModal'
|
|
import PermissionBadge from './PermissionBadge'
|
|
import { useScrollPosition } from '@/hooks'
|
|
import { useRealtimeSync } from '@/hooks/useRealtimeSync'
|
|
|
|
interface Item {
|
|
id: string
|
|
title: string
|
|
description: string | null
|
|
completed: boolean
|
|
priority: string
|
|
dueDate: Date | null
|
|
tags: any
|
|
notes: string | null
|
|
createdAt: Date
|
|
updatedAt: Date
|
|
listId: string
|
|
}
|
|
|
|
interface ListDetailViewProps {
|
|
listId: string
|
|
initialItems: Item[]
|
|
permission?: 'owner' | 'editor' | 'viewer'
|
|
canShare?: boolean
|
|
listTitle?: string
|
|
ownerId?: string
|
|
ownerName?: string | null
|
|
ownerEmail?: string
|
|
collaborators?: Array<{
|
|
id: string
|
|
permission: 'editor' | 'viewer'
|
|
user: {
|
|
id: string
|
|
name: string | null
|
|
email: string
|
|
}
|
|
}>
|
|
}
|
|
|
|
export default function ListDetailView({
|
|
listId,
|
|
initialItems,
|
|
permission,
|
|
canShare = false,
|
|
listTitle = '',
|
|
ownerId = '',
|
|
ownerName = null,
|
|
ownerEmail = '',
|
|
collaborators = []
|
|
}: ListDetailViewProps) {
|
|
const [isShareModalOpen, setIsShareModalOpen] = useState(false)
|
|
|
|
// Use scroll position preservation hook
|
|
useScrollPosition({ key: `list-${listId}` })
|
|
|
|
// Use real-time sync hook to poll for updates
|
|
useRealtimeSync({
|
|
listId,
|
|
enabled: true,
|
|
pollInterval: 1000, // Poll every 1 second for faster updates
|
|
})
|
|
|
|
// Determine which items to display
|
|
const completedItems = useMemo(() => initialItems.filter(item => item.completed), [initialItems])
|
|
const incompleteItems = useMemo(() => initialItems.filter(item => !item.completed), [initialItems])
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
{/* Permission Badge and Share Button */}
|
|
{(permission || canShare) && (
|
|
<div className="flex items-center justify-between gap-4">
|
|
{permission && (
|
|
<div>
|
|
<PermissionBadge permission={permission} />
|
|
</div>
|
|
)}
|
|
{canShare && (
|
|
<button
|
|
onClick={() => setIsShareModalOpen(true)}
|
|
className="ml-auto flex items-center gap-2 px-4 py-2 bg-blue-500 hover:bg-blue-600 active:bg-blue-700 text-white rounded-lg transition-colors text-sm font-medium"
|
|
>
|
|
<Share2 size={16} />
|
|
Share
|
|
</button>
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
{/* Items */}
|
|
<div className="space-y-6">
|
|
{/* Incomplete Items */}
|
|
{incompleteItems.length > 0 && (
|
|
<div className="animate-fade-in">
|
|
<h2 className="text-lg font-semibold text-gray-900 mb-3">
|
|
To Do ({incompleteItems.length})
|
|
</h2>
|
|
<div className="space-y-2">
|
|
{incompleteItems.map((item) => (
|
|
<ItemCard key={item.id} item={item} listId={listId} />
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Completed Items */}
|
|
{completedItems.length > 0 && (
|
|
<div className="animate-fade-in">
|
|
<h2 className="text-lg font-semibold text-gray-500 mb-3">
|
|
Completed ({completedItems.length})
|
|
</h2>
|
|
<div className="space-y-2">
|
|
{completedItems.map((item) => (
|
|
<ItemCard key={item.id} item={item} listId={listId} />
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Empty State */}
|
|
{initialItems.length === 0 && (
|
|
<EmptyState
|
|
icon="inbox"
|
|
title="No tasks found"
|
|
description="Add your first task using the button above"
|
|
/>
|
|
)}
|
|
</div>
|
|
|
|
{/* Share Modal */}
|
|
<ShareListModal
|
|
isOpen={isShareModalOpen}
|
|
onClose={() => setIsShareModalOpen(false)}
|
|
listId={listId}
|
|
listTitle={listTitle}
|
|
isOwner={permission === 'owner'}
|
|
ownerId={ownerId}
|
|
ownerName={ownerName}
|
|
ownerEmail={ownerEmail}
|
|
initialCollaborators={collaborators}
|
|
/>
|
|
</div>
|
|
)
|
|
}
|