generated from Schmax/deno-react-template
Hacked some shit together with Github Copilot
This commit is contained in:
@@ -1,14 +1,16 @@
|
||||
import { BrowserRouter, Route, Routes } from "react-router-dom";
|
||||
import Index from "./pages/Index.tsx";
|
||||
import Dinosaur from "./pages/Dinosaur.tsx";
|
||||
import HistoryPage from "./pages/HistoryPage.tsx";
|
||||
import ServerPage from "./pages/ServerPage.tsx";
|
||||
import "./App.css";
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<Route path="/" element={<Index />} />
|
||||
<Route path="/:selectedDinosaur" element={<Dinosaur />} />
|
||||
<Route path="/" element={<Index />} />
|
||||
<Route path="/history" element={<HistoryPage />} />
|
||||
<Route path="/servers" element={<ServerPage />} />
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
);
|
||||
|
||||
44
client/src/components/History.tsx
Normal file
44
client/src/components/History.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import React from "react";
|
||||
import SnapshotCard from "./SnapshotCard.tsx";
|
||||
|
||||
interface Snapshot {
|
||||
id: string;
|
||||
timestamp: string;
|
||||
progress: number;
|
||||
status: "pending" | "in-progress" | "completed" | "error";
|
||||
}
|
||||
|
||||
interface HistoryProps {
|
||||
currentA: Snapshot[];
|
||||
historyA: Snapshot[];
|
||||
currentB: Snapshot[];
|
||||
historyB: Snapshot[];
|
||||
}
|
||||
|
||||
export default function History({ currentA, historyA, currentB, historyB }: HistoryProps) {
|
||||
// Combine current and history snapshots for both servers
|
||||
const allSnapshotsA = [...currentA, ...historyA];
|
||||
const allSnapshotsB = [...currentB, ...historyB];
|
||||
|
||||
// Pair snapshots by ID
|
||||
const pairedHistory = allSnapshotsA.map((snapshotA) => {
|
||||
const snapshotB = allSnapshotsB.find((snap) => snap.id === snapshotA.id);
|
||||
return { snapshotA, snapshotB };
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="w-full mt-6 p-6 border rounded-lg shadow-md bg-gray-50 dark:bg-gray-800">
|
||||
<h3 className="text-xl font-bold mb-4 text-gray-800 dark:text-gray-200">
|
||||
Snapshot History
|
||||
</h3>
|
||||
<div className="space-y-6">
|
||||
{pairedHistory.map(({ snapshotA, snapshotB }) => (
|
||||
<div key={snapshotA.id} className="flex flex-row gap-4">
|
||||
<SnapshotCard {...snapshotA} />
|
||||
{snapshotB && <SnapshotCard {...snapshotB} />}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
33
client/src/components/ServerCard.tsx
Normal file
33
client/src/components/ServerCard.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import React from "react";
|
||||
import SnapshotCard from "./SnapshotCard.tsx";
|
||||
|
||||
interface Snapshot {
|
||||
id: string;
|
||||
timestamp: string;
|
||||
progress: number;
|
||||
status: "pending" | "in-progress" | "completed" | "error";
|
||||
}
|
||||
|
||||
interface ServerCardProps {
|
||||
serverName: string;
|
||||
snapshots: Snapshot[];
|
||||
}
|
||||
|
||||
export default function ServerCard({ serverName, snapshots }: ServerCardProps) {
|
||||
return (
|
||||
<div className="w-full p-6 border rounded-lg shadow-md bg-gray-50 dark:bg-gray-800">
|
||||
<h2 className="text-2xl font-bold mb-6 text-gray-800 dark:text-gray-200">{serverName}</h2>
|
||||
<div className="grid grid-cols-2 gap-6">
|
||||
{snapshots.map((snapshot) => (
|
||||
<SnapshotCard
|
||||
key={snapshot.id}
|
||||
id={snapshot.id}
|
||||
timestamp={snapshot.timestamp}
|
||||
progress={snapshot.progress}
|
||||
status={snapshot.status}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
36
client/src/components/SnapshotCard.tsx
Normal file
36
client/src/components/SnapshotCard.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import React from "react";
|
||||
|
||||
interface Snapshot {
|
||||
id: string;
|
||||
timestamp: string;
|
||||
progress: number;
|
||||
status: "pending" | "in-progress" | "completed" | "error";
|
||||
}
|
||||
|
||||
export default function SnapshotCard({ id, timestamp, progress, status }: Snapshot) {
|
||||
const statusColors = {
|
||||
completed: "bg-green-100 dark:bg-green-800",
|
||||
"in-progress": "bg-blue-100 dark:bg-blue-800",
|
||||
error: "bg-red-100 dark:bg-red-800",
|
||||
pending: "bg-gray-100 dark:bg-gray-800",
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`p-4 rounded-lg shadow-sm ${statusColors[status]}`}>
|
||||
<div className="text-lg font-semibold text-gray-700 dark:text-gray-200">{id}</div>
|
||||
<div className="text-sm text-gray-500 dark:text-gray-400">
|
||||
{new Date(timestamp).toLocaleString()}
|
||||
</div>
|
||||
{status === "in-progress" && (
|
||||
<div className="mt-2">
|
||||
<div className="w-full bg-gray-300 dark:bg-gray-700 rounded-full h-2.5">
|
||||
<div
|
||||
className="bg-blue-500 dark:bg-blue-400 h-2.5 rounded-full"
|
||||
style={{ width: `${progress}%` }}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
:root {
|
||||
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.5;
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { Link, useParams } from "react-router-dom";
|
||||
import { Dino } from "../types";
|
||||
|
||||
export default function Dinosaur() {
|
||||
const { selectedDinosaur } = useParams();
|
||||
const [dinosaur, setDino] = useState<Dino>({ name: "", description: "" });
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const resp = await fetch(`/api/dinosaurs/${selectedDinosaur}`);
|
||||
const dino = await resp.json() as Dino;
|
||||
setDino(dino);
|
||||
})();
|
||||
}, [selectedDinosaur]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>{dinosaur.name}</h1>
|
||||
<p>{dinosaur.description}</p>
|
||||
<Link to="/">🠠 Back to all dinosaurs</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
70
client/src/pages/HistoryPage.tsx
Normal file
70
client/src/pages/HistoryPage.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import History from "../components/History.tsx";
|
||||
|
||||
interface Snapshot {
|
||||
id: string;
|
||||
timestamp: string;
|
||||
progress: number;
|
||||
status: "pending" | "in-progress" | "completed" | "error";
|
||||
}
|
||||
|
||||
interface ServerData {
|
||||
name: string;
|
||||
current: Snapshot[];
|
||||
history: Snapshot[];
|
||||
}
|
||||
|
||||
export default function HistoryPage() {
|
||||
const [serverA, setServerA] = useState<ServerData | null>(null);
|
||||
const [serverB, setServerB] = useState<ServerData | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchData() {
|
||||
try {
|
||||
const response = await fetch("/api/snapshots");
|
||||
const data = await response.json();
|
||||
setServerA(data.serverA);
|
||||
setServerB(data.serverB);
|
||||
} catch (error) {
|
||||
console.error("Error fetching snapshot data:", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
if (loading) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
if (!serverA || !serverB) {
|
||||
return <div>Error loading server data.</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-dvh flex flex-col bg-gray-100 dark:bg-gray-900 text-gray-800 dark:text-gray-200">
|
||||
{/* Header */}
|
||||
<header className="bg-gray-200 dark:bg-gray-800 p-4 shadow-md">
|
||||
<h1 className="text-3xl font-bold text-center">Snapshot History</h1>
|
||||
</header>
|
||||
|
||||
{/* Main Content */}
|
||||
<main className="flex-1 p-6">
|
||||
<History
|
||||
currentA={serverA.current}
|
||||
historyA={serverA.history}
|
||||
currentB={serverB.current}
|
||||
historyB={serverB.history}
|
||||
/>
|
||||
</main>
|
||||
|
||||
{/* Footer */}
|
||||
<footer className="bg-gray-200 dark:bg-gray-800 p-4 text-center">
|
||||
<p>© 2023 ZFS Monitoring. All rights reserved.</p>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,33 +1,124 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Dino } from "../types.ts";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
interface Snapshot {
|
||||
id: string;
|
||||
timestamp: string;
|
||||
progress: number;
|
||||
status: "pending" | "in-progress" | "completed" | "error";
|
||||
}
|
||||
|
||||
interface ServerData {
|
||||
name: string;
|
||||
current: Snapshot[];
|
||||
history: Snapshot[];
|
||||
}
|
||||
|
||||
export default function Index() {
|
||||
const [dinosaurs, setDinosaurs] = useState<Dino[]>([]);
|
||||
const navigate = useNavigate();
|
||||
const [serverA, setServerA] = useState<ServerData | null>(null);
|
||||
const [serverB, setServerB] = useState<ServerData | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const response = await fetch(`/api/dinosaurs/`);
|
||||
const allDinosaurs = await response.json() as Dino[];
|
||||
setDinosaurs(allDinosaurs);
|
||||
})();
|
||||
async function fetchData() {
|
||||
try {
|
||||
const response = await fetch("/api/snapshots");
|
||||
const data = await response.json();
|
||||
setServerA(data.serverA);
|
||||
setServerB(data.serverB);
|
||||
} catch (error) {
|
||||
console.error("Error fetching snapshot data:", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
if (loading) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
if (!serverA || !serverB) {
|
||||
return <div>Error loading server data.</div>;
|
||||
}
|
||||
|
||||
const latestSnapshot = (server: ServerData) =>
|
||||
server.current.length > 0 ? server.current[0] : null;
|
||||
|
||||
const recentHistory = (server: ServerData) =>
|
||||
server.history.slice(0, 3);
|
||||
|
||||
return (
|
||||
<main>
|
||||
<h1>Welcome to the Dinosaur app</h1>
|
||||
<p>Click on a dinosaur below to learn more.</p>
|
||||
{dinosaurs.map((dinosaur: Dino) => {
|
||||
return (
|
||||
<Link
|
||||
to={`/${dinosaur.name.toLowerCase()}`}
|
||||
key={dinosaur.name}
|
||||
className="dinosaur"
|
||||
>
|
||||
{dinosaur.name}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</main>
|
||||
<div className="min-h-dvh flex flex-col bg-gray-100 dark:bg-gray-900 text-gray-800 dark:text-gray-200">
|
||||
{/* Header */}
|
||||
<header className="bg-gray-200 dark:bg-gray-800 p-4 shadow-md">
|
||||
<h1 className="text-3xl font-bold text-center">ZFS Snapshot Monitoring Dashboard</h1>
|
||||
</header>
|
||||
|
||||
{/* Main Content */}
|
||||
<main className="flex-1 p-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{/* Server Section */}
|
||||
<div className="p-6 border rounded-lg shadow-md bg-blue-500 text-white">
|
||||
<h2 className="text-2xl font-bold mb-4">Server Overview</h2>
|
||||
<div className="space-y-4">
|
||||
{[serverA, serverB].map((server, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="p-4 border rounded-lg bg-blue-600 hover:bg-blue-700 transition"
|
||||
onClick={() => navigate("/servers")}
|
||||
>
|
||||
<h3 className="text-xl font-bold">{server.name}</h3>
|
||||
{latestSnapshot(server) ? (
|
||||
<p className="mt-2">
|
||||
Latest Snapshot:{" "}
|
||||
<span className="font-mono">
|
||||
{latestSnapshot(server)?.timestamp}
|
||||
</span>
|
||||
</p>
|
||||
) : (
|
||||
<p className="mt-2">No snapshots available.</p>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* History Section */}
|
||||
<div className="p-6 border rounded-lg shadow-md bg-green-500 text-white">
|
||||
<h2 className="text-2xl font-bold mb-4">Recent History</h2>
|
||||
<div className="space-y-4">
|
||||
{[serverA, serverB].map((server, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="p-4 border rounded-lg bg-green-600 hover:bg-green-700 transition"
|
||||
onClick={() => navigate("/history")}
|
||||
>
|
||||
<h3 className="text-xl font-bold">{server.name}</h3>
|
||||
<ul className="mt-2 space-y-1">
|
||||
{recentHistory(server).map((snapshot) => (
|
||||
<li
|
||||
key={snapshot.id}
|
||||
className="font-mono text-sm"
|
||||
>
|
||||
{snapshot.timestamp} - {snapshot.status}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
{/* Footer */}
|
||||
<footer className="bg-gray-200 dark:bg-gray-800 p-4 text-center">
|
||||
<p>© 2023 ZFS Monitoring. All rights reserved.</p>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
68
client/src/pages/ServerPage.tsx
Normal file
68
client/src/pages/ServerPage.tsx
Normal file
@@ -0,0 +1,68 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import ServerCard from "../components/ServerCard.tsx";
|
||||
|
||||
interface Snapshot {
|
||||
id: string;
|
||||
timestamp: string;
|
||||
progress: number;
|
||||
status: "pending" | "in-progress" | "completed" | "error";
|
||||
}
|
||||
|
||||
interface ServerData {
|
||||
name: string;
|
||||
current: Snapshot[];
|
||||
history: Snapshot[];
|
||||
}
|
||||
|
||||
export default function ServerPage() {
|
||||
const [serverA, setServerA] = useState<ServerData | null>(null);
|
||||
const [serverB, setServerB] = useState<ServerData | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchData() {
|
||||
try {
|
||||
const response = await fetch("/api/snapshots");
|
||||
const data = await response.json();
|
||||
setServerA(data.serverA);
|
||||
setServerB(data.serverB);
|
||||
} catch (error) {
|
||||
console.error("Error fetching snapshot data:", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
if (loading) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
if (!serverA || !serverB) {
|
||||
return <div>Error loading server data.</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-dvh flex flex-col bg-gray-100 dark:bg-gray-900 text-gray-800 dark:text-gray-200">
|
||||
{/* Header */}
|
||||
<header className="bg-gray-200 dark:bg-gray-800 p-4 shadow-md">
|
||||
<h1 className="text-3xl font-bold text-center">Server Snapshots</h1>
|
||||
</header>
|
||||
|
||||
{/* Main Content */}
|
||||
<main className="flex-1 p-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<ServerCard serverName={serverA.name} snapshots={serverA.current} />
|
||||
<ServerCard serverName={serverB.name} snapshots={serverB.current} />
|
||||
</div>
|
||||
</main>
|
||||
|
||||
{/* Footer */}
|
||||
<footer className="bg-gray-200 dark:bg-gray-800 p-4 text-center">
|
||||
<p>© 2023 ZFS Monitoring. All rights reserved.</p>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -9,12 +9,14 @@
|
||||
"@deno/vite-plugin": "npm:@deno/vite-plugin@^1.0.0",
|
||||
"@oak/oak": "jsr:@oak/oak@^17.1.3",
|
||||
"@std/assert": "jsr:@std/assert@1",
|
||||
"@tailwindcss/vite": "npm:@tailwindcss/vite@^4.1.4",
|
||||
"@tajpouria/cors": "jsr:@tajpouria/cors@^1.2.1",
|
||||
"@types/react": "npm:@types/react@^18.3.12",
|
||||
"@vitejs/plugin-react": "npm:@vitejs/plugin-react@^4.3.3",
|
||||
"react": "npm:react@^18.3.1",
|
||||
"react-dom": "npm:react-dom@^18.3.1",
|
||||
"react-router-dom": "npm:react-router-dom@^7.5.1",
|
||||
"tailwindcss": "npm:tailwindcss@^4.1.4",
|
||||
"vite": "npm:vite@^5.4.11"
|
||||
},
|
||||
"compilerOptions": {
|
||||
|
||||
187
deno.lock
generated
187
deno.lock
generated
@@ -21,6 +21,7 @@
|
||||
"jsr:@std/testing@*": "1.0.5",
|
||||
"jsr:@tajpouria/cors@^1.2.1": "1.2.1",
|
||||
"npm:@deno/vite-plugin@1": "1.0.0_vite@5.4.11__@types+node@22.5.4_@types+node@22.5.4",
|
||||
"npm:@tailwindcss/vite@^4.1.4": "4.1.4_vite@5.4.11__@types+node@22.5.4_@types+node@22.5.4",
|
||||
"npm:@types/node@*": "22.5.4",
|
||||
"npm:@types/react@^18.3.12": "18.3.12",
|
||||
"npm:@vitejs/plugin-react@^4.3.3": "4.3.3_vite@5.4.11__@types+node@22.5.4_@babel+core@7.26.0_@types+node@22.5.4",
|
||||
@@ -28,6 +29,7 @@
|
||||
"npm:react-dom@^18.3.1": "18.3.1_react@18.3.1",
|
||||
"npm:react-router-dom@^7.5.1": "7.5.1_react@18.3.1_react-dom@18.3.1__react@18.3.1",
|
||||
"npm:react@^18.3.1": "18.3.1",
|
||||
"npm:tailwindcss@^4.1.4": "4.1.4",
|
||||
"npm:vite@*": "5.4.11_@types+node@22.5.4",
|
||||
"npm:vite@^5.4.11": "5.4.11_@types+node@22.5.4"
|
||||
},
|
||||
@@ -258,6 +260,25 @@
|
||||
"vite"
|
||||
]
|
||||
},
|
||||
"@emnapi/core@1.4.3": {
|
||||
"integrity": "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==",
|
||||
"dependencies": [
|
||||
"@emnapi/wasi-threads",
|
||||
"tslib"
|
||||
]
|
||||
},
|
||||
"@emnapi/runtime@1.4.3": {
|
||||
"integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==",
|
||||
"dependencies": [
|
||||
"tslib"
|
||||
]
|
||||
},
|
||||
"@emnapi/wasi-threads@1.0.2": {
|
||||
"integrity": "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==",
|
||||
"dependencies": [
|
||||
"tslib"
|
||||
]
|
||||
},
|
||||
"@esbuild/aix-ppc64@0.21.5": {
|
||||
"integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="
|
||||
},
|
||||
@@ -351,6 +372,14 @@
|
||||
"@jridgewell/sourcemap-codec"
|
||||
]
|
||||
},
|
||||
"@napi-rs/wasm-runtime@0.2.9": {
|
||||
"integrity": "sha512-OKRBiajrrxB9ATokgEQoG87Z25c67pCpYcCwmXYX8PBftC9pBfN18gnm/fh1wurSLEKIAt+QRFLFCQISrb66Jg==",
|
||||
"dependencies": [
|
||||
"@emnapi/core",
|
||||
"@emnapi/runtime",
|
||||
"@tybys/wasm-util"
|
||||
]
|
||||
},
|
||||
"@rollup/rollup-android-arm-eabi@4.27.3": {
|
||||
"integrity": "sha512-EzxVSkIvCFxUd4Mgm4xR9YXrcp976qVaHnqom/Tgm+vU79k4vV4eYTjmRvGfeoW8m9LVcsAy/lGjcgVegKEhLQ=="
|
||||
},
|
||||
@@ -405,6 +434,91 @@
|
||||
"@rollup/rollup-win32-x64-msvc@4.27.3": {
|
||||
"integrity": "sha512-vliZLrDmYKyaUoMzEbMTg2JkerfBjn03KmAw9CykO0Zzkzoyd7o3iZNam/TpyWNjNT+Cz2iO3P9Smv2wgrR+Eg=="
|
||||
},
|
||||
"@tailwindcss/node@4.1.4": {
|
||||
"integrity": "sha512-MT5118zaiO6x6hNA04OWInuAiP1YISXql8Z+/Y8iisV5nuhM8VXlyhRuqc2PEviPszcXI66W44bCIk500Oolhw==",
|
||||
"dependencies": [
|
||||
"enhanced-resolve",
|
||||
"jiti",
|
||||
"lightningcss",
|
||||
"tailwindcss"
|
||||
]
|
||||
},
|
||||
"@tailwindcss/oxide-android-arm64@4.1.4": {
|
||||
"integrity": "sha512-xMMAe/SaCN/vHfQYui3fqaBDEXMu22BVwQ33veLc8ep+DNy7CWN52L+TTG9y1K397w9nkzv+Mw+mZWISiqhmlA=="
|
||||
},
|
||||
"@tailwindcss/oxide-darwin-arm64@4.1.4": {
|
||||
"integrity": "sha512-JGRj0SYFuDuAGilWFBlshcexev2hOKfNkoX+0QTksKYq2zgF9VY/vVMq9m8IObYnLna0Xlg+ytCi2FN2rOL0Sg=="
|
||||
},
|
||||
"@tailwindcss/oxide-darwin-x64@4.1.4": {
|
||||
"integrity": "sha512-sdDeLNvs3cYeWsEJ4H1DvjOzaGios4QbBTNLVLVs0XQ0V95bffT3+scptzYGPMjm7xv4+qMhCDrkHwhnUySEzA=="
|
||||
},
|
||||
"@tailwindcss/oxide-freebsd-x64@4.1.4": {
|
||||
"integrity": "sha512-VHxAqxqdghM83HslPhRsNhHo91McsxRJaEnShJOMu8mHmEj9Ig7ToHJtDukkuLWLzLboh2XSjq/0zO6wgvykNA=="
|
||||
},
|
||||
"@tailwindcss/oxide-linux-arm-gnueabihf@4.1.4": {
|
||||
"integrity": "sha512-OTU/m/eV4gQKxy9r5acuesqaymyeSCnsx1cFto/I1WhPmi5HDxX1nkzb8KYBiwkHIGg7CTfo/AcGzoXAJBxLfg=="
|
||||
},
|
||||
"@tailwindcss/oxide-linux-arm64-gnu@4.1.4": {
|
||||
"integrity": "sha512-hKlLNvbmUC6z5g/J4H+Zx7f7w15whSVImokLPmP6ff1QqTVE+TxUM9PGuNsjHvkvlHUtGTdDnOvGNSEUiXI1Ww=="
|
||||
},
|
||||
"@tailwindcss/oxide-linux-arm64-musl@4.1.4": {
|
||||
"integrity": "sha512-X3As2xhtgPTY/m5edUtddmZ8rCruvBvtxYLMw9OsZdH01L2gS2icsHRwxdU0dMItNfVmrBezueXZCHxVeeb7Aw=="
|
||||
},
|
||||
"@tailwindcss/oxide-linux-x64-gnu@4.1.4": {
|
||||
"integrity": "sha512-2VG4DqhGaDSmYIu6C4ua2vSLXnJsb/C9liej7TuSO04NK+JJJgJucDUgmX6sn7Gw3Cs5ZJ9ZLrnI0QRDOjLfNQ=="
|
||||
},
|
||||
"@tailwindcss/oxide-linux-x64-musl@4.1.4": {
|
||||
"integrity": "sha512-v+mxVgH2kmur/X5Mdrz9m7TsoVjbdYQT0b4Z+dr+I4RvreCNXyCFELZL/DO0M1RsidZTrm6O1eMnV6zlgEzTMQ=="
|
||||
},
|
||||
"@tailwindcss/oxide-wasm32-wasi@4.1.4": {
|
||||
"integrity": "sha512-2TLe9ir+9esCf6Wm+lLWTMbgklIjiF0pbmDnwmhR9MksVOq+e8aP3TSsXySnBDDvTTVd/vKu1aNttEGj3P6l8Q==",
|
||||
"dependencies": [
|
||||
"@emnapi/core",
|
||||
"@emnapi/runtime",
|
||||
"@emnapi/wasi-threads",
|
||||
"@napi-rs/wasm-runtime",
|
||||
"@tybys/wasm-util",
|
||||
"tslib"
|
||||
]
|
||||
},
|
||||
"@tailwindcss/oxide-win32-arm64-msvc@4.1.4": {
|
||||
"integrity": "sha512-VlnhfilPlO0ltxW9/BgfLI5547PYzqBMPIzRrk4W7uupgCt8z6Trw/tAj6QUtF2om+1MH281Pg+HHUJoLesmng=="
|
||||
},
|
||||
"@tailwindcss/oxide-win32-x64-msvc@4.1.4": {
|
||||
"integrity": "sha512-+7S63t5zhYjslUGb8NcgLpFXD+Kq1F/zt5Xv5qTv7HaFTG/DHyHD9GA6ieNAxhgyA4IcKa/zy7Xx4Oad2/wuhw=="
|
||||
},
|
||||
"@tailwindcss/oxide@4.1.4": {
|
||||
"integrity": "sha512-p5wOpXyOJx7mKh5MXh5oKk+kqcz8T+bA3z/5VWWeQwFrmuBItGwz8Y2CHk/sJ+dNb9B0nYFfn0rj/cKHZyjahQ==",
|
||||
"dependencies": [
|
||||
"@tailwindcss/oxide-android-arm64",
|
||||
"@tailwindcss/oxide-darwin-arm64",
|
||||
"@tailwindcss/oxide-darwin-x64",
|
||||
"@tailwindcss/oxide-freebsd-x64",
|
||||
"@tailwindcss/oxide-linux-arm-gnueabihf",
|
||||
"@tailwindcss/oxide-linux-arm64-gnu",
|
||||
"@tailwindcss/oxide-linux-arm64-musl",
|
||||
"@tailwindcss/oxide-linux-x64-gnu",
|
||||
"@tailwindcss/oxide-linux-x64-musl",
|
||||
"@tailwindcss/oxide-wasm32-wasi",
|
||||
"@tailwindcss/oxide-win32-arm64-msvc",
|
||||
"@tailwindcss/oxide-win32-x64-msvc"
|
||||
]
|
||||
},
|
||||
"@tailwindcss/vite@4.1.4_vite@5.4.11__@types+node@22.5.4_@types+node@22.5.4": {
|
||||
"integrity": "sha512-4UQeMrONbvrsXKXXp/uxmdEN5JIJ9RkH7YVzs6AMxC/KC1+Np7WZBaNIco7TEjlkthqxZbt8pU/ipD+hKjm80A==",
|
||||
"dependencies": [
|
||||
"@tailwindcss/node",
|
||||
"@tailwindcss/oxide",
|
||||
"tailwindcss",
|
||||
"vite"
|
||||
]
|
||||
},
|
||||
"@tybys/wasm-util@0.9.0": {
|
||||
"integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==",
|
||||
"dependencies": [
|
||||
"tslib"
|
||||
]
|
||||
},
|
||||
"@types/babel__core@7.20.5": {
|
||||
"integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
|
||||
"dependencies": [
|
||||
@@ -491,9 +605,19 @@
|
||||
"ms"
|
||||
]
|
||||
},
|
||||
"detect-libc@2.0.3": {
|
||||
"integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw=="
|
||||
},
|
||||
"electron-to-chromium@1.5.63": {
|
||||
"integrity": "sha512-ddeXKuY9BHo/mw145axlyWjlJ1UBt4WK3AlvkT7W2AbqfRQoacVoRUCF6wL3uIx/8wT9oLKXzI+rFqHHscByaA=="
|
||||
},
|
||||
"enhanced-resolve@5.18.1": {
|
||||
"integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==",
|
||||
"dependencies": [
|
||||
"graceful-fs",
|
||||
"tapable"
|
||||
]
|
||||
},
|
||||
"esbuild@0.21.5": {
|
||||
"integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
|
||||
"dependencies": [
|
||||
@@ -534,6 +658,12 @@
|
||||
"globals@11.12.0": {
|
||||
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="
|
||||
},
|
||||
"graceful-fs@4.2.11": {
|
||||
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
|
||||
},
|
||||
"jiti@2.4.2": {
|
||||
"integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A=="
|
||||
},
|
||||
"js-tokens@4.0.0": {
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
|
||||
},
|
||||
@@ -543,6 +673,52 @@
|
||||
"json5@2.2.3": {
|
||||
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="
|
||||
},
|
||||
"lightningcss-darwin-arm64@1.29.2": {
|
||||
"integrity": "sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA=="
|
||||
},
|
||||
"lightningcss-darwin-x64@1.29.2": {
|
||||
"integrity": "sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w=="
|
||||
},
|
||||
"lightningcss-freebsd-x64@1.29.2": {
|
||||
"integrity": "sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg=="
|
||||
},
|
||||
"lightningcss-linux-arm-gnueabihf@1.29.2": {
|
||||
"integrity": "sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg=="
|
||||
},
|
||||
"lightningcss-linux-arm64-gnu@1.29.2": {
|
||||
"integrity": "sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ=="
|
||||
},
|
||||
"lightningcss-linux-arm64-musl@1.29.2": {
|
||||
"integrity": "sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ=="
|
||||
},
|
||||
"lightningcss-linux-x64-gnu@1.29.2": {
|
||||
"integrity": "sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg=="
|
||||
},
|
||||
"lightningcss-linux-x64-musl@1.29.2": {
|
||||
"integrity": "sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w=="
|
||||
},
|
||||
"lightningcss-win32-arm64-msvc@1.29.2": {
|
||||
"integrity": "sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw=="
|
||||
},
|
||||
"lightningcss-win32-x64-msvc@1.29.2": {
|
||||
"integrity": "sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA=="
|
||||
},
|
||||
"lightningcss@1.29.2": {
|
||||
"integrity": "sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==",
|
||||
"dependencies": [
|
||||
"detect-libc",
|
||||
"lightningcss-darwin-arm64",
|
||||
"lightningcss-darwin-x64",
|
||||
"lightningcss-freebsd-x64",
|
||||
"lightningcss-linux-arm-gnueabihf",
|
||||
"lightningcss-linux-arm64-gnu",
|
||||
"lightningcss-linux-arm64-musl",
|
||||
"lightningcss-linux-x64-gnu",
|
||||
"lightningcss-linux-x64-musl",
|
||||
"lightningcss-win32-arm64-msvc",
|
||||
"lightningcss-win32-x64-msvc"
|
||||
]
|
||||
},
|
||||
"loose-envify@1.4.0": {
|
||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||
"dependencies": [
|
||||
@@ -653,6 +829,15 @@
|
||||
"source-map-js@1.2.1": {
|
||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="
|
||||
},
|
||||
"tailwindcss@4.1.4": {
|
||||
"integrity": "sha512-1ZIUqtPITFbv/DxRmDr5/agPqJwF69d24m9qmM1939TJehgY539CtzeZRjbLt5G6fSy/7YqqYsfvoTEw9xUI2A=="
|
||||
},
|
||||
"tapable@2.2.1": {
|
||||
"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ=="
|
||||
},
|
||||
"tslib@2.8.1": {
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
|
||||
},
|
||||
"turbo-stream@2.4.0": {
|
||||
"integrity": "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g=="
|
||||
},
|
||||
@@ -687,11 +872,13 @@
|
||||
"jsr:@std/assert@1",
|
||||
"jsr:@tajpouria/cors@^1.2.1",
|
||||
"npm:@deno/vite-plugin@1",
|
||||
"npm:@tailwindcss/vite@^4.1.4",
|
||||
"npm:@types/react@^18.3.12",
|
||||
"npm:@vitejs/plugin-react@^4.3.3",
|
||||
"npm:react-dom@^18.3.1",
|
||||
"npm:react-router-dom@^7.5.1",
|
||||
"npm:react@^18.3.1",
|
||||
"npm:tailwindcss@^4.1.4",
|
||||
"npm:vite@^5.4.11"
|
||||
]
|
||||
}
|
||||
|
||||
3078
server/api/data.json
3078
server/api/data.json
File diff suppressed because it is too large
Load Diff
@@ -7,23 +7,11 @@ import data from "./api/data.json" with { type: "json" };
|
||||
export const app = new Application();
|
||||
const router = new Router();
|
||||
|
||||
router.get("/api/dinosaurs", (context) => {
|
||||
context.response.body = data;
|
||||
router.get("/api/snapshots", (context) => {
|
||||
context.response.headers.set("Content-Type", "application/json");
|
||||
context.response.body = JSON.stringify(data);
|
||||
});
|
||||
|
||||
router.get("/api/dinosaurs/:dinosaur", (context) => {
|
||||
if (!context?.params?.dinosaur) {
|
||||
context.response.body = "No dinosaur name provided.";
|
||||
}
|
||||
|
||||
const dinosaur = data.find((item) =>
|
||||
item.name.toLowerCase() === context.params.dinosaur.toLowerCase()
|
||||
);
|
||||
|
||||
context.response.body = dinosaur ?? "No dinosaur found.";
|
||||
});
|
||||
|
||||
|
||||
app.use(oakCors());
|
||||
app.use(router.routes());
|
||||
app.use(router.allowedMethods());
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import deno from "@deno/vite-plugin";
|
||||
import tailwindcss from '@tailwindcss/vite'
|
||||
|
||||
import "react";
|
||||
import "react-dom";
|
||||
@@ -18,6 +19,7 @@ export default defineConfig({
|
||||
},
|
||||
plugins: [
|
||||
react(),
|
||||
tailwindcss(),
|
||||
deno(),
|
||||
],
|
||||
optimizeDeps: {
|
||||
|
||||
Reference in New Issue
Block a user