mirror of
https://github.com/siwa-net/my-finance-pal.git
synced 2024-11-10 00:51:56 +01:00
added ability to add budget
This commit is contained in:
parent
556a1567bd
commit
0eb368d2a0
15 changed files with 1170 additions and 51 deletions
946
package-lock.json
generated
946
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -11,12 +11,17 @@
|
||||||
"codegen": "rm -rf ./src/generated/openapi; openapi --input ./api.yaml --output ./src/generated/openapi"
|
"codegen": "rm -rf ./src/generated/openapi; openapi --input ./api.yaml --output ./src/generated/openapi"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@emotion/react": "^11.10.6",
|
||||||
|
"@emotion/styled": "^11.10.6",
|
||||||
"@fortawesome/fontawesome-svg-core": "^6.3.0",
|
"@fortawesome/fontawesome-svg-core": "^6.3.0",
|
||||||
"@fortawesome/free-regular-svg-icons": "^6.3.0",
|
"@fortawesome/free-regular-svg-icons": "^6.3.0",
|
||||||
"@fortawesome/free-solid-svg-icons": "^6.3.0",
|
"@fortawesome/free-solid-svg-icons": "^6.3.0",
|
||||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||||
|
"@mui/material": "^5.11.15",
|
||||||
|
"@mui/x-date-pickers": "^6.0.4",
|
||||||
"@sindresorhus/is": "^5.3.0",
|
"@sindresorhus/is": "^5.3.0",
|
||||||
"@tanstack/react-query": "^4.28.0",
|
"@tanstack/react-query": "^4.28.0",
|
||||||
|
"@tanstack/react-query-devtools": "^4.28.0",
|
||||||
"@types/node": "18.15.5",
|
"@types/node": "18.15.5",
|
||||||
"@types/react": "18.0.28",
|
"@types/react": "18.0.28",
|
||||||
"@types/react-dom": "18.0.11",
|
"@types/react-dom": "18.0.11",
|
||||||
|
|
|
@ -1,33 +1,71 @@
|
||||||
|
import { Button, Grid, TextField } from '@mui/material';
|
||||||
import { SubmitHandler, useForm } from 'react-hook-form';
|
import { SubmitHandler, useForm } from 'react-hook-form';
|
||||||
|
|
||||||
|
import { resolver } from './resolver';
|
||||||
import { useAddBudget } from '../../hooks/useAddBudget';
|
import { useAddBudget } from '../../hooks/useAddBudget';
|
||||||
import { NewBudgetModel } from '../../models/budget';
|
import { NewBudgetModel } from '../../models/budget';
|
||||||
import { Card } from '../_design/Card/Card';
|
import { Card } from '../_design/Card/Card';
|
||||||
|
import { DatePicker } from '../DatePicker/DatePicker';
|
||||||
|
|
||||||
// TODO check if necessary for type safety
|
|
||||||
// const resolver: Resolver<NewBudgetModel> = async (values: unknown) => ({
|
|
||||||
// values: newBudgetModelSchema.parse(values),
|
|
||||||
// errors: {},
|
|
||||||
// });
|
|
||||||
export const AddBudgetForm = () => {
|
export const AddBudgetForm = () => {
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
|
reset,
|
||||||
|
control,
|
||||||
formState: { errors },
|
formState: { errors },
|
||||||
} = useForm<NewBudgetModel>();
|
} = useForm<NewBudgetModel>({ resolver });
|
||||||
const { mutate: addBudget } = useAddBudget();
|
const { mutate: addBudget, isLoading } = useAddBudget();
|
||||||
const onSubmit: SubmitHandler<NewBudgetModel> = (budgetItem: NewBudgetModel) => {
|
const onSubmit: SubmitHandler<NewBudgetModel> = async (budgetItem) => {
|
||||||
console.log(budgetItem);
|
await addBudget(budgetItem);
|
||||||
addBudget(budgetItem);
|
reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { ref, ...nameStuff } = register('name', { required: true });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
<input type="text" placeholder="Budget name" {...register('name', { required: true })} />
|
<Grid container alignItems="flex-end" rowGap={4} columnGap={2}>
|
||||||
{errors.name && <span>This field is required</span>}
|
<Grid item xs={12} sm={6} lg={4}>
|
||||||
<input type="number" defaultValue={0} {...register('limit', { min: 0, required: true })} />
|
<TextField
|
||||||
{errors.limit && <span>This field is required</span>}
|
fullWidth
|
||||||
<input type="submit" />
|
variant="standard"
|
||||||
|
type="text"
|
||||||
|
label="Budget name"
|
||||||
|
inputRef={ref}
|
||||||
|
{...nameStuff}
|
||||||
|
error={!!errors.name}
|
||||||
|
/>
|
||||||
|
{errors.name && <>ERROR!</>}
|
||||||
|
</Grid>
|
||||||
|
<Grid item lg={4} xs={12} sm={5.5}>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
variant="standard"
|
||||||
|
type="number"
|
||||||
|
label="Limit"
|
||||||
|
{...register('limit', { min: 0, required: true })}
|
||||||
|
error={!!errors.limit}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item lg={4} sm={5} md={4} xs={12}>
|
||||||
|
<DatePicker control={control} name="startDate" label="Start" />
|
||||||
|
</Grid>
|
||||||
|
<Grid item lg={4} sm={5} md={4} xs={12}>
|
||||||
|
<DatePicker control={control} name="endDate" label="End" />
|
||||||
|
</Grid>
|
||||||
|
<Grid item lg sm md justifyContent={'center'}>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
size="large"
|
||||||
|
variant="contained"
|
||||||
|
disabled={isLoading || Object.keys(errors).length > 0}
|
||||||
|
>
|
||||||
|
Create
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
</form>
|
</form>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|
44
src/components/AddBudgetForm/resolver.ts
Normal file
44
src/components/AddBudgetForm/resolver.ts
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import { isBefore } from 'date-fns';
|
||||||
|
import { Resolver } from 'react-hook-form';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import { NewBudgetModel } from '../../models/budget';
|
||||||
|
|
||||||
|
const budgetFormSchema = z
|
||||||
|
.object({
|
||||||
|
name: z.string(),
|
||||||
|
limit: z.string(),
|
||||||
|
startDate: z.date().optional(),
|
||||||
|
endDate: z.date().optional(),
|
||||||
|
})
|
||||||
|
.transform<NewBudgetModel>((formValues) => ({
|
||||||
|
...formValues,
|
||||||
|
startDate: formValues.startDate,
|
||||||
|
endDate: formValues.endDate,
|
||||||
|
limit: parseInt(formValues.limit),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const resolver: Resolver<NewBudgetModel> = async (values: unknown) => {
|
||||||
|
const parseResult = budgetFormSchema.safeParse(values);
|
||||||
|
if (!parseResult.success) {
|
||||||
|
return {
|
||||||
|
values: parseResult,
|
||||||
|
errors: { root: { message: parseResult.error.message } },
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// check whether dates are set up correctly
|
||||||
|
const { startDate, endDate } = parseResult.data;
|
||||||
|
if (startDate && endDate && isBefore(endDate, startDate)) {
|
||||||
|
return {
|
||||||
|
values: parseResult.data,
|
||||||
|
errors: {
|
||||||
|
startDate: { type: 'value', message: 'Start date has to be before end date' },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
values: parseResult.data,
|
||||||
|
errors: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
|
@ -17,7 +17,7 @@ type BudgetItemProps = { budget: BudgetModel };
|
||||||
export const BudgetItem: FC<BudgetItemProps> = ({ budget }) => {
|
export const BudgetItem: FC<BudgetItemProps> = ({ budget }) => {
|
||||||
const { id, spent, name, limit, startDate, endDate } = budget;
|
const { id, spent, name, limit, startDate, endDate } = budget;
|
||||||
|
|
||||||
const spentPercentage = Math.trunc((100 * spent) / limit);
|
const remainingBudget = 100 - Math.trunc((100 * spent) / limit);
|
||||||
|
|
||||||
const isValidDateRange = is.date(startDate) && is.date(endDate) && endDate > startDate;
|
const isValidDateRange = is.date(startDate) && is.date(endDate) && endDate > startDate;
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ export const BudgetItem: FC<BudgetItemProps> = ({ budget }) => {
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<DetailWithTitle title={'Remaining Budget'}>
|
<DetailWithTitle title={'Remaining Budget'}>
|
||||||
<ProgressBar percentage={spentPercentage} />
|
<ProgressBar percentage={remainingBudget} />
|
||||||
</DetailWithTitle>
|
</DetailWithTitle>
|
||||||
|
|
||||||
{isValidDateRange && (
|
{isValidDateRange && (
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core';
|
import { library } from '@fortawesome/fontawesome-svg-core';
|
||||||
import { faAngry } from '@fortawesome/free-regular-svg-icons';
|
import { faAngry } from '@fortawesome/free-regular-svg-icons';
|
||||||
import { faPlus } from '@fortawesome/free-solid-svg-icons';
|
import { faPlus, faMinus } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { FC, useState } from 'react';
|
import { FC } from 'react';
|
||||||
|
|
||||||
import styles from './BudgetList.module.scss';
|
import styles from './BudgetList.module.scss';
|
||||||
import { useBudgets } from '../../hooks/useBudgets';
|
import { useBudgets } from '../../hooks/useBudgets';
|
||||||
|
import { useToggle } from '../../hooks/useToggle';
|
||||||
import { Button } from '../_design/Button/Button';
|
import { Button } from '../_design/Button/Button';
|
||||||
import { AddBudgetForm } from '../AddBudgetForm/AddBudgetForm';
|
import { AddBudgetForm } from '../AddBudgetForm/AddBudgetForm';
|
||||||
import { BudgetItem } from '../BudgetItem/BudgetItem';
|
import { BudgetItem } from '../BudgetItem/BudgetItem';
|
||||||
|
@ -14,17 +15,12 @@ library.add(faAngry);
|
||||||
|
|
||||||
export const BudgetList: FC = () => {
|
export const BudgetList: FC = () => {
|
||||||
const { data: budgets = [] } = useBudgets();
|
const { data: budgets = [] } = useBudgets();
|
||||||
const [showAddBudget, setShowAddBudget] = useState(false);
|
const [showAddBudget, toggleShowAddBudget] = useToggle(false);
|
||||||
|
|
||||||
const handleAddBudgetClick = () => {
|
|
||||||
// TODO create logic for adding budget
|
|
||||||
setShowAddBudget(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={styles.Container}>
|
<section className={styles.Container}>
|
||||||
<Button onClick={handleAddBudgetClick}>
|
<Button onClick={toggleShowAddBudget}>
|
||||||
Add a budget <FontAwesomeIcon icon={faPlus} />
|
Add a budget <FontAwesomeIcon icon={showAddBudget ? faMinus : faPlus} />
|
||||||
</Button>
|
</Button>
|
||||||
{showAddBudget && <AddBudgetForm />}
|
{showAddBudget && <AddBudgetForm />}
|
||||||
<div className={styles.ListItems}>
|
<div className={styles.ListItems}>
|
||||||
|
|
17
src/components/DatePicker/DatePicker.module.scss
Normal file
17
src/components/DatePicker/DatePicker.module.scss
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
.Wrapper {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Error {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -16px;
|
||||||
|
left: 2px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: red;
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.DatePicker {
|
||||||
|
width: 100%
|
||||||
|
}
|
34
src/components/DatePicker/DatePicker.tsx
Normal file
34
src/components/DatePicker/DatePicker.tsx
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import { DatePicker as MUIDatePicker } from '@mui/x-date-pickers';
|
||||||
|
import { Control, Controller, FieldValues, Path } from 'react-hook-form';
|
||||||
|
|
||||||
|
import styles from './DatePicker.module.scss';
|
||||||
|
|
||||||
|
const DATE_FORMAT = 'dd.MM.yyyy';
|
||||||
|
export const DatePicker = <ControlType extends FieldValues>({
|
||||||
|
control,
|
||||||
|
name,
|
||||||
|
label,
|
||||||
|
}: {
|
||||||
|
control: Control<ControlType>;
|
||||||
|
name: Path<ControlType>;
|
||||||
|
label?: string;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
render={({ field: { value, ...rest }, fieldState: { error } }) => (
|
||||||
|
<div className={styles.Wrapper}>
|
||||||
|
<MUIDatePicker
|
||||||
|
className={styles.DatePicker}
|
||||||
|
label={label && <span>{label}</span>}
|
||||||
|
format={DATE_FORMAT}
|
||||||
|
{...rest}
|
||||||
|
value={value ? new Date(value) : null}
|
||||||
|
/>
|
||||||
|
{error && <span className={styles.Error}>{error.message}</span>}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
name={name}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
|
@ -4,11 +4,11 @@
|
||||||
import type { Budget } from '../models/Budget';
|
import type { Budget } from '../models/Budget';
|
||||||
import type { BudgetId } from '../models/BudgetId';
|
import type { BudgetId } from '../models/BudgetId';
|
||||||
import type { BudgetSummary } from '../models/BudgetSummary';
|
import type { BudgetSummary } from '../models/BudgetSummary';
|
||||||
import type { NewBudget } from '../models/NewBudget';
|
|
||||||
|
|
||||||
import type { CancelablePromise } from '../core/CancelablePromise';
|
import type { CancelablePromise } from '../core/CancelablePromise';
|
||||||
import { OpenAPI } from '../core/OpenAPI';
|
import { OpenAPI } from '../core/OpenAPI';
|
||||||
import { request as __request } from '../core/request';
|
import { request as __request } from '../core/request';
|
||||||
|
import {NewBudgetModel} from "../../../models/budget";
|
||||||
|
|
||||||
export class BudgetsService {
|
export class BudgetsService {
|
||||||
|
|
||||||
|
@ -19,8 +19,9 @@ export class BudgetsService {
|
||||||
* @throws ApiError
|
* @throws ApiError
|
||||||
*/
|
*/
|
||||||
public static createBudget(
|
public static createBudget(
|
||||||
requestBody: NewBudget,
|
requestBody: NewBudgetModel,
|
||||||
): CancelablePromise<Budget> {
|
): CancelablePromise<Budget> {
|
||||||
|
console.log('requestBody: ', requestBody);
|
||||||
return __request(OpenAPI, {
|
return __request(OpenAPI, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: '/budgets',
|
url: '/budgets',
|
||||||
|
@ -85,5 +86,4 @@ export class BudgetsService {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,19 @@
|
||||||
import { useMutation, UseMutationResult } from '@tanstack/react-query';
|
import { useMutation, UseMutationResult } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { Budget, BudgetsService, NewBudget } from '../generated/openapi';
|
import { useInvalidateQuery } from './useBudgets';
|
||||||
|
import { Budget, BudgetsService } from '../generated/openapi';
|
||||||
import { NewBudgetModel } from '../models/budget';
|
import { NewBudgetModel } from '../models/budget';
|
||||||
|
|
||||||
export const useAddBudget = (): UseMutationResult<Budget, unknown, NewBudgetModel> =>
|
export const useAddBudget = (): UseMutationResult<Budget, unknown, NewBudgetModel> => {
|
||||||
useMutation((budget: NewBudgetModel) => {
|
const invalidateBudgetQuery = useInvalidateQuery();
|
||||||
const mappedBudget: NewBudget = {
|
|
||||||
...budget,
|
return useMutation(
|
||||||
startDate: budget.startDate?.toISOString(),
|
(budget: NewBudgetModel) => {
|
||||||
endDate: budget.endDate?.toISOString(),
|
console.log('budget: ', budget);
|
||||||
};
|
return BudgetsService.createBudget(budget);
|
||||||
return BudgetsService.createBudget(mappedBudget);
|
},
|
||||||
});
|
{
|
||||||
|
onSuccess: () => invalidateBudgetQuery(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { useQuery, UseQueryResult } from '@tanstack/react-query';
|
import { useQuery, useQueryClient, UseQueryResult } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { Budget, BudgetsService } from '../generated/openapi';
|
import { Budget, BudgetsService } from '../generated/openapi';
|
||||||
import { BudgetModel, mapToBudgetModel } from '../models/budget';
|
import { BudgetModel, mapToBudgetModel } from '../models/budget';
|
||||||
|
|
||||||
const budgetSymbol = Symbol('getBudgets');
|
const budgetsQueryKey = ['/budgets'];
|
||||||
|
|
||||||
const selectValidBudgetModelsFromBudgetDtos = (budgetDtos: Budget[]) =>
|
const selectValidBudgetModelsFromBudgetDtos = (budgetDtos: Budget[]) =>
|
||||||
budgetDtos.reduce<BudgetModel[]>((budgetModels, budgetDto) => {
|
budgetDtos.reduce<BudgetModel[]>((budgetModels, budgetDto) => {
|
||||||
|
@ -12,9 +12,14 @@ const selectValidBudgetModelsFromBudgetDtos = (budgetDtos: Budget[]) =>
|
||||||
return mappedBudget ? [...budgetModels, mappedBudget] : budgetModels;
|
return mappedBudget ? [...budgetModels, mappedBudget] : budgetModels;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
export const useInvalidateQuery = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return () => queryClient.invalidateQueries(budgetsQueryKey);
|
||||||
|
};
|
||||||
|
|
||||||
export const useBudgets = (): UseQueryResult<BudgetModel[]> =>
|
export const useBudgets = (): UseQueryResult<BudgetModel[]> =>
|
||||||
useQuery({
|
useQuery({
|
||||||
queryKey: [budgetSymbol],
|
queryKey: budgetsQueryKey,
|
||||||
queryFn: () => BudgetsService.getBudgets(),
|
queryFn: () => BudgetsService.getBudgets(),
|
||||||
select: selectValidBudgetModelsFromBudgetDtos,
|
select: selectValidBudgetModelsFromBudgetDtos,
|
||||||
});
|
});
|
||||||
|
|
14
src/hooks/useToggle.ts
Normal file
14
src/hooks/useToggle.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import { useCallback, useState } from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom hook to toggle a boolean value.
|
||||||
|
* @param initialValue
|
||||||
|
*/
|
||||||
|
export const useToggle = (initialValue: boolean) => {
|
||||||
|
const [state, setState] = useState(initialValue);
|
||||||
|
const toggleState = useCallback(() => {
|
||||||
|
setState((oldValue) => !oldValue);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return [state, toggleState] as const;
|
||||||
|
};
|
|
@ -11,7 +11,7 @@ export const newBudgetModelSchema = z.object({
|
||||||
|
|
||||||
export const budgetModelSchema = newBudgetModelSchema.extend({
|
export const budgetModelSchema = newBudgetModelSchema.extend({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
spent: z.number().positive(),
|
spent: z.number().nonnegative(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type BudgetModel = z.infer<typeof budgetModelSchema>;
|
export type BudgetModel = z.infer<typeof budgetModelSchema>;
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
import { config } from '@fortawesome/fontawesome-svg-core';
|
import { config } from '@fortawesome/fontawesome-svg-core';
|
||||||
|
import { LocalizationProvider } from '@mui/x-date-pickers';
|
||||||
|
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
|
||||||
import { QueryClient } from '@tanstack/query-core';
|
import { QueryClient } from '@tanstack/query-core';
|
||||||
import { QueryClientProvider } from '@tanstack/react-query';
|
import { QueryClientProvider } from '@tanstack/react-query';
|
||||||
|
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
|
||||||
import { Inter } from 'next/font/google';
|
import { Inter } from 'next/font/google';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
@ -20,12 +23,15 @@ const queryClient = new QueryClient({ defaultOptions: { queries: { refetchOnWind
|
||||||
export default function App({ Component, pageProps }: AppProps) {
|
export default function App({ Component, pageProps }: AppProps) {
|
||||||
return (
|
return (
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
|
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
||||||
<main className={inter.className}>
|
<main className={inter.className}>
|
||||||
<Navigation />
|
<Navigation />
|
||||||
<section className={appStyles.ContentWrapper}>
|
<section className={appStyles.ContentWrapper}>
|
||||||
<Component {...pageProps} />
|
<Component {...pageProps} />
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
<ReactQueryDevtools />
|
||||||
|
</LocalizationProvider>
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,5 +8,14 @@ export const budgetList = budgetFactory.buildList(8);
|
||||||
budgetFactory.rewindSequence();
|
budgetFactory.rewindSequence();
|
||||||
|
|
||||||
export default function handler(_req: NextApiRequest, res: NextApiResponse<Budget[]>) {
|
export default function handler(_req: NextApiRequest, res: NextApiResponse<Budget[]>) {
|
||||||
res.status(200).json(budgetList);
|
switch (_req.method) {
|
||||||
|
case 'POST':
|
||||||
|
const newBudget: Budget = { ..._req.body, id: _req.body.name, spent: 0 };
|
||||||
|
budgetList.push(newBudget);
|
||||||
|
return res.status(200).json(budgetList);
|
||||||
|
case 'GET':
|
||||||
|
default:
|
||||||
|
console.log('budgetList: ', budgetList);
|
||||||
|
return res.status(200).json(budgetList);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue