import { useState } from 'react'
import { FileSystemItem, CreateFileSystemItemRequest } from '@/lib/types/database'
import FileExplorer from './FileExplorer'
import { CreateFileDialog, RenameDialog, DeleteConfirmDialog } from './FileDialogs'
import { IoAddCircleOutline, IoDocumentTextOutline, IoFolderOutline } from 'react-icons/io5'
interface ExplorerTabProps {
files: (FileSystemItem & { children?: FileSystemItem[] })[]
onFileSelect: (file: FileSystemItem) => void
selectedFile: FileSystemItem | null
projectName: string
bookId: string
createItem: (bookId: string, data: CreateFileSystemItemRequest) => Promise<FileSystemItem | null>
updateItem: (bookId: string, fileId: string, data: any) => Promise<FileSystemItem | null>
deleteItem: (bookId: string, fileId: string) => Promise<boolean>
loading: boolean
}
interface DialogState {
type: 'create-file' | 'create-folder' | 'rename' | 'delete' | null
parentId?: string
parentName?: string
item?: FileSystemItem
}
export default function ExplorerTab({
files,
onFileSelect,
selectedFile,
projectName,
bookId,
createItem,
updateItem,
deleteItem,
loading
}: ExplorerTabProps) {
const [dialog, setDialog] = useState<DialogState>({ type: null })
const handleCreateFile = (parentId?: string) => {
const parent = parentId ? findItemById(files, parentId) : null
setDialog({
type: 'create-file',
parentId,
parentName: parent?.name || projectName
})
}
const handleCreateFolder = (parentId?: string) => {
const parent = parentId ? findItemById(files, parentId) : null
setDialog({
type: 'create-folder',
parentId,
parentName: parent?.name || projectName
})
}
const handleRename = (item: FileSystemItem) => {
setDialog({
type: 'rename',
item
})
}
const handleDelete = (item: FileSystemItem) => {
setDialog({
type: 'delete',
item
})
}
const handleDeleteConfirm = async () => {
if (dialog.item) {
const success = await deleteItem(bookId, dialog.item.id)
if (success && selectedFile?.id === dialog.item.id) {
onFileSelect(null as any)
}
// Dispatch event to notify other components
if (typeof window !== 'undefined') {
window.dispatchEvent(new CustomEvent('file-operation-completed', {
detail: { type: 'delete', fileId: dialog.item.id, fileName: dialog.item.name }
}))
}
}
}
const handleMove = async (item: FileSystemItem, newParentId?: string) => {
try {
const success = await updateItem(bookId, item.id, {
parent_id: newParentId || null
})
if (success && selectedFile?.id === item.id) {
onFileSelect(success)
}
} catch (error) {
console.error('Error moving item:', error)
}
}
const handleCreateSubmit = async (name: string, content?: string) => {
const type = dialog.type === 'create-file' ? 'file' : 'folder'
const data: CreateFileSystemItemRequest = {
book_id: bookId,
parent_id: dialog.parentId || null,
name,
type: type as 'file' | 'folder',
...(type === 'file' && {
content: content || '',
file_extension: name.includes('.') ? name.split('.').pop() : 'md',
mime_type: 'text/markdown'
})
}
const newItem = await createItem(bookId, data)
if (newItem) {
if (type === 'file') {
onFileSelect(newItem)
}
// Dispatch event to notify other components
if (typeof window !== 'undefined') {
window.dispatchEvent(new CustomEvent('file-operation-completed', {
detail: { type: 'create', itemType: type, fileId: newItem.id, fileName: newItem.name }
}))
}
}
}
const handleRenameSubmit = async (newName: string) => {
if (dialog.item) {
const updatedItem = await updateItem(bookId, dialog.item.id, { name: newName })
if (updatedItem && selectedFile?.id === dialog.item.id) {
onFileSelect(updatedItem)
}
}
}
const closeDialog = () => {
setDialog({ type: null })
}
return (
<>
<div className="flex-1 overflow-y-auto min-h-0">
<div className="px-4 py-3">
<div className="flex items-center justify-between group">
<div className="flex items-center text-sm text-slate-200 font-medium">
<div className="w-8 h-8 rounded-xl bg-gradient-to-br from-blue-500 to-purple-600 flex items-center justify-center mr-3">
<IoFolderOutline className="w-4 h-4 text-white" />
</div>
{projectName}
</div>
<div className="flex items-center gap-2 opacity-0 group-hover:opacity-100 transition-all duration-200">
<button
onClick={() => handleCreateFolder()}
className="p-1.5 text-slate-400 hover:text-slate-200 hover:bg-slate-700/50 rounded-lg transition-all duration-200"
title="New Folder"
disabled={loading}
>
<IoAddCircleOutline className="w-4 h-4" />
</button>
<button
onClick={() => handleCreateFile()}
className="p-1.5 text-slate-400 hover:text-slate-200 hover:bg-slate-700/50 rounded-lg transition-all duration-200"
title="New File"
disabled={loading}
>
<IoDocumentTextOutline className="w-4 h-4" />
</button>
</div>
</div>
</div>
<FileExplorer
files={files}
onFileSelect={onFileSelect}
selectedFile={selectedFile}
onCreateFile={handleCreateFile}
onCreateFolder={handleCreateFolder}
onRename={handleRename}
onDelete={handleDelete}
onMove={handleMove}
bookId={bookId}
loading={loading}
/>
</div>
<CreateFileDialog
isOpen={dialog.type === 'create-file'}
onClose={closeDialog}
onSubmit={handleCreateSubmit}
type="file"
parentName={dialog.parentName}
loading={loading}
/>
<CreateFileDialog
isOpen={dialog.type === 'create-folder'}
onClose={closeDialog}
onSubmit={handleCreateSubmit}
type="folder"
parentName={dialog.parentName}
loading={loading}
/>
{dialog.item && (
<RenameDialog
isOpen={dialog.type === 'rename'}
onClose={closeDialog}
onSubmit={handleRenameSubmit}
currentName={dialog.item.name}
type={dialog.item.type}
loading={loading}
/>
)}
{dialog.item && (
<DeleteConfirmDialog
isOpen={dialog.type === 'delete'}
onClose={closeDialog}
onConfirm={handleDeleteConfirm}
itemName={dialog.item.name}
itemType={dialog.item.type}
loading={loading}
/>
)}
</>
)
}
function findItemById(
items: (FileSystemItem & { children?: FileSystemItem[] })[],
id: string
): FileSystemItem | null {
for (const item of items) {
if (item.id === id) return item
if (item.children) {
const found = findItemById(item.children, id)
if (found) return found
}
}
return null
}