1
mirror of https://github.com/jakejarvis/hoot.git synced 2025-10-18 20:14:25 -04:00

Refactor domain sections to use isLoading and isError props for improved loading state management

This commit is contained in:
2025-09-28 22:28:21 -04:00
parent 8014d4e61f
commit fccf8eb7f5
9 changed files with 54 additions and 46 deletions

View File

@@ -47,12 +47,12 @@ export default async function DomainPage({
return (
<div className="container mx-auto max-w-4xl px-4 py-6">
{/* Dynamic island: runs on server, reads cookie, captures analytics */}
<DomainSsrAnalytics
domain={normalized}
canonicalized={normalized !== decoded}
/>
<Suspense fallback={<DomainReportFallback />}>
{/* Dynamic island: runs on server, reads cookie, captures analytics */}
<DomainSsrAnalytics
domain={normalized}
canonicalized={normalized !== decoded}
/>
<DomainReportView
domain={normalized}
initialRegistration={registration}

View File

@@ -28,7 +28,7 @@ export function DomainLoadingState() {
help={def.help}
icon={<Icon className="h-4 w-4" />}
accent={def.accent}
status="loading"
isLoading
/>
);
})}

View File

@@ -1,4 +1,4 @@
import { Loader2 } from "lucide-react";
import { Info, Loader2 } from "lucide-react";
import { Skeleton } from "@/components/ui/skeleton";
import { SECTION_DEFS, SECTION_ORDER } from "./sections/sections-meta";
@@ -19,7 +19,7 @@ export function DomainReportFallback() {
{/* Sections matching Section header visuals */}
<div className="space-y-4">
{SECTION_ORDER.map((key) => {
const { title, accent, Icon } = SECTION_DEFS[key];
const { title, accent, Icon, help } = SECTION_DEFS[key];
return (
<div
key={title}
@@ -34,13 +34,24 @@ export function DomainReportFallback() {
<div className="relative">
{/* Header row */}
<div className="px-5 py-4">
<div className="flex w-full items-center gap-3 text-left">
<div className="flex w-full items-center gap-3 text-left opacity-50">
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-foreground/10 text-foreground/80">
<Icon className="h-4 w-4" />
</div>
<div className="flex-1 min-w-0">
<div className="gap-2 flex items-center leading-none font-semibold">
<span className="text-base">{title}</span>
{help ? (
<span
role="img"
aria-label={`More info about ${title}`}
>
<Info
className="h-3.5 w-3.5 opacity-60"
aria-hidden
/>
</span>
) : null}
</div>
<div className="sr-only">Loading</div>
</div>

View File

@@ -24,7 +24,8 @@ export function Section({
help,
icon,
accent,
status,
isLoading,
isError,
headerRight,
children,
}: {
@@ -33,10 +34,16 @@ export function Section({
help?: string;
icon?: React.ReactNode;
accent?: "blue" | "purple" | "green" | "orange" | "pink";
status?: "loading" | "ready" | "error";
isLoading?: boolean;
isError?: boolean;
headerRight?: React.ReactNode;
children?: React.ReactNode;
}) {
const status: "loading" | "ready" | "error" = isError
? "error"
: isLoading
? "loading"
: "ready";
return (
<AccordionItem value={title} className="border-none group">
<Card

View File

@@ -6,7 +6,6 @@ import { ErrorWithRetry } from "@/components/domain/error-with-retry";
import { Favicon } from "@/components/domain/favicon";
import { KeyValue } from "@/components/domain/key-value";
import { Section } from "@/components/domain/section";
import { Skeletons } from "@/components/domain/skeletons";
import {
Tooltip,
TooltipContent,
@@ -19,7 +18,7 @@ import { SECTION_DEFS } from "./sections-meta";
export function CertificatesSection({
data,
isLoading: _isLoading,
isLoading,
isError,
onRetryAction,
}: {
@@ -36,9 +35,10 @@ export function CertificatesSection({
help={Def.help}
icon={<Def.Icon className="h-4 w-4" />}
accent={Def.accent}
status={isError ? "error" : data ? "ready" : "loading"}
isError={isError}
isLoading={isLoading}
>
{data ? (
{isLoading ? null : data ? (
data.map((c, idx) => (
<Fragment key={`cert-${c.subject}-${c.validFrom}-${c.validTo}`}>
<div className="relative overflow-hidden rounded-2xl border bg-background/40 backdrop-blur supports-[backdrop-filter]:bg-background/40 p-3 shadow-[inset_0_1px_0_rgba(255,255,255,0.08)] border-black/10 dark:border-white/10">
@@ -119,9 +119,7 @@ export function CertificatesSection({
message="Failed to load certificates."
onRetry={onRetryAction}
/>
) : (
<Skeletons count={1} />
)}
) : null}
</Section>
);
}

View File

@@ -4,7 +4,6 @@ import { DnsGroup } from "@/components/domain/dns-group";
import { DnsRecordList } from "@/components/domain/dns-record-list";
import { ErrorWithRetry } from "@/components/domain/error-with-retry";
import { Section } from "@/components/domain/section";
import { Skeletons } from "@/components/domain/skeletons";
import { Checkbox } from "@/components/ui/checkbox";
import { Label } from "@/components/ui/label";
import type { DnsRecord } from "@/server/services/dns";
@@ -12,7 +11,7 @@ import { SECTION_DEFS } from "./sections-meta";
export function DnsRecordsSection({
records,
isLoading: _isLoading,
isLoading,
isError,
onRetryAction,
showTtls,
@@ -61,9 +60,10 @@ export function DnsRecordsSection({
icon={<Def.Icon className="h-4 w-4" />}
accent={Def.accent}
headerRight={headerRight}
status={isError ? "error" : records ? "ready" : "loading"}
isError={isError}
isLoading={isLoading}
>
{records ? (
{isLoading ? null : records ? (
<div className="space-y-4">
<DnsGroup
title="A Records"
@@ -103,9 +103,7 @@ export function DnsRecordsSection({
</div>
) : isError ? (
<ErrorWithRetry message="Failed to load DNS." onRetry={onRetryAction} />
) : (
<Skeletons count={6} />
)}
) : null}
</Section>
);
}

View File

@@ -3,13 +3,12 @@
import { ErrorWithRetry } from "@/components/domain/error-with-retry";
import { KeyValue } from "@/components/domain/key-value";
import { Section } from "@/components/domain/section";
import { Skeletons } from "@/components/domain/skeletons";
import type { HttpHeader } from "@/server/services/headers";
import { SECTION_DEFS } from "./sections-meta";
export function HeadersSection({
data,
isLoading: _isLoading,
isLoading,
isError,
onRetryAction,
}: {
@@ -26,9 +25,10 @@ export function HeadersSection({
help={Def.help}
icon={<Def.Icon className="h-4 w-4" />}
accent={Def.accent}
status={isError ? "error" : data ? "ready" : "loading"}
isError={isError}
isLoading={isLoading}
>
{data ? (
{isLoading ? null : data ? (
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
{(() => {
const important = new Set([
@@ -58,9 +58,7 @@ export function HeadersSection({
message="Failed to load headers."
onRetry={onRetryAction}
/>
) : (
<Skeletons count={4} />
)}
) : null}
</Section>
);
}

View File

@@ -5,7 +5,6 @@ import { ErrorWithRetry } from "@/components/domain/error-with-retry";
import { Favicon } from "@/components/domain/favicon";
import { KeyValue } from "@/components/domain/key-value";
import { Section } from "@/components/domain/section";
import { Skeletons } from "@/components/domain/skeletons";
import type { HostingInfo } from "@/server/services/hosting";
import { SECTION_DEFS } from "./sections-meta";
@@ -21,7 +20,7 @@ const HostingMap = dynamic(
export function HostingEmailSection({
data,
isLoading: _isLoading,
isLoading,
isError,
onRetryAction,
}: {
@@ -38,9 +37,10 @@ export function HostingEmailSection({
help={Def.help}
icon={<Def.Icon className="h-4 w-4" />}
accent={Def.accent}
status={isError ? "error" : data ? "ready" : "loading"}
isError={isError}
isLoading={isLoading}
>
{data ? (
{isLoading ? null : data ? (
<>
<KeyValue
label="DNS"
@@ -100,9 +100,7 @@ export function HostingEmailSection({
message="Failed to load hosting details."
onRetry={onRetryAction}
/>
) : (
<Skeletons count={3} />
)}
) : null}
</Section>
);
}

View File

@@ -5,7 +5,6 @@ import { ErrorWithRetry } from "@/components/domain/error-with-retry";
import { Favicon } from "@/components/domain/favicon";
import { KeyValue } from "@/components/domain/key-value";
import { Section } from "@/components/domain/section";
import { Skeletons } from "@/components/domain/skeletons";
import { Badge } from "@/components/ui/badge";
import { formatDate, formatRegistrant } from "@/lib/format";
import type { RegistrationWithProvider } from "@/server/services/registration";
@@ -16,7 +15,7 @@ type RegistrantView = { organization: string; country: string; state?: string };
export function RegistrationSection({
data,
isLoading: _isLoading,
isLoading,
isError,
onRetryAction,
}: {
@@ -37,9 +36,10 @@ export function RegistrationSection({
help={Def.help}
icon={<Def.Icon className="h-4 w-4" />}
accent={Def.accent}
status={isError ? "error" : data ? "ready" : "loading"}
isError={isError}
isLoading={isLoading}
>
{data ? (
{isLoading ? null : data ? (
<>
<KeyValue
label="Registrar"
@@ -89,9 +89,7 @@ export function RegistrationSection({
message="Failed to load WHOIS."
onRetry={onRetryAction}
/>
) : (
<Skeletons count={4} />
)}
) : null}
</Section>
);
}