diff --git a/ui/app/globals.css b/ui/app/globals.css index 9eaa4e84..37756b40 100644 --- a/ui/app/globals.css +++ b/ui/app/globals.css @@ -70,36 +70,36 @@ :root { --radius: 0.5rem; - --background: oklch(0.9900 0 0); + --background: oklch(0.99 0 0); --foreground: oklch(0 0 0); --card: oklch(1 0 0); --card-foreground: oklch(0 0 0); - --popover: oklch(0.9900 0 0); + --popover: oklch(0.99 0 0); --popover-foreground: oklch(0 0 0); --primary: oklch(0 0 0); --primary-foreground: oklch(1 0 0); - --secondary: oklch(0.9400 0 0); + --secondary: oklch(0.94 0 0); --secondary-foreground: oklch(0 0 0); - --muted: oklch(0.9700 0 0); - --muted-foreground: oklch(0.4400 0 0); - --accent: oklch(0.9400 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.44 0 0); + --accent: oklch(0.94 0 0); --accent-foreground: oklch(0 0 0); - --destructive: oklch(0.6300 0.1900 23.0300); - --border: oklch(0.9200 0 0); - --input: oklch(0.9400 0 0); + --destructive: oklch(0.63 0.19 23.03); + --border: oklch(0.92 0 0); + --input: oklch(0.94 0 0); --ring: oklch(0 0 0); - --chart-1: oklch(0.8100 0.1700 75.3500); - --chart-2: oklch(0.5500 0.2200 264.5300); - --chart-3: oklch(0.7200 0 0); - --chart-4: oklch(0.9200 0 0); - --chart-5: oklch(0.5600 0 0); - --sidebar: oklch(0.9900 0 0); + --chart-1: oklch(0.81 0.17 75.35); + --chart-2: oklch(0.55 0.22 264.53); + --chart-3: oklch(0.72 0 0); + --chart-4: oklch(0.92 0 0); + --chart-5: oklch(0.56 0 0); + --sidebar: oklch(0.99 0 0); --sidebar-foreground: oklch(0 0 0); --sidebar-primary: oklch(0 0 0); --sidebar-primary-foreground: oklch(1 0 0); - --sidebar-accent: oklch(0.9400 0 0); + --sidebar-accent: oklch(0.94 0 0); --sidebar-accent-foreground: oklch(0 0 0); - --sidebar-border: oklch(0.9400 0 0); + --sidebar-border: oklch(0.94 0 0); --sidebar-ring: oklch(0 0 0); --destructive-foreground: oklch(1 0 0); --font-sans: Geist, sans-serif; @@ -115,11 +115,16 @@ --spacing: 0.25rem; --shadow-2xs: 0px 1px 2px 0px hsl(0 0% 0% / 0.09); --shadow-xs: 0px 1px 2px 0px hsl(0 0% 0% / 0.09); - --shadow-sm: 0px 1px 2px 0px hsl(0 0% 0% / 0.18), 0px 1px 2px -1px hsl(0 0% 0% / 0.18); - --shadow: 0px 1px 2px 0px hsl(0 0% 0% / 0.18), 0px 1px 2px -1px hsl(0 0% 0% / 0.18); - --shadow-md: 0px 1px 2px 0px hsl(0 0% 0% / 0.18), 0px 2px 4px -1px hsl(0 0% 0% / 0.18); - --shadow-lg: 0px 1px 2px 0px hsl(0 0% 0% / 0.18), 0px 4px 6px -1px hsl(0 0% 0% / 0.18); - --shadow-xl: 0px 1px 2px 0px hsl(0 0% 0% / 0.18), 0px 8px 10px -1px hsl(0 0% 0% / 0.18); + --shadow-sm: + 0px 1px 2px 0px hsl(0 0% 0% / 0.18), 0px 1px 2px -1px hsl(0 0% 0% / 0.18); + --shadow: + 0px 1px 2px 0px hsl(0 0% 0% / 0.18), 0px 1px 2px -1px hsl(0 0% 0% / 0.18); + --shadow-md: + 0px 1px 2px 0px hsl(0 0% 0% / 0.18), 0px 2px 4px -1px hsl(0 0% 0% / 0.18); + --shadow-lg: + 0px 1px 2px 0px hsl(0 0% 0% / 0.18), 0px 4px 6px -1px hsl(0 0% 0% / 0.18); + --shadow-xl: + 0px 1px 2px 0px hsl(0 0% 0% / 0.18), 0px 8px 10px -1px hsl(0 0% 0% / 0.18); --shadow-2xl: 0px 1px 2px 0px hsl(0 0% 0% / 0.45); --tracking-normal: 0em; } @@ -127,35 +132,35 @@ .dark { --background: oklch(0 0 0); --foreground: oklch(1 0 0); - --card: oklch(0.1400 0 0); + --card: oklch(0.14 0 0); --card-foreground: oklch(1 0 0); - --popover: oklch(0.1800 0 0); + --popover: oklch(0.18 0 0); --popover-foreground: oklch(1 0 0); --primary: oklch(1 0 0); --primary-foreground: oklch(0 0 0); - --secondary: oklch(0.2500 0 0); + --secondary: oklch(0.25 0 0); --secondary-foreground: oklch(1 0 0); - --muted: oklch(0.2300 0 0); - --muted-foreground: oklch(0.7200 0 0); - --accent: oklch(0.3200 0 0); + --muted: oklch(0.23 0 0); + --muted-foreground: oklch(0.72 0 0); + --accent: oklch(0.32 0 0); --accent-foreground: oklch(1 0 0); - --destructive: oklch(0.6900 0.2000 23.9100); - --border: oklch(0.2600 0 0); - --input: oklch(0.3200 0 0); - --ring: oklch(0.7200 0 0); - --chart-1: oklch(0.8100 0.1700 75.3500); - --chart-2: oklch(0.5800 0.2100 260.8400); - --chart-3: oklch(0.5600 0 0); - --chart-4: oklch(0.4400 0 0); - --chart-5: oklch(0.9200 0 0); - --sidebar: oklch(0.1800 0 0); + --destructive: oklch(0.69 0.2 23.91); + --border: oklch(0.26 0 0); + --input: oklch(0.32 0 0); + --ring: oklch(0.72 0 0); + --chart-1: oklch(0.81 0.17 75.35); + --chart-2: oklch(0.58 0.21 260.84); + --chart-3: oklch(0.56 0 0); + --chart-4: oklch(0.44 0 0); + --chart-5: oklch(0.92 0 0); + --sidebar: oklch(0.18 0 0); --sidebar-foreground: oklch(1 0 0); --sidebar-primary: oklch(1 0 0); --sidebar-primary-foreground: oklch(0 0 0); - --sidebar-accent: oklch(0.3200 0 0); + --sidebar-accent: oklch(0.32 0 0); --sidebar-accent-foreground: oklch(1 0 0); - --sidebar-border: oklch(0.3200 0 0); - --sidebar-ring: oklch(0.7200 0 0); + --sidebar-border: oklch(0.32 0 0); + --sidebar-ring: oklch(0.72 0 0); --destructive-foreground: oklch(0 0 0); --radius: 0.5rem; --font-sans: Geist, sans-serif; @@ -171,11 +176,16 @@ --spacing: 0.25rem; --shadow-2xs: 0px 1px 2px 0px hsl(0 0% 0% / 0.09); --shadow-xs: 0px 1px 2px 0px hsl(0 0% 0% / 0.09); - --shadow-sm: 0px 1px 2px 0px hsl(0 0% 0% / 0.18), 0px 1px 2px -1px hsl(0 0% 0% / 0.18); - --shadow: 0px 1px 2px 0px hsl(0 0% 0% / 0.18), 0px 1px 2px -1px hsl(0 0% 0% / 0.18); - --shadow-md: 0px 1px 2px 0px hsl(0 0% 0% / 0.18), 0px 2px 4px -1px hsl(0 0% 0% / 0.18); - --shadow-lg: 0px 1px 2px 0px hsl(0 0% 0% / 0.18), 0px 4px 6px -1px hsl(0 0% 0% / 0.18); - --shadow-xl: 0px 1px 2px 0px hsl(0 0% 0% / 0.18), 0px 8px 10px -1px hsl(0 0% 0% / 0.18); + --shadow-sm: + 0px 1px 2px 0px hsl(0 0% 0% / 0.18), 0px 1px 2px -1px hsl(0 0% 0% / 0.18); + --shadow: + 0px 1px 2px 0px hsl(0 0% 0% / 0.18), 0px 1px 2px -1px hsl(0 0% 0% / 0.18); + --shadow-md: + 0px 1px 2px 0px hsl(0 0% 0% / 0.18), 0px 2px 4px -1px hsl(0 0% 0% / 0.18); + --shadow-lg: + 0px 1px 2px 0px hsl(0 0% 0% / 0.18), 0px 4px 6px -1px hsl(0 0% 0% / 0.18); + --shadow-xl: + 0px 1px 2px 0px hsl(0 0% 0% / 0.18), 0px 8px 10px -1px hsl(0 0% 0% / 0.18); --shadow-2xl: 0px 1px 2px 0px hsl(0 0% 0% / 0.45); } @@ -201,4 +211,4 @@ .animate-shimmer { animation: shimmer 2s infinite; -} \ No newline at end of file +} diff --git a/ui/app/logs/log-entry-card.tsx b/ui/app/logs/log-entry-card.tsx index f60d151d..20133908 100644 --- a/ui/app/logs/log-entry-card.tsx +++ b/ui/app/logs/log-entry-card.tsx @@ -53,7 +53,6 @@ export function LogEntryCard({ entry, onClick }: LogEntryCardProps) { return (
onClick(entry)} > diff --git a/ui/app/logs/log-filters.tsx b/ui/app/logs/log-filters.tsx index 6257fbd0..cf85a2ba 100644 --- a/ui/app/logs/log-filters.tsx +++ b/ui/app/logs/log-filters.tsx @@ -15,8 +15,16 @@ import { SelectTrigger, SelectValue, } from '@/components/ui/select'; -import { Calendar, Filter } from 'lucide-react'; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from '@/components/ui/popover'; +import { Calendar } from '@/components/ui/calendar'; +import { CalendarIcon, Filter, X } from 'lucide-react'; import { useState, useEffect } from 'react'; +import { format } from 'date-fns'; +import { cn } from '@/lib/utils'; interface LogFiltersProps { selectedDate: string; @@ -24,7 +32,6 @@ interface LogFiltersProps { requestId: string; searchText: string; limit: number; - availableDates: string[]; onDateChange: (date: string) => void; onLevelChange: (level: string) => void; onRequestIdChange: (requestId: string) => void; @@ -42,7 +49,6 @@ export function LogFilters({ requestId, searchText, limit, - availableDates, onDateChange, onLevelChange, onRequestIdChange, @@ -56,6 +62,9 @@ export function LogFilters({ isPreset ? '' : limit.toString() ); const [isCustom, setIsCustom] = useState(!isPreset); + const [date, setDate] = useState( + selectedDate && selectedDate !== 'all' ? new Date(selectedDate) : undefined + ); useEffect(() => { const currentIsPreset = PRESET_LIMITS.includes(limit.toString()); @@ -65,6 +74,18 @@ export function LogFilters({ } }, [limit]); + useEffect(() => { + if (selectedDate === 'all' || !selectedDate) { + setDate(undefined); + } else { + try { + setDate(new Date(selectedDate)); + } catch { + setDate(undefined); + } + } + }, [selectedDate]); + const handleLimitChange = (value: string) => { if (value === 'custom') { setIsCustom(true); @@ -100,6 +121,15 @@ export function LogFilters({ } }; + const handleDateSelect = (selectedDate: Date | undefined) => { + setDate(selectedDate); + if (selectedDate) { + onDateChange(format(selectedDate, 'yyyy-MM-dd')); + } else { + onDateChange('all'); + } + }; + return ( @@ -115,20 +145,40 @@ export function LogFilters({
- + + + + + + + + + {date && ( + + )}
diff --git a/ui/app/logs/page.tsx b/ui/app/logs/page.tsx index 4a1400ff..b3551aad 100644 --- a/ui/app/logs/page.tsx +++ b/ui/app/logs/page.tsx @@ -17,7 +17,7 @@ import { ScrollArea } from '@/components/ui/scroll-area'; import { FileText, RefreshCw } from 'lucide-react'; import { apiClient } from '@/lib/api/client'; import { SidebarInset, SidebarProvider } from '@/components/ui/sidebar'; -import { LogEntry, LogsResponse, DatesResponse } from './types'; +import { LogEntry, LogsResponse } from './types'; import { LogFilters } from './log-filters'; import { LogEntryCard } from './log-entry-card'; import { LogDetailsDialog } from './log-details-dialog'; @@ -31,12 +31,6 @@ export default function LogsPage() { const [selectedLog, setSelectedLog] = useState(null); const [isDialogOpen, setIsDialogOpen] = useState(false); - const { data: datesData } = useQuery({ - queryKey: ['log-dates'], - queryFn: () => apiClient.get('/admin/api/logs/dates'), - refetchInterval: 300000, - }); - const { data: logsData, refetch: refetchLogs, @@ -107,7 +101,6 @@ export default function LogsPage() { requestId={requestId} searchText={searchText} limit={limit} - availableDates={datesData?.dates || []} onDateChange={setSelectedDate} onLevelChange={setSelectedLevel} onRequestIdChange={setRequestId} @@ -151,9 +144,9 @@ export default function LogsPage() { <>
- {logsData.logs.map((entry) => ( + {logsData.logs.map((entry, index) => ( diff --git a/ui/components/ui/calendar.tsx b/ui/components/ui/calendar.tsx new file mode 100644 index 00000000..3cdae570 --- /dev/null +++ b/ui/components/ui/calendar.tsx @@ -0,0 +1,72 @@ +'use client'; + +import * as React from 'react'; +import { ChevronLeft, ChevronRight } from 'lucide-react'; +import { DayPicker } from 'react-day-picker'; + +import { cn } from '@/lib/utils'; +import { buttonVariants } from '@/components/ui/button'; + +export type CalendarProps = React.ComponentProps; + +function Calendar({ + className, + classNames, + showOutsideDays = true, + ...props +}: CalendarProps) { + return ( + { + if (orientation === 'left') { + return ; + } + return ; + }, + }} + {...props} + /> + ); +} +Calendar.displayName = 'Calendar'; + +export { Calendar };