- The INBUXA palette on warm surfaces, softer cards, buttons and inputs, and self-hosted Inter and Space Grotesk. - Each section's icon sits on a colored tile, colored by what the section is about. - The sidebar gets a guide line for sub-pages and a labeled Management / Settings / Account switch, and folds to a rail of tiles (remembered). - Every page has a header with its section's tile. - Fields and pages with no label of their own are spelled out in words (defaultCertificateId becomes Default certificate ID). - The dashboard greets you, and its stat cards wear colored tiles. - Unavailable live numbers are a calm note, not an error. - The cat appears in empty lists and while loading. - Chart colors work again: they were hex values wrapped in hsl().
46 lines
1.6 KiB
TypeScript
46 lines
1.6 KiB
TypeScript
/*
|
|
* SPDX-FileCopyrightText: 2026 Coffey Labs
|
|
*
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
*/
|
|
|
|
import { useTranslation } from 'react-i18next';
|
|
import { useAuthStore } from '@/stores/authStore';
|
|
import inbuxaMark from '@/assets/inbuxa-mark.png';
|
|
|
|
function partOfDay(hour: number): 'morning' | 'afternoon' | 'evening' {
|
|
if (hour < 12) return 'morning';
|
|
if (hour < 18) return 'afternoon';
|
|
return 'evening';
|
|
}
|
|
|
|
/** The dashboard's hello: whose server this is, and a nod to the time of day. */
|
|
export function Greeting() {
|
|
const { t } = useTranslation();
|
|
const accounts = useAuthStore((s) => s.accounts);
|
|
const activeAccountId = useAuthStore((s) => s.activeAccountId);
|
|
const full = (activeAccountId && accounts[activeAccountId]?.name) || '';
|
|
const name = full.split('@')[0];
|
|
const part = partOfDay(new Date().getHours());
|
|
const hello =
|
|
part === 'morning'
|
|
? t('greeting.morning', 'Good morning')
|
|
: part === 'afternoon'
|
|
? t('greeting.afternoon', 'Good afternoon')
|
|
: t('greeting.evening', 'Good evening');
|
|
return (
|
|
<div className="flex items-center gap-4 rounded-2xl border bg-gradient-to-br from-accent/70 via-card to-card px-5 py-4 shadow-soft">
|
|
<img src={inbuxaMark} alt="" className="h-12 w-auto drop-shadow-sm" />
|
|
<div className="min-w-0">
|
|
<h1 className="truncate text-2xl font-semibold">
|
|
{hello}
|
|
{name && `, ${name}`}
|
|
</h1>
|
|
<p className="text-sm text-muted-foreground">
|
|
{t('greeting.subtitle', "Here's how your mail server is doing.")}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|