page.tsx115 lines · main
1import Image from 'next/image';
2
3export const metadata = { title: 'down for maintenance · briven' };
4// Always render live state — never a cached splash that shows the wrong
5// end time after the window moves.
6export const dynamic = 'force-dynamic';
7
8/** The public maintenance contract from apps/api. */
9interface MaintenanceStatus {
10 active: boolean;
11 scheduled: boolean;
12 upcoming: boolean;
13 startsAt: string | null;
14 endsAt: string | null;
15 message: string | null;
16}
17
18/**
19 * Fetch the maintenance window straight from the public api. Self-contained:
20 * no cookies, no gated data, so it renders even while the rest of the site is
21 * gated behind the maintenance rewrite. Fails soft to nulls so the page still
22 * shows the base "down for maintenance" message if the api is unreachable.
23 */
24async function getMaintenance(): Promise<MaintenanceStatus | null> {
25 const origin = process.env.NEXT_PUBLIC_BRIVEN_API_ORIGIN ?? '';
26 if (!origin) return null;
27 try {
28 const res = await fetch(`${origin}/v1/status/maintenance`, {
29 cache: 'no-store',
30 headers: { accept: 'application/json' },
31 });
32 if (!res.ok) return null;
33 return (await res.json()) as MaintenanceStatus;
34 } catch {
35 return null;
36 }
37}
38
39/** "Wed 3 Jul, 14:30" — a friendly local time the visitor can read. */
40function formatWindowEnd(iso: string): string {
41 const d = new Date(iso);
42 if (Number.isNaN(d.getTime())) return '';
43 return new Intl.DateTimeFormat('en-GB', {
44 weekday: 'short',
45 day: 'numeric',
46 month: 'short',
47 hour: '2-digit',
48 minute: '2-digit',
49 }).format(d);
50}
51
52export default async function MaintenancePage() {
53 const status = await getMaintenance();
54 const endLabel = status?.endsAt ? formatWindowEnd(status.endsAt) : '';
55 const customMessage = status?.message?.trim() || '';
56
57 return (
58 <main className="relative flex min-h-dvh flex-col items-center justify-center bg-[var(--color-bg)] px-6 py-16 text-[var(--color-text)]">
59 <div className="flex w-full max-w-lg flex-col items-center gap-8 text-center">
60 <div className="flex items-center gap-2" aria-label="briven">
61 <Image src="/icon.svg" alt="" width={36} height={36} priority className="opacity-95" />
62 <span className="font-mono tracking-tight text-[var(--color-text)] text-[var(--text-small)]">
63 briven
64 </span>
65 </div>
66
67 <div className="flex flex-col gap-4">
68 <span className="inline-flex items-center justify-center gap-2 self-center rounded-[var(--radius-full)] border border-[var(--color-border-primary)] bg-[var(--color-primary-subtle)] px-3 py-1 font-mono uppercase tracking-wider text-[var(--color-primary)] text-[10px]">
69 <span className="relative flex h-1.5 w-1.5">
70 <span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-[var(--color-primary)] opacity-60" />
71 <span className="relative inline-flex h-1.5 w-1.5 rounded-full bg-[var(--color-primary)]" />
72 </span>
73 maintenance in progress
74 </span>
75
76 <h1 className="font-sans font-medium leading-[1.1] tracking-[-0.03em] text-[var(--color-text)] text-[var(--text-display-3)]">
77 down for maintenance
78 </h1>
79
80 <p className="leading-[1.6] text-[var(--color-text-muted)] text-[var(--text-body)]">
81 we&apos;re making briven better behind the scenes. your data is safe — we&apos;ll be back
82 shortly. thanks for your patience.
83 </p>
84 </div>
85
86 {endLabel ? (
87 <p className="font-mono text-[var(--color-text)] text-[var(--text-small)]">
88 back around{' '}
89 <span className="text-[var(--color-primary)]">{endLabel}</span>
90 </p>
91 ) : null}
92
93 {customMessage ? (
94 <p className="max-w-prose rounded-[var(--radius-md)] border border-[var(--color-border-subtle)] bg-[var(--color-surface)] px-4 py-3 font-mono leading-relaxed text-[var(--color-text-muted)] text-[var(--text-xs)]">
95 {customMessage}
96 </p>
97 ) : null}
98
99 <p className="font-mono text-[var(--color-text-subtle)] text-[var(--text-xs)]">
100 status updates:{' '}
101 <a
102 href="https://docs.briven.tech/status"
103 className="text-[var(--color-text-link)] underline-offset-2 hover:underline"
104 >
105 docs.briven.tech/status
106 </a>
107 </p>
108 </div>
109
110 <footer className="mt-12 font-mono text-[10px] text-[var(--color-text-subtle)]">
111 built with <span className="text-[#e8344a]">♥</span> in Flanders · flndrn Limited
112 </footer>
113 </main>
114 );
115}