save, create and view reports in dashboard
This commit is contained in:
89
apps/web/src/components/report/ReportDateRange.tsx
Normal file
89
apps/web/src/components/report/ReportDateRange.tsx
Normal file
@@ -0,0 +1,89 @@
|
||||
import { useDispatch, useSelector } from "@/redux";
|
||||
import { RadioGroup, RadioGroupItem } from "../ui/radio-group";
|
||||
import { changeDateRanges, changeInterval } from "./reportSlice";
|
||||
import { Combobox } from "../ui/combobox";
|
||||
import { type IInterval } from "@/types";
|
||||
|
||||
export function ReportDateRange() {
|
||||
const dispatch = useDispatch();
|
||||
const interval = useSelector((state) => state.report.interval);
|
||||
|
||||
return (
|
||||
<>
|
||||
<RadioGroup>
|
||||
<RadioGroupItem
|
||||
onClick={() => {
|
||||
dispatch(changeDateRanges(1));
|
||||
}}
|
||||
>
|
||||
Today
|
||||
</RadioGroupItem>
|
||||
<RadioGroupItem
|
||||
onClick={() => {
|
||||
dispatch(changeDateRanges(7));
|
||||
}}
|
||||
>
|
||||
7 days
|
||||
</RadioGroupItem>
|
||||
<RadioGroupItem
|
||||
onClick={() => {
|
||||
dispatch(changeDateRanges(14));
|
||||
}}
|
||||
>
|
||||
14 days
|
||||
</RadioGroupItem>
|
||||
<RadioGroupItem
|
||||
onClick={() => {
|
||||
dispatch(changeDateRanges(30));
|
||||
}}
|
||||
>
|
||||
1 month
|
||||
</RadioGroupItem>
|
||||
<RadioGroupItem
|
||||
onClick={() => {
|
||||
dispatch(changeDateRanges(90));
|
||||
}}
|
||||
>
|
||||
3 month
|
||||
</RadioGroupItem>
|
||||
<RadioGroupItem
|
||||
onClick={() => {
|
||||
dispatch(changeDateRanges(180));
|
||||
}}
|
||||
>
|
||||
6 month
|
||||
</RadioGroupItem>
|
||||
<RadioGroupItem
|
||||
onClick={() => {
|
||||
dispatch(changeDateRanges(356));
|
||||
}}
|
||||
>
|
||||
1 year
|
||||
</RadioGroupItem>
|
||||
</RadioGroup>
|
||||
<div className="w-full max-w-[200px]">
|
||||
<Combobox
|
||||
placeholder="Interval"
|
||||
onChange={(value) => {
|
||||
dispatch(changeInterval(value as IInterval));
|
||||
}}
|
||||
value={interval}
|
||||
items={[
|
||||
{
|
||||
label: "Hour",
|
||||
value: "hour",
|
||||
},
|
||||
{
|
||||
label: "Day",
|
||||
value: "day",
|
||||
},
|
||||
{
|
||||
label: "Month",
|
||||
value: "month",
|
||||
},
|
||||
]}
|
||||
></Combobox>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import { api } from "@/utils/api";
|
||||
import {
|
||||
CartesianGrid,
|
||||
Legend,
|
||||
Line,
|
||||
LineChart,
|
||||
Tooltip,
|
||||
@@ -10,22 +9,13 @@ import {
|
||||
} from "recharts";
|
||||
import { ReportLineChartTooltip } from "./ReportLineChartTooltop";
|
||||
import { useFormatDateInterval } from "@/hooks/useFormatDateInterval";
|
||||
import {
|
||||
type IChartBreakdown,
|
||||
type IChartEvent,
|
||||
type IInterval,
|
||||
} from "@/types";
|
||||
import { type IChartInput } from "@/types";
|
||||
import { getChartColor } from "@/utils/theme";
|
||||
import { ReportTable } from "./ReportTable";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { AutoSizer } from "@/components/AutoSizer";
|
||||
|
||||
type ReportLineChartProps = {
|
||||
interval: IInterval;
|
||||
startDate: Date;
|
||||
endDate: Date;
|
||||
events: IChartEvent[];
|
||||
breakdowns: IChartBreakdown[];
|
||||
type ReportLineChartProps = IChartInput & {
|
||||
showTable?: boolean;
|
||||
};
|
||||
|
||||
@@ -36,16 +26,20 @@ export function ReportLineChart({
|
||||
events,
|
||||
breakdowns,
|
||||
showTable,
|
||||
chartType,
|
||||
name,
|
||||
}: ReportLineChartProps) {
|
||||
const [visibleSeries, setVisibleSeries] = useState<string[]>([]);
|
||||
|
||||
const chart = api.chartMeta.chart.useQuery(
|
||||
{
|
||||
interval,
|
||||
chartType: "linear",
|
||||
chartType,
|
||||
startDate,
|
||||
endDate,
|
||||
events,
|
||||
breakdowns,
|
||||
name,
|
||||
},
|
||||
{
|
||||
enabled: events.length > 0,
|
||||
@@ -58,25 +52,23 @@ export function ReportLineChart({
|
||||
useEffect(() => {
|
||||
if (!ref.current && chart.data) {
|
||||
const max = 20;
|
||||
|
||||
|
||||
setVisibleSeries(
|
||||
chart.data?.series?.slice(0, max).map((serie) => serie.name) ?? [],
|
||||
);
|
||||
// ref.current = true;
|
||||
}
|
||||
}, [chart.data]);
|
||||
|
||||
return (
|
||||
<>
|
||||
);
|
||||
// ref.current = true;
|
||||
}
|
||||
}, [chart.data]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{chart.isSuccess && chart.data?.series?.[0]?.data && (
|
||||
<>
|
||||
<AutoSizer disableHeight>
|
||||
{({ width }) => (
|
||||
<LineChart width={width} height={width * 0.5}>
|
||||
{/* <Legend /> */}
|
||||
<LineChart width={width} height={Math.min(width * 0.5, 400)}>
|
||||
<YAxis dataKey={"count"}></YAxis>
|
||||
<Tooltip content={<ReportLineChartTooltip />} />
|
||||
{/* <Tooltip /> */}
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis
|
||||
dataKey="date"
|
||||
|
||||
9
apps/web/src/components/report/hooks/useReportId.ts
Normal file
9
apps/web/src/components/report/hooks/useReportId.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { useQueryParams } from "@/hooks/useQueryParams";
|
||||
import { z } from "zod";
|
||||
|
||||
export const useReportId = () =>
|
||||
useQueryParams(
|
||||
z.object({
|
||||
reportId: z.string().optional(),
|
||||
}),
|
||||
);
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
type IChartInput,
|
||||
type IChartBreakdown,
|
||||
type IChartEvent,
|
||||
type IInterval,
|
||||
@@ -6,32 +7,17 @@ import {
|
||||
import { getDaysOldDate } from "@/utils/date";
|
||||
import { type PayloadAction, createSlice } from "@reduxjs/toolkit";
|
||||
|
||||
type InitialState = {
|
||||
events: IChartEvent[];
|
||||
breakdowns: IChartBreakdown[];
|
||||
interval: IInterval;
|
||||
startDate: Date;
|
||||
endDate: Date;
|
||||
};
|
||||
type InitialState = IChartInput;
|
||||
|
||||
// First approach: define the initial state using that type
|
||||
const initialState: InitialState = {
|
||||
name: "",
|
||||
chartType: "linear",
|
||||
startDate: getDaysOldDate(7),
|
||||
endDate: new Date(),
|
||||
interval: "day",
|
||||
breakdowns: [
|
||||
{
|
||||
id: "A",
|
||||
name: 'properties.id'
|
||||
}
|
||||
],
|
||||
events: [
|
||||
{
|
||||
id: "A",
|
||||
name: "sign_up",
|
||||
filters: []
|
||||
},
|
||||
],
|
||||
breakdowns: [],
|
||||
events: [],
|
||||
};
|
||||
|
||||
const IDS = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"] as const;
|
||||
@@ -40,6 +26,12 @@ export const reportSlice = createSlice({
|
||||
name: "counter",
|
||||
initialState,
|
||||
reducers: {
|
||||
reset() {
|
||||
return initialState
|
||||
},
|
||||
setReport(state, action: PayloadAction<IChartInput>) {
|
||||
return action.payload
|
||||
},
|
||||
// Events
|
||||
addEvent: (state, action: PayloadAction<Omit<IChartEvent, "id">>) => {
|
||||
state.events.push({
|
||||
@@ -111,21 +103,23 @@ export const reportSlice = createSlice({
|
||||
},
|
||||
|
||||
changeDateRanges: (state, action: PayloadAction<number>) => {
|
||||
if(action.payload === 1) {
|
||||
if (action.payload === 1) {
|
||||
state.interval = "hour";
|
||||
} else if(action.payload <= 30) {
|
||||
} else if (action.payload <= 30) {
|
||||
state.interval = "day";
|
||||
} else {
|
||||
state.interval = "month";
|
||||
}
|
||||
state.startDate = getDaysOldDate(action.payload);
|
||||
state.endDate = new Date();
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Action creators are generated for each case reducer function
|
||||
export const {
|
||||
reset,
|
||||
setReport,
|
||||
addEvent,
|
||||
removeEvent,
|
||||
changeEvent,
|
||||
|
||||
36
apps/web/src/components/report/sidebar/ReportSave.tsx
Normal file
36
apps/web/src/components/report/sidebar/ReportSave.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useReportId } from "../hooks/useReportId";
|
||||
import { api } from "@/utils/api";
|
||||
import { useSelector } from "@/redux";
|
||||
|
||||
export function ReportSave() {
|
||||
const { reportId } = useReportId();
|
||||
const save = api.report.save.useMutation();
|
||||
const update = api.report.update.useMutation();
|
||||
const report = useSelector((state) => state.report);
|
||||
|
||||
if (reportId) {
|
||||
return <Button onClick={() => {
|
||||
update.mutate({
|
||||
reportId,
|
||||
report,
|
||||
dashboardId: "9227feb4-ad59-40f3-b887-3501685733dd",
|
||||
projectId: "f7eabf0c-e0b0-4ac0-940f-1589715b0c3d",
|
||||
});
|
||||
}}>Update</Button>;
|
||||
} else {
|
||||
return (
|
||||
<Button
|
||||
onClick={() => {
|
||||
save.mutate({
|
||||
report,
|
||||
dashboardId: "9227feb4-ad59-40f3-b887-3501685733dd",
|
||||
projectId: "f7eabf0c-e0b0-4ac0-940f-1589715b0c3d",
|
||||
});
|
||||
}}
|
||||
>
|
||||
Create
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
import { ReportEvents } from "./ReportEvents";
|
||||
import { ReportBreakdowns } from "./ReportBreakdowns";
|
||||
import { ReportSave } from "./ReportSave";
|
||||
|
||||
export function ReportSidebar() {
|
||||
return (
|
||||
<div className="flex flex-col gap-4 p-4">
|
||||
<ReportEvents />
|
||||
<ReportBreakdowns />
|
||||
<ReportSave />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user