Dev #7

Merged
sandrodach merged 33 commits from dev into main 2026-10-27 11:42:12 +01:00
2 changed files with 92 additions and 65 deletions
Showing only changes of commit 634195b629 - Show all commits

No files matched your search

@@ -1,12 +1,11 @@
import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react';
import {
Pin,
Share2,
Users,
Trash2,
Download,
Folder as FolderIcon,
Wifi,
WifiOff,
Radio,
Loader2,
ArrowLeft,
Hash,
@@ -590,16 +589,16 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
return (
<div className="flex-1 flex flex-col h-full min-h-0 bg-canvas overflow-y-auto">
{/* Top Document Action Bar */}
<div className="sticky top-0 z-20 flex flex-wrap items-center justify-between gap-2 px-4 sm:px-6 py-2.5 bg-panel border-b border-border shadow-sm">
<div className="sticky top-0 z-20 flex flex-wrap items-center justify-between gap-2 px-4 sm:px-6 py-2 bg-canvas border-b border-border">
<div className="flex items-center gap-2 min-w-0">
{onBackMobile && (
<button
type="button"
onClick={onBackMobile}
className="lg:hidden p-1.5 rounded-btn text-muted hover:text-main hover:bg-panel-elevated"
className="lg:hidden btn-icon"
title="Zurück zur Seitenleiste"
>
<ArrowLeft className="w-4 h-4" />
<ArrowLeft className="w-3.5 h-3.5" />
</button>
)}
@@ -618,7 +617,7 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
</button>
{showFolderMenu && canEdit && (
<div className="absolute left-0 mt-1 w-52 panel-surface shadow-panel border border-border rounded-card py-1 z-30 max-h-60 overflow-y-auto">
<div className="absolute left-0 mt-1 w-52 bg-canvas shadow-panel border border-border rounded-card py-1 z-30 max-h-60 overflow-y-auto">
<button
type="button"
onClick={() => handleMoveToFolder('')}
@@ -654,8 +653,8 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
</div>
{/* Right Status & Action Buttons */}
<div className="flex items-center gap-1">
{/* Active Presence Avatars */}
<div className="flex items-center gap-1.5">
{/* Active Presence Avatars (Immer rund: rounded-full mit Überlappung) */}
{activeUsers.length > 0 && (
<div className="flex items-center -space-x-1.5 mr-1" title="Aktive Bearbeiter im Dokument">
{activeUsers.map((u) => {
@@ -668,13 +667,13 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
return (
<div key={u.clientId} className="relative group/avatar cursor-default">
<div
className="w-5 h-5 rounded-btn flex items-center justify-center text-[9px] font-bold text-white shadow-sm ring-1 ring-panel"
className="w-6 h-6 rounded-full flex items-center justify-center text-[10px] font-bold text-white shadow-sm ring-2 ring-canvas"
style={{ backgroundColor: u.userColor }}
>
{initials}
</div>
{/* Tooltip */}
<div className="pointer-events-none absolute bottom-full mb-1 left-1/2 -translate-x-1/2 opacity-0 group-hover/avatar:opacity-100 transition-opacity whitespace-nowrap bg-panel-elevated text-main text-[10px] px-1.5 py-0.5 rounded-btn border border-border shadow-md z-30">
<div className="pointer-events-none absolute bottom-full mb-1 left-1/2 -translate-x-1/2 opacity-0 group-hover/avatar:opacity-100 transition-opacity whitespace-nowrap bg-canvas text-main text-[10px] px-1.5 py-0.5 rounded-btn border border-border shadow-md z-30">
{u.userName}
</div>
</div>
@@ -683,80 +682,109 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
</div>
)}
{/* Sync Status Badge */}
<span
className={`inline-flex items-center gap-1 px-2 py-0.5 rounded-btn text-[10px] font-mono font-medium ${
{/* Sharing Section (1:1 styling from SharedListsView) */}
{doc && (() => {
const isOwner = doc.isOwner !== false && (doc.userId === currentUser?.id || !doc.userId || currentUser?.role === 'admin');
const sharesCount = doc.sharedWith?.length || 0;
const isShared = sharesCount > 0 || !isOwner || Boolean(doc.sharedFolderId);
const ownerDisplayName = doc.ownerName || 'Eigentümer';
return (
<div className="flex items-center gap-1.5">
{isShared && (
isOwner ? (
<button
type="button"
onClick={() => onOpenShareModal(doc)}
className="px-2 py-0.5 rounded text-[10px] font-semibold bg-accent/15 text-accent border border-accent/30 flex items-center gap-1 hover:bg-accent/25 hover:border-accent/50 transition-colors cursor-pointer"
title="Teilen-Menü öffnen: Geteilt mit Nutzern/Gruppen anzeigen & verwalten"
>
<Users className="w-2.5 h-2.5" />
<span>Geteilt{sharesCount > 0 ? ` (${sharesCount})` : ''}</span>
</button>
) : (
<button
type="button"
onClick={() => onOpenShareModal(doc)}
className="px-2 py-0.5 rounded text-[10px] font-medium border border-border text-muted flex items-center gap-1 bg-surface-subtle hover:bg-panel-elevated hover:text-main transition-colors cursor-pointer"
title="Teilen & Berechtigungen öffnen"
>
<Users className="w-2.5 h-2.5" />
<span>Geteilt von {ownerDisplayName}</span>
</button>
)
)}
<button
type="button"
onClick={() => onOpenShareModal(doc)}
className="btn-icon"
title="Notiz teilen und Berechtigungen"
>
<Users className="w-3.5 h-3.5" />
</button>
</div>
);
})()}
{/* Live Sync Status (1:1 styling from SharedListsView) */}
<div
className={`h-8 w-8 flex items-center justify-center rounded-btn border transition-colors ${
syncStatus === 'connected'
? 'bg-status-ok/10 text-status-ok border border-status-ok/30'
? 'bg-emerald-500/10 border-emerald-500/30 text-emerald-500'
: syncStatus === 'connecting' || syncStatus === 'reconnecting'
? 'bg-status-warn/10 text-status-warn border border-status-warn/30'
: 'bg-panel-elevated text-muted border border-border'
? 'bg-amber-500/10 border-amber-500/30 text-amber-500'
: 'bg-canvas border-border text-muted'
}`}
title={
syncStatus === 'connected'
? 'Echtzeit-Synchronisation aktiv'
: 'Verbindung wird hergestellt…'
? 'Live synchronisiert (WebSocket online)'
: syncStatus === 'connecting' || syncStatus === 'reconnecting'
? 'Verbindung wird aufgebaut…'
: 'Offline'
}
>
{syncStatus === 'connected' ? (
<>
<Wifi className="w-2.5 h-2.5" />
<span className="hidden sm:inline">LIVE</span>
</>
) : syncStatus === 'connecting' || syncStatus === 'reconnecting' ? (
<>
<Loader2 className="w-2.5 h-2.5 animate-spin" />
<span className="hidden sm:inline">SYNC…</span>
</>
) : (
<>
<WifiOff className="w-2.5 h-2.5" />
<span className="hidden sm:inline">OFFLINE</span>
</>
)}
</span>
<Radio
className={`w-3.5 h-3.5 ${
syncStatus === 'connected'
? 'animate-pulse'
: syncStatus === 'connecting' || syncStatus === 'reconnecting'
? 'animate-spin'
: ''
}`}
/>
</div>
<button
type="button"
onClick={handleTogglePin}
className={`p-1.5 rounded-btn transition-colors ${
className={`btn-icon ${
doc?.isPinned
? 'text-status-warn bg-status-warn/10'
: 'text-muted hover:text-main hover:bg-panel-elevated'
? 'text-status-warn bg-status-warn/10 border-status-warn/30'
: ''
}`}
title={doc?.isPinned ? 'Anheftung lösen' : 'Notiz anheften'}
>
<Pin className="w-4 h-4" />
<Pin className="w-3.5 h-3.5" />
</button>
{doc && (
<button
type="button"
onClick={() => onOpenShareModal(doc)}
className="p-1.5 rounded-btn text-muted hover:text-accent hover:bg-panel-elevated transition-colors"
title="Notiz teilen / Berechtigungen"
>
<Share2 className="w-4 h-4" />
</button>
)}
<button
type="button"
onClick={handleExportMarkdown}
className="p-1.5 rounded-btn text-muted hover:text-main hover:bg-panel-elevated transition-colors"
className="btn-icon"
title="Als Markdown (.md) exportieren"
>
<Download className="w-4 h-4" />
<Download className="w-3.5 h-3.5" />
</button>
{canEdit && (doc?.isOwner || doc?.permission === 'owner') && (
<button
type="button"
onClick={() => onDeleteDoc(docId)}
className="p-1.5 rounded-btn text-muted hover:text-status-err hover:bg-status-err/10 transition-colors"
className="btn-icon-danger"
title="Notiz löschen"
>
<Trash2 className="w-4 h-4" />
<Trash2 className="w-3.5 h-3.5" />
</button>
)}
</div>
@@ -773,7 +801,7 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
{breadcrumbs.length > 0 && (
<nav
aria-label="Breadcrumb Navigation"
className="flex items-center flex-wrap gap-1.5 py-1.5 px-3 bg-panel-elevated/70 border border-border rounded-card text-xs text-muted"
className="flex items-center flex-wrap gap-1.5 py-1.5 px-3 bg-canvas border border-border rounded-btn text-xs text-muted"
>
<button
type="button"
@@ -918,7 +946,7 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
<button
type="button"
onClick={() => canEdit && setShowEmojiPicker((prev) => !prev)}
className="w-10 h-10 rounded-btn bg-panel-elevated border border-border hover:border-strong flex items-center justify-center text-xl transition-colors"
className="w-10 h-10 rounded-btn bg-canvas border border-border hover:border-strong flex items-center justify-center text-xl transition-colors"
title="Icon ändern"
>
{doc?.icon || '📝'}
@@ -15,7 +15,6 @@ import {
Users,
Plus,
X,
Share2,
ExternalLink,
Sparkles,
} from 'lucide-react';
@@ -514,12 +513,12 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
}, [activeDocShares, shareModalDoc, shareModalFolder]);
return (
<div className="flex h-[calc(100vh-4rem)] panel-surface rounded-card border border-border overflow-hidden shadow-panel">
<div className="flex h-[calc(100vh-4rem)] bg-canvas rounded-card border border-border overflow-hidden shadow-panel">
{/* LEFT MASTER SIDEBAR */}
<aside
className={`${
mobileShowDetail ? 'hidden lg:flex' : 'flex'
} w-full lg:w-72 xl:w-80 shrink-0 flex-col bg-panel border-r border-border`}
} w-full lg:w-72 xl:w-80 shrink-0 flex-col bg-canvas border-r border-border`}
>
{/* Sidebar Header & Actions */}
<div className="p-3 border-b border-border space-y-2">
@@ -733,9 +732,9 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
type="button"
onClick={() => openFolderShareModal(sharedFolderObj)}
className="p-1 rounded-btn hover:bg-canvas text-accent"
title="Ordner-Freigabe"
title="Ordner teilen und Berechtigungen"
>
<Share2 className="w-3 h-3" />
<Users className="w-3 h-3" />
</button>
)}
<button
@@ -916,7 +915,7 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
{tagDetails?.documents.map((docGroup) => (
<div
key={docGroup.docId}
className="p-3 rounded-card border border-border bg-panel space-y-1.5"
className="p-3 rounded-card border border-border bg-canvas space-y-1.5"
>
<button
type="button"
@@ -1001,10 +1000,10 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
return (
<div
key={folderName}
className="border border-border rounded-card bg-panel overflow-hidden"
className="border border-border rounded-card bg-canvas overflow-hidden"
>
{/* Folder Header */}
<div className="p-2 bg-panel-elevated border-b border-border flex items-center justify-between">
<div className="p-2 bg-canvas border-b border-border flex items-center justify-between">
<div
onClick={() => {
setCollapsedTodoFolders((prev) => {