feat: add filter for active jobs in the dashboard (#601)
* feat: add filter for active jobs in the dashboard * use cursor-pointer when hovering Switch label * remove unused import * remove unnecessary span * add flex-gap
This commit is contained in:
@@ -15,7 +15,7 @@ const variations = {
|
||||
container: "flex items-center gap-x-1.5 rounded hover:bg-slate-850 pr-1 py-[0.1rem] pl-1.5",
|
||||
root: "h-3 w-6",
|
||||
thumb: "h-2.5 w-2.5 data-[state=checked]:translate-x-2.5 data-[state=unchecked]:translate-x-0",
|
||||
text: "text-xs text-slate-400 group-hover:text-slate-200 mt-0.5",
|
||||
text: "text-xs text-slate-400 group-hover:text-slate-200 hover:cursor-pointer",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,9 +1,21 @@
|
||||
import { ProjectJob } from "./useJobs";
|
||||
import { useTextFilter } from "./useTextFilter";
|
||||
import { useToggleFilter } from "./useToggleFilter";
|
||||
|
||||
export function useFilterJobs(jobs: ProjectJob[]) {
|
||||
const { filterText, setFilterText, filteredItems } = useTextFilter<ProjectJob>({
|
||||
export function useFilterJobs(jobs: ProjectJob[], onlyActiveJobs = false) {
|
||||
const toggleFilterRes = useToggleFilter<ProjectJob>({
|
||||
items: jobs,
|
||||
filter: (job, onlyActiveJobs) => {
|
||||
if (onlyActiveJobs && job.status !== "ACTIVE") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
defaultValue: onlyActiveJobs,
|
||||
});
|
||||
|
||||
const textFilterRes = useTextFilter<ProjectJob>({
|
||||
items: toggleFilterRes.filteredItems,
|
||||
filter: (job, text) => {
|
||||
if (job.slug.toLowerCase().includes(text.toLowerCase())) return true;
|
||||
if (job.title.toLowerCase().includes(text.toLowerCase())) return true;
|
||||
@@ -24,5 +36,11 @@ export function useFilterJobs(jobs: ProjectJob[]) {
|
||||
},
|
||||
});
|
||||
|
||||
return { filterText, setFilterText, filteredItems };
|
||||
return {
|
||||
filteredItems: textFilterRes.filteredItems,
|
||||
filterText: textFilterRes.filterText,
|
||||
setFilterText: textFilterRes.setFilterText,
|
||||
onlyActiveJobs: toggleFilterRes.isToggleActive,
|
||||
setOnlyActiveJobs: toggleFilterRes.setToggleActive,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { useMemo, useState } from "react";
|
||||
|
||||
type ToggleFilterProps<T> = {
|
||||
items: T[];
|
||||
filter: (item: T, isToggleActive: boolean) => boolean;
|
||||
defaultValue?: boolean;
|
||||
};
|
||||
|
||||
export function useToggleFilter<T>({ items, filter, defaultValue = false }: ToggleFilterProps<T>) {
|
||||
const [isToggleActive, setToggleActive] = useState(defaultValue);
|
||||
|
||||
const filteredItems = useMemo<T[]>(() => {
|
||||
return items.filter((item) => filter(item, isToggleActive));
|
||||
}, [items, isToggleActive]);
|
||||
|
||||
return {
|
||||
isToggleActive,
|
||||
setToggleActive,
|
||||
filteredItems,
|
||||
};
|
||||
}
|
||||
+16
-4
@@ -19,6 +19,7 @@ import {
|
||||
PageTitleRow,
|
||||
} from "~/components/primitives/PageHeader";
|
||||
import { Paragraph } from "~/components/primitives/Paragraph";
|
||||
import { Switch } from "~/components/primitives/Switch";
|
||||
import { TextLink } from "~/components/primitives/TextLink";
|
||||
import { useFilterJobs } from "~/hooks/useFilterJobs";
|
||||
import { useOrganization } from "~/hooks/useOrganizations";
|
||||
@@ -62,8 +63,11 @@ export default function Page() {
|
||||
const organization = useOrganization();
|
||||
const project = useProject();
|
||||
const { jobs } = useTypedLoaderData<typeof loader>();
|
||||
const { filterText, setFilterText, filteredItems } = useFilterJobs(jobs);
|
||||
const hasJobs = jobs.length > 0;
|
||||
const { filterText, setFilterText, filteredItems, onlyActiveJobs, setOnlyActiveJobs } =
|
||||
useFilterJobs(jobs);
|
||||
const totalJobs = jobs.length;
|
||||
const hasJobs = totalJobs > 0;
|
||||
const activeJobCount = jobs.filter((j) => j.status === "ACTIVE").length;
|
||||
|
||||
return (
|
||||
<PageContainer className={hasJobs ? "" : "grid-rows-1"}>
|
||||
@@ -74,7 +78,8 @@ export default function Page() {
|
||||
</PageTitleRow>
|
||||
<PageInfoRow>
|
||||
<PageInfoGroup>
|
||||
<PageInfoProperty icon={"job"} label={"Active Jobs"} value={jobs.length} />
|
||||
<PageInfoProperty icon={"job"} label={"All Jobs"} value={totalJobs} />
|
||||
<PageInfoProperty icon={"job"} label={"Active Jobs"} value={activeJobCount} />
|
||||
</PageInfoGroup>
|
||||
</PageInfoRow>
|
||||
</PageHeader>
|
||||
@@ -96,7 +101,7 @@ export default function Page() {
|
||||
</Callout>
|
||||
)}
|
||||
<div className="mb-2 flex flex-col">
|
||||
<div className="flex w-full">
|
||||
<div className="flex w-full gap-x-2">
|
||||
<Input
|
||||
placeholder="Search Jobs"
|
||||
variant="tertiary"
|
||||
@@ -106,6 +111,13 @@ export default function Page() {
|
||||
onChange={(e) => setFilterText(e.target.value)}
|
||||
autoFocus
|
||||
/>
|
||||
<Switch
|
||||
variant="small"
|
||||
label="Active Jobs"
|
||||
checked={onlyActiveJobs}
|
||||
onCheckedChange={setOnlyActiveJobs}
|
||||
className={"shrink-0"}
|
||||
/>
|
||||
<HelpTrigger title="Example Jobs and inspiration" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -152,7 +152,7 @@ function PossibleIntegrationsList({
|
||||
onCheckedChange={setOnlyShowIntegrations}
|
||||
variant="small"
|
||||
label={
|
||||
<span className="inline-flex items-center gap-1">
|
||||
<span className="mt-0.5 inline-flex items-center gap-1">
|
||||
<IntegrationIcon /> Trigger.dev Integrations
|
||||
</span>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user