Skip to content

Commit cd32f6d

Browse files
committed
improve logs page loading state / error handling
1 parent 0d3b0cc commit cd32f6d

File tree

1 file changed

+50
-32
lines changed
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/settings/logs/[logId]

1 file changed

+50
-32
lines changed

apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/settings/logs/[logId]/page-client.tsx

Lines changed: 50 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { CopyButton, StatusBadge, TimestampTooltip } from "@dub/ui";
1111
import { ChevronRight, StackY3 } from "@dub/ui/icons";
1212
import { fetcher, formatDateTime } from "@dub/utils";
1313
import Link from "next/link";
14-
import { useParams } from "next/navigation";
14+
import { redirect, useParams } from "next/navigation";
1515
import { useEffect, useState } from "react";
1616
import type { HighlighterCore } from "shiki";
1717
import useSWR from "swr";
@@ -29,6 +29,10 @@ export function LogDetailPageClient() {
2929
fetcher,
3030
);
3131

32+
if (error?.status === 404) {
33+
redirect(`/${slug}/settings/logs`);
34+
}
35+
3236
return (
3337
<PageContent
3438
title={
@@ -72,10 +76,28 @@ export function LogDetailPageClient() {
7276
);
7377
}
7478

79+
function LogDetailSkeleton() {
80+
return (
81+
<div className="flex flex-col gap-6 lg:flex-row">
82+
<div className="order-last min-w-0 flex-1 lg:order-first">
83+
<div className="flex flex-col gap-6">
84+
<div className="h-48 animate-pulse rounded-xl bg-neutral-200" />
85+
<div className="h-48 animate-pulse rounded-xl bg-neutral-200" />
86+
</div>
87+
</div>
88+
<div className="order-first w-full shrink-0 lg:order-last lg:w-[360px]">
89+
<div className="h-64 animate-pulse rounded-xl bg-neutral-200" />
90+
</div>
91+
</div>
92+
);
93+
}
94+
7595
function LogDetailContent({ log }: { log: EnrichedApiLog }) {
7696
const [highlighter, setHighlighter] = useState<HighlighterCore | null>(null);
77-
const [highlightedRequest, setHighlightedRequest] = useState("");
78-
const [highlightedResponse, setHighlightedResponse] = useState("");
97+
const [highlightedBodies, setHighlightedBodies] = useState<{
98+
request: string;
99+
response: string;
100+
} | null>(null);
79101

80102
useEffect(() => {
81103
import("shiki").then(({ createHighlighter }) => {
@@ -87,27 +109,37 @@ function LogDetailContent({ log }: { log: EnrichedApiLog }) {
87109
}, []);
88110

89111
useEffect(() => {
90-
if (!highlighter) return;
112+
if (!highlighter) {
113+
setHighlightedBodies(null);
114+
return;
115+
}
91116

92-
const toHighlightedJson = (raw: string) => {
117+
const toJsonString = (raw: string) => {
93118
let value: unknown;
94119
try {
95120
value = JSON.parse(raw);
96121
} catch {
97122
value = raw;
98123
}
124+
return JSON.stringify(value, null, 2) ?? String(value);
125+
};
99126

100-
const jsonStr = JSON.stringify(value, null, 2) ?? String(value);
101-
return highlighter.codeToHtml(jsonStr, {
127+
setHighlightedBodies({
128+
request: highlighter.codeToHtml(toJsonString(log.request_body), {
102129
theme: "min-light",
103130
lang: "json",
104-
});
105-
};
106-
107-
setHighlightedRequest(toHighlightedJson(log.request_body));
108-
setHighlightedResponse(toHighlightedJson(log.response_body));
131+
}),
132+
response: highlighter.codeToHtml(toJsonString(log.response_body), {
133+
theme: "min-light",
134+
lang: "json",
135+
}),
136+
});
109137
}, [highlighter, log]);
110138

139+
if (!highlightedBodies) {
140+
return <LogDetailSkeleton />;
141+
}
142+
111143
const detailRows: Record<string, React.ReactNode> = {
112144
Path: (
113145
<span className="truncate font-mono" title={log.path}>
@@ -184,10 +216,10 @@ function LogDetailContent({ log }: { log: EnrichedApiLog }) {
184216
<h3 className="text-content-emphasis text-lg font-semibold">
185217
Response body
186218
</h3>
187-
{highlightedResponse ? (
219+
{highlightedBodies.response ? (
188220
<div
189221
className="shiki-wrapper max-h-[800px] overflow-auto rounded-xl border border-neutral-200 bg-white p-4 text-sm"
190-
dangerouslySetInnerHTML={{ __html: highlightedResponse }}
222+
dangerouslySetInnerHTML={{ __html: highlightedBodies.response }}
191223
/>
192224
) : (
193225
<div className="rounded-xl border border-neutral-200 bg-white p-4 font-mono text-xs text-neutral-500">
@@ -200,10 +232,12 @@ function LogDetailContent({ log }: { log: EnrichedApiLog }) {
200232
<h3 className="text-content-emphasis text-lg font-semibold">
201233
Request body
202234
</h3>
203-
{highlightedRequest ? (
235+
{highlightedBodies.request ? (
204236
<div
205237
className="shiki-wrapper max-h-[800px] overflow-auto rounded-xl border border-neutral-200 bg-white p-4 text-sm"
206-
dangerouslySetInnerHTML={{ __html: highlightedRequest }}
238+
dangerouslySetInnerHTML={{
239+
__html: highlightedBodies.request,
240+
}}
207241
/>
208242
) : (
209243
<div className="rounded-xl border border-neutral-200 bg-white p-4 font-mono text-xs text-neutral-500">
@@ -241,19 +275,3 @@ function LogDetailContent({ log }: { log: EnrichedApiLog }) {
241275
</div>
242276
);
243277
}
244-
245-
function LogDetailSkeleton() {
246-
return (
247-
<div className="flex flex-col gap-6 lg:flex-row">
248-
<div className="order-last min-w-0 flex-1 lg:order-first">
249-
<div className="flex flex-col gap-6">
250-
<div className="h-48 animate-pulse rounded-xl bg-neutral-200" />
251-
<div className="h-48 animate-pulse rounded-xl bg-neutral-200" />
252-
</div>
253-
</div>
254-
<div className="order-first w-full shrink-0 lg:order-last lg:w-[360px]">
255-
<div className="h-64 animate-pulse rounded-xl bg-neutral-200" />
256-
</div>
257-
</div>
258-
);
259-
}

0 commit comments

Comments
 (0)