Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const LogHeader = ({
log: KeysOverviewLog;
}) => {
return (
<div className="border-b-[1px] flex justify-between items-center border-gray-4 pb-3">
<div className="border-b-[1px] flex justify-between items-center border-gray-4 h-[50px] px-4 py-2">
<div className="flex gap-2 items-center flex-1 min-w-0">
<Badge
className={cn("uppercase px-[6px] rounded-md font-mono", {
Expand All @@ -21,13 +21,15 @@ export const LogHeader = ({
>
{log.key_details?.enabled ? "Active" : "Disabled"}
</Badge>
<p className="text-xs text-accent-12 truncate flex-1 mr-4">
{log.key_details?.name || log.key_id}{" "}
<p className="text-xs text-accent-12 truncate flex-1">
{log.key_details?.name || log.key_id}
</p>
</div>
<Button size="icon" variant="ghost" onClick={onClose} className="[&_svg]:size-3">
<XMark className="text-gray-12 stroke-2" />
</Button>
<div className="flex gap-1 items-center shrink-0">
<Button size="icon" variant="ghost" onClick={onClose} className="[&_svg]:size-3">
<XMark className="text-grayA-9 stroke-2" size="sm-regular" />
</Button>
</div>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { formatNumber } from "@/lib/fmt";
import { cn } from "@/lib/utils";
import { Card, CardContent, CopyButton } from "@unkey/ui";
import { CopyButton } from "@unkey/ui";
import { formatOutcomeName, getOutcomeColor } from "../../../../../utils";

export const OutcomeDistributionSection = ({
Expand All @@ -21,42 +21,39 @@ export const OutcomeDistributionSection = ({
};

return (
<div className="flex flex-col gap-1 mt-[16px]">
<div className="flex justify-between items-center">
<span className="text-[13px] text-accent-9 font-sans">
<div className="flex flex-col gap-1 mt-[16px] px-4">
<div className="border bg-gray-2 border-gray-4 rounded-[10px] relative group">
<div className="text-gray-11 text-[12px] leading-6 px-[14px] py-1.5 font-sans">
Outcomes ({outcomeEntries.length})
</span>
</div>
<Card className="bg-gray-2 border-gray-4 rounded-lg">
<CardContent className="py-2 px-3 text-xs relative group">
</div>
<div className="border-gray-4 border-t rounded-[10px] bg-white dark:bg-black px-3.5 py-2">
<div className="flex flex-col gap-1 whitespace-pre-wrap leading-relaxed">
{outcomeEntries.map(([outcome, count]) => (
<div className="group flex items-center w-full p-[3px]" key={outcome}>
<div className="flex items-center text-left text-accent-9 whitespace-nowrap">
<div className="flex items-center w-full px-[3px] h-6" key={outcome}>
<div className="flex items-center text-left text-gray-11 whitespace-nowrap">
<div
className={cn(
"size-[10px] rounded-[2px] shadow-sm mr-2",
getOutcomeColor(outcome),
)}
/>
<span>{formatOutcomeName(outcome)}:</span>
<span className="text-xs text-gray-11 ">{formatOutcomeName(outcome)}:</span>
</div>
<span className="ml-2 text-xs text-accent-12 truncate font-mono tabular-nums">
{formatNumber(count)}
</span>
</div>
))}
</div>
<CopyButton
value={getTextToCopy()}
shape="square"
variant="primary"
size="2xlg"
className="absolute bottom-1 right-1 opacity-0 group-hover:opacity-100 transition-opacity rounded-md p-4"
aria-label="Copy content"
/>
</CardContent>
</Card>
</div>
<CopyButton
value={getTextToCopy()}
shape="square"
variant="outline"
className="absolute bottom-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity rounded-md p-4 bg-gray-2 hover:bg-gray-2 size-2"
aria-label="Copy content"
/>
</div>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
"use client";
import { Card, CardContent, CopyButton } from "@unkey/ui";
import React from "react";
import { CopyButton } from "@unkey/ui";

export const LogSection = ({
details,
Expand All @@ -9,115 +7,51 @@ export const LogSection = ({
details: Record<string, React.ReactNode> | string;
title: string;
}) => {
// Helper function to get the text to copy
const getTextToCopy = () => {
let textToCopy: string;

if (typeof details === "string") {
textToCopy = details;
} else {
// Extract text content from React components
textToCopy = Object.entries(details)
.map(([key, value]) => {
if (value === null || value === undefined) {
return key;
}
// If it's a React element, try to extract text content
if (typeof value === "object" && value !== null && "props" in value) {
return `${key}: ${extractTextFromReactElement(value)}`;
}
return `${key}: ${value}`;
})
.join("\n");
}
return textToCopy;
};

// Helper function to extract text from React elements
// This is used to extract text from React elements like TimestampInfo and Link components
const extractTextFromReactElement = (element: React.ReactNode): string => {
if (typeof element === "string" || typeof element === "number") {
return String(element);
return details;
}

if (element === null || element === undefined) {
return "";
}

// Handle React elements
if (React.isValidElement(element)) {
const reactElement = element as React.ReactElement<{
value?: string | Date | number;
children?: React.ReactNode;
href?: string;
title?: string;
}>;

// For TimestampInfo and similar components, check for a 'value' prop first
if (reactElement.props.value) {
// If value is a date/timestamp, format it appropriately
if (reactElement.props.value instanceof Date) {
return reactElement.props.value.toISOString();
return Object.entries(details)
.map(([key, value]) => {
if (value === null || value === undefined) {
return key;
}
return String(reactElement.props.value);
}

// Then check for children
if (reactElement.props.children) {
if (typeof reactElement.props.children === "string") {
return reactElement.props.children;
}
if (Array.isArray(reactElement.props.children)) {
return reactElement.props.children
.map((child: React.ReactNode) => extractTextFromReactElement(child))
.join("");
if (typeof value === "object" && value !== null && "props" in value) {
return `${key}: ${value}`;
}
Comment on lines +20 to 22
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Remove dead code from previous React element extraction logic.

Lines 20-22 check for objects with a props property but perform the same operation as the fallback case (line 23). Based on the past review comments, this appears to be leftover code from the removed React element text-extraction logic.

Apply this diff to remove the redundant check:

     return Object.entries(details)
       .map(([key, value]) => {
         if (value === null || value === undefined) {
           return key;
         }
-        if (typeof value === "object" && value !== null && "props" in value) {
-          return `${key}: ${value}`;
-        }
         return `${key}: ${value}`;
       })
       .join("\n");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (typeof value === "object" && value !== null && "props" in value) {
return `${key}: ${value}`;
}
return Object.entries(details)
.map(([key, value]) => {
if (value === null || value === undefined) {
return key;
}
return `${key}: ${value}`;
})
.join("\n");
🤖 Prompt for AI Agents
In
apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/_overview/components/table/components/log-details/components/log-section.tsx
around lines 20 to 22, remove the dead redundant conditional that checks for
objects with a "props" property (the if (typeof value === "object" && value !==
null && "props" in value) block) since it simply returns the same string as the
fallback; delete that entire conditional so the fallback case handles these
values and keep surrounding logic intact.

return extractTextFromReactElement(reactElement.props.children);
}

// For Link components, check for href or title
if (reactElement.props.href || reactElement.props.title) {
return reactElement.props.title || reactElement.props.href || "";
}
}

return String(element);
return `${key}: ${value}`;
})
.join("\n");
};

return (
<div className="flex flex-col gap-1 mt-[16px]">
<div className="flex justify-between items-center">
<span className="text-[13px] text-accent-9 font-sans">{title}</span>
</div>
<Card className="bg-gray-2 border-gray-4 rounded-lg">
<CardContent className="py-2 px-3 text-xs relative group">
<pre className="flex flex-col gap-1 whitespace-pre-wrap leading-relaxed">
<div className="flex flex-col gap-1 mt-[16px] px-4">
<div className="border bg-gray-2 border-gray-4 rounded-[10px] relative group">
<div className="text-gray-11 text-[12px] leading-6 px-[14px] py-1.5 font-sans">{title}</div>
<div className="border-gray-4 border-t rounded-[10px] bg-white dark:bg-black px-3.5 py-2">
<pre className="whitespace-pre-wrap break-words leading-relaxed text-xs">
{typeof details === "object"
? Object.entries(details).map((detail) => {
const [key, value] = detail;
return (
<div className="group flex items-center w-full p-[3px]" key={key}>
<span className="text-left text-accent-9 whitespace-nowrap">
{key}
{value ? ":" : ""}
</span>
<span className="ml-2 text-xs text-accent-12 truncate">{value}</span>
</div>
);
})
? Object.entries(details).map(([key, value]) => (
<div className="flex items-center w-full px-[3px] leading-7" key={key}>
<span className="text-left text-gray-11 whitespace-nowrap">
{key}
{value ? ":" : ""}
</span>
<span className="ml-2 text-accent-12 truncate">{value}</span>
</div>
))
: details}
</pre>

<CopyButton
value={getTextToCopy()}
shape="square"
variant="primary"
size="2xlg"
className="absolute bottom-1 right-1 opacity-0 group-hover:opacity-100 transition-opacity rounded-md p-4"
aria-label="Copy content"
/>
</CardContent>
</Card>
</div>
<CopyButton
value={getTextToCopy()}
shape="square"
variant="outline"
className="absolute bottom-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity rounded-md p-4 bg-gray-2 hover:bg-gray-2 size-2"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix incorrect button size.

The size-2 class sets the button dimensions to 2px (0.125rem), which is far too small for an interactive element. This appears to be a typo or incorrect value.

Remove size-2 to let the button size be determined by padding and icon size:

-          className="absolute bottom-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity rounded-md p-4 bg-gray-2 hover:bg-gray-2 size-2"
+          className="absolute bottom-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity rounded-md p-4 bg-gray-2 hover:bg-gray-2"

Alternatively, if a specific size is needed, consider size-8 or size-10 for an appropriately sized button.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
className="absolute bottom-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity rounded-md p-4 bg-gray-2 hover:bg-gray-2 size-2"
className="absolute bottom-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity rounded-md p-4 bg-gray-2 hover:bg-gray-2"
🤖 Prompt for AI Agents
In
apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/_overview/components/table/components/log-details/components/log-section.tsx
around line 51, the button element wrongly includes the class "size-2" which
reduces its dimensions to 2px; remove "size-2" so the button size is governed by
padding and icon size, or replace it with an appropriate utility like "size-8"
or "size-10" if a fixed size is required.

aria-label="Copy content"
/>
</div>
</div>
);
};
Loading