style(workspace): align LinkModal 1:1 with UnifiedShareModal design language
MindSift CI & Docker Release / Build, Test in Container & Push (push) Failing after 17s

This commit is contained in:
sandrodach committed 2026-10-27 07:11:28 +01:00
1 parent 4850f9dfad
commit 27b3567008
1 file changed
+119 -89
+119 -89
View File
@@ -1,5 +1,14 @@
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 { 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">
{/* Header */}
{/* Header - identical to UnifiedShareModal */}
<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="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"
onClick={onClose}
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" />
</button>
</div>
{/* Tab Switcher */}
<div className="flex rounded-btn bg-panel p-0.5 border border-border">
<button
type="button"
onClick={() => setTab('note')}
className={`flex-1 flex items-center justify-center gap-1.5 py-1.5 text-xs font-semibold rounded-btn transition-colors cursor-pointer ${
tab === 'note'
? 'bg-panel-elevated text-main shadow-xs'
: 'text-muted hover:text-main'
}`}
>
<FileText className="w-3.5 h-3.5" />
<span>Notiz verknüpfen</span>
</button>
<button
type="button"
onClick={() => setTab('web')}
className={`flex-1 flex items-center justify-center gap-1.5 py-1.5 text-xs font-semibold rounded-btn transition-colors cursor-pointer ${
tab === 'web'
? 'bg-panel-elevated text-main shadow-xs'
: 'text-muted hover:text-main'
}`}
>
<Globe className="w-3.5 h-3.5" />
<span>Web-Link</span>
</button>
</div>
{/* Control Box - matching UnifiedShareModal form header */}
<div className="space-y-3 p-3 bg-panel-elevated border border-border rounded-card">
<div className="flex items-center justify-between gap-2">
<span className="text-xs font-semibold text-main">Link-Typ:</span>
<div className="flex items-center gap-1">
<button
type="button"
onClick={() => setTab('note')}
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'
: 'bg-canvas border-border text-muted hover:text-main'
}`}
>
<FileText className="w-3.5 h-3.5" />
<span>Notiz</span>
</button>
<button
type="button"
onClick={() => setTab('web')}
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'
: 'bg-canvas border-border text-muted hover:text-main'
}`}
>
<Globe className="w-3.5 h-3.5" />
<span>Web-Link</span>
</button>
</div>
</div>
{/* Modal Body */}
<div>
{tab === 'note' ? (
<div className="space-y-3">
<div className="relative">
<Search className="w-3.5 h-3.5 absolute left-2.5 top-1/2 -translate-y-1/2 text-muted" />
<input
ref={searchInputRef}
type="text"
value={search}
onChange={(e) => setSearch(e.target.value)}
placeholder="Notiz nach Titel oder Ordner filtern…"
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 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" />
<input
ref={searchInputRef}
type="text"
value={search}
onChange={(e) => setSearch(e.target.value)}
placeholder="Notiz suchen (Titel oder Ordner)…"
className="input-field bg-panel h-8 pl-8 pr-2.5 text-xs w-full text-main placeholder:text-muted"
/>
</div>
) : (
<form onSubmit={handleWebLinkSubmit} className="space-y-3">
<form onSubmit={handleWebLinkSubmit} className="space-y-2.5">
<div>
<label className="text-[10px] font-bold uppercase tracking-wider text-faint mb-1 block">
Link-Text / Label
@@ -234,7 +205,7 @@ export const LinkModal: React.FC<LinkModalProps> = ({
value={webTitle}
onChange={(e) => setWebTitle(e.target.value)}
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>
@@ -248,30 +219,89 @@ export const LinkModal: React.FC<LinkModalProps> = ({
value={webUrl}
onChange={(e) => setWebUrl(e.target.value)}
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 className="pt-2 flex justify-end gap-2 border-t border-border">
<div className="flex justify-end gap-2 pt-1">
<button
type="button"
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
</button>
<button
type="submit"
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>
</button>
</div>
</form>
)}
</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>
);