From 2f0788cc026a650b4fcfab01ce2e35b38159e2f6 Mon Sep 17 00:00:00 2001 From: clemens <cwalter@ethz.ch> Date: Wed, 20 Nov 2024 13:30:56 +0000 Subject: [PATCH] add login functionality. it stores the api token in localstorage. the backend is responsible for providing the porper env variables. also formated everything with prettier. --- .env | 2 +- README.md | 11 +- eslint.config.mjs | 45 +++-- openapi-ts.config.ts | 21 +- setup.txt | 30 +++ src/Callback.tsx | 33 ++++ src/ErrorBoundary.tsx | 52 ++--- src/ThemeWrapper.tsx | 61 +++--- src/apiClientConfig.ts | 17 +- src/client/schemas.gen.ts | 36 ++-- src/client/types.gen.ts | 8 + src/main.tsx | 53 ++--- src/pages/Belegformular.tsx | 30 +-- src/pages/Bills.tsx | 257 +++++++++++++----------- src/pages/CreditPayment.tsx | 199 +++++++++++-------- src/pages/CreditPaymentsList.tsx | 210 ++++++++++++-------- src/pages/FetchData.tsx | 6 +- src/pages/GenerateItem.tsx | 84 ++++---- src/pages/Reimbursement.tsx | 199 +++++++++++-------- src/pages/index.tsx | 37 ++-- src/pages/root.tsx | 323 +++++++++++++++++++------------ vite.config.ts | 8 +- yarn.lock | 186 +++++++++++++++++- 23 files changed, 1214 insertions(+), 694 deletions(-) create mode 100644 setup.txt create mode 100644 src/Callback.tsx diff --git a/.env b/.env index c2bbd18..af0d8db 100644 --- a/.env +++ b/.env @@ -1 +1 @@ -VITE_API_BASE_URL=http://localhost:8001/ +VITE_API_BASE_URL=http://localhost:8000/ diff --git a/README.md b/README.md index 811be7f..5bde877 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,11 @@ use https://heyapi.dev/ for generating code. -run curl http://127.0.0.1:8000/api/openapi.json > openapi.json to download the latest version from the backend. +run curl http://127.0.0.1:8000/api/openapi.json > openapi.json to download the latest version from the backend. -run -npx @hey-api/openapi-ts -i ./openapi.json -o src/client -c @hey-api/client-fetch +run +npx @hey-api/openapi-ts -i ./openapi.json -o src/client -c @hey-api/client-fetch to compile it. - -Use +Use yarn run -to run the code. Installation needs to run yarn install beforehand \ No newline at end of file +to run the code. Installation needs to run yarn install beforehand diff --git a/eslint.config.mjs b/eslint.config.mjs index 2b5389c..91ed539 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -10,33 +10,42 @@ import { FlatCompat } from "@eslint/eslintrc"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const compat = new FlatCompat({ - baseDirectory: __dirname, - recommendedConfig: js.configs.recommended, - allConfig: js.configs.all + baseDirectory: __dirname, + recommendedConfig: js.configs.recommended, + allConfig: js.configs.all, }); -export default [{ +export default [ + { ignores: ["**/dist", "**/.eslintrc.cjs"], -}, ...fixupConfigRules(compat.extends( - "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "plugin:react-hooks/recommended", -)), { + }, + ...fixupConfigRules( + compat.extends( + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:react-hooks/recommended", + ), + ), + { plugins: { - "react-refresh": reactRefresh, + "react-refresh": reactRefresh, }, languageOptions: { - globals: { - ...globals.browser, - }, + globals: { + ...globals.browser, + }, - parser: tsParser, + parser: tsParser, }, rules: { - "react-refresh/only-export-components": ["warn", { - allowConstantExport: true, - }], + "react-refresh/only-export-components": [ + "warn", + { + allowConstantExport: true, + }, + ], }, -}]; \ No newline at end of file + }, +]; diff --git a/openapi-ts.config.ts b/openapi-ts.config.ts index c2db097..cccec03 100644 --- a/openapi-ts.config.ts +++ b/openapi-ts.config.ts @@ -1,22 +1,23 @@ -import { defineConfig } from '@hey-api/openapi-ts'; +import { defineConfig } from "@hey-api/openapi-ts"; export default defineConfig({ - client: '@hey-api/client-fetch', - input: './openapi.json', + client: "@hey-api/client-fetch", + input: "./openapi.json", output: { - format: 'prettier', - path: './src/client', + format: "prettier", + lint: "eslint", + path: "./src/client", }, plugins: [ - '@hey-api/schemas', - '@hey-api/services', + "@hey-api/schemas", + "@hey-api/services", { dates: true, - name: '@hey-api/transformers', + name: "@hey-api/transformers", }, { - enums: 'javascript', - name: '@hey-api/types', + enums: "javascript", + name: "@hey-api/types", }, ], }); diff --git a/setup.txt b/setup.txt new file mode 100644 index 0000000..fcbbc76 --- /dev/null +++ b/setup.txt @@ -0,0 +1,30 @@ +src/apiClientConfig.ts +src/ErrorBoundary.tsx +src/index.css +src/index.ts +src/main.tsx +src/ThemeWrapper.tsx +src/vite-env.d.ts + +src/assets: +amiv.svg +amiv-wheel.svg +react.svg + +src/client: +index.ts +schemas.gen.ts +services.gen.ts +types.gen.ts + +src/pages: +Belegformular.tsx +Bills.tsx +CreditPaymentsList.tsx +CreditPayment.tsx +FetchData.tsx +GenerateItem.tsx +index.tsx +Reimbursement.tsx +root.tsx +test.tsx diff --git a/src/Callback.tsx b/src/Callback.tsx new file mode 100644 index 0000000..ab99a69 --- /dev/null +++ b/src/Callback.tsx @@ -0,0 +1,33 @@ +import { useEffect, useRef } from "react"; +import { useNavigate } from "react-router-dom"; + +const Callback = () => { + const navigate = useNavigate(); + const isProcessed = useRef(false); // Tracks if token processing is done + + useEffect(() => { + if (isProcessed.current) return; // Prevent multiple executions + isProcessed.current = true; // Mark as processed + + // Extract parameters from URL query string + const searchParams = new URLSearchParams(window.location.search); + //console.log('Search Params:', searchParams.toString()); + const accessToken = searchParams.get("access_token"); + const tokenType = searchParams.get("token_type"); // if needed + const scope = searchParams.get("scope"); // if needed + const state = searchParams.get("state"); // if needed + + if (accessToken) { + //console.log('Access Token:', accessToken); + localStorage.setItem("access_token", accessToken); // Store token securely + navigate("/"); // Redirect to home or dashboard + } else { + console.error("No access token found."); + navigate("/error"); // Redirect to a proper error page + } + }, [navigate]); + + return <div>Processing login...</div>; +}; + +export default Callback; diff --git a/src/ErrorBoundary.tsx b/src/ErrorBoundary.tsx index 54a2f09..4a22352 100644 --- a/src/ErrorBoundary.tsx +++ b/src/ErrorBoundary.tsx @@ -1,32 +1,38 @@ import { Button, Card, Container, Stack, Typography } from "@mui/material"; import { Link, isRouteErrorResponse, useRouteError } from "react-router-dom"; -import AmivWheelSVG from './assets/amiv-wheel.svg'; +import AmivWheelSVG from "./assets/amiv-wheel.svg"; import { Home } from "@mui/icons-material"; export default function ErrorBoundary() { - const error = useRouteError(); - let errorMessage: string; + const error = useRouteError(); + let errorMessage: string; - if (isRouteErrorResponse(error)) { - errorMessage = error.data.message || error.statusText; - } else if (error instanceof Error) { - errorMessage = error.message; - } else if (typeof error === 'string') { - errorMessage = error; - } else { - errorMessage = 'Unknown error'; - } + if (isRouteErrorResponse(error)) { + errorMessage = error.data.message || error.statusText; + } else if (error instanceof Error) { + errorMessage = error.message; + } else if (typeof error === "string") { + errorMessage = error; + } else { + errorMessage = "Unknown error"; + } - return <Container> - <Stack direction="row"> - <img src={AmivWheelSVG} alt="AMIV-Logo" height={96}/> - <Typography variant="h1" component="h1">Error</Typography> - </Stack> - <Stack spacing={2}> - <Card variant="outlined" sx={{padding: 2}}>{errorMessage}</Card> - <Button variant="outlined" component={Link} to="/" startIcon={<Home/>}> - Go home - </Button> - </Stack> + return ( + <Container> + <Stack direction="row"> + <img src={AmivWheelSVG} alt="AMIV-Logo" height={96} /> + <Typography variant="h1" component="h1"> + Error + </Typography> + </Stack> + <Stack spacing={2}> + <Card variant="outlined" sx={{ padding: 2 }}> + {errorMessage} + </Card> + <Button variant="outlined" component={Link} to="/" startIcon={<Home />}> + Go home + </Button> + </Stack> </Container> + ); } diff --git a/src/ThemeWrapper.tsx b/src/ThemeWrapper.tsx index 9a04f07..4613f83 100644 --- a/src/ThemeWrapper.tsx +++ b/src/ThemeWrapper.tsx @@ -1,35 +1,46 @@ import { ThemeProvider } from "@emotion/react"; -import { useMediaQuery, createTheme, CssBaseline, PaletteMode } from "@mui/material"; +import { + useMediaQuery, + createTheme, + CssBaseline, + PaletteMode, +} from "@mui/material"; import { indigo, red } from "@mui/material/colors"; import React from "react"; - const getDesignTokens = (mode: PaletteMode) => ({ - palette: { - mode, - ...(mode === 'light' - ? { - // light mode palette - primary: indigo, - divider: indigo[200], - } - : { - // dark mode palette - primary: red, - divider: red[500] - }) - } -}) + palette: { + mode, + ...(mode === "light" + ? { + // light mode palette + primary: indigo, + divider: indigo[200], + } + : { + // dark mode palette + primary: red, + divider: red[500], + }), + }, +}); -export default function ThemeWrapper({children}: {children: React.ReactElement}) { - const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)'); +export default function ThemeWrapper({ + children, +}: { + children: React.ReactElement; +}) { + const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)"); - const theme = React.useMemo(() => createTheme(getDesignTokens(prefersDarkMode ? 'dark' : 'light')), [prefersDarkMode]); + const theme = React.useMemo( + () => createTheme(getDesignTokens(prefersDarkMode ? "dark" : "light")), + [prefersDarkMode], + ); - return ( + return ( <ThemeProvider theme={theme}> - <CssBaseline /> - {children} + <CssBaseline /> + {children} </ThemeProvider> - ) -} \ No newline at end of file + ); +} diff --git a/src/apiClientConfig.ts b/src/apiClientConfig.ts index c22140c..a1740ec 100644 --- a/src/apiClientConfig.ts +++ b/src/apiClientConfig.ts @@ -1,15 +1,20 @@ -// src/apiClientConfig.ts -import { client } from './client/services.gen'; // Adjust this path based on where your generated client is located +import { client } from "./client/services.gen"; // Correct import path + +const getToken = () => { + // Retrieve the token from a secure place, e.g., localStorage or a secure token service + return localStorage.getItem("access_token"); +}; -// Set the base URL and headers for the client client.setConfig({ - baseUrl: import.meta.env.VITE_API_BASE_URL, // Ensure VITE_API_BASE_URL is set in your .env file + baseUrl: import.meta.env.VITE_API_BASE_URL, // This fetches the base URL from your environment variables headers: { - Authorization: `Bearer asdf`, // Adjust as needed, or use a token-fetching method + Authorization: "Bearer " + getToken(), }, }); -// Add response interceptor for logging +// + +// Response interceptor for logging client.interceptors.response.use((response) => { if (response.status === 200) { console.log(`Request to ${response.url} was successful`); diff --git a/src/client/schemas.gen.ts b/src/client/schemas.gen.ts index 9020125..818b7a5 100644 --- a/src/client/schemas.gen.ts +++ b/src/client/schemas.gen.ts @@ -45,13 +45,13 @@ export const AddressSchema = { type: "string", format: "date-time", title: "Time Create", - default: "2024-11-04T14:37:27.215210", + default: "2024-11-19T16:32:33.918524", }, time_modified: { type: "string", format: "date-time", title: "Time Modified", - default: "2024-11-04T14:37:27.215238", + default: "2024-11-19T16:32:33.918553", }, }, type: "object", @@ -240,13 +240,13 @@ export const BillSchema = { type: "string", format: "date-time", title: "Time Create", - default: "2024-11-04T14:37:27.245576", + default: "2024-11-19T16:32:33.947729", }, time_modified: { type: "string", format: "date-time", title: "Time Modified", - default: "2024-11-04T14:37:27.245603", + default: "2024-11-19T16:32:33.947759", }, ezag_timestamp: { anyOf: [ @@ -502,13 +502,13 @@ export const CreditPaymentSchema = { type: "string", format: "date-time", title: "Time Create", - default: "2024-11-04T14:37:27.255823", + default: "2024-11-19T16:32:33.957864", }, time_modified: { type: "string", format: "date-time", title: "Time Modified", - default: "2024-11-04T14:37:27.255852", + default: "2024-11-19T16:32:33.957889", }, ezag_timestamp: { anyOf: [ @@ -933,13 +933,13 @@ export const InternalTransferSchema = { type: "string", format: "date-time", title: "Time Create", - default: "2024-11-04T14:37:27.286242", + default: "2024-11-19T16:32:33.984901", }, time_modified: { type: "string", format: "date-time", title: "Time Modified", - default: "2024-11-04T14:37:27.286282", + default: "2024-11-19T16:32:33.984931", }, }, type: "object", @@ -1113,13 +1113,13 @@ export const InvoiceSchema = { type: "string", format: "date-time", title: "Time Create", - default: "2024-11-04T14:37:27.306366", + default: "2024-11-19T16:32:33.997400", }, time_modified: { type: "string", format: "date-time", title: "Time Modified", - default: "2024-11-04T14:37:27.306411", + default: "2024-11-19T16:32:33.997428", }, }, type: "object", @@ -1303,13 +1303,13 @@ export const ItemSchema = { type: "string", format: "date-time", title: "Time Create", - default: "2024-11-04T14:37:27.264983", + default: "2024-11-19T16:32:33.967028", }, time_modified: { type: "string", format: "date-time", title: "Time Modified", - default: "2024-11-04T14:37:27.265011", + default: "2024-11-19T16:32:33.967054", }, }, type: "object", @@ -1539,13 +1539,13 @@ export const KstSchema = { type: "string", format: "date-time", title: "Time Create", - default: "2024-11-04T14:37:27.224531", + default: "2024-11-19T16:32:33.927193", }, time_modified: { type: "string", format: "date-time", title: "Time Modified", - default: "2024-11-04T14:37:27.224559", + default: "2024-11-19T16:32:33.927219", }, }, type: "object", @@ -1677,13 +1677,13 @@ export const LedgerSchema = { type: "string", format: "date-time", title: "Time Create", - default: "2024-11-04T14:37:27.231377", + default: "2024-11-19T16:32:33.933799", }, time_modified: { type: "string", format: "date-time", title: "Time Modified", - default: "2024-11-04T14:37:27.231404", + default: "2024-11-19T16:32:33.933825", }, }, type: "object", @@ -1802,13 +1802,13 @@ export const ReimbursementSchema = { type: "string", format: "date-time", title: "Time Create", - default: "2024-11-04T14:37:27.319471", + default: "2024-11-19T16:32:34.007316", }, time_modified: { type: "string", format: "date-time", title: "Time Modified", - default: "2024-11-04T14:37:27.319498", + default: "2024-11-19T16:32:34.007341", }, ezag_timestamp: { anyOf: [ diff --git a/src/client/types.gen.ts b/src/client/types.gen.ts index 8574870..8a4c093 100644 --- a/src/client/types.gen.ts +++ b/src/client/types.gen.ts @@ -858,6 +858,9 @@ export type ReimbursementsReadReimbursementsError = HTTPValidationError; export type ReimbursementsCreateReimbursementData = { body: ReimbursementCreate; + query: { + session_token: string; + }; }; export type ReimbursementsCreateReimbursementResponse = @@ -869,6 +872,9 @@ export type ReimbursementsReadReimbursementData = { path: { Reimbursement_id: string; }; + query: { + session_token: string; + }; }; export type ReimbursementsReadReimbursementResponse = @@ -880,6 +886,7 @@ export type ReimbursementsUpdateReimbursementData = { body: ReimbursementPublic_Input; query: { reimbursement_id: string; + session_token: string; }; }; @@ -891,6 +898,7 @@ export type ReimbursementsUpdateReimbursementError = HTTPValidationError; export type ReimbursementsDeleteReimbursementData = { query: { reimbursement_id: string; + session_token: string; }; }; diff --git a/src/main.tsx b/src/main.tsx index 853e09f..87dd792 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,51 +1,36 @@ // main.tsx -import React from 'react' -import client from './apiClientConfig' -import ReactDOM from 'react-dom/client' +import React from "react"; +import ReactDOM from "react-dom/client"; import { createBrowserRouter, RouterProvider } from "react-router-dom"; -import Root from './pages/root'; -import ErrorBoundary from './ErrorBoundary'; -import Index from './pages/index'; -import ThemeWrapper from './ThemeWrapper'; - -import CreditPaymentsPage from './pages/CreditPaymentsList'; -import Belegformular from './pages/Belegformular'; -import GenerateItem from './pages/GenerateItem'; - +import Root from "./pages/root"; +import ErrorBoundary from "./ErrorBoundary"; +import Index from "./pages/index"; +import ThemeWrapper from "./ThemeWrapper"; +import CreditPaymentsPage from "./pages/CreditPaymentsList"; +import Belegformular from "./pages/Belegformular"; +import GenerateItem from "./pages/GenerateItem"; +import Callback from "./Callback"; const router = createBrowserRouter([ { - path: '/', + path: "/", element: <Root />, errorElement: <ErrorBoundary />, children: [ - { - index: true, - element: <Index />, - }, - { - path: 'belegformular', - element: <Belegformular />, // Add this route - - }, - { - path: 'GenerateItem', - element: <GenerateItem />, // Add this route - - }, - { - path: 'creditinvoices', - element: <CreditPaymentsPage />, // Add this route - }, + { index: true, element: <Index /> }, + { path: "belegformular", element: <Belegformular /> }, + { path: "GenerateItem", element: <GenerateItem /> }, + { path: "creditinvoices", element: <CreditPaymentsPage /> }, ], }, + { path: "callback", element: <Callback /> }, ]); -ReactDOM.createRoot(document.getElementById('root')!).render( + +ReactDOM.createRoot(document.getElementById("root")).render( <React.StrictMode> <ThemeWrapper> - <RouterProvider router={router} /> </ThemeWrapper> - </React.StrictMode> + </React.StrictMode>, ); diff --git a/src/pages/Belegformular.tsx b/src/pages/Belegformular.tsx index 8846d49..6f6b43e 100644 --- a/src/pages/Belegformular.tsx +++ b/src/pages/Belegformular.tsx @@ -1,11 +1,19 @@ -import React, { useState } from 'react'; -import { Container, FormControl, InputLabel, Select, MenuItem, Typography, SelectChangeEvent } from '@mui/material'; -import Reimbursement from './Reimbursement'; -import Bills from './Bills'; -import CreditPayment from './CreditPayment'; +import React, { useState } from "react"; +import { + Container, + FormControl, + InputLabel, + Select, + MenuItem, + Typography, + SelectChangeEvent, +} from "@mui/material"; +import Reimbursement from "./Reimbursement"; +import Bills from "./Bills"; +import CreditPayment from "./CreditPayment"; const MainForm: React.FC = () => { - const [selectedType, setSelectedType] = useState<string>(''); + const [selectedType, setSelectedType] = useState<string>(""); const handleTypeChange = (event: SelectChangeEvent<string>) => { setSelectedType(event.target.value as string); @@ -13,12 +21,12 @@ const MainForm: React.FC = () => { const renderForm = () => { switch (selectedType) { - case 'beleg': + case "beleg": return <Reimbursement />; - case 'bill': - return <Bills /> - case 'creditPayment': - return <CreditPayment /> + case "bill": + return <Bills />; + case "creditPayment": + return <CreditPayment />; default: return null; } diff --git a/src/pages/Bills.tsx b/src/pages/Bills.tsx index 481133b..0b62a9c 100644 --- a/src/pages/Bills.tsx +++ b/src/pages/Bills.tsx @@ -1,88 +1,102 @@ -import React, { useState, useEffect } from 'react'; -import { Container, CircularProgress, Typography, Select, MenuItem, FormControl, InputLabel, Stack, Button, TextField, Alert } from '@mui/material'; -import { kstsReadKsts, billsCreateBill, ledgersReadLedgers } from '../client/services.gen'; // Adjust the import path as needed -import { KstsPublic, KstPublic, BillsCreateBillData, LedgersPublic, LedgerPublic } from '../client/types.gen'; - -import { SelectChangeEvent } from '@mui/material/Select'; - -import { client } from '../client/services.gen'; - -// Configure the client -client.setConfig({ - baseUrl: import.meta.env.VITE_API_BASE_URL, - headers: { - Origin: `localhost`, - }, -}); - - - +import React, { useState, useEffect } from "react"; +import { + Container, + CircularProgress, + Typography, + Select, + MenuItem, + FormControl, + InputLabel, + Stack, + Button, + TextField, + Alert, +} from "@mui/material"; +import { + kstsReadKsts, + billsCreateBill, + ledgersReadLedgers, +} from "../client/services.gen"; // Adjust the import path as needed +import { + KstsPublic, + KstPublic, + BillsCreateBillData, + LedgersPublic, + LedgerPublic, +} from "../client/types.gen"; + +import { SelectChangeEvent } from "@mui/material/Select"; + +import client from "../apiClientConfig"; const CreateBillForm: React.FC = () => { - - const [kstItems, setKstItems] = useState<KstPublic[]>([]); - const [ledgerItems, setLedgerItems] = useState<LedgerPublic[]>([]); - const [loadingKst, setLoadingKst] = useState(true); - const [loadingLedger, setLoadingLedger] = useState(true); - const [errorKst, setErrorKst] = useState<string | null>(null); - const [errorLedger, setErrorLedger] = useState<string | null>(null); - + const [kstItems, setKstItems] = useState<KstPublic[]>([]); + const [ledgerItems, setLedgerItems] = useState<LedgerPublic[]>([]); + const [loadingKst, setLoadingKst] = useState(true); + const [loadingLedger, setLoadingLedger] = useState(true); + const [errorKst, setErrorKst] = useState<string | null>(null); + const [errorLedger, setErrorLedger] = useState<string | null>(null); const [formData, setFormData] = useState<BillsCreateBillData>({ - body:{ - creditor: { - kst_id: '', - ledger_id: '', + body: { + creditor: { + kst_id: "", + ledger_id: "", amount: 0, accounting_year: 2024, - currency: 'CHF', // Default value, adjust as needed - comment: '', - qcomment: '', - }, - address: { - name: '', - address1: '', - address2: '', - address3: '', + currency: "CHF", // Default value, adjust as needed + comment: "", + qcomment: "", + }, + address: { + name: "", + address1: "", + address2: "", + address3: "", plz: 0, - city: '', - country: '', - }, - reference: 0, - iban: '', - recipt: '', - comment: '', - } + city: "", + country: "", + }, + reference: 0, + iban: "", + recipt: "", + comment: "", + }, }); const [error, setError] = useState<string | null>(null); const [success, setSuccess] = useState<string | null>(null); - const [validationErrors, setValidationErrors] = useState<Record<string, string>>({}); - + const [validationErrors, setValidationErrors] = useState< + Record<string, string> + >({}); const fetchKsts = async () => { setErrorKst(null); setLoadingKst(true); try { + var a; const responseKst = await kstsReadKsts(); // Check if response is HTML, indicating a possible error - if (typeof responseKst.data !== 'object' || !('items' in responseKst.data)) { - console.error('Unexpected response format:', responseKst.data); - setErrorKst('Received unexpected response format from the server.'); + if ( + typeof responseKst.data !== "object" || + !("items" in responseKst.data) + ) { + console.error("Unexpected response format:", responseKst.data); + setErrorKst("Received unexpected response format from the server."); return; } const KstList = responseKst.data as KstsPublic; if (!KstList.items) { - setErrorKst('No KSTs found'); + setErrorKst("No KSTs found"); return; } setKstItems(KstList.items); } catch (err) { - setErrorKst('Failed to load KSTs'); - console.error('Error fetching KSTs:', err); + setErrorKst("Failed to load KSTs"); + console.error("Error fetching KSTs:", err); } finally { setLoadingKst(false); } @@ -96,21 +110,24 @@ const CreateBillForm: React.FC = () => { const responseLedger = await ledgersReadLedgers(); // Check if response is HTML, indicating a possible error - if (typeof responseLedger.data !== 'object' || !('items' in responseLedger.data)) { - console.error('Unexpected response format:', responseLedger.data); - setErrorLedger('Received unexpected response format from the server.'); + if ( + typeof responseLedger.data !== "object" || + !("items" in responseLedger.data) + ) { + console.error("Unexpected response format:", responseLedger.data); + setErrorLedger("Received unexpected response format from the server."); return; } const LedgerList = responseLedger.data as LedgersPublic; if (!LedgerList.items) { - setErrorLedger('No Ledgers found'); + setErrorLedger("No Ledgers found"); return; } setLedgerItems(LedgerList.items); } catch (err) { - setErrorLedger('Failed to load Ledgers'); - console.error('Error fetching Ledgers:', err); + setErrorLedger("Failed to load Ledgers"); + console.error("Error fetching Ledgers:", err); } finally { setLoadingLedger(false); } @@ -121,10 +138,12 @@ const CreateBillForm: React.FC = () => { fetchLedgers(); }, []); - const handleChange = (e: React.ChangeEvent<{ name?: string; value: unknown }>) => { + const handleChange = ( + e: React.ChangeEvent<{ name?: string; value: unknown }>, + ) => { const { name, value } = e.target as HTMLInputElement; - const keys = name.split('.'); // Handle nested keys like creditor.kst_id + const keys = name.split("."); // Handle nested keys like creditor.kst_id const updatedFormData = { ...formData }; let current = updatedFormData.body as any; // Use `any` for dynamic nested updates @@ -155,15 +174,15 @@ const CreateBillForm: React.FC = () => { const errors: Record<string, string> = {}; if (!formData.body.creditor.kst_id.trim()) { - errors['creditor.kst_id'] = 'KST-ID is required'; + errors["creditor.kst_id"] = "KST-ID is required"; isValid = false; } if (!formData.body.creditor.ledger_id.trim()) { - errors['creditor.ledger_id'] = 'Ledger-ID is required'; + errors["creditor.ledger_id"] = "Ledger-ID is required"; isValid = false; } if (!formData.body.creditor.amount || formData.body.creditor.amount <= 0) { - errors['creditor.amount'] = 'Amount must be greater than zero'; + errors["creditor.amount"] = "Amount must be greater than zero"; } // Set validation errors if any @@ -179,36 +198,36 @@ const CreateBillForm: React.FC = () => { } try { await billsCreateBill(formData); - setSuccess('Reimbursement created successfully!'); + setSuccess("Reimbursement created successfully!"); setFormData({ - body:{ - creditor: { - kst_id: '', - ledger_id: '', - amount: 0, - accounting_year: 2024, - currency: 'CHF', // Default value, adjust as needed - comment: '', - qcomment: '', - }, - address: { - name: '', - address1: '', - address2: '', - address3: '', - plz: 0, - city: '', - country: '', - }, - reference: 0, - iban: '', - recipt: '', - comment: '', - } + body: { + creditor: { + kst_id: "", + ledger_id: "", + amount: 0, + accounting_year: 2024, + currency: "CHF", // Default value, adjust as needed + comment: "", + qcomment: "", + }, + address: { + name: "", + address1: "", + address2: "", + address3: "", + plz: 0, + city: "", + country: "", + }, + reference: 0, + iban: "", + recipt: "", + comment: "", + }, }); } catch (err) { - setError('Failed to create bill.'); - console.error('Error creating bill:', err); + setError("Failed to create bill."); + console.error("Error creating bill:", err); } }; @@ -221,7 +240,11 @@ const CreateBillForm: React.FC = () => { {error && <Alert severity="error">{error}</Alert>} <Stack spacing={2} sx={{ mt: 2 }}> - <FormControl fullWidth required error={!!validationErrors['creditor.kst_id']}> + <FormControl + fullWidth + required + error={!!validationErrors["creditor.kst_id"]} + > <InputLabel id="kst-id-label">KST ID</InputLabel> <Select labelId="kst-id-label" @@ -235,11 +258,17 @@ const CreateBillForm: React.FC = () => { </MenuItem> ))} </Select> - {validationErrors['creditor.kst_id'] && ( - <Typography color="error">{validationErrors['creditor.kst_id']}</Typography> + {validationErrors["creditor.kst_id"] && ( + <Typography color="error"> + {validationErrors["creditor.kst_id"]} + </Typography> )} </FormControl> - <FormControl fullWidth required error={!!validationErrors['creditor.ledger_id']}> + <FormControl + fullWidth + required + error={!!validationErrors["creditor.ledger_id"]} + > <InputLabel id="ledger-id-label">Ledger ID</InputLabel> <Select labelId="ledger-id-label" @@ -253,8 +282,10 @@ const CreateBillForm: React.FC = () => { </MenuItem> ))} </Select> - {validationErrors['creditor.ledger_id'] && ( - <Typography color="error">{validationErrors['creditor.ledger_id']}</Typography> + {validationErrors["creditor.ledger_id"] && ( + <Typography color="error"> + {validationErrors["creditor.ledger_id"]} + </Typography> )} </FormControl> <TextField @@ -265,8 +296,8 @@ const CreateBillForm: React.FC = () => { onChange={handleChange} fullWidth required - error={!!validationErrors['creditor.amount']} - helperText={validationErrors['creditor.amount']} + error={!!validationErrors["creditor.amount"]} + helperText={validationErrors["creditor.amount"]} /> <TextField label="Accounting Year" @@ -276,8 +307,8 @@ const CreateBillForm: React.FC = () => { onChange={handleChange} fullWidth required - error={!!validationErrors['creditor.accounting_year']} - helperText={validationErrors['creditor.accounting_year']} + error={!!validationErrors["creditor.accounting_year"]} + helperText={validationErrors["creditor.accounting_year"]} /> <TextField label="Currency" @@ -286,8 +317,8 @@ const CreateBillForm: React.FC = () => { onChange={handleChange} fullWidth required - error={!!validationErrors['creditor.currency']} - helperText={validationErrors['creditor.currency']} + error={!!validationErrors["creditor.currency"]} + helperText={validationErrors["creditor.currency"]} /> <TextField label="Comment" @@ -295,8 +326,8 @@ const CreateBillForm: React.FC = () => { value={formData.body.creditor.comment} onChange={handleChange} fullWidth - error={!!validationErrors['creditor.comment']} - helperText={validationErrors['creditor.comment']} + error={!!validationErrors["creditor.comment"]} + helperText={validationErrors["creditor.comment"]} /> <TextField label="QComment" @@ -387,8 +418,8 @@ const CreateBillForm: React.FC = () => { onChange={handleChange} fullWidth required - error={!!validationErrors['recipt']} - helperText={validationErrors['recipt']} + error={!!validationErrors["recipt"]} + helperText={validationErrors["recipt"]} /> <TextField label="Comment" @@ -397,8 +428,8 @@ const CreateBillForm: React.FC = () => { onChange={handleChange} fullWidth required - error={!!validationErrors['recipient']} - helperText={validationErrors['recipient']} + error={!!validationErrors["recipient"]} + helperText={validationErrors["recipient"]} /> <Button onClick={handleSubmit} variant="contained" color="primary"> Create Bill @@ -408,4 +439,4 @@ const CreateBillForm: React.FC = () => { ); }; -export default CreateBillForm; \ No newline at end of file +export default CreateBillForm; diff --git a/src/pages/CreditPayment.tsx b/src/pages/CreditPayment.tsx index a4862f2..f425720 100644 --- a/src/pages/CreditPayment.tsx +++ b/src/pages/CreditPayment.tsx @@ -1,18 +1,32 @@ -import React, { useState, useEffect } from 'react'; -import { Container, CircularProgress, Typography, Select, MenuItem, FormControl, InputLabel, Stack, Button, TextField, Alert } from '@mui/material'; -import { kstsReadKsts, creditPaymentsCreateCreditPayment, ledgersReadLedgers } from '../client/services.gen'; -import { KstsPublic, KstPublic, CreditPaymentsCreateCreditPaymentData, LedgersPublic, LedgerPublic } from '../client/types.gen'; -import { SelectChangeEvent } from '@mui/material/Select'; +import React, { useState, useEffect } from "react"; +import { + Container, + CircularProgress, + Typography, + Select, + MenuItem, + FormControl, + InputLabel, + Stack, + Button, + TextField, + Alert, +} from "@mui/material"; +import { + kstsReadKsts, + creditPaymentsCreateCreditPayment, + ledgersReadLedgers, +} from "../client/services.gen"; +import { + KstsPublic, + KstPublic, + CreditPaymentsCreateCreditPaymentData, + LedgersPublic, + LedgerPublic, +} from "../client/types.gen"; +import { SelectChangeEvent } from "@mui/material/Select"; -import { client } from '../client/services.gen'; - -// Configure the client -client.setConfig({ - baseUrl: import.meta.env.VITE_API_BASE_URL, - headers: { - Origin: `localhost`, - }, -}); +import client from "../apiClientConfig"; const CreditPayment: React.FC = () => { const [kstItems, setKstItems] = useState<KstPublic[]>([]); @@ -22,26 +36,29 @@ const CreditPayment: React.FC = () => { const [errorKst, setErrorKst] = useState<string | null>(null); const [errorLedger, setErrorLedger] = useState<string | null>(null); - const [formData, setFormData] = useState<CreditPaymentsCreateCreditPaymentData>({ - body: { - creditor: { - kst_id: '', - ledger_id: '', - amount: 0, - accounting_year: 2024, - currency: 'CHF', - comment: '', - qcomment: '', + const [formData, setFormData] = + useState<CreditPaymentsCreateCreditPaymentData>({ + body: { + creditor: { + kst_id: "", + ledger_id: "", + amount: 0, + accounting_year: 2024, + currency: "CHF", + comment: "", + qcomment: "", + }, + recipt: "", + card: "President", + creator: "", }, - recipt: '', - card: 'President', - creator: '', - }, - }); + }); const [error, setError] = useState<string | null>(null); const [success, setSuccess] = useState<string | null>(null); - const [validationErrors, setValidationErrors] = useState<Record<string, string>>({}); + const [validationErrors, setValidationErrors] = useState< + Record<string, string> + >({}); const fetchKsts = async () => { setErrorKst(null); @@ -51,21 +68,24 @@ const CreditPayment: React.FC = () => { const responseKst = await kstsReadKsts(); // Check if response is HTML, indicating a possible error - if (typeof responseKst.data !== 'object' || !('items' in responseKst.data)) { - console.error('Unexpected response format:', responseKst.data); - setErrorKst('Received unexpected response format from the server.'); + if ( + typeof responseKst.data !== "object" || + !("items" in responseKst.data) + ) { + console.error("Unexpected response format:", responseKst.data); + setErrorKst("Received unexpected response format from the server."); return; } const KstList = responseKst.data as KstsPublic; if (!KstList.items) { - setErrorKst('No KSTs found'); + setErrorKst("No KSTs found"); return; } setKstItems(KstList.items); } catch (err) { - setErrorKst('Failed to load KSTs'); - console.error('Error fetching KSTs:', err); + setErrorKst("Failed to load KSTs"); + console.error("Error fetching KSTs:", err); } finally { setLoadingKst(false); } @@ -79,21 +99,24 @@ const CreditPayment: React.FC = () => { const responseLedger = await ledgersReadLedgers(); // Check if response is HTML, indicating a possible error - if (typeof responseLedger.data !== 'object' || !('items' in responseLedger.data)) { - console.error('Unexpected response format:', responseLedger.data); - setErrorLedger('Received unexpected response format from the server.'); + if ( + typeof responseLedger.data !== "object" || + !("items" in responseLedger.data) + ) { + console.error("Unexpected response format:", responseLedger.data); + setErrorLedger("Received unexpected response format from the server."); return; } const LedgerList = responseLedger.data as LedgersPublic; if (!LedgerList.items) { - setErrorLedger('No Ledgers found'); + setErrorLedger("No Ledgers found"); return; } setLedgerItems(LedgerList.items); } catch (err) { - setErrorLedger('Failed to load Ledgers'); - console.error('Error fetching Ledgers:', err); + setErrorLedger("Failed to load Ledgers"); + console.error("Error fetching Ledgers:", err); } finally { setLoadingLedger(false); } @@ -104,10 +127,12 @@ const CreditPayment: React.FC = () => { fetchLedgers(); }, []); - const handleChange = (e: React.ChangeEvent<{ name?: string; value: unknown }>) => { + const handleChange = ( + e: React.ChangeEvent<{ name?: string; value: unknown }>, + ) => { const { name, value } = e.target as HTMLInputElement; - const keys = name.split('.'); // Handle nested keys like creditor.kst_id + const keys = name.split("."); // Handle nested keys like creditor.kst_id const updatedFormData = { ...formData }; let current = updatedFormData.body as any; // Use `any` for dynamic nested updates @@ -138,15 +163,15 @@ const CreditPayment: React.FC = () => { const errors: Record<string, string> = {}; if (!formData.body.creditor.kst_id.trim()) { - errors['creditor.kst_id'] = 'KST-ID is required'; + errors["creditor.kst_id"] = "KST-ID is required"; isValid = false; } if (!formData.body.creditor.ledger_id.trim()) { - errors['creditor.ledger_id'] = 'Ledger-ID is required'; + errors["creditor.ledger_id"] = "Ledger-ID is required"; isValid = false; } if (!formData.body.creditor.amount || formData.body.creditor.amount <= 0) { - errors['creditor.amount'] = 'Amount must be greater than zero'; + errors["creditor.amount"] = "Amount must be greater than zero"; } // Set validation errors if any @@ -162,26 +187,26 @@ const CreditPayment: React.FC = () => { } try { await creditPaymentsCreateCreditPayment(formData); - setSuccess('CreditPayment created successfully!'); + setSuccess("CreditPayment created successfully!"); setFormData({ body: { creditor: { - kst_id: '', - ledger_id: '', + kst_id: "", + ledger_id: "", amount: 0, accounting_year: 2024, - currency: 'CHF', - comment: '', - qcomment: '', + currency: "CHF", + comment: "", + qcomment: "", }, - recipt: '', - card: 'President', - creator: '', + recipt: "", + card: "President", + creator: "", }, }); } catch (err) { - setError('Failed to create reimbursement.'); - console.error('Error creating reimbursement:', err); + setError("Failed to create reimbursement."); + console.error("Error creating reimbursement:", err); } }; @@ -195,7 +220,11 @@ const CreditPayment: React.FC = () => { {error && <Alert severity="error">{error}</Alert>} <Stack spacing={2} sx={{ mt: 2 }}> - <FormControl fullWidth required error={!!validationErrors['creditor.kst_id']}> + <FormControl + fullWidth + required + error={!!validationErrors["creditor.kst_id"]} + > <InputLabel id="kst-id-label">KST ID</InputLabel> <Select labelId="kst-id-label" @@ -209,11 +238,17 @@ const CreditPayment: React.FC = () => { </MenuItem> ))} </Select> - {validationErrors['creditor.kst_id'] && ( - <Typography color="error">{validationErrors['creditor.kst_id']}</Typography> + {validationErrors["creditor.kst_id"] && ( + <Typography color="error"> + {validationErrors["creditor.kst_id"]} + </Typography> )} </FormControl> - <FormControl fullWidth required error={!!validationErrors['creditor.ledger_id']}> + <FormControl + fullWidth + required + error={!!validationErrors["creditor.ledger_id"]} + > <InputLabel id="ledger-id-label">Ledger ID</InputLabel> <Select labelId="ledger-id-label" @@ -227,8 +262,10 @@ const CreditPayment: React.FC = () => { </MenuItem> ))} </Select> - {validationErrors['creditor.ledger_id'] && ( - <Typography color="error">{validationErrors['creditor.ledger_id']}</Typography> + {validationErrors["creditor.ledger_id"] && ( + <Typography color="error"> + {validationErrors["creditor.ledger_id"]} + </Typography> )} </FormControl> <TextField @@ -239,8 +276,8 @@ const CreditPayment: React.FC = () => { onChange={handleChange} fullWidth required - error={!!validationErrors['creditor.amount']} - helperText={validationErrors['creditor.amount']} + error={!!validationErrors["creditor.amount"]} + helperText={validationErrors["creditor.amount"]} /> <TextField label="Accounting Year" @@ -250,8 +287,8 @@ const CreditPayment: React.FC = () => { onChange={handleChange} fullWidth required - error={!!validationErrors['creditor.accounting_year']} - helperText={validationErrors['creditor.accounting_year']} + error={!!validationErrors["creditor.accounting_year"]} + helperText={validationErrors["creditor.accounting_year"]} /> <TextField label="Currency" @@ -260,8 +297,8 @@ const CreditPayment: React.FC = () => { onChange={handleChange} fullWidth required - error={!!validationErrors['creditor.currency']} - helperText={validationErrors['creditor.currency']} + error={!!validationErrors["creditor.currency"]} + helperText={validationErrors["creditor.currency"]} /> <TextField label="Comment" @@ -269,8 +306,8 @@ const CreditPayment: React.FC = () => { value={formData.body.creditor.comment} onChange={handleChange} fullWidth - error={!!validationErrors['creditor.comment']} - helperText={validationErrors['creditor.comment']} + error={!!validationErrors["creditor.comment"]} + helperText={validationErrors["creditor.comment"]} /> <TextField label="QComment" @@ -278,8 +315,8 @@ const CreditPayment: React.FC = () => { value={formData.body.creditor.qcomment} onChange={handleChange} fullWidth - error={!!validationErrors['creditor.qcomment']} - helperText={validationErrors['creditor.qcomment']} + error={!!validationErrors["creditor.qcomment"]} + helperText={validationErrors["creditor.qcomment"]} /> <TextField label="Recipt" @@ -288,8 +325,8 @@ const CreditPayment: React.FC = () => { onChange={handleChange} fullWidth required - error={!!validationErrors['recipt']} - helperText={validationErrors['recipt']} + error={!!validationErrors["recipt"]} + helperText={validationErrors["recipt"]} /> <TextField label="Creator" @@ -298,8 +335,8 @@ const CreditPayment: React.FC = () => { onChange={handleChange} fullWidth required - error={!!validationErrors['creator']} - helperText={validationErrors['creator']} + error={!!validationErrors["creator"]} + helperText={validationErrors["creator"]} /> <TextField label="Recipient" @@ -308,8 +345,8 @@ const CreditPayment: React.FC = () => { onChange={handleChange} fullWidth required - error={!!validationErrors['recipient']} - helperText={validationErrors['recipient']} + error={!!validationErrors["recipient"]} + helperText={validationErrors["recipient"]} /> <Button onClick={handleSubmit} variant="contained" color="primary"> Create CreditPayment @@ -319,4 +356,4 @@ const CreditPayment: React.FC = () => { ); }; -export default CreditPayment; \ No newline at end of file +export default CreditPayment; diff --git a/src/pages/CreditPaymentsList.tsx b/src/pages/CreditPaymentsList.tsx index 0a95449..2d111b9 100644 --- a/src/pages/CreditPaymentsList.tsx +++ b/src/pages/CreditPaymentsList.tsx @@ -1,20 +1,50 @@ -import React, { useState, useEffect } from 'react'; -import { Container, CircularProgress, Typography, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper, Alert, TextField, FormControl, InputLabel, Select, MenuItem } from '@mui/material'; -import { billsReadBills, creditPaymentsReadCreditPayments, reimbursementsReadReimbursements, client, ledgersReadLedgers, kstsReadKsts } from '../client/services.gen'; -import { BillsList, BillPublic_Output, CreditPaymentPublic_Output, CreditPaymentsList, ReimbursementsList, ReimbursementPublic_Output, LedgerPublic, KstPublic } from '../client/types.gen'; -import { SelectChangeEvent } from '@mui/material/Select'; -import { useFetchBills } from '../pages/FetchData.tsx' -// Set the base URL and headers for the client -client.setConfig({ - baseUrl: import.meta.env.VITE_API_BASE_URL, - headers: { - Origin: `localhost`, - }, -}); +import React, { useState, useEffect } from "react"; +import { + Container, + CircularProgress, + Typography, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Paper, + Alert, + TextField, + FormControl, + InputLabel, + Select, + MenuItem, +} from "@mui/material"; +import { + billsReadBills, + creditPaymentsReadCreditPayments, + reimbursementsReadReimbursements, + client, + ledgersReadLedgers, + kstsReadKsts, +} from "../client/services.gen"; +import { + BillsList, + BillPublic_Output, + CreditPaymentPublic_Output, + CreditPaymentsList, + ReimbursementsList, + ReimbursementPublic_Output, + LedgerPublic, + KstPublic, +} from "../client/types.gen"; +import { SelectChangeEvent } from "@mui/material/Select"; +import { useFetchBills } from "../pages/FetchData.tsx"; export default function CreditPaymentsPage() { - const [creditPayments, setCreditPayments] = useState<CreditPaymentPublic_Output[]>([]); - const [reimbursements, setReimbursements] = useState<ReimbursementPublic_Output[]>([]); + const [creditPayments, setCreditPayments] = useState< + CreditPaymentPublic_Output[] + >([]); + const [reimbursements, setReimbursements] = useState< + ReimbursementPublic_Output[] + >([]); const [originalPayments, setOriginalPayments] = useState([]); const [filteredPayments, setFilteredPayments] = useState<any[]>([]); const [ledgerItems, setLedgerItems] = useState<LedgerPublic[]>([]); @@ -27,30 +57,28 @@ export default function CreditPaymentsPage() { const [errorKst, setErrorKst] = useState<string | null>(null); const [filters, setFilters] = useState({ - kstId: '', - ledgerId: '', - amount: '', + kstId: "", + ledgerId: "", + amount: "", }); - - const fetchCreditPayments = async () => { setLoading(true); setError(null); try { const response = await creditPaymentsReadCreditPayments(); - if (typeof response.data !== 'object' || !('items' in response.data)) { - console.error('Unexpected response format:', response.data); - setError('Received unexpected response format from the server.'); + if (typeof response.data !== "object" || !("items" in response.data)) { + console.error("Unexpected response format:", response.data); + setError("Received unexpected response format from the server."); return; } const creditPaymentsList = response.data as CreditPaymentsList; setCreditPayments(creditPaymentsList.items); } catch (err) { - console.error('Error fetching credit payments:', err); - setError('Error fetching credit payments'); + console.error("Error fetching credit payments:", err); + setError("Error fetching credit payments"); } finally { setLoading(false); } @@ -62,17 +90,17 @@ export default function CreditPaymentsPage() { try { const response = await reimbursementsReadReimbursements(); - if (typeof response.data !== 'object' || !('items' in response.data)) { - console.error('Unexpected response format:', response.data); - setError('Received unexpected response format from the server.'); + if (typeof response.data !== "object" || !("items" in response.data)) { + console.error("Unexpected response format:", response.data); + setError("Received unexpected response format from the server."); return; } const reimbursementsList = response.data as ReimbursementsList; setReimbursements(reimbursementsList.items); } catch (err) { - console.error('Error fetching reimbursements:', err); - setError('Error fetching reimbursements'); + console.error("Error fetching reimbursements:", err); + setError("Error fetching reimbursements"); } finally { setLoading(false); } @@ -84,17 +112,17 @@ export default function CreditPaymentsPage() { try { const response = await ledgersReadLedgers(); - if (typeof response.data !== 'object' || !('items' in response.data)) { - console.error('Unexpected response format:', response.data); - setErrorLedger('Received unexpected response format from the server.'); + if (typeof response.data !== "object" || !("items" in response.data)) { + console.error("Unexpected response format:", response.data); + setErrorLedger("Received unexpected response format from the server."); return; } const ledgersList = response.data as { items: LedgerPublic[] }; setLedgerItems(ledgersList.items); } catch (err) { - setErrorLedger('Failed to load ledgers'); - console.error('Error fetching ledgers:', err); + setErrorLedger("Failed to load ledgers"); + console.error("Error fetching ledgers:", err); } finally { setLoadingLedger(false); } @@ -106,17 +134,17 @@ export default function CreditPaymentsPage() { try { const response = await kstsReadKsts(); - if (typeof response.data !== 'object' || !('items' in response.data)) { - console.error('Unexpected response format:', response.data); - setErrorKst('Received unexpected response format from the server.'); + if (typeof response.data !== "object" || !("items" in response.data)) { + console.error("Unexpected response format:", response.data); + setErrorKst("Received unexpected response format from the server."); return; } const KstList = response.data as { items: KstPublic[] }; setKstItems(KstList.items); } catch (err) { - setErrorKst('Failed to load KSTs'); - console.error('Error fetching KSTs:', err); + setErrorKst("Failed to load KSTs"); + console.error("Error fetching KSTs:", err); } finally { setLoadingKst(false); } @@ -124,20 +152,20 @@ export default function CreditPaymentsPage() { const mergeData = () => { const merged = [ - ...bills.map(item => ({ + ...bills.map((item) => ({ ...item, - type: 'bill', + type: "bill", })), - ...creditPayments.map(item => ({ + ...creditPayments.map((item) => ({ ...item, - type: 'Credit Payment', + type: "Credit Payment", })), - ...reimbursements.map(item => ({ + ...reimbursements.map((item) => ({ ...item, - type: 'Reimbursement', + type: "Reimbursement", })), ]; - console.log('Merged Data:', merged); + console.log("Merged Data:", merged); setOriginalPayments(merged); setFilteredPayments(merged); }; @@ -146,19 +174,25 @@ export default function CreditPaymentsPage() { let filtered = [...originalPayments]; if (filters.kstId) { - filtered = filtered.filter(item => item.creditor?.kst_id === filters.kstId); + filtered = filtered.filter( + (item) => item.creditor?.kst_id === filters.kstId, + ); } if (filters.ledgerId) { - filtered = filtered.filter(item => item.creditor?.ledger_id === filters.ledgerId); + filtered = filtered.filter( + (item) => item.creditor?.ledger_id === filters.ledgerId, + ); } if (filters.amount) { const amountFilter = parseFloat(filters.amount); - filtered = filtered.filter(item => - item.creditor?.amount && parseFloat(item.creditor.amount) === amountFilter + filtered = filtered.filter( + (item) => + item.creditor?.amount && + parseFloat(item.creditor.amount) === amountFilter, ); } - console.log('Filtered Payments After Filtering:', filtered); + console.log("Filtered Payments After Filtering:", filtered); setFilteredPayments(filtered); }; const { bills, loadingBill, errorBill } = useFetchBills(); @@ -170,7 +204,11 @@ export default function CreditPaymentsPage() { }, []); useEffect(() => { - if (bills.length > 0 && creditPayments.length > 0 && reimbursements.length > 0) { + if ( + bills.length > 0 && + creditPayments.length > 0 && + reimbursements.length > 0 + ) { mergeData(); } }, [bills, creditPayments, reimbursements]); @@ -187,7 +225,9 @@ export default function CreditPaymentsPage() { }); }; - const handleTextFieldChange = (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => { + const handleTextFieldChange = ( + event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>, + ) => { const { name, value } = event.target; setFilters({ ...filters, @@ -196,13 +236,13 @@ export default function CreditPaymentsPage() { }; const getLedgerName = (ledgerId: string) => { - const ledger = ledgerItems.find(item => item.id === ledgerId); - return ledger ? ledger.namede : '-'; + const ledger = ledgerItems.find((item) => item.id === ledgerId); + return ledger ? ledger.namede : "-"; }; const getKstName = (kstId: string) => { - const kst = kstItems.find(item => item.id === kstId); - return kst ? kst.name_de : '-'; + const kst = kstItems.find((item) => item.id === kstId); + return kst ? kst.name_de : "-"; }; return ( @@ -274,31 +314,37 @@ export default function CreditPaymentsPage() { <TableCell>Accounting Year</TableCell> <TableCell>Comment</TableCell> <TableCell>QComment</TableCell> - <TableCell>Creator</TableCell> - <TableCell>Recipient</TableCell> - <TableCell>Card</TableCell> + <TableCell>Creator</TableCell> + <TableCell>Recipient</TableCell> + <TableCell>Card</TableCell> + </TableRow> + </TableHead> + <TableBody> + {filteredPayments.map((payment) => ( + <TableRow key={payment.id}> + <TableCell>{payment.type}</TableCell> {/* Display the type */} + <TableCell> + {getLedgerName(payment.creditor?.ledger_id || "-")} + </TableCell> + <TableCell> + {getKstName(payment.creditor?.kst_id || "-")} + </TableCell> + <TableCell>{payment.creditor?.amount || "-"}</TableCell> + <TableCell> + {payment.creditor?.accounting_year || "-"} + </TableCell> + <TableCell>{payment.creditor?.comment || "-"}</TableCell> + <TableCell>{payment.creditor?.qcomment || "-"}</TableCell> + <TableCell>{payment.creator || "-"}</TableCell> + <TableCell>{payment.recipient || "-"}</TableCell> + <TableCell>{payment.card || "-"}</TableCell>{" "} + {/* Only for creditPayments */} </TableRow> - </TableHead> - <TableBody> - {filteredPayments.map(payment => ( - <TableRow key={payment.id}> - <TableCell>{payment.type}</TableCell> {/* Display the type */} - <TableCell>{getLedgerName(payment.creditor?.ledger_id || '-')}</TableCell> - <TableCell>{getKstName(payment.creditor?.kst_id || '-')}</TableCell> - <TableCell>{payment.creditor?.amount || '-'}</TableCell> - <TableCell>{payment.creditor?.accounting_year || '-'}</TableCell> - <TableCell>{payment.creditor?.comment || '-'}</TableCell> - <TableCell>{payment.creditor?.qcomment || '-'}</TableCell> - <TableCell>{payment.creator || '-'}</TableCell> - <TableCell>{payment.recipient || '-'}</TableCell> - <TableCell>{payment.card || '-'}</TableCell> {/* Only for creditPayments */} - </TableRow> - ))} - </TableBody> - - </Table> - </TableContainer> + ))} + </TableBody> + </Table> + </TableContainer> )} </Container> ); -} \ No newline at end of file +} diff --git a/src/pages/FetchData.tsx b/src/pages/FetchData.tsx index 76a02ea..aadb7ba 100644 --- a/src/pages/FetchData.tsx +++ b/src/pages/FetchData.tsx @@ -1,6 +1,9 @@ -import { useState, useEffect } from "react"; +import React, { useState, useEffect } from "react"; +import client from "../apiClientConfig"; // Ensure correct path import { billsReadBills } from "../client/services.gen"; import { BillsList, BillPublic_Output } from "../client/types.gen"; +//this line is needed , no idea why, it doens't change anything. using it once is enough. +client.getConfig(); export function useFetchBills() { const [bills, setBills] = useState<BillPublic_Output[]>([]); @@ -13,6 +16,7 @@ export function useFetchBills() { setError(null); try { + // Using the `billsReadBills` function which should be part of the generated client const response = await billsReadBills(); if (typeof response.data !== "object" || !("items" in response.data)) { diff --git a/src/pages/GenerateItem.tsx b/src/pages/GenerateItem.tsx index efc9616..e25ecc9 100644 --- a/src/pages/GenerateItem.tsx +++ b/src/pages/GenerateItem.tsx @@ -1,36 +1,37 @@ -import React, { useState } from 'react'; -import { Container, CircularProgress, Typography, Stack, Button, TextField, Alert } from '@mui/material'; -import { itemsCreateItem } from '../client/services.gen'; -import { ItemsCreateItemData } from '../client/types.gen'; - -import { client } from '../client/services.gen'; - -// Configure the client -client.setConfig({ - baseUrl: import.meta.env.VITE_API_BASE_URL, - headers: { - Origin: `localhost`, - }, -}); +import React, { useState } from "react"; +import { + Container, + CircularProgress, + Typography, + Stack, + Button, + TextField, + Alert, +} from "@mui/material"; +import { itemsCreateItem } from "../client/services.gen"; +import { ItemsCreateItemData } from "../client/types.gen"; + +import client from "../apiClientConfig"; export default function GenerateItem() { - // State to hold form values const [formData, setFormData] = useState<ItemsCreateItemData>({ body: { - title_de: '', - description_de: '', - title_en: '', - description_en: '', + title_de: "", + description_de: "", + title_en: "", + description_en: "", price: 0, - unit: '', - active: true - } + unit: "", + active: true, + }, }); const [error, setError] = useState<string | null>(null); const [success, setSuccess] = useState<string | null>(null); const [loading, setLoading] = useState<boolean>(false); - const [validationErrors, setValidationErrors] = useState<Record<string, string>>({}); + const [validationErrors, setValidationErrors] = useState< + Record<string, string> + >({}); // Handle change to update form data const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { @@ -52,23 +53,23 @@ export default function GenerateItem() { // Check if all required fields are filled in if (!formData.body.title_de.trim()) { - errors.title_de = 'Titel Deutsch is required'; + errors.title_de = "Titel Deutsch is required"; isValid = false; } if (!formData.body.title_en.trim()) { - errors.title_en = 'Titel Englisch is required'; + errors.title_en = "Titel Englisch is required"; isValid = false; } if (!formData.body.price || formData.body.price <= 0) { - errors.price = 'Price must be greater than zero'; + errors.price = "Price must be greater than zero"; isValid = false; } if (!formData.body.description_de.trim()) { - errors.description_de = 'Beschreibung Deutsch is required'; + errors.description_de = "Beschreibung Deutsch is required"; isValid = false; } if (!formData.body.description_en.trim()) { - errors.description_en = 'Beschreibung Englisch is required'; + errors.description_en = "Beschreibung Englisch is required"; isValid = false; } @@ -90,23 +91,23 @@ export default function GenerateItem() { try { // Call API to create a new item await itemsCreateItem(formData); - setSuccess('Item created successfully!'); - + setSuccess("Item created successfully!"); + // Reset form data after successful submission setFormData({ body: { - title_de: '', - description_de: '', - title_en: '', - description_en: '', + title_de: "", + description_de: "", + title_en: "", + description_en: "", price: 0, - unit: '', - active: true - } + unit: "", + active: true, + }, }); } catch (err) { - setError('Failed to create New Item.'); - console.error('Error creating new Item:', err); + setError("Failed to create New Item."); + console.error("Error creating new Item:", err); } finally { setLoading(false); // Stop loading } @@ -118,7 +119,8 @@ export default function GenerateItem() { Create new Invoice Item </Typography> <Typography variant="body1" gutterBottom> - Use this form to create a new item for invoices. Enter all requested Data. + Use this form to create a new item for invoices. Enter all requested + Data. </Typography> {/* Display Success or Error messages */} @@ -185,7 +187,7 @@ export default function GenerateItem() { onClick={handleSubmit} disabled={loading} // Disable button while loading > - {loading ? <CircularProgress size={24} /> : 'Submit Payment'} + {loading ? <CircularProgress size={24} /> : "Submit Payment"} </Button> </Stack> </Container> diff --git a/src/pages/Reimbursement.tsx b/src/pages/Reimbursement.tsx index c75ee7b..5a307c6 100644 --- a/src/pages/Reimbursement.tsx +++ b/src/pages/Reimbursement.tsx @@ -1,18 +1,32 @@ -import React, { useState, useEffect } from 'react'; -import { Container, CircularProgress, Typography, Select, MenuItem, FormControl, InputLabel, Stack, Button, TextField, Alert } from '@mui/material'; -import { kstsReadKsts, reimbursementsCreateReimbursement, ledgersReadLedgers } from '../client/services.gen'; -import { KstsPublic, KstPublic, ReimbursementsCreateReimbursementData, LedgersPublic, LedgerPublic } from '../client/types.gen'; -import { SelectChangeEvent } from '@mui/material/Select'; +import React, { useState, useEffect } from "react"; +import { + Container, + CircularProgress, + Typography, + Select, + MenuItem, + FormControl, + InputLabel, + Stack, + Button, + TextField, + Alert, +} from "@mui/material"; +import { + kstsReadKsts, + reimbursementsCreateReimbursement, + ledgersReadLedgers, +} from "../client/services.gen"; +import { + KstsPublic, + KstPublic, + ReimbursementsCreateReimbursementData, + LedgersPublic, + LedgerPublic, +} from "../client/types.gen"; +import { SelectChangeEvent } from "@mui/material/Select"; -import { client } from '../client/services.gen'; - -// Configure the client -client.setConfig({ - baseUrl: import.meta.env.VITE_API_BASE_URL, - headers: { - Origin: `localhost`, - }, -}); +import client from "../apiClientConfig"; const Reimbursement: React.FC = () => { const [kstItems, setKstItems] = useState<KstPublic[]>([]); @@ -22,26 +36,29 @@ const Reimbursement: React.FC = () => { const [errorKst, setErrorKst] = useState<string | null>(null); const [errorLedger, setErrorLedger] = useState<string | null>(null); - const [formData, setFormData] = useState<ReimbursementsCreateReimbursementData>({ - body: { - creditor: { - kst_id: '', - ledger_id: '', - amount: 0, - accounting_year: 2024, - currency: 'CHF', - comment: '', - qcomment: '', + const [formData, setFormData] = + useState<ReimbursementsCreateReimbursementData>({ + body: { + creditor: { + kst_id: "", + ledger_id: "", + amount: 0, + accounting_year: 2024, + currency: "CHF", + comment: "", + qcomment: "", + }, + recipt: "", + creator: "", + recipient: "", }, - recipt: '', - creator: '', - recipient: '', - }, - }); + }); const [error, setError] = useState<string | null>(null); const [success, setSuccess] = useState<string | null>(null); - const [validationErrors, setValidationErrors] = useState<Record<string, string>>({}); + const [validationErrors, setValidationErrors] = useState< + Record<string, string> + >({}); const fetchKsts = async () => { setErrorKst(null); @@ -51,21 +68,24 @@ const Reimbursement: React.FC = () => { const responseKst = await kstsReadKsts(); // Check if response is HTML, indicating a possible error - if (typeof responseKst.data !== 'object' || !('items' in responseKst.data)) { - console.error('Unexpected response format:', responseKst.data); - setErrorKst('Received unexpected response format from the server.'); + if ( + typeof responseKst.data !== "object" || + !("items" in responseKst.data) + ) { + console.error("Unexpected response format:", responseKst.data); + setErrorKst("Received unexpected response format from the server."); return; } const KstList = responseKst.data as KstsPublic; if (!KstList.items) { - setErrorKst('No KSTs found'); + setErrorKst("No KSTs found"); return; } setKstItems(KstList.items); } catch (err) { - setErrorKst('Failed to load KSTs'); - console.error('Error fetching KSTs:', err); + setErrorKst("Failed to load KSTs"); + console.error("Error fetching KSTs:", err); } finally { setLoadingKst(false); } @@ -79,21 +99,24 @@ const Reimbursement: React.FC = () => { const responseLedger = await ledgersReadLedgers(); // Check if response is HTML, indicating a possible error - if (typeof responseLedger.data !== 'object' || !('items' in responseLedger.data)) { - console.error('Unexpected response format:', responseLedger.data); - setErrorLedger('Received unexpected response format from the server.'); + if ( + typeof responseLedger.data !== "object" || + !("items" in responseLedger.data) + ) { + console.error("Unexpected response format:", responseLedger.data); + setErrorLedger("Received unexpected response format from the server."); return; } const LedgerList = responseLedger.data as LedgersPublic; if (!LedgerList.items) { - setErrorLedger('No Ledgers found'); + setErrorLedger("No Ledgers found"); return; } setLedgerItems(LedgerList.items); } catch (err) { - setErrorLedger('Failed to load Ledgers'); - console.error('Error fetching Ledgers:', err); + setErrorLedger("Failed to load Ledgers"); + console.error("Error fetching Ledgers:", err); } finally { setLoadingLedger(false); } @@ -104,10 +127,12 @@ const Reimbursement: React.FC = () => { fetchLedgers(); }, []); - const handleChange = (e: React.ChangeEvent<{ name?: string; value: unknown }>) => { + const handleChange = ( + e: React.ChangeEvent<{ name?: string; value: unknown }>, + ) => { const { name, value } = e.target as HTMLInputElement; - const keys = name.split('.'); // Handle nested keys like creditor.kst_id + const keys = name.split("."); // Handle nested keys like creditor.kst_id const updatedFormData = { ...formData }; let current = updatedFormData.body as any; // Use `any` for dynamic nested updates @@ -138,15 +163,15 @@ const Reimbursement: React.FC = () => { const errors: Record<string, string> = {}; if (!formData.body.creditor.kst_id.trim()) { - errors['creditor.kst_id'] = 'KST-ID is required'; + errors["creditor.kst_id"] = "KST-ID is required"; isValid = false; } if (!formData.body.creditor.ledger_id.trim()) { - errors['creditor.ledger_id'] = 'Ledger-ID is required'; + errors["creditor.ledger_id"] = "Ledger-ID is required"; isValid = false; } if (!formData.body.creditor.amount || formData.body.creditor.amount <= 0) { - errors['creditor.amount'] = 'Amount must be greater than zero'; + errors["creditor.amount"] = "Amount must be greater than zero"; } // Set validation errors if any @@ -162,26 +187,26 @@ const Reimbursement: React.FC = () => { } try { await reimbursementsCreateReimbursement(formData); - setSuccess('Reimbursement created successfully!'); + setSuccess("Reimbursement created successfully!"); setFormData({ body: { creditor: { - kst_id: '', - ledger_id: '', + kst_id: "", + ledger_id: "", amount: 0, accounting_year: 2024, - currency: 'CHF', - comment: '', - qcomment: '', + currency: "CHF", + comment: "", + qcomment: "", }, - recipt: '', - creator: '', - recipient: '', + recipt: "", + creator: "", + recipient: "", }, }); } catch (err) { - setError('Failed to create reimbursement.'); - console.error('Error creating reimbursement:', err); + setError("Failed to create reimbursement."); + console.error("Error creating reimbursement:", err); } }; @@ -195,7 +220,11 @@ const Reimbursement: React.FC = () => { {error && <Alert severity="error">{error}</Alert>} <Stack spacing={2} sx={{ mt: 2 }}> - <FormControl fullWidth required error={!!validationErrors['creditor.kst_id']}> + <FormControl + fullWidth + required + error={!!validationErrors["creditor.kst_id"]} + > <InputLabel id="kst-id-label">KST ID</InputLabel> <Select labelId="kst-id-label" @@ -209,11 +238,17 @@ const Reimbursement: React.FC = () => { </MenuItem> ))} </Select> - {validationErrors['creditor.kst_id'] && ( - <Typography color="error">{validationErrors['creditor.kst_id']}</Typography> + {validationErrors["creditor.kst_id"] && ( + <Typography color="error"> + {validationErrors["creditor.kst_id"]} + </Typography> )} </FormControl> - <FormControl fullWidth required error={!!validationErrors['creditor.ledger_id']}> + <FormControl + fullWidth + required + error={!!validationErrors["creditor.ledger_id"]} + > <InputLabel id="ledger-id-label">Ledger ID</InputLabel> <Select labelId="ledger-id-label" @@ -227,8 +262,10 @@ const Reimbursement: React.FC = () => { </MenuItem> ))} </Select> - {validationErrors['creditor.ledger_id'] && ( - <Typography color="error">{validationErrors['creditor.ledger_id']}</Typography> + {validationErrors["creditor.ledger_id"] && ( + <Typography color="error"> + {validationErrors["creditor.ledger_id"]} + </Typography> )} </FormControl> <TextField @@ -239,8 +276,8 @@ const Reimbursement: React.FC = () => { onChange={handleChange} fullWidth required - error={!!validationErrors['creditor.amount']} - helperText={validationErrors['creditor.amount']} + error={!!validationErrors["creditor.amount"]} + helperText={validationErrors["creditor.amount"]} /> <TextField label="Accounting Year" @@ -250,8 +287,8 @@ const Reimbursement: React.FC = () => { onChange={handleChange} fullWidth required - error={!!validationErrors['creditor.accounting_year']} - helperText={validationErrors['creditor.accounting_year']} + error={!!validationErrors["creditor.accounting_year"]} + helperText={validationErrors["creditor.accounting_year"]} /> <TextField label="Currency" @@ -260,8 +297,8 @@ const Reimbursement: React.FC = () => { onChange={handleChange} fullWidth required - error={!!validationErrors['creditor.currency']} - helperText={validationErrors['creditor.currency']} + error={!!validationErrors["creditor.currency"]} + helperText={validationErrors["creditor.currency"]} /> <TextField label="Comment" @@ -269,8 +306,8 @@ const Reimbursement: React.FC = () => { value={formData.body.creditor.comment} onChange={handleChange} fullWidth - error={!!validationErrors['creditor.comment']} - helperText={validationErrors['creditor.comment']} + error={!!validationErrors["creditor.comment"]} + helperText={validationErrors["creditor.comment"]} /> <TextField label="QComment" @@ -278,8 +315,8 @@ const Reimbursement: React.FC = () => { value={formData.body.creditor.qcomment} onChange={handleChange} fullWidth - error={!!validationErrors['creditor.qcomment']} - helperText={validationErrors['creditor.qcomment']} + error={!!validationErrors["creditor.qcomment"]} + helperText={validationErrors["creditor.qcomment"]} /> <TextField label="Recipt" @@ -288,8 +325,8 @@ const Reimbursement: React.FC = () => { onChange={handleChange} fullWidth required - error={!!validationErrors['recipt']} - helperText={validationErrors['recipt']} + error={!!validationErrors["recipt"]} + helperText={validationErrors["recipt"]} /> <TextField label="Creator" @@ -298,8 +335,8 @@ const Reimbursement: React.FC = () => { onChange={handleChange} fullWidth required - error={!!validationErrors['creator']} - helperText={validationErrors['creator']} + error={!!validationErrors["creator"]} + helperText={validationErrors["creator"]} /> <TextField label="Recipient" @@ -308,8 +345,8 @@ const Reimbursement: React.FC = () => { onChange={handleChange} fullWidth required - error={!!validationErrors['recipient']} - helperText={validationErrors['recipient']} + error={!!validationErrors["recipient"]} + helperText={validationErrors["recipient"]} /> <Button onClick={handleSubmit} variant="contained" color="primary"> Create Reimbursement @@ -319,4 +356,4 @@ const Reimbursement: React.FC = () => { ); }; -export default Reimbursement; \ No newline at end of file +export default Reimbursement; diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 169ff84..e452489 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -2,21 +2,24 @@ import React from "react"; import { Container, CircularProgress, Typography, Box } from "@mui/material"; export default function Index() { - return ( - <Container - sx={{ - display: "flex", - flexDirection: "column", // Stack children vertically - justifyContent: "center", - alignItems: "center", // Center horizontally - padding: 2, - height: "100vh", // Ensure full viewport height - }} - > - <Box sx={{ textAlign: "center", marginBottom: 2 }}> - <Typography variant="h6">This page is still work in Progress, but check out the pages in the side bar, you are up for a treat.</Typography> - </Box> - <CircularProgress /> - </Container> - ); + return ( + <Container + sx={{ + display: "flex", + flexDirection: "column", // Stack children vertically + justifyContent: "center", + alignItems: "center", // Center horizontally + padding: 2, + height: "100vh", // Ensure full viewport height + }} + > + <Box sx={{ textAlign: "center", marginBottom: 2 }}> + <Typography variant="h6"> + This page is still work in Progress, but check out the pages in the + side bar, you are up for a treat. + </Typography> + </Box> + <CircularProgress /> + </Container> + ); } diff --git a/src/pages/root.tsx b/src/pages/root.tsx index 068a240..e3cbf38 100644 --- a/src/pages/root.tsx +++ b/src/pages/root.tsx @@ -1,139 +1,222 @@ -import { AppBar, Toolbar, Typography, Button, useMediaQuery, Box, SxProps, IconButton, useTheme, ListItem, ListItemButton, ListItemIcon, ListItemText, List, Drawer, Checkbox, FormControlLabel, Divider} from "@mui/material"; -import { Outlet, useLocation } from "react-router-dom"; -import { Checklist, Groups, Home, Login, Logout, Menu, Payment, Settings, ShoppingCart } from "@mui/icons-material"; -import ReceiptIcon from "@mui/icons-material/Receipt"; // Import the Receipt icon -import { Link } from "react-router-dom"; -import { useContext, useEffect, useState } from "react"; -import AmivLogoSVG from '../assets/amiv.svg'; -import AmivWheelSVG from '../assets/amiv-wheel.svg'; -import React from "react"; +import React, { useEffect, useState } from "react"; +import { + AppBar, + Toolbar, + Typography, + Button, + useMediaQuery, + Box, + SxProps, + IconButton, + useTheme, + ListItem, + ListItemButton, + ListItemIcon, + ListItemText, + List, + Drawer, +} from "@mui/material"; +import { Outlet, useLocation, useNavigate, Link } from "react-router-dom"; +import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; +import Callback from "../Callback"; +import { + Menu, + Settings, + Login, + Logout, + Payment, + ShoppingCart, +} from "@mui/icons-material"; +import ReceiptIcon from "@mui/icons-material/Receipt"; +import AmivLogoSVG from "../assets/amiv.svg"; +import AmivWheelSVG from "../assets/amiv-wheel.svg"; const navContent = [ - { - path: "/creditinvoices", - name: "Credit Invoices", - icon: <Payment /> - }, - { - path: "/Belegformular", - name: "Belegformular", - icon: <ReceiptIcon /> - }, - { - path: "/GenerateItem", - name: "Generate New Item", - icon: <Payment /> - }, - - { - path: "/cart", - name: "Cart", - icon: <ShoppingCart /> - } -] as {path: string, name: string, icon: React.ReactNode}[] + { + path: "/creditinvoices", + name: "Credit Invoices", + icon: <Payment />, + }, + { + path: "/Belegformular", + name: "Belegformular", + icon: <ReceiptIcon />, + }, + { + path: "/GenerateItem", + name: "Generate New Item", + icon: <Payment />, + }, + { + path: "/cart", + name: "Cart", + icon: <ShoppingCart />, + }, +] as { path: string; name: string; icon: React.ReactNode }[]; function NavigationList() { + return ( + <> + <List> + {navContent.map((item, index) => ( + <ListItem key={index} component={Link} to={item.path}> + <ListItemButton> + <ListItemIcon>{item.icon}</ListItemIcon> + <ListItemText primary={item.name} /> + </ListItemButton> + </ListItem> + ))} + </List> + </> + ); +} - return ( - <> - <List> - {navContent.map((item, index) => ( - <ListItem key={index} component={Link} to={item.path}> - <ListItemButton> - <ListItemIcon> - {item.icon} - </ListItemIcon> - <ListItemText primary={item.name} /> - </ListItemButton> - </ListItem> - ))} - </List> - </> - ) +function DesktopAppBar({ + sx, + toggleDrawer, + isAuthenticated, + onLogin, + onLogout, +}: { + sx?: SxProps; + toggleDrawer: () => void; + isAuthenticated: boolean; + onLogin: () => void; + onLogout: () => void; +}) { + return ( + <AppBar position="static" sx={sx}> + <Toolbar> + <IconButton onClick={toggleDrawer}> + <Menu /> + </IconButton> + <Typography variant="h6" component="div" sx={{ flexGrow: 1 }}> + <img src={AmivLogoSVG} alt="AMIV Logo" style={{ height: "1.5em" }} /> + </Typography> + <IconButton> + <Settings /> + </IconButton> + {isAuthenticated ? ( + <IconButton onClick={onLogout}> + <Logout /> + </IconButton> + ) : ( + <IconButton onClick={onLogin}> + <Login /> + </IconButton> + )} + </Toolbar> + </AppBar> + ); } - -function DesktopAppBar({sx, toggleDrawer}: {sx?: SxProps, toggleDrawer: () => void}) { +function MobileAppBar({ + sx, + toggleDrawer, + isAuthenticated, + onLogin, + onLogout, +}: { + sx?: SxProps; + toggleDrawer: () => void; + isAuthenticated: boolean; + onLogin: () => void; + onLogout: () => void; +}) { + return ( + <AppBar position="static" sx={sx}> + <Toolbar> + <IconButton onClick={toggleDrawer}> + <Menu /> + </IconButton> + <Typography variant="h6" component="div" sx={{ flexGrow: 1 }}> + <img + src={AmivWheelSVG} + alt="AMIV Wheel" + style={{ height: "1.5em" }} + /> + </Typography> + {isAuthenticated ? ( + <IconButton onClick={onLogout}> + <Logout /> + </IconButton> + ) : ( + <IconButton onClick={onLogin}> + <Login /> + </IconButton> + )} + </Toolbar> + </AppBar> + ); +} +function App() { + const theme = useTheme(); + const bigScreen = useMediaQuery(theme.breakpoints.up("md")); + const navigate = useNavigate(); + const location = useLocation(); - return ( - <AppBar position="static" sx={sx}> - <Toolbar> - <IconButton onClick={toggleDrawer}> - <Menu /> - </IconButton> - <Typography variant="h6" component="div" sx={{flexGrow: 1}}> - <img src={AmivLogoSVG} alt="AMIV Logo" style={{height: "1.5em"}}/> - </Typography> - <IconButton> - <Settings /> - </IconButton> - <IconButton> - <Logout /> - </IconButton> - </Toolbar> - </AppBar> - ) -} + const [drawerOpen, setDrawerOpen] = useState<boolean>(false); + const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false); -function MobileAppBar({sx, toggleDrawer}: {sx?: SxProps, toggleDrawer: () => void}) { + const toggleDrawer = () => { + setDrawerOpen((o) => !o); + }; + // Check authentication status on mount and when location changes + useEffect(() => { + const token = localStorage.getItem("access_token"); + setIsAuthenticated(!!token); + }, [location]); - return ( - <AppBar position="static" sx={sx}> - <Toolbar> - <IconButton onClick={toggleDrawer}> - <Menu /> - </IconButton> - <Typography variant="h6" component="div" sx={{flexGrow: 1}}> - <img src={AmivWheelSVG} alt="AMIV Wheel" style={{height: "1.5em"}}/> - </Typography> - <IconButton> - <Login /> - </IconButton> - </Toolbar> - </AppBar> - ) -} + const handleLogin = () => { + // Redirect to the backend login endpoint -function App() { - const theme = useTheme(); - const bigScreen = useMediaQuery(theme.breakpoints.up("md")); + window.location.href = import.meta.env.VITE_API_BASE_URL + "api/login"; + }; - const [drawerOpen, setDrawerOpen] = useState<boolean>(false); - const toggleDrawer = () => { - setDrawerOpen(o => !o); - }; - const location = useLocation(); - useEffect(() => { - setDrawerOpen(false); - }, [location]); + const handleLogout = () => { + // Remove token and update state + localStorage.removeItem("access_token"); + setIsAuthenticated(false); + navigate("/"); + }; - return ( - <> - {bigScreen ? - <DesktopAppBar sx={{ display: {sx: 'none', md: 'block'}}} toggleDrawer={toggleDrawer}/> : - <MobileAppBar sx={{ display: {sx: 'block', md: 'none'}}} toggleDrawer={toggleDrawer}/> } - - <Drawer - anchor={bigScreen ? "left" : "bottom"} - open={drawerOpen} - onClose={toggleDrawer} - > - <Box sx={{width: "100%"}}> - - <NavigationList /> - </Box> - </Drawer> - - <Outlet /> - </> - ) -} + return ( + <> + {bigScreen ? ( + <DesktopAppBar + sx={{ display: { sx: "none", md: "block" } }} + toggleDrawer={toggleDrawer} + isAuthenticated={isAuthenticated} + onLogin={handleLogin} + onLogout={handleLogout} + /> + ) : ( + <MobileAppBar + sx={{ display: { sx: "block", md: "none" } }} + toggleDrawer={toggleDrawer} + isAuthenticated={isAuthenticated} + onLogin={handleLogin} + onLogout={handleLogout} + /> + )} + <Drawer + anchor={bigScreen ? "left" : "bottom"} + open={drawerOpen} + onClose={toggleDrawer} + > + <Box sx={{ width: "100%" }}> + <NavigationList /> + </Box> + </Drawer> + + <Outlet /> + </> + ); +} export default function Root() { - return ( - <App /> - ) + return <App />; } diff --git a/vite.config.ts b/vite.config.ts index b78cba9..cf19b55 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,10 +1,10 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' -import dotenv from 'dotenv'; +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import dotenv from "dotenv"; dotenv.config(); // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], -}) +}); diff --git a/yarn.lock b/yarn.lock index 1f71f4e..0cc55c5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19,7 +19,7 @@ "@types/json-schema" "^7.0.15" js-yaml "^4.1.0" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.22.13", "@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0": version "7.26.2" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85" integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== @@ -692,6 +692,38 @@ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== +"@redocly/ajv@^8.11.2": + version "8.11.2" + resolved "https://registry.yarnpkg.com/@redocly/ajv/-/ajv-8.11.2.tgz#46e1bf321ec0ac1e0fd31dea41a3d1fcbdcda0b5" + integrity sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js-replace "^1.0.1" + +"@redocly/config@^0.16.0": + version "0.16.0" + resolved "https://registry.yarnpkg.com/@redocly/config/-/config-0.16.0.tgz#4b7700a5cb6e04bc6d6fdb94b871c9e260a1fba6" + integrity sha512-t9jnODbUcuANRSl/K4L9nb12V+U5acIHnVSl26NWrtSdDZVtoqUXk2yGFPZzohYf62cCfEQUT8ouJ3bhPfpnJg== + +"@redocly/openapi-core@^1.25.9": + version "1.25.11" + resolved "https://registry.yarnpkg.com/@redocly/openapi-core/-/openapi-core-1.25.11.tgz#93f168284986da6809363b001e9aa7c2104c2fc0" + integrity sha512-bH+a8izQz4fnKROKoX3bEU8sQ9rjvEIZOqU6qTmxlhOJ0NsKa5e+LmU18SV0oFeg5YhWQhhEDihXkvKJ1wMMNQ== + dependencies: + "@redocly/ajv" "^8.11.2" + "@redocly/config" "^0.16.0" + colorette "^1.2.0" + https-proxy-agent "^7.0.4" + js-levenshtein "^1.1.6" + js-yaml "^4.1.0" + lodash.isequal "^4.5.0" + minimatch "^5.0.1" + node-fetch "^2.6.1" + pluralize "^8.0.0" + yaml-ast-parser "0.0.43" + "@remix-run/router@1.20.0": version "1.20.0" resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.20.0.tgz#03554155b45d8b529adf635b2f6ad1165d70d8b4" @@ -969,6 +1001,13 @@ acorn@^8.12.1, acorn@^8.14.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== +agent-base@^7.0.2: + version "7.1.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317" + integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA== + dependencies: + debug "^4.3.4" + ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -979,6 +1018,11 @@ ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ansi-colors@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" + integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== + ansi-styles@^4.1.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" @@ -1078,6 +1122,11 @@ chalk@^4.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +change-case@^5.4.4: + version "5.4.4" + resolved "https://registry.yarnpkg.com/change-case/-/change-case-5.4.4.tgz#0d52b507d8fb8f204343432381d1a6d7bff97a02" + integrity sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w== + chokidar@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.1.tgz#4a6dff66798fb0f72a94f616abbd7e1a19f31d41" @@ -1114,6 +1163,11 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +colorette@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" + integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g== + commander@12.1.0: version "12.1.0" resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3" @@ -1169,7 +1223,7 @@ csstype@^3.0.2, csstype@^3.1.3: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== -debug@^4.1.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: +debug@4, debug@^4.1.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: version "4.3.7" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== @@ -1560,6 +1614,14 @@ hoist-non-react-statics@^3.3.1: dependencies: react-is "^16.7.0" +https-proxy-agent@^7.0.4: + version "7.0.5" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz#9e8b5013873299e11fab6fd548405da2d6c602b2" + integrity sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw== + dependencies: + agent-base "^7.0.2" + debug "4" + human-signals@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28" @@ -1583,6 +1645,11 @@ imurmurhash@^0.1.4: resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== +index-to-position@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/index-to-position/-/index-to-position-0.1.2.tgz#e11bfe995ca4d8eddb1ec43274488f3c201a7f09" + integrity sha512-MWDKS3AS1bGCHLBA2VLImJz42f7bJh8wQsTGCzI3j519/CASStoDONUBVz2I/VID0MpiX3SGSnbOD2xUalbE5g== + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -1627,6 +1694,11 @@ jiti@^2.3.0: resolved "https://registry.yarnpkg.com/jiti/-/jiti-2.4.0.tgz#393d595fb6031a11d11171b5e4fc0b989ba3e053" integrity sha512-H5UpaUI+aHOqZXlYOaFP/8AzKsg+guWu+Pr3Y8i7+Y3zr1aXAvCvTAQ1RxSc6oVD8R8c7brgNtTVP91E7upH/g== +js-levenshtein@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d" + integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -1659,6 +1731,11 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" @@ -1696,6 +1773,11 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== + lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" @@ -1752,6 +1834,13 @@ minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" +minimatch@^5.0.1: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + minimatch@^9.0.4: version "9.0.5" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" @@ -1841,6 +1930,13 @@ node-fetch-native@^1.6.3: resolved "https://registry.yarnpkg.com/node-fetch-native/-/node-fetch-native-1.6.4.tgz#679fc8fd8111266d47d7e72c379f1bed9acff06e" integrity sha512-IhOigYzAKHd244OC0JIMIUrjzctirCmPkaIfhDeGcEETWof5zKYUW7e7MYvChGWh/4CJeXEgsRyGzuF334rOOQ== +node-fetch@^2.6.1: + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" + node-releases@^2.0.18: version "2.0.18" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f" @@ -1889,6 +1985,30 @@ onetime@^6.0.0: dependencies: mimic-fn "^4.0.0" +openapi-fetch@^0.13.0: + version "0.13.0" + resolved "https://registry.yarnpkg.com/openapi-fetch/-/openapi-fetch-0.13.0.tgz#42dcdac4a8e14d2aeba321b46f6fbf4dac997a94" + integrity sha512-6Nlf/BDbtyHwHdNrLPUiyt4CZMzL3ZyAt55yWH8W7+Z+8aYWnvca4uZHQHXViy8KcnCMqAhLM/bifh2Yjjkf6w== + dependencies: + openapi-typescript-helpers "^0.0.15" + +openapi-typescript-helpers@^0.0.15: + version "0.0.15" + resolved "https://registry.yarnpkg.com/openapi-typescript-helpers/-/openapi-typescript-helpers-0.0.15.tgz#96ffa762a5e01ef66a661b163d5f1109ed1967ed" + integrity sha512-opyTPaunsklCBpTK8JGef6mfPhLSnyy5a0IN9vKtx3+4aExf+KxEqYwIy3hqkedXIB97u357uLMJsOnm3GVjsw== + +openapi-typescript@^7.4.3: + version "7.4.3" + resolved "https://registry.yarnpkg.com/openapi-typescript/-/openapi-typescript-7.4.3.tgz#269c367929e8580dae2f7ef2d68bb39ffa30fa47" + integrity sha512-xTIjMIIOv9kNhsr8JxaC00ucbIY/6ZwuJPJBZMSh5FA2dicZN5uM805DWVJojXdom8YI4AQTavPDPHMx/3g0vQ== + dependencies: + "@redocly/openapi-core" "^1.25.9" + ansi-colors "^4.1.3" + change-case "^5.4.4" + parse-json "^8.1.0" + supports-color "^9.4.0" + yargs-parser "^21.1.1" + optionator@^0.9.3: version "0.9.4" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" @@ -1932,6 +2052,15 @@ parse-json@^5.0.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" +parse-json@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-8.1.0.tgz#91cdc7728004e955af9cb734de5684733b24a717" + integrity sha512-rum1bPifK5SSar35Z6EKZuYPJx85pkNaFrxBK3mwdfSJ1/WKbYrjoW/zTPSjRRamfmVX1ACBIdFAO0VRErW/EA== + dependencies: + "@babel/code-frame" "^7.22.13" + index-to-position "^0.1.2" + type-fest "^4.7.1" + path-exists@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" @@ -1986,6 +2115,11 @@ pkg-types@^1.2.0: mlly "^1.7.2" pathe "^1.1.2" +pluralize@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" + integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== + postcss@^8.4.43: version "8.4.47" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.47.tgz#5bf6c9a010f3e724c503bf03ef7947dcb0fea365" @@ -2097,6 +2231,11 @@ regenerator-runtime@^0.14.0: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" @@ -2231,6 +2370,11 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" +supports-color@^9.4.0: + version "9.4.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-9.4.0.tgz#17bfcf686288f531db3dea3215510621ccb55954" + integrity sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw== + supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" @@ -2260,6 +2404,11 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + ts-api-utils@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.0.tgz#709c6f2076e511a81557f3d07a0cbd566ae8195c" @@ -2272,6 +2421,11 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" +type-fest@^4.7.1: + version "4.27.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.27.0.tgz#57329aae32e7b27b942b961e3ef861f0873c4b1b" + integrity sha512-3IMSWgP7C5KSQqmo1wjhKrwsvXAtF33jO3QY+Uy++ia7hqvgSK6iXbbg5PbDBc1P2ZbNEDgejOrN4YooXvhwCw== + typescript@^5.6.3: version "5.6.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.3.tgz#5f3449e31c9d94febb17de03cc081dd56d81db5b" @@ -2295,6 +2449,11 @@ update-browserslist-db@^1.1.1: escalade "^3.2.0" picocolors "^1.1.0" +uri-js-replace@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/uri-js-replace/-/uri-js-replace-1.0.1.tgz#c285bb352b701c9dfdaeffc4da5be77f936c9048" + integrity sha512-W+C9NWNLFOoBI2QWDp4UT9pv65r2w5Cx+3sTYFvtMdDBxkKt1syCqsUdSFAChbEe1uK5TfS04wt/nGwmaeIQ0g== + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" @@ -2318,6 +2477,19 @@ vlq@^0.2.2: resolved "https://registry.yarnpkg.com/vlq/-/vlq-0.2.3.tgz#8f3e4328cf63b1540c0d67e1b2778386f8975b26" integrity sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow== +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" @@ -2345,11 +2517,21 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== +yaml-ast-parser@0.0.43: + version "0.0.43" + resolved "https://registry.yarnpkg.com/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz#e8a23e6fb4c38076ab92995c5dca33f3d3d7c9bb" + integrity sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A== + yaml@^1.10.0: version "1.10.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" -- GitLab