diff --git a/frontend/src/features/workspace/WorkspaceView.tsx b/frontend/src/features/workspace/WorkspaceView.tsx index cadbc02..3354430 100644 --- a/frontend/src/features/workspace/WorkspaceView.tsx +++ b/frontend/src/features/workspace/WorkspaceView.tsx @@ -98,12 +98,17 @@ export const WorkspaceView: React.FC = ({ initialDocId, onStatsChange, }) => { - // Top Workspace Tab Mode: 'editor' vs 'todos' vs 'tag' - const [workspaceMode, setWorkspaceMode] = useState<'editor' | 'todos' | 'tag'>('editor'); + // Top Workspace Tab Mode: 'editor' vs 'todos' vs 'tags' + const [workspaceMode, setWorkspaceMode] = useState<'editor' | 'todos' | 'tags'>('editor'); const [selectedTag, setSelectedTag] = useState(null); const [tagDetails, setTagDetails] = useState(null); const [workspaceTags, setWorkspaceTags] = useState([]); const [isLoadingTag, setIsLoadingTag] = useState(false); + const [tagSearchQuery, setTagSearchQuery] = useState(''); + + // Collapsible sidebar sections + const [isPinnedCollapsed, setIsPinnedCollapsed] = useState(false); + const [isTagsCollapsed, setIsTagsCollapsed] = useState(false); const [docs, setDocs] = useState([]); const [selectedDoc, setSelectedDoc] = useState(null); @@ -394,7 +399,7 @@ export const WorkspaceView: React.FC = ({ const openTagPage = async (tag: string) => { const clean = tag.replace(/^#/, '').toLowerCase(); setSelectedTag(clean); - setWorkspaceMode('tag'); + setWorkspaceMode('tags'); setIsLoadingTag(true); try { const details = await api.getTagDetails(clean); @@ -1817,6 +1822,12 @@ export const WorkspaceView: React.FC = ({ return docs.filter((d) => d.isPinned); }, [docs]); + const filteredWorkspaceTags = useMemo(() => { + const q = tagSearchQuery.trim().replace(/^#/, '').toLowerCase(); + if (!q) return workspaceTags; + return workspaceTags.filter((t) => t.tag.toLowerCase().includes(q)); + }, [workspaceTags, tagSearchQuery]); + const renderDocItem = (doc: Document, depth: number) => { const isSelected = selectedDoc?.id === doc.id && workspaceMode === 'editor'; const isDragging = draggingDocId === doc.id; @@ -2057,6 +2068,24 @@ export const WorkspaceView: React.FC = ({ Notizen + + - - {selectedTag && ( - - )}
@@ -2259,94 +2268,120 @@ export const WorkspaceView: React.FC = ({
)} - {/* Angepinnte Notizen (Favorites) */} + {/* Angepinnte Notizen (Favorites) - Collapsible */} {pinnedDocs.length > 0 && !searchQuery && (
-
- - Angepinnte Notizen ({pinnedDocs.length}) -
-
- {pinnedDocs.map((doc) => { - const isSelected = selectedDoc?.id === doc.id && workspaceMode === 'editor'; - return ( -
{ - e.stopPropagation(); - e.dataTransfer.setData('application/mindsift-doc-id', doc.id); - e.dataTransfer.setData('text/plain', doc.id); - setDraggingDocId(doc.id); - }} - onDragEnd={() => setDraggingDocId(null)} - onClick={() => { - setSelectedDoc(doc); - setZoomedBlockId(null); - setWorkspaceMode('editor'); - }} - className={`group flex items-center justify-between px-2 py-1.5 rounded-btn transition-colors cursor-pointer text-xs ${ - isSelected - ? 'bg-panel-elevated border-l-2 border-accent text-main font-medium' - : 'text-muted hover:text-main hover:bg-canvas/50' - }`} - title="Angepinnte Notiz öffnen" - > -
- {doc.icon || '📄'} - {doc.title || 'Unbenannt'} -
- + {!isPinnedCollapsed && ( +
+ {pinnedDocs.map((doc) => { + const isSelected = selectedDoc?.id === doc.id && workspaceMode === 'editor'; + return ( +
{ e.stopPropagation(); - persistDocument({ ...doc, isPinned: false }, true); + e.dataTransfer.setData('application/mindsift-doc-id', doc.id); + e.dataTransfer.setData('text/plain', doc.id); + setDraggingDocId(doc.id); }} - className="opacity-0 group-hover:opacity-100 p-0.5 text-muted hover:text-accent rounded transition-opacity" - title="Von Angepinnt entfernen" + onDragEnd={() => setDraggingDocId(null)} + onClick={() => { + setSelectedDoc(doc); + setZoomedBlockId(null); + setWorkspaceMode('editor'); + }} + className={`group flex items-center justify-between px-2 py-1.5 rounded-btn transition-colors cursor-pointer text-xs ${ + isSelected + ? 'bg-panel-elevated border-l-2 border-accent text-main font-medium' + : 'text-muted hover:text-main hover:bg-canvas/50' + }`} + title="Angepinnte Notiz öffnen" > - - -
- ); - })} -
+
+ {doc.icon || '📄'} + {doc.title || 'Unbenannt'} +
+ +
+ ); + })} +
+ )} +
+ )} + + {/* Themen & Hashtags (Collapsible) - directly under Pinned Notes */} + {workspaceTags.length > 0 && !searchQuery && ( +
+ + {!isTagsCollapsed && ( +
+ {workspaceTags.map((wt) => ( + + ))} +
+ )}
)} {rootTree.map(renderFolder)} {rootDocs.map((doc) => renderDocItem(doc, 0))} - {/* Workspace Hashtags Section */} - {workspaceTags.length > 0 && !searchQuery && ( -
-
-
- - Themen & Hashtags ({workspaceTags.length}) -
-
-
- {workspaceTags.map((wt) => ( - - ))} -
-
- )} - {filteredDocs.length === 0 && searchQuery && (
Keine Dateien gefunden für "{searchQuery}" @@ -3233,64 +3268,161 @@ export const WorkspaceView: React.FC = ({ {/* ===================================================================== */} {/* VIEW MODE 3: HASHTAG / THEMEN-SEITE (LOGSEQ STYLE) */} {/* ===================================================================== */} - {workspaceMode === 'tag' && ( + {workspaceMode === 'tags' && (
- {/* Tag Page Header */} -
+ {/* Header Toolbar */} +
- -
-
-
- + {selectedTag ? ( + <> + +
+
+
+ +
+
+

+ #{selectedTag} +

+

+ {isLoadingTag + ? 'Wird geladen…' + : `${tagDetails?.totalMatches || 0} ${ + tagDetails?.totalMatches === 1 ? 'Abschnitt' : 'Abschnitte' + } in ${tagDetails?.documents.length || 0} ${ + tagDetails?.documents.length === 1 ? 'Notiz' : 'Notizen' + }`} +

+
+
+ + ) : ( +
+
+ +
+
+

Themen & Hashtags

+

+ {workspaceTags.length} {workspaceTags.length === 1 ? 'Thema' : 'Themen'} im gesamten Workspace +

+
-
-

- #{selectedTag} -

-

- {isLoadingTag - ? 'Wird geladen…' - : `${tagDetails?.totalMatches || 0} ${ - tagDetails?.totalMatches === 1 ? 'Abschnitt' : 'Abschnitte' - } in ${tagDetails?.documents.length || 0} ${ - tagDetails?.documents.length === 1 ? 'Notiz' : 'Notizen' - }`} -

-
-
+ )}
- {/* Other tags pills for fast hopping */} -
- {workspaceTags - .filter((t) => t.tag !== selectedTag) - .slice(0, 6) - .map((t) => ( - - ))} + {/* Search Bar for tags */} +
+ + setTagSearchQuery(e.target.value)} + placeholder="Hashtags durchsuchen…" + className="w-full h-8 pl-8 pr-7 input-field text-xs rounded-btn" + /> + {tagSearchQuery && ( + + )}
+ + {/* Quick tag hopping pills */} + {workspaceTags.length > 0 && ( +
+ Filter: + + {(tagSearchQuery ? filteredWorkspaceTags : workspaceTags).slice(0, 10).map((t) => ( + + ))} +
+ )}
- {/* Matched Documents List */} - {isLoadingTag ? ( + {/* If NO tag is selected, show the big overview grid! */} + {!selectedTag ? ( +
+ {filteredWorkspaceTags.length === 0 ? ( +
+ +

+ {tagSearchQuery + ? `Keine Hashtags gefunden für "${tagSearchQuery}"` + : 'Noch keine Themen oder Hashtags vorhanden'} +

+

+ Füge einfach in irgendeiner Notiz ein Tag wie #projekt oder #idee ein. +

+
+ ) : ( +
+ {filteredWorkspaceTags.map((wt) => ( +
openTagPage(wt.tag)} + className="group panel-surface p-4 rounded-card border border-border hover:border-accent hover:shadow-md cursor-pointer transition-all flex flex-col justify-between" + > +
+
+ +
+ + {wt.count} {wt.count === 1 ? 'Abschnitt' : 'Abschnitte'} + +
+
+

+ #{wt.tag} +

+

+ Abschnitte ansehen + +

+
+
+ ))} +
+ )} +
+ ) : isLoadingTag ? (
Hashtag-Verknüpfungen werden geladen…