diff --git a/migrations/010_color_tags.sql b/migrations/010_color_tags.sql new file mode 100644 index 0000000..4105aac --- /dev/null +++ b/migrations/010_color_tags.sql @@ -0,0 +1,4 @@ +-- Farbmarkierungen wie bei Picdrop (Rot / Orange / Grün / Final). +ALTER TABLE items ADD COLUMN IF NOT EXISTS color_tag text + CHECK (color_tag IN ('red','orange','green','final')); +CREATE INDEX IF NOT EXISTS items_color_tag_idx ON items(color_tag); diff --git a/src/components/LibraryApp.tsx b/src/components/LibraryApp.tsx index ca378a4..3155f48 100644 --- a/src/components/LibraryApp.tsx +++ b/src/components/LibraryApp.tsx @@ -2,7 +2,16 @@ import React, { useState, useEffect, useCallback } from 'react'; interface Item { id: string; filename: string; output_px: string; has_alpha: boolean; folder_id: string | null; by_name: string; tasks: string[]; delivery_status: string; nas_status: string; - model_used: string; variant_of: string | null; mode: string; thumb_path: string | null; prompt_used: string | null; created_at: string; } + model_used: string; variant_of: string | null; mode: string; thumb_path: string | null; prompt_used: string | null; + color_tag: string | null; created_at: string; } + +const TAGS = [ + { id: 'red', label: 'Rot', color: '#E5484D' }, + { id: 'orange', label: 'Orange', color: '#E8830C' }, + { id: 'green', label: 'Grün', color: '#46A758' }, + { id: 'final', label: 'Final', color: '#3A3733' }, +]; +const tagColor = (t: string | null) => TAGS.find((x) => x.id === t)?.color || null; export default function LibraryApp() { const [items, setItems] = useState([]); @@ -13,6 +22,9 @@ export default function LibraryApp() { const [variantSel, setVariantSel] = useState>({}); const [compare, setCompare] = useState(null); const [promptOf, setPromptOf] = useState(null); + const [saveView, setSaveView] = useState(null); + const [tagMenu, setTagMenu] = useState(null); + const [tagFilter, setTagFilter] = useState(null); const [toast, setToast] = useState(null); const notify = (t: string) => { setToast(t); setTimeout(() => setToast(null), 2400); }; @@ -36,6 +48,11 @@ export default function LibraryApp() { const rename = async (id: string, filename: string) => fetch(`/api/items/${id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ filename }) }); + const setTag = async (id: string, color_tag: string | null) => { + setItems((x) => x.map((y) => y.id === id ? { ...y, color_tag } : y)); + setTagMenu(null); + await fetch(`/api/items/${id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ color_tag }) }); + }; const setFolder = async (id: string, folder_id: string | null) => { await fetch(`/api/items/${id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ folder_id }) }); load(); @@ -90,8 +107,10 @@ export default function LibraryApp() { load(); }; - // Nach Ordner filtern, dann Alternativen unter ihrem Ursprung gruppieren. - const filtered = folderFilter ? items.filter((v) => v.folder_id === folderFilter) : items; + // Nach Ordner + Markierung filtern, dann Alternativen unter ihrem Ursprung gruppieren. + const filtered = items + .filter((v) => !folderFilter || v.folder_id === folderFilter) + .filter((v) => !tagFilter || v.color_tag === tagFilter); const groupMap = new Map(); for (const it of filtered) { const root = it.variant_of || it.id; @@ -139,6 +158,16 @@ export default function LibraryApp() {
Ordner gruppieren die Bibliothek und können je Ordner eine eigene Picdrop-Galerie bekommen (Pfeil = eigene Galerie hinterlegt).
+ {/* Farbmarkierungen filtern */} +
+ + {TAGS.map((t) => ( + + ))} +
+ {filtered.length === 0 ? (

{folderFilter ? 'Ordner ist leer' : 'Noch nichts erstellt'}

{folderFilter ? 'Weise Bildern über das Ordner-Menü diesen Ordner zu.' : 'Lege im Studio los — die Ergebnisse sammeln sich hier.'}

@@ -154,6 +183,7 @@ export default function LibraryApp() { {pick.has(v.id) ? '✓' : ''} {(v.mode === 'compose' || v.mode === 'generate') && {v.mode === 'compose' ? 'kombiniert' : 'erzeugt'}} + {v.color_tag && } {multi && Fassung {g.indexOf(v) + 1}/{g.length}} @@ -177,11 +207,23 @@ export default function LibraryApp() { {v.nas_status === 'failed' && NAS ✕}
+
+ + {tagMenu === v.id && ( +
setTagMenu(null)}> + + {TAGS.map((t) => ( + + ))} +
+ )} +
{v.prompt_used && } {v.mode !== 'generate' && } - Laden - - + + +