diff --git a/frontend/src/components/ui/badge.tsx b/frontend/src/components/ui/badge.tsx new file mode 100644 index 0000000..cacff11 --- /dev/null +++ b/frontend/src/components/ui/badge.tsx @@ -0,0 +1,49 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" +import { Slot } from "radix-ui" + +import { cn } from "@/lib/utils" + +const badgeVariants = cva( + "group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80", + secondary: + "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80", + destructive: + "bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20", + outline: + "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground", + ghost: + "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50", + link: "text-primary underline-offset-4 hover:underline", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +function Badge({ + className, + variant = "default", + asChild = false, + ...props +}: React.ComponentProps<"span"> & + VariantProps & { asChild?: boolean }) { + const Comp = asChild ? Slot.Root : "span" + + return ( + + ) +} + +export { Badge, badgeVariants } diff --git a/frontend/src/routes/admin/logs.tsx b/frontend/src/routes/admin/logs.tsx index ed172cd..21f13e7 100644 --- a/frontend/src/routes/admin/logs.tsx +++ b/frontend/src/routes/admin/logs.tsx @@ -1,6 +1,16 @@ import { createFileRoute, redirect } from "@tanstack/react-router"; +import { createColumnHelper } from "@tanstack/react-table"; +import { format } from "date-fns-tz"; import { useSocketRoom } from "@/hooks/socket.io.hook"; import { authClient } from "@/lib/auth-client"; +import { Badge } from "../../components/ui/badge"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "../../components/ui/tooltip"; +import LstTable from "../../lib/tableStuff/LstTable"; +import SearchableHeader from "../../lib/tableStuff/SearchableHeader"; export const Route = createFileRoute("/admin/logs")({ beforeLoad: async ({ location }) => { @@ -37,117 +47,152 @@ interface LogEntry { [key: string]: any; // catch any extra fields } -function LevelBadge({ level }: { level: number }) { - const config: Record = { - 10: { label: "TRACE", className: "bg-gray-100 text-gray-600" }, - 20: { label: "DEBUG", className: "bg-blue-100 text-blue-700" }, - 30: { label: "INFO", className: "bg-green-100 text-green-700" }, - 40: { label: "WARN", className: "bg-yellow-100 text-yellow-700" }, - 50: { label: "ERROR", className: "bg-red-100 text-red-700" }, - 60: { label: "FATAL", className: "bg-purple-100 text-purple-700" }, +function LevelBadge({ level }: { level: string }) { + const config: Record = { + trace: { label: "TRACE", className: "bg-gray-100 text-gray-600" }, + debug: { label: "DEBUG", className: "bg-blue-50 text-blue-700" }, + info: { label: "INFO", className: "bg-green-50 text-green-700" }, + warn: { label: "WARN", className: "bg-yellow-100 text-yellow-700" }, + error: { label: "ERROR", className: "bg-red-50 text-red-700" }, + fatal: { label: "FATAL", className: "bg-purple-100 text-purple-700" }, }; - const { label, className } = config[level] ?? { - label: String(level), - className: "bg-gray-100", - }; + const badge = config[level]; return ( - - {label} - + + {badge?.label ?? level} + ); } function RouteComponent() { - const { data: logs, info: logsInfo } = useSocketRoom("logs"); - //const { user } = Route.useRouteContext(); - //const router = useRouter(); - // const [logs, setLogs] = useState([]); - // const [logsInfo, setLogInfo] = useState( - // "No logs yet — join the room to start receiving", - // ); + const { data: logs } = useSocketRoom("logs"); + const columnHelper = createColumnHelper(); + const column = [ + columnHelper.accessor("createdAt", { + header: ({ column }) => , + filterFn: "includesString", + cell: (i) => format(i.getValue(), "MM/dd/yyyy HH:mm:ss"), + }), + columnHelper.accessor("level", { + header: ({ column }) => ( + + ), + filterFn: "includesString", + cell: (i) => , + }), + columnHelper.accessor("module", { + header: ({ column }) => ( + + ), + filterFn: "includesString", + cell: (i) => i.getValue(), + }), + columnHelper.accessor("subModule", { + header: ({ column }) => ( + + ), + filterFn: "includesString", + cell: (i) => i.getValue(), + }), + columnHelper.accessor("hostname", { + header: ({ column }) => ( + + ), + filterFn: "includesString", + cell: (i) => i.getValue(), + }), + columnHelper.accessor("message", { + header: ({ column }) => ( + + ), + filterFn: "includesString", + cell: (i) => i.getValue(), + }), + columnHelper.accessor("stack", { + header: ({ column }) => ( + + ), + filterFn: "includesString", + cell: (i) => { + const stack = i.row.original.stack; + if (!stack) return ; - // useEffect(() => { - // // Connect if not already connected - // if (!socket.connected) { - // socket.connect(); - // } - - // socket.on("connect", () => { - // socket.emit("join-room", "logs"); - // }); - - // socket.emit("join-room", "logs"); - // socket.on( - // "room-update", - // (data: { payloads: LogEntry[]; roomId: string }) => { - // setLogs((prev) => [...data.payloads, ...prev]); - // }, - // ); - - // socket.on("room-error", (data) => { - // setLogInfo(data.message); - // }); - - // // socket.on("logs", (data) => { - // // console.log(data); - // // setLogs((prev) => [...data.payloads, ...prev]); - // // }); - - // // Cleanup listeners on unmount - // return () => { - // socket.emit("leave-room", "logs"); - // socket.off("room-update"); - // socket.off("room-error"); - // socket.off("logs"); - // }; - // }, []); + return ( + + + + + +
+								{JSON.stringify(stack, null, 2)}
+							
+
+
+ ); + }, + }), + ]; return ( -
- {/* Log Table */} -
- - - - - - - - - - - - {logs.length === 0 ? ( - - - - ) : ( - logs.map((log, i) => ( - - - - - - - - )) - )} - -
TimeLevelModuleHostMessage
- {logsInfo} -
- {new Date(log.createdAt).toLocaleTimeString()} - - - {log.module}{log.hostname}{log.message}
-
-
+ //
+ // {/* Log Table */} + //
+ // + // + // + // + // + // + // + // + // + // + // + // {logs.length === 0 ? ( + // + // + // + // ) : ( + // logs.map((log, i) => ( + // + // + // + // + // + // + // + // )) + // )} + // + //
TimeLevelModuleHostMessage
+ // {logsInfo} + //
+ // {new Date(log.createdAt).toLocaleTimeString()} + // + // + // {log.module}{log.hostname}{log.message}
+ //
+ //
+ ); }