次.ストラピとチャクラUIとのJS電子商取引アプリ



この記事では、我々は我々が前にした私たちの管理パネルの電子商取引アプリを作成します.
これは、現在3のバージョンを絞り込みでヘッド無しで使用されます.あなたはヘッドレス機能を使用して任意のUIライブラリを使用することができます.
私たちはStrapi and Chakra-UI 一緒に電子商取引クライアントの例のアプリケーションで.

プロジェクトセットアップの改善
私たちの洗練されたプロジェクトを作成することから始めましょう.あなたはsuperplate リファインプロジェクトを作成するには
npx superplate-cli -p refine-nextjs refine-ecommerce-example
✔ What will be the name of your app · refine-ecommerce-example
✔ Package manager: · npm
✔ Do you want to using UI Framework? > No(headless)
✔ Data Provider: Strapi
✔ i18n - Internationalization: · no
SuperPlateはすぐに我々が選択した機能に応じて我々の洗練されたプロジェクトを作成します.あとで使用するChackra UIパッケージをインストールし続けましょう.

インストール
cd refine-ecommerce-example

npm i @pankod/refine-strapi-v4
npm i @chakra-ui/react @emotion/react@^11 @emotion/styled@^11 framer-motion@^6
我々の洗練されたプロジェクトとインストールは、現在準備ができています!使い始めましょう.

用途

Strapi - V 4用のリファインの設定​pages/index.tsx:
import React from "react";
import { AppProps } from "next/app";
import Head from "next/head";
import { Refine } from "@pankod/refine-core";
import routerProvider from "@pankod/refine-nextjs-router";
import { DataProvider } from "@pankod/refine-strapi-v4";

const API_URL = "https://api.strapi-multi-tenant.refine.dev/api";

function MyApp({ Component, pageProps }: AppProps): JSX.Element {
    const dataProvider = DataProvider(API_URL);

    return (
        <Refine
            routerProvider={routerProvider}
            dataProvider={dataProvider}
        >
            <Component {...pageProps} />
        </Refine>
    );
}

チャクラのUIプロバイダのセットアップ​pages/index.tsx:
import React from "react";
import { AppProps } from "next/app";
import Head from "next/head";
import { Refine } from "@pankod/refine-core";
import routerProvider from "@pankod/refine-nextjs-router";
import { DataProvider } from "@pankod/refine-strapi-v4";

import { ChakraProvider } from "@chakra-ui/react";

const API_URL = "https://api.strapi-multi-tenant.refine.dev/api";

function MyApp({ Component, pageProps }: AppProps): JSX.Element {
    const dataProvider = DataProvider(API_URL);

    return (
        <Refine routerProvider={routerProvider} dataProvider={dataProvider}>
            <ChakraProvider>
                <Component {...pageProps} />
            </ChakraProvider>
        </Refine>
    );
}

コレクションの作成
私たちはstrapistore , product and order との間の関係を追加しました.コレクションを作成する方法の詳細については、チェックすることができますhere .
我々は以前のstrapiの多国間ガイドで私達のコレクションを作成しました.現在、同じコレクションを使用します.
Refer to the Project Collections for detailed information. →

レイアウトの作成
リファインヘッドレスは、任意のUIと提携していません.それは完全にあなたのUIをカスタマイズすることです.この例の簡単なレイアウトを作りましょう.
我々が今作成したレイアウトは、洗練されたロゴだけを示します.次の手順でレイアウトを編集します.components/Layout.tsx:
import { Box, Container, Flex, Image } from "@chakra-ui/react";

export const Layout: React.FC = ({ children }) => {
    return (
        <Box
            display={"flex"}
            flexDirection={"column"}
            backgroundColor={"#eeeeee"}
            minH={"100vh"}
        >
            <Container maxW={"container.lg"}>
                <Flex justify={"space-between"} mt={4} alignSelf={"center"}>
                    <a href="https://refine.dev">
                        <Image alt="Refine Logo" src={"./refine_logo.png"} />
                    </a>
                </Flex>
                {children}
            </Container>
        </Box>
    );
};
pages/_app.tsx:
import React from "react";
import { AppProps } from "next/app";
import Head from "next/head";
import { Refine } from "@pankod/refine-core";
import routerProvider from "@pankod/refine-nextjs-router";
import { DataProvider } from "@pankod/refine-strapi-v4";

import { ChakraProvider } from "@chakra-ui/react";
import { Layout } from "src/components";

const API_URL = "https://api.strapi-multi-tenant.refine.dev/api";

function MyApp({ Component, pageProps }: AppProps): JSX.Element {
    const dataProvider = DataProvider(API_URL);

    return (
        <Refine
            routerProvider={routerProvider}
            dataProvider={dataProvider}
            Layout={Layout}
        >
            <ChakraProvider>
                <Component {...pageProps} />
            </ChakraProvider>
        </Refine>
    );
}


プロダクトカードデザイン
チャクラのUIを使用して製品カードを設計しましょう.src/components/ProductCard.tsx
import React from "react";
import { Box, Image, Badge, Button } from "@chakra-ui/react";

export type ProductProps = {
    id: string;
    title: string;
    description: string;
    cardImage: string;
};

export const ProductCard: React.FC<ProductProps> = ({
    id,
    title,
    description,
    cardImage,
}) => {
    return (
        <Box maxH={"sm"} borderWidth="1px" borderRadius="lg" overflow="hidden">
            <Image w={"100%"} h={200} src={cardImage} />
            <Box p="6" bgColor={"gray.600"}>
                <Box display="flex" alignItems="baseline" mb={2} ml={-2}>
                    <Badge borderRadius="full" px="2" colorScheme="teal">
                        New Product
                    </Badge>
                </Box>

                <Box
                    mt="1"
                    fontWeight="semibold"
                    as="h4"
                    lineHeight="tight"
                    isTruncated
                    color={"white"}
                >
                    {title}
                </Box>

                <Box color={"white"}>{}</Box>
                <Box
                    color="white"
                    fontSize="sm"
                    display={"flex"}
                    mt={4}
                    justifyContent={"flex-end"}
                ></Box>
            </Box>
        </Box>
    );
};

弊社製品カードコンポーネントを作成しました.さあ、私たちの製品をレピから取り出して見せてみましょう.

製品をSSRでフェッチする
最初に、我々の製品をNextjsgetServerSideProps 関数.
GetServerSideProps pages/index.tsx:
import { GetServerSideProps } from "next";
import { DataProvider } from "@pankod/refine-strapi-v4";

import { IProduct } from "interfaces";

const API_URL = "https://api.strapi-multi-tenant.refine.dev/api";

export const getServerSideProps: GetServerSideProps = async (context) => {
    const data = await DataProvider(API_URL).getList<IProduct>({
        resource: "products",
        metaData: { populate: ["image"] },
    });

    return {
        props: { products: data },
    };
};

製品リストの作成
上記のデータをリファインのuseTable フック.では、PropertCardコンポーネントにデータを入れましょう.pages/index.tsx:
import { GetServerSideProps } from "next";
import { LayoutWrapper, GetListResponse, useTable } from "@pankod/refine-core";
import { DataProvider } from "@pankod/refine-strapi-v4";

import { IProduct } from "interfaces";
import { SimpleGrid } from "@chakra-ui/react";
import { ProductCard } from "src/components";

const API_URL = "https://api.strapi-multi-tenant.refine.dev/api";

type ItemProps = {
    products: GetListResponse<IProduct>;
};

export const ProductList: React.FC<ItemProps> = ({ products }) => {
    const { tableQueryResult } = useTable<IProduct>({
        resource: "products",
        queryOptions: {
            initialData: products,
        },
        metaData: { populate: ["image"] },
    });

    return (
        <LayoutWrapper>
            <SimpleGrid columns={[1, 2, 3]} mt={6} spacing={3}>
                {tableQueryResult.data?.data.map((item) => (
                    <ProductCard
                        id={item.id}
                        title={item.title}
                        description={item.description}
                        cardImage={
                            item.image
                                ? API_URL + item.image.url
                                : "./error.png"
                        }
                    />
                ))}
            </SimpleGrid>
        </LayoutWrapper>
    );
};

export default ProductList;

export const getServerSideProps: GetServerSideProps = async (context) => {
    const data = await DataProvider(API_URL).getList<IProduct>({
        resource: "products",
        metaData: { populate: ["image"] },
    });

    return {
        props: { products: data },
    };
};


ストアベースのフィルタリングの追加
我々は上記のすべての製品を取得している.さて、ストアを取得し、ストア固有の製品を個別に一覧表示しましょう.
まず、リファインを使って店に行きましょうuseMany フックの内側getServerSideProps 関数.次に、ストアのボタンを作成します.これらのボタンをクリックすると、ストアが選択され、我々はフィルタリングを行いますuseTable setFilters とそのストアに固有の製品を一覧表示します.pages/index.tsx:
export const getServerSideProps: GetServerSideProps = async (context) => {
    const data = await DataProvider(API_URL).getList<IProduct>({
        resource: "products",
        metaData: { populate: ["image"] },
        pagination: { current: 1, pageSize: 9 },
    });

    const { data: storesData } = await DataProvider(API_URL).getMany({
        resource: "stores",
        ids: ["1", "2", "3"],
    });

    return {
        props: {
            products: data,
            stores: storesData,
        },
    };
};
pages/index.tsx:
import { GetServerSideProps } from "next";
import { LayoutWrapper, GetListResponse, useTable } from "@pankod/refine-core";
import { DataProvider } from "@pankod/refine-strapi-v4";

import { IProduct, IStore } from "interfaces";
import { Button, SimpleGrid, Flex, Text } from "@chakra-ui/react";
import { ProductCard, FilterButton } from "src/components";

const API_URL = "https://api.strapi-multi-tenant.refine.dev/api";

type ItemProps = {
    products: GetListResponse<IProduct>;
    stores: IStore[];
};

export const ProductList: React.FC<ItemProps> = ({ products, stores }) => {
    const { tableQueryResult, setFilters } = useTable<IProduct>({
        resource: "products",
        queryOptions: {
            initialData: products,
        },
        metaData: { populate: ["image"] },
    });

    return (
        <LayoutWrapper>
            <Flex mt={6} gap={2}>
                <FilterButton
                    setFilters={() =>
                        setFilters([
                            {
                                field: "stores][id]",
                                operator: "eq",
                                value: undefined,
                            },
                        ])
                    }
                >
                    <Text fontSize={{ base: "12px", md: "14px", lg: "14px" }}>
                        All Products
                    </Text>
                </FilterButton>
                {stores?.map((item) => {
                    return (
                        <FilterButton
                            setFilters={() =>
                                setFilters([
                                    {
                                        field: "stores][id]",
                                        operator: "eq",
                                        value: item.id,
                                    },
                                ])
                            }
                        >
                            <Text
                                fontSize={{
                                    base: "12px",
                                    md: "14px",
                                    lg: "14px",
                                }}
                            >
                                {item.title}
                            </Text>
                        </FilterButton>
                    );
                })}
            </Flex>
            <SimpleGrid columns={[1, 2, 3]} mt={6} spacing={3}>
                {tableQueryResult.data?.data.map((item) => (
                    <ProductCard
                        id={item.id}
                        title={item.title}
                        description={item.description}
                        cardImage={
                            item.image
                                ? API_URL + item.image.url
                                : "./error.png"
                        }
                    />
                ))}
            </SimpleGrid>
        </LayoutWrapper>
    );
};

export default ProductList;

export const getServerSideProps: GetServerSideProps = async (context) => {
    const data = await DataProvider(API_URL).getList<IProduct>({
        resource: "products",
        metaData: { populate: ["image"] },
        pagination: { current: 1, pageSize: 9 },
    });

    const { data: storesData } = await DataProvider(API_URL).getMany({
        resource: "stores",
        ids: ["1", "2", "3"],
    });

    return {
        props: {
            products: data,
            stores: storesData,
        },
    };
};


ページの追加
我々はすべての製品を一覧表示しますAll Products ページ.ページにページを追加し、ページに製品を分割しましょう.を使ってページネーションを行いますpageSize , current USETableフックからのSetcurrentプロパティ.
Refer to the useTable documentation for detailed information. → pages/index.tsx:
import { GetServerSideProps } from "next";
import { LayoutWrapper, GetListResponse, useTable } from "@pankod/refine-core";
import { DataProvider } from "@pankod/refine-strapi-v4";

import { IProduct, IStore } from "interfaces";
import { Button, SimpleGrid, Flex, Text } from "@chakra-ui/react";
import { ProductCard, FilterButton } from "src/components";

const API_URL = "https://api.strapi-multi-tenant.refine.dev/api";

type ItemProps = {
    products: GetListResponse<IProduct>;
    stores: IStore[];
};

export const ProductList: React.FC<ItemProps> = ({ products, stores }) => {
    const { tableQueryResult, setFilters, current, setCurrent, pageSize } =
        useTable<IProduct>({
            resource: "products",
            queryOptions: {
                initialData: products,
            },
            initialPageSize: 9,
            metaData: { populate: ["image"] },
        });

    const totalPageCount = Math.ceil(tableQueryResult.data?.total!! / pageSize);

    return (
        <LayoutWrapper>
            <Flex mt={6} gap={2}>
                <FilterButton
                    setFilters={() =>
                        setFilters([
                            {
                                field: "stores][id]",
                                operator: "eq",
                                value: undefined,
                            },
                        ])
                    }
                >
                    <Text fontSize={{ base: "12px", md: "14px", lg: "14px" }}>
                        All Products
                    </Text>
                </FilterButton>
                {stores?.map((item) => {
                    return (
                        <FilterButton
                            setFilters={() =>
                                setFilters([
                                    {
                                        field: "stores][id]",
                                        operator: "eq",
                                        value: item.id,
                                    },
                                ])
                            }
                        >
                            <Text
                                fontSize={{
                                    base: "12px",
                                    md: "14px",
                                    lg: "14px",
                                }}
                            >
                                {item.title}
                            </Text>
                        </FilterButton>
                    );
                })}
            </Flex>
            <SimpleGrid columns={[1, 2, 3]} mt={6} spacing={3}>
                {tableQueryResult.data?.data.map((item) => (
                    <ProductCard
                        id={item.id}
                        title={item.title}
                        description={item.description}
                        cardImage={
                            item.image
                                ? API_URL + item.image.url
                                : "./error.png"
                        }
                    />
                ))}
            </SimpleGrid>
            <Flex justify={"flex-end"} mt={4} mb={4} gap={2}>
                {Array.from(Array(totalPageCount), (e, i) => {
                    if (current > totalPageCount) {
                        setCurrent(i);
                    }
                    return (
                        <Button
                            colorScheme={"teal"}
                            onClick={() => setCurrent(i + 1)}
                        >
                            {"Page: " + (i + 1)}
                        </Button>
                    );
                })}
            </Flex>
        </LayoutWrapper>
    );
};

export default ProductList;

export const getServerSideProps: GetServerSideProps = async (context) => {
    const data = await DataProvider(API_URL).getList<IProduct>({
        resource: "products",
        metaData: { populate: ["image"] },
        pagination: { current: 1, pageSize: 9 },
    });

    const { data: storesData } = await DataProvider(API_URL).getMany({
        resource: "stores",
        ids: ["1", "2", "3"],
    });

    return {
        props: { products: data, stores: storesData },
    };
};


バスケットとお支払い機能を追加する
電子商取引アプリケーションでなければならないステップの1つはカートと支払い取引です.この例では、Snipcart このプロセスのために.
Refer to the Snipcart documentation for detailed information. →

ウィジェットのインストールpages/_app.tsx:
function MyApp({ Component, pageProps }: AppProps): JSX.Element {
    const dataProvider = DataProvider(API_URL);

    return (
        <>
            <Head>
                <link rel="preconnect" href="https://app.snipcart.com" />
                <link
                    rel="stylesheet"
                    href="https://cdn.snipcart.com/themes/v3.0.16/default/snipcart.css"
                />
                <script
                    async
                    src="https://cdn.snipcart.com/themes/v3.0.16/default/snipcart.js"
                />
            </Head>
            <Refine
                routerProvider={routerProvider}
                dataProvider={dataProvider}
                resources={[{ name: "products" }]}
                Layout={Layout}
            >
                <ChakraProvider>
                    <Component {...pageProps} />
                </ChakraProvider>
            </Refine>
            <div hidden id="snipcart" data-api-key="YOUR_SNIPCART_TEST_KEY" />
        </>
    );
}

ProductCardコンポーネントの「バスケットに追加」ボタンの追加src/components/ProductCard.tsx:
import React from "react";
import { Box, Image, Badge, Button } from "@chakra-ui/react";

export type ProductProps = {
    id: string;
    title: string;
    description: string;
    cardImage: string;
};

export const ProductCard: React.FC<ProductProps> = ({
    id,
    title,
    description,
    cardImage,
}) => {
    return (
        <Box
            maxH={"sm"}
            maxW="sm"
            borderWidth="1px"
            borderRadius="lg"
            overflow="hidden"
        >
            <Image w={"100%"} h={200} src={cardImage} />
            <Box p="6" bgColor={"gray.600"}>
                <Box display="flex" alignItems="baseline" mb={2} ml={-2}>
                    <Badge borderRadius="full" px="2" colorScheme="teal">
                        New Product
                    </Badge>
                </Box>

                <Box
                    mt="1"
                    fontWeight="semibold"
                    as="h4"
                    lineHeight="tight"
                    isTruncated
                    color={"white"}
                >
                    {title}
                </Box>
                <Box
                    color="white"
                    fontSize="sm"
                    display={"flex"}
                    mt={4}
                    justifyContent={"flex-end"}
                >
                    <Button
                        className="buy-button snipcart-add-item"
                        bgColor={"green.400"}
                        data-item-id={id}
                        data-item-price="5"
                        data-item-url="/"
                        data-item-name={title}
                        data-item-description={description}
                        data-item-image={cardImage}
                    >
                        Add to Basket
                    </Button>
                </Box>
            </Box>
        </Box>
    );
};


結論
つのフレームワークから他のフレームワークを区別する最大の機能の一つは、それがカスタマイズ可能です.洗練されたヘッドレスと組み合わせると、今より多くのカスタマイズオプションを提供します.これはあなたが開発するプロジェクトの利便性を大いに提供します.
この記事でご覧のように、クライアントの一部を開発しましたAdmin Panel , 私たちは前にした、洗練された.リファインは、B 2 BとB 2 Cアプリケーションを任意の制限なしに開発し、完全にカスタマイズ可能な方法で機会を提供しています.
Refer to the Admin side of the project →
Source Code
Live CodeSandbox Example
Check out for detailed information about refine. →