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:
@@ -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}
|
||||
|
@@ -28,7 +28,7 @@ export function DomainLoadingState() {
|
||||
help={def.help}
|
||||
icon={<Icon className="h-4 w-4" />}
|
||||
accent={def.accent}
|
||||
status="loading"
|
||||
isLoading
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
@@ -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>
|
||||
|
@@ -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
|
||||
|
@@ -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>
|
||||
);
|
||||
}
|
||||
|
@@ -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>
|
||||
);
|
||||
}
|
||||
|
@@ -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>
|
||||
);
|
||||
}
|
||||
|
@@ -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>
|
||||
);
|
||||
}
|
||||
|
@@ -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>
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user