Skip to content

Commit 6df5e78

Browse files
committed
feat: unified search param for filament modal (carry from PR846)
- Use single debounced ?search= call (mirrors spool API / PR846 approach) - Remove 4-field parallel fetch loop, getAPIURL import, allSearchResults state - API ?search= param was already present via 'Carry filament search foundation'
1 parent e81b670 commit 6df5e78

1 file changed

Lines changed: 24 additions & 44 deletions

File tree

client/src/pages/printing/filamentSelectModal.tsx

Lines changed: 24 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,6 @@ function collapseFilament(element: IFilament): IFilamentCollapsed {
2424
return { ...element, "vendor.name": element.vendor?.name ?? null };
2525
}
2626

27-
// Keep the quick search local to the currently loaded page instead of changing the server-side query contract.
28-
function matchesSearch(filament: IFilamentCollapsed, searchTerm: string): boolean {
29-
const needle = searchTerm.trim().toLowerCase();
30-
if (needle.length === 0) {
31-
return true;
32-
}
33-
34-
const haystacks = [String(filament.id), filament["vendor.name"] ?? "", filament.name ?? "", filament.material ?? ""];
35-
return haystacks.some((value) => value.toLowerCase().includes(needle));
36-
}
37-
3827
const MIN_TABLE_SCROLL_Y = 180;
3928
const TABLE_BOTTOM_GAP = 16;
4029

@@ -43,9 +32,9 @@ const FilamentSelectModal = ({ description, onPrint, searchPlaceholder }: Props)
4332
const [selectedItems, setSelectedItems] = useState<number[]>([]);
4433
const [messageApi, contextHolder] = message.useMessage();
4534
const navigate = useNavigate();
46-
const [searchValue, setSearchValue] = useState("");
47-
const [serverSearchValue, setServerSearchValue] = useState("");
4835
const [tableScrollY, setTableScrollY] = useState<number>(300);
36+
const [searchTerm, setSearchTerm] = useState("");
37+
const [debouncedSearch, setDebouncedSearch] = useState("");
4938
const rootRef = useRef<HTMLDivElement | null>(null);
5039
const tableContainerRef = useRef<HTMLDivElement | null>(null);
5140

@@ -54,7 +43,7 @@ const FilamentSelectModal = ({ description, onPrint, searchPlaceholder }: Props)
5443
resource: "filament",
5544
meta: {
5645
queryParams: {
57-
...(serverSearchValue.trim().length > 0 ? { search: serverSearchValue.trim() } : {}),
46+
...(debouncedSearch.length > 0 ? { search: debouncedSearch } : {}),
5847
},
5948
},
6049
syncWithLocation: false,
@@ -85,16 +74,9 @@ const FilamentSelectModal = ({ description, onPrint, searchPlaceholder }: Props)
8574
pagination: { currentPage, pageSize },
8675
};
8776

88-
const dataSource: IFilamentCollapsed[] = useMemo(
89-
() => (tableProps.dataSource || []).map((record) => ({ ...record })),
90-
[tableProps.dataSource],
91-
);
92-
// Keep typing responsive by narrowing the current page immediately even while the backend query is in flight.
93-
const visibleDataSource = useMemo(
94-
() => dataSource.filter((filament) => matchesSearch(filament, searchValue)),
95-
[dataSource, searchValue],
96-
);
77+
const dataSource = [...(tableProps.dataSource ?? [])];
9778
const selectedSet = useMemo(() => new Set(selectedItems), [selectedItems]);
79+
const paginationTotal = tableProps.pagination ? (tableProps.pagination.total ?? 0) : 0;
9880

9981
useEffect(() => {
10082
const computeScrollHeight = () => {
@@ -130,8 +112,6 @@ const FilamentSelectModal = ({ description, onPrint, searchPlaceholder }: Props)
130112
resizeObserver?.disconnect();
131113
};
132114
}, []);
133-
134-
const paginationTotal = tableProps.pagination ? (tableProps.pagination.total ?? 0) : 0;
135115
const handlePageChange = useCallback(
136116
(page: number, nextPageSize?: number) => {
137117
if (typeof nextPageSize === "number" && nextPageSize !== pageSize) {
@@ -145,17 +125,22 @@ const FilamentSelectModal = ({ description, onPrint, searchPlaceholder }: Props)
145125
setPageSize(size);
146126
setCurrentPage(1);
147127
}, []);
148-
const applySearchFilter = useCallback((nextSearch: string) => {
149-
setServerSearchValue(nextSearch.trim());
150-
setCurrentPage(1);
151-
}, []);
152128

153-
// Bulk toggles only touch the rows currently visible after search and paging.
129+
// Debounce search input to avoid excessive API calls while typing
130+
useEffect(() => {
131+
const timer = setTimeout(() => {
132+
setDebouncedSearch(searchTerm.trim());
133+
setCurrentPage(1);
134+
}, 300);
135+
return () => clearTimeout(timer);
136+
}, [searchTerm, setCurrentPage]);
137+
138+
// Bulk toggles only touch the rows currently visible after paging and server-side filtering.
154139
const selectUnselectFiltered = useCallback(
155140
(select: boolean) => {
156141
setSelectedItems((prevSelected) => {
157142
const nextSelected = new Set(prevSelected);
158-
visibleDataSource.forEach((filament) => {
143+
dataSource.forEach((filament) => {
159144
if (select) {
160145
nextSelected.add(filament.id);
161146
} else {
@@ -165,7 +150,7 @@ const FilamentSelectModal = ({ description, onPrint, searchPlaceholder }: Props)
165150
return Array.from(nextSelected);
166151
});
167152
},
168-
[visibleDataSource],
153+
[dataSource],
169154
);
170155

171156
const handleSelectItem = useCallback((item: number) => {
@@ -174,10 +159,9 @@ const FilamentSelectModal = ({ description, onPrint, searchPlaceholder }: Props)
174159
);
175160
}, []);
176161

177-
const isAllFilteredSelected =
178-
visibleDataSource.length > 0 && visibleDataSource.every((filament) => selectedSet.has(filament.id));
162+
const isAllFilteredSelected = dataSource.length > 0 && dataSource.every((filament) => selectedSet.has(filament.id));
179163
const isSomeButNotAllFilteredSelected =
180-
visibleDataSource.some((filament) => selectedSet.has(filament.id)) && !isAllFilteredSelected;
164+
dataSource.some((filament) => selectedSet.has(filament.id)) && !isAllFilteredSelected;
181165

182166
const commonProps = {
183167
t,
@@ -227,17 +211,14 @@ const FilamentSelectModal = ({ description, onPrint, searchPlaceholder }: Props)
227211
<Col xs={24} md={12}>
228212
<Input.Search
229213
placeholder={resolvedSearchPlaceholder}
230-
value={searchValue}
214+
value={searchTerm}
231215
allowClear
232216
enterButton
233217
onChange={(event) => {
234-
const value = event.target.value;
235-
setSearchValue(value);
236-
applySearchFilter(value);
218+
setSearchTerm(event.target.value);
237219
}}
238220
onSearch={(value) => {
239-
setSearchValue(value);
240-
applySearchFilter(value);
221+
setSearchTerm(value);
241222
}}
242223
/>
243224
</Col>
@@ -246,8 +227,7 @@ const FilamentSelectModal = ({ description, onPrint, searchPlaceholder }: Props)
246227
<Col flex="none">
247228
<Button
248229
onClick={() => {
249-
setSearchValue("");
250-
setServerSearchValue("");
230+
setSearchTerm("");
251231
setFilters([], "replace");
252232
setCurrentPage(1);
253233
}}
@@ -303,7 +283,7 @@ const FilamentSelectModal = ({ description, onPrint, searchPlaceholder }: Props)
303283
rowKey="id"
304284
tableLayout="fixed"
305285
pagination={false}
306-
dataSource={visibleDataSource}
286+
dataSource={dataSource}
307287
scroll={{ y: tableScrollY, x: "max-content" }}
308288
columns={removeUndefined([
309289
{

0 commit comments

Comments
 (0)