mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Merge pull request #2699 from Infisical/misc/address-remaining-ui-ux-issues-audit
misc: address other ui/ux issues with audit logs
This commit is contained in:
@@ -1,10 +1,12 @@
|
|||||||
|
import { ChangeEventHandler, useState } from "react";
|
||||||
import { DayPicker, DayPickerProps } from "react-day-picker";
|
import { DayPicker, DayPickerProps } from "react-day-picker";
|
||||||
import { faCalendar } from "@fortawesome/free-solid-svg-icons";
|
import { faCalendar } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { PopoverContentProps, PopoverProps } from "@radix-ui/react-popover";
|
import { PopoverContentProps, PopoverProps } from "@radix-ui/react-popover";
|
||||||
import { format } from "date-fns";
|
import { format, setHours, setMinutes } from "date-fns";
|
||||||
|
|
||||||
import { Button } from "../Button";
|
import { Button } from "../Button";
|
||||||
|
import { Input } from "../Input";
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from "../Popoverv2";
|
import { Popover, PopoverContent, PopoverTrigger } from "../Popoverv2";
|
||||||
|
|
||||||
export type DatePickerProps = Omit<DayPickerProps, "selected"> & {
|
export type DatePickerProps = Omit<DayPickerProps, "selected"> & {
|
||||||
@@ -22,15 +24,58 @@ export const DatePicker = ({
|
|||||||
popUpContentProps,
|
popUpContentProps,
|
||||||
...props
|
...props
|
||||||
}: DatePickerProps) => {
|
}: DatePickerProps) => {
|
||||||
|
const [timeValue, setTimeValue] = useState<string>(value ? format(value, "HH:mm") : "00:00");
|
||||||
|
|
||||||
|
const handleTimeChange: ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||||
|
const time = e.target.value;
|
||||||
|
if (time) {
|
||||||
|
setTimeValue(time);
|
||||||
|
if (value) {
|
||||||
|
const [hours, minutes] = time.split(":").map((str) => parseInt(str, 10));
|
||||||
|
const newSelectedDate = setHours(setMinutes(value, minutes), hours);
|
||||||
|
onChange(newSelectedDate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDaySelect = (date: Date | undefined) => {
|
||||||
|
if (!timeValue || !date) {
|
||||||
|
onChange(date);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [hours, minutes] = timeValue.split(":").map((str) => parseInt(str, 10));
|
||||||
|
const newDate = new Date(date.getFullYear(), date.getMonth(), date.getDate(), hours, minutes);
|
||||||
|
onChange(newDate);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover {...popUpProps}>
|
<Popover {...popUpProps}>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
<Button variant="outline_bg" leftIcon={<FontAwesomeIcon icon={faCalendar} />}>
|
<Button variant="outline_bg" leftIcon={<FontAwesomeIcon icon={faCalendar} />}>
|
||||||
{value ? format(value, "PPP") : "Pick a date"}
|
{value ? format(value, "PPP") : "Pick a date and time"}
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className="w-fit p-2" {...popUpContentProps}>
|
<PopoverContent className="w-fit p-2" {...popUpContentProps}>
|
||||||
<DayPicker {...props} mode="single" selected={value} onSelect={onChange} />
|
<div className="mx-4 my-4">
|
||||||
|
<Input
|
||||||
|
type="time"
|
||||||
|
value={timeValue}
|
||||||
|
onChange={handleTimeChange}
|
||||||
|
className="bg-mineshaft-700 text-white [color-scheme:dark]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<DayPicker
|
||||||
|
{...props}
|
||||||
|
mode="single"
|
||||||
|
selected={value}
|
||||||
|
onSelect={handleDaySelect}
|
||||||
|
modifiersStyles={{
|
||||||
|
selected: {
|
||||||
|
background: "#cad62d"
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export const AuditLogsPage = withPermission(
|
|||||||
<p className="text-3xl font-semibold text-gray-200">Audit Logs</p>
|
<p className="text-3xl font-semibold text-gray-200">Audit Logs</p>
|
||||||
<div />
|
<div />
|
||||||
</div>
|
</div>
|
||||||
<LogsSection filterClassName="static p-2" showFilters isOrgAuditLogs showActorColumn />
|
<LogsSection filterClassName="static py-2" showFilters isOrgAuditLogs showActorColumn />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
/* eslint-disable no-nested-ternary */
|
/* eslint-disable no-nested-ternary */
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Control, Controller, UseFormReset, UseFormSetValue, UseFormWatch } from "react-hook-form";
|
import { Control, Controller, UseFormReset, UseFormSetValue, UseFormWatch } from "react-hook-form";
|
||||||
import {
|
import { faCaretDown, faCheckCircle, faFilterCircleXmark } from "@fortawesome/free-solid-svg-icons";
|
||||||
faCheckCircle,
|
|
||||||
faChevronDown,
|
|
||||||
faFilterCircleXmark
|
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
@@ -20,7 +16,7 @@ import {
|
|||||||
Select,
|
Select,
|
||||||
SelectItem
|
SelectItem
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import { useWorkspace } from "@app/context";
|
import { useOrganization, useWorkspace } from "@app/context";
|
||||||
import { useGetAuditLogActorFilterOpts } from "@app/hooks/api";
|
import { useGetAuditLogActorFilterOpts } from "@app/hooks/api";
|
||||||
import { eventToNameMap, userAgentTTypeoNameMap } from "@app/hooks/api/auditLogs/constants";
|
import { eventToNameMap, userAgentTTypeoNameMap } from "@app/hooks/api/auditLogs/constants";
|
||||||
import { ActorType, EventType } from "@app/hooks/api/auditLogs/enums";
|
import { ActorType, EventType } from "@app/hooks/api/auditLogs/enums";
|
||||||
@@ -60,11 +56,15 @@ export const LogsFilter = ({
|
|||||||
const [isEndDatePickerOpen, setIsEndDatePickerOpen] = useState(false);
|
const [isEndDatePickerOpen, setIsEndDatePickerOpen] = useState(false);
|
||||||
|
|
||||||
const { currentWorkspace, workspaces } = useWorkspace();
|
const { currentWorkspace, workspaces } = useWorkspace();
|
||||||
|
const { currentOrg } = useOrganization();
|
||||||
|
|
||||||
|
const workspacesInOrg = workspaces.filter((ws) => ws.orgId === currentOrg?.id);
|
||||||
|
|
||||||
const { data, isLoading } = useGetAuditLogActorFilterOpts(currentWorkspace?.id ?? "");
|
const { data, isLoading } = useGetAuditLogActorFilterOpts(currentWorkspace?.id ?? "");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (workspaces.length) {
|
if (workspacesInOrg.length) {
|
||||||
setValue("projectId", workspaces[0].id);
|
setValue("projectId", workspacesInOrg[0].id);
|
||||||
}
|
}
|
||||||
}, [workspaces]);
|
}, [workspaces]);
|
||||||
|
|
||||||
@@ -130,7 +130,7 @@ export const LogsFilter = ({
|
|||||||
: selectedEventTypes?.length === 0
|
: selectedEventTypes?.length === 0
|
||||||
? "All events"
|
? "All events"
|
||||||
: `${selectedEventTypes?.length} events selected`}
|
: `${selectedEventTypes?.length} events selected`}
|
||||||
<FontAwesomeIcon icon={faChevronDown} className="ml-2 text-xs" />
|
<FontAwesomeIcon icon={faCaretDown} className="ml-2 text-xs" />
|
||||||
</div>
|
</div>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="start" className="z-[100] max-h-80 overflow-hidden">
|
<DropdownMenuContent align="start" className="z-[100] max-h-80 overflow-hidden">
|
||||||
@@ -221,10 +221,7 @@ export const LogsFilter = ({
|
|||||||
if (e === "all") onChange(undefined);
|
if (e === "all") onChange(undefined);
|
||||||
else onChange(e);
|
else onChange(e);
|
||||||
}}
|
}}
|
||||||
className={twMerge(
|
className={twMerge("w-full border border-mineshaft-500 bg-mineshaft-700")}
|
||||||
"w-full border border-mineshaft-500 bg-mineshaft-700 text-mineshaft-100",
|
|
||||||
value === undefined && "text-mineshaft-400"
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
<SelectItem value="all" key="all">
|
<SelectItem value="all" key="all">
|
||||||
All sources
|
All sources
|
||||||
@@ -239,7 +236,7 @@ export const LogsFilter = ({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{isOrgAuditLogs && workspaces.length > 0 && (
|
{isOrgAuditLogs && workspacesInOrg.length > 0 && (
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="projectId"
|
name="projectId"
|
||||||
@@ -255,11 +252,11 @@ export const LogsFilter = ({
|
|||||||
{...field}
|
{...field}
|
||||||
onValueChange={(e) => onChange(e)}
|
onValueChange={(e) => onChange(e)}
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
"w-full border border-mineshaft-500 bg-mineshaft-700 text-mineshaft-100",
|
"w-full border border-mineshaft-500 bg-mineshaft-700 ",
|
||||||
value === undefined && "text-mineshaft-400"
|
value === undefined && "text-mineshaft-400"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{workspaces.map((project) => (
|
{workspacesInOrg.map((project) => (
|
||||||
<SelectItem value={String(project.id || "")} key={project.id}>
|
<SelectItem value={String(project.id || "")} key={project.id}>
|
||||||
{project.name}
|
{project.name}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
@@ -277,10 +274,7 @@ export const LogsFilter = ({
|
|||||||
<FormControl label="Start date" errorText={error?.message} isError={Boolean(error)}>
|
<FormControl label="Start date" errorText={error?.message} isError={Boolean(error)}>
|
||||||
<DatePicker
|
<DatePicker
|
||||||
value={field.value || undefined}
|
value={field.value || undefined}
|
||||||
onChange={(date) => {
|
onChange={onChange}
|
||||||
onChange(date);
|
|
||||||
setIsStartDatePickerOpen(false);
|
|
||||||
}}
|
|
||||||
popUpProps={{
|
popUpProps={{
|
||||||
open: isStartDatePickerOpen,
|
open: isStartDatePickerOpen,
|
||||||
onOpenChange: setIsStartDatePickerOpen
|
onOpenChange: setIsStartDatePickerOpen
|
||||||
@@ -299,11 +293,7 @@ export const LogsFilter = ({
|
|||||||
<FormControl label="End date" errorText={error?.message} isError={Boolean(error)}>
|
<FormControl label="End date" errorText={error?.message} isError={Boolean(error)}>
|
||||||
<DatePicker
|
<DatePicker
|
||||||
value={field.value || undefined}
|
value={field.value || undefined}
|
||||||
onChange={(pickedDate) => {
|
onChange={onChange}
|
||||||
pickedDate?.setHours(23, 59, 59, 999); // we choose the end of today not the start of it (going off of aws cloud watch)
|
|
||||||
onChange(pickedDate);
|
|
||||||
setIsEndDatePickerOpen(false);
|
|
||||||
}}
|
|
||||||
popUpProps={{
|
popUpProps={{
|
||||||
open: isEndDatePickerOpen,
|
open: isEndDatePickerOpen,
|
||||||
onOpenChange: setIsEndDatePickerOpen
|
onOpenChange: setIsEndDatePickerOpen
|
||||||
|
|||||||
Reference in New Issue
Block a user