feat: mobile Responsiveness für Suche und Mail-Detailansicht

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
sysops
2026-06-13 13:18:19 +02:00
parent bc82854165
commit cca27c663a
3 changed files with 25 additions and 18 deletions
+2 -2
View File
@@ -24,7 +24,7 @@
| PROJ-10 | Admin-Bereich: Nutzer- & Postfachverwaltung | Deployed | [PROJ-10](PROJ-10-admin-bereich.md) | 2026-03-12 | | PROJ-10 | Admin-Bereich: Nutzer- & Postfachverwaltung | Deployed | [PROJ-10](PROJ-10-admin-bereich.md) | 2026-03-12 |
| PROJ-11 | Audit-Log & Compliance-Berichte | Deployed | [PROJ-11](PROJ-11-audit-log.md) | 2026-03-12 | | PROJ-11 | Audit-Log & Compliance-Berichte | Deployed | [PROJ-11](PROJ-11-audit-log.md) | 2026-03-12 |
| PROJ-12 | E-Mail-Export (EML/PDF) | Deployed | [PROJ-12](PROJ-12-export.md) | 2026-03-12 | | PROJ-12 | E-Mail-Export (EML/PDF) | Deployed | [PROJ-12](PROJ-12-export.md) | 2026-03-12 |
| PROJ-13 | REST API für externe CRM-Anbindung | In Progress | [PROJ-13](PROJ-13-rest-api-crm.md) | 2026-03-13 | | PROJ-13 | REST API für externe CRM-Anbindung | Deployed | [PROJ-13](PROJ-13-rest-api-crm.md) | 2026-03-13 |
| PROJ-14 | E-Mail-Import: POP3-Verbindung | Deployed | [PROJ-14](PROJ-14-import-pop3.md) | 2026-03-13 | | PROJ-14 | E-Mail-Import: POP3-Verbindung | Deployed | [PROJ-14](PROJ-14-import-pop3.md) | 2026-03-13 |
| PROJ-15 | CLI Import & Export (archivmail-User) | Deployed | [PROJ-15](PROJ-15-cli-import-export.md) | 2026-03-13 | | PROJ-15 | CLI Import & Export (archivmail-User) | Deployed | [PROJ-15](PROJ-15-cli-import-export.md) | 2026-03-13 |
| PROJ-16 | LDAP / Active Directory Anbindung | Deployed | [PROJ-16](PROJ-16-ldap-active-directory.md) | 2026-03-13 | | PROJ-16 | LDAP / Active Directory Anbindung | Deployed | [PROJ-16](PROJ-16-ldap-active-directory.md) | 2026-03-13 |
@@ -42,7 +42,7 @@
| PROJ-26 | IMAP-Server-Schnittstelle (Read-Only Archivzugriff) | Deployed | [PROJ-26](PROJ-26-imap-server-schnittstelle.md) | 2026-03-18 | | PROJ-26 | IMAP-Server-Schnittstelle (Read-Only Archivzugriff) | Deployed | [PROJ-26](PROJ-26-imap-server-schnittstelle.md) | 2026-03-18 |
| PROJ-27 | Container-Ready (Dockerfile + Env-Vars) | In Review | [PROJ-27](PROJ-27-container-ready.md) | 2026-03-28 | | PROJ-27 | Container-Ready (Dockerfile + Env-Vars) | In Review | [PROJ-27](PROJ-27-container-ready.md) | 2026-03-28 |
| PROJ-28 | Self-Service Onboarding (Sign-up, E-Mail-Verifikation, Passwort-Reset) | In Progress | [PROJ-28](PROJ-28-self-service-onboarding.md) | 2026-03-28 | | PROJ-28 | Self-Service Onboarding (Sign-up, E-Mail-Verifikation, Passwort-Reset) | Deployed | [PROJ-28](PROJ-28-self-service-onboarding.md) | 2026-03-28 |
| PROJ-29 | Tenant-Quotas & Usage-Limits | Deployed | [PROJ-29](PROJ-29-tenant-quotas.md) | 2026-03-28 | | PROJ-29 | Tenant-Quotas & Usage-Limits | Deployed | [PROJ-29](PROJ-29-tenant-quotas.md) | 2026-03-28 |
| PROJ-30 | Volltext-Index: Xapian → Manticore Search Migration | Deployed | [PROJ-30](PROJ-30-bleve-migration.md) | 2026-03-28 | | PROJ-30 | Volltext-Index: Xapian → Manticore Search Migration | Deployed | [PROJ-30](PROJ-30-bleve-migration.md) | 2026-03-28 |
| PROJ-31 | Billing & Subscriptions (Stripe) | Planned | [PROJ-31](PROJ-31-billing-subscriptions.md) | 2026-03-28 | | PROJ-31 | Billing & Subscriptions (Stripe) | Planned | [PROJ-31](PROJ-31-billing-subscriptions.md) | 2026-03-28 |
+4 -3
View File
@@ -168,11 +168,12 @@ function MailBodyView({ mail }: { mail: MailDetail }) {
<div className="space-y-2"> <div className="space-y-2">
{!showExternal && ( {!showExternal && (
<Alert> <Alert>
<AlertDescription className="flex items-center justify-between gap-4 text-sm"> <AlertDescription className="flex flex-col items-start gap-2 text-sm sm:flex-row sm:items-center sm:justify-between sm:gap-4">
<span>Externe Inhalte (Bilder, Tracker) sind blockiert.</span> <span>Externe Inhalte (Bilder, Tracker) sind blockiert.</span>
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
className="w-full sm:w-auto"
onClick={() => setShowExternal(true)} onClick={() => setShowExternal(true)}
> >
Externe Inhalte laden Externe Inhalte laden
@@ -346,8 +347,8 @@ export default function MailViewPage({
</Button> </Button>
{mail && ( {mail && (
<div className="flex items-center gap-2"> <div className="flex w-full flex-wrap items-center gap-2 sm:w-auto">
<Badge variant="outline" className="font-mono text-xs"> <Badge variant="outline" className="max-w-[40vw] truncate font-mono text-xs sm:max-w-none">
{id} {id}
</Badge> </Badge>
<Button <Button
+19 -13
View File
@@ -383,18 +383,18 @@ export default function SearchPage() {
{/* Main content */} {/* Main content */}
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<form onSubmit={handleSubmit} className="space-y-4"> <form onSubmit={handleSubmit} className="space-y-4">
<div className="flex gap-2"> <div className="flex flex-wrap items-center gap-2">
<Input <Input
placeholder="Volltextsuche..." placeholder="Volltextsuche..."
value={query} value={query}
onChange={(e) => setQuery(e.target.value)} onChange={(e) => setQuery(e.target.value)}
className="flex-1" className="w-full flex-1 sm:w-auto"
aria-label="Suchbegriff" aria-label="Suchbegriff"
/> />
<Button type="submit" disabled={searching}> <Button type="submit" disabled={searching} className="flex-1 sm:flex-none">
{searching ? "Suche..." : "Suchen"} {searching ? "Suche..." : "Suchen"}
</Button> </Button>
<Button type="button" variant="outline" onClick={() => setUploadOpen(true)}> <Button type="button" variant="outline" onClick={() => setUploadOpen(true)} className="flex-1 sm:flex-none">
Importieren Importieren
</Button> </Button>
{hasActiveSearch && ( {hasActiveSearch && (
@@ -596,7 +596,7 @@ export default function SearchPage() {
</Button> </Button>
</div> </div>
<Card> <Card className="overflow-x-auto">
<Table> <Table>
<TableHeader> <TableHeader>
<TableRow> <TableRow>
@@ -610,12 +610,12 @@ export default function SearchPage() {
aria-label="Alle auswählen" aria-label="Alle auswählen"
/> />
</TableHead> </TableHead>
<TableHead className="w-32">Datum</TableHead> <TableHead className="w-28 sm:w-32">Datum</TableHead>
<TableHead className="w-56">Von</TableHead> <TableHead className="hidden w-56 md:table-cell">Von</TableHead>
<TableHead>Betreff</TableHead> <TableHead>Betreff</TableHead>
<TableHead className="w-48">An</TableHead> <TableHead className="hidden w-48 lg:table-cell">An</TableHead>
<TableHead className="w-8 text-center" title="Anhang">📎</TableHead> <TableHead className="w-8 text-center" title="Anhang">📎</TableHead>
<TableHead className="w-20 text-right">Größe</TableHead> <TableHead className="hidden w-20 text-right sm:table-cell">Größe</TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <TableBody>
@@ -650,8 +650,8 @@ export default function SearchPage() {
? new Date(hit.date).toLocaleString("de-DE", { dateStyle: "short", timeStyle: "short" }) ? new Date(hit.date).toLocaleString("de-DE", { dateStyle: "short", timeStyle: "short" })
: "-"} : "-"}
</TableCell> </TableCell>
<TableCell className="max-w-[14rem] truncate text-sm">{hit.from || "-"}</TableCell> <TableCell className="hidden max-w-[14rem] truncate text-sm md:table-cell">{hit.from || "-"}</TableCell>
<TableCell className="font-medium"> <TableCell className="max-w-[60vw] font-medium sm:max-w-none">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span className="truncate">{hit.subject || "(kein Betreff)"}</span> <span className="truncate">{hit.subject || "(kein Betreff)"}</span>
{hit.thread_size && hit.thread_size > 1 && ( {hit.thread_size && hit.thread_size > 1 && (
@@ -660,13 +660,19 @@ export default function SearchPage() {
</span> </span>
)} )}
</div> </div>
{/* Absender nur auf Mobile, da Spalte "Von" dort ausgeblendet ist */}
{hit.from && (
<div className="mt-0.5 truncate text-xs text-muted-foreground md:hidden">
{hit.from}
</div>
)}
<SnippetLine hit={hit} /> <SnippetLine hit={hit} />
</TableCell> </TableCell>
<TableCell className="max-w-[12rem] truncate text-sm text-muted-foreground">{hit.to || "-"}</TableCell> <TableCell className="hidden max-w-[12rem] truncate text-sm text-muted-foreground lg:table-cell">{hit.to || "-"}</TableCell>
<TableCell className="text-center text-sm"> <TableCell className="text-center text-sm">
{hit.has_attachments ? "📎" : ""} {hit.has_attachments ? "📎" : ""}
</TableCell> </TableCell>
<TableCell className="text-right text-xs text-muted-foreground whitespace-nowrap"> <TableCell className="hidden text-right text-xs text-muted-foreground whitespace-nowrap sm:table-cell">
{hit.size ? formatBytes(hit.size) : ""} {hit.size ? formatBytes(hit.size) : ""}
</TableCell> </TableCell>
</TableRow> </TableRow>