mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-02 20:25:47 +00:00
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:
@@ -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>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user