import { json } from "@remix-run/node"; import { useLoaderData, Form, useActionData } from "@remix-run/react"; import { Page, Layout, Card, TextField, Checkbox, Button, Thumbnail, Spinner, Toast, Frame, Text, } 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"; async function checkShopExists(shop) { try { const resp = await fetch( `https://backend.data4autos.com/checkisshopdataexists/${shop}` ); const data = await resp.json(); return data.status === 1; // βœ… true if shop exists, false otherwise } catch (err) { console.error("Error checking shop:", err); return false; // default to false if error } } // export const loader = async ({ request }) => { // // const accessToken = await getTurn14AccessTokenFromMetafield(request); // const { admin } = await authenticate.admin(request); // const { session } = await authenticate.admin(request); // const shop = session.shop; // var accessToken = "" // try { // accessToken = await getTurn14AccessTokenFromMetafield(request); // } catch (err) { // return json({ brands: [], collections: [], selectedBrandsFromShopify: [], shop, err }); // console.error("Error getting Turn14 access token:", err); // // Proceeding with empty accessToken // } // // fetch 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 }); // } // // fetch Shopify collections // const gqlRaw = await admin.graphql(` // { // collections(first: 100) { // edges { // node { // id // title // } // } // } // } // `); // const gql = await gqlRaw.json(); // const collections = gql?.data?.collections?.edges.map(e => e.node) || []; // const res = await admin.graphql(`{ // shop { // metafield(namespace: "turn14", key: "selected_brands") { // value // } // } // }`); // const data = await res.json(); // const rawValue = data?.data?.shop?.metafield?.value; // let brands = []; // try { // brands = JSON.parse(rawValue); // } catch (err) { // console.error("❌ Failed to parse metafield value:", err); // } // return json({ brands: brandJson.data, collections, selectedBrandsFromShopify: brands || [], shop }); // }; export const loader = async ({ request }) => { console.log("πŸš€ Loader started"); let admin, session, shop; try { const authResult = await authenticate.admin(request); admin = authResult.admin; session = authResult.session; shop = session?.shop; console.log("βœ… Shopify auth success"); console.log("πŸͺ Shop:", shop); } catch (err) { console.error("❌ Shopify authentication failed:", err); return json({ brands: [], collections: [], selectedBrandsFromShopify: [], shop: "", error: "Shopify authentication failed", }); } let accessToken = ""; try { console.log("πŸ”‘ Fetching Turn14 access token from metafield..."); accessToken = await getTurn14AccessTokenFromMetafield(request); console.log("βœ… Turn14 access token received:", accessToken ? "YES" : "EMPTY"); } catch (err) { console.error("❌ Error getting Turn14 access token:", err); return json({ brands: [], collections: [], selectedBrandsFromShopify: [], shop, error: "Failed to fetch Turn14 access token", }); } /* ========================= FETCH TURN14 BRANDS ========================== */ let brandJson; try { console.log("πŸ“¦ Fetching Turn14 brands..."); const brandRes = await fetch("https://turn14.data4autos.com/v1/brands", { headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json", }, }); console.log("πŸ”„ Awaiting Turn14 brands response...",brandRes); console.log("2345678909876543567 Turn14 brands fetch initiated"); console.log("πŸ“‘ Turn14 brands fetch completed", accessToken); console.log("πŸ“‘ Turn14 brands HTTP status:", brandRes.status); brandJson = await brandRes.json(); console.log("πŸ“¦ Turn14 brands raw response:", brandJson); if (!brandRes.ok) { console.error("❌ Turn14 brands fetch failed"); return json({ brands: [], collections: [], selectedBrandsFromShopify: [], shop, error: brandJson?.error || "Failed to fetch brands", }); } } catch (err) { console.error("❌ Exception while fetching Turn14 brands:", err); return json({ brands: [], collections: [], selectedBrandsFromShopify: [], shop, error: "Turn14 brands fetch crashed", }); } /* ========================= FETCH SHOPIFY COLLECTIONS ========================== */ let collections = []; try { console.log("πŸ—‚οΈ Fetching Shopify collections..."); const gqlRaw = await admin.graphql(` { collections(first: 100) { edges { node { id title } } } } `); const gql = await gqlRaw.json(); console.log("🧾 Shopify collections raw response:", gql); collections = gql?.data?.collections?.edges?.map((e) => e.node) || []; console.log("βœ… Parsed collections count:", collections.length); } catch (err) { console.error("❌ Error fetching Shopify collections:", err); } /* ========================= FETCH SELECTED BRANDS METAFIELD ========================== */ let selectedBrands = []; try { console.log("🏷️ Fetching shop metafield: turn14.selected_brands"); const res = await admin.graphql(` { shop { metafield(namespace: "turn14", key: "selected_brands") { value } } } `); const data = await res.json(); console.log("🧾 Metafield raw response:", data); const rawValue = data?.data?.shop?.metafield?.value; console.log("πŸ“„ Raw metafield value:", rawValue); if (rawValue) { selectedBrands = JSON.parse(rawValue); console.log( "βœ… Parsed selectedBrands count:", selectedBrands.length ); } else { console.log("ℹ️ No metafield value found (first-time setup)"); } } catch (err) { console.error("❌ Failed parsing selected_brands metafield:", err); } /* ========================= FINAL RETURN ========================== */ console.log("🎯 Loader final return payload:", { brandsCount: brandJson?.data?.length || 0, collectionsCount: collections.length, selectedBrandsCount: selectedBrands.length, shop, }); return json({ brands: brandJson?.data || [], collections, selectedBrandsFromShopify: selectedBrands, shop, }); }; export const action = async ({ request }) => { const formData = await request.formData(); const selectedBrands = JSON.parse(formData.get("selectedBrands") || "[]"); const selectedOldBrands = JSON.parse(formData.get("selectedOldBrands") || "[]"); const { session } = await authenticate.admin(request); const shop = session.shop; // "veloxautomotive.myshopify.com" selectedBrands.forEach(brand => { delete brand.pricegroups; }); selectedOldBrands.forEach(brand => { delete brand.pricegroups; }); const resp = await fetch("https://backend.data4autos.com/managebrands", { method: "POST", headers: { "Content-Type": "application/json", "shop-domain": shop, }, body: JSON.stringify({ shop, selectedBrands, selectedOldBrands }), }); if (!resp.ok) { const err = await resp.text(); return json({ error: err }, { status: resp.status }); } const { processId, status } = await resp.json(); return json({ processId, status }); }; export default function BrandsPage() { const { brands = [], collections = [], selectedBrandsFromShopify = [], shop = "", err, error, } = useLoaderData() || {}; console.log(err) console.log(`selectedBrandsFromShopify: ${JSON.stringify(selectedBrandsFromShopify)}`); const actionData = useActionData() || {}; const [selectedIdsold, setSelectedIdsold] = useState([]) const [selectedIds, setSelectedIds] = useState(() => { return (selectedBrandsFromShopify ?? []).map((b) => b.id); }); // console.log("Selected IDS : ", selectedIds) const [search, setSearch] = useState(""); const [filteredBrands, setFilteredBrands] = useState(brands); const [toastActive, setToastActive] = useState(false); const [polling, setPolling] = useState(false); const [status, setStatus] = useState(actionData.status || ""); const [Turn14Enabled, setTurn14Enabled] = useState(null); // null | true | false useEffect(() => { if (!shop) { console.log("⚠️ shop is undefined or empty"); return; } (async () => { const result = await checkShopExists(shop); console.log("βœ… API status result:", result, "| shop:", shop); setTurn14Enabled(result); })(); }, [shop]); useEffect(() => { const selids = selectedIds // console.log("Selected IDS : ", selids) setSelectedIdsold(selids) }, [toastActive]); useEffect(() => { const term = search.toLowerCase(); setFilteredBrands(brands.filter(b => b.name.toLowerCase().includes(term))); }, [search, brands]); useEffect(() => { if (actionData.status) { setStatus(actionData.status); setToastActive(true); } }, [actionData.status]); const checkStatus = async () => { if (!actionData.processId) return; setPolling(true); const resp = await fetch( `https://backend.data4autos.com/managebrands/status/${actionData.processId}`, { headers: { "shop-domain": window.shopify.shop || "" } } ); const jsonBody = await resp.json(); setStatus( jsonBody.status + (jsonBody.detail ? ` (${jsonBody.detail})` : "") ); setPolling(false); }; const toggleSelect = id => setSelectedIds(prev => prev.includes(id) ? prev.filter(i => i !== id) : [...prev, id] ); const allFilteredSelected = filteredBrands.length > 0 && filteredBrands.every(b => selectedIds.includes(b.id)); const toggleSelectAll = () => { const ids = filteredBrands.map(b => b.id); if (allFilteredSelected) { setSelectedIds(prev => prev.filter(id => !ids.includes(id))); } else { setSelectedIds(prev => Array.from(new Set([...prev, ...ids]))); } }; var isSubmitting; // console.log("actionData", actionData); if (actionData.status) { isSubmitting = !actionData.status && !actionData.error && !actionData.processId; } else { isSubmitting = false; } // console.log("isSubmitting", isSubmitting); const toastMarkup = toastActive ? ( setToastActive(false)} /> ) : null; const selectedBrands = brands.filter(b => selectedIds.includes(b.id)); const selectedOldBrands = brands.filter(b => selectedIdsold.includes(b.id)); const shopDomain = (shop || "").split(".")[0]; const items = [ { icon: "βš™οΈ", text: "Manage API settings", link: `https://admin.shopify.com/store/${shopDomain}/apps/d4a-turn14/app/settings` }, { icon: "🏷️", text: "Browse and import available brands", link: `https://admin.shopify.com/store/${shopDomain}/apps/d4a-turn14/app/brands` }, { icon: "πŸ“¦", text: "Sync brand collections to Shopify", link: `https://admin.shopify.com/store/${shopDomain}/apps/d4a-turn14/app/managebrand` }, { icon: "πŸ”", text: "Handle secure Turn14 login credentials", link: `https://admin.shopify.com/store/${shopDomain}/apps/d4a-turn14/app/help` }, ]; // If Turn14 is explicitly NOT connected, show a lightweight call-to-action screen if (Turn14Enabled === false) { return (
Turn14 isn’t connected yet
This shop hasn’t been configured with Turn14 / Data4Autos. To get started, open Settings and complete the connection.
{/* Primary actions */}
Once connected, you’ll be able to browse brands and sync collections.
{/* Secondary links */}
); } // console.log("Selected Brands:", selectedBrands) return (
Data4Autos Turn14 Brands List
{/*

Turn 14 Status:{" "} {Turn14Enabled === true ? "βœ… Turn14 x Shopify Connected!" : Turn14Enabled === false ? "❌ Turn14 x Shopify Connection Doesn't Exists" : "Checking..."}

*/}
{/* Left side - Search + Select All */}
{(actionData?.processId || false) && (

Process ID: {actionData.processId}

Status: {status || "β€”"}

)}
{/* Right side - Save Button */}
{/*
{filteredBrands.map((brand) => (
{/* Checkbox in top-right corner */}
toggleSelect(brand.id)} />
{/* Brand image */}
{/* Brand name */}
{brand.name}
))}
{toastMarkup}
); }