diff --git a/messages/de.json b/messages/de.json index c7ec2d512..4f004de08 100644 --- a/messages/de.json +++ b/messages/de.json @@ -8073,8 +8073,6 @@ "listLabel": "Dokumenten-Zeitleiste" }, "upload": { - "zoneTitle": "Dateien hier ablegen oder vom Gerät auswählen", - "zoneHint": "PDF, Fotos und Office-Dateien bis je {maxSize}", "browse": "Dateien auswählen", "quotaAlmostFull": "Speicher fast voll", "quotaUsed": "{used} von {quota} belegt", diff --git a/messages/en.json b/messages/en.json index 9f3a0a5a2..ccd34b44c 100644 --- a/messages/en.json +++ b/messages/en.json @@ -8073,8 +8073,6 @@ "listLabel": "Documents timeline" }, "upload": { - "zoneTitle": "Drop files here, or choose from your device", - "zoneHint": "PDF, photos, and office files up to {maxSize} each", "browse": "Choose files", "quotaAlmostFull": "Storage almost full", "quotaUsed": "{used} of {quota} used", diff --git a/messages/es.json b/messages/es.json index 68948531b..a640f86eb 100644 --- a/messages/es.json +++ b/messages/es.json @@ -8073,8 +8073,6 @@ "listLabel": "Cronología de documentos" }, "upload": { - "zoneTitle": "Suelta archivos aquí o elige desde tu dispositivo", - "zoneHint": "PDF, fotos y archivos de Office de hasta {maxSize} cada uno", "browse": "Elegir archivos", "quotaAlmostFull": "Almacenamiento casi lleno", "quotaUsed": "{used} de {quota} usados", diff --git a/messages/fr.json b/messages/fr.json index 46914f47c..87256c3e2 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -8073,8 +8073,6 @@ "listLabel": "Chronologie des documents" }, "upload": { - "zoneTitle": "Déposez des fichiers ici ou choisissez depuis votre appareil", - "zoneHint": "PDF, photos et fichiers Office jusqu'à {maxSize} chacun", "browse": "Choisir des fichiers", "quotaAlmostFull": "Stockage presque plein", "quotaUsed": "{used} sur {quota} utilisés", diff --git a/messages/it.json b/messages/it.json index 7d13b2fa0..43ae6aefa 100644 --- a/messages/it.json +++ b/messages/it.json @@ -8073,8 +8073,6 @@ "listLabel": "Cronologia dei documenti" }, "upload": { - "zoneTitle": "Trascina i file qui o scegli dal dispositivo", - "zoneHint": "PDF, foto e file Office fino a {maxSize} ciascuno", "browse": "Scegli file", "quotaAlmostFull": "Spazio quasi pieno", "quotaUsed": "{used} di {quota} usati", diff --git a/messages/pl.json b/messages/pl.json index e51e0c00d..27cfe24be 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -8073,8 +8073,6 @@ "listLabel": "Oś czasu dokumentów" }, "upload": { - "zoneTitle": "Upuść pliki tutaj lub wybierz z urządzenia", - "zoneHint": "PDF, zdjęcia i pliki Office do {maxSize} każdy", "browse": "Wybierz pliki", "quotaAlmostFull": "Miejsce prawie pełne", "quotaUsed": "Użyto {used} z {quota}", diff --git a/src/components/documents/document-detail-sheet.tsx b/src/components/documents/document-detail-sheet.tsx index 5c82a3e6a..1c89bb981 100644 --- a/src/components/documents/document-detail-sheet.tsx +++ b/src/components/documents/document-detail-sheet.tsx @@ -285,22 +285,27 @@ export function DocumentDetailSheet({ contentWidth="3xl" footer={ doc ? ( - <> - + // Delete sits bottom-LEFT, quieted to a text button: the + // bottom-right slot reads as the primary/confirm action, and a + // destructive control there is a footgun. Download — the safe, + // expected action — keeps the trailing edge. +
- + +
) : undefined } > diff --git a/src/components/documents/document-filter-bar.tsx b/src/components/documents/document-filter-bar.tsx index b0e877829..b17dfe4a0 100644 --- a/src/components/documents/document-filter-bar.tsx +++ b/src/components/documents/document-filter-bar.tsx @@ -68,8 +68,12 @@ export function DocumentFilterBar({ data-slot="document-filter-bar" className="bg-background/95 border-border sticky top-0 z-10 -mx-4 border-b px-4 pt-1 pb-3 backdrop-blur md:-mx-6 md:px-6" > -
-
+ {/* Search shares one row with the tag chips on desktop — it wraps to + its own line only on phone widths where a chip scroller plus an + input can't share a row. The chip scroller flexes to fill the + rest; the clear button pins to the trailing edge. */} +
+
+ +
+ + {DOCUMENT_KIND_ORDER.map((kind) => { + const Icon = DOCUMENT_KIND_ICONS[kind]; + const active = activeKinds.has(kind); + return ( + + ); + })} + + + {conditionChips.length > 0 ? ( + <> + + + {conditionChips.map((chip) => { + const active = chip.episodeId === activeEpisodeId; + return ( + + ); + })} + + + ) : null} + + {years.length > 0 ? ( + <> + + + {years.map((year) => { + const active = year === activeYear; + return ( + + ); + })} + + + ) : null} +
+ {activeCount > 0 ? ( ) : null}
- -
- - {DOCUMENT_KIND_ORDER.map((kind) => { - const Icon = DOCUMENT_KIND_ICONS[kind]; - const active = activeKinds.has(kind); - return ( - - ); - })} - - - {conditionChips.length > 0 ? ( - <> - - - {conditionChips.map((chip) => { - const active = chip.episodeId === activeEpisodeId; - return ( - - ); - })} - - - ) : null} - - {years.length > 0 ? ( - <> - - - {years.map((year) => { - const active = year === activeYear; - return ( - - ); - })} - - - ) : null} -
); } diff --git a/src/components/documents/upload-zone.tsx b/src/components/documents/upload-zone.tsx index 91c4ba1e8..c94863234 100644 --- a/src/components/documents/upload-zone.tsx +++ b/src/components/documents/upload-zone.tsx @@ -1,23 +1,23 @@ "use client"; /** - * The vault's upload entry: a dashed drop-target card with a file button. + * The vault's upload plumbing: the hidden file input the page header's + * Upload button opens, plus a quota bar that appears only above 80 % + * usage. There is no standing drop-target card — it took a full band of + * vertical space on every visit for an affordance the header button and + * the page-wide drag-and-drop overlay already cover. Dropping a file + * anywhere on the page is caught by that overlay; the header button opens + * this input. + * * The `accept` list comes verbatim from the usage endpoint (HEIC is * deliberately absent so the iOS picker transcodes camera photos to JPEG), * `multiple` is on, and there is NO `capture` attribute — mobile Safari - * then offers camera AND library. Dropping files onto the card feeds the - * same queue as the picker. - * - * The quota bar renders only above 80 % usage — a calm surface until - * storage actually becomes a topic. + * then offers camera AND library. */ -import { UploadCloud } from "lucide-react"; -import { useRef, useState, type RefObject } from "react"; +import { type RefObject } from "react"; -import { Button } from "@/components/ui/button"; import { Progress } from "@/components/ui/progress"; import { useTranslations } from "@/lib/i18n/context"; -import { cn } from "@/lib/utils"; import type { DocumentUsageDto } from "@/lib/validations/inbound-documents"; import { formatBytes } from "./vault-utils"; @@ -36,8 +36,6 @@ export function UploadZone({ inputRef: RefObject; }) { const { t, locale } = useTranslations(); - const [dragOver, setDragOver] = useState(false); - const dragDepth = useRef(0); const accept = usage?.acceptedExtensions.join(","); const usedFraction = @@ -49,71 +47,20 @@ export function UploadZone({ }; return ( -
-
{ - e.preventDefault(); - dragDepth.current += 1; - setDragOver(true); +
+ { + pickFiles(e.target.files); + // Allow re-selecting the same file (duplicate flow) immediately. + e.target.value = ""; }} - onDragOver={(e) => e.preventDefault()} - onDragLeave={() => { - dragDepth.current = Math.max(0, dragDepth.current - 1); - if (dragDepth.current === 0) setDragOver(false); - }} - onDrop={(e) => { - e.preventDefault(); - dragDepth.current = 0; - setDragOver(false); - pickFiles(e.dataTransfer.files); - }} - className={cn( - "border-border rounded-xl border-2 border-dashed px-4 py-6 text-center transition-colors", - dragOver && "border-primary bg-primary/5", - )} - > -
- -

- {t("documents.upload.zoneTitle")} -

- {usage ? ( -

- {t("documents.upload.zoneHint", { - maxSize: formatBytes(usage.maxFileBytes, locale), - })} -

- ) : null} - -
- { - pickFiles(e.target.files); - // Allow re-selecting the same file (duplicate flow) immediately. - e.target.value = ""; - }} - /> -
+ /> {usage && usedFraction >= QUOTA_BAR_THRESHOLD ? (
diff --git a/src/components/layout/auth-shell.tsx b/src/components/layout/auth-shell.tsx index c2db1f0bd..23fb7b3a7 100644 --- a/src/components/layout/auth-shell.tsx +++ b/src/components/layout/auth-shell.tsx @@ -36,13 +36,6 @@ const PUBLIC_PATHS = [ "/c/", ]; -// Routes that opt into the wide content container (`max-w-screen-2xl` -// instead of the standard `max-w-screen-xl`). The shell stays the single -// owner of the page frame — a wide page never re-implements paddings or -// margins; it is listed here instead. Currently: the Dokumente vault, -// whose multi-column document timeline earns the extra width. -const WIDE_CONTENT_PATHS = ["/documents"]; - export function AuthShell({ children, demoMode = false, @@ -76,9 +69,6 @@ export function AuthShell({ pathname.startsWith("/about") || pathname.startsWith("/c/"); const isAdminPage = pathname.startsWith("/admin"); - const isWidePage = WIDE_CONTENT_PATHS.some( - (p) => pathname === p || pathname.startsWith(`${p}/`), - ); const isOnboardingPage = pathname === "/onboarding"; const showUnlockNotifier = isAuthenticated && !isPublicPage && !!user?.id; @@ -343,13 +333,7 @@ export function AuthShell({ the desktop bottom-6 anchor), so the last line of content can always scroll out from under the floating button. */} -
+
{children}