style(workspace): align LinkModal 1:1 with UnifiedShareModal design language
MindSift CI & Docker Release / Build, Test in Container & Push (push) Failing after 17s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Failing after 17s
This commit is contained in:
1 parent
4850f9dfad
commit
27b3567008
1 file changed
+94
-64
@@ -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">
|
||||||
|
<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
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setTab('note')}
|
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 ${
|
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'
|
tab === 'note'
|
||||||
? 'bg-panel-elevated text-main shadow-xs'
|
? 'bg-panel border-accent text-accent font-semibold shadow-xs'
|
||||||
: 'text-muted hover:text-main'
|
: 'bg-canvas border-border text-muted hover:text-main'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<FileText className="w-3.5 h-3.5" />
|
<FileText className="w-3.5 h-3.5" />
|
||||||
<span>Notiz verknüpfen</span>
|
<span>Notiz</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setTab('web')}
|
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 ${
|
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'
|
tab === 'web'
|
||||||
? 'bg-panel-elevated text-main shadow-xs'
|
? 'bg-panel border-accent text-accent font-semibold shadow-xs'
|
||||||
: 'text-muted hover:text-main'
|
: 'bg-canvas border-border text-muted hover:text-main'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Globe className="w-3.5 h-3.5" />
|
<Globe className="w-3.5 h-3.5" />
|
||||||
<span>Web-Link</span>
|
<span>Web-Link</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</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" />
|
<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
|
<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 nach Titel oder Ordner filtern…"
|
placeholder="Notiz suchen (Titel oder Ordner)…"
|
||||||
className="input-field w-full text-xs pl-8 pr-3 py-2 text-main"
|
className="input-field bg-panel h-8 pl-8 pr-2.5 text-xs w-full text-main placeholder:text-muted"
|
||||||
/>
|
/>
|
||||||
</div>
|
</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">
|
<form onSubmit={handleWebLinkSubmit} className="space-y-2.5">
|
||||||
{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>
|
|
||||||
) : (
|
|
||||||
<form onSubmit={handleWebLinkSubmit} className="space-y-3">
|
|
||||||
<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>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in new issue
Block a user