diff --git a/frontend/src/api/api.ts b/frontend/src/api/api.ts index c0400bd..a53a21e 100644 --- a/frontend/src/api/api.ts +++ b/frontend/src/api/api.ts @@ -79,13 +79,17 @@ export const getRecentListens = async ( export const getListensReport = async ( options: ListenReportOptions ): Promise => { - const { timeFrame, timeStart, timeEnd } = options; + const { + timeFrame, + time: { timePreset, customTimeStart, customTimeEnd }, + } = options; const res = await fetch( `/api/v1/reports/listens?${qs({ timeFrame, - timeStart: formatISO(timeStart), - timeEnd: formatISO(timeEnd), + timePreset, + customTimeStart: formatISO(customTimeStart), + customTimeEnd: formatISO(customTimeEnd), })}`, { headers: getDefaultHeaders(), diff --git a/frontend/src/api/entities/listen-report-options.ts b/frontend/src/api/entities/listen-report-options.ts index c8b2006..9596457 100644 --- a/frontend/src/api/entities/listen-report-options.ts +++ b/frontend/src/api/entities/listen-report-options.ts @@ -1,5 +1,6 @@ +import { TimeOptions } from "./time-options"; + export interface ListenReportOptions { timeFrame: "day" | "week" | "month" | "year"; - timeStart: Date; - timeEnd: Date; + time: TimeOptions; } diff --git a/frontend/src/components/ReportListens.tsx b/frontend/src/components/ReportListens.tsx index 87b0ab7..3d0776c 100644 --- a/frontend/src/components/ReportListens.tsx +++ b/frontend/src/components/ReportListens.tsx @@ -1,5 +1,5 @@ import { format, getTime } from "date-fns"; -import React, { useEffect, useMemo, useState } from "react"; +import React, { useMemo, useState } from "react"; import { Redirect } from "react-router-dom"; import { Area, @@ -14,21 +14,29 @@ import { import { getListensReport } from "../api/api"; import { ListenReportItem } from "../api/entities/listen-report-item"; import { ListenReportOptions } from "../api/entities/listen-report-options"; +import { TimeOptions } from "../api/entities/time-options"; +import { TimePreset } from "../api/entities/time-preset.enum"; import { useAsync } from "../hooks/use-async"; import { useAuth } from "../hooks/use-auth"; +import { ReportTimeOptions } from "./ReportTimeOptions"; export const ReportListens: React.FC = () => { const { user } = useAuth(); - const [reportOptions, setReportOptions] = useState({ - timeFrame: "day", - timeStart: new Date("2020-05-01"), - timeEnd: new Date(), + const [timeFrame, setTimeFrame] = useState<"day" | "week" | "month" | "year">( + "day" + ); + + const [timeOptions, setTimeOptions] = useState({ + timePreset: TimePreset.LAST_7_DAYS, + customTimeStart: new Date(0), + customTimeEnd: new Date(), }); - const fetchData = useMemo(() => () => getListensReport(reportOptions), [ - reportOptions, - ]); + const fetchData = useMemo( + () => () => getListensReport({ timeFrame, time: timeOptions }), + [timeFrame, timeOptions] + ); const { value: report, pending: isLoading } = useAsync(fetchData, []); @@ -51,14 +59,9 @@ export const ReportListens: React.FC = () => { + {isLoading && (
@@ -80,7 +87,7 @@ export const ReportListens: React.FC = () => { )} {reportHasItems && (
- +
)}
@@ -90,9 +97,9 @@ export const ReportListens: React.FC = () => { }; const ReportGraph: React.FC<{ - options: ListenReportOptions; + timeFrame: ListenReportOptions["timeFrame"]; data: ListenReportItem[]; -}> = ({ options, data }) => { +}> = ({ timeFrame, data }) => { const dataLocal = data.map(({ date, ...other }) => ({ ...other, date: getTime(date), @@ -108,10 +115,7 @@ const ReportGraph: React.FC<{ } const [{ value: listens }] = payload; - const date = format( - label as number, - dateFormatFromTimeFrame(options.timeFrame) - ); + const date = format(label as number, dateFormatFromTimeFrame(timeFrame)); return (
@@ -145,7 +149,7 @@ const ReportGraph: React.FC<{ domain={["auto", "auto"]} dataKey="date" tickFormatter={(date) => - format(date, shortDateFormatFromTimeFrame(options.timeFrame)) + format(date, shortDateFormatFromTimeFrame(timeFrame)) } /> diff --git a/src/reports/dto/get-listen-report.dto.ts b/src/reports/dto/get-listen-report.dto.ts index 96c8f39..f9f482c 100644 --- a/src/reports/dto/get-listen-report.dto.ts +++ b/src/reports/dto/get-listen-report.dto.ts @@ -1,6 +1,7 @@ -import { IsEnum, IsISO8601 } from "class-validator"; +import { IsEnum, ValidateNested } from "class-validator"; import { User } from "../../users/user.entity"; import { Timeframe } from "../timeframe.enum"; +import { ReportTimeDto } from "./report-time.dto"; export class GetListenReportDto { user: User; @@ -8,9 +9,6 @@ export class GetListenReportDto { @IsEnum(Timeframe) timeFrame: Timeframe; - @IsISO8601() - timeStart: string; - - @IsISO8601() - timeEnd: string; + @ValidateNested() + time: ReportTimeDto; } diff --git a/src/reports/dto/listen-report.dto.ts b/src/reports/dto/listen-report.dto.ts index b6f522c..dd2e22c 100644 --- a/src/reports/dto/listen-report.dto.ts +++ b/src/reports/dto/listen-report.dto.ts @@ -5,10 +5,4 @@ export class ListenReportDto { date: string; count: number; }[]; - - timeFrame: Timeframe; - - timeStart: string; - - timeEnd: string; } diff --git a/src/reports/reports.controller.ts b/src/reports/reports.controller.ts index 54e0462..f8cfc32 100644 --- a/src/reports/reports.controller.ts +++ b/src/reports/reports.controller.ts @@ -2,11 +2,11 @@ import { Controller, Get, Query } from "@nestjs/common"; import { Auth } from "src/auth/decorators/auth.decorator"; import { ReqUser } from "../auth/decorators/req-user.decorator"; import { User } from "../users/user.entity"; -import { GetListenReportDto } from "./dto/get-listen-report.dto"; -import { GetTopArtistsReportDto } from "./dto/get-top-artists-report.dto"; import { ListenReportDto } from "./dto/listen-report.dto"; +import { ReportTimeDto } from "./dto/report-time.dto"; import { TopArtistsReportDto } from "./dto/top-artists-report.dto"; import { ReportsService } from "./reports.service"; +import { Timeframe } from "./timeframe.enum"; @Controller("api/v1/reports") export class ReportsController { @@ -15,10 +15,11 @@ export class ReportsController { @Get("listens") @Auth() async getListens( - @Query() options: GetListenReportDto, + @Query() time: ReportTimeDto, + @Query("timeFrame") timeFrame: Timeframe, @ReqUser() user: User ): Promise { - return this.reportsService.getListens({ ...options, user }); + return this.reportsService.getListens({ user, timeFrame, time }); } @Get("top-artists") diff --git a/src/reports/reports.service.ts b/src/reports/reports.service.ts index b4fa1a8..4e249ef 100644 --- a/src/reports/reports.service.ts +++ b/src/reports/reports.service.ts @@ -65,14 +65,9 @@ export class ReportsService { constructor(private readonly listensService: ListensService) {} async getListens(options: GetListenReportDto): Promise { - const { user, timeFrame, timeStart, timeEnd } = options; + const { user, timeFrame, time: timePreset } = options; - // Function should eventually be rewritten to accept a timepreset - const interval = this.getIntervalFromPreset({ - timePreset: TimePreset.CUSTOM, - customTimeStart: timeStart, - customTimeEnd: timeEnd, - }); + const interval = this.getIntervalFromPreset(timePreset); const { items: listens } = await this.listensService.getListens({ user, @@ -81,20 +76,15 @@ export class ReportsService { limit: PAGINATION_LIMIT_UNLIMITED, }); - const reportInterval: Interval = { - start: parseISO(timeStart), - end: parseISO(timeEnd), - }; - const { eachOfInterval, isSame } = timeframeToDateFns[timeFrame]; - const reportItems = eachOfInterval(reportInterval).map((date) => { + const reportItems = eachOfInterval(interval).map((date) => { const count = listens.filter((listen) => isSame(date, listen.playedAt)) .length; return { date: formatISO(date), count }; }); - return { items: reportItems, timeStart, timeEnd, timeFrame }; + return { items: reportItems }; } async getTopArtists(