diff --git a/apps/dashboard/app/(app)/logs-v2/components/charts/index.tsx b/apps/dashboard/app/(app)/logs-v2/components/charts/index.tsx index 62785edb85..ab5a675205 100644 --- a/apps/dashboard/app/(app)/logs-v2/components/charts/index.tsx +++ b/apps/dashboard/app/(app)/logs-v2/components/charts/index.tsx @@ -33,7 +33,7 @@ const formatTimestampTooltip = (value: string | number) => { const date = new Date(value); const offset = new Date().getTimezoneOffset() * -1; const localDate = addMinutes(date, offset); - return format(localDate, "MMM dd HH:mm:ss.SS aa"); + return format(localDate, "MMM dd HH:mm aa"); }; const formatTimestampLabel = (timestamp: string | number | Date) => { diff --git a/apps/dashboard/app/(app)/logs-v2/components/control-cloud/index.tsx b/apps/dashboard/app/(app)/logs-v2/components/control-cloud/index.tsx index e4fb5e946d..7c5bbd1c26 100644 --- a/apps/dashboard/app/(app)/logs-v2/components/control-cloud/index.tsx +++ b/apps/dashboard/app/(app)/logs-v2/components/control-cloud/index.tsx @@ -27,11 +27,11 @@ const formatValue = (value: string | number): string => { const statusFamily = Math.floor(Number.parseInt(value) / 100); switch (statusFamily) { case 5: - return "5XX (Error)"; + return "5xx (Error)"; case 4: - return "4XX (Warning)"; + return "4xx (Warning)"; case 2: - return "2XX (Success)"; + return "2xx (Success)"; default: return `${statusFamily}xx`; } @@ -106,6 +106,10 @@ export const ControlCloud = () => { updateFilters([]); }); + useKeyboardShortcut({ key: "c", meta: true }, () => { + setFocusedIndex(0); + }); + const handleRemoveFilter = useCallback( (id: string) => { removeFilter(id); @@ -192,7 +196,7 @@ export const ControlCloud = () => { return (
{filters.map((filter, index) => ( @@ -205,9 +209,12 @@ export const ControlCloud = () => { index={index} /> ))} -
+
Clear filters +
+ Focus filters +
); diff --git a/apps/dashboard/app/(app)/logs-v2/components/controls/components/logs-display/components/display-popover.tsx b/apps/dashboard/app/(app)/logs-v2/components/controls/components/logs-display/components/display-popover.tsx new file mode 100644 index 0000000000..db6e699603 --- /dev/null +++ b/apps/dashboard/app/(app)/logs-v2/components/controls/components/logs-display/components/display-popover.tsx @@ -0,0 +1,169 @@ +import { isDisplayProperty, useLogsContext } from "@/app/(app)/logs-v2/context/logs"; +import { useKeyboardShortcut } from "@/app/(app)/logs-v2/hooks/use-keyboard-shortcut"; +import { KeyboardButton } from "@/components/keyboard-button"; +import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; +import { + type KeyboardEvent, + type PropsWithChildren, + useCallback, + useEffect, + useState, +} from "react"; + +const DISPLAY_PROPERTIES = [ + { id: "time", label: "Time" }, + { id: "response_status", label: "Status" }, + { id: "method", label: "Method" }, + { id: "path", label: "Path" }, + { id: "response_body", label: "Response Body" }, + { id: "request_id", label: "Request ID" }, + { id: "host", label: "Host" }, + { id: "request_headers", label: "Request Headers" }, + { id: "request_body", label: "Request Body" }, + { id: "response_headers", label: "Response Headers" }, +]; + +const DisplayPropertyItem = ({ + label, + selected, + onClick, + isFocused, + index, +}: { + label: string; + selected: boolean; + onClick: () => void; + isFocused: boolean; + index: number; +}) => ( +
{ + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + onClick(); + } + }} + > + {label} +
+); + +const PopoverHeader = () => ( +
+ Display Properties... + +
+); + +export const DisplayPopover = ({ children }: PropsWithChildren) => { + const [open, setOpen] = useState(false); + const [focusedIndex, setFocusedIndex] = useState(null); + const { displayProperties, toggleDisplayProperty } = useLogsContext(); + + useKeyboardShortcut("d", () => { + setOpen((prev) => !prev); + if (!open) { + setFocusedIndex(0); + } + }); + + const handleKeyNavigation = useCallback( + (e: KeyboardEvent) => { + const itemsPerRow = Math.floor(384 / 120); // Approximate width / item width + const totalItems = DISPLAY_PROPERTIES.length; + const currentRow = Math.floor((focusedIndex ?? 0) / itemsPerRow); + const currentCol = (focusedIndex ?? 0) % itemsPerRow; + + const moveToIndex = (newIndex: number) => { + e.preventDefault(); + setFocusedIndex(Math.max(0, Math.min(newIndex, totalItems - 1))); + }; + + switch (e.key) { + case "ArrowRight": + case "l": { + moveToIndex((focusedIndex ?? -1) + 1); + break; + } + case "ArrowLeft": + case "h": { + moveToIndex((focusedIndex ?? 1) - 1); + break; + } + case "ArrowDown": + case "j": { + const nextRowIndex = (currentRow + 1) * itemsPerRow + currentCol; + if (nextRowIndex < totalItems) { + moveToIndex(nextRowIndex); + } + break; + } + case "ArrowUp": + case "k": { + const prevRowIndex = (currentRow - 1) * itemsPerRow + currentCol; + if (prevRowIndex >= 0) { + moveToIndex(prevRowIndex); + } + break; + } + case "Enter": + case " ": { + if (focusedIndex !== null) { + const prop = DISPLAY_PROPERTIES[focusedIndex]; + if (isDisplayProperty(prop.id)) { + toggleDisplayProperty(prop.id); + } + } + break; + } + } + }, + [focusedIndex, toggleDisplayProperty], + ); + + useEffect(() => { + if (!open) { + setFocusedIndex(null); + } + }, [open]); + + return ( + + {children} + +
+ +
+ {DISPLAY_PROPERTIES.map((prop, index) => ( + { + if (isDisplayProperty(prop.id)) { + toggleDisplayProperty(prop.id); + } + }} + isFocused={focusedIndex === index} + index={index} + /> + ))} +
+
+
+
+ ); +}; + +export default DisplayPopover; diff --git a/apps/dashboard/app/(app)/logs-v2/components/controls/components/logs-display/index.tsx b/apps/dashboard/app/(app)/logs-v2/components/controls/components/logs-display/index.tsx new file mode 100644 index 0000000000..c34fdda8e3 --- /dev/null +++ b/apps/dashboard/app/(app)/logs-v2/components/controls/components/logs-display/index.tsx @@ -0,0 +1,23 @@ +import { Sliders } from "@unkey/icons"; +import { Button } from "@unkey/ui"; +import { cn } from "@unkey/ui/src/lib/utils"; +import { DisplayPopover } from "./components/display-popover"; + +export const LogsDisplay = () => { + return ( + +
+ +
+
+ ); +}; diff --git a/apps/dashboard/app/(app)/logs-v2/components/controls/components/logs-filters/components/filter-checkbox.tsx b/apps/dashboard/app/(app)/logs-v2/components/controls/components/logs-filters/components/filter-checkbox.tsx index 7541df11ef..97edf2abf5 100644 --- a/apps/dashboard/app/(app)/logs-v2/components/controls/components/logs-filters/components/filter-checkbox.tsx +++ b/apps/dashboard/app/(app)/logs-v2/components/controls/components/logs-filters/components/filter-checkbox.tsx @@ -69,7 +69,7 @@ export const FilterCheckbox = ({ >
@@ -89,7 +89,7 @@ export const FilterCheckbox = ({ {checkboxes.map((checkbox, index) => (