From b3da84f8a52778b8242e2895f5a32aa0e546a2f3 Mon Sep 17 00:00:00 2001 From: Fynn Heintz Date: Fri, 28 Apr 2023 13:02:55 +0200 Subject: [PATCH] feat: quick allocation --- cypress/e2e/dashboard.cy.ts | 18 ++++++ src/App.scss | 2 +- src/components/Dashboard.tsx | 29 +++++++-- src/components/QuickAllocationForm.tsx | 89 ++++++++++++++++++++++++++ src/translations/en.json | 8 ++- src/types.d.ts | 4 ++ 6 files changed, 141 insertions(+), 9 deletions(-) create mode 100644 src/components/QuickAllocationForm.tsx diff --git a/cypress/e2e/dashboard.cy.ts b/cypress/e2e/dashboard.cy.ts index f1be8ca4e..aac7c6ecc 100644 --- a/cypress/e2e/dashboard.cy.ts +++ b/cypress/e2e/dashboard.cy.ts @@ -171,4 +171,22 @@ describe('Dashboard', () => { cy.contains('Second Transaction') cy.contains('First Transaction').should('not.exist') }) + + it('can quickly allocate funds to all envelopes', () => { + cy.visit('#', { qs: { month: '2023-04' } }) + cy.awaitLoading() + + // set allocation + cy.get('[aria-label*="Edit Allocation for First Envelope"]').click() + cy.getInputFor('Set to amount').type('12.00') + cy.get('button[type="submit"]').click() + cy.awaitLoading() + + cy.visit('#', { qs: { month: '2023-05' } }) + cy.awaitLoading() + cy.contains('-12.00 Available to budget') + cy.get('select').select("Last month's allocation") + cy.clickAndWait('Submit') + cy.contains('-24.00 Available to budget') + }) }) diff --git a/src/App.scss b/src/App.scss index 5fd241d31..d464f62f3 100644 --- a/src/App.scss +++ b/src/App.scss @@ -94,7 +94,7 @@ } .error { - @apply bg-red-100 p-2 rounded-md dark:text-red-800; + @apply bg-red-100 p-2 rounded-md text-red-800 dark:text-red-600; } .alert { diff --git a/src/components/Dashboard.tsx b/src/components/Dashboard.tsx index 73cd0c67f..b349d5452 100644 --- a/src/components/Dashboard.tsx +++ b/src/components/Dashboard.tsx @@ -18,6 +18,7 @@ import { } from '../lib/dates' import CategoryMonth from './CategoryMonth' import MonthPicker from './MonthPicker' +import QuickAllocationForm from './QuickAllocationForm' type DashboardProps = { budget: Budget } @@ -59,12 +60,16 @@ const Dashboard = ({ budget }: DashboardProps) => { const useNativeMonthPicker = isSupported.inputTypeMonth() - const loadBudgetMonth = useCallback(async () => { - const [year, month] = activeMonth.split('-') + const replaceMonthInLinks = useCallback( + (link: string) => { + const [year, month] = activeMonth.split('-') + return link.replace('YYYY', year).replace('MM', month) + }, + [activeMonth] + ) - return get( - budget.links.groupedMonth.replace('YYYY', year).replace('MM', month) - ) + const loadBudgetMonth = useCallback(async () => { + return get(replaceMonthInLinks(budget.links.groupedMonth)) .then(data => { setBudgetMonth(data) if (error) { @@ -80,6 +85,11 @@ const Dashboard = ({ budget }: DashboardProps) => { loadBudgetMonth().then(() => setIsLoading(false)) }, [loadBudgetMonth, budget, activeMonth]) + const reloadBudgetMonth = () => { + setIsLoading(true) + loadBudgetMonth().then(() => setIsLoading(false)) + } + return (
@@ -166,6 +176,12 @@ const Dashboard = ({ budget }: DashboardProps) => { {t('dashboard.available')}
+
+ +
@@ -253,8 +269,7 @@ const Dashboard = ({ budget }: DashboardProps) => { editingEnvelope={editingEnvelope} editEnvelope={setEditingEnvelope} reloadBudgetMonth={() => { - setIsLoading(true) - loadBudgetMonth().then(() => setIsLoading(false)) + reloadBudgetMonth() }} setError={setError} /> diff --git a/src/components/QuickAllocationForm.tsx b/src/components/QuickAllocationForm.tsx new file mode 100644 index 000000000..f5179a8a5 --- /dev/null +++ b/src/components/QuickAllocationForm.tsx @@ -0,0 +1,89 @@ +import { useState } from 'react' +import { useTranslation } from 'react-i18next' +import { QuickAllocationMode, Translation } from '../types' +import { checkStatus } from '../lib/fetch-helper' +import Alert from './Alert' +import Error from './Error' + +const quickAllocationModes = [ + 'ALLOCATE_LAST_MONTH_BUDGET', + 'ALLOCATE_LAST_MONTH_SPEND', +] + +type Props = { link: string; reloadBudgetMonth: () => void } + +const QuickAllocationForm = ({ link, reloadBudgetMonth }: Props) => { + const { t }: Translation = useTranslation() + + const [mode, setMode] = useState('') + const [error, setError] = useState('') + + return ( +
{ + e.preventDefault() + if (!quickAllocationModes.includes(mode)) { + console.log(mode) + return + } + + fetch(link, { method: 'POST', body: JSON.stringify({ mode }) }) + .then(checkStatus) + .then(() => { + setMode('') + reloadBudgetMonth() + if (error) { + setError('') + } + }) + .catch(error => { + setError(error.message) + }) + }} + onReset={() => { + setMode('') + }} + > + + + + + + {mode === '' ? null : ( +
+ + +
+ )} + + ) +} + +export default QuickAllocationForm diff --git a/src/translations/en.json b/src/translations/en.json index 50b5d91a9..bbf70ad9b 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -136,13 +136,19 @@ }, "dashboard": { "available": "Available to budget", + "quickAllocation": "Quick Allocation", "allocation": "Allocation", "allocationForEnvelope": "Allocation for {{envelope}}", "allocationForEnvelopeMonth": "Allocation for {{envelope}} ({{month}})", "balance": "Balance", "spent": "Spent", "selectMonth": "Select Month", - "goToCurrentMonth": "Go to current month" + "goToCurrentMonth": "Go to current month", + "quickAllocationMode": { + "allocateBasedOn": "Allocate based on...", + "ALLOCATE_LAST_MONTH_BUDGET": "Last month's allocation", + "ALLOCATE_LAST_MONTH_SPEND": "Last month's spending" + } }, "envelopeMonth": { "absoluteAllocation": "Set to amount", diff --git a/src/types.d.ts b/src/types.d.ts index ef3e33c1b..f45f1d837 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -158,3 +158,7 @@ export type FilterOptions = { } export type Theme = 'dark' | 'light' | 'default' + +export type QuickAllocationMode = + | 'ALLOCATE_LAST_MONTH_BUDGET' + | 'ALLOCATE_LAST_MONTH_SPEND'