Dev #7

Merged
sandrodach merged 33 commits from dev into main 2026-10-27 11:42:12 +01:00
Showing only changes of commit 27b3567008 - Show all commits
+119 -89
View File
@@ -1,5 +1,14 @@
import React, { useState, useEffect, useMemo, useRef } from 'react'; import React, { useState, useEffect, useMemo, useRef } from 'react';
import { Link as LinkIcon, FileText, Globe, X, Search, ExternalLink, Loader2 } from 'lucide-react'; import {
Link as LinkIcon,
FileText,
Globe,
X,
Search,
ExternalLink,
Loader2,
Plus,
} from 'lucide-react';
import { api } from '../../lib/api'; import { api } from '../../lib/api';
import { Document } from '../../lib/types'; import { Document } from '../../lib/types';
@@ -117,7 +126,7 @@ export const LinkModal: React.FC<LinkModalProps> = ({
}} }}
> >
<div className="panel-surface w-full max-w-lg p-5 shadow-panel rounded-card border border-border space-y-4 animate-in fade-in zoom-in-95 duration-150"> <div className="panel-surface w-full max-w-lg p-5 shadow-panel rounded-card border border-border space-y-4 animate-in fade-in zoom-in-95 duration-150">
{/* Header */} {/* Header - identical to UnifiedShareModal */}
<div className="flex items-center justify-between pb-3 border-b border-border"> <div className="flex items-center justify-between pb-3 border-b border-border">
<div className="flex items-center gap-2.5 min-w-0"> <div className="flex items-center gap-2.5 min-w-0">
<div className="w-8 h-8 rounded-btn bg-accent/15 text-accent flex items-center justify-center shrink-0"> <div className="w-8 h-8 rounded-btn bg-accent/15 text-accent flex items-center justify-center shrink-0">
@@ -134,96 +143,58 @@ export const LinkModal: React.FC<LinkModalProps> = ({
type="button" type="button"
onClick={onClose} onClick={onClose}
className="p-1.5 text-muted hover:text-main rounded-btn hover:bg-panel transition-colors cursor-pointer" className="p-1.5 text-muted hover:text-main rounded-btn hover:bg-panel transition-colors cursor-pointer"
title="Schließen (Esc)" title="Schließen"
> >
<X className="w-4 h-4" /> <X className="w-4 h-4" />
</button> </button>
</div> </div>
{/* Tab Switcher */} {/* Control Box - matching UnifiedShareModal form header */}
<div className="flex rounded-btn bg-panel p-0.5 border border-border"> <div className="space-y-3 p-3 bg-panel-elevated border border-border rounded-card">
<button <div className="flex items-center justify-between gap-2">
type="button" <span className="text-xs font-semibold text-main">Link-Typ:</span>
onClick={() => setTab('note')} <div className="flex items-center gap-1">
className={`flex-1 flex items-center justify-center gap-1.5 py-1.5 text-xs font-semibold rounded-btn transition-colors cursor-pointer ${ <button
tab === 'note' type="button"
? 'bg-panel-elevated text-main shadow-xs' onClick={() => setTab('note')}
: 'text-muted hover:text-main' className={`h-7 px-2.5 text-xs rounded-btn font-medium flex items-center gap-1.5 border transition-colors cursor-pointer ${
}`} tab === 'note'
> ? 'bg-panel border-accent text-accent font-semibold shadow-xs'
<FileText className="w-3.5 h-3.5" /> : 'bg-canvas border-border text-muted hover:text-main'
<span>Notiz verknüpfen</span> }`}
</button> >
<button <FileText className="w-3.5 h-3.5" />
type="button" <span>Notiz</span>
onClick={() => setTab('web')} </button>
className={`flex-1 flex items-center justify-center gap-1.5 py-1.5 text-xs font-semibold rounded-btn transition-colors cursor-pointer ${ <button
tab === 'web' type="button"
? 'bg-panel-elevated text-main shadow-xs' onClick={() => setTab('web')}
: 'text-muted hover:text-main' className={`h-7 px-2.5 text-xs rounded-btn font-medium flex items-center gap-1.5 border transition-colors cursor-pointer ${
}`} tab === 'web'
> ? 'bg-panel border-accent text-accent font-semibold shadow-xs'
<Globe className="w-3.5 h-3.5" /> : 'bg-canvas border-border text-muted hover:text-main'
<span>Web-Link</span> }`}
</button> >
</div> <Globe className="w-3.5 h-3.5" />
<span>Web-Link</span>
</button>
</div>
</div>
{/* Modal Body */}
<div>
{tab === 'note' ? ( {tab === 'note' ? (
<div className="space-y-3"> <div className="relative">
<div className="relative"> <Search className="w-3.5 h-3.5 absolute left-2.5 top-1/2 -translate-y-1/2 text-muted pointer-events-none" />
<Search className="w-3.5 h-3.5 absolute left-2.5 top-1/2 -translate-y-1/2 text-muted" /> <input
<input ref={searchInputRef}
ref={searchInputRef} type="text"
type="text" value={search}
value={search} onChange={(e) => setSearch(e.target.value)}
onChange={(e) => setSearch(e.target.value)} placeholder="Notiz suchen (Titel oder Ordner)…"
placeholder="Notiz nach Titel oder Ordner filtern…" className="input-field bg-panel h-8 pl-8 pr-2.5 text-xs w-full text-main placeholder:text-muted"
className="input-field w-full text-xs pl-8 pr-3 py-2 text-main" />
/>
</div>
{loading ? (
<div className="py-8 flex items-center justify-center text-xs text-muted gap-2">
<Loader2 className="w-4 h-4 animate-spin text-accent" />
<span>Notizen werden geladen…</span>
</div>
) : (
<div className="max-h-64 overflow-y-auto space-y-1 pr-1">
{filteredDocs.map((d) => (
<button
key={d.id}
type="button"
onClick={() => handleSelectNote(d)}
className="w-full text-left px-3 py-2 rounded-btn hover:bg-panel border border-transparent hover:border-border flex items-center justify-between text-xs transition-colors group cursor-pointer"
>
<div className="flex items-center gap-2 min-w-0">
<span className="text-base shrink-0">{d.icon || '📝'}</span>
<span className="font-semibold text-main truncate group-hover:text-accent">
{d.title || 'Unbenannte Notiz'}
</span>
</div>
{d.folderPath && (
<span className="badge-neutral text-[10px] ml-2 shrink-0 truncate max-w-[120px]">
📁 {d.folderPath}
</span>
)}
</button>
))}
{filteredDocs.length === 0 && (
<div className="py-8 text-center text-xs text-muted border border-dashed border-border rounded-card">
{search
? `Keine Notiz für "${search}" gefunden.`
: 'Keine Notizen im Workspace vorhanden.'}
</div>
)}
</div>
)}
</div> </div>
) : ( ) : (
<form onSubmit={handleWebLinkSubmit} className="space-y-3"> <form onSubmit={handleWebLinkSubmit} className="space-y-2.5">
<div> <div>
<label className="text-[10px] font-bold uppercase tracking-wider text-faint mb-1 block"> <label className="text-[10px] font-bold uppercase tracking-wider text-faint mb-1 block">
Link-Text / Label Link-Text / Label
@@ -234,7 +205,7 @@ export const LinkModal: React.FC<LinkModalProps> = ({
value={webTitle} value={webTitle}
onChange={(e) => setWebTitle(e.target.value)} onChange={(e) => setWebTitle(e.target.value)}
placeholder="z. B. MindSift Dokumentation" placeholder="z. B. MindSift Dokumentation"
className="input-field w-full p-2 text-xs text-main" className="input-field bg-panel h-8 px-2.5 text-xs w-full text-main placeholder:text-muted"
/> />
</div> </div>
@@ -248,30 +219,89 @@ export const LinkModal: React.FC<LinkModalProps> = ({
value={webUrl} value={webUrl}
onChange={(e) => setWebUrl(e.target.value)} onChange={(e) => setWebUrl(e.target.value)}
placeholder="https://example.com" placeholder="https://example.com"
className="input-field w-full p-2 text-xs text-main" className="input-field bg-panel h-8 px-2.5 text-xs w-full text-main placeholder:text-muted"
/> />
</div> </div>
<div className="pt-2 flex justify-end gap-2 border-t border-border"> <div className="flex justify-end gap-2 pt-1">
<button <button
type="button" type="button"
onClick={onClose} onClick={onClose}
className="btn-action px-3 py-1.5 text-xs text-main cursor-pointer" className="btn-action text-xs h-8 px-3.5 cursor-pointer"
> >
Abbrechen Abbrechen
</button> </button>
<button <button
type="submit" type="submit"
disabled={!webUrl.trim()} disabled={!webUrl.trim()}
className="btn-primary inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-semibold text-on-accent rounded-btn disabled:opacity-50 transition-opacity cursor-pointer" className="btn-primary text-xs h-8 px-3.5 flex items-center gap-1.5 cursor-pointer disabled:opacity-50"
> >
<ExternalLink className="w-3.5 h-3.5" /> <Plus className="w-3.5 h-3.5" />
<span>Link einfügen</span> <span>Link einfügen</span>
</button> </button>
</div> </div>
</form> </form>
)} )}
</div> </div>
{/* Note List Section (Matching Active Shares list in UnifiedShareModal) */}
{tab === 'note' && (
<div className="space-y-2">
<div className="flex items-center justify-between text-xs font-semibold text-muted uppercase tracking-wider px-0.5">
<span>Verfügbare Notizen</span>
<span className="font-mono text-[11px] font-normal lowercase">
{filteredDocs.length} {filteredDocs.length === 1 ? 'Notiz' : 'Notizen'}
</span>
</div>
<div className="space-y-1.5 max-h-56 overflow-y-auto pr-0.5">
{loading ? (
<div className="p-6 bg-canvas border border-border rounded-card text-xs text-muted flex items-center gap-2 justify-center text-center">
<Loader2 className="w-4 h-4 animate-spin text-accent" />
<span>Notizen werden geladen…</span>
</div>
) : filteredDocs.length === 0 ? (
<div className="p-4 bg-canvas border border-border rounded-card text-xs text-muted flex items-center gap-2.5 justify-center text-center">
<Search className="w-4 h-4 text-faint shrink-0" />
<span>
{search
? `Keine Notiz für "${search}" gefunden.`
: 'Keine Notizen im Workspace vorhanden.'}
</span>
</div>
) : (
filteredDocs.map((d) => (
<div
key={d.id}
onClick={() => handleSelectNote(d)}
className="flex items-center justify-between p-2.5 bg-canvas border border-border rounded-card hover:border-accent/60 transition-colors group cursor-pointer"
>
<div className="flex items-center gap-2.5 min-w-0">
<span className="text-base shrink-0">{d.icon || '📝'}</span>
<div className="min-w-0">
<div className="text-xs font-semibold text-main truncate group-hover:text-accent">
{d.title || 'Unbenannte Notiz'}
</div>
{d.folderPath && (
<div className="text-[10px] text-muted truncate">
📁 {d.folderPath}
</div>
)}
</div>
</div>
<button
type="button"
className="btn-action text-xs h-7 px-2.5 opacity-0 group-hover:opacity-100 transition-opacity flex items-center gap-1 text-accent cursor-pointer shrink-0"
>
<span>Einfügen</span>
<ExternalLink className="w-3 h-3" />
</button>
</div>
))
)}
</div>
</div>
)}
</div> </div>
</div> </div>
); );