Skip to content

Commit a9a42d2

Browse files
authored
Merge pull request #3121 from TonyStef/feat/project-search-url-params
feat: persist search query in URL parameters on projects page
2 parents d2be085 + 0f6ac31 commit a9a42d2

File tree

1 file changed

+40
-5
lines changed
  • apps/dokploy/components/dashboard/projects

1 file changed

+40
-5
lines changed

apps/dokploy/components/dashboard/projects/show.tsx

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
TrashIcon,
1111
} from "lucide-react";
1212
import Link from "next/link";
13+
import { useRouter } from "next/router";
1314
import { useEffect, useMemo, useState } from "react";
1415
import { toast } from "sonner";
1516
import { BreadcrumbSidebar } from "@/components/shared/breadcrumb-sidebar";
@@ -54,16 +55,23 @@ import {
5455
} from "@/components/ui/select";
5556
import { TimeBadge } from "@/components/ui/time-badge";
5657
import { api } from "@/utils/api";
58+
import { useDebounce } from "@/utils/hooks/use-debounce";
5759
import { HandleProject } from "./handle-project";
5860
import { ProjectEnvironment } from "./project-environment";
5961

6062
export const ShowProjects = () => {
6163
const utils = api.useUtils();
64+
const router = useRouter();
6265
const { data: isCloud } = api.settings.isCloud.useQuery();
6366
const { data, isLoading } = api.project.all.useQuery();
6467
const { data: auth } = api.user.get.useQuery();
6568
const { mutateAsync } = api.project.remove.useMutation();
66-
const [searchQuery, setSearchQuery] = useState("");
69+
70+
const [searchQuery, setSearchQuery] = useState(
71+
router.isReady && typeof router.query.q === "string" ? router.query.q : "",
72+
);
73+
const debouncedSearchQuery = useDebounce(searchQuery, 500);
74+
6775
const [sortBy, setSortBy] = useState<string>(() => {
6876
if (typeof window !== "undefined") {
6977
return localStorage.getItem("projectsSort") || "createdAt-desc";
@@ -75,14 +83,41 @@ export const ShowProjects = () => {
7583
localStorage.setItem("projectsSort", sortBy);
7684
}, [sortBy]);
7785

86+
useEffect(() => {
87+
if (!router.isReady) return;
88+
const urlQuery = typeof router.query.q === "string" ? router.query.q : "";
89+
if (urlQuery !== searchQuery) {
90+
setSearchQuery(urlQuery);
91+
}
92+
}, [router.isReady, router.query.q]);
93+
94+
useEffect(() => {
95+
if (!router.isReady) return;
96+
const urlQuery = typeof router.query.q === "string" ? router.query.q : "";
97+
if (debouncedSearchQuery === urlQuery) return;
98+
99+
const newQuery = { ...router.query };
100+
if (debouncedSearchQuery) {
101+
newQuery.q = debouncedSearchQuery;
102+
} else {
103+
delete newQuery.q;
104+
}
105+
router.replace({ pathname: router.pathname, query: newQuery }, undefined, {
106+
shallow: true,
107+
});
108+
}, [debouncedSearchQuery]);
109+
78110
const filteredProjects = useMemo(() => {
79111
if (!data) return [];
80112

81-
// First filter by search query
82113
const filtered = data.filter(
83114
(project) =>
84-
project.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
85-
project.description?.toLowerCase().includes(searchQuery.toLowerCase()),
115+
project.name
116+
.toLowerCase()
117+
.includes(debouncedSearchQuery.toLowerCase()) ||
118+
project.description
119+
?.toLowerCase()
120+
.includes(debouncedSearchQuery.toLowerCase()),
86121
);
87122

88123
// Then sort the filtered results
@@ -130,7 +165,7 @@ export const ShowProjects = () => {
130165
}
131166
return direction === "asc" ? comparison : -comparison;
132167
});
133-
}, [data, searchQuery, sortBy]);
168+
}, [data, debouncedSearchQuery, sortBy]);
134169

135170
return (
136171
<>

0 commit comments

Comments
 (0)