fixed build issues. its builds now

This commit is contained in:
2026-01-19 09:11:42 -05:00
parent b0db3167c5
commit 775a351425
17 changed files with 64 additions and 37 deletions
@@ -4,7 +4,7 @@ import { useEffect, useMemo, useState } from "react";
import Link from "next/link"; import Link from "next/link";
import { useParams, useRouter, useSearchParams } from "next/navigation"; import { useParams, useRouter, useSearchParams } from "next/navigation";
import type { CategoryId } from "@/types/builderSlots"; import type { BuilderSlotKey } from "@/types/builderSlots";
import { PART_ROLE_TO_CATEGORY, normalizePartRole } from "@/lib/catalogMappings"; import { PART_ROLE_TO_CATEGORY, normalizePartRole } from "@/lib/catalogMappings";
/** /**
@@ -46,7 +46,7 @@ type GunbuilderProductFromApi = {
offers?: OfferFromApi[] | null; offers?: OfferFromApi[] | null;
}; };
type BuildState = Partial<Record<CategoryId, string>>; type BuildState = Partial<Record<BuilderSlotKey, string>>;
const API_BASE_URL = const API_BASE_URL =
process.env.NEXT_PUBLIC_API_BASE_URL ?? "http://localhost:8080"; process.env.NEXT_PUBLIC_API_BASE_URL ?? "http://localhost:8080";
+5 -5
View File
@@ -1,6 +1,6 @@
"use client"; "use client";
import { useEffect, useMemo, useState } from "react"; import { useCallback, useEffect, useMemo, useState } from "react";
import { useAuth } from "@/context/AuthContext"; import { useAuth } from "@/context/AuthContext";
import { import {
fetchEmailRequests, fetchEmailRequests,
@@ -32,13 +32,13 @@ export default function AdminEmailPage() {
const [statusTab, setStatusTab] = useState<EmailStatusTab>("ALL"); const [statusTab, setStatusTab] = useState<EmailStatusTab>("ALL");
const loadEmails = async () => { const loadEmails = useCallback(async () => {
if (!token) return; if (!token) return;
try { try {
setLoading(true); setLoading(true);
const data = await fetchEmailRequests(token); const data = await fetchEmailRequests(token);
setEmails(data.data); setEmails(data);
setError(null); setError(null);
} catch (e: any) { } catch (e: any) {
console.error(e); console.error(e);
@@ -46,11 +46,11 @@ export default function AdminEmailPage() {
} finally { } finally {
setLoading(false); setLoading(false);
} }
}; }, [token]);
useEffect(() => { useEffect(() => {
loadEmails(); loadEmails();
}, [token]); }, [loadEmails]);
const normalizeStatus = (s: unknown) => String(s ?? "").trim().toUpperCase(); const normalizeStatus = (s: unknown) => String(s ?? "").trim().toUpperCase();
+6 -6
View File
@@ -1,6 +1,6 @@
"use client"; "use client";
import { useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { useAuth } from "@/context/AuthContext"; import { useAuth } from "@/context/AuthContext";
import { import {
fetchEmailRequests, fetchEmailRequests,
@@ -27,13 +27,13 @@ export default function AdminEmailPage() {
}); });
const [submitting, setSubmitting] = useState(false); const [submitting, setSubmitting] = useState(false);
const loadEmails = async () => { const loadEmails = useCallback(async () => {
if (!token) return; if (!token) return;
try { try {
setLoading(true); setLoading(true);
const data = await fetchEmailRequests(token); const data = await fetchEmailRequests(token);
setEmails(data.data); setEmails(data);
setError(null); setError(null);
} catch (e: any) { } catch (e: any) {
console.error(e); console.error(e);
@@ -41,11 +41,11 @@ export default function AdminEmailPage() {
} finally { } finally {
setLoading(false); setLoading(false);
} }
}; }, [token]);
useEffect(() => { useEffect(() => {
loadEmails(); loadEmails();
}, [token]); }, [loadEmails]);
const handleCreate = () => { const handleCreate = () => {
setModalMode("create"); setModalMode("create");
+1 -1
View File
@@ -1,7 +1,7 @@
"use client"; "use client";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { sendEmailAction, type ApiResponse, type EmailRequest } from "/app/actions/sendEmail"; import { sendEmailAction, type ApiResponse, type EmailRequest } from "@/app/actions/sendEmail";
import { Button, Field, Input, Textarea } from "@/components/ui/form"; import { Button, Field, Input, Textarea } from "@/components/ui/form";
export default function SendEmailForm(): JSX.Element { export default function SendEmailForm(): JSX.Element {
@@ -1,6 +1,6 @@
"use client"; "use client";
import { useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { useApi } from "@/lib/useApi"; import { useApi } from "@/lib/useApi";
type Merchant = { type Merchant = {
@@ -91,7 +91,7 @@ export default function MerchantCategoryMappingPage() {
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
// 2) Load mappings when merchant changes (THIS sends merchantId) // 2) Load mappings when merchant changes (THIS sends merchantId)
useEffect(() => { const loadMappings = useCallback(() => {
if (selectedMerchantId == null) return; if (selectedMerchantId == null) return;
setLoadingMappings(true); setLoadingMappings(true);
@@ -112,7 +112,11 @@ export default function MerchantCategoryMappingPage() {
); );
}) })
.finally(() => setLoadingMappings(false)); .finally(() => setLoadingMappings(false));
}, [selectedMerchantId]); }, [selectedMerchantId, get]);
useEffect(() => {
loadMappings();
}, [loadMappings]);
// 3) Save a single mapping row (adjust endpoint if yours differs) // 3) Save a single mapping row (adjust endpoint if yours differs)
const handleSaveMapping = async ( const handleSaveMapping = async (
+5 -5
View File
@@ -1,6 +1,6 @@
"use client"; "use client";
import { useMemo, useState } from "react"; import { useCallback, useMemo, useState } from "react";
import type { CaliberDto, PlatformDto } from "../_lib/types"; import type { CaliberDto, PlatformDto } from "../_lib/types";
import { RightDrawer } from "./RightDrawer"; import { RightDrawer } from "./RightDrawer";
@@ -91,12 +91,10 @@ export function BulkBar(props: {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
if (selectedCount <= 0) return null;
const disabled = loading || selectedCount <= 0; const disabled = loading || selectedCount <= 0;
// one button to apply staged field updates // one button to apply staged field updates
const applyFieldChanges = () => { const applyFieldChanges = useCallback(() => {
const patch: BulkUpdateSet = { classificationReason: "Admin bulk override" }; const patch: BulkUpdateSet = { classificationReason: "Admin bulk override" };
if (bulkPlatformKey) { if (bulkPlatformKey) {
@@ -121,7 +119,9 @@ export function BulkBar(props: {
if (keys.length === 0) return; if (keys.length === 0) return;
runBulk(patch); runBulk(patch);
}; }, [bulkPlatformKey, bulkPlatformLock, bulkRole, bulkRoleLock, bulkCaliber, bulkCaliberGroup, bulkCaliberLock, bulkForceCaliber, runBulk]);
if (selectedCount <= 0) return null;
let footer: JSX.Element; let footer: JSX.Element;
// eslint-disable-next-line react-hooks/rules-of-hooks // eslint-disable-next-line react-hooks/rules-of-hooks
+1
View File
@@ -45,6 +45,7 @@ export default function BetaMagicPage() {
// We can simulate "login" by directly setting localStorage like AuthContext does, // We can simulate "login" by directly setting localStorage like AuthContext does,
// but easiest is just to store it here in the same shape. // but easiest is just to store it here in the same shape.
setSession(data.token ?? data.accessToken, { setSession(data.token ?? data.accessToken, {
uuid: data.uuid ?? data.id,
email: data.email, email: data.email,
displayName: data.displayName ?? null, displayName: data.displayName ?? null,
role: data.role ?? "USER", role: data.role ?? "USER",
+4 -1
View File
@@ -1,6 +1,9 @@
"use client"; "use client";
import type { PriceHistoryPoint } from "@/app/(app)/(builder)/parts/__DELETE[partRole]/_old_prod_details/data"; type PriceHistoryPoint = {
date: string;
price: number;
};
interface PricingHistoryGraphProps { interface PricingHistoryGraphProps {
data: PriceHistoryPoint[]; data: PriceHistoryPoint[];
+10 -2
View File
@@ -1,6 +1,14 @@
"use client"; "use client";
import type { RetailerOffer } from "@/app/(app)/(builder)/parts/__DELETE[partRole]/_old_prod_details/data"; type RetailerOffer = {
id?: string | number;
retailer: string;
price: number;
originalPrice?: number;
url: string;
affiliateUrl?: string;
inStock: boolean;
};
interface RetailersListProps { interface RetailersListProps {
retailers: RetailerOffer[]; retailers: RetailerOffer[];
@@ -46,7 +54,7 @@ export function RetailersList({ retailers }: RetailersListProps) {
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-1"> <div className="flex items-center gap-2 mb-1">
<span className="text-sm font-semibold text-zinc-50"> <span className="text-sm font-semibold text-zinc-50">
{retailer.retailerName} {retailer.retailer}
</span> </span>
{isLowestPrice && retailer.inStock && ( {isLowestPrice && retailer.inStock && (
<span className="text-[0.65rem] font-semibold uppercase tracking-wide px-1.5 py-0.5 rounded bg-amber-400/20 text-amber-300 border border-amber-400/30"> <span className="text-[0.65rem] font-semibold uppercase tracking-wide px-1.5 py-0.5 rounded bg-amber-400/20 text-amber-300 border border-amber-400/30">
+1 -1
View File
@@ -1,7 +1,7 @@
"use client"; "use client";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { sendEmailAction, type ApiResponse, type EmailRequest } from "/app/actions/sendEmail"; import { sendEmailAction, type ApiResponse, type EmailRequest } from "@/app/actions/sendEmail";
import { Button, Field, Input, Textarea } from "@/components/ui/form"; import { Button, Field, Input, Textarea } from "@/components/ui/form";
export default function SendEmailForm(): JSX.Element { export default function SendEmailForm(): JSX.Element {
+1 -1
View File
@@ -1,7 +1,7 @@
"use client"; "use client";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { sendEmailAction, type ApiResponse, type EmailRequest } from "/app/actions/sendEmail"; import { sendEmailAction, type ApiResponse, type EmailRequest } from "@/app/actions/sendEmail";
import { Button, Field, Input } from "@/components/ui/form"; import { Button, Field, Input } from "@/components/ui/form";
import RichTextEditor from "@/components/ui/RichTextEditor"; import RichTextEditor from "@/components/ui/RichTextEditor";
+7 -2
View File
@@ -1,11 +1,16 @@
"use client"; "use client";
import QuillImageResize from "quill-image-resize-module";
Quill.register("modules/imageResize", QuillImageResize);
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
import type React from "react"; import type React from "react";
import "react-quill/dist/quill.snow.css"; import "react-quill/dist/quill.snow.css";
// Import Quill modules after React Quill is loaded
if (typeof window !== 'undefined') {
const Quill = require('quill');
const QuillImageResize = require('quill-image-resize-module').default;
Quill.register("modules/imageResize", QuillImageResize);
}
const ReactQuill = dynamic(() => import("react-quill"), { ssr: false }); const ReactQuill = dynamic(() => import("react-quill"), { ssr: false });
type RichTextEditorProps = { type RichTextEditorProps = {
+1 -1
View File
@@ -1,7 +1,7 @@
"use client"; "use client";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { sendEmailAction, type ApiResponse, type EmailRequest } from "/app/actions/sendEmail"; import { sendEmailAction, type ApiResponse, type EmailRequest } from "@/app/actions/sendEmail";
import { Button, Field, Input } from "@/components/ui/form"; import { Button, Field, Input } from "@/components/ui/form";
import RichTextEditor from "@/components/ui/RichTextEditor"; import RichTextEditor from "@/components/ui/RichTextEditor";
+5 -3
View File
@@ -1,5 +1,7 @@
// /lib/buildOverlaps.ts // /lib/buildOverlaps.ts
import type { CategoryId } from "@/types/builderSlots"; import type { BuilderSlotKey } from "@/types/builderSlots";
type CategoryId = BuilderSlotKey;
export type OverlapChipModel = { export type OverlapChipModel = {
key: string; key: string;
@@ -25,10 +27,10 @@ export const UPPER_OVERLAP_CATEGORIES = new Set<CategoryId>([
export const LOWER_OVERLAP_CATEGORIES = new Set<CategoryId>([ export const LOWER_OVERLAP_CATEGORIES = new Set<CategoryId>([
"lower-receiver", "lower-receiver",
"lower-parts", "lower-parts-kit",
"trigger", "trigger",
"grip", "grip",
"safety", "safety-selector",
"buffer", "buffer",
"stock", "stock",
]); ]);
+1 -1
View File
@@ -41,7 +41,7 @@ export const CATEGORY_TO_PART_ROLES: Partial<Record<BuilderSlotKey, string[]>> =
"lower-parts-kit": ["lower-parts-kit", "lower-parts"], "lower-parts-kit": ["lower-parts-kit", "lower-parts"],
trigger: ["trigger", "trigger-kit"], trigger: ["trigger", "trigger-kit"],
grip: ["pistol-grip", "grip"], grip: ["pistol-grip", "grip"],
safety: ["safety", "safety-selector"], "safety-selector": ["safety", "safety-selector"],
buffer: ["buffer-kit", "buffer"], buffer: ["buffer-kit", "buffer"],
stock: ["stock"], stock: ["stock"],
+3 -3
View File
@@ -2,15 +2,15 @@
set -e set -e
REGISTRY="gitea.gofwd.group" REGISTRY="gitea.gofwd.group"
OWNER="sean/shadow-gunbuilder-ai-proto" OWNER="forward_group/ballistic-builder-spring"
IMAGE="battlbuilder" IMAGE="spring-api"
TAG=$(git rev-parse --short HEAD) TAG=$(git rev-parse --short HEAD)
FULL_IMAGE="$REGISTRY/$OWNER/$IMAGE" FULL_IMAGE="$REGISTRY/$OWNER/$IMAGE"
echo "Building $FULL_IMAGE:$TAG" echo "Building $FULL_IMAGE:$TAG"
docker build -f frontend/Dockerfile -t $FULL_IMAGE:$TAG . docker build -f docker/backend/Dockerfile -t $FULL_IMAGE:$TAG .
echo "Tagging latest" echo "Tagging latest"
docker tag $FULL_IMAGE:$TAG $FULL_IMAGE:latest docker tag $FULL_IMAGE:$TAG $FULL_IMAGE:latest
+4
View File
@@ -0,0 +1,4 @@
declare module 'quill-image-resize-module' {
const QuillImageResize: any;
export default QuillImageResize;
}