fix timepicker

This commit is contained in:
Carl-Gerhard Lindesvärd
2026-03-09 13:48:02 +01:00
parent c9cf7901ad
commit 0f9e5f6e93
9 changed files with 174 additions and 189 deletions

View File

@@ -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<IChartType>(
props.report.chartType,
props.report.chartType
);
const [startDate, setStartDate] = useState<string>(props.report.startDate);
const [endDate, setEndDate] = useState<string>(props.report.endDate);
@@ -35,47 +38,48 @@ export function ChatReport({
};
return (
<div className="card">
<div className="text-center text-sm font-mono font-medium pt-4">
<div className="pt-4 text-center font-medium font-mono text-sm">
{props.report.name}
</div>
<div className="p-4">
<ReportChart lazy={lazy} report={report} />
</div>
<div className="row justify-between gap-1 border-t border-border p-2">
<div className="row justify-between gap-1 border-border border-t p-2">
<div className="col md:row gap-1">
<TimeWindowPicker
className="min-w-0"
onChange={setRange}
value={report.range}
onStartDateChange={setStartDate}
onEndDateChange={setEndDate}
endDate={report.endDate}
onChange={setRange}
onEndDateChange={setEndDate}
onIntervalChange={setInterval}
onStartDateChange={setStartDate}
startDate={report.startDate}
value={report.range}
/>
<ReportInterval
chartType={chartType}
className="min-w-0"
interval={interval}
range={range}
chartType={chartType}
onChange={setInterval}
range={range}
/>
<ReportChartType
value={chartType}
onChange={(type) => {
setChartType(type);
}}
value={chartType}
/>
</div>
<Button
icon={SaveIcon}
variant="outline"
size="sm"
onClick={() => {
pushModal('SaveReport', {
report,
disableRedirect: true,
});
}}
size="sm"
variant="outline"
>
Save report
</Button>

View File

@@ -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 (
<TimeWindowPicker
onChange={setRange}
value={range}
onStartDateChange={setStartDate}
onEndDateChange={setEndDate}
endDate={endDate}
onChange={setRange}
onEndDateChange={setEndDate}
onIntervalChange={setInterval}
onStartDateChange={setStartDate}
startDate={startDate}
value={range}
/>
);
}

View File

@@ -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 (
<Sheet>
<div>
<div className="p-4 flex items-center justify-between">
<div className="flex items-center justify-between p-4">
<EditReportName />
{initialReport?.id && (
<Button
variant="outline"
icon={ShareIcon}
onClick={() =>
pushModal('ShareReportModal', { reportId: initialReport.id })
}
variant="outline"
>
Share
</Button>
@@ -71,9 +69,9 @@ export default function ReportEditor({
<div className="grid grid-cols-2 gap-2 p-4 pt-0 md:grid-cols-6">
<SheetTrigger asChild>
<Button
className="self-start"
icon={GanttChartSquareIcon}
variant="cta"
className="self-start"
>
Pick events
</Button>
@@ -88,23 +86,26 @@ export default function ReportEditor({
/>
<TimeWindowPicker
className="min-w-0 flex-1"
endDate={report.endDate}
onChange={(value) => {
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}
/>
<ReportInterval
chartType={report.chartType}
className="min-w-0 flex-1"
endDate={report.endDate}
interval={report.interval}
onChange={(newInterval) => dispatch(changeInterval(newInterval))}
range={report.range}
chartType={report.chartType}
startDate={report.startDate}
endDate={report.endDate}
/>
<ReportLineType className="min-w-0 flex-1" />
</div>
@@ -114,7 +115,7 @@ export default function ReportEditor({
</div>
<div className="flex flex-col gap-4 p-4" id="report-editor">
{report.ready && (
<ReportChart report={{ ...report, projectId }} isEditMode />
<ReportChart isEditMode report={{ ...report, projectId }} />
)}
</div>
</div>

View File

@@ -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({
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
icon={CalendarIcon}
className={cn('justify-start', className)}
icon={CalendarIcon}
variant="outline"
>
{timeWindow?.label}
</Button>

View File

@@ -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 (
<DayPicker
showOutsideDays={showOutsideDays}
captionLayout={captionLayout}
className={cn(
'bg-background group/calendar p-3 [--cell-size:--spacing(8)] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent',
'group/calendar p-3 [--cell-size:--spacing(8)] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent',
String.raw`rtl:**:[.rdp-button\_next>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 (
<div
className={cn(className)}
data-slot="calendar"
ref={rootRef}
className={cn(className)}
{...props}
/>
);
@@ -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<HTMLButtonElement>(null);
React.useEffect(() => {
if (modifiers.focused) ref.current?.focus();
if (modifiers.focused) {
ref.current?.focus();
}
}, [modifiers.focused]);
return (
<Button
ref={ref}
variant="ghost"
size="icon"
className={cn(
'flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 font-normal leading-none data-[range-end=true]:rounded-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md data-[range-end=true]:rounded-r-md data-[range-start=true]:rounded-l-md data-[range-end=true]:bg-primary data-[range-middle=true]:bg-accent data-[range-start=true]:bg-primary data-[selected-single=true]:bg-primary data-[range-end=true]:text-primary-foreground data-[range-middle=true]:text-accent-foreground data-[range-start=true]:text-primary-foreground data-[selected-single=true]:text-primary-foreground group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-[3px] group-data-[focused=true]/day:ring-ring/50 dark:hover:text-accent-foreground [&>span]:text-xs [&>span]:opacity-70',
defaultClassNames.day,
className
)}
data-day={day.date.toLocaleDateString()}
data-range-end={modifiers.range_end}
data-range-middle={modifiers.range_middle}
data-range-start={modifiers.range_start}
data-selected-single={
modifiers.selected &&
!modifiers.range_start &&
!modifiers.range_end &&
!modifiers.range_middle
}
data-range-start={modifiers.range_start}
data-range-end={modifiers.range_end}
data-range-middle={modifiers.range_middle}
className={cn(
'data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 dark:hover:text-accent-foreground flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px] data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md [&>span]:text-xs [&>span]:opacity-70',
defaultClassNames.day,
className,
)}
ref={ref}
size="icon"
variant="ghost"
{...props}
/>
);

View File

@@ -1,20 +1,24 @@
import { getDefaultIntervalByDates } from '@openpanel/constants';
import type { IInterval } from '@openpanel/validation';
import { endOfDay, subMonths } from 'date-fns';
import { CheckIcon, XIcon } from 'lucide-react';
import { useState } from 'react';
import { popModal } from '.';
import { ModalContent } from './Modal/Container';
import { Button } from '@/components/ui/button';
import { Calendar } from '@/components/ui/calendar';
import { useBreakpoint } from '@/hooks/use-breakpoint';
import { subMonths } from 'date-fns';
import { useState } from 'react';
import { Input } from '@/components/ui/input';
import { formatDate } from '@/utils/date';
import { CheckIcon, XIcon } from 'lucide-react';
import { popModal } from '.';
import { ModalContent, ModalHeader } from './Modal/Container';
type Props = {
onChange: (payload: { startDate: Date; endDate: Date }) => void;
interface Props {
onChange: (payload: {
startDate: Date;
endDate: Date;
interval: IInterval;
}) => void;
startDate?: Date;
endDate?: Date;
};
}
export default function DateRangerPicker({
onChange,
startDate: initialStartDate,
@@ -25,20 +29,20 @@ export default function DateRangerPicker({
const [endDate, setEndDate] = useState(initialEndDate);
return (
<ModalContent className="p-4 md:p-8 min-w-fit">
<ModalContent className="min-w-fit p-4 md:p-8">
<Calendar
captionLayout="dropdown"
initialFocus
mode="range"
className="mx-auto min-h-[310px] p-0 [&_table]:mx-auto [&_table]:w-auto"
defaultMonth={subMonths(
startDate ? new Date(startDate) : new Date(),
isBelowSm ? 0 : 1,
isBelowSm ? 0 : 1
)}
selected={{
from: startDate,
to: endDate,
hidden={{
after: endOfDay(new Date()),
}}
toDate={new Date()}
initialFocus
mode="range"
numberOfMonths={isBelowSm ? 1 : 2}
onSelect={(range) => {
if (range?.from) {
setStartDate(range.from);
@@ -47,33 +51,39 @@ export default function DateRangerPicker({
setEndDate(range.to);
}
}}
numberOfMonths={isBelowSm ? 1 : 2}
className="mx-auto min-h-[310px] [&_table]:mx-auto [&_table]:w-auto p-0"
selected={{
from: startDate,
to: endDate,
}}
/>
<div className="col flex-col-reverse md:row gap-2">
<div className="col md:row flex-col-reverse gap-2">
<Button
icon={XIcon}
onClick={() => popModal()}
type="button"
variant="outline"
onClick={() => popModal()}
icon={XIcon}
>
Cancel
</Button>
{startDate && endDate && (
<Button
type="button"
className="md:ml-auto"
icon={startDate && endDate ? CheckIcon : XIcon}
onClick={() => {
popModal();
if (startDate && endDate) {
onChange({
startDate: startDate,
endDate: endDate,
startDate,
endDate,
interval: getDefaultIntervalByDates(
startDate.toISOString(),
endDate.toISOString()
)!,
});
}
}}
icon={startDate && endDate ? CheckIcon : XIcon}
type="button"
>
{startDate && endDate
? `Select ${formatDate(startDate)} - ${formatDate(endDate)}`

View File

@@ -27,7 +27,7 @@ type GscChartData = { date: string; clicks: number; impressions: number };
const { TooltipProvider, Tooltip: GscTooltip } = createChartTooltip<
GscChartData,
Record<string, never>
Record<string, unknown>
>(({ data }) => {
const item = data[0];
if (!item) {
@@ -267,7 +267,7 @@ function GscViewsChart({
const yAxisProps = useYAxisProps();
return (
<TooltipProvider data={[]}>
<TooltipProvider>
<ResponsiveContainer height={160} width="100%">
<ComposedChart data={data}>
<defs>
@@ -328,7 +328,7 @@ function GscTimeseriesChart({
const yAxisProps = useYAxisProps();
return (
<TooltipProvider data={data}>
<TooltipProvider>
<ResponsiveContainer height={160} width="100%">
<ComposedChart data={data}>
<defs>

View File

@@ -1,13 +1,6 @@
import {
getDefaultIntervalByRange,
intervals,
timeWindows,
} from '@openpanel/constants';
import type { IChartRange, IInterval } from '@openpanel/validation';
import { useQuery } from '@tanstack/react-query';
import { createFileRoute, useNavigate } from '@tanstack/react-router';
import { SearchIcon } from 'lucide-react';
import { parseAsString, parseAsStringEnum, useQueryState } from 'nuqs';
import { useMemo, useState } from 'react';
import {
CartesianGrid,
@@ -23,8 +16,11 @@ import {
createChartTooltip,
} from '@/components/charts/chart-tooltip';
import { FullPageEmptyState } from '@/components/full-page-empty-state';
import { OverviewInterval } from '@/components/overview/overview-interval';
import { OverviewMetricCard } from '@/components/overview/overview-metric-card';
import { OverviewRange } from '@/components/overview/overview-range';
import { OverviewWidgetTable } from '@/components/overview/overview-widget-table';
import { useOverviewOptions } from '@/components/overview/useOverviewOptions';
import { GscCannibalization } from '@/components/page/gsc-cannibalization';
import { GscCtrBenchmark } from '@/components/page/gsc-ctr-benchmark';
import { GscPositionChart } from '@/components/page/gsc-position-chart';
@@ -32,14 +28,12 @@ import { PagesInsights } from '@/components/page/pages-insights';
import { PageContainer } from '@/components/page-container';
import { PageHeader } from '@/components/page-header';
import { Pagination } from '@/components/pagination';
import { ReportInterval } from '@/components/report/ReportInterval';
import {
useYAxisProps,
X_AXIS_STYLE_PROPS,
} from '@/components/report-chart/common/axis';
import { SerieIcon } from '@/components/report-chart/common/serie-icon';
import { Skeleton } from '@/components/skeleton';
import { TimeWindowPicker } from '@/components/time-window-picker';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { useAppParams } from '@/hooks/use-app-params';
@@ -94,27 +88,13 @@ function SeoPage() {
const { projectId, organizationId } = useAppParams();
const trpc = useTRPC();
const navigate = useNavigate();
const [range, setRange] = useQueryState(
'range',
parseAsStringEnum(Object.keys(timeWindows) as IChartRange[]).withDefault(
'30d' as IChartRange
)
);
const [startDate, setStartDate] = useQueryState('start', parseAsString);
const [endDate, setEndDate] = useQueryState('end', parseAsString);
const [interval, setInterval] = useQueryState(
'interval',
parseAsStringEnum(Object.keys(intervals) as IInterval[]).withDefault(
(getDefaultIntervalByRange(range) ?? 'day') as IInterval
)
);
const { range, startDate, endDate, interval } = useOverviewOptions();
const dateInput = {
range,
interval,
startDate: startDate ?? undefined,
endDate: endDate ?? undefined,
startDate,
endDate,
};
const connectionQuery = useQuery(
@@ -265,31 +245,8 @@ function SeoPage() {
<PageHeader
actions={
<>
<ReportInterval
chartType="linear"
endDate={endDate}
interval={interval ?? 'day'}
onChange={(v) => setInterval(v)}
range={range}
startDate={startDate}
/>
<TimeWindowPicker
endDate={endDate}
onChange={(v) => {
if (v !== 'custom') {
setStartDate(null);
setEndDate(null);
}
setInterval(
(getDefaultIntervalByRange(v) ?? 'day') as IInterval
);
setRange(v);
}}
onEndDateChange={setEndDate}
onStartDateChange={setStartDate}
startDate={startDate}
value={range}
/>
<OverviewRange />
<OverviewInterval />
</>
}
description={`Search performance for ${connection.siteUrl}`}

View File

@@ -24,11 +24,13 @@ const zGscDateInput = z.object({
projectId: z.string(),
range: zRange,
interval: zTimeInterval.optional().default('day'),
startDate: z.string().nullish(),
endDate: z.string().nullish(),
});
async function resolveDates(
projectId: string,
input: { range: string; startDate?: string; endDate?: string }
input: { range: string; startDate?: string | null; endDate?: string | null }
) {
const { timezone } = await getSettingsForProject(projectId);
const { startDate, endDate } = getChartStartEndDate(