ORDNERUEBERSICHT. Eine Spalte neben der Galerie mit allen Ordnern, auf allen Ebenen, mit Anzahl und Groesse; ein Klick filtert. Neu dafuer: - GET /api/dash/folders -- die Ordner werden in JavaScript zusammengezaehlt und nicht in SQL: fuer eine Baumansicht braucht es JEDE Ebene, und das waere in SQLite eine rekursive Abfrage, die niemand mehr liest. - GET /api/dash/media?folder=... als EIGENER Filter neben der Suche. Die Suche findet mit LIKE %...% auch "alt/vehicles/adder.webp"; ein Ordner, der Dinge zeigt, die nicht darin liegen, ist keiner. Hier ist der Anfang verankert. - Die LIKE-Sonderzeichen werden maskiert: ein Ordner "fotos_alt" wuerde sonst auch "fotosXalt" treffen, weil _ in LIKE ein beliebiges Zeichen ist. Die Zahlen sind kumulativ -- "vehicles" enthaelt, was in "vehicles/showroom" liegt. Alles andere ueberrascht in einer Baumansicht. Nachgemessen ueber drei Ebenen: vehicles (24) > showroom (2) > tief (1), Filter und Zaehlung stimmen, und nach dem Aufraeumen steht wieder vehicles (22). Flache Liste mit Einrueckung statt aufklappbarem Baum: bei einer Handvoll Ordner ist ein Aufklappmechanismus mehr Bedienung als Nutzen -- und was man nicht aufklappen muss, kann man auch nicht zugeklappt uebersehen. Dazu: der NPM-Zwischenspeicher ist zu. Der Schnipsel steht im Feld "Custom Nginx Configuration"; NPM setzt ihn innerhalb des server-Blocks ein, eine location ist dort also gueltig. Nachgemessen: cache-control kommt wieder von uns, und ein ueberschriebenes Bild ist sofort das neue. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
92 lines
3.1 KiB
TypeScript
92 lines
3.1 KiB
TypeScript
import { Folder, FolderOpen, Layers } from 'lucide-react'
|
|
import type { Folders } from '../api'
|
|
import { bytes } from '../format'
|
|
|
|
/** Die Ordnerübersicht neben der Galerie.
|
|
*
|
|
* Kein aufklappbarer Baum, sondern eine flache Liste mit Einrückung. Bei
|
|
* einer Handvoll Ordner ist ein Aufklappmechanismus mehr Bedienung als
|
|
* Nutzen — und was man nicht aufklappen muss, kann man auch nicht zugeklappt
|
|
* übersehen. */
|
|
export function Ordner({
|
|
daten,
|
|
gewaehlt,
|
|
onWahl,
|
|
}: {
|
|
daten: Folders | null
|
|
gewaehlt: string
|
|
onWahl: (ordner: string) => void
|
|
}) {
|
|
if (!daten) return null
|
|
|
|
const zeile = (
|
|
aktiv: boolean,
|
|
kind: React.ReactNode,
|
|
name: string,
|
|
anzahl: number,
|
|
groesse: number,
|
|
tiefe = 0,
|
|
onClick?: () => void,
|
|
) => (
|
|
<button
|
|
key={name + tiefe}
|
|
onClick={onClick}
|
|
className={`flex w-full items-center gap-2 rounded-md py-1.5 pr-2 text-left text-sm
|
|
transition-colors ${
|
|
aktiv
|
|
? 'bg-flaeche-hoch text-schrift'
|
|
: 'text-schrift-leise hover:bg-flaeche-hoch/50 hover:text-schrift'
|
|
}`}
|
|
style={{ paddingLeft: `${0.5 + tiefe * 0.85}rem` }}
|
|
>
|
|
<span className={aktiv ? 'text-akzent' : ''}>{kind}</span>
|
|
<span className="flex-1 truncate font-mono text-xs" title={name}>
|
|
{name}
|
|
</span>
|
|
<span className="shrink-0 text-[11px] tabular-nums text-schrift-leise">
|
|
{anzahl}
|
|
</span>
|
|
<span className="w-14 shrink-0 text-right text-[11px] tabular-nums text-schrift-leise">
|
|
{bytes(groesse)}
|
|
</span>
|
|
</button>
|
|
)
|
|
|
|
return (
|
|
<aside className="w-full shrink-0 self-start rounded-lg border border-rand bg-flaeche p-2 lg:w-72">
|
|
<h2 className="px-2 py-1 text-xs font-semibold text-schrift-leise">Ordner</h2>
|
|
|
|
{zeile(
|
|
gewaehlt === '',
|
|
<Layers size={14} />,
|
|
'Alle',
|
|
daten.gesamt.files,
|
|
daten.gesamt.bytes,
|
|
0,
|
|
() => onWahl(''),
|
|
)}
|
|
|
|
{daten.items.map((o) =>
|
|
zeile(
|
|
gewaehlt === o.folder,
|
|
gewaehlt === o.folder ? <FolderOpen size={14} /> : <Folder size={14} />,
|
|
// Nur das letzte Stück anzeigen — der Weg dorthin steht
|
|
// schon in der Einrückung. Der volle Pfad hängt im title.
|
|
o.folder.split('/').pop() ?? o.folder,
|
|
o.files,
|
|
o.bytes,
|
|
o.tiefe,
|
|
() => onWahl(o.folder),
|
|
),
|
|
)}
|
|
|
|
{daten.wurzel.files > 0 && (
|
|
<p className="mt-2 px-2 text-[11px] text-schrift-leise">
|
|
{daten.wurzel.files} Datei(en) liegen ohne Ordner direkt an der Wurzel — zu
|
|
sehen unter „Alle".
|
|
</p>
|
|
)}
|
|
</aside>
|
|
)
|
|
}
|