import { useState } from "react";
import { useNavigate } from "react-router-dom";
import {
Page,
Layout,
Card,
Stack,
Button,
Text,
Banner,
List,
Modal,
Spinner,
} from "@shopify/polaris";
import { TitleBar } from "@shopify/app-bridge-react";
import { useAppBridge } from "@shopify/app-bridge-react";
import { useQuery, useQueryClient } from "react-query";
export default function HomePage() {
const navigate = useNavigate();
const app = useAppBridge();
const qc = useQueryClient();
const { data } = useQuery({
queryKey: ["groups"],
queryFn: async () => (await fetch("/api/groups")).json(),
refetchOnWindowFocus: false,
});
const groups = data?.groups ?? [];
const published = groups.filter((g) => g.status === "published").length;
const drafts = groups.filter((g) => g.status === "draft").length;
const [preview, setPreview] = useState(null); // detected groups
const [loading, setLoading] = useState(false);
const [creating, setCreating] = useState(false);
const runDetect = async () => {
setLoading(true);
setPreview(null);
const res = await fetch("/api/groups/auto/preview");
const json = await res.json();
setLoading(false);
setPreview(json.detected || []);
};
const createAll = async () => {
setCreating(true);
const res = await fetch("/api/groups/auto", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({}),
});
const json = await res.json();
setCreating(false);
setPreview(null);
qc.invalidateQueries(["groups"]);
app.toast.show(`Created ${json.created} draft group(s)`);
if (json.created > 0) navigate("/groups");
};
return (
<Page>
<TitleBar title="Linked Products" />
<Layout>
<Layout.Section>
<Banner title="Connect related products for better SEO" status="info">
<p>
Link separate products (like the same item in different colours)
so each product page shows crawlable links to the others. This
improves internal linking and discoverability. Links render
server-side, so search engines index them.
</p>
</Banner>
</Layout.Section>
<Layout.Section oneHalf>
<Card title="Automatic setup" sectioned>
<Stack vertical spacing="loose">
<Text as="p">
Scan your catalogue and detect products that belong together by
their names (e.g. <i>“T-Shirt - Red”</i> and{" "}
<i>“T-Shirt - Blue”</i>). Detected groups are created as{" "}
<b>drafts</b> for you to review before publishing.
</Text>
<Button primary loading={loading} onClick={runDetect}>
Auto-detect groups
</Button>
</Stack>
</Card>
</Layout.Section>
<Layout.Section oneHalf>
<Card title="Your groups" sectioned>
<Stack vertical spacing="loose">
<Stack distribution="fillEvenly">
<Stack vertical spacing="extraTight">
<Text variant="headingLg" as="p">
{published}
</Text>
<Text color="subdued" as="p">
Published
</Text>
</Stack>
<Stack vertical spacing="extraTight">
<Text variant="headingLg" as="p">
{drafts}
</Text>
<Text color="subdued" as="p">
Drafts
</Text>
</Stack>
</Stack>
<Stack>
<Button onClick={() => navigate("/groups")}>Manage groups</Button>
<Button onClick={() => navigate("/groups/new")}>
Create manually
</Button>
</Stack>
</Stack>
</Card>
</Layout.Section>
</Layout>
<Modal
open={preview !== null}
onClose={() => setPreview(null)}
title="Detected product groups"
primaryAction={{
content: creating ? "Creating…" : "Create all as drafts",
onAction: createAll,
disabled: !preview || preview.length === 0 || creating,
loading: creating,
}}
secondaryActions={[{ content: "Cancel", onAction: () => setPreview(null) }]}
>
<Modal.Section>
{loading ? (
<Spinner />
) : preview && preview.length === 0 ? (
<Text as="p">
No new groups detected. Products may already be grouped, or their
names don’t share a common prefix (e.g. “Name - Value”).
</Text>
) : (
<Stack vertical spacing="loose">
<Text as="p" color="subdued">
{preview?.length} group(s) will be created as drafts:
</Text>
{(preview || []).map((g) => (
<div key={g.base}>
<Text variant="headingsm" fontWeight="bold" as="h3">
{g.base} · {g.style}
</Text>
<List type="bullet">
{g.members.map((m) => (
<List.Item key={m.productId}>
{m.productTitle} → <b>{m.swatchValue}</b>
</List.Item>
))}
</List>
</div>
))}
</Stack>
)}
</Modal.Section>
</Modal>
</Page>
);
}