312 lines
10 KiB
JavaScript
312 lines
10 KiB
JavaScript
// app/routes/store-credentials.jsx
|
|
|
|
import { json, redirect } from "@remix-run/node";
|
|
import { useLoaderData, useActionData, Form } from "@remix-run/react";
|
|
import { useEffect, useState } from "react";
|
|
import {
|
|
Page,
|
|
Layout,
|
|
Card,
|
|
TextField,
|
|
Button,
|
|
TextContainer,
|
|
InlineError,
|
|
Text,
|
|
BlockStack,
|
|
InlineStack,
|
|
Box,
|
|
} from "@shopify/polaris";
|
|
import { TitleBar } from "@shopify/app-bridge-react";
|
|
import { authenticate } from "../shopify.server";
|
|
|
|
const SCOPES = [
|
|
"read_inventory",
|
|
"read_products",
|
|
"write_inventory",
|
|
"write_products",
|
|
"read_publications",
|
|
"write_publications",
|
|
].join(",");
|
|
const REDIRECT_URI = "https://backend.data4autos.com/auth/callback";
|
|
const CLIENT_ID = "b7534c980967bad619cfdb9d3f837cfa";
|
|
|
|
export const loader = async ({ request }) => {
|
|
const { admin } = await authenticate.admin(request);
|
|
const resp = await admin.graphql(`
|
|
{
|
|
shop {
|
|
id
|
|
name
|
|
metafield(namespace: "turn14", key: "credentials") { value }
|
|
}
|
|
}
|
|
`);
|
|
const { data } = await resp.json();
|
|
let creds = {};
|
|
if (data.shop.metafield?.value) {
|
|
try { creds = JSON.parse(data.shop.metafield.value); } catch { }
|
|
}
|
|
//creds = {};
|
|
return json({
|
|
shopName: data.shop.name,
|
|
shopId: data.shop.id,
|
|
savedCreds: creds,
|
|
});
|
|
};
|
|
|
|
// export const action = async ({ request }) => {
|
|
// const formData = await request.formData();
|
|
// const { admin } = await authenticate.admin(request);
|
|
|
|
// // ——— Handle Shopify-install trigger ———
|
|
// if (formData.get("install_shopify") === "1") {
|
|
// const shopName = formData.get("shop_name");
|
|
// const stateNonce = Math.random().toString(36).slice(2);
|
|
// const installUrl =
|
|
// `https://${shopName}.myshopify.com/admin/oauth/authorize` +
|
|
// `?client_id=${CLIENT_ID}` +
|
|
// `&scope=${SCOPES}` +
|
|
// `&redirect_uri=${encodeURIComponent(REDIRECT_URI)}` +
|
|
// `&state=${stateNonce}` +
|
|
// `&grant_options%5B%5D=per-user`;
|
|
|
|
// // return the URL instead of redirecting
|
|
// return json({ confirmationUrl: installUrl });
|
|
// }
|
|
|
|
|
|
// // ——— Otherwise handle Turn14 token exchange ———
|
|
// const clientId = formData.get("client_id");
|
|
// const clientSecret = formData.get("client_secret");
|
|
// const shopInfo = await admin.graphql(`{ shop { id } }`);
|
|
// const shopId = (await shopInfo.json()).data.shop.id;
|
|
|
|
// let tokenData;
|
|
// try {
|
|
// const tokenRes = await fetch("https://turn14.data4autos.com/v1/auth/token", {
|
|
// method: "POST",
|
|
// headers: { "Content-Type": "application/json" },
|
|
// body: JSON.stringify({
|
|
// grant_type: "client_credentials",
|
|
// client_id: clientId,
|
|
// client_secret: clientSecret,
|
|
// }),
|
|
// });
|
|
// tokenData = await tokenRes.json();
|
|
// if (!tokenRes.ok) {
|
|
// throw new Error(tokenData.error || "Failed to fetch Turn14 token");
|
|
// }
|
|
// } catch (err) {
|
|
// return json({ success: false, error: err.message });
|
|
// }
|
|
|
|
// // upsert as Shopify metafield
|
|
// const creds = {
|
|
// clientId,
|
|
// clientSecret,
|
|
// accessToken: tokenData.access_token,
|
|
// expiresAt: new Date(Date.now() + 3600 * 1000).toISOString(),
|
|
// };
|
|
// const mutation = `
|
|
// mutation {
|
|
// metafieldsSet(metafields: [{
|
|
// ownerId: "${shopId}",
|
|
// namespace: "turn14",
|
|
// key: "credentials",
|
|
// type: "json",
|
|
// value: "${JSON.stringify(creds).replace(/"/g, '\\"')}"
|
|
// }]) {
|
|
// userErrors { message }
|
|
// }
|
|
// }
|
|
// `;
|
|
// const saveRes = await admin.graphql(mutation);
|
|
// const saveJson = await saveRes.json();
|
|
// const errs = saveJson.data.metafieldsSet.userErrors;
|
|
// if (errs.length) {
|
|
// return json({ success: false, error: errs[0].message });
|
|
// }
|
|
|
|
// return json({ success: true, creds });
|
|
// };
|
|
|
|
|
|
export const action = async ({ request }) => {
|
|
const formData = await request.formData();
|
|
const { admin } = await authenticate.admin(request);
|
|
|
|
// ——— Turn14 token exchange ———
|
|
const clientId = formData.get("client_id");
|
|
const clientSecret = formData.get("client_secret");
|
|
const shopResp = await admin.graphql(`{ shop { id name } }`);
|
|
const shopJson = await shopResp.json();
|
|
const shopId = shopJson.data.shop.id;
|
|
const shopName = shopJson.data.shop.name;
|
|
|
|
let tokenData;
|
|
try {
|
|
const tokenRes = await fetch("https://turn14.data4autos.com/v1/auth/token", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({
|
|
grant_type: "client_credentials",
|
|
client_id: clientId,
|
|
client_secret: clientSecret,
|
|
}),
|
|
});
|
|
tokenData = await tokenRes.json();
|
|
if (!tokenRes.ok) {
|
|
throw new Error(tokenData.error || "Failed to fetch Turn14 token");
|
|
}
|
|
} catch (err) {
|
|
return json({ success: false, error: err.message });
|
|
}
|
|
|
|
// ——— Upsert to Shopify metafield ———
|
|
const creds = {
|
|
clientId,
|
|
clientSecret,
|
|
accessToken: tokenData.access_token,
|
|
expiresAt: new Date(Date.now() + 3600 * 1000).toISOString(),
|
|
};
|
|
const mutation = `
|
|
mutation {
|
|
metafieldsSet(metafields: [{
|
|
ownerId: "${shopId}",
|
|
namespace: "turn14",
|
|
key: "credentials",
|
|
type: "json",
|
|
value: "${JSON.stringify(creds).replace(/"/g, '\\"')}"
|
|
}]) {
|
|
userErrors { message }
|
|
}
|
|
}
|
|
`;
|
|
const saveRes = await admin.graphql(mutation);
|
|
const saveJson = await saveRes.json();
|
|
const errs = saveJson.data.metafieldsSet.userErrors;
|
|
if (errs.length) {
|
|
return json({ success: false, error: errs[0].message });
|
|
}
|
|
|
|
// ——— Build the Shopify OAuth URL and return it ———
|
|
const stateNonce = Math.random().toString(36).slice(2);
|
|
const installUrl =
|
|
`https://${shopName}.myshopify.com/admin/oauth/authorize` +
|
|
`?client_id=${CLIENT_ID}` +
|
|
`&scope=${SCOPES}` +
|
|
`&redirect_uri=${encodeURIComponent(REDIRECT_URI)}` +
|
|
`&state=${stateNonce}`
|
|
//+ `&grant_options%5B%5D=per-user`;
|
|
|
|
return json({
|
|
success: true,
|
|
confirmationUrl: installUrl,
|
|
});
|
|
};
|
|
|
|
|
|
|
|
export default function StoreCredentials() {
|
|
const { shopName, shopId, savedCreds } = useLoaderData();
|
|
const actionData = useActionData();
|
|
|
|
|
|
useEffect(() => {
|
|
if (actionData?.confirmationUrl) {
|
|
window.open(actionData.confirmationUrl, "_blank", "noopener,noreferrer");
|
|
}
|
|
}, [actionData?.confirmationUrl]);
|
|
|
|
|
|
const [clientId, setClientId] = useState(actionData?.creds?.clientId || savedCreds.clientId || "");
|
|
const [clientSecret, setClientSecret] = useState(actionData?.creds?.clientSecret || savedCreds.clientSecret || "");
|
|
const connected = actionData?.success || Boolean(savedCreds.accessToken);
|
|
|
|
return (
|
|
<Page >
|
|
<TitleBar title="Turn14 & Shopify Connect" />
|
|
<div style={{ display: "flex", justifyContent: "center", textAlign: "center", paddingBottom: "20px" }}>
|
|
<Text as="h1" variant="headingLg">
|
|
Data4Autos Turn14 Integration
|
|
</Text>
|
|
</div>
|
|
<Layout>
|
|
<Layout.Section>
|
|
<div style={{ display: "flex", alignItems: "center", justifyContent: "center" }}>
|
|
<Box maxWidth="450px" width="100%" marginInline="auto" >
|
|
<Card sectioned padding="600">
|
|
<BlockStack gap="400">
|
|
<TextContainer spacing="tight">
|
|
<Text variant="headingLg" align="center" fontWeight="medium">Shop: {shopName}</Text>
|
|
</TextContainer>
|
|
|
|
{/* —— TURN14 FORM —— */}
|
|
<Form method="post">
|
|
<BlockStack gap="400" >
|
|
|
|
<BlockStack gap="200">
|
|
<input type="hidden" name="shop_name" value={shopName} />
|
|
<TextField
|
|
label="Turn14 Client ID"
|
|
name="client_id"
|
|
value={clientId}
|
|
onChange={setClientId}
|
|
autoComplete="off"
|
|
requiredIndicator
|
|
padding="200"
|
|
/>
|
|
</BlockStack>
|
|
<BlockStack gap="200">
|
|
<TextField
|
|
label="Turn14 Client Secret"
|
|
name="client_secret"
|
|
value={clientSecret}
|
|
onChange={setClientSecret}
|
|
autoComplete="off"
|
|
requiredIndicator
|
|
padding="200"
|
|
/>
|
|
</BlockStack>
|
|
|
|
|
|
<BlockStack gap="200">
|
|
<Button submit primary size="large"
|
|
variant="primary">
|
|
Connect Turn14
|
|
</Button>
|
|
</BlockStack>
|
|
</BlockStack>
|
|
</Form>
|
|
</BlockStack>
|
|
{actionData?.error && (
|
|
<TextContainer spacing="tight" style={{ marginTop: "1rem" }}>
|
|
<InlineError message={actionData.error} fieldID="client_id" />
|
|
</TextContainer>
|
|
)}
|
|
|
|
{connected && (
|
|
<TextContainer spacing="tight" style={{ marginTop: "1.5rem" }}>
|
|
<p style={{ color: "green", paddingTop: "5px" }}>✅ Turn14 connected successfully!</p>
|
|
|
|
{/* —— SHOPIFY INSTALL FORM —— */}
|
|
{/* <Form method="post">
|
|
<input type="hidden" name="shop_name" value={shopName} />
|
|
<input type="hidden" name="install_shopify" value="1" />
|
|
<div style={{ marginTop: "1rem" }}>
|
|
<Button submit primary>
|
|
Connect to Shopify
|
|
</Button>
|
|
</div>
|
|
</Form> */}
|
|
</TextContainer>
|
|
)}
|
|
</Card>
|
|
</Box>
|
|
</div>
|
|
</Layout.Section>
|
|
</Layout >
|
|
</Page >
|
|
);
|
|
}
|