diff --git a/src/components/Chart/Chart.tsx b/src/components/Chart/Chart.tsx index 4b80aa9..6d6c277 100644 --- a/src/components/Chart/Chart.tsx +++ b/src/components/Chart/Chart.tsx @@ -1,36 +1,96 @@ import { useState, useEffect } from 'react'; import Highcharts from 'highcharts'; import { CircularProgress } from '@mui/material'; -import { chartOptions } from '../../data/chart-options'; +import { getTotalStatus } from '../../utils/get-mock-data'; +import { useDateRange } from '../../hooks/useDateRange'; +import dayjs from 'dayjs'; + +import type { Options, SeriesOptionsType } from 'highcharts'; +import type { TotalStatus } from '../../types/status'; import './Chart.css'; const Chart = () => { const [isLoading, setIsLoading] = useState(true); + const { date } = useDateRange(); + useEffect(() => { - // simulate fetch - try { - setTimeout(() => { + let isMounted: boolean = true; + + const fetchData = async () => { + try { + await new Promise((resolve) => setTimeout(resolve, 1100)); + + if (!isMounted) return; + + const chartTitle: string = 'Status Totals'; + const chartCategoriesX: string[] = ['Open', 'Closed', 'In Progress']; + const chartTitleY: string = 'Total'; + + const totalStatus: TotalStatus = getTotalStatus(dayjs(date.startDate), dayjs(date.endDate)); + + const chartSeries: SeriesOptionsType[] = [ + { + data: [ + { + y: totalStatus.Open, + color: '#DEC5E3', + }, + { + y: totalStatus.Closed, + color: '#81F7E5', + }, + { + y: totalStatus['In Progress'], + color: '#B6DCFE', + }, + ], + }, + ] as SeriesOptionsType[]; + + const chartOptions = (): Options => ({ + chart: { + type: 'column', + }, + title: { + text: chartTitle, + }, + xAxis: { + categories: chartCategoriesX, + }, + yAxis: { + title: { + text: chartTitleY, + }, + }, + series: chartSeries, + accessibility: { + enabled: true, + }, + colors: ['#000000'], + }); + const columnChart: Highcharts.Options = chartOptions(); - // check if the element exists/is mounted before rendering - if (document.getElementById('chart1')) { + if (isMounted && document.getElementById('chart1')) { Highcharts.chart('chart1', columnChart); } - }, 1100); - // catch any thrown errors - } catch (err) { - console.error(err); - } + } catch (err) { + console.error(err); + } finally { + if (isMounted) { + setIsLoading(false); + } + } + }; + + fetchData(); - // clean up return () => { - setTimeout(() => { - setIsLoading(false); - }, 1100); + isMounted = false; }; - }, []); + }, [date.startDate, date.endDate]); return (
diff --git a/src/components/DataGridTable/DataGridTable.tsx b/src/components/DataGridTable/DataGridTable.tsx index 56ff4a9..92f3f48 100644 --- a/src/components/DataGridTable/DataGridTable.tsx +++ b/src/components/DataGridTable/DataGridTable.tsx @@ -17,20 +17,34 @@ const DataGridTable = () => { const [isLoading, setIsLoading] = useState(true); useEffect(() => { - setTimeout(() => { - // filter rows based on date range - const filtered: GridTable['rows'] = gridTable.rows.filter((field) => - isBetween(dayjs(field.DateCreated), dayjs(date.startDate), dayjs(date.endDate)) - ); + let isMounted: boolean = true; - setFilterRows(filtered); - }, 500); + const fetchData = async () => { + try { + await new Promise((resolve) => setTimeout(resolve, 1100)); + + if (!isMounted) return; + + // filter rows based on date range + const filtered: GridTable['rows'] = gridTable.rows.filter((field) => + isBetween(dayjs(field.DateCreated), dayjs(date.startDate), dayjs(date.endDate)) + ); + + setFilterRows(filtered); + } catch (err) { + console.error(err); + } finally { + if (isMounted) { + setIsLoading(false); + } + } + }; + + fetchData(); // clean up return () => { - setTimeout(() => { - setIsLoading(false); - }, 1100); + isMounted = false; }; }, [date]); diff --git a/src/data/data.ts b/src/data/data.ts index 1eed37d..03a60b6 100644 --- a/src/data/data.ts +++ b/src/data/data.ts @@ -1,53 +1,6 @@ +import data from './json/data.json'; + import type { MockData } from '../types/mockdata'; // mock data -export const mockData: MockData[] = [ - { - id: 1, - title: 'Set 1', - description: 'Description for Set 1', - dateCreated: '01/01/2024', - status: 'Open', - comments: 'No comments.', - }, - { - id: 2, - title: 'Set 2', - description: 'Description for Set 2', - dateCreated: '01/02/2024', - status: 'Open', - comments: 'No comments.', - }, - { - id: 3, - title: 'Set 3', - description: 'Description for Set 3', - dateCreated: '01/03/2024', - status: 'Closed', - comments: 'No comments.', - }, - { - id: 4, - title: 'Set 4', - description: 'Description for Set 4', - dateCreated: '01/04/2024', - status: 'Closed', - comments: 'No comments.', - }, - { - id: 5, - title: 'Set 5', - description: 'Description for Set 5', - dateCreated: '01/05/2024', - status: 'In Progress', - comments: 'No comments.', - }, - { - id: 6, - title: 'Set 6', - description: 'Description for Set 6', - dateCreated: '01/06/2024', - status: 'In Progress', - comments: 'No comments.', - }, -]; +export const mockData: MockData[] = JSON.parse(JSON.stringify(data)); diff --git a/src/data/json/data.json b/src/data/json/data.json new file mode 100644 index 0000000..e1382d6 --- /dev/null +++ b/src/data/json/data.json @@ -0,0 +1,50 @@ +[ + { + "id": 1, + "title": "Set 1", + "description": "Description for Set 1", + "dateCreated": "01/01/2024", + "status": "Open", + "comments": "No comments." + }, + { + "id": 2, + "title": "Set 2", + "description": "Description for Set 2", + "dateCreated": "01/02/2024", + "status": "Open", + "comments": "No comments." + }, + { + "id": 3, + "title": "Set 3", + "description": "Description for Set 3", + "dateCreated": "01/03/2024", + "status": "Closed", + "comments": "No comments." + }, + { + "id": 4, + "title": "Set 4", + "description": "Description for Set 4", + "dateCreated": "01/04/2024", + "status": "Closed", + "comments": "No comments." + }, + { + "id": 5, + "title": "Set 5", + "description": "Description for Set 5", + "dateCreated": "01/05/2024", + "status": "In Progress", + "comments": "No comments." + }, + { + "id": 6, + "title": "Set 6", + "description": "Description for Set 6", + "dateCreated": "01/06/2024", + "status": "In Progress", + "comments": "No comments." + } +] diff --git a/src/types/status.ts b/src/types/status.ts index a32313a..fece602 100644 --- a/src/types/status.ts +++ b/src/types/status.ts @@ -1 +1,7 @@ export type Status = 'Open' | 'Closed' | 'In Progress'; + +export type TotalStatus = { + Open: number; + Closed: number; + 'In Progress': number; +}; diff --git a/src/utils/get-mock-data.ts b/src/utils/get-mock-data.ts index ddd3d64..3f0ffc5 100644 --- a/src/utils/get-mock-data.ts +++ b/src/utils/get-mock-data.ts @@ -1,6 +1,24 @@ import { mockData } from '../data/data'; +import dayjs from 'dayjs'; -import type { Status } from '../types/status'; +import type { Dayjs } from 'dayjs'; +import type { MockData } from '../types/mockdata'; +import type { TotalStatus } from '../types/status'; + +/** + * Filter by date range. + * + * @param start Start date + * @param end End date + * @returns + */ +export function filterByDateRange(start: string | Dayjs, end: string | Dayjs): MockData[] { + const filter: MockData[] = mockData.filter( + (props) => dayjs(props.dateCreated) >= dayjs(start) && dayjs(props.dateCreated) <= dayjs(end) + ); + + return filter; +} /** * Get total mock data entries @@ -11,14 +29,35 @@ export function getTotalMockData(): number { return mockData?.length; } -export function getTotalStatus(status: Status): number { - let statusCount: number = 0; +/** + * Get total data entries by status. + * + * @param status Status to filter by + * @param start Start of date range + * @param end End of date range + * @returns Object with the total of each status + */ +export function getTotalStatus(start: Dayjs, end: Dayjs): TotalStatus { + let openCount: number = 0; + let closedCount: number = 0; + let inProgressCount: number = 0; - mockData.map((data) => { - if (data.status === status) { - statusCount++; + filterByDateRange(start, end).map((props) => { + switch (props.status) { + case 'Open': + openCount++; + break; + case 'Closed': + closedCount++; + break; + case 'In Progress': + inProgressCount++; } }); - return statusCount; + return { + Open: openCount, + Closed: closedCount, + 'In Progress': inProgressCount, + }; }