mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-04-21 11:17:45 +08:00
Fix: Fixed the issue where agent log time could not be selected. (#13756)
### What problem does this PR solve? Fix: Fixed the issue where agent log time could not be selected. ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
233
web/src/components/ui/calendar.tsx
Normal file
233
web/src/components/ui/calendar.tsx
Normal file
@ -0,0 +1,233 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
import {
|
||||
DayPicker,
|
||||
getDefaultClassNames,
|
||||
type DayButton,
|
||||
type Locale,
|
||||
} from 'react-day-picker';
|
||||
|
||||
import { Button, buttonVariants } from '@/components/ui/button';
|
||||
import { cn } from '@/lib/utils';
|
||||
import {
|
||||
ChevronDownIcon,
|
||||
ChevronLeftIcon,
|
||||
ChevronRightIcon,
|
||||
} from 'lucide-react';
|
||||
|
||||
function Calendar({
|
||||
className,
|
||||
classNames,
|
||||
showOutsideDays = true,
|
||||
captionLayout = 'label',
|
||||
buttonVariant = 'ghost',
|
||||
locale,
|
||||
formatters,
|
||||
components,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DayPicker> & {
|
||||
buttonVariant?: React.ComponentProps<typeof Button>['variant'];
|
||||
}) {
|
||||
const defaultClassNames = getDefaultClassNames();
|
||||
|
||||
return (
|
||||
<DayPicker
|
||||
showOutsideDays={showOutsideDays}
|
||||
className={cn(
|
||||
'p-3 [--cell-radius:var(--radius)] [--cell-size:2.25rem] group/calendar bg-background',
|
||||
className,
|
||||
)}
|
||||
captionLayout={captionLayout}
|
||||
locale={locale}
|
||||
formatters={{
|
||||
formatMonthDropdown: (date) =>
|
||||
date.toLocaleString(locale?.code, { month: 'short' }),
|
||||
...formatters,
|
||||
}}
|
||||
classNames={{
|
||||
root: cn('w-fit', defaultClassNames.root),
|
||||
months: cn(
|
||||
'relative flex flex-col gap-4 md:flex-row',
|
||||
defaultClassNames.months,
|
||||
),
|
||||
month: cn('flex w-full flex-col gap-4', defaultClassNames.month),
|
||||
nav: cn(
|
||||
'absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1',
|
||||
defaultClassNames.nav,
|
||||
),
|
||||
button_previous: cn(
|
||||
buttonVariants({ variant: buttonVariant }),
|
||||
'w-[var(--cell-size)] h-[var(--cell-size)] p-0 select-none aria-disabled:opacity-50',
|
||||
defaultClassNames.button_previous,
|
||||
),
|
||||
button_next: cn(
|
||||
buttonVariants({ variant: buttonVariant }),
|
||||
'w-[var(--cell-size)] h-[var(--cell-size)] p-0 select-none aria-disabled:opacity-50',
|
||||
defaultClassNames.button_next,
|
||||
),
|
||||
month_caption: cn(
|
||||
'flex h-[var(--cell-size)] w-full items-center justify-center px-[var(--cell-size)]',
|
||||
defaultClassNames.month_caption,
|
||||
),
|
||||
dropdowns: cn(
|
||||
'flex h-[var(--cell-size)] w-full items-center justify-center gap-1.5 text-sm font-medium',
|
||||
defaultClassNames.dropdowns,
|
||||
),
|
||||
dropdown_root: cn(
|
||||
'cn-calendar-dropdown-root relative rounded-[var(--cell-radius)]',
|
||||
defaultClassNames.dropdown_root,
|
||||
),
|
||||
dropdown: cn(
|
||||
'absolute inset-0 bg-popover opacity-0',
|
||||
defaultClassNames.dropdown,
|
||||
),
|
||||
caption_label: cn(
|
||||
'font-medium select-none',
|
||||
captionLayout === 'label'
|
||||
? 'text-sm'
|
||||
: 'cn-calendar-caption-label flex items-center gap-1 rounded-[var(--cell-radius)] 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(
|
||||
'flex-1 rounded-[var(--cell-radius)] text-[0.8rem] font-normal text-muted-foreground select-none',
|
||||
defaultClassNames.weekday,
|
||||
),
|
||||
week: cn('mt-2 flex w-full', defaultClassNames.week),
|
||||
week_number_header: cn(
|
||||
'w-[var(--cell-size)] select-none',
|
||||
defaultClassNames.week_number_header,
|
||||
),
|
||||
week_number: cn(
|
||||
'text-[0.8rem] text-muted-foreground select-none',
|
||||
defaultClassNames.week_number,
|
||||
),
|
||||
day: cn(
|
||||
'group/day relative aspect-square h-full w-full rounded-[var(--cell-radius)] p-0 text-center select-none [&:last-child[data-selected=true]_button]:rounded-r-[var(--cell-radius)]',
|
||||
props.showWeekNumber
|
||||
? '[&:nth-child(2)[data-selected=true]_button]:rounded-l-[var(--cell-radius)]'
|
||||
: '[&:first-child[data-selected=true]_button]:rounded-l-[var(--cell-radius)]',
|
||||
defaultClassNames.day,
|
||||
),
|
||||
range_start: cn(
|
||||
'relative isolate z-0 rounded-l-[var(--cell-radius)] bg-muted after:absolute after:inset-y-0 after:right-0 after:w-4 after:bg-muted',
|
||||
defaultClassNames.range_start,
|
||||
),
|
||||
range_middle: cn('rounded-none', defaultClassNames.range_middle),
|
||||
range_end: cn(
|
||||
'relative isolate z-0 rounded-r-[var(--cell-radius)] bg-muted after:absolute after:inset-y-0 after:left-0 after:w-4 after:bg-muted',
|
||||
defaultClassNames.range_end,
|
||||
),
|
||||
today: cn(
|
||||
'rounded-[var(--cell-radius)] bg-muted text-foreground data-[selected=true]:rounded-none',
|
||||
defaultClassNames.today,
|
||||
),
|
||||
outside: cn(
|
||||
'text-muted-foreground aria-selected:text-muted-foreground',
|
||||
defaultClassNames.outside,
|
||||
),
|
||||
disabled: cn(
|
||||
'text-muted-foreground opacity-50',
|
||||
defaultClassNames.disabled,
|
||||
),
|
||||
hidden: cn('invisible', defaultClassNames.hidden),
|
||||
...classNames,
|
||||
}}
|
||||
components={{
|
||||
Root: ({ className, rootRef, ...props }) => {
|
||||
return (
|
||||
<div
|
||||
data-slot="calendar"
|
||||
ref={rootRef}
|
||||
className={cn(className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
},
|
||||
Chevron: ({ className, orientation, ...props }) => {
|
||||
if (orientation === 'left') {
|
||||
return (
|
||||
<ChevronLeftIcon className={cn('size-4', className)} {...props} />
|
||||
);
|
||||
}
|
||||
|
||||
if (orientation === 'right') {
|
||||
return (
|
||||
<ChevronRightIcon
|
||||
className={cn('size-4', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ChevronDownIcon className={cn('size-4', className)} {...props} />
|
||||
);
|
||||
},
|
||||
DayButton: ({ ...props }) => (
|
||||
<CalendarDayButton locale={locale} {...props} />
|
||||
),
|
||||
WeekNumber: ({ children, ...props }) => {
|
||||
return (
|
||||
<td {...props}>
|
||||
<div className="flex w-[var(--cell-size)] h-[var(--cell-size)] items-center justify-center text-center">
|
||||
{children}
|
||||
</div>
|
||||
</td>
|
||||
);
|
||||
},
|
||||
...components,
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function CalendarDayButton({
|
||||
className,
|
||||
day,
|
||||
modifiers,
|
||||
locale,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DayButton> & { locale?: Partial<Locale> }) {
|
||||
const defaultClassNames = getDefaultClassNames();
|
||||
|
||||
const ref = React.useRef<HTMLButtonElement>(null);
|
||||
React.useEffect(() => {
|
||||
if (modifiers.focused) ref.current?.focus();
|
||||
}, [modifiers.focused]);
|
||||
|
||||
return (
|
||||
<Button
|
||||
ref={ref}
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
data-day={day.date.toLocaleDateString(locale?.code)}
|
||||
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(
|
||||
'relative isolate z-10 flex aspect-square size-auto w-full min-w-[var(--cell-size)] flex-col gap-1 border-0 leading-none font-normal',
|
||||
'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',
|
||||
'data-[range-end=true]:rounded-[var(--cell-radius)] data-[range-end=true]:rounded-r-[var(--cell-radius)] data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground',
|
||||
'data-[range-middle=true]:rounded-none data-[range-middle=true]:bg-muted data-[range-middle=true]:text-foreground',
|
||||
'data-[range-start=true]:rounded-[var(--cell-radius)] data-[range-start=true]:rounded-l-[var(--cell-radius)] data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground',
|
||||
'data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground',
|
||||
'dark:hover:text-foreground [&>span]:text-xs [&>span]:opacity-70',
|
||||
defaultClassNames.day,
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export { Calendar, CalendarDayButton };
|
||||
57
web/src/components/ui/range-picker.tsx
Normal file
57
web/src/components/ui/range-picker.tsx
Normal file
@ -0,0 +1,57 @@
|
||||
'use client';
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Calendar } from '@/components/ui/calendar';
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from '@/components/ui/popover';
|
||||
import { format } from 'date-fns';
|
||||
import { CalendarIcon } from 'lucide-react';
|
||||
import { PropsRangeRequired } from 'react-day-picker';
|
||||
|
||||
export function DatePickerWithRange({
|
||||
selected,
|
||||
...props
|
||||
}: Omit<PropsRangeRequired, 'mode'>) {
|
||||
// const [date, setDate] = React.useState<DateRange | undefined>({
|
||||
// from: new Date(new Date().getFullYear(), 0, 20),
|
||||
// to: addDays(new Date(new Date().getFullYear(), 0, 20), 20),
|
||||
// });
|
||||
|
||||
return (
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
id="date-picker-range"
|
||||
className="justify-start px-2.5 font-normal"
|
||||
>
|
||||
<CalendarIcon />
|
||||
{selected?.from ? (
|
||||
selected.to ? (
|
||||
<>
|
||||
{format(selected.from, 'LLL dd, y')} -{' '}
|
||||
{format(selected.to, 'LLL dd, y')}
|
||||
</>
|
||||
) : (
|
||||
format(selected.from, 'LLL dd, y')
|
||||
)
|
||||
) : (
|
||||
<span>Pick a date</span>
|
||||
)}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0" align="start">
|
||||
<Calendar
|
||||
mode="range"
|
||||
selected={selected}
|
||||
// defaultMonth={date?.from}
|
||||
numberOfMonths={2}
|
||||
{...props}
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
@ -1,4 +1,3 @@
|
||||
import TimeRangePicker from '@/components/originui/time-range-picker';
|
||||
import { PageHeader } from '@/components/page-header';
|
||||
import {
|
||||
Breadcrumb,
|
||||
@ -11,6 +10,7 @@ import {
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { SearchInput } from '@/components/ui/input';
|
||||
import { RAGFlowPagination } from '@/components/ui/ragflow-pagination';
|
||||
import { DatePickerWithRange } from '@/components/ui/range-picker';
|
||||
import { Spin } from '@/components/ui/spin';
|
||||
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
||||
import { useFetchAgentLog } from '@/hooks/use-agent-request';
|
||||
@ -19,6 +19,7 @@ import {
|
||||
IAgentLogResponse,
|
||||
} from '@/interfaces/database/agent';
|
||||
import { IReferenceObject } from '@/interfaces/database/chat';
|
||||
import { formatDate } from '@/utils/date';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -46,6 +47,7 @@ const getEndOfToday = (): Date => {
|
||||
today.setHours(23, 59, 59, 999);
|
||||
return today;
|
||||
};
|
||||
|
||||
const AgentLogPage: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const { navigateToAgents, navigateToAgent } = useNavigatePage();
|
||||
@ -106,12 +108,18 @@ const AgentLogPage: React.FC = () => {
|
||||
dataIndex: 'update_date',
|
||||
key: 'update_date',
|
||||
sortable: true,
|
||||
render(text: string) {
|
||||
return formatDate(text);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('flow.createDate'),
|
||||
dataIndex: 'create_date',
|
||||
key: 'create_date',
|
||||
sortable: true,
|
||||
render(text: string) {
|
||||
return formatDate(text);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('flow.version.version'),
|
||||
@ -128,11 +136,8 @@ const AgentLogPage: React.FC = () => {
|
||||
to: searchParams.to_date,
|
||||
});
|
||||
const [keywords, setKeywords] = useState(searchParams.keywords);
|
||||
const handleDateRangeChange = ({
|
||||
from: startDate,
|
||||
to: endDate,
|
||||
}: DateRange) => {
|
||||
setCurrentDate({ from: startDate, to: endDate });
|
||||
const handleDateRangeChange = (dateRange: DateRange) => {
|
||||
setCurrentDate({ from: dateRange.from, to: dateRange.to });
|
||||
};
|
||||
|
||||
const [pagination, setPagination] = useState<{
|
||||
@ -271,10 +276,14 @@ const AgentLogPage: React.FC = () => {
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<span className="whitespace-nowrap">Latest Date</span>
|
||||
<TimeRangePicker
|
||||
onSelect={handleDateRangeChange}
|
||||
selectDateRange={currentDate}
|
||||
/>
|
||||
<DatePickerWithRange
|
||||
required
|
||||
selected={currentDate}
|
||||
onSelect={(range) =>
|
||||
range.from &&
|
||||
handleDateRangeChange({ from: range.from, to: range.to })
|
||||
}
|
||||
></DatePickerWithRange>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@ -5,6 +5,7 @@ import { Separator } from '@/components/ui/separator';
|
||||
import { DatasetMetadata } from '@/constants/chat';
|
||||
import { useSetModalState } from '@/hooks/common-hooks';
|
||||
import { useFetchDialog, useSetDialog } from '@/hooks/use-chat-request';
|
||||
import { cn } from '@/lib/utils';
|
||||
import {
|
||||
removeUselessFieldsFromValues,
|
||||
setLLMSettingEnabledValues,
|
||||
@ -33,7 +34,7 @@ export function ChatSettings({ hasSingleChatBox }: ChatSettingsProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { visible: settingVisible, switchVisible: switchSettingVisible } =
|
||||
useSetModalState(true);
|
||||
useSetModalState(false);
|
||||
|
||||
type FormSchemaType = z.infer<typeof formSchema>;
|
||||
|
||||
@ -100,71 +101,74 @@ export function ChatSettings({ hasSingleChatBox }: ChatSettingsProps) {
|
||||
}
|
||||
}, [data, form]);
|
||||
|
||||
if (settingVisible) {
|
||||
return (
|
||||
<div className="p-5">
|
||||
<Button
|
||||
onClick={switchSettingVisible}
|
||||
disabled={!hasSingleChatBox}
|
||||
variant={'ghost'}
|
||||
size="icon-sm"
|
||||
data-testid="chat-settings"
|
||||
>
|
||||
<LucideSettings />
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<section
|
||||
className="w-[440px] flex flex-col"
|
||||
data-testid="chat-detail-settings"
|
||||
>
|
||||
<div className="p-5 pb-2 flex justify-between items-center text-base">
|
||||
{t('chat.chatSetting')}
|
||||
|
||||
<Button
|
||||
variant="transparent"
|
||||
size="icon-sm"
|
||||
className="border-0"
|
||||
onClick={switchSettingVisible}
|
||||
data-testid="chat-detail-settings-close"
|
||||
>
|
||||
<LucidePanelRightClose
|
||||
className="size-4 cursor-pointer"
|
||||
<>
|
||||
{settingVisible || (
|
||||
<div className="p-5">
|
||||
<Button
|
||||
onClick={switchSettingVisible}
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
disabled={!hasSingleChatBox}
|
||||
variant={'ghost'}
|
||||
size="icon-sm"
|
||||
data-testid="chat-settings"
|
||||
>
|
||||
<LucideSettings />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(onSubmit, onInvalid)}
|
||||
className="flex-1 flex flex-col min-h-0"
|
||||
>
|
||||
<ScrollArea>
|
||||
<section className="p-5 space-y-6 overflow-auto flex-1 min-h-0">
|
||||
<ChatBasicSetting></ChatBasicSetting>
|
||||
<Separator />
|
||||
<ChatPromptEngine></ChatPromptEngine>
|
||||
<Separator />
|
||||
<ChatModelSettings></ChatModelSettings>
|
||||
</section>
|
||||
</ScrollArea>
|
||||
<section
|
||||
data-testid="chat-detail-settings"
|
||||
className={cn(
|
||||
'transition-[width] ease-out duration-300 flex-shrink-0 flex flex-col',
|
||||
settingVisible ? 'w-[440px]' : 'w-0',
|
||||
)}
|
||||
>
|
||||
<div className="p-5 pb-2 flex justify-between items-center text-base">
|
||||
{t('chat.chatSetting')}
|
||||
|
||||
<div className="p-5 pt-4 space-x-5 text-right">
|
||||
<Button
|
||||
variant={'outline'}
|
||||
<Button
|
||||
variant="transparent"
|
||||
size="icon-sm"
|
||||
className="border-0"
|
||||
onClick={switchSettingVisible}
|
||||
data-testid="chat-detail-settings-close"
|
||||
>
|
||||
<LucidePanelRightClose
|
||||
className="size-4 cursor-pointer"
|
||||
onClick={switchSettingVisible}
|
||||
data-testid="chat-detail-settings-cancel"
|
||||
>
|
||||
{t('chat.cancel')}
|
||||
</Button>
|
||||
<SavingButton loading={loading}></SavingButton>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
</section>
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(onSubmit, onInvalid)}
|
||||
className="flex-1 flex flex-col min-h-0"
|
||||
>
|
||||
<ScrollArea>
|
||||
<section className="p-5 space-y-6 overflow-auto flex-1 min-h-0">
|
||||
<ChatBasicSetting></ChatBasicSetting>
|
||||
<Separator />
|
||||
<ChatPromptEngine></ChatPromptEngine>
|
||||
<Separator />
|
||||
<ChatModelSettings></ChatModelSettings>
|
||||
</section>
|
||||
</ScrollArea>
|
||||
|
||||
<div className="p-5 pt-4 space-x-5 text-right">
|
||||
<Button
|
||||
variant={'outline'}
|
||||
onClick={switchSettingVisible}
|
||||
data-testid="chat-detail-settings-cancel"
|
||||
>
|
||||
{t('chat.cancel')}
|
||||
</Button>
|
||||
<SavingButton loading={loading}></SavingButton>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user