import React, { useState, useEffect, useCallback } from 'react';
import { useDropzone } from 'react-dropzone';
import * as Dialog from '@radix-ui/react-dialog';
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
import toast from 'react-hot-toast';
import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
import 'dayjs/locale/id';
import {
  Upload, Search, X, MoreHorizontal, Eye, Download, Pencil, Share2,
  Link2, Trash2, FileText, Image, Film, Music, FileArchive, File,
  RefreshCw, CheckCircle2, Copy, ExternalLink, FolderPlus, Folder,
  FolderOpen, ChevronRight, Home, RotateCcw, AlertTriangle, Trash,
  FolderInput
} from 'lucide-react';
import {
  getFiles, initUpload, uploadToR2, completeUpload,
  deleteFile, renameFile, shareFile, getDownloadUrl, getPreviewUrl,
  createFolder, renameFolder, deleteFolder, shareFolder,
  restoreFile, restoreFolder, moveFile, moveFolder,
  getTrash, emptyTrash
} from '../api';

dayjs.extend(relativeTime);
dayjs.locale('id');

function humanSize(bytes) {
  if (!bytes) return '0 B';
  const u = ['B', 'KB', 'MB', 'GB', 'TB'];
  let i = 0, v = bytes;
  while (v >= 1024 && i < u.length - 1) { v /= 1024; i++; }
  return v.toFixed(1).replace(/\.0$/, '') + ' ' + u[i];
}

function fileIcon(mime) {
  if (mime && mime.startsWith('image/')) return { icon: Image, color: 'var(--pink)', bg: 'var(--pink-soft)', border: 'var(--pink)' };
  if (mime && mime.startsWith('video/')) return { icon: Film, color: 'var(--blue)', bg: 'var(--blue-soft)', border: 'var(--blue)' };
  if (mime && mime.startsWith('audio/')) return { icon: Music, color: 'var(--teal)', bg: 'var(--teal-soft)', border: 'var(--teal)' };
  if (mime === 'application/pdf') return { icon: FileText, color: 'var(--danger)', bg: 'var(--danger-bg)', border: 'var(--danger)' };
  if (mime && mime.startsWith('text/')) return { icon: FileText, color: 'var(--accent)', bg: 'var(--accent-soft)', border: 'var(--accent)' };
  if (mime && (mime.includes('zip') || mime.includes('rar') || mime.includes('7z'))) return { icon: FileArchive, color: 'var(--yellow)', bg: 'var(--yellow-soft)', border: 'var(--yellow)' };
  return { icon: File, color: 'var(--ink-muted)', bg: 'var(--panel-alt)', border: 'var(--border-soft)' };
}

function fileTypeBadge(mime) {
  if (mime && mime.startsWith('image/')) return { label: 'IMG', cls: 'badge-pink' };
  if (mime && mime.startsWith('video/')) return { label: 'VID', cls: 'badge-blue' };
  if (mime && mime.startsWith('audio/')) return { label: 'AUD', cls: 'badge-teal' };
  if (mime === 'application/pdf') return { label: 'PDF', cls: 'badge-pink' };
  if (mime && mime.startsWith('text/')) return { label: 'TXT', cls: 'badge-accent' };
  if (mime && (mime.includes('zip') || mime.includes('rar') || mime.includes('7z'))) return { label: 'ZIP', cls: 'badge-yellow' };
  return { label: 'FILE', cls: 'badge-accent' };
}

function canPreview(mime) {
  return (mime && mime.startsWith('image/')) || mime === 'application/pdf';
}

function SkeletonRows() {
  return (
    <div>
      {Array.from({ length: 5 }).map(function(_, i) {
        return (
          <div key={i} className="file-row">
            <div className="skeleton" style={{ width: 40, height: 40, borderRadius: 8, flexShrink: 0 }} />
            <div className="flex-1 min-w-0">
              <div className="skeleton mb-2" style={{ width: '55%', height: 14 }} />
              <div className="skeleton" style={{ width: '30%', height: 11 }} />
            </div>
          </div>
        );
      })}
    </div>
  );
}

function EmptyState({ hasSearch, onUpload, isTrash }) {
  return (
    <div className="flex flex-col items-center justify-center py-16 px-6 h-full">
      <div
        className="rounded-xl mb-4 flex items-center justify-center"
        style={{ width: 80, height: 80, background: 'var(--accent-soft)', border: '2.5px solid var(--border-soft)', boxShadow: 'var(--shadow-btn)' }}
      >
        <span style={{ fontSize: 36 }}>{isTrash ? '🗑️' : hasSearch ? '🔍' : '📂'}</span>
      </div>
      <p className="font-bold mb-1 text-base" style={{ color: 'var(--ink)' }}>
        {isTrash ? 'Sampah kosong' : hasSearch ? 'Tidak ada hasil' : 'Belum ada file'}
      </p>
      <p className="text-sm mb-4 text-center" style={{ color: 'var(--ink-muted)', maxWidth: 260 }}>
        {isTrash ? 'File dan folder yang dihapus akan muncul di sini.' : hasSearch ? 'Coba ubah kata kunci pencarian.' : 'Seret file ke sini atau klik Unggah.'}
      </p>
      {!hasSearch && !isTrash && (
        <button onClick={onUpload} className="btn btn-primary">
          <Upload size={16} /> Unggah File
        </button>
      )}
    </div>
  );
}

function UploadItem({ item, onRemove, onRetry }) {
  var fi = fileIcon(item.file ? item.file.type : '');
  var IconComp = fi.icon;
  return (
    <div className="flex items-center gap-3 py-2.5 px-3" style={{ fontSize: 13, borderBottom: '1.5px dashed var(--border-faint)' }}>
      <div className="file-icon-wrap" style={{ width: 32, height: 32, background: fi.bg, borderColor: fi.border, fontSize: 12, borderWidth: 1.5 }}>
        <IconComp size={14} style={{ color: fi.color }} />
      </div>
      <div className="flex-1 min-w-0">
        <div className="truncate font-semibold text-sm">{item.name}</div>
        {item.status === 'uploading' && (
          <div className="progress-track mt-1" style={{ height: 14, borderWidth: 1.5 }}>
            <div className="progress-fill" style={{ width: item.progress + '%' }} />
            <div className="progress-text" style={{ fontSize: 10 }}>{item.progress}%</div>
          </div>
        )}
        {item.status === 'error' && (
          <p className="truncate mt-0.5 text-xs" style={{ color: 'var(--danger)' }}>{item.error}</p>
        )}
      </div>
      <div className="flex items-center gap-1 flex-shrink-0">
        {item.status === 'done' && <CheckCircle2 size={18} style={{ color: 'var(--success)' }} />}
        {item.status === 'error' && (
          <button onClick={function() { onRetry(item); }} className="btn btn-ghost btn-sm btn-icon">
            <RefreshCw size={14} />
          </button>
        )}
        {item.status !== 'uploading' && (
          <button onClick={function() { onRemove(item.id); }} className="btn btn-ghost btn-sm btn-icon">
            <X size={14} />
          </button>
        )}
      </div>
    </div>
  );
}

export default function Dashboard({ usage, onUsageChange, onTrashChange, navigate, page, trashCount }) {
  var isTrashView = page === 'trash';

  var [files, setFiles] = useState([]);
  var [folders, setFolders] = useState([]);
  var [breadcrumb, setBreadcrumb] = useState([]);
  var [currentFolderId, setCurrentFolderId] = useState(null);
  var [loading, setLoading] = useState(true);
  var [search, setSearch] = useState('');
  var [uploads, setUploads] = useState([]);

  var [trashedFiles, setTrashedFiles] = useState([]);
  var [trashedFolders, setTrashedFolders] = useState([]);
  var [emptyingTrash, setEmptyingTrash] = useState(false);

  var [renameModal, setRenameModal] = useState(null);
  var [renameName, setRenameName] = useState('');
  var [renameLoading, setRenameLoading] = useState(false);

  var [previewModal, setPreviewModal] = useState(null);
  var [previewData, setPreviewData] = useState(null);

  var [shareModal, setShareModal] = useState(null);
  var [shareLoading, setShareLoading] = useState(false);

  var [deleteModal, setDeleteModal] = useState(null);
  var [deleteLoading, setDeleteLoading] = useState(false);

  var [newFolderModal, setNewFolderModal] = useState(false);
  var [newFolderName, setNewFolderName] = useState('');
  var [newFolderLoading, setNewFolderLoading] = useState(false);

  var [moveModal, setMoveModal] = useState(null);
  var [moveTargetId, setMoveTargetId] = useState('__root__');
  var [moveFoldersList, setMoveFoldersList] = useState([]);
  var [moveLoading, setMoveLoading] = useState(false);

  var [copied, setCopied] = useState(false);
  var [isDragOver, setIsDragOver] = useState(false);

  var loadFiles = useCallback(function(folderId) {
    setLoading(true);
    getFiles(folderId).then(function(res) {
      setFiles(res.data.files || []);
      setFolders(res.data.folders || []);
      setBreadcrumb(res.data.breadcrumb || []);
      setCurrentFolderId(folderId);
    }).catch(function() {
      toast.error('Gagal memuat daftar file.');
    }).finally(function() {
      setLoading(false);
    });
  }, []);

  var loadTrash = useCallback(function() {
    setLoading(true);
    getTrash().then(function(res) {
      setTrashedFiles(res.data.files || []);
      setTrashedFolders(res.data.folders || []);
    }).catch(function() {
      toast.error('Gagal memuat sampah.');
    }).finally(function() {
      setLoading(false);
    });
  }, []);

  useEffect(function() {
    if (isTrashView) {
      loadTrash();
    } else {
      loadFiles(currentFolderId);
    }
  }, [isTrashView]); // eslint-disable-line

  var navigateToFolder = function(folderId) {
    if (isTrashView) return;
    setCurrentFolderId(folderId);
    loadFiles(folderId);
  };

  var loadFoldersForMove = useCallback(function() {
    getFiles(null).then(function(res) {
      setMoveFoldersList(res.data.folders || []);
    }).catch(function() {
      setMoveFoldersList([]);
    });
  }, []);

  var processUpload = useCallback(function(file, uid) {
    return initUpload(file.name, file.type || 'application/octet-stream', file.size).then(function(initRes) {
      var data = initRes.data;
      return uploadToR2(data.uploadUrl, file, data.contentType, function(progress) {
        setUploads(function(prev) {
          return prev.map(function(u) { return u.id === uid ? Object.assign({}, u, { progress: progress }) : u; });
        });
      }).then(function() {
        return completeUpload(data.objectKey, file.name, file.name, file.type || 'application/octet-stream', currentFolderId);
      });
    }).then(function() {
      setUploads(function(prev) {
        return prev.map(function(u) { return u.id === uid ? Object.assign({}, u, { progress: 100, status: 'done' }) : u; });
      });
      toast.success(file.name + ' berhasil diunggah!');
    }).catch(function(err) {
      var msg = (err.response && err.response.data && err.response.data.error && err.response.data.error.message) || 'Gagal mengunggah.';
      setUploads(function(prev) {
        return prev.map(function(u) { return u.id === uid ? Object.assign({}, u, { status: 'error', error: msg }) : u; });
      });
      toast.error(file.name + ': ' + msg);
    });
  }, [currentFolderId]);

  var onDrop = useCallback(function(acceptedFiles) {
    setIsDragOver(false);
    if (isTrashView) return;
    var newItems = acceptedFiles.map(function(file) {
      return {
        id: Date.now() + '_' + Math.random().toString(36).slice(2),
        name: file.name, progress: 0, status: 'uploading', error: null, file: file
      };
    });
    setUploads(function(prev) { return newItems.concat(prev); });
    var chain = Promise.resolve();
    newItems.forEach(function(item) {
      chain = chain.then(function() {
        return processUpload(item.file, item.id);
      });
    });
    chain.then(function() {
      loadFiles(currentFolderId);
      if (onUsageChange) onUsageChange();
    });
  }, [processUpload, loadFiles, onUsageChange, isTrashView, currentFolderId]);

  var retryUpload = useCallback(function(item) {
    setUploads(function(prev) {
      return prev.map(function(u) { return u.id === item.id ? Object.assign({}, u, { status: 'uploading', progress: 0, error: null }) : u; });
    });
    processUpload(item.file, item.id).then(function() {
      loadFiles(currentFolderId);
      if (onUsageChange) onUsageChange();
    });
  }, [processUpload, loadFiles, onUsageChange, currentFolderId]);

  var dropzone = useDropzone({
    onDrop: onDrop,
    onDragEnter: function() { setIsDragOver(true); },
    onDragLeave: function() { setIsDragOver(false); },
    multiple: true, noClick: true, noKeyboard: true,
  });

  var removeUpload = function(id) {
    setUploads(function(prev) { return prev.filter(function(u) { return u.id !== id; }); });
  };

  var handleDownload = function(file) {
    getDownloadUrl(file.id).then(function(res) {
      window.open(res.data.url, '_blank');
    }).catch(function() {
      toast.error('Gagal mendapatkan tautan unduhan.');
    });
  };

  var handlePreview = function(file) {
    setPreviewModal(file);
    setPreviewData(null);
    getPreviewUrl(file.id).then(function(res) {
      setPreviewData(res.data);
    }).catch(function() {
      toast.error('Gagal memuat pratinjau.');
    });
  };

  var handleDelete = function() {
    if (!deleteModal) return;
    setDeleteLoading(true);
    var promise = deleteModal.type === 'file'
      ? deleteFile(deleteModal.item.id, isTrashView)
      : deleteFolder(deleteModal.item.id, isTrashView);
    promise.then(function() {
      if (deleteModal.type === 'file') {
        if (isTrashView) setTrashedFiles(function(p) { return p.filter(function(f) { return f.id !== deleteModal.item.id; }); });
        else setFiles(function(p) { return p.filter(function(f) { return f.id !== deleteModal.item.id; }); });
      } else {
        if (isTrashView) setTrashedFolders(function(p) { return p.filter(function(f) { return f.id !== deleteModal.item.id; }); });
        else setFolders(function(p) { return p.filter(function(f) { return f.id !== deleteModal.item.id; }); });
      }
      toast.success(isTrashView ? 'Dihapus permanen.' : 'Dipindahkan ke sampah.');
      setDeleteModal(null);
      if (onUsageChange) onUsageChange();
      if (onTrashChange) onTrashChange();
    }).catch(function(err) {
      toast.error((err.response && err.response.data && err.response.data.error && err.response.data.error.message) || 'Gagal menghapus.');
    }).finally(function() { setDeleteLoading(false); });
  };

  var handleRestore = function(type, item) {
    var promise = type === 'file' ? restoreFile(item.id) : restoreFolder(item.id);
    promise.then(function() {
      if (type === 'file') setTrashedFiles(function(p) { return p.filter(function(f) { return f.id !== item.id; }); });
      else setTrashedFolders(function(p) { return p.filter(function(f) { return f.id !== item.id; }); });
      toast.success('Berhasil dipulihkan!');
      if (onTrashChange) onTrashChange();
    }).catch(function(err) {
      toast.error((err.response && err.response.data && err.response.data.error && err.response.data.error.message) || 'Gagal memulihkan.');
    });
  };

  var handleEmptyTrash = function() {
    setEmptyingTrash(true);
    emptyTrash().then(function() {
      setTrashedFiles([]);
      setTrashedFolders([]);
      toast.success('Sampah dikosongkan!');
      if (onUsageChange) onUsageChange();
      if (onTrashChange) onTrashChange();
    }).catch(function(err) {
      toast.error((err.response && err.response.data && err.response.data.error && err.response.data.error.message) || 'Gagal mengosongkan sampah.');
    }).finally(function() { setEmptyingTrash(false); });
  };

  var handleRename = function() {
    if (!renameModal || !renameName.trim()) return;
    setRenameLoading(true);
    var promise = renameModal.type === 'file'
      ? renameFile(renameModal.item.id, renameName.trim())
      : renameFolder(renameModal.item.id, renameName.trim());
    promise.then(function() {
      if (renameModal.type === 'file') {
        setFiles(function(p) { return p.map(function(f) { return f.id === renameModal.item.id ? Object.assign({}, f, { display_name: renameName.trim() }) : f; }); });
      } else {
        setFolders(function(p) { return p.map(function(f) { return f.id === renameModal.item.id ? Object.assign({}, f, { name: renameName.trim() }) : f; }); });
      }
      toast.success('Nama berhasil diubah.');
      setRenameModal(null);
    }).catch(function(err) {
      toast.error((err.response && err.response.data && err.response.data.error && err.response.data.error.message) || 'Gagal mengganti nama.');
    }).finally(function() { setRenameLoading(false); });
  };

  var handleShare = function(type, item, enabled) {
    setShareLoading(true);
    var promise = type === 'file' ? shareFile(item.id, enabled) : shareFolder(item.id, enabled);
    promise.then(function(res) {
      var updated = Object.assign({}, item, { shareUrl: res.data.shareUrl, shared_enabled: enabled ? 1 : 0, shared_token: res.data.token });
      setShareModal({ type: type, item: updated });
      if (type === 'file') {
        setFiles(function(p) { return p.map(function(f) { return f.id === item.id ? Object.assign({}, f, { shared_enabled: enabled ? 1 : 0, shareUrl: res.data.shareUrl }) : f; }); });
      } else {
        setFolders(function(p) { return p.map(function(f) { return f.id === item.id ? Object.assign({}, f, { shared_enabled: enabled ? 1 : 0, shareUrl: res.data.shareUrl }) : f; }); });
      }
      toast.success(enabled ? 'Tautan publik aktif!' : 'Tautan publik dinonaktifkan.');
    }).catch(function(err) {
      toast.error((err.response && err.response.data && err.response.data.error && err.response.data.error.message) || 'Gagal mengubah berbagi.');
    }).finally(function() { setShareLoading(false); });
  };

  var handleCreateFolder = function() {
    if (!newFolderName.trim()) return;
    setNewFolderLoading(true);
    createFolder(newFolderName.trim(), currentFolderId).then(function(res) {
      setFolders(function(p) { return [res.data.folder].concat(p); });
      toast.success('Folder berhasil dibuat!');
      setNewFolderModal(false);
      setNewFolderName('');
    }).catch(function(err) {
      toast.error((err.response && err.response.data && err.response.data.error && err.response.data.error.message) || 'Gagal membuat folder.');
    }).finally(function() { setNewFolderLoading(false); });
  };

  var openMoveModal = function(type, item) {
    setMoveModal({ type: type, item: item });
    setMoveTargetId('__root__');
    loadFoldersForMove();
  };

  var handleMove = function() {
    if (!moveModal) return;
    setMoveLoading(true);
    var targetId = moveTargetId === '__root__' ? null : parseInt(moveTargetId);
    var promise = moveModal.type === 'file'
      ? moveFile(moveModal.item.id, targetId)
      : moveFolder(moveModal.item.id, targetId);
    promise.then(function() {
      toast.success('Berhasil dipindahkan!');
      setMoveModal(null);
      loadFiles(currentFolderId);
    }).catch(function(err) {
      toast.error((err.response && err.response.data && err.response.data.error && err.response.data.error.message) || 'Gagal memindahkan.');
    }).finally(function() { setMoveLoading(false); });
  };

  var copyLink = function(url) {
    navigator.clipboard.writeText(url);
    setCopied(true);
    toast.success('Tautan disalin!');
    setTimeout(function() { setCopied(false); }, 2000);
  };

  var displayFiles = isTrashView ? trashedFiles : files;
  var displayFolders = isTrashView ? trashedFolders : folders;
  var filteredFiles = displayFiles.filter(function(f) {
    return (f.display_name || f.original_name || '').toLowerCase().includes(search.toLowerCase());
  });
  var filteredFolders = displayFolders.filter(function(f) {
    return f.name.toLowerCase().includes(search.toLowerCase());
  });
  var totalItems = filteredFiles.length + filteredFolders.length;
  var activeUploads = uploads.filter(function(u) { return u.status === 'uploading'; }).length;
  var hasUploads = uploads.length > 0;

  return (
    <div {...dropzone.getRootProps()} className="flex flex-col h-full gap-2 min-h-0">
      <input {...dropzone.getInputProps()} />

      {isDragOver && !isTrashView && (
        <div
          className="fixed inset-0 z-50 flex items-center justify-center"
          style={{ background: 'rgba(155, 109, 215, 0.15)', backdropFilter: 'blur(4px)' }}
          onDragLeave={function() { setIsDragOver(false); }}
        >
          <div className="window-card" style={{ width: 340 }}>
            <div className="window-header">
              <div className="window-dots">
                <div className="window-dot window-dot-red" />
                <div className="window-dot window-dot-yellow" />
                <div className="window-dot window-dot-green" />
              </div>
              <div className="window-title">Unggah File</div>
            </div>
            <div className="window-body text-center py-10">
              <span style={{ fontSize: 48, display: 'block', marginBottom: 12 }}>📥</span>
              <p className="font-bold mb-2">Lepaskan file di sini</p>
              <p className="text-sm" style={{ color: 'var(--ink-muted)' }}>
                {currentFolderId ? 'File akan diunggah ke folder ini' : 'File akan langsung diunggah'}
              </p>
            </div>
          </div>
        </div>
      )}

      {hasUploads && (
        <div className="window-card flex-shrink-0" style={{ maxHeight: 160 }}>
          <div className="window-header" style={{ background: 'linear-gradient(135deg, var(--teal), var(--blue))' }}>
            <div className="window-dots">
              <div className="window-dot window-dot-red" />
              <div className="window-dot window-dot-yellow" />
              <div className="window-dot window-dot-green" />
            </div>
            <div className="window-title">
              {'📋 Unggah ' + (activeUploads > 0 ? '(' + activeUploads + ' aktif)' : '✓')}
            </div>
            {uploads.every(function(u) { return u.status !== 'uploading'; }) && (
              <button onClick={function() { setUploads([]); }} className="btn btn-ghost btn-sm" style={{ color: '#fff', borderColor: 'transparent' }}>
                <X size={14} /> Tutup
              </button>
            )}
          </div>
          <div style={{ maxHeight: 110, overflowY: 'auto', background: 'var(--panel)' }}>
            {uploads.map(function(u) {
              return <UploadItem key={u.id} item={u} onRemove={removeUpload} onRetry={retryUpload} />;
            })}
          </div>
        </div>
      )}

      <div className="window-card flex-1 flex flex-col min-h-0">
        <div className="window-header">
          <div className="window-dots">
            <div className="window-dot window-dot-red" />
            <div className="window-dot window-dot-yellow" />
            <div className="window-dot window-dot-green" />
          </div>
          <div className="window-title">
            {isTrashView ? '🗑️ Tempat Sampah' : '📂 File Manager'}
          </div>
        </div>

        {/* Toolbar */}
        <div className="flex flex-wrap items-center gap-2 px-4 py-3 flex-shrink-0" style={{ background: 'var(--panel-alt)', borderBottom: '1.5px solid var(--border-faint)' }}>
          {isTrashView ? (
            <React.Fragment>
              <button onClick={function() { navigate('/app'); }} className="btn btn-secondary btn-sm">
                <FolderOpen size={14} /> File Saya
              </button>
              {(trashedFiles.length > 0 || trashedFolders.length > 0) && (
                <button onClick={handleEmptyTrash} disabled={emptyingTrash} className="btn btn-danger btn-sm">
                  <Trash size={14} /> {emptyingTrash ? 'Mengosongkan...' : 'Kosongkan Sampah'}
                </button>
              )}
            </React.Fragment>
          ) : (
            <React.Fragment>
              <button onClick={dropzone.open} className="btn btn-primary btn-sm">
                <Upload size={14} /> Unggah
              </button>
              <button onClick={function() { setNewFolderName(''); setNewFolderModal(true); }} className="btn btn-secondary btn-sm">
                <FolderPlus size={14} /> Folder Baru
              </button>
              <button onClick={function() { loadFiles(currentFolderId); }} className="btn btn-secondary btn-sm btn-icon" title="Muat ulang">
                <RefreshCw size={14} />
              </button>
            </React.Fragment>
          )}
          <div className="flex-1" />
          <div className="relative">
            <Search size={14} className="absolute left-3 top-1/2 -translate-y-1/2" style={{ color: 'var(--ink-muted)' }} />
            <input type="text" value={search} onChange={function(e) { setSearch(e.target.value); }} placeholder="Cari..." className="input input-sm" style={{ paddingLeft: 32, width: 180 }} />
            {search && (
              <button onClick={function() { setSearch(''); }} className="absolute right-2 top-1/2 -translate-y-1/2 btn btn-ghost btn-sm btn-icon" style={{ minWidth: 24, minHeight: 24 }}>
                <X size={12} />
              </button>
            )}
          </div>
        </div>

        {/* Breadcrumb */}
        {!isTrashView && (
          <div className="flex items-center gap-1 px-4 py-2 flex-shrink-0 overflow-x-auto" style={{ background: 'var(--panel)', borderBottom: '1.5px dashed var(--border-faint)', fontSize: 13, minHeight: 36 }}>
            <button onClick={function() { navigateToFolder(null); }} className="btn btn-ghost btn-sm" style={{ padding: '2px 8px', gap: 4 }}>
              <Home size={14} /> Root
            </button>
            {breadcrumb.map(function(bc) {
              return (
                <React.Fragment key={bc.id}>
                  <ChevronRight size={12} style={{ color: 'var(--ink-faint)', flexShrink: 0 }} />
                  <button onClick={function() { navigateToFolder(bc.id); }} className="btn btn-ghost btn-sm truncate" style={{ padding: '2px 8px', maxWidth: 140 }}>
                    {bc.name}
                  </button>
                </React.Fragment>
              );
            })}
          </div>
        )}

        {/* Column headers */}
        <div className="hidden sm:flex items-center px-4 py-2 gap-3 flex-shrink-0" style={{ background: 'var(--panel-alt)', borderBottom: '2px solid var(--border-faint)', fontSize: 12, fontWeight: 700, color: 'var(--ink-muted)', textTransform: 'uppercase', letterSpacing: '0.5px' }}>
          <div style={{ width: 40 }} />
          <div className="flex-1">Nama</div>
          <div style={{ width: 56, textAlign: 'center' }}>Tipe</div>
          <div style={{ width: 80, textAlign: 'right' }}>Ukuran</div>
          <div style={{ width: 100, textAlign: 'center' }}>Tanggal</div>
          <div style={{ width: 40 }} />
        </div>

        {/* FILE LIST */}
        <div className="flex-1 overflow-y-auto min-h-0" style={{ background: 'var(--panel)' }}>
          {loading ? (
            <SkeletonRows />
          ) : totalItems === 0 ? (
            <EmptyState hasSearch={search.length > 0} onUpload={dropzone.open} isTrash={isTrashView} />
          ) : (
            <React.Fragment>
              {filteredFolders.map(function(folder) {
                return (
                  <FolderRow
                    key={'folder-' + folder.id}
                    folder={folder}
                    isTrash={isTrashView}
                    onOpen={function() { navigateToFolder(folder.id); }}
                    onRename={function() { setRenameModal({ type: 'folder', item: folder }); setRenameName(folder.name); }}
                    onShare={function() { setShareModal({ type: 'folder', item: folder }); }}
                    onDelete={function() { setDeleteModal({ type: 'folder', item: folder }); }}
                    onRestore={function() { handleRestore('folder', folder); }}
                    onMove={function() { openMoveModal('folder', folder); }}
                  />
                );
              })}
              {filteredFiles.map(function(file) {
                return (
                  <FileRow
                    key={'file-' + file.id}
                    file={file}
                    isTrash={isTrashView}
                    onPreview={function() { handlePreview(file); }}
                    onDownload={function() { handleDownload(file); }}
                    onRename={function() { setRenameModal({ type: 'file', item: file }); setRenameName(file.display_name); }}
                    onShare={function() { setShareModal({ type: 'file', item: file }); }}
                    onDelete={function() { setDeleteModal({ type: 'file', item: file }); }}
                    onRestore={function() { handleRestore('file', file); }}
                    onMove={function() { openMoveModal('file', file); }}
                  />
                );
              })}
            </React.Fragment>
          )}
        </div>

        {/* Footer */}
        {!loading && (
          <div className="px-4 py-2 text-center flex-shrink-0 flex items-center justify-between" style={{ fontSize: 12, color: 'var(--ink-muted)', borderTop: '1.5px dashed var(--border-faint)', background: 'var(--panel-alt)' }}>
            <span>
              {isTrashView
                ? trashedFolders.length + ' folder, ' + trashedFiles.length + ' file dalam sampah'
                : filteredFolders.length + ' folder, ' + filteredFiles.length + ' file'
              }
            </span>
            {usage && <span>{usage.usedHuman} / {usage.quotaHuman}</span>}
          </div>
        )}
      </div>

      {/* ═══ NEW FOLDER DIALOG ═══ */}
      <Dialog.Root open={newFolderModal} onOpenChange={function(o) { if (!o) setNewFolderModal(false); }}>
        <Dialog.Portal>
          <Dialog.Overlay className="dialog-overlay" />
          <Dialog.Content className="dialog-content" style={{ maxWidth: 440 }} aria-describedby={undefined}>
            <div className="window-card">
              <Dialog.Title asChild>
                <div className="window-header">
                  <div className="window-dots"><div className="window-dot window-dot-red" /><div className="window-dot window-dot-yellow" /><div className="window-dot window-dot-green" /></div>
                  <div className="window-title">📁 Folder Baru</div>
                  <Dialog.Close asChild>
                    <button className="btn btn-ghost btn-sm" style={{ color: '#fff', borderColor: 'transparent' }}><X size={14} /></button>
                  </Dialog.Close>
                </div>
              </Dialog.Title>
              <div className="window-body">
                <p className="text-sm mb-3" style={{ color: 'var(--ink-secondary)' }}>Nama folder:</p>
                <form onSubmit={function(e) { e.preventDefault(); handleCreateFolder(); }}>
                  <input type="text" value={newFolderName} onChange={function(e) { setNewFolderName(e.target.value); }} className="input mb-4" placeholder="Folder baru" autoFocus />
                  <div className="flex justify-end gap-3">
                    <Dialog.Close asChild>
                      <button type="button" className="btn btn-secondary btn-sm">Batal</button>
                    </Dialog.Close>
                    <button type="submit" disabled={newFolderLoading || !newFolderName.trim()} className="btn btn-primary btn-sm">
                      {newFolderLoading ? '⏳ ...' : '📁 Buat'}
                    </button>
                  </div>
                </form>
              </div>
            </div>
          </Dialog.Content>
        </Dialog.Portal>
      </Dialog.Root>

      {/* ═══ RENAME DIALOG ═══ */}
      <Dialog.Root open={!!renameModal} onOpenChange={function(o) { if (!o) setRenameModal(null); }}>
        <Dialog.Portal>
          <Dialog.Overlay className="dialog-overlay" />
          <Dialog.Content className="dialog-content" style={{ maxWidth: 440 }} aria-describedby={undefined}>
            <div className="window-card">
              <Dialog.Title asChild>
                <div className="window-header">
                  <div className="window-dots"><div className="window-dot window-dot-red" /><div className="window-dot window-dot-yellow" /><div className="window-dot window-dot-green" /></div>
                  <div className="window-title">✏️ Ganti Nama</div>
                  <Dialog.Close asChild>
                    <button className="btn btn-ghost btn-sm" style={{ color: '#fff', borderColor: 'transparent' }}><X size={14} /></button>
                  </Dialog.Close>
                </div>
              </Dialog.Title>
              <div className="window-body">
                <p className="text-sm mb-3" style={{ color: 'var(--ink-secondary)' }}>
                  {'Masukkan nama baru untuk ' + (renameModal && renameModal.type === 'folder' ? 'folder' : 'file') + ':'}
                </p>
                <form onSubmit={function(e) { e.preventDefault(); handleRename(); }}>
                  <input type="text" value={renameName} onChange={function(e) { setRenameName(e.target.value); }} className="input mb-4" autoFocus />
                  <div className="flex justify-end gap-3">
                    <Dialog.Close asChild>
                      <button type="button" className="btn btn-secondary btn-sm">Batal</button>
                    </Dialog.Close>
                    <button type="submit" disabled={renameLoading || !renameName.trim()} className="btn btn-primary btn-sm">
                      {renameLoading ? '⏳ ...' : '💾 Simpan'}
                    </button>
                  </div>
                </form>
              </div>
            </div>
          </Dialog.Content>
        </Dialog.Portal>
      </Dialog.Root>

      {/* ═══ DELETE DIALOG ═══ */}
      <Dialog.Root open={!!deleteModal} onOpenChange={function(o) { if (!o) setDeleteModal(null); }}>
        <Dialog.Portal>
          <Dialog.Overlay className="dialog-overlay" />
          <Dialog.Content className="dialog-content" style={{ maxWidth: 440 }} aria-describedby={undefined}>
            <div className="window-card">
              <Dialog.Title asChild>
                <div className="window-header" style={{ background: 'linear-gradient(135deg, var(--danger), var(--danger-hover))' }}>
                  <div className="window-dots"><div className="window-dot window-dot-red" /><div className="window-dot window-dot-yellow" /><div className="window-dot window-dot-green" /></div>
                  <div className="window-title">
                    {isTrashView ? '⚠️ Hapus Permanen' : '🗑️ Pindah ke Sampah'}
                  </div>
                  <Dialog.Close asChild>
                    <button className="btn btn-ghost btn-sm" style={{ color: '#fff', borderColor: 'transparent' }}><X size={14} /></button>
                  </Dialog.Close>
                </div>
              </Dialog.Title>
              <div className="window-body">
                <div className="flex items-start gap-4 mb-4">
                  <div className="rounded-lg p-2 flex-shrink-0" style={{ background: 'var(--danger-bg)', border: '2px solid var(--danger)' }}>
                    {isTrashView ? <AlertTriangle size={24} style={{ color: 'var(--danger)' }} /> : <Trash2 size={24} style={{ color: 'var(--danger)' }} />}
                  </div>
                  <div>
                    <p className="font-bold mb-2">
                      {isTrashView ? 'Hapus permanen?' : 'Pindahkan ke sampah?'}
                    </p>
                    <div className="p-3 rounded-lg mb-2" style={{ background: 'var(--panel-alt)', border: '1.5px solid var(--border-faint)' }}>
                      <p className="font-bold truncate text-sm">
                        {deleteModal && deleteModal.type === 'folder' ? '📁 ' + deleteModal.item.name : (deleteModal ? deleteModal.item.display_name : '')}
                      </p>
                      {deleteModal && deleteModal.type === 'file' && (
                        <p className="text-xs mt-0.5" style={{ color: 'var(--ink-muted)' }}>{humanSize(deleteModal.item.size_bytes)}</p>
                      )}
                    </div>
                    <p className="text-sm" style={{ color: 'var(--danger)' }}>
                      {isTrashView ? 'Tidak dapat dikembalikan setelah dihapus permanen.' : 'Anda bisa memulihkannya dari Tempat Sampah.'}
                    </p>
                  </div>
                </div>
                <div className="flex justify-end gap-3">
                  <Dialog.Close asChild>
                    <button className="btn btn-secondary btn-sm">Batal</button>
                  </Dialog.Close>
                  <button onClick={handleDelete} disabled={deleteLoading} className="btn btn-danger btn-sm">
                    {deleteLoading ? '⏳ ...' : (isTrashView ? '🗑️ Hapus Permanen' : '🗑️ Hapus')}
                  </button>
                </div>
              </div>
            </div>
          </Dialog.Content>
        </Dialog.Portal>
      </Dialog.Root>

      {/* ═══ SHARE DIALOG ═══ */}
      <Dialog.Root open={!!shareModal} onOpenChange={function(o) { if (!o) setShareModal(null); }}>
        <Dialog.Portal>
          <Dialog.Overlay className="dialog-overlay" />
          <Dialog.Content className="dialog-content" style={{ maxWidth: 480 }} aria-describedby={undefined}>
            <div className="window-card">
              <Dialog.Title asChild>
                <div className="window-header" style={{ background: 'linear-gradient(135deg, var(--teal), var(--accent))' }}>
                  <div className="window-dots"><div className="window-dot window-dot-red" /><div className="window-dot window-dot-yellow" /><div className="window-dot window-dot-green" /></div>
                  <div className="window-title">🔗 Bagikan</div>
                  <Dialog.Close asChild>
                    <button className="btn btn-ghost btn-sm" style={{ color: '#fff', borderColor: 'transparent' }}><X size={14} /></button>
                  </Dialog.Close>
                </div>
              </Dialog.Title>
              <div className="window-body">
                <div className="p-3 rounded-lg mb-4" style={{ background: 'var(--panel-alt)', border: '1.5px solid var(--border-faint)' }}>
                  <span className="font-bold truncate block text-sm">
                    {shareModal && shareModal.type === 'folder' ? '📁 ' + shareModal.item.name : (shareModal ? shareModal.item.display_name : '')}
                  </span>
                </div>
                <div className="flex items-center justify-between p-3 rounded-lg mb-4" style={{ background: 'var(--accent-soft)', border: '2px solid var(--border-soft)' }}>
                  <div className="flex items-center gap-2">
                    <ExternalLink size={16} style={{ color: 'var(--accent)' }} />
                    <span className="font-bold text-sm">Tautan Publik</span>
                  </div>
                  <label className="flex items-center gap-2 cursor-pointer">
                    <input
                      type="checkbox"
                      checked={!!(shareModal && shareModal.item && shareModal.item.shared_enabled)}
                      onChange={function(e) { handleShare(shareModal.type, shareModal.item, e.target.checked); }}
                      disabled={shareLoading}
                      className="w-4 h-4 rounded"
                      style={{ accentColor: 'var(--accent)' }}
                    />
                    <span className="text-sm font-semibold">
                      {shareModal && shareModal.item && shareModal.item.shared_enabled ? 'Aktif' : 'Nonaktif'}
                    </span>
                  </label>
                </div>
                {shareModal && shareModal.item && shareModal.item.shared_enabled && shareModal.item.shareUrl && (
                  <div>
                    <p className="text-sm mb-2" style={{ color: 'var(--ink-secondary)' }}>Tautan:</p>
                    <div className="flex gap-2">
                      <input type="text" readOnly value={shareModal.item.shareUrl} className="input input-sm flex-1" style={{ fontSize: 13 }} />
                      <button onClick={function() { copyLink(shareModal.item.shareUrl); }} className="btn btn-primary btn-sm">
                        {copied ? <React.Fragment><CheckCircle2 size={14} /> Ok</React.Fragment> : <React.Fragment><Copy size={14} /> Salin</React.Fragment>}
                      </button>
                    </div>
                    <p className="text-xs mt-2" style={{ color: 'var(--ink-muted)' }}>
                      ⚠ Siapa pun dengan tautan ini dapat mengakses.
                    </p>
                  </div>
                )}
              </div>
            </div>
          </Dialog.Content>
        </Dialog.Portal>
      </Dialog.Root>

      {/* ═══ MOVE DIALOG ═══ */}
      <Dialog.Root open={!!moveModal} onOpenChange={function(o) { if (!o) setMoveModal(null); }}>
        <Dialog.Portal>
          <Dialog.Overlay className="dialog-overlay" />
          <Dialog.Content className="dialog-content" style={{ maxWidth: 440 }} aria-describedby={undefined}>
            <div className="window-card">
              <Dialog.Title asChild>
                <div className="window-header" style={{ background: 'linear-gradient(135deg, var(--blue), var(--accent))' }}>
                  <div className="window-dots"><div className="window-dot window-dot-red" /><div className="window-dot window-dot-yellow" /><div className="window-dot window-dot-green" /></div>
                  <div className="window-title">📦 Pindahkan</div>
                  <Dialog.Close asChild>
                    <button className="btn btn-ghost btn-sm" style={{ color: '#fff', borderColor: 'transparent' }}><X size={14} /></button>
                  </Dialog.Close>
                </div>
              </Dialog.Title>
              <div className="window-body">
                <div className="p-3 rounded-lg mb-3" style={{ background: 'var(--panel-alt)', border: '1.5px solid var(--border-faint)' }}>
                  <span className="font-bold truncate block text-sm">
                    {moveModal && moveModal.type === 'folder' ? '📁 ' + moveModal.item.name : (moveModal ? '📄 ' + moveModal.item.display_name : '')}
                  </span>
                </div>
                <p className="text-sm mb-3" style={{ color: 'var(--ink-secondary)' }}>Pilih folder tujuan:</p>
                <div className="mb-4" style={{ maxHeight: 240, overflowY: 'auto', border: '2px solid var(--border-faint)', borderRadius: 10, background: 'var(--input-bg)' }}>
                  <div
                    onClick={function() { setMoveTargetId('__root__'); }}
                    className="flex items-center gap-3 px-4 py-3 cursor-pointer"
                    style={{
                      background: moveTargetId === '__root__' ? 'var(--accent-soft)' : 'transparent',
                      borderBottom: '1.5px solid var(--border-faint)',
                      fontSize: 14
                    }}
                  >
                    <Home size={16} style={{ color: moveTargetId === '__root__' ? 'var(--accent)' : 'var(--ink-muted)' }} />
                    <span className="font-semibold" style={{ color: moveTargetId === '__root__' ? 'var(--accent)' : 'var(--ink)' }}>Root (Beranda)</span>
                  </div>
                  {moveFoldersList.filter(function(f) {
                    return !(moveModal && moveModal.type === 'folder' && f.id === moveModal.item.id);
                  }).map(function(f) {
                    var isSelected = moveTargetId === String(f.id);
                    return (
                      <div
                        key={f.id}
                        onClick={function() { setMoveTargetId(String(f.id)); }}
                        className="flex items-center gap-3 px-4 py-3 cursor-pointer"
                        style={{
                          background: isSelected ? 'var(--accent-soft)' : 'transparent',
                          borderBottom: '1.5px solid var(--border-faint)',
                          fontSize: 14
                        }}
                      >
                        <Folder size={16} style={{ color: isSelected ? 'var(--accent)' : 'var(--yellow)' }} />
                        <span className="font-semibold truncate" style={{ color: isSelected ? 'var(--accent)' : 'var(--ink)' }}>{f.name}</span>
                      </div>
                    );
                  })}
                  {moveFoldersList.length === 0 && (
                    <div className="px-4 py-4 text-center text-sm" style={{ color: 'var(--ink-muted)' }}>
                      Tidak ada folder lain.
                    </div>
                  )}
                </div>
                <div className="flex justify-end gap-3">
                  <Dialog.Close asChild>
                    <button className="btn btn-secondary btn-sm">Batal</button>
                  </Dialog.Close>
                  <button onClick={handleMove} disabled={moveLoading} className="btn btn-primary btn-sm">
                    {moveLoading ? '⏳ ...' : '📦 Pindahkan'}
                  </button>
                </div>
              </div>
            </div>
          </Dialog.Content>
        </Dialog.Portal>
      </Dialog.Root>

      {/* ═══ PREVIEW DIALOG ═══ */}
      <Dialog.Root open={!!previewModal} onOpenChange={function(o) { if (!o) { setPreviewModal(null); setPreviewData(null); } }}>
        <Dialog.Portal>
          <Dialog.Overlay className="dialog-overlay" />
          <Dialog.Content className="dialog-content" style={{ maxWidth: 900, width: '95%' }} aria-describedby={undefined}>
            <div className="window-card flex flex-col" style={{ maxHeight: '88vh' }}>
              <Dialog.Title asChild>
                <div className="window-header flex-shrink-0">
                  <div className="window-dots"><div className="window-dot window-dot-red" /><div className="window-dot window-dot-yellow" /><div className="window-dot window-dot-green" /></div>
                  <div className="window-title truncate">
                    {'👁️ ' + (previewModal ? previewModal.display_name : '')}
                  </div>
                  <button onClick={function() { if (previewModal) handleDownload(previewModal); }} className="btn btn-ghost btn-sm" style={{ color: '#fff', borderColor: 'transparent' }}>
                    <Download size={14} /> Unduh
                  </button>
                  <Dialog.Close asChild>
                    <button className="btn btn-ghost btn-sm" style={{ color: '#fff', borderColor: 'transparent' }}><X size={14} /></button>
                  </Dialog.Close>
                </div>
              </Dialog.Title>
              <div className="flex-1 overflow-auto min-h-0" style={{ background: 'var(--panel-alt)' }}>
                <div className="m-3 rounded-lg overflow-hidden" style={{ background: 'var(--input-bg)', border: '2.5px solid var(--border-faint)', minHeight: 200 }}>
                  {!previewData ? (
                    <div className="flex items-center justify-center py-20">
                      <div className="text-center">
                        <span className="text-3xl block mb-3 animate-bounce">⏳</span>
                        <p className="text-sm" style={{ color: 'var(--ink-muted)' }}>Memuat pratinjau...</p>
                      </div>
                    </div>
                  ) : previewData.mimeType && previewData.mimeType.startsWith('image/') ? (
                    <div className="flex items-center justify-center p-4">
                      <img src={previewData.url} alt={previewModal ? previewModal.display_name : ''} style={{ maxWidth: '100%', maxHeight: '68vh', objectFit: 'contain', borderRadius: 6 }} />
                    </div>
                  ) : previewData.mimeType === 'application/pdf' ? (
                    <iframe src={previewData.url} style={{ width: '100%', height: '68vh', border: 'none' }} title="Pratinjau PDF" />
                  ) : (
                    <div className="flex flex-col items-center justify-center py-20">
                      <File size={48} style={{ color: 'var(--ink-muted)', marginBottom: 12 }} />
                      <p className="text-base">Pratinjau tidak tersedia.</p>
                    </div>
                  )}
                </div>
              </div>
              <div className="flex items-center justify-between px-4 py-2 flex-shrink-0" style={{ fontSize: 12, color: 'var(--ink-muted)', borderTop: '1.5px solid var(--border-faint)', background: 'var(--panel-alt)' }}>
                <span>{previewModal ? humanSize(previewModal.size_bytes) : ''}</span>
                <span>{previewModal ? previewModal.mime_type : ''}</span>
              </div>
            </div>
          </Dialog.Content>
        </Dialog.Portal>
      </Dialog.Root>
    </div>
  );
}


function FolderRow({ folder, isTrash, onOpen, onRename, onShare, onDelete, onRestore, onMove }) {
  var timeAgo = dayjs(folder.created_at).fromNow();
  return (
    <div className="file-row" onDoubleClick={isTrash ? undefined : onOpen}>
      <div className="file-icon-wrap" style={{ background: 'var(--yellow-soft)', borderColor: 'var(--yellow)' }}>
        <Folder size={18} style={{ color: 'var(--yellow)' }} />
      </div>
      <div className="flex-1 min-w-0">
        <div className="flex items-center gap-2">
          <span className="truncate font-semibold text-sm">{folder.name}</span>
          {folder.shared_enabled ? <Link2 size={12} style={{ color: 'var(--teal)', flexShrink: 0 }} title="Publik" /> : null}
        </div>
        <div className="flex items-center gap-2 sm:hidden mt-0.5" style={{ fontSize: 12, color: 'var(--ink-muted)' }}>
          <span>Folder</span><span>·</span><span>{timeAgo}</span>
        </div>
      </div>
      <div className="hidden sm:block" style={{ width: 56, textAlign: 'center' }}>
        <span className="badge badge-yellow">DIR</span>
      </div>
      <div className="hidden sm:block" style={{ width: 80, textAlign: 'right', fontSize: 13, color: 'var(--ink-secondary)' }}>—</div>
      <div className="hidden sm:block" style={{ width: 100, textAlign: 'center', fontSize: 13, color: 'var(--ink-muted)' }}>{timeAgo}</div>
      <div style={{ width: 40, flexShrink: 0 }}>
        <DropdownMenu.Root>
          <DropdownMenu.Trigger asChild>
            <button className="btn btn-ghost btn-icon btn-sm"><MoreHorizontal size={16} /></button>
          </DropdownMenu.Trigger>
          <DropdownMenu.Portal>
            <DropdownMenu.Content className="dropdown-content" align="end" sideOffset={4}>
              {isTrash ? (
                <React.Fragment>
                  <DropdownMenu.Item onClick={onRestore} className="dropdown-item"><RotateCcw size={15} /> Pulihkan</DropdownMenu.Item>
                  <DropdownMenu.Separator className="dropdown-separator" />
                  <DropdownMenu.Item onClick={onDelete} className="dropdown-item dropdown-item-danger"><Trash2 size={15} /> Hapus Permanen</DropdownMenu.Item>
                </React.Fragment>
              ) : (
                <React.Fragment>
                  <DropdownMenu.Item onClick={onOpen} className="dropdown-item"><FolderOpen size={15} /> Buka</DropdownMenu.Item>
                  <DropdownMenu.Item onClick={onRename} className="dropdown-item"><Pencil size={15} /> Ganti Nama</DropdownMenu.Item>
                  <DropdownMenu.Item onClick={onMove} className="dropdown-item"><FolderInput size={15} /> Pindahkan</DropdownMenu.Item>
                  <DropdownMenu.Item onClick={onShare} className="dropdown-item"><Share2 size={15} /> Bagikan</DropdownMenu.Item>
                  <DropdownMenu.Separator className="dropdown-separator" />
                  <DropdownMenu.Item onClick={onDelete} className="dropdown-item dropdown-item-danger"><Trash2 size={15} /> Hapus</DropdownMenu.Item>
                </React.Fragment>
              )}
            </DropdownMenu.Content>
          </DropdownMenu.Portal>
        </DropdownMenu.Root>
      </div>
    </div>
  );
}


function FileRow({ file, isTrash, onPreview, onDownload, onRename, onShare, onDelete, onRestore, onMove }) {
  var fi = fileIcon(file.mime_type);
  var IconComp = fi.icon;
  var badge = fileTypeBadge(file.mime_type);
  var previewable = canPreview(file.mime_type);
  var timeAgo = dayjs(file.created_at).fromNow();
  return (
    <div className="file-row" onDoubleClick={isTrash ? undefined : (previewable ? onPreview : onDownload)}>
      <div className="file-icon-wrap" style={{ background: fi.bg, borderColor: fi.border }}>
        <IconComp size={18} style={{ color: fi.color }} />
      </div>
      <div className="flex-1 min-w-0">
        <div className="flex items-center gap-2">
          <span className="truncate font-semibold text-sm">{file.display_name}</span>
          {file.shared_enabled ? <Link2 size={12} style={{ color: 'var(--teal)', flexShrink: 0 }} title="Publik" /> : null}
        </div>
        <div className="flex items-center gap-2 sm:hidden mt-0.5" style={{ fontSize: 12, color: 'var(--ink-muted)' }}>
          <span>{humanSize(file.size_bytes)}</span><span>·</span><span>{timeAgo}</span>
        </div>
      </div>
      <div className="hidden sm:block" style={{ width: 56, textAlign: 'center' }}>
        <span className={'badge ' + badge.cls}>{badge.label}</span>
      </div>
      <div className="hidden sm:block" style={{ width: 80, textAlign: 'right', fontSize: 13, color: 'var(--ink-secondary)' }}>
        {humanSize(file.size_bytes)}
      </div>
      <div className="hidden sm:block" style={{ width: 100, textAlign: 'center', fontSize: 13, color: 'var(--ink-muted)' }}>
        {timeAgo}
      </div>
      <div style={{ width: 40, flexShrink: 0 }}>
        <DropdownMenu.Root>
          <DropdownMenu.Trigger asChild>
            <button className="btn btn-ghost btn-icon btn-sm"><MoreHorizontal size={16} /></button>
          </DropdownMenu.Trigger>
          <DropdownMenu.Portal>
            <DropdownMenu.Content className="dropdown-content" align="end" sideOffset={4}>
              {isTrash ? (
                <React.Fragment>
                  <DropdownMenu.Item onClick={onRestore} className="dropdown-item"><RotateCcw size={15} /> Pulihkan</DropdownMenu.Item>
                  <DropdownMenu.Separator className="dropdown-separator" />
                  <DropdownMenu.Item onClick={onDelete} className="dropdown-item dropdown-item-danger"><Trash2 size={15} /> Hapus Permanen</DropdownMenu.Item>
                </React.Fragment>
              ) : (
                <React.Fragment>
                  {previewable && (
                    <DropdownMenu.Item onClick={onPreview} className="dropdown-item"><Eye size={15} /> Pratinjau</DropdownMenu.Item>
                  )}
                  <DropdownMenu.Item onClick={onDownload} className="dropdown-item"><Download size={15} /> Unduh</DropdownMenu.Item>
                  <DropdownMenu.Item onClick={onRename} className="dropdown-item"><Pencil size={15} /> Ganti Nama</DropdownMenu.Item>
                  <DropdownMenu.Item onClick={onMove} className="dropdown-item"><FolderInput size={15} /> Pindahkan</DropdownMenu.Item>
                  <DropdownMenu.Item onClick={onShare} className="dropdown-item"><Share2 size={15} /> Bagikan</DropdownMenu.Item>
                  <DropdownMenu.Separator className="dropdown-separator" />
                  <DropdownMenu.Item onClick={onDelete} className="dropdown-item dropdown-item-danger"><Trash2 size={15} /> Hapus</DropdownMenu.Item>
                </React.Fragment>
              )}
            </DropdownMenu.Content>
          </DropdownMenu.Portal>
        </DropdownMenu.Root>
      </div>
    </div>
  );
}
