Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
270 changes: 161 additions & 109 deletions src/features/instance/log/components/LogsFiltersForm.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Form } from '@/components/ui/form/Form';
import { FormControl } from '@/components/ui/form/FormControl';
import { FormField } from '@/components/ui/form/FormField';
Expand All @@ -8,6 +9,9 @@ import { FormMessage } from '@/components/ui/form/FormMessage';
import { Input } from '@/components/ui/input';
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { LogFiltersFormSchema } from '@/integrations/api/instance/status/logFiltersFormSchema';
import { cn } from '@/lib/cn';
import { ChevronDownIcon, SearchIcon, SlidersHorizontalIcon } from 'lucide-react';
import { useState } from 'react';
import { UseFormReturn } from 'react-hook-form';
import z from 'zod';

Expand All @@ -16,131 +20,179 @@ export function LogsFiltersForm({
resetFilters,
submitFilters,
showLogName,
showFilter,
}: {
form: UseFormReturn<z.infer<typeof LogFiltersFormSchema>>;
resetFilters: () => void;
submitFilters: (data: z.infer<typeof LogFiltersFormSchema>) => void;
showLogName?: boolean;
showFilter?: boolean;
}) {
const [isOpen, setIsOpen] = useState(false);

return (
<div>
<Form {...form}>
<form
id="instance-edit-log-filters-form"
name="instance-edit-log-filters-form"
onSubmit={form.handleSubmit(submitFilters)}
className="flex-col space-y-5"
<Card>
<CardHeader>
<button
type="button"
onClick={() => setIsOpen((open) => !open)}
aria-expanded={isOpen}
aria-controls="logs-filters-content"
className="flex w-full items-center justify-between md:pointer-events-none"
>
{showLogName && (
<CardTitle className="flex items-center gap-2 text-base">
<SlidersHorizontalIcon className="size-4 text-muted-foreground" />
Filters
</CardTitle>
<ChevronDownIcon
className={cn('size-4 text-muted-foreground transition-transform md:hidden', isOpen && 'rotate-180')}
/>
</button>
</CardHeader>
<CardContent id="logs-filters-content" className={cn(!isOpen && 'hidden', 'md:block')}>
<Form {...form}>
<form
id="instance-edit-log-filters-form"
name="instance-edit-log-filters-form"
onSubmit={form.handleSubmit(submitFilters)}
className="flex-col space-y-4"
>
{showFilter && (
<FormField
control={form.control}
name="filter"
render={({ field }) => (
<FormItem>
<FormControl>
<div className="relative">
<SearchIcon className="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
<Input
type="search"
placeholder="Search logs…"
className="pl-9"
value={field.value ?? ''}
onChange={field.onChange}
/>
</div>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
)}
{showLogName && (
<FormField
control={form.control}
name="log_name"
render={({ field }) => (
<FormItem>
<FormLabel>Log file</FormLabel>
<Select onValueChange={field.onChange} value={field.value ?? undefined}>
<SelectTrigger className="w-full bg-white dark:bg-grey-700">
<SelectValue placeholder="Select log file" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem value="hdb.log">Application (hdb.log)</SelectItem>
<SelectItem value="system.log">System (system.log)</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
)}
<div className="grid grid-cols-2 gap-3">
<FormField
control={form.control}
name="limit"
render={({ field }) => (
<FormItem>
<FormLabel>Limit</FormLabel>
<Select onValueChange={field.onChange} value={field.value ?? undefined}>
<SelectTrigger className="w-full bg-white dark:bg-grey-700">
<SelectValue placeholder="Limit" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem value="1000">1000</SelectItem>
<SelectItem value="500">500</SelectItem>
<SelectItem value="250">250</SelectItem>
<SelectItem value="100">100</SelectItem>
<SelectItem value="10">10</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="level"
render={({ field }) => (
<FormItem>
<FormLabel>Level</FormLabel>
<Select onValueChange={field.onChange} value={field.value ?? undefined}>
<SelectTrigger className="w-full bg-white dark:bg-grey-700">
<SelectValue placeholder="Level" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem value="undefined">All</SelectItem>
<SelectItem value="notify">Notify</SelectItem>
<SelectItem value="error">Error</SelectItem>
<SelectItem value="warn">Warn</SelectItem>
<SelectItem value="info">Info</SelectItem>
<SelectItem value="debug">Debug</SelectItem>
<SelectItem value="trace">Trace</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
</div>
<FormField
control={form.control}
name="log_name"
name="from"
render={({ field }) => (
<FormItem>
<FormLabel>Log File:</FormLabel>
<Select onValueChange={field.onChange} value={field.value ?? undefined}>
<SelectTrigger className="w-full">
<SelectValue placeholder="Select log file" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem value="hdb.log">Application (hdb.log)</SelectItem>
<SelectItem value="system.log">System (system.log)</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
<FormLabel>Start date</FormLabel>
<FormControl>
<Input type="datetime-local" value={field.value ?? undefined} onChange={field.onChange} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="until"
render={({ field }) => (
<FormItem>
<FormLabel>End date</FormLabel>
<FormControl>
<Input type="datetime-local" value={field.value ?? undefined} onChange={field.onChange} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
)}
<FormField
control={form.control}
name="limit"
render={({ field }) => (
<FormItem>
<FormLabel>Log Limit:</FormLabel>
<Select onValueChange={field.onChange} value={field.value ?? undefined}>
<SelectTrigger className="w-full">
<SelectValue placeholder="Select log limit" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem value="1000">1000</SelectItem>
<SelectItem value="500">500</SelectItem>
<SelectItem value="250">250</SelectItem>
<SelectItem value="100">100</SelectItem>
<SelectItem value="10">10</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="level"
render={({ field }) => (
<FormItem>
<FormLabel>Log Level:</FormLabel>
<Select onValueChange={field.onChange} value={field.value ?? undefined}>
<SelectTrigger className="w-full">
<SelectValue placeholder="Select log level" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem value="undefined">All</SelectItem>
<SelectItem value="notify">Notify</SelectItem>
<SelectItem value="error">Error</SelectItem>
<SelectItem value="warn">Warn</SelectItem>
<SelectItem value="info">Info</SelectItem>
<SelectItem value="debug">Debug</SelectItem>
<SelectItem value="trace">Trace</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="from"
render={({ field }) => (
<FormItem>
<FormLabel>Start Date:</FormLabel>
<FormControl>
<Input type="datetime-local" value={field.value ?? undefined} onChange={field.onChange} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="until"
render={({ field }) => (
<FormItem>
<FormLabel>End Date:</FormLabel>
<FormControl>
<Input type="datetime-local" value={field.value ?? undefined} onChange={field.onChange} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>

<div className="flex items-center justify-between space-x-2 mt-5">
<Button type="submit" variant="positiveOutline" className="grow">
Apply Filters
</Button>
<Button type="reset" variant="destructiveOutline" onClick={resetFilters}>
Clear Filters
</Button>
</div>
</form>
</Form>
</div>
<div className="flex items-center gap-2 pt-1">
<Button type="submit" variant="positiveOutline" className="grow">
Apply Filters
</Button>
<Button type="reset" variant="destructiveOutline" onClick={resetFilters}>
Clear
</Button>
</div>
</form>
</Form>
</CardContent>
</Card>
);
}
9 changes: 6 additions & 3 deletions src/features/instance/log/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const defaultFormValues: z.infer<typeof LogFiltersFormSchema> = {
level: 'undefined',
from: '',
until: '',
filter: '',
};

const levelIcons: Record<ReadLogItem['level'], React.ReactNode> = {
Expand Down Expand Up @@ -184,6 +185,7 @@ export function Logs() {
const { data: registrationInfo } = useQuery(getRegistrationInfoQueryOptions(instanceParams));
const version = registrationInfo?.version;
const showLogName = !isLocalStudio && !!version && wasAReleasedBeforeB('5.0.13', version);
const showFilter = !!version && wasAReleasedBeforeB('4.7.16', version);

const {
data: instanceLogs,
Expand Down Expand Up @@ -233,16 +235,17 @@ export function Logs() {
const onRefreshClick = useRefreshClick(refetchReadLogQueryOptions);

return (
<div className="grid grid-cols-1 gap-4 text-foreground md:grid-cols-12">
<section className="col-span-1 md:col-span-4 lg:col-span-3 px-2 pt-4 md:pt-12">
<div className="grid grid-cols-1 gap-4 pt-2 text-foreground md:grid-cols-12">
<section className="col-span-1 md:col-span-4 lg:col-span-3 px-2 pb-4 md:self-start md:sticky md:top-34 md:max-h-[calc(100vh-(--spacing(34)))] md:overflow-y-auto">
<LogsFiltersForm
form={form}
resetFilters={resetFilters}
submitFilters={submitFilters}
showLogName={showLogName}
showFilter={showFilter}
/>

<div className="flex items-center gap-2 mt-5">
<div className="flex items-center gap-2 mt-3">
<Button
variant="defaultOutline"
onClick={onRefreshClick}
Expand Down
2 changes: 2 additions & 0 deletions src/integrations/api/instance/status/getReadLog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export async function getReadLog(
from: logFilters.from ? new Date(logFilters.from).toISOString() : undefined,
until: logFilters.until ? new Date(logFilters.until).toISOString() : undefined,
log_name: logFilters.log_name ?? undefined,
filter: logFilters.filter ? logFilters.filter : undefined,
order: 'desc',
});
return data;
Expand All @@ -46,6 +47,7 @@ export function getReadLogQueryOptions(params: GetReadLogParams & InstanceClient
logFilters.from,
logFilters.until,
logFilters.log_name,
logFilters.filter,
params.replicated,
] as const,
queryFn: () => getReadLog(params),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ export const LogFiltersFormSchema = z.object({
from: z.string().or(z.undefined()).or(z.null()).optional(),
until: z.string().or(z.undefined()).or(z.null()).optional(),
log_name: z.enum(['hdb.log', 'system.log']).or(z.undefined()).or(z.null()).optional(),
filter: z.string().or(z.undefined()).or(z.null()).optional(),
});