style(lists): align list design with tasks view using square checkboxes, white hashtag headers on canvas, and panel-surface cards
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 59s

This commit is contained in:
sandrodach committed 2026-10-25 18:27:38 +01:00
1 parent 0d6c603e9c
commit 3b4a8b125b
1 file changed
+144 -97
+144 -97
View File
@@ -1,8 +1,6 @@
import React, { useEffect, useMemo, useRef, useState } from 'react';
import {
Plus,
CheckCircle2,
Circle,
Trash2,
Users,
Check,
@@ -497,111 +495,139 @@ export const SharedListsView: React.FC<SharedListsViewProps> = ({ onRefreshGloba
</form>
)}
{/* Active Shared List Card */}
{/* Active Shared List View */}
{activeList ? (
<div className="p-3 panel-surface">
<div className="space-y-4">
{/* Quick-Add Form */}
<form onSubmit={handleAddItem} className="space-y-2 mb-3">
<div className="flex flex-col sm:flex-row gap-2">
<form
onSubmit={handleAddItem}
className="flex flex-col sm:flex-row items-center gap-2 panel-surface p-1.5"
>
<div className="flex items-center gap-2 flex-1 w-full px-1.5">
<Plus className="w-4 h-4 text-accent flex-shrink-0" />
<input
type="text"
value={newItemText}
onChange={(e) => setNewItemText(e.target.value)}
placeholder={`Eintrag zu "${activeList.title}" hinzufügen (z. B. Hafermilch #Kühlregal oder #Kaffee)…`}
className="flex-1 h-9 px-3 input-field text-sm"
placeholder={`Eintrag zu "${activeList.title}" hinzufügen (z. B. Hafermilch #Kühlregal)…`}
className="flex-1 bg-transparent py-1 text-sm text-main placeholder-faint focus:outline-none"
/>
<div className="flex gap-2">
<input
type="text"
value={newItemQty}
onChange={(e) => setNewItemQty(e.target.value)}
placeholder="Menge (z.B. 2x)"
className="w-32 h-9 px-3 input-field text-sm"
/>
<button
type="submit"
disabled={!newItemText.trim()}
className="btn-primary h-9 px-4 disabled:opacity-40 disabled:cursor-not-allowed text-xs sm:text-sm flex items-center gap-1.5"
>
<Plus className="w-4 h-4" />
<span>Hinzufügen</span>
</button>
</div>
</div>
<div className="flex items-center gap-1.5 w-full sm:w-auto justify-end px-1 sm:px-0">
<input
type="text"
value={newItemQty}
onChange={(e) => setNewItemQty(e.target.value)}
placeholder="Menge (z.B. 2x)"
className="w-28 h-7 px-2 input-field text-xs"
/>
<button
type="submit"
disabled={!newItemText.trim()}
className="btn-primary h-7 px-3 text-xs disabled:opacity-40 disabled:cursor-not-allowed flex items-center gap-1 flex-shrink-0"
>
Hinzufügen
</button>
</div>
</form>
{/* Open Items Grouped by Hashtags / Categories */}
<div className="border border-border divide-y divide-border rounded-card overflow-hidden">
{openItems.length === 0 ? (
<div className="py-6 text-center text-muted text-sm flex flex-col items-center gap-1.5">
{openItems.length === 0 ? (
<div className="p-8 text-center text-muted text-sm space-y-1 panel-surface">
<p className="font-medium text-main flex items-center justify-center gap-1.5">
<Sparkles className="w-4 h-4 text-accent" />
<span>Alles erledigt. Keine offenen Punkte auf dieser Liste.</span>
</div>
) : (
groupedOpenItems.map((group) => (
<div key={group.category || 'uncategorized'} className="divide-y divide-border">
{/* Category Group Header (only when categories are in use) */}
<span>Alles erledigt</span>
</p>
<p className="text-xs text-faint">Keine offenen Punkte auf dieser Liste.</p>
</div>
) : (
<div className="space-y-4">
{groupedOpenItems.map((group) => (
<div key={group.category || 'uncategorized'} className="space-y-1.5">
{/* Category Group Header: white text directly on canvas background */}
{group.label && (
<div className="px-3 py-1.5 bg-surface-2/70 text-xs font-semibold text-accent flex items-center justify-between">
<span className="flex items-center gap-1">
<span>{group.label}</span>
<div className="flex items-center justify-between px-1">
<span className="text-xs font-semibold text-main tracking-wide">
{group.label}
</span>
<span className="text-[10px] text-muted font-normal">
{group.items.length} {group.items.length === 1 ? 'Eintrag' : 'Einträge'}
<span className="badge-neutral font-mono text-[10px]">
{group.items.length}
</span>
</div>
)}
{group.items.map((item) => {
const cat = getItemCategory(item);
const displayContent = getItemDisplayContent(item);
{/* Lighter panel-surface card container with items */}
<div className="panel-surface divide-y divide-border overflow-hidden">
{group.items.map((item) => {
const cat = getItemCategory(item);
const displayContent = getItemDisplayContent(item);
return (
<div
key={item.id}
onClick={() => handleToggleItem(item)}
className="px-3 py-2 bg-canvas hover:bg-panel-elevated flex items-center justify-between gap-3 cursor-pointer transition-colors group"
>
<div className="flex items-center gap-2.5 min-w-0">
<Circle className="w-4 h-4 text-faint group-hover:text-accent flex-shrink-0 transition-colors" />
<span className="text-sm font-normal text-main truncate">
{displayContent}
</span>
{item.quantity && (
<span className="badge-accent">
{item.quantity}
</span>
)}
{cat && (
<span className="px-2 py-0.5 text-xs font-medium text-accent bg-accent/10 border border-accent/25 rounded-full">
#{cat}
</span>
)}
</div>
<button
type="button"
onClick={(e) => {
e.stopPropagation();
handleDeleteItem(item.id);
}}
className="btn-icon btn-icon-danger w-6 h-6"
return (
<div
key={item.id}
onClick={() => handleToggleItem(item)}
className="px-3 py-2.5 flex items-center justify-between gap-3 cursor-pointer transition-colors hover:bg-panel-hover group"
>
<Trash2 className="w-3 h-3" />
</button>
</div>
);
})}
<div className="flex items-center gap-3 min-w-0 flex-1">
{/* Eckiges Abhaken */}
<button
type="button"
onClick={(e) => {
e.stopPropagation();
handleToggleItem(item);
}}
className={`w-4 h-4 checkbox-square flex items-center justify-center flex-shrink-0 transition-colors border ${
item.isCompleted
? 'bg-status-ok border-status-ok text-on-accent'
: 'border-strong hover:border-accent text-transparent'
}`}
title={item.isCompleted ? 'Als offen markieren' : 'Als erledigt abhaken'}
>
<Check className="w-3 h-3 stroke-[3]" />
</button>
<span className="text-sm font-medium text-main truncate">
{displayContent}
</span>
{item.quantity && (
<span className="badge-accent">
{item.quantity}
</span>
)}
{cat && !group.label && (
<span className="px-1.5 py-0.5 text-[10px] font-medium text-accent bg-accent/10 border border-accent/25 rounded-md">
#{cat}
</span>
)}
</div>
<button
type="button"
onClick={(e) => {
e.stopPropagation();
handleDeleteItem(item.id);
}}
className="btn-icon btn-icon-danger opacity-0 group-hover:opacity-100 transition-opacity"
title="Eintrag löschen"
>
<Trash2 className="w-3.5 h-3.5" />
</button>
</div>
);
})}
</div>
</div>
))
)}
</div>
))}
</div>
)}
{/* Completed Items Section */}
{completedItems.length > 0 && (
<div className="mt-3 pt-2.5 border-t border-border">
<div className="flex items-center justify-between mb-2">
<span className="text-xs font-medium uppercase tracking-wider text-muted">
<div className="space-y-1.5 pt-2">
<div className="flex items-center justify-between px-1">
<span className="text-xs font-semibold text-muted uppercase tracking-wider">
Erledigt ({completedItems.length})
</span>
<button
@@ -612,34 +638,55 @@ export const SharedListsView: React.FC<SharedListsViewProps> = ({ onRefreshGloba
</button>
</div>
<div className="border border-border divide-y divide-border opacity-75 rounded-card overflow-hidden">
<div className="panel-surface divide-y divide-border opacity-70 overflow-hidden">
{completedItems.map((item) => {
const cat = getItemCategory(item);
const displayContent = getItemDisplayContent(item);
return (
<div
key={item.id}
onClick={() => handleToggleItem(item)}
className="px-3 py-1.5 bg-canvas/50 hover:bg-canvas/80 flex items-center justify-between gap-3 cursor-pointer transition-colors"
className="px-3 py-2 flex items-center justify-between gap-3 cursor-pointer hover:bg-panel-hover transition-colors group"
>
<div className="flex items-center gap-2.5 min-w-0">
<CheckCircle2 className="w-4 h-4 text-accent flex-shrink-0" />
<span className="text-sm text-muted line-through truncate">
<div className="flex items-center gap-3 min-w-0 flex-1">
{/* Eckiges Abhaken */}
<button
type="button"
onClick={(e) => {
e.stopPropagation();
handleToggleItem(item);
}}
className="w-4 h-4 checkbox-square flex items-center justify-center flex-shrink-0 transition-colors border bg-status-ok border-status-ok text-on-accent"
title="Als offen markieren"
>
<Check className="w-3 h-3 stroke-[3]" />
</button>
<span className="text-sm text-faint line-through truncate">
{displayContent}
</span>
{item.quantity && (
<span className="text-xs text-faint">({item.quantity})</span>
)}
{cat && (
<span className="text-[11px] px-1.5 py-0.2 badge-neutral text-muted rounded-full">
#{cat}
</span>
)}
</div>
{item.completedByName && (
<span className="text-xs text-faint">✓ {item.completedByName}</span>
)}
<div className="flex items-center gap-2">
{item.completedByName && (
<span className="text-xs text-faint">✓ {item.completedByName}</span>
)}
<button
type="button"
onClick={(e) => {
e.stopPropagation();
handleDeleteItem(item.id);
}}
className="btn-icon btn-icon-danger opacity-0 group-hover:opacity-100 transition-opacity"
title="Löschen"
>
<Trash2 className="w-3.5 h-3.5" />
</button>
</div>
</div>
);
})}