import { json } from "@remix-run/node"; import { useLoaderData, useFetcher } from "@remix-run/react"; import { Page, Layout, Card, TextField, Checkbox, Button, Thumbnail, Spinner, Toast, Frame, } from "@shopify/polaris"; import { useEffect, useState } from "react"; import { TitleBar } from "@shopify/app-bridge-react"; import { getTurn14AccessTokenFromMetafield } from "../../utils/turn14Token.server"; import { authenticate } from "../../shopify.server"; export const loader = async ({ request }) => { const accessToken = await getTurn14AccessTokenFromMetafield(request); const { admin } = await authenticate.admin(request); // Get brands const brandRes = await fetch("https://turn14.data4autos.com/v1/brands", { headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json", }, }); const brandJson = await brandRes.json(); if (!brandRes.ok) { return json({ error: brandJson.error || "Failed to fetch brands" }, { status: 500 }); } // Get collections const gqlRaw = await admin.graphql(` { collections(first: 100) { edges { node { id title handle } } } } `); const gql = await gqlRaw.json(); const collections = gql?.data?.collections?.edges?.map((e) => e.node) || []; return json({ brands: brandJson.data, collections, }); }; export const action = async ({ request }) => { const formData = await request.formData(); const selectedBrands = JSON.parse(formData.get("selectedBrands") || "[]"); // get the shop domain from the headers (as you mentioned) const shop = request.headers.get("shop-domain") || ""; // make the POST to your backend const resp = await fetch("https://backend.dine360.ca/managebrands", { method: "POST", headers: { "Content-Type": "application/json", "shop-domain": shop, }, body: JSON.stringify({ shop, selectedBrands }), }); console.log("Request to Home:", { shop, selectedBrands }); console.log("Request headers:", { "shop-domain": shop }); console.log("Request body:", { selectedBrands }); console.log("Response status:", resp.status); console.log("Response headers:", resp.headers); console.log("Response URL:", resp.url); console.log("Response status text:", resp.statusText); console.log("Response ok:", resp.ok); console.log("Response type:", resp.type); console.log("Response redirected:", resp.redirected); console.log("Response from backend:", resp); if (!resp.ok) { const err = await resp.text(); return json({ error: err }, { status: resp.status }); } const { processId, status } = await resp.json(); // return the processId (and initial status if you like) to the client return json({ processId, status }); }; export default function BrandsPage() { const actionData = useActionData(); const [status, setStatus] = useState(actionData?.status || ""); const [polling, setPolling] = useState(false); // the processId returned from the action const processId = actionData?.processId; async function checkStatus() { if (!processId) return; setPolling(true); const resp = await fetch( `https://backend.dine360.ca/managebrands/status/${processId}`, { headers: { "shop-domain": window.shopify.shop || "" }, } ); const json = await resp.json(); setStatus(json.status + (json.detail ? ` (${json.detail})` : "")); setPolling(false); } const { brands, collections } = useLoaderData(); const fetcher = useFetcher(); const isSubmitting = fetcher.state === "submitting"; const [toastActive, setToastActive] = useState(false); const [search, setSearch] = useState(""); const collectionTitles = new Set(collections.map((c) => c.title.toLowerCase())); const defaultSelected = brands .filter((b) => collectionTitles.has(b.name.toLowerCase())) .map((b) => b.id); const [selectedIds, setSelectedIds] = useState(defaultSelected); const [filteredBrands, setFilteredBrands] = useState(brands); useEffect(() => { const term = search.toLowerCase(); setFilteredBrands(brands.filter((b) => b.name.toLowerCase().includes(term))); }, [search, brands]); useEffect(() => { if (fetcher.data?.success) { setToastActive(true); } }, [fetcher.data]); const toggleSelect = (id) => { setSelectedIds((prev) => prev.includes(id) ? prev.filter((i) => i !== id) : [...prev, id] ); }; const toggleSelectAll = () => { const filteredBrandIds = filteredBrands.map(b => b.id); const allFilteredSelected = filteredBrandIds.every(id => selectedIds.includes(id)); if (allFilteredSelected) { // Deselect all filtered brands setSelectedIds(prev => prev.filter(id => !filteredBrandIds.includes(id))); } else { // Select all filtered brands setSelectedIds(prev => { const combined = new Set([...prev, ...filteredBrandIds]); return Array.from(combined); }); } }; const toastMarkup = toastActive ? ( setToastActive(false)} /> ) : null; const selectedBrands = brands.filter((b) => selectedIds.includes(b.id)); const allFilteredSelected = filteredBrands.length > 0 && filteredBrands.every(brand => selectedIds.includes(brand.id)); return (
{filteredBrands.map((brand) => (
toggleSelect(brand.id)} />
{brand.name}
))} {processId && (

Process ID: {processId}

Status: {status || "—"}

)}
{toastMarkup}
); }