bookwiz.io / components / search / IndexingStatus.tsx
IndexingStatus.tsx
Raw
import { IoFlashOutline, IoCheckmarkCircleOutline, IoWarningOutline } from 'react-icons/io5'
import { SearchMode } from './SearchInput'

interface IndexingStatusProps {
  mode: SearchMode
  indexingStatus: any
  isIndexing: boolean
  onStartIndexing: () => void
}

export default function IndexingStatus({ 
  mode, 
  indexingStatus, 
  isIndexing, 
  onStartIndexing 
}: IndexingStatusProps) {
  // Only show for semantic search mode
  if (mode !== 'semantic') return null

  const needsIndexing = indexingStatus && 
    (indexingStatus.pendingFiles > 0 || indexingStatus.totalFiles === 0)

  const indexingProgress = indexingStatus ? 
    Math.round((indexingStatus.completedFiles / indexingStatus.totalFiles) * 100) : 0

  const isComplete = indexingStatus && indexingStatus.pendingFiles === 0 && indexingStatus.totalFiles > 0

  return (
    <div className="px-3 py-2.5 border-b border-slate-800/60 backdrop-blur-sm">
      {/* Indexing Status */}
      <div className="flex items-center justify-between">
        <div className="flex items-center gap-2.5">
          {isComplete ? (
            <>
              <div className="p-1 bg-emerald-500/20 rounded-md backdrop-blur-sm border border-emerald-500/30">
                <IoCheckmarkCircleOutline className="w-3.5 h-3.5 text-emerald-400" />
              </div>
              <div>
                <span className="text-xs font-semibold text-slate-200">
                  {indexingStatus.totalFiles} indexed
                </span>
                <div className="text-xs text-emerald-400/80 mt-0.5">Ready</div>
              </div>
            </>
          ) : needsIndexing ? (
            <>
              <div className="p-1 bg-amber-500/20 rounded-md backdrop-blur-sm border border-amber-500/30">
                <IoWarningOutline className="w-3.5 h-3.5 text-amber-400" />
              </div>
              <div>
                <span className="text-xs font-semibold text-slate-300">
                  {indexingStatus?.pendingFiles || 0} need indexing
                </span>
                <div className="text-xs text-amber-400/80 mt-0.5">Index to enable AI</div>
              </div>
            </>
          ) : (
            <>
              <div className="p-1 bg-violet-500/20 rounded-md backdrop-blur-sm border border-violet-500/30">
                <IoFlashOutline className="w-3.5 h-3.5 text-violet-400" />
              </div>
              <div>
                <span className="text-xs font-semibold text-slate-300">AI Ready</span>
                <div className="text-xs text-violet-400/80 mt-0.5">Indexed</div>
              </div>
            </>
          )}
        </div>

        {needsIndexing && (
          <button
            onClick={onStartIndexing}
            disabled={isIndexing}
            className="group flex items-center gap-1.5 text-xs font-semibold px-3 py-1.5 bg-gradient-to-r from-violet-600/90 to-purple-600/90 hover:from-violet-600 hover:to-purple-600 disabled:from-violet-800/50 disabled:to-purple-800/50 text-white rounded-md transition-all duration-200 disabled:cursor-not-allowed shadow-lg backdrop-blur-sm border border-violet-500/20 transform hover:scale-105 disabled:transform-none"
          >
            {isIndexing ? (
              <>
                <div className="animate-spin rounded-full h-3 w-3 border-2 border-white/30 border-t-white"></div>
                <span>Indexing</span>
              </>
            ) : (
              <>
                <IoFlashOutline className="w-3 h-3 group-hover:animate-pulse" />
                <span>Index</span>
              </>
            )}
          </button>
        )}
      </div>

      {/* Progress Bar */}
      {isIndexing && indexingStatus && (
        <div className="mt-3 space-y-1.5">
          <div className="relative w-full bg-slate-800/60 rounded-full h-1.5 overflow-hidden backdrop-blur-sm border border-slate-700/50">
            <div 
              className="absolute top-0 left-0 h-full bg-gradient-to-r from-violet-500 via-purple-500 to-blue-500 transition-all duration-500 ease-out rounded-full"
              style={{ width: `${indexingProgress}%` }}
            >
              <div className="absolute inset-0 bg-gradient-to-r from-white/20 to-transparent animate-pulse"></div>
            </div>
          </div>
          <div className="flex justify-between items-center text-xs">
            <span className="text-slate-400 font-medium">
              Processing...
            </span>
            <div className="flex items-center gap-1.5">
              <span className="text-violet-400 font-semibold">
                {indexingStatus.completedFiles}/{indexingStatus.totalFiles}
              </span>
              <span className="text-slate-500 bg-slate-800/50 px-1.5 py-0.5 rounded text-xs border border-slate-700/50">
                {indexingProgress}%
              </span>
            </div>
          </div>
        </div>
      )}

      {/* Help Text */}
      {mode === 'semantic' && !isIndexing && (
        <div className="mt-2.5 p-2.5 bg-slate-900/40 backdrop-blur-sm rounded-md border border-slate-800/50">
          <div className="text-xs font-medium text-slate-400 leading-tight">
            {isComplete ? (
              <span className="flex items-center gap-1.5">
                <span className="text-emerald-400">🎯</span>
                AI search by meaning & context
              </span>
            ) : needsIndexing ? (
              <span className="flex items-center gap-1.5">
                <span className="text-amber-400"></span>
                Enable AI-powered semantic search
              </span>
            ) : (
              <span className="flex items-center gap-1.5">
                <span className="text-violet-400"></span>
                Search by concepts & themes
              </span>
            )}
          </div>
        </div>
      )}
    </div>
  )
}