feat(nextcloud-status): Kundenname voll ausgeschrieben, Kachel-Knoepfe in die Fusszeile
Tessera CI/CD / Lint & Type Check (push) Successful in 57s
Tessera CI/CD / Tests (push) Successful in 1m45s
Tessera CI/CD / Desktop-Pakete bauen (push) Successful in 21s
Tessera CI/CD / Build & Publish Images (push) Successful in 3m47s

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-10-08 15:57:02 +02:00
parent ff4938fa97
commit 83b842b72c
2 changed files with 64 additions and 63 deletions
+1 -1
View File
@@ -15,7 +15,7 @@ Diese Liste beschreibt in einfachen Worten, was sich von Version zu Version an T
### Geändert ### Geändert
- DKV-Rechnung: Die Modulbeschreibung im Marktplatz weist jetzt darauf hin, dass das Modul die Freigabestufe „Verwalten“ benötigt. Mit „Benutzen“ erscheint es zwar in der Seitenleiste, öffnet aber nur die Seite „Kein Zugriff“. Modulversion 1.1.0. - DKV-Rechnung: Die Modulbeschreibung im Marktplatz weist jetzt darauf hin, dass das Modul die Freigabestufe „Verwalten“ benötigt. Mit „Benutzen“ erscheint es zwar in der Seitenleiste, öffnet aber nur die Seite „Kein Zugriff“. Modulversion 1.1.0.
- Nextcloud-Status: Über der Kachelliste gibt es jetzt ein Suchfeld. Es zeigt nur die Clouds, deren Kundenname den eingegebenen Text enthält; Groß- und Kleinschreibung spielen keine Rolle, die gewählte Sortierung bleibt erhalten. Die Kacheln sind kompakter, und die Adresse der Cloud steht in voller Länge in einer eigenen Zeile, statt abgeschnitten zu werden. - Nextcloud-Status: Über der Kachelliste gibt es jetzt ein Suchfeld. Es zeigt nur die Clouds, deren Kundenname den eingegebenen Text enthält; Groß- und Kleinschreibung spielen keine Rolle, die gewählte Sortierung bleibt erhalten. Die Kacheln sind kompakter, der Kundenname und die Adresse der Cloud stehen in voller Länge da, statt abgeschnitten zu werden. Die Knöpfe Benachrichtigen, Prüfen und Bearbeiten sitzen jetzt unten rechts neben „Zuletzt geprüft“.
- Nextcloud-Status: Als Logo können Sie jetzt auch eine Bildadresse eingeben, die mit http:// beginnt. Tessera holt das Bild beim Speichern einmalig ab und behält es wie ein hochgeladenes Bild (höchstens 1 MB; PNG, JPEG, GIF oder WebP). Adressen, die nur intern erreichbar sind, holt Tessera nicht ab – solche Bilder laden Sie über „Bild hochladen“ hoch. Adressen mit https:// lädt weiterhin direkt Ihr Browser. Das Feld heißt jetzt „Bildadresse“. - Nextcloud-Status: Als Logo können Sie jetzt auch eine Bildadresse eingeben, die mit http:// beginnt. Tessera holt das Bild beim Speichern einmalig ab und behält es wie ein hochgeladenes Bild (höchstens 1 MB; PNG, JPEG, GIF oder WebP). Adressen, die nur intern erreichbar sind, holt Tessera nicht ab – solche Bilder laden Sie über „Bild hochladen“ hoch. Adressen mit https:// lädt weiterhin direkt Ihr Browser. Das Feld heißt jetzt „Bildadresse“.
### Behoben ### Behoben
@@ -98,9 +98,69 @@ export function CloudTile({
)} )}
</div> </div>
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
<div className="truncate font-semibold leading-tight" title={instance.customerName}> <div className="break-words font-semibold leading-tight">{instance.customerName}</div>
{instance.customerName} </div>
</div> </div>
{/* Adresse in voller Laenge auf eigener Zeile — bricht notfalls um statt abgeschnitten zu werden. */}
<a
href={instance.baseUrl}
target="_blank"
rel="noopener noreferrer"
className="block break-all text-xs text-muted-foreground underline-offset-2 hover:underline"
title={t('card.openCloud')}
>
{instance.baseUrl}
</a>
<div className="flex flex-wrap items-center justify-between gap-x-2 gap-y-1">
<span
data-testid="rating-pill"
className={`inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-xs font-medium ${style.pill}`}
>
<span aria-hidden="true" className={`h-1.5 w-1.5 rounded-full ${style.fill}`} />
{reason}
</span>
<span className="text-xs text-muted-foreground">
{t('card.version')}:{' '}
<span className="font-medium tabular-nums text-foreground">
{instance.status.versionString ?? t('card.versionUnknown')}
</span>
</span>
</div>
{hintText && (
<p
data-testid="error-hint"
className="break-words text-xs text-muted-foreground"
title={hintRaw}
>
{hintText}
</p>
)}
{instance.status.pendingRetry === true && (
<p
data-testid="pending-retry"
className="flex items-center gap-1.5 text-xs text-muted-foreground"
>
<span aria-hidden="true" className="h-1.5 w-1.5 shrink-0 rounded-full bg-status-warn" />
{t('card.pendingRetry')}
</p>
)}
{/* Fusszeile: Pruefzeitpunkt links, Knoepfe rechts — der Kopf bleibt dem vollen Kundennamen. */}
<div className="mt-auto flex items-center justify-between gap-2">
<div
data-testid="last-check"
className="min-w-0 text-xs text-muted-foreground"
title={
instance.status.checkedAt
? new Date(instance.status.checkedAt).toLocaleString(locale)
: undefined
}
>
{checkedAge ? t('card.lastCheck', { age: checkedAge }) : t('card.neverChecked')}
</div> </div>
<div className="flex shrink-0 items-center gap-1"> <div className="flex shrink-0 items-center gap-1">
<button <button
@@ -183,65 +243,6 @@ export function CloudTile({
)} )}
</div> </div>
</div> </div>
{/* Adresse in voller Laenge auf eigener Zeile — bricht notfalls um statt abgeschnitten zu werden. */}
<a
href={instance.baseUrl}
target="_blank"
rel="noopener noreferrer"
className="block break-all text-xs text-muted-foreground underline-offset-2 hover:underline"
title={t('card.openCloud')}
>
{instance.baseUrl}
</a>
<div className="flex flex-wrap items-center justify-between gap-x-2 gap-y-1">
<span
data-testid="rating-pill"
className={`inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-xs font-medium ${style.pill}`}
>
<span aria-hidden="true" className={`h-1.5 w-1.5 rounded-full ${style.fill}`} />
{reason}
</span>
<span className="text-xs text-muted-foreground">
{t('card.version')}:{' '}
<span className="font-medium tabular-nums text-foreground">
{instance.status.versionString ?? t('card.versionUnknown')}
</span>
</span>
</div>
{hintText && (
<p
data-testid="error-hint"
className="break-words text-xs text-muted-foreground"
title={hintRaw}
>
{hintText}
</p>
)}
{instance.status.pendingRetry === true && (
<p
data-testid="pending-retry"
className="flex items-center gap-1.5 text-xs text-muted-foreground"
>
<span aria-hidden="true" className="h-1.5 w-1.5 shrink-0 rounded-full bg-status-warn" />
{t('card.pendingRetry')}
</p>
)}
<div
data-testid="last-check"
className="mt-auto text-xs text-muted-foreground"
title={
instance.status.checkedAt
? new Date(instance.status.checkedAt).toLocaleString(locale)
: undefined
}
>
{checkedAge ? t('card.lastCheck', { age: checkedAge }) : t('card.neverChecked')}
</div>
</div> </div>
</article> </article>
); );