Skip to content

Commit a803a61

Browse files
committed
devin fix
1 parent bbd2fbd commit a803a61

File tree

1 file changed

+68
-19
lines changed
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam._index

1 file changed

+68
-19
lines changed

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam._index/route.tsx

Lines changed: 68 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import {
55
ChevronUpIcon,
66
ExclamationTriangleIcon,
77
LightBulbIcon,
8+
MagnifyingGlassIcon,
9+
XMarkIcon,
810
UserPlusIcon,
911
VideoCameraIcon,
1012
} from "@heroicons/react/20/solid";
@@ -14,7 +16,7 @@ import { type ActionFunctionArgs, type LoaderFunctionArgs } from "@remix-run/ser
1416
import { DiscordIcon } from "@trigger.dev/companyicons";
1517
import { formatDurationMilliseconds } from "@trigger.dev/core/v3";
1618
import type { TaskRunStatus } from "@trigger.dev/database";
17-
import { Fragment, Suspense, useCallback, useEffect, useMemo, useRef, useState } from "react";
19+
import { Fragment, Suspense, useCallback, useEffect, useRef, useState } from "react";
1820
import type { PanelHandle } from "react-window-splitter";
1921
import { Bar, BarChart, ResponsiveContainer, Tooltip, type TooltipProps } from "recharts";
2022
import { TypedAwait, typeddefer, useTypedLoaderData } from "remix-typedjson";
@@ -36,7 +38,7 @@ import { Callout } from "~/components/primitives/Callout";
3638
import { formatDateTime } from "~/components/primitives/DateTime";
3739
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "~/components/primitives/Dialog";
3840
import { Header2, Header3 } from "~/components/primitives/Headers";
39-
import { SearchInput } from "~/components/primitives/SearchInput";
41+
import { Input } from "~/components/primitives/Input";
4042
import { NavBar, PageAccessories, PageTitle } from "~/components/primitives/PageHeader";
4143
import { Paragraph } from "~/components/primitives/Paragraph";
4244
import { PopoverMenuItem } from "~/components/primitives/Popover";
@@ -70,8 +72,7 @@ import {
7072
} from "~/components/runs/v3/TaskTriggerSource";
7173
import { useEnvironment } from "~/hooks/useEnvironment";
7274
import { useEventSource } from "~/hooks/useEventSource";
73-
import { useSearchParams } from "~/hooks/useSearchParam";
74-
import { matchSorter } from "match-sorter";
75+
import { useFuzzyFilter } from "~/hooks/useFuzzyFilter";
7576
import { useOrganization } from "~/hooks/useOrganizations";
7677
import { useProject } from "~/hooks/useProject";
7778
import { findProjectBySlug } from "~/models/project.server";
@@ -87,6 +88,7 @@ import {
8788
uiPreferencesStorage,
8889
} from "~/services/preferences/uiPreferences.server";
8990
import { requireUserId } from "~/services/session.server";
91+
import { motion } from "framer-motion";
9092
import { cn } from "~/utils/cn";
9193
import {
9294
docsPath,
@@ -174,19 +176,10 @@ export default function Page() {
174176
const environment = useEnvironment();
175177
const { tasks, activity, runningStats, durations, usefulLinksPreference } =
176178
useTypedLoaderData<typeof loader>();
177-
const { value: searchValue } = useSearchParams();
178-
const search = searchValue("search") ?? "";
179-
const filteredItems = useMemo(() => {
180-
const terms = search
181-
.trim()
182-
.split(" ")
183-
.filter((t) => t !== "");
184-
if (terms.length === 0) return tasks;
185-
return terms.reduceRight(
186-
(results, term) => matchSorter(results, term, { keys: ["slug", "filePath", "triggerSource"] }),
187-
tasks
188-
);
189-
}, [tasks, search]);
179+
const { filterText, setFilterText, filteredItems } = useFuzzyFilter<TaskListItem>({
180+
items: tasks,
181+
keys: ["slug", "filePath", "triggerSource"],
182+
});
190183

191184
const hasTasks = tasks.length > 0;
192185

@@ -251,8 +244,13 @@ export default function Page() {
251244
{tasks.length === 0 ? <UserHasNoTasks /> : null}
252245
<div className="max-h-full overflow-hidden">
253246
<div className="flex items-center justify-between gap-1 p-2">
254-
<SearchInput placeholder="Search tasks…" autoFocus />
255-
{!showUsefulLinks && (
247+
<AnimatedSearchField
248+
value={filterText}
249+
onChange={setFilterText}
250+
placeholder="Search tasks…"
251+
autoFocus
252+
/>
253+
{!showUsefulLinks && (
256254
<Button
257255
variant="secondary/small"
258256
TrailingIcon={LightBulbIcon}
@@ -871,3 +869,54 @@ function FailedToLoadStats() {
871869
/>
872870
);
873871
}
872+
873+
function AnimatedSearchField({
874+
value,
875+
onChange,
876+
placeholder,
877+
autoFocus,
878+
}: {
879+
value: string;
880+
onChange: (value: string) => void;
881+
placeholder?: string;
882+
autoFocus?: boolean;
883+
}) {
884+
const [isFocused, setIsFocused] = useState(false);
885+
886+
return (
887+
<motion.div
888+
initial={{ width: "auto" }}
889+
animate={{ width: isFocused && value.length > 0 ? "24rem" : "auto" }}
890+
transition={{ type: "spring", stiffness: 300, damping: 30 }}
891+
className="relative h-6 min-w-52"
892+
>
893+
<Input
894+
type="text"
895+
variant="secondary-small"
896+
placeholder={placeholder}
897+
value={value}
898+
onChange={(e) => onChange(e.target.value)}
899+
fullWidth
900+
autoFocus={autoFocus}
901+
className={cn(isFocused && "placeholder:text-text-dimmed/70")}
902+
onFocus={() => setIsFocused(true)}
903+
onBlur={() => setIsFocused(false)}
904+
onKeyDown={(e) => {
905+
if (e.key === "Escape") e.currentTarget.blur();
906+
}}
907+
icon={<MagnifyingGlassIcon className="size-4" />}
908+
accessory={
909+
value.length > 0 ? (
910+
<button
911+
type="button"
912+
onClick={() => onChange("")}
913+
className="flex size-4.5 items-center justify-center rounded-[2px] border border-text-dimmed/40 text-text-dimmed transition hover:bg-charcoal-600 hover:text-text-bright"
914+
>
915+
<XMarkIcon className="size-3" />
916+
</button>
917+
) : undefined
918+
}
919+
/>
920+
</motion.div>
921+
);
922+
}

0 commit comments

Comments
 (0)