From c15012afa7ff7bbb499262018b2a9ffd5a675206 Mon Sep 17 00:00:00 2001 From: MiguelMLorente Date: Sun, 7 Dec 2025 00:50:51 +0100 Subject: [PATCH] Introduce invocing page --- src/App.tsx | 10 ++- src/api/client.ts | 20 +++++ src/pages/{ => auth}/Login.tsx | 2 +- src/pages/{ => auth}/Register.tsx | 2 +- src/pages/{ => buy}/Buy.tsx | 3 +- src/pages/{ => buy}/BuyReturn.tsx | 2 +- src/pages/invoice/InvoicePage.tsx | 39 ++++++++++ src/pages/invoice/PurchaseSummaryTable.tsx | 85 ++++++++++++++++++++++ src/pages/invoice/SessionSummaryTable.tsx | 81 +++++++++++++++++++++ src/pages/signup/TokensSummary.tsx | 4 +- vite.config.ts | 1 + 11 files changed, 238 insertions(+), 11 deletions(-) rename src/pages/{ => auth}/Login.tsx (97%) rename src/pages/{ => auth}/Register.tsx (98%) rename src/pages/{ => buy}/Buy.tsx (97%) rename src/pages/{ => buy}/BuyReturn.tsx (89%) create mode 100644 src/pages/invoice/InvoicePage.tsx create mode 100644 src/pages/invoice/PurchaseSummaryTable.tsx create mode 100644 src/pages/invoice/SessionSummaryTable.tsx diff --git a/src/App.tsx b/src/App.tsx index 47472b6..0163b17 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,14 +1,15 @@ import "./App.css"; -import { Login } from "./pages/Login.tsx"; +import { Login } from "./pages/auth/Login.tsx"; import icon from "./paella-icon.png"; import { Outlet, Route, Routes } from "react-router"; import { SignUpPage } from "./pages/signup/SignUpPage.tsx"; -import { Register } from "./pages/Register.tsx"; -import { Buy } from "./pages/Buy.tsx"; +import { Register } from "./pages/auth/Register.tsx"; +import { Buy } from "./pages/buy/Buy.tsx"; import { NotFound } from "./pages/NotFound.tsx"; -import { BuyReturn } from "./pages/BuyReturn.tsx"; +import { BuyReturn } from "./pages/buy/BuyReturn.tsx"; import { Grid, Paper } from "@mui/material"; import { AuthenticatedRoute } from "./pages/auth/AuthenticatedRoute.tsx"; +import { InvoicePage } from "./pages/invoice/InvoicePage.tsx"; const App = () => ( ( } /> } /> } /> + } /> } /> diff --git a/src/api/client.ts b/src/api/client.ts index ca79cad..89ca835 100644 --- a/src/api/client.ts +++ b/src/api/client.ts @@ -86,3 +86,23 @@ export const getAvailableTokenCount = () => (axios.get("/tokens", getJwtHeader()) as Promise>).then( (response) => response.data, ); + +export interface InvoiceSummary { + purchaseData: { + product: string; + units: number; + status: string; + date: string; + }[]; + sessionsData: { + date: string; + status: string; + }[]; +} + +export const getInvoicingSummary = () => + ( + axios.get("/invoice", getJwtHeader()) as Promise< + AxiosResponse + > + ).then((response) => response.data); diff --git a/src/pages/Login.tsx b/src/pages/auth/Login.tsx similarity index 97% rename from src/pages/Login.tsx rename to src/pages/auth/Login.tsx index 6ca80e4..be8de71 100644 --- a/src/pages/Login.tsx +++ b/src/pages/auth/Login.tsx @@ -1,5 +1,5 @@ import { useState } from "react"; -import { login } from "../api/client"; +import { login } from "../../api/client"; import { useNavigate } from "react-router"; import { Button, diff --git a/src/pages/Register.tsx b/src/pages/auth/Register.tsx similarity index 98% rename from src/pages/Register.tsx rename to src/pages/auth/Register.tsx index cbd52c5..e0e9749 100644 --- a/src/pages/Register.tsx +++ b/src/pages/auth/Register.tsx @@ -1,6 +1,6 @@ import { useState } from "react"; import * as zod from "zod"; -import { registerUser } from "../api/client"; +import { registerUser } from "../../api/client"; import { useNavigate } from "react-router"; import { Alert, diff --git a/src/pages/Buy.tsx b/src/pages/buy/Buy.tsx similarity index 97% rename from src/pages/Buy.tsx rename to src/pages/buy/Buy.tsx index 3dc6673..21194be 100644 --- a/src/pages/Buy.tsx +++ b/src/pages/buy/Buy.tsx @@ -1,5 +1,5 @@ import { useState } from "react"; -import { startBuyFlow } from "../api/client"; +import { startBuyFlow } from "../../api/client"; import { Button, FormControlLabel, @@ -22,7 +22,6 @@ export const Buy = () => { return ( Buy tokens - { diff --git a/src/pages/invoice/InvoicePage.tsx b/src/pages/invoice/InvoicePage.tsx new file mode 100644 index 0000000..378ec68 --- /dev/null +++ b/src/pages/invoice/InvoicePage.tsx @@ -0,0 +1,39 @@ +import { Async } from "react-async"; +import { getInvoicingSummary, type InvoiceSummary } from "../../api/client"; +import { Alert, Button, Grid, Typography } from "@mui/material"; +import { PurchaseSummaryTable } from "./PurchaseSummaryTable"; +import { SessionSummaryTable } from "./SessionSummaryTable"; +import { useNavigate } from "react-router"; + +export const InvoicePage = () => { + const invoiceSummaryPromise = getInvoicingSummary(); + const navigate = useNavigate(); + + return ( + + Summary + + > + {(invoiceSummary) => ( + + + + + + + )} + + + Something went wrong + + + + ); +}; diff --git a/src/pages/invoice/PurchaseSummaryTable.tsx b/src/pages/invoice/PurchaseSummaryTable.tsx new file mode 100644 index 0000000..fc5755b --- /dev/null +++ b/src/pages/invoice/PurchaseSummaryTable.tsx @@ -0,0 +1,85 @@ +import { type InvoiceSummary } from "../../api/client"; +import { + Paper, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TablePagination, + TableRow, + Typography, +} from "@mui/material"; +import { useState } from "react"; +import { FormattedDate } from "react-intl"; + +export const PurchaseSummaryTable = (props: { + invoiceSummary: InvoiceSummary; +}) => { + const { invoiceSummary } = props; + const [page, setPage] = useState(0); + const rowsPerPage = 5; + + return ( + + + Purchases summary + + + + + + + Purchase date + + + Units + + + Purchase status + + + + + {invoiceSummary.purchaseData + .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) + .map(({ date, units, status }) => ( + + + + + {units} + {status} + + ))} + +
+
+ setPage(newPage)} + rowsPerPageOptions={[]} + /> +
+ ); +}; diff --git a/src/pages/invoice/SessionSummaryTable.tsx b/src/pages/invoice/SessionSummaryTable.tsx new file mode 100644 index 0000000..fd3d1e0 --- /dev/null +++ b/src/pages/invoice/SessionSummaryTable.tsx @@ -0,0 +1,81 @@ +import { type InvoiceSummary } from "../../api/client"; +import { + Paper, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TablePagination, + TableRow, + Typography, +} from "@mui/material"; +import { useState } from "react"; +import { FormattedDate } from "react-intl"; + +export const SessionSummaryTable = (props: { + invoiceSummary: InvoiceSummary; +}) => { + const { invoiceSummary } = props; + const [page, setPage] = useState(0); + const rowsPerPage = 5; + + return ( + + + Sessions summary + + + + + + + Session date + + + Session status + + + + + {invoiceSummary.sessionsData + .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) + .map(({ date, status }) => ( + + + + + {status} + + ))} + +
+
+ setPage(newPage)} + rowsPerPageOptions={[]} + /> +
+ ); +}; diff --git a/src/pages/signup/TokensSummary.tsx b/src/pages/signup/TokensSummary.tsx index 3fc4e2d..ec1b92f 100644 --- a/src/pages/signup/TokensSummary.tsx +++ b/src/pages/signup/TokensSummary.tsx @@ -68,9 +68,9 @@ export const TokensSummary = (props: { tokens: Tokens }) => { variant="contained" color="secondary" sx={{ width: "100%" }} - onClick={() => navigate("/invoices")} + onClick={() => navigate("/summary")} > - See all purchases + See your summary
diff --git a/vite.config.ts b/vite.config.ts index 0fdf14b..0e6c40b 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -8,6 +8,7 @@ const serverRoutes = [ "/buy/cancel", "/session", "/tokens", + "/invoice", ]; const baseServerConfigBuilder = (domain: string) => ({ proxy: Object.fromEntries(