From 91ea30f71577fa58b1c40047b8868600651e35c3 Mon Sep 17 00:00:00 2001 From: Roland Bernard <rolbernard@unibz.it> Date: Sun, 20 Jun 2021 20:35:33 +0200 Subject: [PATCH] Added time selection to user and project stats --- .../src/components/navigation/Tabs/index.tsx | 4 +- .../ProjectDetail/ProjectDetails/index.tsx | 47 +++++++++++++-- .../pages/Projects/ProjectDetail/index.tsx | 2 +- client/src/pages/Stats/index.tsx | 58 +++++++++---------- client/src/pages/Teams/TeamsStats/index.tsx | 16 ++--- 5 files changed, 80 insertions(+), 47 deletions(-) diff --git a/client/src/components/navigation/Tabs/index.tsx b/client/src/components/navigation/Tabs/index.tsx index 5f053b8..9239426 100644 --- a/client/src/components/navigation/Tabs/index.tsx +++ b/client/src/components/navigation/Tabs/index.tsx @@ -6,7 +6,7 @@ import './tabs.scss'; export interface Tab { label: string; - route: string; + route: string | string[]; link?: string; component: ReactNode } @@ -24,7 +24,7 @@ export default function Tabs({ tabs }: Props) { key={tab.label} className="tab" activeClassName="active" - to={tab.link ?? tab.route} + to={tab.link ?? (typeof tab.route === 'string' ? tab.route : tab.route[0])} isActive={(_, location) => matchPath(location.pathname, { path: tab.route, exact: true }) ? true diff --git a/client/src/pages/Projects/ProjectDetail/ProjectDetails/index.tsx b/client/src/pages/Projects/ProjectDetail/ProjectDetails/index.tsx index cf083b7..d1f64bf 100644 --- a/client/src/pages/Projects/ProjectDetail/ProjectDetails/index.tsx +++ b/client/src/pages/Projects/ProjectDetail/ProjectDetails/index.tsx @@ -1,9 +1,11 @@ import { useEffect, useState } from 'react'; +import { useParams } from 'react-router'; import { getTeam } from 'adapters/team'; import { formatDate, subtractTime } from 'timely'; +import Dropdown from 'components/navigation/Dropdown'; import DetailGrid from 'components/layout/DetailGrid'; import LoadingScreen from 'components/ui/LoadingScreen'; import { getProjectActivity, Project } from 'adapters/project'; @@ -11,6 +13,16 @@ import BarChart, { ChartItem, parseActivity } from 'components/graphs/BarChart'; import './project-details.scss'; +enum Timespan { + WEEK = 'week', + MONTH = 'month', + YEAR = 'year', +} + +interface Params { + time?: Timespan; +} + interface Props { project: Project } @@ -19,13 +31,34 @@ export default function ProjectDetails({ project }: Props) { const [teams, setTeams] = useState<string[]>([]); const [activity, setActivity] = useState<ChartItem[]>([]); + const time = useParams<Params>().time ?? Timespan.WEEK; + const dropdowns = [ + { + time: 'week', + label: 'Last week', + route: '/projects/' + project.id + '/stats/week' + }, + { + time: 'month', + label: 'Last month', + route: '/projects/' + project.id + '/stats/month' + }, + { + time: 'year', + label: 'Last year', + route: '/projects/' + project.id + '/stats/year' + } + ]; + useEffect(() => { - project.teams.forEach(teamId => { - getTeam(teamId).then((team) => setTeams(prev => [...prev, team.name])); - }); - getProjectActivity(project.id, subtractTime(new Date(), 1, 'week'), new Date()).then((a) => setActivity(parseActivity(a))) + Promise.all(project.teams.map(getTeam)) + .then(teams => setTeams(teams.map(team => team.name))); }, [project]); + useEffect(() => { + getProjectActivity(project.id, subtractTime(new Date(), 1, time), new Date()).then((a) => setActivity(parseActivity(a))) + }, [project, time]); + let details = [{ icon: 'group', title: 'Teams', @@ -43,6 +76,12 @@ export default function ProjectDetails({ project }: Props) { return ( <section className="project-details"> <DetailGrid details={details} /> + <Dropdown items={dropdowns.filter(d => d.time !== time)}> + <span className="material-icons icon"> + expand_more + </span> + {dropdowns.find(d => d.time === time)?.label} + </Dropdown> { activity ? <BarChart unit="h" multiplier={1 / 60 / 60 / 1000} data={activity} /> diff --git a/client/src/pages/Projects/ProjectDetail/index.tsx b/client/src/pages/Projects/ProjectDetail/index.tsx index d345422..0060bf5 100644 --- a/client/src/pages/Projects/ProjectDetail/index.tsx +++ b/client/src/pages/Projects/ProjectDetail/index.tsx @@ -35,7 +35,7 @@ export default function ProjectDetail() { setTabs([ { label: 'Details', - route: '/projects/' + projectId, + route: ['/projects/' + projectId, '/projects/' + projectId + '/stats/:time'], component: <ProjectDetails project={project} /> }, { diff --git a/client/src/pages/Stats/index.tsx b/client/src/pages/Stats/index.tsx index d5a3121..630a4fe 100644 --- a/client/src/pages/Stats/index.tsx +++ b/client/src/pages/Stats/index.tsx @@ -7,7 +7,7 @@ import { getUserActivity, getUserCompletion } from 'adapters/user'; import LoadingScreen from 'components/ui/LoadingScreen'; import CompletionGrid from 'components/layout/CompletionGrid'; -import Dropdown, { DropDownItem } from 'components/navigation/Dropdown'; +import Dropdown from 'components/navigation/Dropdown'; import { CompletionProps, parseCompletion } from 'components/ui/Completion'; import BarChart, { ChartItem, parseActivity } from 'components/graphs/BarChart'; @@ -23,14 +23,12 @@ interface Params { time?: Timespan; } -interface FilterDropdownItem extends DropDownItem { - time: string -} - export default function Tasks() { const [completions, setCompletions] = useState<CompletionProps[]>(); const [activity, setActivity] = useState<ChartItem[]>(); - const [dropdowns] = useState<FilterDropdownItem[]>([ + + const time = useParams<Params>().time ?? Timespan.WEEK; + const dropdowns = [ { time: 'week', label: 'Last week', @@ -46,9 +44,7 @@ export default function Tasks() { label: 'Last year', route: '/stats/year' } - ]); - - const time = useParams<Params>().time ?? Timespan.WEEK; + ]; useEffect(() => { getUserCompletion().then((completion) => setCompletions(parseCompletion(completion))); @@ -56,28 +52,30 @@ export default function Tasks() { }, [time]); return ( - (completions && activity) - ? ( - <div className="stats-page"> - <div className="content-container"> - <h1 className="underlined">Stats</h1> - <div className="description-container"> - Here are some of your recent statistics. - </div> - <Dropdown items={dropdowns.filter(d => d.time !== time)}> - <span className="material-icons icon"> - expand_more - </span> - {dropdowns.find(d => d.time === time)?.label} - </Dropdown> - <h2>Activity</h2> - <BarChart unit="h" multiplier={1 / 60 / 60 / 1000} data={activity} /> - <h2>Completion</h2> - <CompletionGrid items={completions} /> - </div> + <div className="stats-page"> + <div className="content-container"> + <h1 className="underlined">Stats</h1> + <div className="description-container"> + Here are some of your recent statistics. </div> - ) - : <LoadingScreen /> + <Dropdown items={dropdowns.filter(d => d.time !== time)}> + <span className="material-icons icon"> + expand_more + </span> + {dropdowns.find(d => d.time === time)?.label} + </Dropdown> + <h2>Activity</h2> + { activity + ? <BarChart unit="h" multiplier={1 / 60 / 60 / 1000} data={activity} /> + : <LoadingScreen /> + } + <h2>Completion</h2> + { completions + ? <CompletionGrid items={completions} /> + : <LoadingScreen /> + } + </div> + </div> ); } diff --git a/client/src/pages/Teams/TeamsStats/index.tsx b/client/src/pages/Teams/TeamsStats/index.tsx index 255ccf6..b56fc4e 100644 --- a/client/src/pages/Teams/TeamsStats/index.tsx +++ b/client/src/pages/Teams/TeamsStats/index.tsx @@ -7,7 +7,7 @@ import { getTeamActivity, getTeamCompletion } from 'adapters/team'; import LoadingScreen from 'components/ui/LoadingScreen'; import CompletionGrid from 'components/layout/CompletionGrid'; -import Dropdown, { DropDownItem } from 'components/navigation/Dropdown'; +import Dropdown from 'components/navigation/Dropdown'; import { CompletionProps, parseCompletion } from 'components/ui/Completion'; import BarChart, { ChartItem, parseActivity } from 'components/graphs/BarChart'; @@ -27,14 +27,13 @@ interface Params { time: Timespan; } -interface FilterDropdownItem extends DropDownItem { - time: string -} - export default function TeamsStats({ teamId }: Props) { const [activity, setActivity] = useState<ChartItem[]>([]); const [completions, setCompletions] = useState<CompletionProps[]>([]); - const [dropdowns] = useState<FilterDropdownItem[]>([ + const history = useHistory(); + + const { time } = useParams<Params>(); + const dropdowns = [ { time: 'week', label: 'Last week', @@ -50,10 +49,7 @@ export default function TeamsStats({ teamId }: Props) { label: 'Last year', route: '/teams/' + teamId + '/stats/year' } - ]); - const history = useHistory(); - - const { time } = useParams<Params>(); + ]; useEffect(() => { getTeamActivity(teamId, subtractTime(new Date(), 1, time), new Date()) -- GitLab