Skip to content
This repository has been archived by the owner on May 11, 2021. It is now read-only.

Commit

Permalink
feat(category): make categories page ok
Browse files Browse the repository at this point in the history
  • Loading branch information
igorkamyshev committed Apr 17, 2019
1 parent dc7a51e commit 1362e27
Show file tree
Hide file tree
Showing 10 changed files with 250 additions and 31 deletions.
19 changes: 13 additions & 6 deletions front/pages/internal/categories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import { getDefaultCurrency } from '@front/domain/user/selectors/getDefaultCurre
import { Categories } from '@front/features/categories'
import { GroupBy } from '@shared/enum/GroupBy'
import { createRangeForGroup } from '@front/helpers/createRangeForGroup'
import { getFirstTransactionDate } from '@front/domain/money/selectors/getFirstTransactionDate'

interface Query {
group: GroupBy
group?: GroupBy
}

export default class CateogiesPage extends React.Component<Query> {
Expand All @@ -20,14 +21,20 @@ export default class CateogiesPage extends React.Component<Query> {
query,
}: AppContext<Query>) {
const { group } = query
const { from, to } = createRangeForGroup(group)

await reduxStore.dispatch(fetchFirstTransactionDate() as any)
const firstTransactionDate = getFirstTransactionDate(reduxStore.getState())

const { from, to } = !!group
? createRangeForGroup(group)
: {
from: firstTransactionDate,
to: new Date(),
}

const currency = getDefaultCurrency(reduxStore.getState())

await Promise.all([
reduxStore.dispatch(fetchFirstTransactionDate() as any),
reduxStore.dispatch(fetchStatsCategories(from, to, currency) as any),
])
await reduxStore.dispatch(fetchStatsCategories(from, to, currency) as any)

return { group }
}
Expand Down
5 changes: 4 additions & 1 deletion front/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ module.exports = nextRoutes()
.add({ pattern: '/hello', page: 'internal/hello' })
.add({ pattern: '/app', page: 'internal/app' })
.add({ pattern: '/app/stats', page: 'internal/stats' })
.add({ pattern: '/app/stats/categories/:group', page: 'internal/categories' })
.add({
pattern: '/app/stats/categories/:group?',
page: 'internal/categories',
})
.add({ pattern: '/app/history', page: 'internal/history' })
.add({ pattern: '/app/profile', page: 'internal/profile' })
39 changes: 39 additions & 0 deletions front/src/features/categories/Categories.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
.categories {
display: grid;

grid-template:
'aside chart'
/ 1fr 2fr;

gap: 16px;

@media (max-width: 1024px) {
grid-template:
'aside chart'
/ 1fr 1fr;
}

@media (max-width: 768px) {
grid-template:
'aside'
'chart';
}
}

.chart {
grid-area: chart;

& > * {
@media (max-width: 768px) {
margin-bottom: 32px;
}
}
}

.aside {
grid-area: aside;

& > *:not(:last-child) {
margin-bottom: 16px;
}
}
60 changes: 42 additions & 18 deletions front/src/features/categories/Categories.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,37 @@
import useMedia from 'use-media'
import { useMappedState } from 'redux-react-hook'
import { useMemo } from 'react'
import { useState } from 'react'

import { GroupBy } from '@shared/enum/GroupBy'
import { Container } from '@front/ui/components/layout/container'
import { getStatsCategoriesFetchingStatus } from '@front/domain/money/selectors/getStatsCategoriesFetchingStatus'
import { createRangeForGroup } from '@front/helpers/createRangeForGroup'
import { useMemoState } from '@front/domain/store'
import { getStatsCategories } from '@front/domain/money/selectors/getStatsCategories'
import { fetchStatsCategories } from '@front/domain/money/actions/fetchStatsCategories'
import { getDefaultCurrency } from '@front/domain/user/selectors/getDefaultCurrency'
import { PageHeader } from '@front/ui/components/layout/page-header'
import { Loader } from '@front/ui/components/layout/loader'
import { displayMoney } from '@shared/helpers/displayMoney'
import { PieChart } from '@front/ui/components/chart/pie-chart'

import { pushRoute } from '../routing'
import { displayMoney } from '@shared/helpers/displayMoney'
import useMedia from 'use-media'
import { PeriodChooser } from './features/period-chooser'
import { useDateRange } from './helpers/useDateRange'
import * as styles from './Categories.css'
import { GroupChooser } from './features/group-chooser'

interface Props {
group: GroupBy
group?: GroupBy
}

export const Categories = ({ group }: Props) => {
const fetching = useMappedState(getStatsCategoriesFetchingStatus)
const currency = useMappedState(getDefaultCurrency)
const isSmall = useMedia({ maxWidth: 768 })

const { from, to } = useMemo(() => createRangeForGroup(group), [group])
const [previousPeriodNumber, setPreviousPeriodNumber] = useState(0)

const { from, to } = useDateRange(previousPeriodNumber, group)

const stats = useMemoState(
() => getStatsCategories(from, to, currency),
Expand All @@ -38,18 +43,37 @@ export const Categories = ({ group }: Props) => {
<Container>
<PageHeader title="Categories" onBack={() => pushRoute('/app/stats')} />

<Loader status={fetching}>
<PieChart
dataSets={stats.get().map(({ category, outcome }) => ({
name: category,
data: outcome,
}))}
displayValue={value =>
displayMoney(currency)(value, { withPenny: false })
}
fitToContainer={isSmall}
/>
</Loader>
<section className={styles.categories}>
<aside className={styles.aside}>
<GroupChooser group={group} />
{group && (
<PeriodChooser
setPreviousPeriodNumber={setPreviousPeriodNumber}
previousPeriodNumber={previousPeriodNumber}
from={from}
to={to}
group={group}
/>
)}
</aside>

<div className={styles.chart}>
<Loader status={fetching}>
{stats.nonEmpty() && (
<PieChart
dataSets={stats.get().map(({ category, outcome }) => ({
name: category,
data: outcome,
}))}
displayValue={value =>
displayMoney(currency)(value, { withPenny: false })
}
fitToContainer={isSmall}
/>
)}
</Loader>
</div>
</section>
</Container>
)
}
49 changes: 49 additions & 0 deletions front/src/features/categories/features/group-chooser.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { GroupBy } from '@shared/enum/GroupBy'
import { Button, ButtonType } from '@front/ui/components/form/button'
import { Card } from '@front/ui/components/layout/card'
import { pushRoute } from '@front/features/routing'

interface Props {
group?: GroupBy
}

export const GroupChooser = ({ group }: Props) => {
const showYear = !group || group !== GroupBy.Year
const showMonth = !group || group !== GroupBy.Month
const showWhole = !!group

return (
<Card
title={'Group'}
actions={[
showYear && (
<Button
onClick={() => pushRoute('/app/stats/categories/year')}
type={ButtonType.Text}
>
Show year
</Button>
),
showMonth && (
<Button
onClick={() => pushRoute('/app/stats/categories/month')}
type={ButtonType.Text}
>
Show month
</Button>
),
showWhole && (
<Button
onClick={() => pushRoute('/app/stats/categories')}
type={ButtonType.Text}
>
Show all time
</Button>
),
].filter(Boolean)}
>
{!!group && <p>You see data for one {group}</p>}
{!group && <p>You see data for all time</p>}
</Card>
)
}
51 changes: 51 additions & 0 deletions front/src/features/categories/features/period-chooser.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { format } from 'date-fns'
import { GroupBy } from '@shared/enum/GroupBy'
import { useCallback } from 'react'
import { Button, ButtonType } from '@front/ui/components/form/button'
import { Card } from '@front/ui/components/layout/card'

interface Props {
previousPeriodNumber: number
setPreviousPeriodNumber: (t: (v: number) => number) => void
from: Date
to: Date
group: GroupBy
}

export const PeriodChooser = ({
from,
to,
group,
setPreviousPeriodNumber,
previousPeriodNumber,
}: Props) => {
const back = useCallback(() => setPreviousPeriodNumber(v => v + 1), [
setPreviousPeriodNumber,
])
const next = useCallback(() => setPreviousPeriodNumber(v => v - 1), [
setPreviousPeriodNumber,
])

return (
<Card
title={'Period'}
actions={[
<Button
onClick={back}
type={ButtonType.Text}
>{`Previous ${group}`}</Button>,
previousPeriodNumber > 0 && (
<Button
onClick={next}
type={ButtonType.Text}
>{`Next ${group}`}</Button>
),
].filter(Boolean)}
>
<p>
List of categories for the period from{' '}
<b>{format(from, 'YYYY.MM.DD')}</b> to <b>{format(to, 'YYYY.MM.DD')}</b>
</p>
</Card>
)
}
23 changes: 23 additions & 0 deletions front/src/features/categories/helpers/useDateRange.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { useMemo } from 'react'
import { useMappedState } from 'redux-react-hook'

import { createRangeForGroup } from '@front/helpers/createRangeForGroup'
import { GroupBy } from '@shared/enum/GroupBy'
import { getFirstTransactionDate } from '@front/domain/money/selectors/getFirstTransactionDate'

export const useDateRange = (previousPeriodNumber: number, group?: GroupBy) => {
const firstTransactionDate = useMappedState(getFirstTransactionDate)

const { from, to } = useMemo(() => {
if (group) {
return createRangeForGroup(group, previousPeriodNumber)
}

return {
from: firstTransactionDate,
to: new Date(),
}
}, [group, previousPeriodNumber])

return { from, to }
}
10 changes: 10 additions & 0 deletions front/src/features/routing/transitions/animations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,14 @@ export const routeAnimations = [
nextRoute: '/app',
styles: backwards,
},
{
prevRoute: '/app/stats',
nextRoute: '/app/stats/(.+)',
styles: forwards,
},
{
prevRoute: '/app/stats/(.+)',
nextRoute: '/app/stats',
styles: backwards,
},
]
23 changes: 18 additions & 5 deletions front/src/helpers/createRangeForGroup.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,25 @@
import { startOfMonth, startOfYear, endOfYear, endOfMonth } from 'date-fns'
import {
startOfMonth,
startOfYear,
endOfYear,
endOfMonth,
subYears,
subMonths,
} from 'date-fns'

import { GroupBy } from '@shared/enum/GroupBy'

import { wantUTC } from './wantUTC'

export const createRangeForGroup = (group: GroupBy) => {
const start = group === GroupBy.Month ? startOfMonth : startOfYear
const end = group === GroupBy.Month ? endOfMonth : endOfYear
export const createRangeForGroup = (
group: GroupBy,
previousPeriodNumber = 0,
) => {
const minus = wantUTC(group === GroupBy.Month ? subMonths : subYears)
const now = minus(new Date(), previousPeriodNumber)

return { from: wantUTC(start)(new Date()), to: wantUTC(end)(new Date()) }
const start = wantUTC(group === GroupBy.Month ? startOfMonth : startOfYear)
const end = wantUTC(group === GroupBy.Month ? endOfMonth : endOfYear)

return { from: start(now), to: end(now) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const createOptions = (
},
},
legend: {
position: maintainAspectRatio ? 'bottom' : 'right',
position: !maintainAspectRatio ? 'bottom' : 'right',
},
maintainAspectRatio,
})

0 comments on commit 1362e27

Please sign in to comment.