bookwiz.io / components / TemplatePreview.tsx
TemplatePreview.tsx
Raw
'use client'

import { BookTemplate, TemplateFolder, TemplateFile } from '@/lib/types/database'
import { 
  FolderIcon, 
  DocumentTextIcon,
  ChevronRightIcon,
  ChevronDownIcon,
  EyeIcon
} from '@heroicons/react/24/outline'
import { useState, useMemo } from 'react'

interface TemplatePreviewProps {
  template: BookTemplate | null
  className?: string
}

interface FolderItemProps {
  folder: TemplateFolder
  level?: number
}

interface FileItemProps {
  file: TemplateFile
  level?: number
}

function FileItem({ file, level = 0 }: FileItemProps) {
  return (
    <div 
      className="flex items-center space-x-2 py-2 text-sm hover:bg-slate-700/30 rounded-lg transition-colors duration-150"
      style={{ paddingLeft: `${level * 20 + 12}px` }}
    >
      <DocumentTextIcon className="h-4 w-4 text-cyan-400 flex-shrink-0" />
      <span className="text-slate-200 truncate font-medium">{file.name}</span>
    </div>
  )
}

function FolderItem({ folder, level = 0 }: FolderItemProps) {
  const [isExpanded, setIsExpanded] = useState(folder.expanded || level === 0)
  const hasChildren = folder.children && folder.children.length > 0

  return (
    <div>
      <div 
        className="flex items-center space-x-2 py-2 text-sm cursor-pointer hover:bg-slate-700/30 rounded-lg transition-colors duration-150"
        style={{ paddingLeft: `${level * 20 + 12}px` }}
        onClick={() => hasChildren && setIsExpanded(!isExpanded)}
      >
        {hasChildren ? (
          isExpanded ? (
            <ChevronDownIcon className="h-4 w-4 text-slate-400 flex-shrink-0" />
          ) : (
            <ChevronRightIcon className="h-4 w-4 text-slate-400 flex-shrink-0" />
          )
        ) : (
          <div className="w-4 h-4 flex-shrink-0" />
        )}
        <FolderIcon className="h-4 w-4 text-blue-400 flex-shrink-0" />
        <span className="text-slate-100 font-semibold truncate">{folder.name}</span>
      </div>
      
      {hasChildren && isExpanded && (
        <div className="ml-2">
          {folder.children!.map((child, index) => (
            <div key={`${child.name}-${index}`}>
              {child.type === 'folder' ? (
                <FolderItem folder={child as TemplateFolder} level={level + 1} />
              ) : (
                <FileItem file={child as TemplateFile} level={level + 1} />
              )}
            </div>
          ))}
        </div>
      )}
    </div>
  )
}

export default function TemplatePreview({ template, className = '' }: TemplatePreviewProps) {
  // Memoize file counts to avoid recalculation on every render
  const { totalFolders, totalFiles } = useMemo(() => {
    if (!template) return { totalFolders: 0, totalFiles: 0 }
    
    const allFiles = [
      ...template.structure.files,
      ...template.structure.folders.flatMap(folder => 
        folder.children?.filter(child => child.type === 'file') || []
      )
    ]

    return {
      totalFolders: template.structure.folders.length,
      totalFiles: allFiles.length
    }
  }, [template])

  if (!template) {
    return (
      <div className={`bg-slate-800/30 rounded-2xl border border-slate-600/50 overflow-hidden ${className}`}>
        <div className="text-center py-16 px-6">
          <EyeIcon className="h-16 w-16 mx-auto mb-4 text-slate-500" />
          <h3 className="text-lg font-semibold text-slate-400 mb-2">Preview Your Template</h3>
          <p className="text-sm text-slate-500">Select a template to see its structure and organization</p>
        </div>
      </div>
    )
  }

  return (
    <div className={`bg-slate-800/30 rounded-2xl border border-slate-600/50 overflow-hidden ${className}`}>
      {/* Header */}
      <div className="border-b border-slate-700/50 p-6 bg-slate-800/50">
        <div className="flex items-center space-x-3 mb-4">
          <div className="p-2 bg-gradient-to-r from-blue-500 to-purple-500 rounded-xl">
            <EyeIcon className="h-5 w-5 text-white" />
          </div>
          <div>
            <h3 className="text-lg font-bold text-white">{template.name}</h3>
            <p className="text-sm text-slate-400">Template Structure</p>
          </div>
        </div>
        
        <div className="flex items-center space-x-6 text-sm">
          <div className="flex items-center space-x-2 px-3 py-1.5 bg-slate-700/50 rounded-lg">
            <FolderIcon className="h-4 w-4 text-blue-400" />
            <span className="text-slate-300 font-medium">{totalFolders} folders</span>
          </div>
          <div className="flex items-center space-x-2 px-3 py-1.5 bg-slate-700/50 rounded-lg">
            <DocumentTextIcon className="h-4 w-4 text-cyan-400" />
            <span className="text-slate-300 font-medium">{totalFiles} files</span>
          </div>
        </div>
      </div>

      {/* File tree */}
      <div className="p-4">
        <div className="max-h-80 overflow-y-auto">
          <div className="space-y-1">
            {/* Root files first */}
            {template.structure.files.map((file, index) => (
              <FileItem key={`file-${index}`} file={file} level={0} />
            ))}
            
            {/* Then folders */}
            {template.structure.folders.map((folder, index) => (
              <FolderItem key={`folder-${index}`} folder={folder} level={0} />
            ))}
          </div>
        </div>
      </div>

      {/* Footer with description */}
      {template.description && (
        <div className="border-t border-slate-700/50 p-6 bg-slate-800/20">
          <div className="flex items-start space-x-3">
            <div className="p-1.5 bg-gradient-to-r from-teal-500/20 to-cyan-500/20 rounded-lg">
              <DocumentTextIcon className="h-4 w-4 text-teal-400" />
            </div>
            <div>
              <h4 className="text-sm font-semibold text-slate-300 mb-1">About this template</h4>
              <p className="text-sm text-slate-400 leading-relaxed">{template.description}</p>
            </div>
          </div>
        </div>
      )}
    </div>
  )
}