/** * Component: Manual Import Browse Phase * Documentation: documentation/features/manual-import.md * * Directory listing with root tiles, breadcrumb navigation, * folder metadata, audio file badges, and selection state. */ 'use client'; import React from 'react'; import { FolderIcon, FolderOpenIcon, FolderArrowDownIcon, InboxArrowDownIcon, HomeIcon, ChevronRightIcon, ArrowLeftIcon, MusicalNoteIcon, ExclamationTriangleIcon, ArrowPathIcon, } from '@heroicons/react/24/outline'; import { RootEntry, DirectoryEntry, AudioFileEntry, formatBytes } from './types'; function SkeletonRow() { return (
); } interface BrowsePhaseProps { roots: RootEntry[]; currentPath: string | null; entries: DirectoryEntry[]; currentAudioFiles: AudioFileEntry[]; checkedFiles: Set; isLoading: boolean; error: string | null; hoveredFolder: string | null; breadcrumbs: Array<{ label: string; index: number }>; slideClass: string; onNavigateInto: (path: string) => void; onNavigateBack: () => void; onNavigateToRoot: () => void; onNavigateToBreadcrumb: (index: number) => void; onFolderClick: (entry: DirectoryEntry) => void; onSelectFiles: () => void; onToggleFile: (fileName: string) => void; onHoverFolder: (name: string | null) => void; onRetry: () => void; } export function BrowsePhase({ roots, currentPath, entries, currentAudioFiles, checkedFiles, isLoading, error, hoveredFolder, breadcrumbs, slideClass, onNavigateInto, onNavigateBack, onNavigateToRoot, onNavigateToBreadcrumb, onFolderClick, onSelectFiles, onToggleFile, onHoverFolder, onRetry, }: BrowsePhaseProps) { const hasSelection = checkedFiles.size > 0; const totalSize = currentAudioFiles.reduce((sum, f) => sum + f.size, 0); const checkedSize = hasSelection ? currentAudioFiles.filter((f) => checkedFiles.has(f.name)).reduce((sum, f) => sum + f.size, 0) : totalSize; return (
{/* Breadcrumb bar */} {currentPath && (
{breadcrumbs.map((crumb, i) => ( {crumb.index === -1 ? ( ... ) : i === breadcrumbs.length - 1 ? ( {crumb.label} ) : ( )} ))}
)} {/* Listing */}
{/* Loading */} {isLoading && (
{[...Array(5)].map((_, i) => ( ))}
)} {/* Error */} {error && !isLoading && (

{error}

)} {/* Root view */} {!currentPath && !isLoading && !error && (
{roots.map((root) => ( ))}
)} {/* Directory + audio file listing */} {currentPath && !isLoading && !error && (entries.length > 0 || currentAudioFiles.length > 0) && (
{/* Subdirectories */} {entries.map((entry) => { const isHovered = hoveredFolder === entry.name; return ( ); })} {/* Audio files in current directory */} {currentAudioFiles.length > 0 && entries.length > 0 && (

Audio Files {hasSelection && `\u00B7 click to select`}

)} {currentAudioFiles.map((file) => { const isSelected = checkedFiles.has(file.name); return ( ); })}
)} {/* Empty state */} {currentPath && !isLoading && !error && entries.length === 0 && currentAudioFiles.length === 0 && (

This folder is empty

)}
{/* Footer */} {currentPath && !isLoading && currentAudioFiles.length > 0 && (

{hasSelection ? ( <> {checkedFiles.size} {' '}of {currentAudioFiles.length} file{currentAudioFiles.length !== 1 ? 's' : ''} selected ) : ( <> {currentAudioFiles.length} {' '}audio file{currentAudioFiles.length !== 1 ? 's' : ''} in this folder )} {checkedSize > 0 && ( · {formatBytes(checkedSize)} )}

)}
); }