new product admin page to flag bad products
This commit is contained in:
@@ -92,10 +92,10 @@ const CATEGORY_GROUPS: {
|
||||
categoryIds: [
|
||||
"lower-receiver",
|
||||
"complete-lower",
|
||||
"lower-parts-kit", // ✅ was lower-parts
|
||||
"lower-parts-kit",
|
||||
"trigger",
|
||||
"grip",
|
||||
"safety-selector", // ✅ was safety
|
||||
"safety-selector",
|
||||
"buffer",
|
||||
"stock",
|
||||
],
|
||||
@@ -192,6 +192,21 @@ export default function GunbuilderPage() {
|
||||
return {};
|
||||
});
|
||||
|
||||
type PageResponse<T> = {
|
||||
content: T[];
|
||||
totalElements?: number;
|
||||
totalPages?: number;
|
||||
number?: number;
|
||||
size?: number;
|
||||
};
|
||||
|
||||
function unwrapPage<T>(json: any): T[] {
|
||||
if (!json) return [];
|
||||
if (Array.isArray(json)) return json;
|
||||
if (Array.isArray(json.content)) return json.content;
|
||||
return [];
|
||||
}
|
||||
|
||||
// -----------------------------
|
||||
// Toast notifications (save success / errors)
|
||||
// -----------------------------
|
||||
@@ -374,10 +389,11 @@ export default function GunbuilderPage() {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
const size = 2000; // temporary "big bucket" until we optimize
|
||||
const scopedUrl = `${API_BASE_URL}/api/v1/products?platform=${encodeURIComponent(
|
||||
platform
|
||||
)}`;
|
||||
const universalUrl = `${API_BASE_URL}/api/v1/products`;
|
||||
)}&page=0&size=${size}`;
|
||||
const universalUrl = `${API_BASE_URL}/api/v1/products?page=0&size=${size}`;
|
||||
|
||||
const [scopedRes, universalRes] = await Promise.all([
|
||||
fetch(scopedUrl, { signal: controller.signal }),
|
||||
@@ -391,13 +407,15 @@ export default function GunbuilderPage() {
|
||||
throw new Error(`Failed to load products (${status})`);
|
||||
}
|
||||
|
||||
const scopedData: GunbuilderProductFromApi[] = await scopedRes.json();
|
||||
const scopedJson = await scopedRes.json();
|
||||
const scopedData: GunbuilderProductFromApi[] =
|
||||
unwrapPage<GunbuilderProductFromApi>(scopedJson);
|
||||
|
||||
let universalData: GunbuilderProductFromApi[] = [];
|
||||
if (universalRes && universalRes.ok) {
|
||||
universalData = await universalRes.json();
|
||||
const universalJson = await universalRes.json();
|
||||
universalData = unwrapPage<GunbuilderProductFromApi>(universalJson);
|
||||
}
|
||||
|
||||
const normalize = (data: GunbuilderProductFromApi[]): Part[] =>
|
||||
data
|
||||
.map((p): Part | null => {
|
||||
@@ -649,31 +667,30 @@ export default function GunbuilderPage() {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// -----------------------------
|
||||
// Selection handler (with hint warnings)
|
||||
// -----------------------------
|
||||
const handleSelectPart = useCallback(
|
||||
(
|
||||
categoryId: BuilderSlotKey,
|
||||
partId: string,
|
||||
_opts?: { confirm?: boolean }
|
||||
) => {
|
||||
const warn = (msg: string) => {
|
||||
setShareStatus(msg);
|
||||
window.setTimeout(() => setShareStatus(null), 4000);
|
||||
};
|
||||
// Selection handler (with hint warnings)
|
||||
// -----------------------------
|
||||
const handleSelectPart = useCallback(
|
||||
(
|
||||
categoryId: BuilderSlotKey,
|
||||
partId: string,
|
||||
_opts?: { confirm?: boolean }
|
||||
) => {
|
||||
const warn = (msg: string) => {
|
||||
setShareStatus(msg);
|
||||
window.setTimeout(() => setShareStatus(null), 4000);
|
||||
};
|
||||
|
||||
const hints = getSelectionHints(categoryId, build);
|
||||
if (hints.length > 0) warn(hints[0]);
|
||||
const hints = getSelectionHints(categoryId, build);
|
||||
if (hints.length > 0) warn(hints[0]);
|
||||
|
||||
setBuild((prev) => ({
|
||||
...prev,
|
||||
[categoryId]: partId,
|
||||
}));
|
||||
},
|
||||
[build]
|
||||
);
|
||||
setBuild((prev) => ({
|
||||
...prev,
|
||||
[categoryId]: partId,
|
||||
}));
|
||||
},
|
||||
[build]
|
||||
);
|
||||
// -----------------------------
|
||||
// Handle URL query parameters:
|
||||
// - ?select=categoryId:partId
|
||||
|
||||
Reference in New Issue
Block a user