Skip to content

Commit

Permalink
feat: runs filtering by relative timeframe (#832)
Browse files Browse the repository at this point in the history
* feat: runs filtering by relative timeframe

* improve logic

* Used formatDateTime

* fix: logic of  determineTimeFrame() function

---------

Co-authored-by: Matt Aitken <[email protected]>
  • Loading branch information
Kritik-J and matt-aitken authored Jan 15, 2024
1 parent ff4ff86 commit 3bb82ed
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 1 deletion.
19 changes: 18 additions & 1 deletion apps/webapp/app/components/runs/RunFilters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ import {
environmentKeys,
statusKeys,
} from "./RunStatuses";
import { RunTimeFrameFilter } from "./RunTimeFrameFilter";

export function RunsFilters() {
const navigate = useNavigate();
const location = useOptimisticLocation();
const searchParams = new URLSearchParams(location.search);
const { environment, status } = RunListSearchSchema.parse(
const { environment, status, from, to } = RunListSearchSchema.parse(
Object.fromEntries(searchParams.entries())
);

Expand All @@ -55,6 +56,20 @@ export function RunsFilters() {
handleFilterChange("environment", value === "ALL" ? undefined : value);
};

const handleRelativeTimeFrameChange = (value: number) => {
if (value) {
const date = new Date().getTime();
searchParams.set("from", (date - value).toString());
searchParams.set("to", date.toString());
} else {
searchParams.delete("from");
searchParams.delete("to");
}
searchParams.delete("cursor");
searchParams.delete("direction");
navigate(`${location.pathname}?${searchParams.toString()}`);
};

return (
<div className="flex flex-row justify-between gap-x-2">
<SelectGroup>
Expand Down Expand Up @@ -108,6 +123,8 @@ export function RunsFilters() {
</SelectContent>
</Select>
</SelectGroup>

<RunTimeFrameFilter from={from} to={to} onValueChange={handleRelativeTimeFrameChange} />
</div>
);
}
Expand Down
8 changes: 8 additions & 0 deletions apps/webapp/app/components/runs/RunStatuses.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,14 @@ export const RunListSearchSchema = z.object({
direction: DirectionSchema.optional(),
status: FilterableStatus.optional(),
environment: FilterableEnvironment.optional(),
from: z
.string()
.transform((value) => parseInt(value))
.optional(),
to: z
.string()
.transform((value) => parseInt(value))
.optional(),
});

export const filterableStatuses: Record<FilterableStatus, JobRunStatus[]> = {
Expand Down
130 changes: 130 additions & 0 deletions apps/webapp/app/components/runs/RunTimeFrameFilter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import { useState } from "react";
import { Popover, PopoverContent, PopoverTrigger } from "../primitives/Popover";
import { Button } from "../primitives/Buttons";
import { ChevronDownIcon } from "lucide-react";
import { Paragraph } from "../primitives/Paragraph";
import { cn } from "~/utils/cn";
import { formatDateTime } from "../primitives/DateTime";

type RunTimeFrameFilterProps = {
from?: number;
to?: number;
onValueChange: (value: number) => void;
};

export function RunTimeFrameFilter({ from, to, onValueChange }: RunTimeFrameFilterProps) {
const [isOpen, setIsOpen] = useState(false);
const [activeTimeFrame, setActiveTimeFrame] = useState<RelativeTimeFrameItem | undefined>();

const determineTimeFrame = (from: number | undefined, to: number | undefined) => {
if (!from || !to) {
return "Timeframe";
}

if (activeTimeFrame) {
return activeTimeFrame.label;
}

const toDateTime = formatDateTime(new Date(to), "UTC", ["en-US"], false, true);
const fromDateTime = formatDateTime(new Date(from), "UTC", ["en-US"], false, true);

return `${fromDateTime} - ${toDateTime} (UTC)`;
};

return (
<Popover onOpenChange={(open) => setIsOpen(open)} open={isOpen} modal>
<PopoverTrigger asChild>
<Button
variant="secondary/small"
className="bg-slate-800 group-hover:bg-tertiary-foreground"
>
<Paragraph variant="extra-small" className="mr-2">
{determineTimeFrame(from, to)}
</Paragraph>

<ChevronDownIcon className="h-4 w-4 text-bright" />
</Button>
</PopoverTrigger>

<PopoverContent align="start" className="border border-slate-800 bg-popover">
<Paragraph variant="extra-small" className="mb-4 uppercase">
TimeFrame
</Paragraph>

<div className="grid grid-cols-3 gap-2">
{timeFrameValues.map((timeframe) => (
<Button
key={timeframe.value}
variant="tertiary/small"
className={cn(
"w-full border border-slate-700 group-hover:bg-slate-700",
activeTimeFrame?.value === timeframe.value &&
"border-slate-700 bg-slate-700 group-hover:border-slate-700 group-hover:bg-slate-700"
)}
onClick={() => {
setIsOpen(false);
setActiveTimeFrame(timeframe);
onValueChange(timeframe.value);
}}
>
<Paragraph variant="extra-small">{timeframe.label}</Paragraph>
</Button>
))}
</div>
</PopoverContent>
</Popover>
);
}

const timeFrameValues = [
{
label: "5 mins",
value: 5 * 60 * 1000,
},
{
label: "15 mins",
value: 15 * 60 * 1000,
},
{
label: "30 mins",
value: 30 * 60 * 1000,
},
{
label: "1 hour",
value: 60 * 60 * 1000,
},
{
label: "3 hours",
value: 3 * 60 * 60 * 1000,
},
{
label: "6 hours",
value: 6 * 60 * 60 * 1000,
},
{
label: "1 day",
value: 24 * 60 * 60 * 1000,
},
{
label: "3 days",
value: 3 * 24 * 60 * 60 * 1000,
},
{
label: "7 days",
value: 7 * 24 * 60 * 60 * 1000,
},
{
label: "10 days",
value: 10 * 24 * 60 * 60 * 1000,
},
{
label: "14 days",
value: 14 * 24 * 60 * 60 * 1000,
},
{
label: "30 days",
value: 30 * 24 * 60 * 60 * 1000,
},
];

export type RelativeTimeFrameItem = (typeof timeFrameValues)[number];
8 changes: 8 additions & 0 deletions apps/webapp/app/presenters/RunListPresenter.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ type RunListOptions = {
filterEnvironment?: FilterableEnvironment;
cursor?: string;
pageSize?: number;
from?: number;
to?: number;
};

const DEFAULT_PAGE_SIZE = 20;
Expand All @@ -43,6 +45,8 @@ export class RunListPresenter {
direction = "forward",
cursor,
pageSize = DEFAULT_PAGE_SIZE,
from,
to,
}: RunListOptions) {
const filterStatuses = filterStatus ? filterableStatuses[filterStatus] : undefined;

Expand Down Expand Up @@ -133,6 +137,10 @@ export class RunListPresenter {
},
status: filterStatuses ? { in: filterStatuses } : undefined,
environment: filterEnvironment ? { type: filterEnvironment } : undefined,
startedAt: {
gte: from ? new Date(from).toISOString() : undefined,
lte: to ? new Date(to).toISOString() : undefined,
},
},
orderBy: [{ id: "desc" }],
//take an extra record to tell if there are more
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
organizationSlug,
direction: searchParams.direction,
cursor: searchParams.cursor,
from: searchParams.from,
to: searchParams.to,
});

return typedjson({ event, list });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
organizationSlug,
direction: searchParams.direction,
cursor: searchParams.cursor,
from: searchParams.from,
to: searchParams.to,
});

return typedjson({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
direction: searchParams.direction,
cursor: searchParams.cursor,
pageSize: 25,
from: searchParams.from,
to: searchParams.to,
});

return typedjson({
Expand Down

0 comments on commit 3bb82ed

Please sign in to comment.