import { useState, useEffect, useRef, useCallback } from 'react'; import { api, apiUpload, S } from '../lib.js'; import { useConfirm } from '../confirm.jsx'; import { UploadIcon, DownloadIcon, TrashIcon, SearchIcon, ChevronIcon } from '../icons.jsx'; const fmtSize = b => b<1024?`${b} B`:b<1024*1024?`${(b/1024).toFixed(1)} KB`:`${(b/(1024*1024)).toFixed(1)} MB`; const fmtDate = s => { if (!s) return ''; const d = new Date(String(s).replace(' ','T')); return isNaN(d)?'':d.toLocaleDateString('de-DE',{day:'2-digit',month:'2-digit',year:'numeric'}); }; const extIcon = name => { const e = name.split('.').pop().toLowerCase(); if (['jpg','jpeg','png','gif','webp'].includes(e)) return 'πΌ'; if (e==='pdf') return 'π'; if (['zip','rar','7z'].includes(e)) return 'π¦'; if (['mp4','mov','avi'].includes(e)) return 'π¬'; if (['mp3','wav'].includes(e)) return 'π΅'; if (['stl','3mf'].includes(e)) return 'π¨'; if (['txt','md'].includes(e)) return 'π'; if (['xlsx','csv'].includes(e)) return 'π'; if (['docx','doc'].includes(e)) return 'π'; return 'π'; }; const IconBtn = ({ onClick, color, title, children, stop }) => ( ); // ββ Γffentlicher Link-Share Modal βββββββββββββββββββββββββββββββββββββββββββββ function PublicShareModal({ item, itemType, onClose, onShareCreated, toast }) { const [password, setPassword] = useState(() => genPw()); const [expiresH, setExpiresH] = useState(1); const [label, setLabel] = useState(''); const [shares, setShares] = useState([]); const [creating, setCreating] = useState(false); const [newLink, setNewLink] = useState(null); const [newPw, setNewPw] = useState(''); const isFolder = itemType === 'folder'; const base = '/tools/dateien/public-shares'; function genPw() { const upper = 'ABCDEFGHJKLMNPQRSTUVWXYZ'; const lower = 'abcdefghjkmnpqrstuvwxyz'; const digits = '23456789'; const special = '!@#$%&*-+?'; const all = upper + lower + digits + special; // Mindestens je 1 aus jeder Kategorie, Rest zufΓ€llig const pick = s => s[Math.floor(Math.random()*s.length)]; const base = [pick(upper), pick(lower), pick(digits), pick(special)]; const rest = Array.from({length:8}, () => pick(all)); return [...base, ...rest].sort(() => Math.random()-0.5).join(''); } useEffect(() => { api(base).then(d => setShares((d.shares||[]).filter(s => isFolder ? s.folder_id === item.id : s.file_id === item.id ))).catch(()=>{}); }, []); async function createShare() { if (!password.trim()) { toast('Passwort ist Pflicht'); return; } setCreating(true); try { const body = { [isFolder ? 'folder_id' : 'file_id']: item.id, password: password.trim(), expires_hours: expiresH, label: label || undefined, }; const d = await api(base, { body }); const link = `${window.location.origin}/s/${d.token}`; setNewLink(link); setNewPw(password.trim()); setShares(p => [d.share, ...p]); if (onShareCreated) { onShareCreated(d.token, password.trim()); } navigator.clipboard?.writeText(link).catch(()=>{}); toast('Link erstellt & kopiert β'); setPassword(genPw()); } catch(e) { toast('Fehler: ' + e.message); } finally { setCreating(false); } } async function deleteShare(id) { await api(`${base}/${id}`, { method:'DELETE' }); setShares(p => p.filter(s => s.id !== id)); toast('Link gelΓΆscht'); } function copyLink(token) { const link = `${window.location.origin}/s/${token}`; navigator.clipboard?.writeText(link).then(() => toast('Link kopiert β')).catch(() => toast(link)); } function fmtExp(exp) { if (!exp) return 'Kein Ablauf'; const d = new Date(exp * 1000); return d.toLocaleDateString('de-DE') + ' ' + d.toLocaleTimeString('de-DE', {hour:'2-digit',minute:'2-digit'}); } const expiryOptions = [ { label:'1 Stunde', h:1 }, { label:'24 Stunden', h:24 }, { label:'7 Tage', h:168 }, { label:'30 Tage', h:720 }, ]; return (