frontend added and socket io
This commit is contained in:
20
frontend/src/routes/__root.tsx
Normal file
20
frontend/src/routes/__root.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import { createRootRoute, Link, Outlet } from '@tanstack/react-router'
|
||||
import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'
|
||||
|
||||
const RootLayout = () => (
|
||||
<>
|
||||
<div className="p-2 flex gap-2">
|
||||
<Link to="/" className="[&.active]:font-bold">
|
||||
Home
|
||||
</Link>{' '}
|
||||
<Link to="/about" className="[&.active]:font-bold">
|
||||
About
|
||||
</Link>
|
||||
</div>
|
||||
<hr />
|
||||
<Outlet />
|
||||
<TanStackRouterDevtools />
|
||||
</>
|
||||
)
|
||||
|
||||
export const Route = createRootRoute({ component: RootLayout })
|
||||
9
frontend/src/routes/about.tsx
Normal file
9
frontend/src/routes/about.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/about')({
|
||||
component: About,
|
||||
})
|
||||
|
||||
function About() {
|
||||
return <div className="p-2">Hello from About!</div>
|
||||
}
|
||||
165
frontend/src/routes/index.tsx
Normal file
165
frontend/src/routes/index.tsx
Normal file
@@ -0,0 +1,165 @@
|
||||
import { createFileRoute, useRouter } from "@tanstack/react-router";
|
||||
import { useEffect, useState } from "react";
|
||||
import socket from "@/lib/socket.io";
|
||||
import { Button } from "../components/ui/button";
|
||||
import { authClient, useSession } from "../lib/auth-client";
|
||||
|
||||
export const Route = createFileRoute("/")({
|
||||
component: Index,
|
||||
});
|
||||
|
||||
interface LogEntry {
|
||||
level: number;
|
||||
createdAt: Date;
|
||||
pid: number;
|
||||
hostname: string;
|
||||
module: string;
|
||||
message?: string;
|
||||
[key: string]: any; // catch any extra fields
|
||||
}
|
||||
|
||||
function Index() {
|
||||
const { data: session, isPending } = useSession();
|
||||
const [logs, setLogs] = useState<LogEntry[]>([]);
|
||||
const router = useRouter();
|
||||
|
||||
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("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("logs");
|
||||
};
|
||||
}, []); // empty array = runs once on mount
|
||||
|
||||
const removeLog = (i: LogEntry) => {
|
||||
setLogs(logs.filter((l) => l.id !== i.id));
|
||||
};
|
||||
|
||||
const login = async () => {
|
||||
try {
|
||||
await authClient.signIn.email({
|
||||
email: "blake.matthes@alpla.com",
|
||||
password: "nova0511",
|
||||
});
|
||||
} catch (error: any) {
|
||||
console.error(error.response);
|
||||
}
|
||||
|
||||
//console.log(session)
|
||||
router.invalidate();
|
||||
};
|
||||
|
||||
if (isPending) return <div>Loading...</div>;
|
||||
// if (!session) return <button>Sign In</button>
|
||||
|
||||
return (
|
||||
<div className="p-2">
|
||||
<h3 className="w-2xl text-3xl">Welcome Home!</h3>
|
||||
{!session ? (
|
||||
<Button onClick={login}>Sign In</Button>
|
||||
) : (
|
||||
<div>
|
||||
<span>welcome {session.user?.name}</span>
|
||||
<Button onClick={() => authClient.signOut()}>Signout</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Log Table */}
|
||||
<div className="rounded border overflow-auto max-h-[600px]">
|
||||
<table className="w-full text-sm">
|
||||
<thead className="bg-muted sticky top-0">
|
||||
<tr>
|
||||
<th className="text-left px-3 py-2">Time</th>
|
||||
<th className="text-left px-3 py-2">Level</th>
|
||||
<th className="text-left px-3 py-2">Module</th>
|
||||
<th className="text-left px-3 py-2">Host</th>
|
||||
<th className="text-left px-3 py-2">PID</th>
|
||||
<th className="text-left px-3 py-2">Message</th>
|
||||
<th className="text-left px-3 py-2">logId</th>
|
||||
<th className="text-left px-3 py-2">remove</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{logs.length === 0 ? (
|
||||
<tr>
|
||||
<td
|
||||
colSpan={6}
|
||||
className="text-center py-6 text-muted-foreground"
|
||||
>
|
||||
No logs yet — join the room to start receiving
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
logs.map((log, i) => (
|
||||
<tr
|
||||
key={`${log.id}-${i}`}
|
||||
className="border-t hover:bg-muted/50"
|
||||
>
|
||||
<td className="px-3 py-2 whitespace-nowrap">
|
||||
{new Date(log.createdAt).toLocaleTimeString()}
|
||||
</td>
|
||||
<td className="px-3 py-2">
|
||||
<LevelBadge level={log.level} />
|
||||
</td>
|
||||
<td className="px-3 py-2">{log.module}</td>
|
||||
<td className="px-3 py-2">{log.hostname}</td>
|
||||
<td className="px-3 py-2">{log.pid}</td>
|
||||
<td className="px-3 py-2 max-w-sm truncate">{log.message}</td>
|
||||
<td className="px-3 py-2">{log.id}</td>
|
||||
<td className="px-3 py-2">
|
||||
<Button onClick={() => removeLog(log)}>Delete</Button>
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function LevelBadge({ level }: { level: number }) {
|
||||
const config: Record<number, { label: string; className: string }> = {
|
||||
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" },
|
||||
};
|
||||
|
||||
const { label, className } = config[level] ?? {
|
||||
label: String(level),
|
||||
className: "bg-gray-100",
|
||||
};
|
||||
|
||||
return (
|
||||
<span className={`px-2 py-0.5 rounded text-xs font-medium ${className}`}>
|
||||
{label}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user