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 React, { useState, useEffect, useRef, useCallback, useMemo } from 'react';
import { import {
Pin, Pin,
Share2, Users,
Trash2, Trash2,
Download, Download,
Folder as FolderIcon, Folder as FolderIcon,
Wifi, Radio,
WifiOff,
Loader2, Loader2,
ArrowLeft, ArrowLeft,
Hash, Hash,
@@ -590,16 +589,16 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
return ( return (
<div className="flex-1 flex flex-col h-full min-h-0 bg-canvas overflow-y-auto"> <div className="flex-1 flex flex-col h-full min-h-0 bg-canvas overflow-y-auto">
{/* Top Document Action Bar */} {/* 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"> <div className="flex items-center gap-2 min-w-0">
{onBackMobile && ( {onBackMobile && (
<button <button
type="button" type="button"
onClick={onBackMobile} 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" title="Zurück zur Seitenleiste"
> >
<ArrowLeft className="w-4 h-4" /> <ArrowLeft className="w-3.5 h-3.5" />
</button> </button>
)} )}
@@ -618,7 +617,7 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
</button> </button>
{showFolderMenu && canEdit && ( {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 <button
type="button" type="button"
onClick={() => handleMoveToFolder('')} onClick={() => handleMoveToFolder('')}
@@ -654,8 +653,8 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
</div> </div>
{/* Right Status & Action Buttons */} {/* Right Status & Action Buttons */}
<div className="flex items-center gap-1"> <div className="flex items-center gap-1.5">
{/* Active Presence Avatars */} {/* Active Presence Avatars (Immer rund: rounded-full mit Überlappung) */}
{activeUsers.length > 0 && ( {activeUsers.length > 0 && (
<div className="flex items-center -space-x-1.5 mr-1" title="Aktive Bearbeiter im Dokument"> <div className="flex items-center -space-x-1.5 mr-1" title="Aktive Bearbeiter im Dokument">
{activeUsers.map((u) => { {activeUsers.map((u) => {
@@ -668,13 +667,13 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
return ( return (
<div key={u.clientId} className="relative group/avatar cursor-default"> <div key={u.clientId} className="relative group/avatar cursor-default">
<div <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 }} style={{ backgroundColor: u.userColor }}
> >
{initials} {initials}
</div> </div>
{/* Tooltip */} {/* 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} {u.userName}
</div> </div>
</div> </div>
@@ -683,80 +682,109 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
</div> </div>
)} )}
{/* Sync Status Badge */} {/* Sharing Section (1:1 styling from SharedListsView) */}
<span {doc && (() => {
className={`inline-flex items-center gap-1 px-2 py-0.5 rounded-btn text-[10px] font-mono font-medium ${ 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' 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' : syncStatus === 'connecting' || syncStatus === 'reconnecting'
? 'bg-status-warn/10 text-status-warn border border-status-warn/30' ? 'bg-amber-500/10 border-amber-500/30 text-amber-500'
: 'bg-panel-elevated text-muted border border-border' : 'bg-canvas border-border text-muted'
}`} }`}
title={ title={
syncStatus === 'connected' syncStatus === 'connected'
? 'Echtzeit-Synchronisation aktiv' ? 'Live synchronisiert (WebSocket online)'
: 'Verbindung wird hergestellt…' : syncStatus === 'connecting' || syncStatus === 'reconnecting'
? 'Verbindung wird aufgebaut…'
: 'Offline'
} }
> >
{syncStatus === 'connected' ? ( <Radio
<> className={`w-3.5 h-3.5 ${
<Wifi className="w-2.5 h-2.5" /> syncStatus === 'connected'
<span className="hidden sm:inline">LIVE</span> ? 'animate-pulse'
</> : syncStatus === 'connecting' || syncStatus === 'reconnecting'
) : syncStatus === 'connecting' || syncStatus === 'reconnecting' ? ( ? 'animate-spin'
<> : ''
<Loader2 className="w-2.5 h-2.5 animate-spin" /> }`}
<span className="hidden sm:inline">SYNC…</span> />
</> </div>
) : (
<>
<WifiOff className="w-2.5 h-2.5" />
<span className="hidden sm:inline">OFFLINE</span>
</>
)}
</span>
<button <button
type="button" type="button"
onClick={handleTogglePin} onClick={handleTogglePin}
className={`p-1.5 rounded-btn transition-colors ${ className={`btn-icon ${
doc?.isPinned doc?.isPinned
? 'text-status-warn bg-status-warn/10' ? 'text-status-warn bg-status-warn/10 border-status-warn/30'
: 'text-muted hover:text-main hover:bg-panel-elevated' : ''
}`} }`}
title={doc?.isPinned ? 'Anheftung lösen' : 'Notiz anheften'} title={doc?.isPinned ? 'Anheftung lösen' : 'Notiz anheften'}
> >
<Pin className="w-4 h-4" /> <Pin className="w-3.5 h-3.5" />
</button> </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 <button
type="button" type="button"
onClick={handleExportMarkdown} 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" title="Als Markdown (.md) exportieren"
> >
<Download className="w-4 h-4" /> <Download className="w-3.5 h-3.5" />
</button> </button>
{canEdit && (doc?.isOwner || doc?.permission === 'owner') && ( {canEdit && (doc?.isOwner || doc?.permission === 'owner') && (
<button <button
type="button" type="button"
onClick={() => onDeleteDoc(docId)} 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" title="Notiz löschen"
> >
<Trash2 className="w-4 h-4" /> <Trash2 className="w-3.5 h-3.5" />
</button> </button>
)} )}
</div> </div>
@@ -773,7 +801,7 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
{breadcrumbs.length > 0 && ( {breadcrumbs.length > 0 && (
<nav <nav
aria-label="Breadcrumb Navigation" 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 <button
type="button" type="button"
@@ -918,7 +946,7 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
<button <button
type="button" type="button"
onClick={() => canEdit && setShowEmojiPicker((prev) => !prev)} 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" title="Icon ändern"
> >
{doc?.icon || '📝'} {doc?.icon || '📝'}
@@ -15,7 +15,6 @@ import {
Users, Users,
Plus, Plus,
X, X,
Share2,
ExternalLink, ExternalLink,
Sparkles, Sparkles,
} from 'lucide-react'; } from 'lucide-react';
@@ -514,12 +513,12 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
}, [activeDocShares, shareModalDoc, shareModalFolder]); }, [activeDocShares, shareModalDoc, shareModalFolder]);
return ( 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 */} {/* LEFT MASTER SIDEBAR */}
<aside <aside
className={`${ className={`${
mobileShowDetail ? 'hidden lg:flex' : 'flex' 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 */} {/* Sidebar Header & Actions */}
<div className="p-3 border-b border-border space-y-2"> <div className="p-3 border-b border-border space-y-2">
@@ -733,9 +732,9 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
type="button" type="button"
onClick={() => openFolderShareModal(sharedFolderObj)} onClick={() => openFolderShareModal(sharedFolderObj)}
className="p-1 rounded-btn hover:bg-canvas text-accent" 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>
)} )}
<button <button
@@ -916,7 +915,7 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
{tagDetails?.documents.map((docGroup) => ( {tagDetails?.documents.map((docGroup) => (
<div <div
key={docGroup.docId} 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 <button
type="button" type="button"
@@ -1001,10 +1000,10 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
return ( return (
<div <div
key={folderName} key={folderName}
className="border border-border rounded-card bg-panel overflow-hidden" className="border border-border rounded-card bg-canvas overflow-hidden"
> >
{/* Folder Header */} {/* 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 <div
onClick={() => { onClick={() => {
setCollapsedTodoFolders((prev) => { setCollapsedTodoFolders((prev) => {