Skip to content

Commit

Permalink
feat: used account store to load account data centrally
Browse files Browse the repository at this point in the history
- split AccountBalance, AccountDashboard and AccountHistory into dumb component and logic component
- created accountStore and added guards for concurrency plus a short cache
  • Loading branch information
Macavity committed Feb 19, 2024
1 parent 090597e commit 6125dd3
Show file tree
Hide file tree
Showing 9 changed files with 303 additions and 213 deletions.
119 changes: 63 additions & 56 deletions assets/client/components/account/AccountBalance.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import AccountLayout from '@/client/layouts/AccountLayout';
import { useTranslation } from 'react-i18next';
import useApi from '@/client/hooks/useApi';
import { Account } from '@/client/interfaces/Account';
import { useParams } from 'react-router-dom';
import { useEffect, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { useEffect, useRef } from 'react';
import Loading from '@/client/components/Loading';
import useFunnyImage from '@/client/hooks/useFunnyImage';
import { formatCentsToEuro } from '@/client/utils/currency';
Expand All @@ -14,76 +13,84 @@ import {
} from '@/client/utils/date';
import { Locale } from '@/client/interfaces/Locale';
import { pathToRoute } from '@/client/utils/pathToRoute';
import { useAccountStore } from '@/client/store/useAccountStore';

export default function AccountBalance() {
interface Props {
account: Account;
}

function AccountBalanceView({ account }: Props) {
const { t, i18n } = useTranslation();
const { id } = useParams();
const {
data: account,
isLoading,
fetchData,
} = useApi<Account>(`accounts/${id}`);
const [balance, setBalance] = useState<string | null>(null);

const { imagePath } = useFunnyImage(account?.balance);
const [nextPayday, setNextPayday] = useState<string | null>(null);
const [daysUntilNextPayday, setDaysUntilNextPayday] = useState<string | null>(
null

const nextPayday = account.nextPayday
? getFormattedNextPayday(account.nextPayday, i18n.language as Locale)
: null;
const daysUntilNextPayday = account.nextPayday
? getDaysUntilNextPayday(account.nextPayday)
: null;

return (
<div className="custom-text-box">
{account.balance && (
<div className="balance-display">
{formatCentsToEuro(account.balance)}
</div>
)}

<div className="funny-image-wrapper">
<figure>
<img src={imagePath} alt="funny image" />
</figure>
</div>
<div className="payday-info">
<div>
{t('OVERVIEW.PAYDAY_WEEKDAY_LABEL')}&nbsp;
<span className="bold-text">
{nextPayday ?? t('OVERVIEW.PAYDAY_WEEKDAY_UNKNOWN')}
</span>
</div>
<div>
{t('OVERVIEW.PAYDAY_COUNTER_LABEL')}&nbsp;
<span className="bold-text">
{daysUntilNextPayday ?? t('OVERVIEW.PAYDAY_COUNTER_UNKNOWN')}
</span>
</div>
</div>
</div>
);
}

useEffect(() => {
fetchData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
export default function AccountBalance() {
const { t } = useTranslation();
const { id } = useParams();
const navigate = useNavigate();
const { account, isLoading, fetchData } = useAccountStore();

const fetchDataRef = useRef(fetchData);

useEffect(() => {
if (!account) {
if (!id) {
return;
}
fetchDataRef.current(id);
}, [id]);

if (account.balance) {
setBalance(formatCentsToEuro(account.balance));
}

if (account.nextPayday) {
setNextPayday(
getFormattedNextPayday(account.nextPayday!, i18n.language as Locale)
);
setDaysUntilNextPayday(getDaysUntilNextPayday(account.nextPayday!));
}
}, [account, i18n.language]);
if (!id) {
navigate(pathToRoute('dashboard'));
return;
}

return (
<AccountLayout
title={t('PAGE_HEADER.PAGE_NAME.OVERVIEW')}
backTo={pathToRoute('accounts_dashboard', { id })}
>
<div className="custom-container no-transparency">
{isLoading ? (
<Loading />
) : (
<div className="custom-text-box">
{balance && <div className="balance-display">{balance}</div>}

<div className="funny-image-wrapper">
<figure>
<img src={imagePath} alt="funny image" />
</figure>
</div>
<div className="payday-info">
<div>
{t('OVERVIEW.PAYDAY_WEEKDAY_LABEL')}&nbsp;
<span className="bold-text">
{nextPayday ?? t('OVERVIEW.PAYDAY_WEEKDAY_UNKNOWN')}
</span>
</div>
<div>
{t('OVERVIEW.PAYDAY_COUNTER_LABEL')}&nbsp;
<span className="bold-text">
{daysUntilNextPayday ?? t('OVERVIEW.PAYDAY_COUNTER_UNKNOWN')}
</span>
</div>
</div>
</div>
{isLoading && <Loading />}
{!isLoading && id && account && (
<AccountBalanceView account={account} />
)}
</div>
</AccountLayout>
Expand Down
50 changes: 33 additions & 17 deletions assets/client/components/account/AccountDashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useTranslation } from 'react-i18next';
import AccountLayout from '@/client/layouts/AccountLayout';
import { useParams } from 'react-router-dom';
import { useNavigate, useParams } from 'react-router-dom';
import './AccountDashboard.scss';
import { pathToRoute } from '@/client/utils/pathToRoute';

Expand All @@ -10,49 +10,65 @@ interface Section {
url: string;
}

interface Props {
sections: Section[];
}

/**
* This page shows the details of an account and is the entry point for any account holder
* - Does not show a logout or back button
*/
function AccountDashboardView({ sections }: Props) {
return (
<div className="custom-container no-transparency">
<div className="button-container">
{sections.map((section, index) => (
<div key={index}>
<a href={section.url} className="image-text-button">
<img src={section.image} />
<label>{section.name}</label>
</a>
</div>
))}
</div>
</div>
);
}

export default function AccountDashboard() {
const { t } = useTranslation();
const { id } = useParams();
const { t } = useTranslation();
const navigate = useNavigate();

const sections: Section[] = [
{
name: 'DASHBOARD.SECTION_NAME.OVERVIEW',
name: t('DASHBOARD.SECTION_NAME.OVERVIEW'),
image: './assets/images/overview.png',
url: pathToRoute('accounts_balance', { id }),
},
{
name: 'DASHBOARD.SECTION_NAME.HISTORY',
name: t('DASHBOARD.SECTION_NAME.HISTORY'),
image: './assets/images/history.png',
url: pathToRoute('accounts_history', { id }),
},
{
name: 'DASHBOARD.SECTION_NAME.PLAN',
name: t('DASHBOARD.SECTION_NAME.PLAN'),
image: './assets/images/plan.png',
url: pathToRoute('accounts_plan', { id }),
},
];

if (!id) {
navigate(pathToRoute('dashboard'));
return;
}

return (
<AccountLayout
title={t('PAGE_HEADER.PAGE_NAME.DASHBOARD')}
backTo={pathToRoute('dashboard')}
>
<div className="custom-container no-transparency">
<div className="button-container">
{sections.map((section, index) => (
<div key={index}>
<a href={section.url} className="image-text-button">
<img src={section.image} />
<label>{t(section.name)}</label>
</a>
</div>
))}
</div>
</div>
<AccountDashboardView sections={sections} />
</AccountLayout>
);
}
Loading

0 comments on commit 6125dd3

Please sign in to comment.