Skip to content

Commit

Permalink
add date range picker
Browse files Browse the repository at this point in the history
  • Loading branch information
gurgelio committed Sep 26, 2024
1 parent f223191 commit 44a094b
Show file tree
Hide file tree
Showing 7 changed files with 244 additions and 5 deletions.
53 changes: 53 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-popover": "^1.1.1",
"@radix-ui/react-select": "^2.1.1",
"@radix-ui/react-separator": "^1.1.0",
"@radix-ui/react-slot": "^1.1.0",
Expand All @@ -29,6 +30,7 @@
"lucide-react": "^0.427.0",
"match-sorter": "^6.3.4",
"react": "^18.3.1",
"react-day-picker": "^8.10.1",
"react-dom": "^18.3.1",
"react-helmet-async": "^2.0.5",
"react-hook-form": "^7.52.2",
Expand Down
6 changes: 4 additions & 2 deletions src/api/metrics.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { api } from "@/lib/axios";
import type { DateRange } from "react-day-picker";

interface GetDayOrdersAmountResponse {
amount: number;
Expand All @@ -22,7 +23,7 @@ type GetPopularProductsResponse = {

type GetDailyRevenueInPeriodResponse = {
date: string;
receitp: number;
receipt: number;
}[];

export async function getDayOrdersAmount() {
Expand Down Expand Up @@ -60,9 +61,10 @@ export async function getPopularProducts() {
return response.data;
}

export async function getDailyRevenueInPeriod() {
export async function getDailyRevenueInPeriod(dateRange?: DateRange) {
const response = await api.get<GetDailyRevenueInPeriodResponse>(
"/metrics/daily-receipt-in-period",
{ params: dateRange },
);
return response.data;
}
64 changes: 64 additions & 0 deletions src/components/ui/calendar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { ChevronLeft, ChevronRight } from "lucide-react";
import type * as React from "react";
import { DayPicker } from "react-day-picker";

import { buttonVariants } from "@/components/ui/button";
import { cn } from "@/lib/utils";

export type CalendarProps = React.ComponentProps<typeof DayPicker>;

function Calendar({
className,
classNames,
showOutsideDays = true,
...props
}: CalendarProps) {
return (
<DayPicker
showOutsideDays={showOutsideDays}
className={cn("p-3", className)}
classNames={{
months: "flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0",
month: "space-y-4",
caption: "flex justify-center pt-1 relative items-center",
caption_label: "text-sm font-medium",
nav: "space-x-1 flex items-center",
nav_button: cn(
buttonVariants({ variant: "outline" }),
"h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100",
),
nav_button_previous: "absolute left-1",
nav_button_next: "absolute right-1",
table: "w-full border-collapse space-y-1",
head_row: "flex",
head_cell:
"text-muted-foreground rounded-md w-9 font-normal text-[0.8rem]",
row: "flex w-full mt-2",
cell: "h-9 w-9 text-center text-sm p-0 relative [&:has([aria-selected].day-range-end)]:rounded-r-md [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected])]:bg-accent first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md focus-within:relative focus-within:z-20",
day: cn(
buttonVariants({ variant: "ghost" }),
"h-9 w-9 p-0 font-normal aria-selected:opacity-100",
),
day_range_end: "day-range-end",
day_selected:
"bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
day_today: "bg-accent text-accent-foreground",
day_outside:
"day-outside text-muted-foreground opacity-50 aria-selected:bg-accent/50 aria-selected:text-muted-foreground aria-selected:opacity-30",
day_disabled: "text-muted-foreground opacity-50",
day_range_middle:
"aria-selected:bg-accent aria-selected:text-accent-foreground",
day_hidden: "invisible",
...classNames,
}}
components={{
IconLeft: () => <ChevronLeft className="h-4 w-4" />,
IconRight: () => <ChevronRight className="h-4 w-4" />,
}}
{...props}
/>
);
}
Calendar.displayName = "Calendar";

export { Calendar };
65 changes: 65 additions & 0 deletions src/components/ui/date-range-picker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { format } from "date-fns";
import { Calendar as CalendarIcon } from "lucide-react";
import type { DateRange } from "react-day-picker";

import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { cn } from "@/lib/utils";
import type { ComponentProps } from "react";

interface DateRangePickerProps extends ComponentProps<"div"> {
date: DateRange | undefined;
onDateChange: (date: DateRange | undefined) => void;
}

export function DateRangePicker({
className,
date,
onDateChange,
}: DateRangePickerProps) {
return (
<div className={cn("grid gap-2", className)}>
<Popover>
<PopoverTrigger asChild>
<Button
id="date"
variant={"outline"}
className={cn(
"w-[300px] justify-start text-left font-normal",
!date && "text-muted-foreground",
)}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{date?.from ? (
date.to ? (
<>
{format(date.from, "LLL dd, y")} -{" "}
{format(date.to, "LLL dd, y")}
</>
) : (
format(date.from, "LLL dd, y")
)
) : (
<span>Pick a date</span>
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
initialFocus
mode="range"
defaultMonth={date?.from}
selected={date}
onSelect={onDateChange}
numberOfMonths={2}
/>
</PopoverContent>
</Popover>
</div>
);
}
29 changes: 29 additions & 0 deletions src/components/ui/popover.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import * as PopoverPrimitive from "@radix-ui/react-popover";
import * as React from "react";

import { cn } from "@/lib/utils";

const Popover = PopoverPrimitive.Root;

const PopoverTrigger = PopoverPrimitive.Trigger;

const PopoverContent = React.forwardRef<
React.ElementRef<typeof PopoverPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
<PopoverPrimitive.Portal>
<PopoverPrimitive.Content
ref={ref}
align={align}
sideOffset={sideOffset}
className={cn(
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=closed]:animate-out data-[state=open]:animate-in",
className,
)}
{...props}
/>
</PopoverPrimitive.Portal>
));
PopoverContent.displayName = PopoverPrimitive.Content.displayName;

export { Popover, PopoverTrigger, PopoverContent };
30 changes: 27 additions & 3 deletions src/pages/app/dashboard/revenue-chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ import {
import colors from "tailwindcss/colors";

import { getDailyRevenueInPeriod } from "@/api/metrics";
import { DateRangePicker } from "@/components/ui/date-range-picker";
import { Label } from "@/components/ui/label";
import { useQuery } from "@tanstack/react-query";
import { subDays } from "date-fns";
import { useMemo, useState } from "react";
import type { DateRange } from "react-day-picker";
import {
CartesianGrid,
Line,
Expand All @@ -19,11 +24,25 @@ import {
} from "recharts";

export function RevenueChart() {
const [dateRange, setDateRange] = useState<DateRange | undefined>({
from: subDays(new Date(), 7),
to: new Date(),
});

const { data } = useQuery({
queryKey: ["metrics", "daily-revenue-in-period"],
queryFn: getDailyRevenueInPeriod,
queryKey: ["metrics", "daily-revenue-in-period", dateRange],
queryFn: () => getDailyRevenueInPeriod(dateRange),
});

const chartData = useMemo(
() =>
data?.map((chartItem) => ({
date: chartItem.date,
receipt: chartItem.receipt / 100,
})),
[data],
);

return (
<Card className="col-span-6">
<CardHeader className="justify-betwaeen flex-row items-center pb-8">
Expand All @@ -33,10 +52,15 @@ export function RevenueChart() {
</CardTitle>
<CardDescription>Receita diária no período</CardDescription>
</div>

<div className="ml-auto flex items-center gap-3">
<Label>Período</Label>
<DateRangePicker date={dateRange} onDateChange={setDateRange} />
</div>
</CardHeader>
<CardContent>
<ResponsiveContainer width="100%" height={240}>
<LineChart data={data} className="!text-xs">
<LineChart data={chartData} className="!text-xs">
<XAxis
dataKey="date"
tickLine={false}
Expand Down

0 comments on commit 44a094b

Please sign in to comment.