Linkliste: Apps & Dev-Tools-Unterfunktionen als Schnellzugriff hinzufügbar (tool://-Schema)
This commit is contained in:
@@ -389,7 +389,7 @@ function BottomNav({ active, setActive, user, onLogout, unreadMsgs=0 }) {
|
|||||||
|
|
||||||
// ── Dashboard Widgets ─────────────────────────────────────────────────────────
|
// ── Dashboard Widgets ─────────────────────────────────────────────────────────
|
||||||
// ── QuickLinks Carousel (Mobile) ─────────────────────────────────────────────
|
// ── QuickLinks Carousel (Mobile) ─────────────────────────────────────────────
|
||||||
function QuickLinksCarousel({ links, onFolderClick }) {
|
function QuickLinksCarousel({ links, onFolderClick, navigateTool }) {
|
||||||
const perRow = 4;
|
const perRow = 4;
|
||||||
const gap = 6;
|
const gap = 6;
|
||||||
const pageSize = perRow * 2;
|
const pageSize = perRow * 2;
|
||||||
@@ -413,7 +413,7 @@ function QuickLinksCarousel({ links, onFolderClick }) {
|
|||||||
|
|
||||||
if (pageCount <= 1) return (
|
if (pageCount <= 1) return (
|
||||||
<div style={{ display:'grid', gridTemplateColumns:`repeat(${perRow}, 1fr)`, gap }}>
|
<div style={{ display:'grid', gridTemplateColumns:`repeat(${perRow}, 1fr)`, gap }}>
|
||||||
{links.map(l => <QuickLinkIcon key={l.id} l={l} onFolderClick={onFolderClick}/>)}
|
{links.map(l => <QuickLinkIcon key={l.id} l={l} onFolderClick={onFolderClick} navigateTool={navigateTool}/>)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -431,7 +431,7 @@ function QuickLinksCarousel({ links, onFolderClick }) {
|
|||||||
gap, flexShrink:0, scrollSnapAlign:'start',
|
gap, flexShrink:0, scrollSnapAlign:'start',
|
||||||
width:'100%', minWidth:'100%', boxSizing:'border-box',
|
width:'100%', minWidth:'100%', boxSizing:'border-box',
|
||||||
}}>
|
}}>
|
||||||
{links.slice(pi*pageSize, (pi+1)*pageSize).map(l => <QuickLinkIcon key={l.id} l={l} onFolderClick={onFolderClick}/>)}
|
{links.slice(pi*pageSize, (pi+1)*pageSize).map(l => <QuickLinkIcon key={l.id} l={l} onFolderClick={onFolderClick} navigateTool={navigateTool}/>)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -452,19 +452,20 @@ function QuickLinksCarousel({ links, onFolderClick }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function QuickLinkIcon({ l, onFolderClick }) {
|
function QuickLinkIcon({ l, onFolderClick, navigateTool }) {
|
||||||
const [imgOk, setImgOk] = useState(true);
|
const [imgOk, setImgOk] = useState(true);
|
||||||
|
const isTool = l.url?.startsWith('tool://');
|
||||||
|
|
||||||
const hasCustomIcon = l.icon && l.icon !== '🔗';
|
const hasCustomIcon = l.icon && l.icon !== '🔗';
|
||||||
const faviconUrl = (!hasCustomIcon && !l.isFolder) ? (() => {
|
const faviconUrl = (!hasCustomIcon && !l.isFolder && !isTool) ? (() => {
|
||||||
try { return `https://www.google.com/s2/favicons?sz=64&domain=${new URL(l.url).hostname}`; }
|
try { return `https://www.google.com/s2/favicons?sz=64&domain=${new URL(l.url).hostname}`; }
|
||||||
catch { return null; }
|
catch { return null; }
|
||||||
})() : null;
|
})() : null;
|
||||||
|
|
||||||
const showFavicon = !hasCustomIcon && !!faviconUrl && imgOk && !l.isFolder;
|
const showFavicon = !hasCustomIcon && !!faviconUrl && imgOk && !l.isFolder && !isTool;
|
||||||
const showCustomImg = hasCustomIcon && !l.isFolder && l.icon.startsWith('http');
|
const showCustomImg = hasCustomIcon && !l.isFolder && l.icon.startsWith('http');
|
||||||
const showEmoji = !showFavicon && !showCustomImg;
|
const showEmoji = !showFavicon && !showCustomImg;
|
||||||
const emoji = l.isFolder ? (l.icon||'📁') : (hasCustomIcon ? l.icon : '🔗');
|
const emoji = l.isFolder ? (l.icon||'📁') : (hasCustomIcon ? l.icon : (isTool ? '⚡' : '🔗'));
|
||||||
|
|
||||||
const content = (
|
const content = (
|
||||||
<>
|
<>
|
||||||
@@ -477,10 +478,13 @@ function QuickLinkIcon({ l, onFolderClick }) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const baseStyle = {display:'flex',flexDirection:'column',alignItems:'center',justifyContent:'center',gap:3,
|
const baseStyle = {display:'flex',flexDirection:'column',alignItems:'center',justifyContent:'center',gap:3,
|
||||||
padding:'4px 2px',background:'rgba(255,255,255,0.04)',
|
padding:'4px 2px',background: isTool ? 'rgba(160,132,250,0.06)' : 'rgba(255,255,255,0.04)',
|
||||||
border: l.isFolder ? '1px solid rgba(78,205,196,0.25)' : '1px solid rgba(255,255,255,0.08)',
|
border: l.isFolder ? '1px solid rgba(78,205,196,0.25)' : isTool ? '1px solid rgba(160,132,250,0.2)' : '1px solid rgba(255,255,255,0.08)',
|
||||||
borderRadius:10,textDecoration:'none',height:48,width:'100%',boxSizing:'border-box',cursor:'pointer'};
|
borderRadius:10,textDecoration:'none',height:48,width:'100%',boxSizing:'border-box',cursor:'pointer'};
|
||||||
|
|
||||||
|
if (isTool) {
|
||||||
|
return <button onClick={()=>navigateTool?.(l.url)} style={baseStyle}>{content}</button>;
|
||||||
|
}
|
||||||
if (l.isFolder && onFolderClick) {
|
if (l.isFolder && onFolderClick) {
|
||||||
const realId = typeof l.id==='string'&&l.id.startsWith('folder-') ? parseInt(l.id.replace('folder-','')) : l.id;
|
const realId = typeof l.id==='string'&&l.id.startsWith('folder-') ? parseInt(l.id.replace('folder-','')) : l.id;
|
||||||
return <button onClick={()=>onFolderClick({...l,id:realId})} style={{...baseStyle,background:'rgba(78,205,196,0.04)'}}>{content}</button>;
|
return <button onClick={()=>onFolderClick({...l,id:realId})} style={{...baseStyle,background:'rgba(78,205,196,0.04)'}}>{content}</button>;
|
||||||
@@ -488,7 +492,7 @@ function QuickLinkIcon({ l, onFolderClick }) {
|
|||||||
return <a href={l.url} target="_blank" rel="noopener noreferrer" style={baseStyle}>{content}</a>;
|
return <a href={l.url} target="_blank" rel="noopener noreferrer" style={baseStyle}>{content}</a>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function FolderModal({ folder, onClose }) {
|
function FolderModal({ folder, onClose, navigateTool }) {
|
||||||
const [links, setLinks] = useState([]);
|
const [links, setLinks] = useState([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
useEffect(()=>{
|
useEffect(()=>{
|
||||||
@@ -514,7 +518,7 @@ function FolderModal({ folder, onClose }) {
|
|||||||
{loading ? <div style={{color:'rgba(255,255,255,0.4)',fontFamily:'monospace',fontSize:12,textAlign:'center',padding:20}}>…</div>
|
{loading ? <div style={{color:'rgba(255,255,255,0.4)',fontFamily:'monospace',fontSize:12,textAlign:'center',padding:20}}>…</div>
|
||||||
: links.length===0 ? <div style={{color:'rgba(255,255,255,0.4)',fontFamily:'monospace',fontSize:12,textAlign:'center',padding:20}}>Ordner ist leer</div>
|
: links.length===0 ? <div style={{color:'rgba(255,255,255,0.4)',fontFamily:'monospace',fontSize:12,textAlign:'center',padding:20}}>Ordner ist leer</div>
|
||||||
: <div style={{display:'grid',gridTemplateColumns:'repeat(auto-fill,minmax(72px,1fr))',gap:8}}>
|
: <div style={{display:'grid',gridTemplateColumns:'repeat(auto-fill,minmax(72px,1fr))',gap:8}}>
|
||||||
{links.map(l=><QuickLinkIcon key={l.id} l={l}/>)}
|
{links.map(l=><QuickLinkIcon key={l.id} l={l} navigateTool={navigateTool}/>)}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@@ -522,13 +526,27 @@ function FolderModal({ folder, onClose }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function QuickLinks({ toast, mobile }) {
|
function QuickLinks({ toast, mobile, setActive, setDevToolsNav }) {
|
||||||
const [links, setLinks] = useState([]);
|
const [links, setLinks] = useState([]);
|
||||||
const [folderModal, setFolderModal] = useState(null);
|
const [folderModal, setFolderModal] = useState(null);
|
||||||
const [editMode, setEditMode] = useState(false);
|
const [editMode, setEditMode] = useState(false);
|
||||||
|
|
||||||
useEffect(() => { api('/dashboard/links').then(setLinks).catch(() => {}); }, []);
|
useEffect(() => { api('/dashboard/links').then(setLinks).catch(() => {}); }, []);
|
||||||
|
|
||||||
|
// Tool-Navigation für tool://-Links
|
||||||
|
const navigateTool = (url) => {
|
||||||
|
if (!url?.startsWith('tool://')) return;
|
||||||
|
const withoutScheme = url.replace('tool://', '');
|
||||||
|
const [toolId, query] = withoutScheme.split('?');
|
||||||
|
const sub = query?.replace('sub=', '');
|
||||||
|
if (toolId === 'devtools' && sub && setDevToolsNav) {
|
||||||
|
setActive('devtools');
|
||||||
|
setDevToolsNav({ tool: sub, ts: Date.now() });
|
||||||
|
} else if (setActive) {
|
||||||
|
setActive(toolId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const legacyItems = links.filter(l => l.item_type === 'quicklink' || !l.item_type);
|
const legacyItems = links.filter(l => l.item_type === 'quicklink' || !l.item_type);
|
||||||
const hasLegacy = legacyItems.length > 0;
|
const hasLegacy = legacyItems.length > 0;
|
||||||
|
|
||||||
@@ -547,7 +565,7 @@ function QuickLinks({ toast, mobile }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ ...S.card, marginBottom:10 }}>
|
<div style={{ ...S.card, marginBottom:10 }}>
|
||||||
{folderModal && <FolderModal folder={folderModal} onClose={()=>setFolderModal(null)}/>}
|
{folderModal && <FolderModal folder={folderModal} onClose={()=>setFolderModal(null)} navigateTool={navigateTool}/>}
|
||||||
<div style={{ display:'flex', alignItems:'center', justifyContent:'space-between', marginBottom:10 }}>
|
<div style={{ display:'flex', alignItems:'center', justifyContent:'space-between', marginBottom:10 }}>
|
||||||
<div style={S.head}>SCHNELLZUGRIFF</div>
|
<div style={S.head}>SCHNELLZUGRIFF</div>
|
||||||
{hasLegacy && (
|
{hasLegacy && (
|
||||||
@@ -591,9 +609,9 @@ function QuickLinks({ toast, mobile }) {
|
|||||||
Links oder Ordner in der Linkliste mit ● Schnellzugriff aktivieren.
|
Links oder Ordner in der Linkliste mit ● Schnellzugriff aktivieren.
|
||||||
</div>
|
</div>
|
||||||
: mobile
|
: mobile
|
||||||
? <QuickLinksCarousel links={allItems} onFolderClick={setFolderModal}/>
|
? <QuickLinksCarousel links={allItems} onFolderClick={setFolderModal} navigateTool={navigateTool}/>
|
||||||
: <div style={{ display:'grid', gridTemplateColumns:'repeat(auto-fill,minmax(58px,1fr))', gap:6 }}>
|
: <div style={{ display:'grid', gridTemplateColumns:'repeat(auto-fill,minmax(58px,1fr))', gap:6 }}>
|
||||||
{allItems.map(l => <QuickLinkIcon key={l.id} l={l} onFolderClick={l.isFolder?setFolderModal:undefined}/>)}
|
{allItems.map(l => <QuickLinkIcon key={l.id} l={l} onFolderClick={l.isFolder?setFolderModal:undefined} navigateTool={navigateTool}/>)}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@@ -1947,7 +1965,7 @@ function Dashboard({ toast, mobile, setActive, setDevToolsNav, unreadMsgs=0, unr
|
|||||||
</div>
|
</div>
|
||||||
{/* Globale Suche */}
|
{/* Globale Suche */}
|
||||||
<GlobalSearch setActive={setActive} setDevToolsNav={setDevToolsNav} mobile={mobile}/>
|
<GlobalSearch setActive={setActive} setDevToolsNav={setDevToolsNav} mobile={mobile}/>
|
||||||
<QuickLinks toast={toast} mobile={mobile} />
|
<QuickLinks toast={toast} mobile={mobile} setActive={setActive} setDevToolsNav={setDevToolsNav} />
|
||||||
<PushScheduler toast={toast}/>
|
<PushScheduler toast={toast}/>
|
||||||
<CalendarWidget/>
|
<CalendarWidget/>
|
||||||
<BestellStats/>
|
<BestellStats/>
|
||||||
|
|||||||
@@ -4,6 +4,122 @@ import { api, S } from '../lib.js';
|
|||||||
const LINK_ICONS = ['🔗','📄','🌐','📊','🎯','⚙️','📁','🔧','📱','💡','🏠','🛒','📺','🎮','🎵','📧','💬','📰','🔬','🏥','🏦','✈️','🚀','🔐','📂','⭐','🔑','💻','🖥','📡'];
|
const LINK_ICONS = ['🔗','📄','🌐','📊','🎯','⚙️','📁','🔧','📱','💡','🏠','🛒','📺','🎮','🎵','📧','💬','📰','🔬','🏥','🏦','✈️','🚀','🔐','📂','⭐','🔑','💻','🖥','📡'];
|
||||||
const FOLDER_ICONS = ['📁','📂','🗂','💼','🗃','📦','🏷','🔖','📋','📌','🗓','🖇'];
|
const FOLDER_ICONS = ['📁','📂','🗂','💼','🗃','📦','🏷','🔖','📋','📌','🗓','🖇'];
|
||||||
|
|
||||||
|
// ── Alle DockStation-Tools & Unterfunktionen ──────────────────────────────────
|
||||||
|
const TOOL_ENTRIES = [
|
||||||
|
{ url:'tool://statistik', label:'📊 Statistik', group:'3D-Druck' },
|
||||||
|
{ url:'tool://bestellungen', label:'📦 Bestellungen', group:'3D-Druck' },
|
||||||
|
{ url:'tool://kalkulator3d', label:'🧮 Kostenrechner', group:'3D-Druck' },
|
||||||
|
{ url:'tool://kanban', label:'🗂 Kanban', group:'Werkzeuge' },
|
||||||
|
{ url:'tool://dateien', label:'📁 Dateien', group:'Werkzeuge' },
|
||||||
|
{ url:'tool://nachrichten', label:'💬 Nachrichten', group:'Werkzeuge' },
|
||||||
|
{ url:'tool://linkliste', label:'🔗 Linkliste', group:'Werkzeuge' },
|
||||||
|
{ url:'tool://codeschnipsel', label:'</> Code-Schnipsel', group:'Werkzeuge' },
|
||||||
|
{ url:'tool://media', label:'🎬 Media / Kino', group:'Freizeit' },
|
||||||
|
{ url:'tool://devtools', label:'🔧 Dev-Tools (alle)', group:'Dev-Tools' },
|
||||||
|
{ url:'tool://devtools?sub=cron', label:'⏱ Crontab', group:'Dev-Tools' },
|
||||||
|
{ url:'tool://devtools?sub=elektro', label:'⚡ Elektro', group:'Dev-Tools' },
|
||||||
|
{ url:'tool://devtools?sub=json', label:'{ } JSON', group:'Dev-Tools' },
|
||||||
|
{ url:'tool://devtools?sub=regex', label:'🔍 Regex', group:'Dev-Tools' },
|
||||||
|
{ url:'tool://devtools?sub=diff', label:'± Text Diff', group:'Dev-Tools' },
|
||||||
|
{ url:'tool://devtools?sub=netzwerk', label:'🌐 Netzwerk', group:'Dev-Tools' },
|
||||||
|
{ url:'tool://devtools?sub=datetime', label:'📅 Datetime', group:'Dev-Tools' },
|
||||||
|
{ url:'tool://devtools?sub=farben', label:'🎨 Farben', group:'Dev-Tools' },
|
||||||
|
{ url:'tool://devtools?sub=keygen', label:'🔐 Key-Gen', group:'Dev-Tools' },
|
||||||
|
{ url:'tool://devtools?sub=qrcode', label:'◻ QR-Code', group:'Dev-Tools' },
|
||||||
|
];
|
||||||
|
|
||||||
|
// ── Tool-Picker Modal ─────────────────────────────────────────────────────────
|
||||||
|
function ToolPicker({ folders, onSave, onClose, toast }) {
|
||||||
|
const [search, setSearch] = useState('');
|
||||||
|
const [folderId, setFolderId] = useState(null);
|
||||||
|
const [saving, setSaving] = useState(null);
|
||||||
|
const mob = isMob();
|
||||||
|
|
||||||
|
const filtered = TOOL_ENTRIES.filter(t =>
|
||||||
|
!search || t.label.toLowerCase().includes(search.toLowerCase()) || t.group.toLowerCase().includes(search.toLowerCase())
|
||||||
|
);
|
||||||
|
|
||||||
|
const groups = [...new Set(filtered.map(t => t.group))];
|
||||||
|
|
||||||
|
const pick = async (entry) => {
|
||||||
|
setSaving(entry.url);
|
||||||
|
try {
|
||||||
|
await onSave({ title: entry.label.replace(/^[^\s]+\s/, ''), url: entry.url, icon: entry.label.split(' ')[0], folder_id: folderId, in_quickaccess: 1 });
|
||||||
|
toast(`${entry.label} zum Schnellzugriff hinzugefügt ✓`);
|
||||||
|
onClose();
|
||||||
|
} catch(e) { toast(e.message, 'error'); setSaving(null); }
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div onClick={e=>e.target===e.currentTarget&&onClose()}
|
||||||
|
style={{position:'fixed',inset:0,background:'rgba(0,0,0,0.75)',zIndex:6000,
|
||||||
|
display:'flex',alignItems:mob?'flex-end':'center',justifyContent:'center',padding:mob?0:20}}>
|
||||||
|
<div style={{background:'#1a1a1e',border:'1px solid rgba(255,255,255,0.12)',
|
||||||
|
borderRadius:mob?'16px 16px 0 0':14,width:'100%',maxWidth:480,
|
||||||
|
display:'flex',flexDirection:'column',maxHeight:mob?'85vh':'80vh',
|
||||||
|
paddingBottom:mob?'calc(56px + env(safe-area-inset-bottom,0px))':0}}>
|
||||||
|
|
||||||
|
{/* Header */}
|
||||||
|
<div style={{padding:'18px 20px 0',flexShrink:0}}>
|
||||||
|
{mob && <div style={{width:36,height:4,background:'rgba(255,255,255,0.15)',borderRadius:2,margin:'0 auto 14px'}}/>}
|
||||||
|
<div style={{display:'flex',justifyContent:'space-between',alignItems:'center',marginBottom:12}}>
|
||||||
|
<div style={{...S.head,marginBottom:0}}>APP HINZUFÜGEN</div>
|
||||||
|
<button onClick={onClose} style={{background:'transparent',border:'none',color:'rgba(255,255,255,0.4)',cursor:'pointer',fontSize:18}}>✕</button>
|
||||||
|
</div>
|
||||||
|
{/* Ordner-Auswahl */}
|
||||||
|
{folders.length > 0 && (
|
||||||
|
<div style={{marginBottom:10}}>
|
||||||
|
<div style={{fontSize:9,fontFamily:'monospace',color:'rgba(255,255,255,0.35)',letterSpacing:1,marginBottom:5}}>IN ORDNER (optional)</div>
|
||||||
|
<div style={{display:'flex',gap:5,flexWrap:'wrap'}}>
|
||||||
|
<button onClick={()=>setFolderId(null)} style={{
|
||||||
|
...S.btn(folderId===null?'#4ecdc4':'#888888',true),fontSize:10,padding:'3px 8px'}}>
|
||||||
|
Kein Ordner
|
||||||
|
</button>
|
||||||
|
{folders.map(f=>(
|
||||||
|
<button key={f.id} onClick={()=>setFolderId(f.id)} style={{
|
||||||
|
...S.btn(folderId===f.id?'#4ecdc4':'#888888',true),fontSize:10,padding:'3px 8px'}}>
|
||||||
|
{f.icon||'📁'} {f.name}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<input value={search} onChange={e=>setSearch(e.target.value)}
|
||||||
|
placeholder="App oder Funktion suchen…"
|
||||||
|
style={{...S.inp,marginBottom:10,fontSize:12}}/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Liste */}
|
||||||
|
<div style={{overflowY:'auto',padding:'0 20px 20px',scrollbarWidth:'thin',scrollbarColor:'rgba(255,255,255,0.08) transparent'}}>
|
||||||
|
{groups.map(group=>(
|
||||||
|
<div key={group} style={{marginBottom:10}}>
|
||||||
|
<div style={{fontSize:9,fontFamily:'monospace',color:'rgba(255,255,255,0.3)',letterSpacing:1,marginBottom:5,paddingTop:4}}>
|
||||||
|
{group.toUpperCase()}
|
||||||
|
</div>
|
||||||
|
{filtered.filter(t=>t.group===group).map(entry=>(
|
||||||
|
<button key={entry.url} onClick={()=>pick(entry)} disabled={!!saving}
|
||||||
|
style={{display:'flex',alignItems:'center',gap:10,width:'100%',
|
||||||
|
background:saving===entry.url?'rgba(78,205,196,0.1)':'rgba(255,255,255,0.03)',
|
||||||
|
border:'1px solid rgba(255,255,255,0.07)',borderRadius:8,
|
||||||
|
padding:'9px 12px',marginBottom:4,cursor:'pointer',textAlign:'left',
|
||||||
|
transition:'background 0.12s'}}>
|
||||||
|
<span style={{fontSize:16,flexShrink:0}}>{entry.label.split(' ')[0]}</span>
|
||||||
|
<span style={{flex:1,fontSize:12,fontFamily:'monospace',color:'#fff'}}>
|
||||||
|
{entry.label.replace(/^[^\s]+\s/, '')}
|
||||||
|
</span>
|
||||||
|
{saving===entry.url
|
||||||
|
? <span style={{fontSize:10,color:'#4ecdc4',fontFamily:'monospace'}}>…</span>
|
||||||
|
: <span style={{fontSize:10,color:'rgba(255,255,255,0.25)',fontFamily:'monospace'}}>+ hinzufügen</span>}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function extIcon(url) {
|
function extIcon(url) {
|
||||||
try { return `https://www.google.com/s2/favicons?sz=32&domain=${new URL(url).hostname}`; }
|
try { return `https://www.google.com/s2/favicons?sz=32&domain=${new URL(url).hostname}`; }
|
||||||
catch { return null; }
|
catch { return null; }
|
||||||
@@ -203,6 +319,7 @@ export default function Linkliste({ toast, mobile }) {
|
|||||||
const [tab, setTab] = useState('own');
|
const [tab, setTab] = useState('own');
|
||||||
const [showForm, setShowForm] = useState(false);
|
const [showForm, setShowForm] = useState(false);
|
||||||
const [showFolderForm, setShowFolderForm] = useState(false);
|
const [showFolderForm, setShowFolderForm] = useState(false);
|
||||||
|
const [showToolPicker, setShowToolPicker] = useState(false);
|
||||||
const [editItem, setEditItem] = useState(null);
|
const [editItem, setEditItem] = useState(null);
|
||||||
const [editFolder,setEditFolder]= useState(null);
|
const [editFolder,setEditFolder]= useState(null);
|
||||||
const [shareItem, setShareItem] = useState(null);
|
const [shareItem, setShareItem] = useState(null);
|
||||||
@@ -507,6 +624,10 @@ export default function Linkliste({ toast, mobile }) {
|
|||||||
<button onClick={()=>{setShowFolderForm(true);setShowForm(false);}}
|
<button onClick={()=>{setShowFolderForm(true);setShowForm(false);}}
|
||||||
style={{...S.btn('#ffe66d',true),fontSize:11,padding:'6px 12px'}}>+ Ordner</button>
|
style={{...S.btn('#ffe66d',true),fontSize:11,padding:'6px 12px'}}>+ Ordner</button>
|
||||||
)}
|
)}
|
||||||
|
{!showForm && !editItem && !sortMode && (
|
||||||
|
<button onClick={()=>{setShowToolPicker(true);setShowForm(false);setShowFolderForm(false);}}
|
||||||
|
style={{...S.btn('#a78bfa',true),fontSize:11,padding:'6px 12px'}}>+ App</button>
|
||||||
|
)}
|
||||||
{!showForm && !editItem && !sortMode && (
|
{!showForm && !editItem && !sortMode && (
|
||||||
<button onClick={()=>{setShowForm(true);setShowFolderForm(false);setEditItem(null);}}
|
<button onClick={()=>{setShowForm(true);setShowFolderForm(false);setEditItem(null);}}
|
||||||
style={{...S.btn('#4ecdc4'),padding:'7px 16px',fontSize:12}}>+ Link</button>
|
style={{...S.btn('#4ecdc4'),padding:'7px 16px',fontSize:12}}>+ Link</button>
|
||||||
@@ -517,6 +638,7 @@ export default function Linkliste({ toast, mobile }) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Formulare */}
|
{/* Formulare */}
|
||||||
|
{showToolPicker && <ToolPicker folders={folders} onSave={addLink} onClose={()=>setShowToolPicker(false)} toast={toast}/>}
|
||||||
{showFolderForm && !editFolder && <FolderForm onSave={addFolder} onCancel={()=>setShowFolderForm(false)} toast={toast}/>}
|
{showFolderForm && !editFolder && <FolderForm onSave={addFolder} onCancel={()=>setShowFolderForm(false)} toast={toast}/>}
|
||||||
{editFolder && <FolderForm initial={editFolder} onSave={updateFolder} onCancel={()=>setEditFolder(null)} toast={toast}/>}
|
{editFolder && <FolderForm initial={editFolder} onSave={updateFolder} onCancel={()=>setEditFolder(null)} toast={toast}/>}
|
||||||
{showForm && !editItem && <LinkForm folders={folders} onSave={addLink} onCancel={()=>setShowForm(false)} toast={toast}/>}
|
{showForm && !editItem && <LinkForm folders={folders} onSave={addLink} onCancel={()=>setShowForm(false)} toast={toast}/>}
|
||||||
|
|||||||
Reference in New Issue
Block a user