Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Query logs component #2879

Draft
wants to merge 15 commits into
base: main
Choose a base branch
from
Draft
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export const LogsDateTime = () => {
<Button
variant="ghost"
className={cn(
"group-data-[state=open]:bg-gray-4 px-2",
"group-data-[state=open]:bg-gray-4 px-2 mr-0",
!title ? "opacity-50" : "",
title !== "Last 12 hours" ? "bg-gray-4" : "",
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
// import { type PropsWithChildren, useEffect, useState } from "react";
import type { SavedFiltersGroup } from "@/app/(app)/logs/hooks/use-bookmarked-filters";
import { toast } from "@/components/ui/toaster";
import { cn } from "@/lib/utils";
import { Bookmark, ChartActivity2, Check, Conversion, Layers2, Link4 } from "@unkey/icons";
import { Tooltip, TooltipContent, TooltipTrigger } from "@unkey/ui";
import { useState } from "react";
import { QueriesMadeBy } from "./queries-made-by";
import { QueriesPill } from "./queries-pill";

type QueriesItemProps = {
filterList: SavedFiltersGroup;
user: { name: string; url: string; since: string };
index: number;
total: number;
selectedIndex: number;
querySelected: (index: number) => void;
changeBookmark: (index: number, isSaved: boolean) => void;
};
export const QueriesToast = () => {
return (
<div className="flex flex-row items-center gap-4 p-2 font-sans">
<Check className="size-[18px] text-success-9" />
<span className="w-full font-medium text-sm leading-6 text-accent-12">Query Saved!</span>
</div>
);
};
export const QueriesItem = ({
filterList,
user,
index,
total,
selectedIndex,
querySelected,
changeBookmark,
}: QueriesItemProps) => {
const { status, methods, paths } = filterList.filters;
const [isSaved, setIsSaved] = useState(false);
const [tooltipMessage, setTooltipMessage] = useState<"Saved!" | "Save Query">(
isSaved ? "Saved!" : "Save Query",
);
const [toolTipOpen, setToolTipOpen] = useState(false);
const handleBookmarkChanged = () => {
if (!isSaved) {
setIsSaved(!isSaved);
setTooltipMessage("Saved!");
toast.success(<QueriesToast />);
changeBookmark(index, !isSaved);
} else {
setIsSaved(!isSaved);
setTooltipMessage("Save Query");
toast.success("Query no longer saved");
changeBookmark(index, !isSaved);
}

changeBookmark(index, !isSaved);
};
const handleSelection = (index: number) => {
querySelected(index);
};

return (
<div className="w-full">
<div
className={cn(
"flex flex-row hover:bg-gray-2 cursor-pointer whitespace-nowrap rounded rounded-[8px] pb-[9px]",
index === selectedIndex ? "bg-gray-2" : "",
)}
>
<div
className={cn("flex flex-col w-full")}
role="button"
onClick={() => handleSelection(index)}
onKeyUp={(e) => e.key === "Enter" && console.log("clicked", index)}
tabIndex={index}
>
{/* Change bg-gray-3 back to 2 */}
<div className="w-full pt-[7px] px-[8px]">
{/* Top Row for each */}
<div className="flex flex-row justify-start items-center h-6">
<div className="inline-flex gap-2 w-full">
<span className="font-mono font-normal text-xs text-gray-9">from</span>
<Layers2 className="size-3 mt-[1px]" />
<span className="font-mono font-medium text-xs">Logs</span>
</div>
</div>

{/* Filters */}
<div className="flex flex-row mt-2">
{/* Vertical Line on Left */}
<div className="flex flex-col ml-[9px] border-l-[1px] border-l-gray-5 w-[1px]" />
<div className="flex flex-col gap-2 ml-0 pl-[18px] ">
{/* Map Thru each Status filter */}
{status && status.length > 0 && (
<div className="flex flex-row justify-start items-center gap-2">
<div className="flex-col font-mono font-normal text-xs text-gray-9 align-start w-[43px]">
Status
</div>
<ChartActivity2 className="size-3.5 mb-[2px]" />
<span className="font-mono font-normal text-xs text-gray-9">
{status[0]?.operator}
</span>
{status?.map((item) => {
return <QueriesPill value={item.value} />;
})}
</div>
)}
{/* Map Thru each Method filter */}
{methods && methods.length > 0 && (
<div className="flex flex-row justify-start items-center gap-2">
<div className="flex-col font-mono font-normal text-xs text-gray-9 align-start w-[43px]">
Method
</div>
<Conversion className="size-3.5 mb-[2px] ml-[-1px]" />
<span className="font-mono font-normal text-xs text-gray-9">
{methods[0]?.operator}
</span>
{methods?.map((item) => {
return <QueriesPill value={item.value} />;
})}
</div>
)}
{/* Map Thru each Path filter */}
{paths && paths.length > 0 && (
<div className="flex flex-row justify-start items-center gap-2">
<div className="flex-col font-mono font-normal text-xs text-gray-9 align-start w-[43px]">
Path
</div>
<Link4 className="size-3 ml-[1px]" />
<span className="font-mono font-normal text-xs text-gray-9">
{paths[0]?.operator}
</span>
<QueriesPill value={paths[0].value} />
</div>
)}
</div>
</div>
<QueriesMadeBy
userName={user.name}
userImageSrc={user.url}
createdString={user.since}
/>
</div>
</div>
<div
className="flex flex-col h-6 w-6 mr-2"
onMouseEnter={() => setToolTipOpen(true)}
onMouseLeave={() => setToolTipOpen(false)}
>
<Tooltip open={toolTipOpen}>
<TooltipTrigger>
<div
className={cn(
"flex h-6 w-6 ml-[1px] mt-1.5 justify-center items-center text-accent-9 rounded-md",
isSaved ? "text-info-9 hover:bg-info-3" : "hover:bg-gray-3 hover:text-accent-12",
)}
role="button"
onClick={handleBookmarkChanged}
onKeyUp={(e) => e.key === "Enter" && console.log("Saved", index)}
>
<Bookmark size="sm-regular" filled={isSaved} />
</div>
</TooltipTrigger>
<TooltipContent
className="flex h-8 py-1 px-2 rounded-lg font-500 text-[12px] justify-center items-center leading-6 shadow-[0_12px_32px_-16px_rgba(0,0,0,0.3)] shadow-[0_12px_60px_1px_rgba(0,0,0,0.15)] shadow-[0_0px_0px_1px_rgba(0,0,0,0.1)]"
side="bottom"
>
{tooltipMessage}
</TooltipContent>
</Tooltip>
</div>
</div>
<div
className={cn(
"flex flex-row bg-white dark:bg-black h-[1px] mt-[7px] mb-[8px] w-full",
index < total - 1 && "border-b-[1px] border-b-gray-3",
)}
/>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { CircleHalfDottedClock } from "@unkey/icons";
import Image from "next/image";

type QueriesMadeByProps = {
userName?: string;
userImageSrc?: string;
createdString: string;
};

export const QueriesMadeBy = ({ userName, userImageSrc, createdString }: QueriesMadeByProps) => {
return (
<div className="flex flex-row w-full justify-start items-center h-6 gap-2 mt-2">
{/* User Avatar */}
{userName && <span className="font-mono font-normal text-xs text-gray-9">by</span>}
{userName && (
<Image
className="rounded-full border border-gray-4 border-[1px]"
src={userImageSrc ?? "/images/user.png"}
width={21}
height={21}
alt="Picture of the user"
/>
)}
{userName && (
<span className="font-mono font-medium leading-4 text-xs text-gray-12">{userName}</span>
)}
<CircleHalfDottedClock className="size-3.5 text-gray-12 mb-[2px] ml-[2px]" />
<span className="font-mono font-normal text-xs leading-4 text-gray-9">{createdString}</span>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { cn } from "@/lib/utils";

type QueriesPillType = {
value: string | number;
};
export const QueriesPill = ({ value }: QueriesPillType) => {
let color = undefined;
let wording = value;
if (value === 200 || value === "200") {
color = "bg-success-9";
wording = "2xx";
} else if (value === 400 || value === "400") {
color = "bg-warning-9";
wording = "4xx";
} else if (value === 500 || value === "500") {
color = "bg-error-9";
wording = "5xx";
}
return (
<div className="h-6 bg-gray-3 inline-flex justify-start items-center py-1.5 px-2 rounded rounded-md gap-2 ">
{color && <div className={cn("w-2 h-2 rounded-[2px]", color)} />}
<span className="font-mono font-medium text-xs text-gray-12 text-xs">{wording}</span>
</div>
);
};
Loading