mirror of
https://github.com/dcarrillo/atalaya.git
synced 2026-04-18 02:24:05 +00:00
feat: add custom banner to status page (#4)
This commit is contained in:
@@ -7,9 +7,13 @@ interface Props {
|
||||
};
|
||||
lastUpdated: number;
|
||||
title: string;
|
||||
banner?: {
|
||||
url?: string;
|
||||
link?: string;
|
||||
};
|
||||
}
|
||||
|
||||
const { summary, lastUpdated, title } = Astro.props;
|
||||
const { summary, lastUpdated, title, banner } = Astro.props;
|
||||
|
||||
function formatAbsoluteTime(unixTimestamp: number): string {
|
||||
const date = new Date(unixTimestamp * 1000);
|
||||
@@ -28,7 +32,19 @@ const absoluteTime = formatAbsoluteTime(lastUpdated);
|
||||
|
||||
<header class="header">
|
||||
<div class="header-top">
|
||||
<h1>{title}</h1>
|
||||
{banner?.url ? (
|
||||
<div class="banner-container">
|
||||
{banner.link ? (
|
||||
<a href={banner.link} class="banner-link" aria-label={title} target="_self">
|
||||
<img src={banner.url} alt={title} class="banner-image" />
|
||||
</a>
|
||||
) : (
|
||||
<img src={banner.url} alt={title} class="banner-image" />
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<h1>{title}</h1>
|
||||
)}
|
||||
<button class="theme-toggle" aria-label="Toggle theme" title="Toggle light/dark theme">
|
||||
<svg class="theme-icon sun" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="5"/>
|
||||
@@ -156,6 +172,29 @@ const absoluteTime = formatAbsoluteTime(lastUpdated);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.banner-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.banner-link {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
transition: opacity var(--transition-normal);
|
||||
}
|
||||
|
||||
.banner-link:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.banner-image {
|
||||
max-height: 60px;
|
||||
max-width: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.theme-icon {
|
||||
transition: opacity var(--transition-normal), transform var(--transition-normal);
|
||||
}
|
||||
@@ -275,6 +314,10 @@ const absoluteTime = formatAbsoluteTime(lastUpdated);
|
||||
font-size: var(--text-2xl);
|
||||
}
|
||||
|
||||
.banner-image {
|
||||
max-height: 50px;
|
||||
}
|
||||
|
||||
.theme-toggle {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
@@ -302,6 +345,10 @@ const absoluteTime = formatAbsoluteTime(lastUpdated);
|
||||
font-size: var(--text-xl);
|
||||
}
|
||||
|
||||
.banner-image {
|
||||
max-height: 40px;
|
||||
}
|
||||
|
||||
.summary-counts {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
@@ -10,6 +10,7 @@ import { env } from 'cloudflare:workers';
|
||||
|
||||
let data: Awaited<ReturnType<typeof getStatusApiData>> | null = null;
|
||||
let error: Error | null = null;
|
||||
let banner: { url: string; link?: string } | undefined = undefined;
|
||||
|
||||
try {
|
||||
// TypeScript doesn't know about MONITORS_CONFIG in cloudflare:workers env
|
||||
@@ -21,6 +22,10 @@ try {
|
||||
const configYaml = interpolateSecrets(monitorsConfig, envAny);
|
||||
const config = parseConfig(configYaml);
|
||||
data = await getStatusApiData(env.DB, config);
|
||||
|
||||
const bannerUrl = envAny.STATUS_BANNER_URL;
|
||||
const bannerLink = envAny.STATUS_BANNER_LINK;
|
||||
banner = bannerUrl ? { url: bannerUrl, link: bannerLink } : undefined;
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch status data:', err);
|
||||
error = err as Error;
|
||||
@@ -55,7 +60,7 @@ const sortedMonitors = data ? [...data.monitors].sort(
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<Header summary={data!.summary} lastUpdated={data!.lastUpdated} title={data!.title} />
|
||||
<Header summary={data!.summary} lastUpdated={data!.lastUpdated} title={data!.title} banner={banner} />
|
||||
<div class="monitors">
|
||||
{sortedMonitors.map(monitor => <MonitorCard monitor={monitor} />)}
|
||||
</div>
|
||||
@@ -80,7 +85,7 @@ const sortedMonitors = data ? [...data.monitors].sort(
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image:
|
||||
background-image:
|
||||
linear-gradient(var(--border-subtle) 1px, transparent 1px),
|
||||
linear-gradient(90deg, var(--border-subtle) 1px, transparent 1px);
|
||||
background-size: 40px 40px;
|
||||
@@ -90,7 +95,7 @@ const sortedMonitors = data ? [...data.monitors].sort(
|
||||
}
|
||||
|
||||
[data-theme='dark'] .container::before {
|
||||
background-image:
|
||||
background-image:
|
||||
linear-gradient(var(--border) 1px, transparent 1px),
|
||||
linear-gradient(90deg, var(--border) 1px, transparent 1px);
|
||||
opacity: 0.1;
|
||||
@@ -106,7 +111,7 @@ const sortedMonitors = data ? [...data.monitors].sort(
|
||||
.container {
|
||||
padding: var(--space-16) var(--space-12) var(--space-20);
|
||||
}
|
||||
|
||||
|
||||
.monitors {
|
||||
gap: var(--space-5);
|
||||
}
|
||||
@@ -201,18 +206,18 @@ const sortedMonitors = data ? [...data.monitors].sort(
|
||||
.container {
|
||||
padding: var(--space-5) var(--space-4);
|
||||
}
|
||||
|
||||
|
||||
.error-state,
|
||||
.loading-state,
|
||||
.empty-state {
|
||||
padding: var(--space-12) var(--space-4);
|
||||
}
|
||||
|
||||
|
||||
.error-icon,
|
||||
.empty-icon {
|
||||
font-size: 2.25rem;
|
||||
}
|
||||
|
||||
|
||||
.error-state h2,
|
||||
.empty-state h2 {
|
||||
font-size: var(--text-xl);
|
||||
|
||||
Reference in New Issue
Block a user