Hello support team.
I hope this message finds you well.
I am building remix Shopify app.
Customer can create their product in the Shopify.
So I made the form modal for it
document.querySelector(‘.modal-form’).onsubmit = async function (event) {
event.preventDefault();
const form = event.target;
const formData = new FormData();
// Add form values
formData.append(‘productTitle’, document.getElementById(‘productTitle’).value);
formData.append(‘productTag’, email_${document.getElementById('productTag').value});
formData.append(‘image1’, document.getElementById(‘image1’).files[0]);
if (document.getElementById(‘image2’).files[0]) {
formData.append(‘image2’, document.getElementById(‘image2’).files[0]);
}
if (document.getElementById(‘image3’).files[0]) {
formData.append(‘image3’, document.getElementById(‘image3’).files[0]);
}
try {
const response = await fetch(‘/apps/greetabl/template_manager’, {
method: ‘POST’,
body: formData,
});
const result = await response.json();
if (result.success) {
alert(‘Product created successfully!’);
// form.reset(); // Reset the form
} else {
alert(Error: ${result.error});
}
} catch (error) {
console.error(‘Submission error:’, error);
alert(‘An error occurred. Please try again.’);
}
};
There is this code in Shopify theme.
import { json } from “@remix-run/node”;
import fetch from “node-fetch”;
const prepareFiles = (files) =>
files.map((file) => ({
filename: file.name,
mimeType: file.type,
resource: file.type.includes(“image”) ? “IMAGE” : “FILE”,
fileSize: file.size.toString(),
httpMethod: “POST”,
}));
const prepareFilesToCreate = (stagedTargets, files) =>
stagedTargets.map((stagedTarget, index) => {
return {
originalSource: stagedTarget.resourceUrl,
contentType: files[index].type.includes(“image”) ? “IMAGE” : “FILE”,
filename: files[index].name,
};
});
export const action = async ({ request }) => {
const formData = await request.formData();
const productTitle = formData.get(“productTitle”);
const productTag = formData.get(“productTag”);
const newTag = “pending”;
let updatedTags = [productTag, newTag];
const image1 = formData.get(“image1”);
const image2 = formData.get(“image2”);
const image3 = formData.get(“image3”);
try {
// Step 1: Upload images to Shopify
const imageFiles = [image1, image2, image3].filter(Boolean);
const uploadedImages = await uploadImagesToShopify(imageFiles);
// for (const file of imageFiles) {
// const uploadedImagesrc=await uploadImageToShopify(file);
// if (uploadedImage) images.push({ src: uploadedImage });
// }
// Step 2: Create product with uploaded images
const query = mutation CreateProduct($input: ProductInput!) { productCreate(input: $input) { product { id title images(first: 10) { edges { node { id src } } } } userErrors { field message } } };
const input = {
title: productTitle,
tags: updatedTags,
images: uploadedImages.map((image) => ({ src: image.imageUrl })),
variants: [
{
price: “10.00”, // Set product price
},
],
};
const response = await fetch(
https://${process.env.SHOPIFY_STORE_DOMAIN}/admin/api/2023-10/graphql.json,
{
method: “POST”,
headers: {
“Content-Type”: “application/json”,
“X-Shopify-Access-Token”: process.env.SHOPIFY_ADMIN_API_TOKEN,
},
body: JSON.stringify({ query, variables: { input: input } }),
}
);
const result = await response.json();
if (result.errors) {
throw new Error(result.errors.map((err) => err.message).join(", "));
}
return json({ success: true, product: result.data.productCreate.product });
} catch (error) {
console.error(“Error creating product:”, error);
return json({ success: false, error: error.message });
}
};
// Helper function to upload image
async function uploadImagesToShopify(files) {
const preparedFiles = prepareFiles(files);
const uploadMutation = mutation stagedUploadsCreate($input: [StagedUploadInput!]!) { stagedUploadsCreate(input: $input) { stagedTargets { url resourceUrl parameters { name value } } userErrors { field message } } };
// Step 1: Request a signed upload URL from Shopify
const result = await fetch(
https://${process.env.SHOPIFY_STORE_DOMAIN}/admin/api/2023-10/graphql.json,
{
method: “POST”,
headers: {
“Content-Type”: “application/json”,
“X-Shopify-Access-Token”: process.env.SHOPIFY_ADMIN_API_TOKEN,
},
body: JSON.stringify({ query: uploadMutation, variables: { input: preparedFiles } }),
}
);
const response = await result.json();
const promises = ;
files.forEach((file, index) => {
const url = response.data.stagedUploadsCreate.stagedTargets[index].url;
const params = response.data.stagedUploadsCreate.stagedTargets[index].parameters;
const formData = new FormData();
params.forEach((param) => {
formData.append(param.name, param.value);
});
formData.append(“file”, file);
const promise = fetch(url, {
method: “POST”,
body: formData,
});
promises.push(promise);
});
await Promise.all(promises);
const createMutation = mutation fileCreate($files: [FileCreateInput!]!) { fileCreate(files: $files) { files { id preview { image { url } } } userErrors { field message } } };
const stagedTargets = response.data.stagedUploadsCreate.stagedTargets;
const filesToCreate = prepareFilesToCreate(stagedTargets, files);
const createResult = await fetch(
https://${process.env.SHOPIFY_STORE_DOMAIN}/admin/api/2023-10/graphql.json,
{
method: “POST”,
headers: {
“Content-Type”: “application/json”,
“X-Shopify-Access-Token”: process.env.SHOPIFY_ADMIN_API_TOKEN,
},
body: JSON.stringify({
query: createMutation,
variables: {
files: filesToCreate,
},
}),
}
);
const responseData = await createResult.json();
const createdfiles = responseData.data.fileCreate.files;
console.log(“---------0”, createdfiles);
let results = ; // Array to store all file details
for (const file of createdfiles) {
const fileId = file.id;
let status = “”;
let imageUrl = “”;
// Poll for file status
while (status === “”) {
const createFilesMutation = query ($id: ID!) { node(id: $id) { ... on MediaImage { id fileStatus image { url } } } };
const responseQuery = await fetch(
https://${process.env.SHOPIFY_STORE_DOMAIN}/admin/api/2023-10/graphql.json,
{
method: “POST”,
headers: {
“Content-Type”: “application/json”,
“X-Shopify-Access-Token”: process.env.SHOPIFY_ADMIN_API_TOKEN,
},
body: JSON.stringify({
query: createFilesMutation,
variables: {
id: fileId,
},
}),
}
);
const responseJson = await responseQuery.json();
console.log(“---------1”, responseJson.data.node.fileStatus);
// If the file status is READY, exit the loop
if (responseJson.data.node.fileStatus === “READY”) {
status = responseJson.data.node.fileStatus;
imageUrl = responseJson.data.node.image.url;
console.log(‘___+++++’, imageUrl);
} else {
// Add a delay to prevent spamming the server
await new Promise((resolve) => setTimeout(resolve, 2000));
}
}
// Add the file details to the results array
results.push({
fileId: fileId,
status: status,
imageUrl: imageUrl,
});
}
// Return all results
return {
stagedfiles: results,
stagedTargets: responseData.data.stagedUploadsCreate.stagedTargets,
errors: responseData.data.stagedUploadsCreate.userErrors,
};
}
There is this code in remix app router folder.
So when submitting the form, this code will be called.
But image preview is null in console.log(‘--------0’,…) and console.log(“---------1”, responseJson.data.node.fileStatus) is FAILED.
What am I missing in the remix code?
I hope for you to solve my issue.
Sorry to bother you.
