diff --git a/.vscode/settings.json b/.vscode/settings.json index 2057e4ba..cc89dd2d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,7 @@ { - "editor.codeActionsOnSave": { "source.fixAll.eslint": true }, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit" + }, "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true, "eslint.rules.customizations": [{ "rule": "*", "severity": "warn" }], diff --git a/apps/web/src/components/report/chart/ReportLineChart.tsx b/apps/web/src/components/report/chart/ReportLineChart.tsx index 749d200d..6c7cf931 100644 --- a/apps/web/src/components/report/chart/ReportLineChart.tsx +++ b/apps/web/src/components/report/chart/ReportLineChart.tsx @@ -25,6 +25,7 @@ export function ReportLineChart({ interval, data }: ReportLineChartProps) { const { editMode } = useChartContext(); const [visibleSeries, setVisibleSeries] = useState([]); const formatDate = useFormatDateInterval(interval); + console.log(JSON.stringify(data.series[0]?.data, null, 2)); const ref = useRef(false); useEffect(() => { diff --git a/apps/web/src/components/report/chart/ReportTable.tsx b/apps/web/src/components/report/chart/ReportTable.tsx index cf4bc265..2fcf0d40 100644 --- a/apps/web/src/components/report/chart/ReportTable.tsx +++ b/apps/web/src/components/report/chart/ReportTable.tsx @@ -39,73 +39,99 @@ export function ReportTable({ const total = 'bg-gray-50 text-emerald-600 font-medium border-r border-border'; return ( -
- {/* Labels */} -
-
Name
- {data.series.map((serie, index) => { - const checked = visibleSeries.includes(serie.name); + <> +
+ {/* Labels */} +
+
Name
+ {/*
+
+ Summary +
+
*/} + {data.series.map((serie, index) => { + const checked = visibleSeries.includes(serie.name); - return ( -
- - handleChange(serie.name, !!checked) - } - style={ - checked - ? { - background: getChartColor(index), - borderColor: getChartColor(index), - } - : undefined - } - checked={checked} - /> -
- {getLabel(serie.name)} + return ( +
+ + handleChange(serie.name, !!checked) + } + style={ + checked + ? { + background: getChartColor(index), + borderColor: getChartColor(index), + } + : undefined + } + checked={checked} + /> +
+ {getLabel(serie.name)} +
-
- ); - })} -
- - {/* ScrollView for all values */} -
- {/* Header */} -
-
Total
- {data.series[0]?.data.map((serie) => ( -
- {formatDate(serie.date)} -
- ))} + ); + })}
- {/* Values */} - {data.series.map((serie) => { - return ( -
-
- {serie.totalCount} + {/* ScrollView for all values */} +
+ {/* Header */} +
+
Total
+ {data.series[0]?.data.map((serie) => ( +
+ {formatDate(serie.date)}
- {serie.data.map((item) => { - return ( -
- {item.count} -
- ); - })} -
- ); - })} + ))} +
+ + {/* Values */} + {data.series.map((serie) => { + return ( +
+
+ {serie.totalCount} +
+ {serie.data.map((item) => { + return ( +
+ {item.count} +
+ ); + })} +
+ ); + })} +
-
+
+
Summary
+
+ {data.series.reduce((acc, serie) => serie.totalCount + acc, 0)} +
+
+ ); } diff --git a/apps/web/src/components/report/chart/index.tsx b/apps/web/src/components/report/chart/index.tsx index 9c37abfc..b8347b0d 100644 --- a/apps/web/src/components/report/chart/index.tsx +++ b/apps/web/src/components/report/chart/index.tsx @@ -40,6 +40,8 @@ export const Chart = memo( } ); + console.log(chart.data); + const anyData = Boolean(chart.data?.series?.[0]?.data); if (chart.isFetching && !anyData) { diff --git a/apps/web/src/components/report/sidebar/ReportEventFilters.tsx b/apps/web/src/components/report/sidebar/ReportEventFilters.tsx index b11c606a..71ef0a43 100644 --- a/apps/web/src/components/report/sidebar/ReportEventFilters.tsx +++ b/apps/web/src/components/report/sidebar/ReportEventFilters.tsx @@ -15,7 +15,7 @@ import { import { RenderDots } from '@/components/ui/RenderDots'; import { useMappings } from '@/hooks/useMappings'; import { useOrganizationParams } from '@/hooks/useOrganizationParams'; -import { useDispatch } from '@/redux'; +import { useDispatch, useSelector } from '@/redux'; import type { IChartEvent, IChartEventFilter, diff --git a/apps/web/src/pages/[organization]/[project]/reports/index.tsx b/apps/web/src/pages/[organization]/[project]/reports/index.tsx index 45a74fe5..23b10bc5 100644 --- a/apps/web/src/pages/[organization]/[project]/reports/index.tsx +++ b/apps/web/src/pages/[organization]/[project]/reports/index.tsx @@ -10,14 +10,7 @@ import { ReportSaveButton } from '@/components/report/ReportSaveButton'; import { reset, setReport } from '@/components/report/reportSlice'; import { ReportSidebar } from '@/components/report/sidebar/ReportSidebar'; import { Button } from '@/components/ui/button'; -import { - Sheet, - SheetContent, - SheetDescription, - SheetHeader, - SheetTitle, - SheetTrigger, -} from '@/components/ui/sheet'; +import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet'; import { useRouterBeforeLeave } from '@/hooks/useRouterBeforeLeave'; import { useDispatch, useSelector } from '@/redux'; import { createServerSideProps } from '@/server/getServerSideProps'; diff --git a/apps/web/src/server/api/routers/chart.ts b/apps/web/src/server/api/routers/chart.ts index 785df900..9c302dbb 100644 --- a/apps/web/src/server/api/routers/chart.ts +++ b/apps/web/src/server/api/routers/chart.ts @@ -2,7 +2,12 @@ import { createTRPCRouter, protectedProcedure } from '@/server/api/trpc'; import * as cache from '@/server/cache'; import { db } from '@/server/db'; import { getProjectBySlug } from '@/server/services/project.service'; -import type { IChartEvent, IChartInputWithDates, IChartRange } from '@/types'; +import type { + IChartEvent, + IChartInputWithDates, + IChartRange, + IInterval, +} from '@/types'; import { getDaysOldDate } from '@/utils/date'; import { toDots } from '@/utils/object'; import { zChartInputWithDates } from '@/utils/validation'; @@ -81,6 +86,7 @@ export const chartRouter = createTRPCRouter({ }) ) .query(async ({ input: { event, property, projectSlug } }) => { + const intervalInDays = 180; const project = await getProjectBySlug(projectSlug); if (isJsonPath(property)) { const events = await db.$queryRawUnsafe<{ value: string }[]>( @@ -88,14 +94,7 @@ export const chartRouter = createTRPCRouter({ property )} AS value from events WHERE project_id = '${ project.id - }' AND name = '${event}' AND "createdAt" >= NOW() - INTERVAL '30 days'` - ); - console.log( - `SELECT ${selectJsonPath( - property - )} AS value from events WHERE project_id = '${ - project.id - }' AND name = '${event}' AND "createdAt" >= NOW() - INTERVAL '30 days'` + }' AND name = '${event}' AND "createdAt" >= NOW() - INTERVAL '${intervalInDays} days'` ); return { @@ -110,7 +109,9 @@ export const chartRouter = createTRPCRouter({ not: null, }, createdAt: { - gte: new Date(new Date().getTime() - 1000 * 60 * 60 * 24 * 30), + gte: new Date( + new Date().getTime() - 1000 * 60 * 60 * 24 * intervalInDays + ), }, }, distinct: property as any, @@ -483,7 +484,7 @@ async function getChartData({ function fillEmptySpotsInTimeline( items: ResultItem[], - interval: string, + interval: IInterval, startDate: string, endDate: string ) { @@ -493,31 +494,36 @@ function fillEmptySpotsInTimeline( const today = new Date(); if (interval === 'minute') { - clonedStartDate.setSeconds(0, 0); - clonedEndDate.setMinutes(clonedEndDate.getMinutes() + 1, 0, 0); + clonedStartDate.setUTCSeconds(0, 0); + clonedEndDate.setUTCMinutes(clonedEndDate.getUTCMinutes() + 1, 0, 0); } else if (interval === 'hour') { - clonedStartDate.setMinutes(0, 0, 0); - clonedEndDate.setMinutes(0, 0, 0); + clonedStartDate.setUTCMinutes(0, 0, 0); + clonedEndDate.setUTCMinutes(0, 0, 0); } else { clonedStartDate.setUTCHours(0, 0, 0, 0); clonedEndDate.setUTCHours(0, 0, 0, 0); } + if (interval === 'month') { + clonedStartDate.setDate(1); + clonedEndDate.setDate(1); + } + // Force if interval is month and the start date is the same month as today const shouldForce = () => interval === 'month' && - clonedStartDate.getFullYear() === today.getFullYear() && - clonedStartDate.getMonth() === today.getMonth(); + clonedStartDate.getUTCFullYear() === today.getUTCFullYear() && + clonedStartDate.getUTCMonth() === today.getUTCMonth(); while ( shouldForce() || clonedStartDate.getTime() <= clonedEndDate.getTime() ) { - const getYear = (date: Date) => date.getFullYear(); - const getMonth = (date: Date) => date.getMonth(); - const getDay = (date: Date) => date.getDate(); - const getHour = (date: Date) => date.getHours(); - const getMinute = (date: Date) => date.getMinutes(); + const getYear = (date: Date) => date.getUTCFullYear(); + const getMonth = (date: Date) => date.getUTCMonth(); + const getDay = (date: Date) => date.getUTCDate(); + const getHour = (date: Date) => date.getUTCHours(); + const getMinute = (date: Date) => date.getUTCMinutes(); const item = items.find((item) => { const date = new Date(item.date); @@ -555,7 +561,10 @@ function fillEmptySpotsInTimeline( }); if (item) { - result.push(item); + result.push({ + ...item, + date: clonedStartDate.toISOString(), + }); } else { result.push({ date: clonedStartDate.toISOString(), @@ -566,19 +575,19 @@ function fillEmptySpotsInTimeline( switch (interval) { case 'day': { - clonedStartDate.setDate(clonedStartDate.getDate() + 1); + clonedStartDate.setDate(clonedStartDate.getUTCDate() + 1); break; } case 'hour': { - clonedStartDate.setHours(clonedStartDate.getHours() + 1); + clonedStartDate.setHours(clonedStartDate.getUTCHours() + 1); break; } case 'minute': { - clonedStartDate.setMinutes(clonedStartDate.getMinutes() + 1); + clonedStartDate.setMinutes(clonedStartDate.getUTCMinutes() + 1); break; } case 'month': { - clonedStartDate.setMonth(clonedStartDate.getMonth() + 1); + clonedStartDate.setMonth(clonedStartDate.getUTCMonth() + 1); break; } } diff --git a/apps/web/src/styles/globals.css b/apps/web/src/styles/globals.css index 77ed4ecb..52e84e75 100644 --- a/apps/web/src/styles/globals.css +++ b/apps/web/src/styles/globals.css @@ -146,3 +146,14 @@ opacity: 1; } } + +/* For Webkit-based browsers (Chrome, Safari and Opera) */ +.scrollbar-hide::-webkit-scrollbar { + display: none; +} + +/* For IE, Edge and Firefox */ +.scrollbar-hide { + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ +}