Skip to content

Commit

Permalink
Start reports
Browse files Browse the repository at this point in the history
  • Loading branch information
bombies committed Nov 4, 2023
1 parent 3d8ba8c commit e10c0f0
Show file tree
Hide file tree
Showing 7 changed files with 187 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ const DashboardSidebar: FC = () => {
title="Search Dreams"
href="/dashboard/search"
/>
<SidebarItem
startContent={<StatisticsIcon/>}
title="Dream Reports"
href="/dashboard"
/>
{/*<SidebarItem*/}
{/* startContent={<StatisticsIcon/>}*/}
{/* title="Dream Reports"*/}
{/* href="/dashboard/reports"*/}
{/*/>*/}
</Sidebar>
</SidebarProvider>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"use client"

import {FC, Fragment} from "react";
import {fetcherWithArgs} from "@/utils/client/client-utils";
import {CountResponse, DreamReportsParams} from "@/app/api/me/dreams/reports/dream-reports.dto";
import useSWRMutation from "swr/mutation";
import {Button} from "@nextui-org/button";

const useCountInsight = () => (
useSWRMutation(`/api/me/dreams/reports/count`, fetcherWithArgs<DreamReportsParams, CountResponse>)
)

const DreamReportsContext: FC = () => {
const {trigger, isMutating} = useCountInsight()

return (
<Fragment>
<Button
onPress={() => {
trigger({
body: {
from: new Date().setHours(0, 0, 0, 0).toString(),
to: Date.now().toString(),
characters: "clokca7bu000f01l0095jkeo3"
}
})
}}
>
Fetch data
</Button>
</Fragment>
)
}

export default DreamReportsContext
18 changes: 18 additions & 0 deletions src/app/(site)/(internal)/dashboard/reports/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {FC, Fragment} from "react";
import DreamReportsContext from "@/app/(site)/(internal)/dashboard/reports/components/DreamReportsContext";
import {notFound} from "next/navigation";

const DreamReportsPage: FC = () => {
if (process.env.NODE_ENV !== "development")
notFound()

return (
<Fragment>
<h1 id="dash_reports_title"
className="font-bold text-7xl phone:text-4xl mb-24 phone:mb-16 w-fit">Dream Reports</h1>
<DreamReportsContext/>
</Fragment>
)
}

export default DreamReportsPage
8 changes: 8 additions & 0 deletions src/app/api/me/dreams/reports/count/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import {ApiRoute, authenticated} from "@/app/api/utils/api-utils";
import dreamReportsService from "@/app/api/me/dreams/reports/dream-reports-service";

export const GET: ApiRoute = async (req) => (
authenticated((session) => (
dreamReportsService.getDreamsAggregate(session, new URL(req.url).searchParams)
))
)
96 changes: 96 additions & 0 deletions src/app/api/me/dreams/reports/dream-reports-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import {Session} from "next-auth";
import {CountResponse, DreamReportsParamsSchema} from "@/app/api/me/dreams/reports/dream-reports.dto";
import {buildFailedValidationResponse, buildResponse} from "@/app/api/utils/types";
import prisma from "@/libs/prisma";
import {NextResponse} from "next/server";

class DreamReportsService {

public async getDreamsAggregate(session: Session, params: URLSearchParams): Promise<NextResponse<CountResponse | null>> {
const paramsValidated = DreamReportsParamsSchema.safeParse(params)
if (!paramsValidated.success)
return buildFailedValidationResponse(paramsValidated.error)

const {from, to, characters, tags} = paramsValidated.data
const charactersArr = characters?.split(",")
const tagsArr = tags?.split(",")

const count = await prisma.dream.aggregate({
where: {
...this.getTimeLimitedWhereQuery(session.user.id, from, to),
characters: charactersArr && {
every: {
id: {
in: charactersArr
}
}
},
tags: tagsArr && {
every: {
id: {
in: tagsArr
}
}
},
},
_count: {
id: true,
}
})

return buildResponse({
data: {count: count._count.id}
})
}

public async getCharactersAggregate(session: Session, params: URLSearchParams): Promise<NextResponse<CountResponse | null>> {
const paramsValidated = DreamReportsParamsSchema.safeParse(params)
if (!paramsValidated.success)
return buildFailedValidationResponse(paramsValidated.error)

const {from, to} = paramsValidated.data

const count = await prisma.dreamCharacter.aggregate({
where: this.getTimeLimitedWhereQuery(session.user.id, from, to),
_count: {
id: true,
}
})

return buildResponse({
data: {count: count._count.id}
})
}

public async getTagsAggregate(session: Session, params: URLSearchParams): Promise<NextResponse<CountResponse | null>> {
const paramsValidated = DreamReportsParamsSchema.safeParse(params)
if (!paramsValidated.success)
return buildFailedValidationResponse(paramsValidated.error)

const {from, to} = paramsValidated.data

const count = await prisma.dreamTag.aggregate({
where: this.getTimeLimitedWhereQuery(session.user.id, from, to),
_count: {
id: true,
}
})

return buildResponse({
data: {count: count._count.id}
})
}

private getTimeLimitedWhereQuery(userId: string, from?: string, to?: string) {
return ({
userId,
createdAt: {
gte: from ? new Date(Number(from)) : undefined,
lte: to ? new Date(Number(to)) : undefined
}
})
}
}

const dreamReportsService = new DreamReportsService()
export default dreamReportsService
20 changes: 20 additions & 0 deletions src/app/api/me/dreams/reports/dream-reports.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import {z} from "zod";
import {zfd} from "zod-form-data";

export type CountResponse = {
count: number
}

export type DreamReportsParams = {
from?: string,
to?: string,
tags?: string,
characters?: string
}

export const DreamReportsParamsSchema = zfd.formData({
from: z.string().regex(/[0-9]+/, "\"from\" must be a valid number!").optional(),
to: z.string().regex(/[0-9]+/, "\"to\" must be a valid number!").optional(),
tags: z.string().optional(),
characters: z.string().optional()
})
6 changes: 5 additions & 1 deletion src/app/api/utils/api-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@ import prisma from "@/libs/prisma";
import PrismaClientKnownRequestError = Prisma.PrismaClientKnownRequestError;
import RateLimiter, {RateLimiterGeoLimit} from "@/app/api/utils/rate-limiter";

export type RouteContext<T extends { [K: string]: string }> = {
export type RouteContext<T extends { [K: string]: string } | unknown> = {
params: T
}

export type ApiRoute<T extends {
[K: string]: string
} | unknown = unknown> = (request: NextRequest, context: RouteContext<T>) => Promise<NextResponse<any>>

export type IdObject = { id: string }

type PrismaErrorOptions = {
Expand Down

0 comments on commit e10c0f0

Please sign in to comment.