forked from syntaxbullet/aurorabot
feat: implement basic items page, with a placeholder for item creation tool.
This commit is contained in:
99
panel/src/lib/useItems.ts
Normal file
99
panel/src/lib/useItems.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { get } from "./api";
|
||||
|
||||
export interface Item {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string | null;
|
||||
type: string;
|
||||
rarity: string;
|
||||
price: string | null;
|
||||
iconUrl: string;
|
||||
imageUrl: string;
|
||||
}
|
||||
|
||||
export interface ItemFilters {
|
||||
search: string;
|
||||
type: string | null;
|
||||
rarity: string | null;
|
||||
}
|
||||
|
||||
export function useItems() {
|
||||
const [items, setItems] = useState<Item[]>([]);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [limit, setLimit] = useState(50);
|
||||
const [filters, setFiltersState] = useState<ItemFilters>({
|
||||
search: "",
|
||||
type: null,
|
||||
rarity: null,
|
||||
});
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetchItems = useCallback(async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
const params = new URLSearchParams();
|
||||
if (filters.search) params.set("search", filters.search);
|
||||
if (filters.type) params.set("type", filters.type);
|
||||
if (filters.rarity) params.set("rarity", filters.rarity);
|
||||
params.set("limit", String(limit));
|
||||
params.set("offset", String((currentPage - 1) * limit));
|
||||
|
||||
const data = await get<{ items: Item[]; total: number }>(
|
||||
`/api/items?${params.toString()}`
|
||||
);
|
||||
|
||||
setItems(data.items);
|
||||
setTotal(data.total);
|
||||
} catch (e) {
|
||||
setError(e instanceof Error ? e.message : "Failed to load items");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [filters, currentPage, limit]);
|
||||
|
||||
const setFilters = useCallback((newFilters: Partial<ItemFilters>) => {
|
||||
setFiltersState((prev) => ({ ...prev, ...newFilters }));
|
||||
setCurrentPage(1);
|
||||
}, []);
|
||||
|
||||
const setSearchDebounced = useCallback(
|
||||
(() => {
|
||||
let timeoutId: NodeJS.Timeout;
|
||||
return (search: string) => {
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = setTimeout(() => {
|
||||
setFilters({ search });
|
||||
}, 300);
|
||||
};
|
||||
})(),
|
||||
[setFilters]
|
||||
);
|
||||
|
||||
const setPage = useCallback((page: number) => {
|
||||
setCurrentPage(page);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetchItems();
|
||||
}, [fetchItems]);
|
||||
|
||||
return {
|
||||
items,
|
||||
total,
|
||||
currentPage,
|
||||
limit,
|
||||
setLimit,
|
||||
filters,
|
||||
setFilters,
|
||||
setSearchDebounced,
|
||||
setPage,
|
||||
loading,
|
||||
error,
|
||||
refetch: fetchItems,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user