feat(client): elevate auth pages with a two-column brand layout

- BaseAuthPage now renders a left form column and a right brand panel with eyebrow / tagline / description copy and a chess-board pattern over the brand green gradient
- Move the language selector and theme toggle to the page's top-right inside the form column so they read clearly in light and dark
- Every auth page (login, register, recovery, set new password) inherits the new layout
- Add brandPanel translations across all 16 locales; phrase the tagline as "your servers" instead of "stack" so it translates cleanly
This commit is contained in:
gorkem-bwl authored and Alex Holliday committed 2026-04-27 10:52:24 -07:00
1 parent 880b424622
commit 6b71daa88e
17 files changed
+225 -34

No files matched your search

@@ -13,6 +13,7 @@ import { Breadcrumb } from "@/Components/design-elements/Breadcrumb";
import { PageHeader } from "@/Components/design-elements/PageHeader";
import CircularProgress from "@mui/material/CircularProgress";
import { HeaderAuthControls } from "@/Pages/Auth/components/HeaderAuthControls";
import { LanguageSelector, SwitchTheme } from "@/Components/inputs";
import type { StackProps } from "@mui/material/Stack";
import { useTheme, alpha } from "@mui/material/styles";
@@ -261,49 +262,159 @@ export const BaseAuthPage = ({
title,
subtitle,
children,
...props
component,
onSubmit,
}: BaseAuthPageProps) => {
const theme = useTheme();
const { t } = useTranslation();
return (
<BasePage
breadcrumbOverride={[]}
gap={theme.spacing(LAYOUT.MD)}
alignItems={"center"}
justifyContent={"center"}
<Stack
direction={{ xs: "column", md: "row" }}
minHeight="100vh"
position={"relative"}
{...props}
width="100%"
position="relative"
sx={{ backgroundColor: theme.palette.background.default }}
>
<HeaderAuthControls
hideLogo
py={theme.spacing(LAYOUT.XS)}
position={"absolute"}
top={0}
left={0}
/>
<Box width={{ xs: 60, sm: 70, md: 80 }}>
<Logo
width={"100%"}
height={"100%"}
/>
</Box>
<Stack alignItems={"center"}>
<Typography
variant="h1"
mb={theme.spacing(SPACING.LG)}
<Stack
flex={1}
alignItems="center"
justifyContent="center"
position="relative"
px={theme.spacing(LAYOUT.MD)}
py={{ xs: theme.spacing(20), md: theme.spacing(12) }}
>
<Stack
direction="row"
spacing={theme.spacing(2)}
alignItems="center"
sx={{
position: "absolute",
top: theme.spacing(LAYOUT.MD),
right: theme.spacing(LAYOUT.MD),
zIndex: 3,
}}
>
{title}
</Typography>
<Typography variant="h2">{subtitle}</Typography>
<LanguageSelector />
<SwitchTheme />
</Stack>
<Stack
component={component}
onSubmit={onSubmit}
gap={theme.spacing(LAYOUT.MD)}
width="100%"
maxWidth={360}
>
<Stack
direction="row"
alignItems="center"
gap={theme.spacing(3)}
mb={theme.spacing(2)}
>
<Box width={28}>
<Logo width="100%" />
</Box>
<Typography sx={{ fontWeight: 500, letterSpacing: "-0.01em", fontSize: 16 }}>
{t("common.appName")}
</Typography>
</Stack>
<Stack gap={theme.spacing(2)}>
<Typography
sx={{
fontSize: 26,
fontWeight: 400,
lineHeight: 1.15,
letterSpacing: "-0.02em",
color: theme.palette.text.primary,
}}
>
{title}
</Typography>
<Typography
sx={{
fontSize: 14,
color: theme.palette.text.secondary,
}}
>
{subtitle}
</Typography>
</Stack>
{children}
</Stack>
</Stack>
<Stack
gap={theme.spacing(LAYOUT.MD)}
width="100%"
maxWidth={400}
px={theme.spacing(LAYOUT.MD)}
flex={1}
justifyContent="space-between"
display={{ xs: "none", md: "flex" }}
sx={{
background: "linear-gradient(135deg, #0c4d3d 0%, #155a48 60%, #0f5d4a 100%)",
color: "#fff",
p: theme.spacing(28),
position: "relative",
overflow: "hidden",
"&::after": {
content: '""',
position: "absolute",
inset: 0,
backgroundImage: `
linear-gradient(45deg, rgba(255,255,255,0.04) 25%, transparent 25%),
linear-gradient(-45deg, rgba(255,255,255,0.04) 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, rgba(255,255,255,0.04) 75%),
linear-gradient(-45deg, transparent 75%, rgba(255,255,255,0.04) 75%)
`,
backgroundSize: "80px 80px",
backgroundPosition: "0 0, 0 40px, 40px -40px, -40px 0px",
maskImage: "linear-gradient(135deg, transparent 0%, black 70%)",
WebkitMaskImage: "linear-gradient(135deg, transparent 0%, black 70%)",
pointerEvents: "none",
},
}}
>
{children}
<div />
<Stack sx={{ position: "relative", zIndex: 1, gap: theme.spacing(4) }}>
<Typography
sx={{
fontSize: 13,
fontWeight: 600,
textTransform: "uppercase",
letterSpacing: "0.12em",
color: "rgba(255,255,255,0.75)",
}}
>
{t("pages.auth.brandPanel.eyebrow")}
</Typography>
<Typography
sx={{
fontSize: 44,
fontWeight: 400,
lineHeight: 1.1,
letterSpacing: "-0.02em",
maxWidth: 460,
}}
>
{t("pages.auth.brandPanel.tagline")}
</Typography>
<Typography
sx={{
fontSize: 17,
lineHeight: 1.5,
color: "rgba(255,255,255,0.75)",
maxWidth: 460,
}}
>
{t("pages.auth.brandPanel.description")}
</Typography>
</Stack>
<Typography
sx={{
position: "relative",
zIndex: 1,
color: "rgba(255,255,255,0.5)",
fontSize: 12,
}}
>
v{__APP_VERSION__}
</Typography>
</Stack>
</BasePage>
</Stack>
);
};
+5
View File
@@ -298,6 +298,11 @@
}
},
"auth": {
"brandPanel": {
"eyebrow": "المراقبة المفتوحة المصدر للبنية التحتية",
"tagline": "راقب كل خطوة تقوم بها خوادمك.",
"description": "تتبع حالة التشغيل وأوقات الاستجابة والحوادث في الوقت الفعلي - ذاتية الاستضافة وتبقى لك."
},
"common": {
"passwordRules": {
"length": {
+5
View File
@@ -298,6 +298,11 @@
}
},
"auth": {
"brandPanel": {
"eyebrow": "Open-source monitoring infrastruktury",
"tagline": "Sledujte každý krok vašich serverů.",
"description": "Monitorování dostupnosti, doby odezvy a incidentů v reálném čase - self-hosted a vaše."
},
"common": {
"passwordRules": {
"length": {
+5
View File
@@ -298,6 +298,11 @@
}
},
"auth": {
"brandPanel": {
"eyebrow": "Open-Source-Infrastruktur-Monitoring",
"tagline": "Beobachten Sie jeden Zug Ihrer Server.",
"description": "Echtzeit-Überwachung von Verfügbarkeit, Antwortzeiten und Vorfällen - selbst gehostet und in Ihrer Hand."
},
"common": {
"passwordRules": {
"length": {
+5
View File
@@ -298,6 +298,11 @@
}
},
"auth": {
"brandPanel": {
"eyebrow": "Open-source infrastructure monitoring",
"tagline": "Watch every move your servers make.",
"description": "Real-time uptime, response time and incident tracking - self-hosted and yours to keep."
},
"common": {
"passwordRules": {
"length": {
+5
View File
@@ -298,6 +298,11 @@
}
},
"auth": {
"brandPanel": {
"eyebrow": "Monitorización de infraestructura de código abierto",
"tagline": "Vigila cada movimiento de tus servidores.",
"description": "Monitorización en tiempo real de disponibilidad, tiempo de respuesta e incidentes - autoalojada y tuya."
},
"common": {
"passwordRules": {
"length": {
+5
View File
@@ -298,6 +298,11 @@
}
},
"auth": {
"brandPanel": {
"eyebrow": "Avoimen lähdekoodin infrastruktuurin valvonta",
"tagline": "Seuraa palvelimiesi jokaista liikettä.",
"description": "Reaaliaikainen käytettävyyden, vasteajan ja häiriöiden valvonta - itse isännöity ja sinun."
},
"common": {
"passwordRules": {
"length": {
+5
View File
@@ -298,6 +298,11 @@
}
},
"auth": {
"brandPanel": {
"eyebrow": "Surveillance d'infrastructure open source",
"tagline": "Surveillez chaque mouvement de vos serveurs.",
"description": "Suivi en temps réel de la disponibilité, du temps de réponse et des incidents - auto-hébergé et à vous."
},
"common": {
"passwordRules": {
"length": {
+5
View File
@@ -298,6 +298,11 @@
}
},
"auth": {
"brandPanel": {
"eyebrow": "オープンソースのインフラ監視",
"tagline": "サーバーのすべての動きを見守ろう。",
"description": "稼働状況、応答時間、インシデントをリアルタイムで追跡 - セルフホスト型で完全にあなたのもの。"
},
"common": {
"passwordRules": {
"length": {
+5
View File
@@ -298,6 +298,11 @@
}
},
"auth": {
"brandPanel": {
"eyebrow": "Monitoramento de infraestrutura open-source",
"tagline": "Acompanhe cada passo dos seus servidores.",
"description": "Monitoramento em tempo real de disponibilidade, tempo de resposta e incidentes - auto-hospedado e seu."
},
"common": {
"passwordRules": {
"length": {
+5
View File
@@ -298,6 +298,11 @@
}
},
"auth": {
"brandPanel": {
"eyebrow": "Мониторинг инфраструктуры с открытым исходным кодом",
"tagline": "Следите за каждым шагом ваших серверов.",
"description": "Мониторинг доступности, времени отклика и инцидентов в реальном времени - self-hosted и принадлежит вам."
},
"common": {
"passwordRules": {
"length": {
+5
View File
@@ -298,6 +298,11 @@
}
},
"auth": {
"brandPanel": {
"eyebrow": "การมอนิเตอร์โครงสร้างพื้นฐานโอเพนซอร์ส",
"tagline": "คอยจับตาดูทุกการเคลื่อนไหวของเซิร์ฟเวอร์ของคุณ",
"description": "ติดตามสถานะ เวลาตอบสนอง และเหตุการณ์ผิดปกติแบบเรียลไทม์ - โฮสต์เองและเป็นของคุณ"
},
"common": {
"passwordRules": {
"length": {
+5
View File
@@ -298,6 +298,11 @@
}
},
"auth": {
"brandPanel": {
"eyebrow": "Açık kaynaklı altyapı izleme",
"tagline": "Sunucularınızın her hareketini izleyin.",
"description": "Sistemlerinizin çalışma süresini, yanıt hızını ve yaşanan kesintileri tek bir panelden anlık takip edin. Kendi sunucunuzda çalışır, verileriniz sizde kalır."
},
"common": {
"passwordRules": {
"length": {
+5
View File
@@ -298,6 +298,11 @@
}
},
"auth": {
"brandPanel": {
"eyebrow": "Моніторинг інфраструктури з відкритим кодом",
"tagline": "Слідкуйте за кожним кроком ваших серверів.",
"description": "Моніторинг доступності, часу відгуку та інцидентів у реальному часі - self-hosted і належить вам."
},
"common": {
"passwordRules": {
"length": {
+5
View File
@@ -298,6 +298,11 @@
}
},
"auth": {
"brandPanel": {
"eyebrow": "Giám sát hạ tầng mã nguồn mở",
"tagline": "Theo dõi mọi chuyển động của các máy chủ của bạn.",
"description": "Theo dõi thời gian hoạt động, thời gian phản hồi và sự cố theo thời gian thực - tự lưu trữ và thuộc về bạn."
},
"common": {
"passwordRules": {
"length": {
+5
View File
@@ -298,6 +298,11 @@
}
},
"auth": {
"brandPanel": {
"eyebrow": "开源基础设施监控",
"tagline": "关注您服务器的每一步动作。",
"description": "实时跟踪运行状态、响应时间和事件 - 自托管,完全属于您。"
},
"common": {
"passwordRules": {
"length": {
+5
View File
@@ -298,6 +298,11 @@
}
},
"auth": {
"brandPanel": {
"eyebrow": "開源基礎設施監控",
"tagline": "關注您伺服器的每一步動作。",
"description": "即時追蹤運行狀態、回應時間和事件 - 自主託管,完全屬於您。"
},
"common": {
"passwordRules": {
"length": {