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 ─────────────────────────────────────────────────────────
|
||||
// ── QuickLinks Carousel (Mobile) ─────────────────────────────────────────────
|
||||
function QuickLinksCarousel({ links, onFolderClick }) {
|
||||
function QuickLinksCarousel({ links, onFolderClick, navigateTool }) {
|
||||
const perRow = 4;
|
||||
const gap = 6;
|
||||
const pageSize = perRow * 2;
|
||||
@@ -413,7 +413,7 @@ function QuickLinksCarousel({ links, onFolderClick }) {
|
||||
|
||||
if (pageCount <= 1) return (
|
||||
<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>
|
||||
);
|
||||
|
||||
@@ -431,7 +431,7 @@ function QuickLinksCarousel({ links, onFolderClick }) {
|
||||
gap, flexShrink:0, scrollSnapAlign:'start',
|
||||
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>
|
||||
@@ -452,19 +452,20 @@ function QuickLinksCarousel({ links, onFolderClick }) {
|
||||
);
|
||||
}
|
||||
|
||||
function QuickLinkIcon({ l, onFolderClick }) {
|
||||
function QuickLinkIcon({ l, onFolderClick, navigateTool }) {
|
||||
const [imgOk, setImgOk] = useState(true);
|
||||
const isTool = l.url?.startsWith('tool://');
|
||||
|
||||
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}`; }
|
||||
catch { return 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 showEmoji = !showFavicon && !showCustomImg;
|
||||
const emoji = l.isFolder ? (l.icon||'📁') : (hasCustomIcon ? l.icon : '🔗');
|
||||
const emoji = l.isFolder ? (l.icon||'📁') : (hasCustomIcon ? l.icon : (isTool ? '⚡' : '🔗'));
|
||||
|
||||
const content = (
|
||||
<>
|
||||
@@ -477,10 +478,13 @@ function QuickLinkIcon({ l, onFolderClick }) {
|
||||
);
|
||||
|
||||
const baseStyle = {display:'flex',flexDirection:'column',alignItems:'center',justifyContent:'center',gap:3,
|
||||
padding:'4px 2px',background:'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)',
|
||||
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)' : 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'};
|
||||
|
||||
if (isTool) {
|
||||
return <button onClick={()=>navigateTool?.(l.url)} style={baseStyle}>{content}</button>;
|
||||
}
|
||||
if (l.isFolder && onFolderClick) {
|
||||
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>;
|
||||
@@ -488,7 +492,7 @@ function QuickLinkIcon({ l, onFolderClick }) {
|
||||
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 [loading, setLoading] = useState(true);
|
||||
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>
|
||||
: 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}}>
|
||||
{links.map(l=><QuickLinkIcon key={l.id} l={l}/>)}
|
||||
{links.map(l=><QuickLinkIcon key={l.id} l={l} navigateTool={navigateTool}/>)}
|
||||
</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 [folderModal, setFolderModal] = useState(null);
|
||||
const [editMode, setEditMode] = useState(false);
|
||||
|
||||
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 hasLegacy = legacyItems.length > 0;
|
||||
|
||||
@@ -547,7 +565,7 @@ function QuickLinks({ toast, mobile }) {
|
||||
|
||||
return (
|
||||
<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={S.head}>SCHNELLZUGRIFF</div>
|
||||
{hasLegacy && (
|
||||
@@ -591,9 +609,9 @@ function QuickLinks({ toast, mobile }) {
|
||||
Links oder Ordner in der Linkliste mit ● Schnellzugriff aktivieren.
|
||||
</div>
|
||||
: 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 }}>
|
||||
{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>
|
||||
@@ -1947,7 +1965,7 @@ function Dashboard({ toast, mobile, setActive, setDevToolsNav, unreadMsgs=0, unr
|
||||
</div>
|
||||
{/* Globale Suche */}
|
||||
<GlobalSearch setActive={setActive} setDevToolsNav={setDevToolsNav} mobile={mobile}/>
|
||||
<QuickLinks toast={toast} mobile={mobile} />
|
||||
<QuickLinks toast={toast} mobile={mobile} setActive={setActive} setDevToolsNav={setDevToolsNav} />
|
||||
<PushScheduler toast={toast}/>
|
||||
<CalendarWidget/>
|
||||
<BestellStats/>
|
||||
|
||||
Reference in New Issue
Block a user