-
Notifications
You must be signed in to change notification settings - Fork 505
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
4344d3a
commit 42e481d
Showing
6 changed files
with
303 additions
and
258 deletions.
There are no files selected for viewing
46 changes: 46 additions & 0 deletions
46
apps/dashboard/app/(app)/logs/log-details/components/log-body.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import { Button } from "@/components/ui/button"; | ||
import { Card, CardContent } from "@/components/ui/card"; | ||
import { toast } from "@/components/ui/toaster"; | ||
import { Copy } from "lucide-react"; | ||
import { highlighter } from ".."; | ||
import type { Log } from "../../data"; | ||
import { getObjectsFromLogs } from "../../utils"; | ||
|
||
type Props = { | ||
log: Log; | ||
}; | ||
|
||
export const LogBody = ({ log }: Props) => { | ||
return ( | ||
<div className="p-2"> | ||
<Card className="rounded-[5px] relative"> | ||
<CardContent | ||
className="whitespace-pre-wrap text-[12px]" | ||
dangerouslySetInnerHTML={{ | ||
__html: highlighter.codeToHtml(getObjectsFromLogs(log), { | ||
lang: "json", | ||
themes: { | ||
dark: "github-dark", | ||
light: "github-light", | ||
}, | ||
mergeWhitespaces: true, | ||
}), | ||
}} | ||
/> | ||
<div className="absolute bottom-2 right-3"> | ||
<Button | ||
size="block" | ||
variant="primary" | ||
className="bg-background border-border text-current" | ||
onClick={() => { | ||
navigator.clipboard.writeText(getObjectsFromLogs(log)); | ||
toast.success("Copied to clipboard"); | ||
}} | ||
> | ||
<Copy className="w-4 h-4" /> | ||
</Button> | ||
</div> | ||
</Card> | ||
</div> | ||
); | ||
}; |
132 changes: 132 additions & 0 deletions
132
apps/dashboard/app/(app)/logs/log-details/components/log-footer.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
import { Badge } from "@/components/ui/badge"; | ||
import { Card, CardContent } from "@/components/ui/card"; | ||
import { cn } from "@/lib/utils"; | ||
import { format } from "date-fns"; | ||
import { highlighter } from ".."; | ||
import { RED_STATES, YELLOW_STATES } from "../../constants"; | ||
import type { Log } from "../../data"; | ||
import { getRequestHeader, getResponseBodyFieldOutcome } from "../../utils"; | ||
import { RequestResponseDetails } from "./request-response-details"; | ||
|
||
type Props = { | ||
log: Log; | ||
}; | ||
export const LogFooter = ({ log }: Props) => { | ||
return ( | ||
<RequestResponseDetails | ||
className="pl-3" | ||
fields={[ | ||
{ | ||
label: "Time", | ||
description: (content) => ( | ||
<span className="text-[13px] font-mono">{content}</span> | ||
), | ||
content: format(log.time, "MMM dd HH:mm:ss.SS"), | ||
tooltipContent: "Copy Time", | ||
tooltipSuccessMessage: "Time copied to clipboard", | ||
}, | ||
{ | ||
label: "Host", | ||
description: (content) => ( | ||
<span className="text-[13px] font-mono">{content}</span> | ||
), | ||
content: log.host, | ||
tooltipContent: "Copy Host", | ||
tooltipSuccessMessage: "Host copied to clipboard", | ||
}, | ||
{ | ||
label: "Request Path", | ||
description: (content) => ( | ||
<span className="text-[13px] font-mono">{content}</span> | ||
), | ||
content: log.path, | ||
tooltipContent: "Copy Request Path", | ||
tooltipSuccessMessage: "Request path copied to clipboard", | ||
}, | ||
{ | ||
label: "Request ID", | ||
description: (content) => ( | ||
<span className="text-[13px] font-mono">{content}</span> | ||
), | ||
content: log.request_id, | ||
tooltipContent: "Copy Request ID", | ||
tooltipSuccessMessage: "Request ID copied to clipboard", | ||
}, | ||
{ | ||
label: "Request User Agent", | ||
description: (content) => ( | ||
<span className="text-[13px] font-mono">{content}</span> | ||
), | ||
content: getRequestHeader(log, "user-agent") ?? "", | ||
tooltipContent: "Copy Request User Agent", | ||
tooltipSuccessMessage: "Request user agent copied to clipboard", | ||
}, | ||
{ | ||
label: "Outcome", | ||
description: (content) => { | ||
let contentCopy = content; | ||
if (contentCopy == null) { | ||
contentCopy = "VALID"; | ||
} | ||
return ( | ||
<Badge | ||
className={cn( | ||
{ | ||
"bg-amber-2 text-amber-11 hover:bg-amber-3": | ||
YELLOW_STATES.includes(contentCopy), | ||
"bg-red-2 text-red-11 hover:bg-red-3": | ||
RED_STATES.includes(contentCopy), | ||
}, | ||
"uppercase" | ||
)} | ||
> | ||
{content} | ||
</Badge> | ||
); | ||
}, | ||
content: getResponseBodyFieldOutcome(log, "code"), | ||
tooltipContent: "Copy Outcome", | ||
tooltipSuccessMessage: "Outcome copied to clipboard", | ||
}, | ||
{ | ||
label: "Permissions", | ||
description: (content) => ( | ||
<span className="text-[13px] font-mono flex gap-1"> | ||
{content.map((permission) => ( | ||
<Badge key={permission} variant="secondary"> | ||
{permission} | ||
</Badge> | ||
))} | ||
</span> | ||
), | ||
content: getResponseBodyFieldOutcome(log, "permissions"), | ||
tooltipContent: "Copy Permissions", | ||
tooltipSuccessMessage: "Permissions copied to clipboard", | ||
}, | ||
{ | ||
label: "Meta", | ||
description: (content) => ( | ||
<Card className="rounded-[5px]"> | ||
<CardContent | ||
className="whitespace-pre-wrap text-[12px] w-[300px]" | ||
dangerouslySetInnerHTML={{ | ||
__html: highlighter.codeToHtml(JSON.stringify(content), { | ||
lang: "json", | ||
themes: { | ||
dark: "github-dark", | ||
light: "github-light", | ||
}, | ||
mergeWhitespaces: true, | ||
}), | ||
}} | ||
/> | ||
</Card> | ||
), | ||
content: getResponseBodyFieldOutcome(log, "meta"), | ||
tooltipContent: "Copy Meta", | ||
tooltipSuccessMessage: "Meta copied to clipboard", | ||
}, | ||
]} | ||
/> | ||
); | ||
}; |
40 changes: 40 additions & 0 deletions
40
apps/dashboard/app/(app)/logs/log-details/components/log-header.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { Badge } from "@/components/ui/badge"; | ||
import { cn } from "@/lib/utils"; | ||
import { X } from "lucide-react"; | ||
import type { Log } from "../../data"; | ||
|
||
type Props = { | ||
log: Log; | ||
onClose: () => void; | ||
}; | ||
export const LogHeader = ({ onClose, log }: Props) => { | ||
return ( | ||
<div className="border-b-[1px] px-3 py-4 flex justify-between border-border items-center"> | ||
<div className="flex gap-2"> | ||
<Badge variant="secondary" className="bg-transparent"> | ||
POST | ||
</Badge> | ||
<p className="text-[13px] text-content/65">{log.path}</p> | ||
</div> | ||
|
||
<div className="flex gap-1 items-center"> | ||
<Badge | ||
className={cn( | ||
"bg-background border border-solid border-border text-current hover:bg-transparent", | ||
log.response_status >= 400 && "border-red-6 text-red-11" | ||
)} | ||
> | ||
{log.response_status} | ||
</Badge> | ||
|
||
<span className="text-content/65">|</span> | ||
<X | ||
onClick={onClose} | ||
size="22" | ||
strokeWidth="1.5" | ||
className="text-content/65 cursor-pointer" | ||
/> | ||
</div> | ||
</div> | ||
); | ||
}; |
74 changes: 74 additions & 0 deletions
74
apps/dashboard/app/(app)/logs/log-details/components/request-response-details.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import { toast } from "@/components/ui/toaster"; | ||
import { | ||
Tooltip, | ||
TooltipContent, | ||
TooltipProvider, | ||
TooltipTrigger, | ||
} from "@/components/ui/tooltip"; | ||
import { cn } from "@/lib/utils"; | ||
import type { ReactNode } from "react"; | ||
|
||
type Field<T> = { | ||
label: string; | ||
description: (content: NonNullable<T>) => ReactNode; | ||
content: T | null; | ||
tooltipContent: ReactNode; | ||
tooltipSuccessMessage: string; | ||
className?: string; | ||
}; | ||
|
||
type Props<T extends unknown[]> = { | ||
fields: { [K in keyof T]: Field<T[K]> }; | ||
className?: string; | ||
}; | ||
//This function ensures that content is not nil, and if it's an object or array, it has some content. | ||
const isNonEmpty = (content: unknown): boolean => { | ||
if (Array.isArray(content)) { | ||
return content.length > 0; | ||
} | ||
if (typeof content === "object" && content !== null) { | ||
return Object.keys(content).length > 0; | ||
} | ||
return Boolean(content); | ||
}; | ||
|
||
export const RequestResponseDetails = <T extends unknown[]>({ | ||
fields, | ||
className, | ||
}: Props<T>) => { | ||
return ( | ||
<div className={cn("font-sans", className)}> | ||
{fields.map( | ||
(field, index) => | ||
isNonEmpty(field.content) && ( | ||
// biome-ignore lint/suspicious/noArrayIndexKey: <explanation> | ||
<TooltipProvider key={index}> | ||
<Tooltip> | ||
<TooltipTrigger | ||
className={cn( | ||
"flex w-full justify-between border-border border-solid pr-3 py-[10px] items-center", | ||
index !== fields.length - 1 && "border-b", | ||
field.className | ||
)} | ||
onClick={() => { | ||
navigator.clipboard.writeText( | ||
typeof field.content === "object" | ||
? JSON.stringify(field.content) | ||
: String(field.content) | ||
); | ||
toast.success(field.tooltipSuccessMessage); | ||
}} | ||
> | ||
<span className="text-sm text-content/65">{field.label}</span> | ||
{field.description(field.content as NonNullable<T[number]>)} | ||
</TooltipTrigger> | ||
<TooltipContent side="left"> | ||
{field.tooltipContent} | ||
</TooltipContent> | ||
</Tooltip> | ||
</TooltipProvider> | ||
) | ||
)} | ||
</div> | ||
); | ||
}; |
Oops, something went wrong.