feat(web): persist list-page filters in URL search params
Resources, projects, and allocations filter state now syncs to/from URL so filters survive refresh and can be shared via link. Text inputs are debounced (300ms) to avoid URL churn. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect, useMemo, useCallback, useRef } from "react";
|
||||
import { useUrlFilters } from "~/hooks/useUrlFilters.js";
|
||||
import { useLocalStorage } from "~/hooks/useLocalStorage.js";
|
||||
import { formatDate } from "~/lib/format.js";
|
||||
import { trpc } from "~/lib/trpc/client.js";
|
||||
@@ -106,12 +107,29 @@ function getAllocationQueryFailure(error: AllocationQueryError) {
|
||||
export function AllocationsClient() {
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const [editingAllocation, setEditingAllocation] = useState<AllocationWithDetails | null>(null);
|
||||
const [filterProjectId, setFilterProjectId] = useState<string>("");
|
||||
const [filterResourceId, setFilterResourceId] = useState<string>("");
|
||||
const [filterStatus, setFilterStatus] = useState<string>("");
|
||||
const [hidePastProjects, setHidePastProjects] = useState(true);
|
||||
const [hideCompletedProjects, setHideCompletedProjects] = useState(true);
|
||||
const [hideDraftProjects, setHideDraftProjects] = useState(false);
|
||||
|
||||
const [allocFilters, setAllocFilters] = useUrlFilters({
|
||||
projectId: "",
|
||||
resourceId: "",
|
||||
status: "",
|
||||
hidePast: "true",
|
||||
hideCompleted: "true",
|
||||
hideDraft: "false",
|
||||
});
|
||||
|
||||
const filterProjectId = allocFilters.projectId;
|
||||
const filterResourceId = allocFilters.resourceId;
|
||||
const filterStatus = allocFilters.status;
|
||||
const hidePastProjects = allocFilters.hidePast === "true";
|
||||
const hideCompletedProjects = allocFilters.hideCompleted === "true";
|
||||
const hideDraftProjects = allocFilters.hideDraft === "true";
|
||||
|
||||
const setFilterProjectId = useCallback((v: string) => setAllocFilters({ projectId: v }), [setAllocFilters]);
|
||||
const setFilterResourceId = useCallback((v: string) => setAllocFilters({ resourceId: v }), [setAllocFilters]);
|
||||
const setFilterStatus = useCallback((v: string) => setAllocFilters({ status: v }), [setAllocFilters]);
|
||||
const setHidePastProjects = useCallback((v: boolean) => setAllocFilters({ hidePast: v ? "true" : "false" }), [setAllocFilters]);
|
||||
const setHideCompletedProjects = useCallback((v: boolean) => setAllocFilters({ hideCompleted: v ? "true" : "false" }), [setAllocFilters]);
|
||||
const setHideDraftProjects = useCallback((v: boolean) => setAllocFilters({ hideDraft: v ? "true" : "false" }), [setAllocFilters]);
|
||||
const [confirmDelete, setConfirmDelete] = useState<{ single?: AllocationWithDetails; ids?: string[] } | null>(null);
|
||||
const [batchStatusPicker, setBatchStatusPicker] = useState(false);
|
||||
const [confirmBatchStatus, setConfirmBatchStatus] = useState<{ ids: string[]; status: string } | null>(null);
|
||||
@@ -405,12 +423,7 @@ export function AllocationsClient() {
|
||||
}
|
||||
|
||||
function clearAll() {
|
||||
setFilterProjectId("");
|
||||
setFilterResourceId("");
|
||||
setFilterStatus("");
|
||||
setHidePastProjects(false);
|
||||
setHideCompletedProjects(false);
|
||||
setHideDraftProjects(false);
|
||||
setAllocFilters({ projectId: "", resourceId: "", status: "", hidePast: "false", hideCompleted: "false", hideDraft: "false" });
|
||||
}
|
||||
|
||||
const chips = [
|
||||
@@ -450,9 +463,7 @@ export function AllocationsClient() {
|
||||
hideDraftProjects,
|
||||
})
|
||||
) {
|
||||
setHidePastProjects(false);
|
||||
setHideCompletedProjects(false);
|
||||
setHideDraftProjects(false);
|
||||
setAllocFilters({ hidePast: "false", hideCompleted: "false", hideDraft: "false" });
|
||||
}
|
||||
}, [
|
||||
assignmentList.length,
|
||||
|
||||
Reference in New Issue
Block a user