Here is my Code, the File name is products.$handle.jsx and is in the routes folder :
import { json, redirect } from "@remix-run/node";
import { useLoaderData, useActionData, Form, Link } from "@remix-run/react";
import shopify from "../shopify.server";
import {
Page,
Layout,
Card,
TextField,
Button,
AppProvider,
BlockStack,
} from "@shopify/polaris";
import { TitleBar } from "@shopify/app-bridge-react";
import enTranslations from '@shopify/polaris/locales/en.json';
import React, { useState } from "react";
export async function loader({ params, request }) {
const { admin } = await shopify.authenticate.admin(request);
const response = await admin.graphql(
`
query Product($handle: String!) {
productByHandle(handle: $handle) {
id
title
handle
descriptionHtml
seo {
title
description
}
}
}
`,
{
variables: { handle: params.handle },
}
);
const data = await response.json();
const product = data.data.productByHandle;
if (!product) {
throw new Response("Product not found", { status: 404 });
}
return json({ product });
}
export async function action({ request, params }) {
const formData = new URLSearchParams(await request.text());
const id = formData.get("id");
const title = formData.get("title");
const handle = formData.get("handle");
const description = formData.get("description");
const seoTitle = formData.get("seoTitle");
const seoDescription = formData.get("seoDescription");
const { admin } = await shopify.authenticate.admin(request);
const response = await admin.graphql(`
mutation UpdateProduct($input: ProductInput!) {
productUpdate(input: $input) {
product {
id
title
handle
descriptionHtml
seo {
title
description
}
}
userErrors {
field
message
}
}
}
`, {
variables: {
input: {
id: id,
title,
handle,
descriptionHtml: description,
seo: {
title: seoTitle,
description: seoDescription
}
}
}
});
const parsedResponse = await response.json();
const errors = parsedResponse.data.productUpdate.userErrors;
if (errors.length > 0) {
return json({ errors }, { status: 400 });
}
const updatedProduct = parsedResponse.data.productUpdate.product;
return redirect(`/products/${updatedProduct.handle}`);
}
export default function EditProductPage() {
const { product } = useLoaderData();
const actionData = useActionData();
const [title, setTitle] = useState(product.title || "");
const [handle, setHandle] = useState(product.handle || "");
const [descriptionHtml, setDescriptionHtml] = useState(product.descriptionHtml || "");
const [seoTitle, setSeoTitle] = useState(product.seo?.title || "");
const [seoDescription, setSeoDescription] = useState(product.seo?.description || "");
const [focusedKeyword, setFocusedKeyword] = useState(seoScore?.focusedKeyword || "");
return (
<AppProvider i18n={enTranslations}>
<Page>
<TitleBar title="Edit Product" />
<Layout>
<Layout.Section>
<Card sectioned>
<Form method="post">
<BlockStack gap="200">
<input type="hidden" name="id" value={product.id} />
<TextField
label="Title"
name="title"
value={title}
onChange={(value) => setTitle(value)}
/>
<TextField
label="Handle"
name="handle"
value={handle}
onChange={(value) => setHandle(value)}
/>
<TextField
label="Description"
name="description"
value={descriptionHtml}
multiline={4}
onChange={(value) => setDescriptionHtml(value)}
/>
<TextField
label="SEO Title"
name="seoTitle"
value={seoTitle}
onChange={(value) => setSeoTitle(value)}
/>
<TextField
label="SEO Description"
name="seoDescription"
value={seoDescription}
multiline={2}
onChange={(value) => setSeoDescription(value)}
/>
{actionData?.errors && (
<div>
{actionData.errors.map((error, index) => (
<p key={index} style={{ color: "red" }}>{error.message}</p>
))}
</div>
)}
<Button submit primary>Save</Button>
<Link to="/products">
<Button>Cancel</Button>
</Link>
</BlockStack>
</Form>
</Card>
</Layout.Section>
</Layout>
</Page>
</AppProvider>
);
}
I'm using latest version of Polaris, and have a problem with the route if I rename that file to app.products.$handle.jsx and route from app.products.jsx to <Link to={`/app/products/${handle}`}>{title}</Link>
it doesn't work, it works only if the file name is products.$handle.jsx and route to :
<Link to={`/products/${handle}`}>{title}</Link>