From 0f9e5f6e93d4ba58aa40590e33624a6d4cabd2b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl-Gerhard=20Lindesva=CC=88rd?= Date: Mon, 9 Mar 2026 13:48:02 +0100 Subject: [PATCH] fix timepicker --- .../start/src/components/chat/chat-report.tsx | 36 +++--- .../components/overview/overview-range.tsx | 20 +++- .../components/report-chart/report-editor.tsx | 33 ++--- .../src/components/time-window-picker.tsx | 28 +++-- apps/start/src/components/ui/calendar.tsx | 113 +++++++++--------- apps/start/src/modals/date-ranger-picker.tsx | 64 +++++----- apps/start/src/modals/gsc-details.tsx | 6 +- .../_app.$organizationId.$projectId.seo.tsx | 59 ++------- packages/trpc/src/routers/gsc.ts | 4 +- 9 files changed, 174 insertions(+), 189 deletions(-) diff --git a/apps/start/src/components/chat/chat-report.tsx b/apps/start/src/components/chat/chat-report.tsx index bc967001..745f674b 100644 --- a/apps/start/src/components/chat/chat-report.tsx +++ b/apps/start/src/components/chat/chat-report.tsx @@ -1,24 +1,27 @@ -import { pushModal } from '@/modals'; import type { - IReport, IChartRange, IChartType, IInterval, + IReport, } from '@openpanel/validation'; import { SaveIcon } from 'lucide-react'; import { useState } from 'react'; -import { ReportChart } from '../report-chart'; import { ReportChartType } from '../report/ReportChartType'; import { ReportInterval } from '../report/ReportInterval'; +import { ReportChart } from '../report-chart'; import { TimeWindowPicker } from '../time-window-picker'; import { Button } from '../ui/button'; +import { pushModal } from '@/modals'; export function ChatReport({ lazy, ...props -}: { report: IReport & { startDate: string; endDate: string }; lazy: boolean }) { +}: { + report: IReport & { startDate: string; endDate: string }; + lazy: boolean; +}) { const [chartType, setChartType] = useState( - props.report.chartType, + props.report.chartType ); const [startDate, setStartDate] = useState(props.report.startDate); const [endDate, setEndDate] = useState(props.report.endDate); @@ -35,47 +38,48 @@ export function ChatReport({ }; return (
-
+
{props.report.name}
-
+
{ setChartType(type); }} + value={chartType} />
diff --git a/apps/start/src/components/overview/overview-range.tsx b/apps/start/src/components/overview/overview-range.tsx index d30c0864..ee1c6453 100644 --- a/apps/start/src/components/overview/overview-range.tsx +++ b/apps/start/src/components/overview/overview-range.tsx @@ -2,17 +2,25 @@ import { useOverviewOptions } from '@/components/overview/useOverviewOptions'; import { TimeWindowPicker } from '@/components/time-window-picker'; export function OverviewRange() { - const { range, setRange, setStartDate, setEndDate, endDate, startDate } = - useOverviewOptions(); + const { + range, + setRange, + setStartDate, + setEndDate, + endDate, + startDate, + setInterval, + } = useOverviewOptions(); return ( ); } diff --git a/apps/start/src/components/report-chart/report-editor.tsx b/apps/start/src/components/report-chart/report-editor.tsx index dc1a04a9..af7bbeb1 100644 --- a/apps/start/src/components/report-chart/report-editor.tsx +++ b/apps/start/src/components/report-chart/report-editor.tsx @@ -1,4 +1,7 @@ -import { ReportChart } from '@/components/report-chart'; +import type { IServiceReport } from '@openpanel/db'; +import { GanttChartSquareIcon, ShareIcon } from 'lucide-react'; +import { useEffect } from 'react'; +import EditReportName from '../report/edit-report-name'; import { ReportChartType } from '@/components/report/ReportChartType'; import { ReportInterval } from '@/components/report/ReportInterval'; import { ReportLineType } from '@/components/report/ReportLineType'; @@ -14,18 +17,13 @@ import { setReport, } from '@/components/report/reportSlice'; import { ReportSidebar } from '@/components/report/sidebar/ReportSidebar'; +import { ReportChart } from '@/components/report-chart'; import { TimeWindowPicker } from '@/components/time-window-picker'; import { Button } from '@/components/ui/button'; import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet'; import { useAppParams } from '@/hooks/use-app-params'; import { pushModal } from '@/modals'; import { useDispatch, useSelector } from '@/redux'; -import { bind } from 'bind-event-listener'; -import { GanttChartSquareIcon, ShareIcon } from 'lucide-react'; -import { useEffect } from 'react'; - -import type { IServiceReport } from '@openpanel/db'; -import EditReportName from '../report/edit-report-name'; interface ReportEditorProps { report: IServiceReport | null; @@ -54,15 +52,15 @@ export default function ReportEditor({ return (
-
+
{initialReport?.id && ( @@ -71,9 +69,9 @@ export default function ReportEditor({
@@ -88,23 +86,26 @@ export default function ReportEditor({ /> { dispatch(changeDateRanges(value)); }} - value={report.range} - onStartDateChange={(date) => dispatch(changeStartDate(date))} onEndDateChange={(date) => dispatch(changeEndDate(date))} - endDate={report.endDate} + onIntervalChange={(interval) => + dispatch(changeInterval(interval)) + } + onStartDateChange={(date) => dispatch(changeStartDate(date))} startDate={report.startDate} + value={report.range} /> dispatch(changeInterval(newInterval))} range={report.range} - chartType={report.chartType} startDate={report.startDate} - endDate={report.endDate} />
@@ -114,7 +115,7 @@ export default function ReportEditor({
{report.ready && ( - + )}
diff --git a/apps/start/src/components/time-window-picker.tsx b/apps/start/src/components/time-window-picker.tsx index aed61ede..e611e3e0 100644 --- a/apps/start/src/components/time-window-picker.tsx +++ b/apps/start/src/components/time-window-picker.tsx @@ -1,3 +1,9 @@ +import { timeWindows } from '@openpanel/constants'; +import type { IChartRange, IInterval } from '@openpanel/validation'; +import { bind } from 'bind-event-listener'; +import { endOfDay, format, startOfDay } from 'date-fns'; +import { CalendarIcon } from 'lucide-react'; +import { useCallback, useEffect, useRef } from 'react'; import { Button } from '@/components/ui/button'; import { DropdownMenu, @@ -11,24 +17,18 @@ import { } from '@/components/ui/dropdown-menu'; import { pushModal, useOnPushModal } from '@/modals'; import { cn } from '@/utils/cn'; -import { bind } from 'bind-event-listener'; -import { CalendarIcon } from 'lucide-react'; -import { useCallback, useEffect, useRef } from 'react'; - import { shouldIgnoreKeypress } from '@/utils/should-ignore-keypress'; -import { timeWindows } from '@openpanel/constants'; -import type { IChartRange } from '@openpanel/validation'; -import { endOfDay, format, startOfDay } from 'date-fns'; -type Props = { +interface Props { value: IChartRange; onChange: (value: IChartRange) => void; onStartDateChange: (date: string) => void; onEndDateChange: (date: string) => void; + onIntervalChange: (interval: IInterval) => void; endDate: string | null; startDate: string | null; className?: string; -}; +} export function TimeWindowPicker({ value, onChange, @@ -36,6 +36,7 @@ export function TimeWindowPicker({ onStartDateChange, endDate, onEndDateChange, + onIntervalChange, className, }: Props) { const isDateRangerPickerOpen = useRef(false); @@ -46,10 +47,11 @@ export function TimeWindowPicker({ const handleCustom = useCallback(() => { pushModal('DateRangerPicker', { - onChange: ({ startDate, endDate }) => { + onChange: ({ startDate, endDate, interval }) => { onStartDateChange(format(startOfDay(startDate), 'yyyy-MM-dd HH:mm:ss')); onEndDateChange(format(endOfDay(endDate), 'yyyy-MM-dd HH:mm:ss')); onChange('custom'); + onIntervalChange(interval); }, startDate: startDate ? new Date(startDate) : undefined, endDate: endDate ? new Date(endDate) : undefined, @@ -69,7 +71,7 @@ export function TimeWindowPicker({ } const match = Object.values(timeWindows).find( - (tw) => event.key === tw.shortcut.toLowerCase(), + (tw) => event.key === tw.shortcut.toLowerCase() ); if (match?.key === 'custom') { handleCustom(); @@ -84,9 +86,9 @@ export function TimeWindowPicker({ diff --git a/apps/start/src/components/ui/calendar.tsx b/apps/start/src/components/ui/calendar.tsx index c3742167..0673f170 100644 --- a/apps/start/src/components/ui/calendar.tsx +++ b/apps/start/src/components/ui/calendar.tsx @@ -9,7 +9,6 @@ import { DayPicker, getDefaultClassNames, } from 'react-day-picker'; - import { Button, buttonVariants } from '@/components/ui/button'; import { cn } from '@/lib/utils'; @@ -29,99 +28,93 @@ function Calendar({ return ( svg]:rotate-180`, String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`, - className, + className )} - captionLayout={captionLayout} - formatters={{ - formatMonthDropdown: (date) => - date.toLocaleString('default', { month: 'short' }), - ...formatters, - }} classNames={{ root: cn('w-fit', defaultClassNames.root), months: cn( - 'flex gap-4 flex-col sm:flex-row relative', - defaultClassNames.months, + 'relative flex flex-col gap-4 sm:flex-row', + defaultClassNames.months ), - month: cn('flex flex-col w-full gap-4', defaultClassNames.month), + month: cn('flex w-full flex-col gap-4', defaultClassNames.month), nav: cn( - 'flex items-center gap-1 w-full absolute top-0 inset-x-0 justify-between', - defaultClassNames.nav, + 'absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1', + defaultClassNames.nav ), button_previous: cn( buttonVariants({ variant: buttonVariant }), - 'size-(--cell-size) aria-disabled:opacity-50 p-0 select-none', - defaultClassNames.button_previous, + 'size-(--cell-size) select-none p-0 aria-disabled:opacity-50', + defaultClassNames.button_previous ), button_next: cn( buttonVariants({ variant: buttonVariant }), - 'size-(--cell-size) aria-disabled:opacity-50 p-0 select-none', - defaultClassNames.button_next, + 'size-(--cell-size) select-none p-0 aria-disabled:opacity-50', + defaultClassNames.button_next ), month_caption: cn( - 'flex items-center justify-center h-(--cell-size) w-full px-(--cell-size)', - defaultClassNames.month_caption, + 'flex h-(--cell-size) w-full items-center justify-center px-(--cell-size)', + defaultClassNames.month_caption ), dropdowns: cn( - 'w-full flex items-center text-sm font-medium justify-center h-(--cell-size) gap-1.5', - defaultClassNames.dropdowns, + 'flex h-(--cell-size) w-full items-center justify-center gap-1.5 font-medium text-sm', + defaultClassNames.dropdowns ), dropdown_root: cn( - 'relative has-focus:border-ring border border-input shadow-xs has-focus:ring-ring/50 has-focus:ring-[3px] rounded-md', - defaultClassNames.dropdown_root, + 'relative rounded-md border border-input shadow-xs has-focus:border-ring has-focus:ring-[3px] has-focus:ring-ring/50', + defaultClassNames.dropdown_root ), dropdown: cn( - 'absolute bg-popover inset-0 opacity-0', - defaultClassNames.dropdown, + 'absolute inset-0 bg-popover opacity-0', + defaultClassNames.dropdown ), caption_label: cn( 'select-none font-medium', captionLayout === 'label' ? 'text-sm' - : 'rounded-md pl-2 pr-1 flex items-center gap-1 text-sm h-8 [&>svg]:text-muted-foreground [&>svg]:size-3.5', - defaultClassNames.caption_label, + : 'flex h-8 items-center gap-1 rounded-md pr-1 pl-2 text-sm [&>svg]:size-3.5 [&>svg]:text-muted-foreground', + defaultClassNames.caption_label ), table: 'w-full border-collapse', weekdays: cn('flex', defaultClassNames.weekdays), weekday: cn( - 'text-muted-foreground rounded-md flex-1 font-normal text-[0.8rem] select-none', - defaultClassNames.weekday, + 'flex-1 select-none rounded-md font-normal text-[0.8rem] text-muted-foreground', + defaultClassNames.weekday ), - week: cn('flex w-full mt-2', defaultClassNames.week), + week: cn('mt-2 flex w-full', defaultClassNames.week), week_number_header: cn( - 'select-none w-(--cell-size)', - defaultClassNames.week_number_header, + 'w-(--cell-size) select-none', + defaultClassNames.week_number_header ), week_number: cn( - 'text-[0.8rem] select-none text-muted-foreground', - defaultClassNames.week_number, + 'select-none text-[0.8rem] text-muted-foreground', + defaultClassNames.week_number ), day: cn( - 'relative w-full h-full p-0 text-center [&:first-child[data-selected=true]_button]:rounded-l-md [&:last-child[data-selected=true]_button]:rounded-r-md group/day aspect-square select-none', - defaultClassNames.day, + 'group/day relative aspect-square h-full w-full select-none p-0 text-center [&:first-child[data-selected=true]_button]:rounded-l-md [&:last-child[data-selected=true]_button]:rounded-r-md', + defaultClassNames.day ), range_start: cn( 'rounded-l-md bg-accent', - defaultClassNames.range_start, + defaultClassNames.range_start ), range_middle: cn('rounded-none', defaultClassNames.range_middle), range_end: cn('rounded-r-md bg-accent', defaultClassNames.range_end), today: cn( - 'bg-accent text-accent-foreground rounded-md data-[selected=true]:rounded-none', - defaultClassNames.today, + 'rounded-md bg-accent text-accent-foreground data-[selected=true]:rounded-none', + defaultClassNames.today ), outside: cn( 'text-muted-foreground aria-selected:text-muted-foreground', - defaultClassNames.outside, + defaultClassNames.outside ), disabled: cn( 'text-muted-foreground opacity-50', - defaultClassNames.disabled, + defaultClassNames.disabled ), hidden: cn('invisible', defaultClassNames.hidden), ...classNames, @@ -130,9 +123,9 @@ function Calendar({ Root: ({ className, rootRef, ...props }) => { return (
); @@ -169,6 +162,12 @@ function Calendar({ }, ...components, }} + formatters={{ + formatMonthDropdown: (date) => + date.toLocaleString('default', { month: 'short' }), + ...formatters, + }} + showOutsideDays={showOutsideDays} {...props} /> ); @@ -184,29 +183,31 @@ function CalendarDayButton({ const ref = React.useRef(null); React.useEffect(() => { - if (modifiers.focused) ref.current?.focus(); + if (modifiers.focused) { + ref.current?.focus(); + } }, [modifiers.focused]); return ( {startDate && endDate && (