← back to Letsbegin

components/RecentItemsFooter.tsx

279 lines

'use client'

import { useState, useEffect } from 'react'
import { Clock, FolderOpen, X, ChevronDown, ChevronUp, FileText } from 'lucide-react'

export interface RecentItem {
  name: string
  path: string
  timestamp: number
  type: 'project' | 'file'
  prdData?: {
    featureTitle?: string
    problemStatement?: string
    scope?: string
    priority?: string
  }
}

interface RecentItemsFooterProps {
  onItemClick?: (item: RecentItem) => void
}

const STORAGE_KEY = 'letsbegin_recent_items'
const MAX_ITEMS = 10

export function addRecentItem(item: Omit<RecentItem, 'timestamp'>) {
  if (typeof window === 'undefined') return

  const stored = localStorage.getItem(STORAGE_KEY)
  let items: RecentItem[] = stored ? JSON.parse(stored) : []

  // Remove if already exists
  items = items.filter(i => i.path !== item.path)

  // Add to front with timestamp
  items.unshift({
    ...item,
    timestamp: Date.now()
  })

  // Keep only MAX_ITEMS
  items = items.slice(0, MAX_ITEMS)

  localStorage.setItem(STORAGE_KEY, JSON.stringify(items))

  // Dispatch event for other components to listen
  window.dispatchEvent(new CustomEvent('recentItemsUpdated'))
}

export function getRecentItems(): RecentItem[] {
  if (typeof window === 'undefined') return []

  const stored = localStorage.getItem(STORAGE_KEY)
  return stored ? JSON.parse(stored) : []
}

export default function RecentItemsFooter({ onItemClick }: RecentItemsFooterProps) {
  const [recentItems, setRecentItems] = useState<RecentItem[]>([])
  const [isExpanded, setIsExpanded] = useState(false)
  const [expandedItems, setExpandedItems] = useState<Set<string>>(new Set())

  useEffect(() => {
    // Load initial items
    setRecentItems(getRecentItems())

    // Listen for updates
    const handleUpdate = () => {
      setRecentItems(getRecentItems())
    }

    window.addEventListener('recentItemsUpdated', handleUpdate)
    window.addEventListener('storage', handleUpdate)

    return () => {
      window.removeEventListener('recentItemsUpdated', handleUpdate)
      window.removeEventListener('storage', handleUpdate)
    }
  }, [])

  function removeItem(path: string, e: React.MouseEvent) {
    e.stopPropagation()
    const items = recentItems.filter(i => i.path !== path)
    localStorage.setItem(STORAGE_KEY, JSON.stringify(items))
    setRecentItems(items)
    expandedItems.delete(path)
    setExpandedItems(new Set(expandedItems))
  }

  function toggleItemExpand(path: string, e: React.MouseEvent) {
    e.stopPropagation()
    const newExpanded = new Set(expandedItems)
    if (newExpanded.has(path)) {
      newExpanded.delete(path)
    } else {
      newExpanded.add(path)
    }
    setExpandedItems(newExpanded)
  }

  function formatTime(timestamp: number): string {
    const diff = Date.now() - timestamp
    const minutes = Math.floor(diff / 60000)
    const hours = Math.floor(diff / 3600000)

    if (minutes < 1) return 'Just now'
    if (minutes < 60) return `${minutes}m ago`
    if (hours < 24) return `${hours}h ago`
    return new Date(timestamp).toLocaleDateString()
  }

  function formatFullTime(timestamp: number): string {
    return new Date(timestamp).toLocaleString()
  }

  if (recentItems.length === 0) return null

  return (
    <footer className={`flex-shrink-0 border-t border-[var(--border)] bg-[var(--surface-alt)] transition-all ${isExpanded ? 'max-h-[400px]' : 'max-h-16'}`}>
      {/* Header - always visible */}
      <div className="px-4 py-2 flex items-center justify-between">
        <div className="flex items-center gap-4">
          <button
            onClick={() => setIsExpanded(!isExpanded)}
            className="flex items-center gap-1.5 text-xs text-[var(--foreground-tertiary)] hover:text-[var(--foreground)] transition-colors"
          >
            <Clock className="w-3 h-3" />
            <span>Recent ({recentItems.length})</span>
            {isExpanded ? <ChevronDown className="w-3 h-3" /> : <ChevronUp className="w-3 h-3" />}
          </button>

          {/* Quick access items (shown when collapsed) */}
          {!isExpanded && (
            <div className="flex items-center gap-2 overflow-x-auto">
              {recentItems.slice(0, 5).map((item) => (
                <button
                  key={item.path}
                  onClick={() => onItemClick?.(item)}
                  className="group flex items-center gap-2 px-2 py-1 rounded-md bg-[var(--surface)] border border-[var(--border)] hover:border-[var(--primary)] hover:bg-[var(--primary)]/5 transition-all text-xs"
                >
                  <FolderOpen className="w-3 h-3 text-[var(--primary)]" />
                  <span className="text-[var(--foreground)] font-medium max-w-[100px] truncate">
                    {item.name}
                  </span>
                </button>
              ))}
              {recentItems.length > 5 && (
                <span className="text-xs text-[var(--foreground-tertiary)]">
                  +{recentItems.length - 5} more
                </span>
              )}
            </div>
          )}
        </div>

        <button
          onClick={() => setIsExpanded(!isExpanded)}
          className="text-xs px-2 py-1 rounded hover:bg-[var(--surface)] text-[var(--foreground-secondary)]"
        >
          {isExpanded ? 'Collapse' : 'Expand'}
        </button>
      </div>

      {/* Expanded view with table */}
      {isExpanded && (
        <div className="px-4 pb-4 max-h-[340px] overflow-y-auto">
          <table className="w-full text-sm">
            <thead className="sticky top-0 bg-[var(--surface-alt)]">
              <tr className="text-left text-xs text-[var(--foreground-tertiary)] border-b border-[var(--border)]">
                <th className="pb-2 pr-3 w-8"></th>
                <th className="pb-2 pr-3">Name</th>
                <th className="pb-2 pr-3 hidden md:table-cell">Type</th>
                <th className="pb-2 pr-3">Time</th>
                <th className="pb-2 pr-3 hidden lg:table-cell">Path</th>
                <th className="pb-2 w-20">Actions</th>
              </tr>
            </thead>
            <tbody>
              {recentItems.map((item, index) => {
                const isItemExpanded = expandedItems.has(item.path)
                return (
                  <>
                    <tr
                      key={item.path}
                      className="border-b border-[var(--border)]/50 hover:bg-[var(--surface)]/50 cursor-pointer"
                      onClick={() => onItemClick?.(item)}
                    >
                      <td className="py-2 pr-3">
                        <span className="text-[var(--foreground-tertiary)] text-xs">{index + 1}</span>
                      </td>
                      <td className="py-2 pr-3">
                        <div className="flex items-center gap-2">
                          {item.type === 'project' ? (
                            <FolderOpen className="w-4 h-4 text-[var(--primary)]" />
                          ) : (
                            <FileText className="w-4 h-4 text-[var(--warning)]" />
                          )}
                          <span className="font-medium text-[var(--foreground)]">{item.name}</span>
                        </div>
                      </td>
                      <td className="py-2 pr-3 hidden md:table-cell">
                        <span className={`text-xs px-2 py-0.5 rounded-full ${
                          item.type === 'project'
                            ? 'bg-[var(--primary)]/20 text-[var(--primary)]'
                            : 'bg-[var(--warning)]/20 text-[var(--warning)]'
                        }`}>
                          {item.type}
                        </span>
                      </td>
                      <td className="py-2 pr-3">
                        <span className="text-[var(--foreground-secondary)] text-xs">
                          {formatTime(item.timestamp)}
                        </span>
                      </td>
                      <td className="py-2 pr-3 hidden lg:table-cell">
                        <span className="text-[var(--foreground-tertiary)] text-xs font-mono max-w-[200px] truncate block">
                          {item.path}
                        </span>
                      </td>
                      <td className="py-2">
                        <div className="flex items-center gap-1">
                          <button
                            onClick={(e) => toggleItemExpand(item.path, e)}
                            className="p-1 rounded hover:bg-[var(--surface)] text-[var(--foreground-secondary)] hover:text-[var(--foreground)]"
                            title="Show details"
                          >
                            {isItemExpanded ? <ChevronUp className="w-3 h-3" /> : <ChevronDown className="w-3 h-3" />}
                          </button>
                          <button
                            onClick={(e) => removeItem(item.path, e)}
                            className="p-1 rounded hover:bg-[var(--surface)] text-[var(--foreground-tertiary)] hover:text-[var(--error)]"
                            title="Remove"
                          >
                            <X className="w-3 h-3" />
                          </button>
                        </div>
                      </td>
                    </tr>
                    {/* Expanded details row */}
                    {isItemExpanded && (
                      <tr key={`${item.path}-details`} className="bg-[var(--surface)]/30">
                        <td colSpan={6} className="py-3 px-4">
                          <div className="grid grid-cols-2 md:grid-cols-4 gap-4 text-xs">
                            <div>
                              <span className="text-[var(--foreground-tertiary)]">Full Path:</span>
                              <div className="font-mono text-[var(--foreground)] mt-1 break-all">{item.path}</div>
                            </div>
                            <div>
                              <span className="text-[var(--foreground-tertiary)]">Accessed:</span>
                              <div className="text-[var(--foreground)] mt-1">{formatFullTime(item.timestamp)}</div>
                            </div>
                            <div>
                              <span className="text-[var(--foreground-tertiary)]">Type:</span>
                              <div className="text-[var(--foreground)] mt-1 capitalize">{item.type}</div>
                            </div>
                            {item.prdData && (
                              <div>
                                <span className="text-[var(--foreground-tertiary)]">PRD Data:</span>
                                <div className="text-[var(--foreground)] mt-1">
                                  {item.prdData.featureTitle && <div>Feature: {item.prdData.featureTitle}</div>}
                                  {item.prdData.scope && <div>Scope: {item.prdData.scope}</div>}
                                  {item.prdData.priority && <div>Priority: {item.prdData.priority}</div>}
                                </div>
                              </div>
                            )}
                          </div>
                        </td>
                      </tr>
                    )}
                  </>
                )
              })}
            </tbody>
          </table>
        </div>
      )}
    </footer>
  )
}