Skip to content

Commit 3c9ae8e

Browse files
committed
feat: dashboard filter
1 parent 1f48d7d commit 3c9ae8e

File tree

3 files changed

+168
-25
lines changed

3 files changed

+168
-25
lines changed

app/(protected)/dashboard/page.tsx

Lines changed: 79 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,17 @@ import type { Metadata } from 'next'
1717
import Link from 'next/link'
1818
import { LibrarySelector } from '@/components/dashboard/LibrarySelector'
1919
import { DateRangeSelector } from '@/components/dashboard/DateRangeSelector'
20+
import { IsLoggedIn } from '@/lib/firebase/firebase'
21+
import { redirect } from 'next/navigation'
22+
import { format, subMonths, parse } from 'date-fns'
23+
import { getListLibraries } from '@/lib/api/library'
24+
import { DateRange } from 'react-day-picker'
2025

2126
export const metadata: Metadata = {
2227
title: `Dashboard · ${SITE_NAME}`,
2328
}
2429

25-
export default async function Dashboard({
30+
export default async function DashboardPage({
2631
searchParams,
2732
}: {
2833
searchParams: Promise<{
@@ -33,25 +38,75 @@ export default async function Dashboard({
3338
skip: number
3439
}>
3540
}) {
41+
const claims = await IsLoggedIn()
42+
if (!claims) redirect('/login?from=/dashboard')
43+
3644
const {
37-
library_id = 'a32af9bd-74b0-4ef2-8974-4570e2bfb4bb',
38-
from = '2024-01-05T17:00:47.680Z',
39-
to = new Date().toJSON(),
40-
limit = 5,
41-
skip,
42-
} = await searchParams
43-
const res = await getAnalysis({
44-
limit,
45-
skip,
45+
library_id,
4646
from,
4747
to,
48-
library_id,
49-
})
50-
console.log(res)
48+
// limit = 5,
49+
// skip,
50+
} = await searchParams
51+
52+
if (claims.librarease.role != 'USER' && (!to || !from || !library_id)) {
53+
const now = new Date()
54+
const to = format(now, 'dd-MM-yyyy')
55+
const from = format(subMonths(now, 4), 'dd-MM-yyyy')
56+
const libID = claims.librarease.admin_libs.concat(
57+
claims.librarease.staff_libs
58+
)
59+
const sp = new URLSearchParams()
60+
sp.set('from', from)
61+
sp.set('to', to)
62+
sp.set('library_id', libID[0])
63+
64+
redirect('?' + sp.toString())
65+
}
66+
67+
const [res, libsRes] = await Promise.all([
68+
getAnalysis({
69+
skip: 0,
70+
limit: 5,
71+
from: parse(from, 'dd-MM-yyyy', new Date()).toJSON(),
72+
to: parse(to, 'dd-MM-yyyy', new Date()).toJSON(),
73+
library_id,
74+
}),
75+
getListLibraries({ limit: 5 }),
76+
])
5177

5278
if ('error' in res) {
53-
console.log(res)
54-
return <div>{JSON.stringify(res.message)}</div>
79+
return <div>{res.error}</div>
80+
}
81+
if ('error' in libsRes) {
82+
return <div>{libsRes.error}</div>
83+
}
84+
85+
async function onLibraryChange(libraryID: string) {
86+
'use server'
87+
88+
const p = await searchParams
89+
// @ts-expect-error: skip and imit are not used now
90+
const sp = new URLSearchParams(p)
91+
sp.set('library_id', libraryID)
92+
93+
redirect('/dashboard?' + sp.toString())
94+
}
95+
96+
const fromDate = parse(from, 'dd-MM-yyyy', new Date())
97+
const toDate = parse(to, 'dd-MM-yyyy', new Date())
98+
99+
async function onDateRangeChange(range?: DateRange) {
100+
'use server'
101+
102+
const p = await searchParams
103+
// @ts-expect-error: skip and imit are not used now
104+
const sp = new URLSearchParams(p)
105+
if (!range) return
106+
if (range.from) sp.set('from', format(range.from, 'dd-MM-yyyy'))
107+
if (range.to) sp.set('to', format(range.to, 'dd-MM-yyyy'))
108+
109+
redirect('/dashboard?' + sp.toString())
55110
}
56111

57112
return (
@@ -73,8 +128,15 @@ export default async function Dashboard({
73128
</Breadcrumb>
74129

75130
<div className="grid my-4 grid-cols-1 gap-4 md:grid-cols-2">
76-
<LibrarySelector />
77-
<DateRangeSelector />
131+
<LibrarySelector
132+
libs={libsRes.data}
133+
lib={library_id}
134+
onChangeAction={onLibraryChange}
135+
/>
136+
<DateRangeSelector
137+
range={{ from: fromDate, to: toDate }}
138+
onChangeAction={onDateRangeChange}
139+
/>
78140
<MostBorrowedBookChart data={res.data.book} />
79141
<TopMembershipChart data={res.data.membership} />
80142
<MontlyBorrowChart data={res.data.borrowing} />

components/dashboard/DateRangeSelector.tsx

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client'
22

33
import * as React from 'react'
4-
import { addDays, format } from 'date-fns'
4+
import { format, subMonths, startOfMonth, endOfMonth } from 'date-fns'
55
import { Calendar as CalendarIcon } from 'lucide-react'
66
import { DateRange } from 'react-day-picker'
77

@@ -13,17 +13,37 @@ import {
1313
PopoverContent,
1414
PopoverTrigger,
1515
} from '@/components/ui/popover'
16+
import {
17+
Select,
18+
SelectContent,
19+
SelectItem,
20+
SelectTrigger,
21+
SelectValue,
22+
} from '../ui/select'
1623

1724
export function DateRangeSelector({
1825
className,
19-
}: React.HTMLAttributes<HTMLDivElement>) {
26+
range,
27+
onChangeAction,
28+
}: React.HTMLAttributes<HTMLDivElement> & {
29+
range: DateRange
30+
onChangeAction: (range?: DateRange) => void
31+
}) {
2032
const [date, setDate] = React.useState<DateRange | undefined>({
21-
from: new Date(2022, 0, 20),
22-
to: addDays(new Date(2022, 0, 20), 20),
33+
from: range.from,
34+
to: range.to,
2335
})
2436

37+
const onInteractOutside = onChangeAction.bind(null, date)
38+
39+
const onPresetChange = React.useCallback(
40+
(label: string) =>
41+
setDate(rangePresets.find((r) => r.label === label)?.date),
42+
[]
43+
)
44+
2545
return (
26-
<div className={cn('grid gap-2', className)}>
46+
<div className={cn('grid gap-2 justify-self-end', className)}>
2747
<Popover>
2848
<PopoverTrigger asChild>
2949
<Button
@@ -49,7 +69,23 @@ export function DateRangeSelector({
4969
)}
5070
</Button>
5171
</PopoverTrigger>
52-
<PopoverContent className="w-auto p-0" align="start">
72+
<PopoverContent
73+
onInteractOutside={onInteractOutside}
74+
className="flex w-auto flex-col space-y-2 p-2"
75+
align="start"
76+
>
77+
<Select onValueChange={onPresetChange}>
78+
<SelectTrigger>
79+
<SelectValue placeholder="Select" />
80+
</SelectTrigger>
81+
<SelectContent position="popper">
82+
{rangePresets.map((r) => (
83+
<SelectItem key={r.label} value={r.label}>
84+
{r.label}
85+
</SelectItem>
86+
))}
87+
</SelectContent>
88+
</Select>
5389
<Calendar
5490
initialFocus
5591
mode="range"
@@ -63,3 +99,17 @@ export function DateRangeSelector({
6399
</div>
64100
)
65101
}
102+
103+
const rangePresets: { label: string; date: DateRange }[] = [
104+
{
105+
label: 'Last Month',
106+
date: {
107+
from: startOfMonth(subMonths(new Date(), 1)),
108+
to: endOfMonth(subMonths(new Date(), 1)),
109+
},
110+
},
111+
{
112+
label: 'Past 4 Months',
113+
date: { from: subMonths(new Date(), 4), to: new Date() },
114+
},
115+
]
Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,36 @@
11
'use client'
22

3-
export function LibrarySelector() {
4-
return <div>Select library</div>
3+
import {
4+
Select,
5+
SelectContent,
6+
SelectGroup,
7+
SelectItem,
8+
SelectLabel,
9+
SelectTrigger,
10+
SelectValue,
11+
} from '@/components/ui/select'
12+
import { Library } from '@/lib/types/library'
13+
14+
export const LibrarySelector: React.FC<{
15+
libs: Library[]
16+
lib: string
17+
onChangeAction: (libID: string) => void
18+
}> = ({ libs, lib, onChangeAction: onChange }) => {
19+
return (
20+
<Select value={lib} onValueChange={onChange}>
21+
<SelectTrigger className="w-1/2 justify-self-end">
22+
<SelectValue placeholder="Select a library" />
23+
</SelectTrigger>
24+
<SelectContent>
25+
<SelectGroup>
26+
<SelectLabel>Library</SelectLabel>
27+
{libs.map((lib) => (
28+
<SelectItem value={lib.id} key={lib.id}>
29+
{lib.name}
30+
</SelectItem>
31+
))}
32+
</SelectGroup>
33+
</SelectContent>
34+
</Select>
35+
)
536
}

0 commit comments

Comments
 (0)