Merge pull request #4801 from Infisical/ENG-3501

feat(frontend): add text filter support to audit log event and user agent filters
This commit is contained in:
Scott Wilson
2025-11-05 08:49:28 -08:00
committed by GitHub
@@ -1,7 +1,8 @@
/* eslint-disable no-nested-ternary */ /* eslint-disable no-nested-ternary */
import { useMemo } from "react"; import { useMemo } from "react";
import { Controller, useForm } from "react-hook-form"; import { Controller, useForm } from "react-hook-form";
import { faCaretDown, faCheckCircle, faFilterCircleXmark } from "@fortawesome/free-solid-svg-icons"; import { MultiValue, SingleValue } from "react-select";
import { faFilterCircleXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { AnimatePresence, motion } from "framer-motion"; import { AnimatePresence, motion } from "framer-motion";
@@ -11,13 +12,10 @@ import {
Button, Button,
DropdownMenu, DropdownMenu,
DropdownMenuContent, DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger, DropdownMenuTrigger,
FilterableSelect, FilterableSelect,
FormControl, FormControl,
Input, Input
Select,
SelectItem
} from "@app/components/v2"; } from "@app/components/v2";
import { Badge } from "@app/components/v3"; import { Badge } from "@app/components/v3";
import { useOrganization } from "@app/context"; import { useOrganization } from "@app/context";
@@ -132,7 +130,7 @@ export const LogsFilter = ({ presets, setFilter, filter, project }: Props) => {
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="end" className="mt-4 overflow-visible py-4"> <DropdownMenuContent align="end" className="mt-4 overflow-visible py-4">
<form onSubmit={handleSubmit(setFilter)}> <form onSubmit={handleSubmit(setFilter)}>
<div className="flex min-w-64 flex-col font-inter"> <div className="flex max-w-96 min-w-96 flex-col font-inter">
<div className="mb-3 flex items-center border-b border-b-mineshaft-500 px-3 pb-2"> <div className="mb-3 flex items-center border-b border-b-mineshaft-500 px-3 pb-2">
<div className="flex w-full items-center justify-between"> <div className="flex w-full items-center justify-between">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
@@ -173,76 +171,24 @@ export const LogsFilter = ({ presets, setFilter, filter, project }: Props) => {
name="eventType" name="eventType"
render={({ field }) => ( render={({ field }) => (
<FormControl> <FormControl>
<DropdownMenu> <FilterableSelect
<DropdownMenuTrigger asChild> value={filteredEventTypes.filter((eventType) =>
<div className="inline-flex thin-scrollbar w-full cursor-pointer items-center justify-between rounded-md border border-mineshaft-500 bg-mineshaft-700 px-3 py-2 font-inter text-sm font-normal whitespace-nowrap text-bunker-200 outline-hidden data-placeholder:text-mineshaft-200"> field.value.includes(eventType.value as EventType)
{selectedEventTypes?.length === 1 )}
? filteredEventTypes.find( isMulti
(eventType) => eventType.value === selectedEventTypes[0] isClearable
)?.label onChange={(options) =>
: selectedEventTypes?.length === 0 field.onChange(
? "All events" (options as MultiValue<(typeof filteredEventTypes)[number]>).map(
: `${selectedEventTypes?.length} events selected`} (option) => option.value
<FontAwesomeIcon icon={faCaretDown} className="ml-2 text-xs" /> )
</div> )
</DropdownMenuTrigger> }
<DropdownMenuContent placeholder="All events"
align="end" options={filteredEventTypes}
sideOffset={2} getOptionValue={(option) => option.value}
className="z-100 max-h-80 thin-scrollbar overflow-hidden" getOptionLabel={(option) => option.label}
> />
<div className="max-h-80 overflow-y-auto">
{filteredEventTypes.length > 0 ? (
filteredEventTypes.map((eventType) => {
const isSelected = selectedEventTypes?.includes(
eventType.value as EventType
);
return (
<DropdownMenuItem
onSelect={(event) =>
filteredEventTypes.length > 1 && event.preventDefault()
}
onClick={() => {
if (
selectedEventTypes?.includes(eventType.value as EventType)
) {
field.onChange(
selectedEventTypes?.filter(
(e: string) => e !== eventType.value
)
);
} else {
field.onChange([
...(selectedEventTypes || []),
eventType.value
]);
}
}}
key={`event-type-${eventType.value}`}
icon={
isSelected ? (
<FontAwesomeIcon
icon={faCheckCircle}
className="pr-0.5 text-primary"
/>
) : (
<div className="pl-[1.01rem]" />
)
}
iconPos="left"
className="w-[28.4rem] text-sm"
>
{eventType.label}
</DropdownMenuItem>
);
})
) : (
<div />
)}
</div>
</DropdownMenuContent>
</DropdownMenu>
</FormControl> </FormControl>
)} )}
/> />
@@ -256,31 +202,27 @@ export const LogsFilter = ({ presets, setFilter, filter, project }: Props) => {
<Controller <Controller
control={control} control={control}
name="userAgentType" name="userAgentType"
render={({ field: { onChange, value, ...field }, fieldState: { error } }) => ( render={({ field: { onChange, value }, fieldState: { error } }) => (
<FormControl <FormControl
errorText={error?.message} errorText={error?.message}
isError={Boolean(error)} isError={Boolean(error)}
className="w-full" className="w-full"
> >
<Select <FilterableSelect
{...field} value={
value={value === undefined ? "all" : value} userAgentTypes.find(
onValueChange={(e) => { (userAgentType) => value === (userAgentType.value as UserAgentType)
if (e === "all") onChange(undefined); ) ?? null
else setValue("userAgentType", e as UserAgentType, { shouldDirty: true }); }
}} isClearable
className={twMerge("w-full border border-mineshaft-500 bg-mineshaft-700")} onChange={(option) =>
position="popper" onChange((option as SingleValue<(typeof userAgentTypes)[number]>)?.value)
> }
<SelectItem value="all" key="all"> placeholder="All sources"
All sources options={userAgentTypes}
</SelectItem> getOptionValue={(option) => option.value}
{userAgentTypes.map(({ label, value: userAgent }) => ( getOptionLabel={(option) => option.label}
<SelectItem value={userAgent} key={label}> />
{label}
</SelectItem>
))}
</Select>
</FormControl> </FormControl>
)} )}
/> />