Render transactions on <table>

Use react-router in data mode to layout the application.
Do a SSR to the datastore API /transactions.
This commit is contained in:
Luís Murta 2025-03-23 00:38:44 +00:00
parent 56d3c2a5a7
commit 4160886cf5
Signed by: satprog
GPG Key ID: 169EF1BBD7049F94
8 changed files with 127 additions and 44 deletions

View File

@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
<title>Personal Finance tools web app</title>
</head>
<body>
<div id="root"></div>

49
package-lock.json generated
View File

@ -2309,6 +2309,21 @@
"node": ">=6.9.0"
}
},
"node_modules/get-tsconfig": {
"version": "4.10.0",
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz",
"integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"resolve-pkg-maps": "^1.0.0"
},
"funding": {
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
}
},
"node_modules/glob-parent": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
@ -2868,6 +2883,18 @@
"node": ">=4"
}
},
"node_modules/resolve-pkg-maps": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"funding": {
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
}
},
"node_modules/reusify": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
@ -3049,6 +3076,28 @@
"typescript": ">=4.8.4"
}
},
"node_modules/tsx": {
"version": "4.19.3",
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.3.tgz",
"integrity": "sha512-4H8vUNGNjQ4V2EOoGw005+c+dGuPSnhpPBPHBtsZdGZBk/iJb4kguGlPWaZTZ3q5nMtFOEsY0nRDlh9PJyd6SQ==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"esbuild": "~0.25.0",
"get-tsconfig": "^4.7.5"
},
"bin": {
"tsx": "dist/cli.mjs"
},
"engines": {
"node": ">=18.0.0"
},
"optionalDependencies": {
"fsevents": "~2.3.3"
}
},
"node_modules/turbo-stream": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz",

View File

@ -1,35 +0,0 @@
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'
function App() {
const [count, setCount] = useState(0)
return (
<>
<div>
<a href="https://vite.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<h1>Vite + React</h1>
<div className="card">
<button onClick={() => setCount((count) => count + 1)}>
count is {count}
</button>
<p>
Edit <code>src/App.tsx</code> and save to test HMR
</p>
</div>
<p className="read-the-docs">
Click on the Vite and React logos to learn more
</p>
</>
)
}
export default App

View File

@ -1,10 +1,11 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { RouterProvider } from "react-router";
import "./index.css";
import { router } from "./routes.ts";
createRoot(document.getElementById('root')!).render(
createRoot(document.getElementById("root")!).render(
<StrictMode>
<App />
</StrictMode>,
)
<RouterProvider router={router} />
</StrictMode>
);

14
src/root.tsx Normal file
View File

@ -0,0 +1,14 @@
import "./root.css";
import { NavLink, Outlet } from "react-router";
export default function App() {
return (
<>
<h1>Personal Finance</h1>
<nav>
<NavLink to="/transactions">Transactions</NavLink>
</nav>
<Outlet />
</>
);
}

17
src/routes.ts Normal file
View File

@ -0,0 +1,17 @@
import { createBrowserRouter } from "react-router";
import Transactions, { loader } from "./routes/transactions";
import App from "./root";
export const router = createBrowserRouter([
{
path: "/",
Component: App,
children: [
{
path: "transactions",
Component: Transactions,
loader: loader,
},
],
},
]);

View File

@ -0,0 +1,37 @@
import { useLoaderData } from "react-router";
export async function loader() {
return await fetch(`http://localhost:9000/transactions`).then((response) => {
return response.json();
});
}
export default function Transactions() {
const transactions: {
id: number;
date: string;
description: string;
value: number;
}[] = useLoaderData<typeof loader>();
return (
<table>
<thead>
<tr>
<th scope="col">date</th>
<th scope="col">description</th>
<th scope="col">value</th>
</tr>
</thead>
<tbody>
{transactions.map((t) => (
<tr>
<th>{t.date}</th>
<td>{t.description}</td>
<td>{t.value}</td>
</tr>
))}
</tbody>
</table>
);
}