129 lines
3.7 KiB
TypeScript
129 lines
3.7 KiB
TypeScript
import * as stylex from "@stylexjs/stylex";
|
|
import { formatTime } from "@/lib/format";
|
|
import type { SourceStatus } from "@/lib/types";
|
|
import { styles as shared } from "@/ui/styles";
|
|
import { colors } from "@/ui/tokens.stylex";
|
|
|
|
function formatAttempt(unixSeconds: number): string {
|
|
return unixSeconds === 0 ? "never" : formatTime(unixSeconds);
|
|
}
|
|
|
|
interface SourceStatusSectionProps {
|
|
sources: SourceStatus[] | null;
|
|
namesById: ReadonlyMap<number, string>;
|
|
}
|
|
|
|
const styles = stylex.create({
|
|
section: {
|
|
marginTop: "2rem",
|
|
},
|
|
heading: {
|
|
fontSize: "1.125rem",
|
|
lineHeight: "1.75rem",
|
|
fontWeight: 500,
|
|
},
|
|
note: {
|
|
marginTop: "0.5rem",
|
|
color: colors.textMuted,
|
|
},
|
|
tableWrap: {
|
|
marginTop: "0.5rem",
|
|
overflowX: "auto",
|
|
},
|
|
table: {
|
|
width: "100%",
|
|
minWidth: "max-content",
|
|
borderCollapse: "collapse",
|
|
fontSize: "0.875rem",
|
|
lineHeight: "1.25rem",
|
|
},
|
|
name: {
|
|
fontWeight: 500,
|
|
},
|
|
url: {
|
|
marginTop: "0.125rem",
|
|
display: "block",
|
|
maxWidth: "16rem",
|
|
overflow: "hidden",
|
|
textOverflow: "ellipsis",
|
|
whiteSpace: "nowrap",
|
|
fontSize: "0.75rem",
|
|
lineHeight: "1rem",
|
|
color: colors.textMuted,
|
|
},
|
|
/** Green has no token: a loaded source is the only success state in the app. */
|
|
loaded: {
|
|
color: {
|
|
default: "oklch(52.7% 0.154 150.069)",
|
|
"@media (prefers-color-scheme: dark)": "oklch(79.2% 0.209 151.711)",
|
|
},
|
|
},
|
|
failed: {
|
|
color: colors.danger,
|
|
},
|
|
absent: {
|
|
color: colors.textMuted,
|
|
},
|
|
});
|
|
|
|
export default function SourceStatusSection({ sources, namesById }: SourceStatusSectionProps) {
|
|
return (
|
|
<section {...stylex.props(styles.section)}>
|
|
<h2 {...stylex.props(styles.heading)}>Source status</h2>
|
|
{sources === null ? (
|
|
<p {...stylex.props(styles.note)}>
|
|
No status snapshot yet — run “Update now” to fetch status for every enabled source.
|
|
</p>
|
|
) : sources.length === 0 ? (
|
|
<p {...stylex.props(styles.note)}>The last update ran against no enabled sources.</p>
|
|
) : (
|
|
<div {...stylex.props(styles.tableWrap)}>
|
|
<table {...stylex.props(styles.table)}>
|
|
<thead>
|
|
<tr>
|
|
<th {...stylex.props(shared.th)}>Source</th>
|
|
<th {...stylex.props(shared.th)}>State</th>
|
|
<th {...stylex.props(shared.th)}>Last attempt</th>
|
|
<th {...stylex.props(shared.th)}>Last success</th>
|
|
<th {...stylex.props(shared.th)}>Domains</th>
|
|
<th {...stylex.props(shared.th)}>Wildcards</th>
|
|
<th {...stylex.props(shared.th)}>Skipped regex</th>
|
|
<th {...stylex.props(shared.th)}>Last error</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{sources.map((source) => (
|
|
<tr key={source.id}>
|
|
<td {...stylex.props(shared.td)}>
|
|
<span {...stylex.props(styles.name)}>
|
|
{namesById.get(source.id) ?? source.url}
|
|
</span>
|
|
<span {...stylex.props(styles.url)}>{source.url}</span>
|
|
</td>
|
|
<td {...stylex.props(shared.td)}>
|
|
<span {...stylex.props(source.loaded ? styles.loaded : styles.failed)}>
|
|
{source.state}
|
|
</span>
|
|
</td>
|
|
<td {...stylex.props(shared.td)}>{formatAttempt(source.last_attempt)}</td>
|
|
<td {...stylex.props(shared.td)}>{formatAttempt(source.last_success)}</td>
|
|
<td {...stylex.props(shared.td, shared.tabularNums)}>{source.domains}</td>
|
|
<td {...stylex.props(shared.td, shared.tabularNums)}>{source.wildcards}</td>
|
|
<td {...stylex.props(shared.td, shared.tabularNums)}>{source.skipped_regex}</td>
|
|
<td {...stylex.props(shared.td)}>
|
|
{source.last_error === "" ? (
|
|
<span {...stylex.props(styles.absent)}>—</span>
|
|
) : (
|
|
<span {...stylex.props(styles.failed)}>{source.last_error}</span>
|
|
)}
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
)}
|
|
</section>
|
|
);
|
|
}
|