paint-brush
NextJS、TypeScript、Tailwind、OneEntry CMS を使用して e コマース ストアを構築する方法 🛒👨‍💻@madzadev
1,553 測定値
1,553 測定値

NextJS、TypeScript、Tailwind、OneEntry CMS を使用して e コマース ストアを構築する方法 🛒👨‍💻

Madza2024/01/24
Read on Terminal Reader
Read this story w/o Javascript

長すぎる; 読むには

実践的な e コマース プロジェクトを構築して、NextJS、TypeScript、Tailwind CSS、OneEntry CMS がどのように連携するのか、またそれらを使用してコンテンツ管理を簡素化する方法を確認します。
featured image - NextJS、TypeScript、Tailwind、OneEntry CMS を使用して e コマース ストアを構築する方法 🛒👨‍💻
Madza HackerNoon profile picture
0-item

導入

これはOneEntry CMSのスポンサーによるパートナーシップ記事です。


e コマース アプリケーションの構築は、多くの場合、困難な作業です。利用可能な代替手段が非常に多いため、プロジェクトの要件、拡張性のニーズ、長期的な持続可能性を満たす技術スタックを選択するのは簡単ではありません。


もう 1 つの重要な点は、e コマース プロジェクトが大量のデータと CRUD 操作を扱うことです。堅牢でスケーラブルで安全なバックエンド システムを作成するには、経験豊富な開発者でも長い時間がかかることがあります。


私は、NextJS、TypeScript、Tailwind CSS、OneEntry CMS に基づく技術スタックを選択しました。私たちは実際的な e コマース プロジェクトを自分たちで構築し、それがどのように連携し、コンテンツ管理を簡素化するためにどのように使用できるかを確認します。


このプロジェクトのコードは、 GitHub リポジトリで入手できます。

技術スタックの選択

NextJS は、高速かつ効率的な Web アプリケーションを構築するための React フレームワークであり、クライアントとサーバーのレンダリング、データのフェッチ、ルート ハンドラー、ミドルウェア、組み込みの最適化などの機能が付属しています。


TypeScript はJavaScript に静的型付けを追加し、e コマースなどのスケーラブルなプロジェクトのエラーを見つけて修正しやすくします。また、オートコンプリートやリファクタリング支援などの機能により生産性も向上します。


Tailwind CSS は、Web アプリのスタイル部分を高速化し、開発者が外部 CSS ファイルを切り替えたり、それぞれのクラス名を考えたりすることなく、マークアップ内の要素をスタイル設定できるようにします。


OneEntry CMS は、使いやすいインターフェイス、簡単に拡張できるバックエンド、高速 API、わかりやすいドキュメントを備えたヘッドレス コンテンツ管理システムで、Web サイトのコンテンツ作成と管理エクスペリエンスの生産性を向上させます。

コンテンツとデザイン

ランディング ページには見出しタイトルが表示され、ショップの特徴がリストされ、ヒーロー画像が含まれます。


ECストアのホームページ


最初のショップセクションは衣料品に特化します。


ECストアの商品ページ


2 番目のショップ セクションにはギアが含まれます。


EC ストアの商品ページ #2


各項目には、詳細を含む個別のプレビュー ページがあります。


Eコマースストアの商品プレビューページ


すでにカートに入っている商品には、削除するオプションがあります。


E コマース ストアの製品プレビュー ページ #2


カートには選択したすべてのアイテムがリストされ、合計が計算されます。


Eコマースストアのカートページ

OneEntry プロジェクトの作成

まず、ユーザーは新しいアカウントにサインアップする必要があります。これを行うには、OneEntry ホームページに移動し、電子メール アカウント経由でサインアップします


OneEntry サインアップ ページ


その後、ログインすると、OneEntry ダッシュボードが表示されます。


新しいプロジェクトを作成することから始めます。


OneEntry ダッシュボード ページ


Study プランを 1 か月間使用するための無料コードを受け取ります。プロジェクトの作成プロセス中にアクティブ化する機会があります。


OneEntry プロジェクト ページ


プロジェクトの作成には数分かかります。準備が完了すると、プロジェクトのステータスが「作業中」に変わり、ステータス インジケーターが緑色になります。


OneEntry ダッシュボード ページ

ページの作成

プロジェクトの作成後、CMS ポータルにアクセスしてアプリのデータを作成および保存するためのログイン詳細が記載された電子メールが届きます。


OneEntry CMS ログイン ページ


ログイン後、最初のページを作成できるようになります。


[コンテンツ管理] に移動し、[新しいページの作成] をクリックして、ページの種類、ページ タイトル、ページ ULR、メニュー項目の名前など、必要なデータをすべて入力します。


OneEntry CMS - ページを編集する


入力するとすべてのデータが自動的に保存されます。


ホーム、衣類、ギア、カートの 4 つの異なるページを作成します。作成すると、ページは以下のスクリーンショットのようになります。


OneEntry CMS ページのリスト

属性の作成

次に、保存するデータ構造を作成する必要があります。 OneEntry CMS では、データの属性を作成することによってこれが実現されます。


「設定」に移動し、水平メニューで「属性」を選択します。名前、マーカー、タイプを指定するホームページの属性セットを作成します。


OneEntry CMS ページの種類


作成すると、以下のスクリーンショットのようになります。


OneEntry CMS ページ属性


同様に、衣類とギアに 2 つの個別の属性セットを作成してみましょう。作成すると、結果は以下のスクリーンショットのようになります。


OneEntry CMS 属性 #2


次に、各セットに特定の属性を定義しましょう。


先ほどホーム セクションのワイヤーフレームに含めたコンテンツに基づいて、タイトル、説明、画像を表示したいと思います。


ホームの歯車アイテムをクリックし、以下のリストに示すように属性名、マーカー、および属性タイプを作成します。


OneEntry CMS ページ設定


次に、戻って「衣類」の歯車アイコンをクリックします。


製品のタイトル、サブタイトル、説明、画像、価格を表示したいため、このページの属性は少し異なります。


属性構造は次のようになります。


OneEntry CMS ページ属性


次に、同じ構造を使用する Gear ページに対して同じことを行います。


OneEntry CMS ページ属性 #2

コンテンツの追加

プロジェクトのこの段階では、コンテンツ構造がすでに定義されており、コンテンツ自体の作成を開始する準備ができています。


以前にサイトのすべてのページを作成したコンテンツ管理セクションに移動します。


OneEntry CMS ページのリスト


ホームの編集ボタンをクリックします。その後、水平メニューの「属性」タブをクリックします。


OneEntry CMS 選択属性


属性セットとして「ホーム」を選択します。これにより、ホームページの設定で以前に作成したすべての属性がロードされます。


次に、ホームページに表示するサンプル データを入力します。


OneEntry CMS コンテンツの作成


次に、衣類とギアのページにコンテンツを追加しましょう。


ページ タイプとしてカタログを選択したため、左側のメニューから [カタログ] を選択すると、両方のページがそこに表示されるはずです。


OneEntry CMS カタログ


ここで、「衣類」の「追加」アイコンをクリックし、いくつかのアイテムを追加します。


まず、追加する製品のヘッダーを追加します。


OneEntry CMS カタログ ヘッダー


次に、「属性」タブに切り替え、属性セットとして「衣類」を選択し、必要なデータを入力します。


OneEntry CMS は製品を追加します


カタログ メニューに戻り、衣類とギアの両方のアイテムをさらにいくつか表示します。デモ アプリでは、以下のスクリーンショットに示すように 4 つの項目を追加しました。


OneEntry CMS 製品リスト

APIアクセストークンを作成する

OneEntry CMS で作成されたデータはすべて保護されているため、アクセスできるようにするにはプライベート トークンを作成する必要があります。


これを行うには、[設定] に移動し、[アプリ トークン] を選択します。アプリ名と有効期限を入力し、「作成」をクリックします。これにより、一意の API キーが生成されます。


OneEntry CMS - アプリトークンの作成


アクション リストの表示アイコンをクリックすると、キーが表示されます。チュートリアルの次のセクションで必要になるため、これをクリップボードにコピーします。


OneEntry CMS - アプリトークンのコピー

NextJS プロジェクトのセットアップ

チュートリアルのこのセクションでは、コードの操作を開始し、OneEntry CMS で動作するように NextJS プロジェクトを構成します。


ターミナルを開き、コマンドnpx create-next-app@latestを実行します。


CLI によりセットアップ ウィザードが開始されます。プロジェクトの名前を入力し、以下に示すようにデフォルト値をすべて選択します。


ターミナルの NextJS セットアップ ウィザード


セットアップが完了するまで 1 分ほどかかります。NextJS アプリが作成されると通知が届きます。


その後、コマンドcd winter-sportsを使用してディレクトリを新しく作成したフォルダーに変更し、 npm run devを実行して開発者サーバーを起動します。


アクセスするには、ターミナルに表示されるリンクをクリックするか、Web ブラウザを開いてhttp://localhost:3000に手動で移動します。


NextJS 開発者サーバーのランディング ページが表示されます。


NextJS 開発者サーバーのプレビュー


次に、アプリに必要な環境値を作成しましょう。コード エディターに戻り、プロジェクトのルートに.envファイルを作成します。


前にクリップボードにコピーした API キーを次のように貼り付けます。


 API_KEY=your-api-code-from-oneentry


これにより、API 呼び出しを行って OneEntry CMS からデータを取得すると、 process.env.API_KEY経由でキーにアクセスできるようになります。


また、外部ドメインからのメディアを含めることができるように NextJS を構成する必要もあります。これは、OneEntry CMS から画像にアクセスするために必要になります。


プロジェクト ルートにあるファイルnext.config.jsを開き、次のように編集します。


 const nextConfig = { images: { remotePatterns: [ { hostname: "ecommerce.oneentry.cloud", }, ], }, }; module.exports = nextConfig;


最後に、すべてのスタイルを最初から作成するため、アプリの Tailwind のデフォルト スタイルをリセットする必要があります。


srcフォルダー内のappディレクトリにあるglobals.cssファイルを開き、ファイルの内容を次のように変更します。


 @tailwind base; @tailwind components; @tailwind utilities;

タイプの作成

TypeScript を使用するので、アプリケーションで使用するデータ型を定義する必要があります。


ページやコンポーネント内でこれを行うこともできますが、コードをクリーンに保ち、繰り返しを避けるために、 appディレクトリ内に新しいフォルダーinterfacesを作成します。新しく作成したフォルダー内にファイルdata.tsxを作成し、コードを含めます。


 export interface Product { id: string; category: string; title: string; subtitle: string; description: string; image: string; price: number; } export interface ProductAPI { id: string; attributeValues: { en_US: { producttitle: { value: { htmlValue: string }[]; }; productsubtitle: { value: { htmlValue: string }[]; }; productdescription: { value: { htmlValue: string }[]; }; productimage: { value: { downloadLink: string }[]; }; productprice: { value: number; }; }; }; } export interface Page { pageUrl: string; title: string; description: string; image: string; localizeInfos: { en_US: { title: string; }; }; } export interface PageAPI { attributeValues: { en_US: { herotitle: { value: { htmlValue: string }[]; }; herodescription: { value: { htmlValue: string }[]; }; heroimage: { value: { downloadLink: string }[]; }; }; }; } export interface URLProps { params: { category: string; productId: string; }; } export interface TextProps { className: string; text: string; }


製品とページのデータは両方とも、フロントエンドのレンダリング データ構造の型と、フェッチ メソッドを介した API からの応答を持ちます。


また、URL パラメーターからのデータのデータ型と、CMS のテキスト入力フィールドから受信したデータのテキスト レンダラーを定義しました。

APIフェッチ関数の作成

次に、OneEntry CMS と通信してページと商品のデータを取得するために使用する関数をいくつか作成しましょう。


繰り返しますが、これを各ファイルで行うこともできますが、コードをわかりやすくするために、 appディレクトリ内に新しいフォルダーservices作成し、その中にファイルfetchData.tsxを作成しましょう。


 export async function getPages() { const response = await fetch( "https://ecommerce.oneentry.cloud/api/content/pages", { method: "GET", headers: { "x-app-token": `${process.env.API_KEY}`, }, } ); return await response.json(); } export async function getProducts(category: string) { const response = await fetch( `https://ecommerce.oneentry.cloud/api/content/products/page/url/${category}?limit=4&offset=0&langCode=en_US&sortOrder=DESC&sortKey=id`, { method: "GET", headers: { "x-app-token": `${process.env.API_KEY}`, }, } ); return await response.json(); } export async function getProduct(id: string) { const response = await fetch( `https://ecommerce.oneentry.cloud/api/content/products/${id}`, { method: "GET", headers: { "x-app-token": `${process.env.API_KEY}`, }, } ); return await response.json(); }


getPages関数は、OneEntry CMS で作成したすべてのページに関するデータを取得します。


getProducts関数は、 categoryパラメーターに基づいて製品の特定のコレクションのデータをフェッチします。関数を製品ページにインポートするときにパラメーターを渡します。


getProduct関数は、開いた製品のidに基づいてデータを取得します。特定の製品のプレビュー ページに関数をインポートするときにパラメーターを渡します。


process.env.API_KEYを使用して、OneEntry CMS へのアクセスを認証するために.envファイルで定義した API キーにアクセスしていることに注目してください。

ヘルパー関数の作成

また、まだservicesフォルダー内にいますが、その中にhelpers.tsxという名前の別の新しいファイルを作成しましょう。これには小さなユーティリティ関数が含まれます。


 export function calculateTotal(items: { price: number }[]) { return items.reduce((total, item) => total + Number(item.price), 0); } export function boughtStatus(items: { id: string }[], id: string) { return items.some((item) => item.id === id); } export function cartIndex(items: { id: string }[], id: string) { return items.findIndex((item) => item.id === id); }


calculateTotal関数は、カートに追加された製品の価格を合計し、合計値を返します。


boughtStatus 、プレビュー ルート内の個々のアイテムが既にカートに追加されているかどうかを検出します。


cartIndexは、カートに追加された製品の配列内の項目の位置を検出します。

コンポーネントの作成

appディレクトリに戻り、その中に新しいフォルダーcomponentsを作成します。


新しく作成したフォルダーを開き、その中に 7 つの個別のファイル ( Header.tsxFooter.tsxText.tsxCard.tsxPreview.tsxOrder.tsxAddToCart.tsx ) を含めます。


ヘッダーコンポーネント

ファイルHeader.tsxを開き、次のコードを含めます。


 import Link from "next/link"; import { Page } from "../interfaces/data"; export default function Header({ pages }: { pages: Page[] }) { return ( <div className="flex justify-between items-center mb-10 p-6"> <Link href="/"> <h1 className="text-xl">🏂 Alpine Sports</h1> </Link> <div className="flex space-x-4 list-none"> {pages.map((page, index: number) => ( <Link key={index} href={page.pageUrl === "home" ? "/" : `/${page.pageUrl}`} > {page.localizeInfos.en_US.title} </Link> ))} </div> </div> ); }

ヘッダーには会社名を表示し、コンポーネントがページにインポートされると API から取得するナビゲーション リンクをループします。


2 列のレイアウトを作成し、両方の要素を画面の反対側に水平に配置して、典型的なナビゲーションの外観を実現しました。


フッターコンポーネント

ファイルFooter.tsxを開き、次のコードを含めます。


 export default function Footer() { return ( <div className="text-center mt-auto p-6"> <h1>Alpine Sports, Inc.</h1> <p>All rights reserved, {new Date().getFullYear()}</p> </div> ); }


フッターには、会社のサンプル名と今年のコンテンツの権利を含めました。コンテンツを中央に配置し、パディングを追加しました。


テキストコンポーネント

Text.tsxファイルを開き、次のコードを含めます。


 import { TextProps } from "../interfaces/data"; export default function Text({ className, text }: TextProps) { return ( <div className={className} dangerouslySetInnerHTML={{ __html: text }} /> ); }


Text コンポーネントは、OneEntry CMS から受信したテキスト データをレンダリングし、HTML タグなしでアプリケーションに適切に表示します。


カードコンポーネント

Card.tsxファイルを開き、次のコードを含めます。


 import Link from "next/link"; import Text from "../components/Text"; import { Product } from "../interfaces/data"; export default function Card({ product }: { product: Product }) { return ( <Link href={`/${product.category}/${product.id}`}> <div className="group relative"> <div className="group-hover:opacity-75 h-80"> <img src={product.image} alt="Product card image" className="h-full w-full object-cover object-center" /> </div> <div className="mt-4 flex justify-between"> <div> <h3 className="text-sm text-gray-700"> <Text className="" text={product.title} /> </h3> <Text className="mt-1 text-sm text-gray-500" text={product.subtitle} /> </div> <p className="text-sm font-medium text-gray-900">${product.price}</p> </div> </div> </Link> ); }


カードコンポーネントには、各商品の画像、タイトル、サブタイトル、価格を表示しました。ページにインポートされたら、すべての項目をマッピングします。


画像はカードの上部に表示され、その後にタイトルと説明が表示され、コンポーネントの右下に価格が表示されます。


プレビューコンポーネント

ファイルPreview.tsxを開き、次のコードを含めます。


 "use-client"; import Image from "next/image"; import Text from "./Text"; import { Product } from "../interfaces/data"; export default function Preview({ children, productItem, }: { children: React.ReactNode; productItem: Product; }) { return ( <div className="flex mx-auto max-w-screen-xl"> <div className="flex-1 flex justify-start items-center"> <Image src={productItem.image} alt="Product preview image" width="450" height="900" /> </div> <div className="flex-1"> <Text className="text-5xl pb-8" text={productItem.title} /> <Text className="text-4xl pb-8 text-gray-700" text={`$${productItem.price}`} /> <Text className="pb-8 text-gray-500 text-justify" text={productItem.description} /> {children} </div> </div> ); }


プレビュー コンポーネントは、ユーザーがクリックすると各製品に関する詳細情報を表示するために使用されます。


商品画像、タイトル、価格、説明が表示されます。レイアウトは 2 つの列に分割され、画像が左の列に表示され、残りのコンテンツが右の列に表示されます。


注文コンポーネント

ファイルOrder.tsxを開き、次のコードを含めます。


 "use client"; import { useState, useEffect } from "react"; import Link from "next/link"; import Image from "next/image"; import Text from "./Text"; import { calculateTotal } from "../services/helpers"; import { Product } from "../interfaces/data"; export default function Order() { const [cartItems, setCartItems] = useState<Product[]>([]); useEffect(() => { const storedCartItems = localStorage.getItem("cartItems"); const cartItems = storedCartItems ? JSON.parse(storedCartItems) : []; setCartItems(cartItems); }, []); return ( <div> {cartItems.map((item, index) => ( <div key={index} className="flex items-center border-b border-gray-300 py-2" > <div className="w-20 h-20 mr-12"> <Image src={item.image} alt={item.title} width={80} height={80} /> </div> <div> <Link href={`/${item.category}/${item.id}`} className="text-lg font-semibold" > <Text className="" text={item.title} /> </Link> <Text className="text-gray-600" text={item.subtitle} /> <p className="text-gray-800">Price: ${item.price}</p> </div> </div> ))} <div className="mt-4 text-end"> <h2 className="text-xl font-semibold mb-8"> Total Amount: ${calculateTotal(cartItems)} </h2> <button className="bg-blue-500 hover:bg-blue-700 py-2 px-8 rounded"> Proceed to checkout </button> </div> </div> ); }


注文コンポーネントには、ユーザーがカートに追加したすべての商品がリストされます。各商品の画像、タイトル、サブタイトル、価格が表示されます。


コンポーネントがレンダリングされると、アプリは現在カート内にあるすべてのアイテムにアクセスし、それらをcardItems状態変数に設定し、 mapメソッドを介して画面にレンダリングします。


レンダリングされたアイテムの合計量は、 helpers.tsxファイルからインポートしたcalculateTotal関数によって計算されます。


カートに追加コンポーネント

ファイルAddToCart.tsxを開き、次のコードを含めます。


 "use client"; import React, { useState, useEffect } from "react"; import { boughtStatus, cartIndex } from "../services/helpers"; import { Product } from "../interfaces/data"; export default function AddToCart({ category, id, title, subtitle, image, price, }: Product) { const storedCartItems = JSON.parse(localStorage.getItem("cartItems") || "[]"); const isPurchased = boughtStatus(storedCartItems, id); const indexInCart = cartIndex(storedCartItems, id); const [btnState, setBtnState] = useState(false); useEffect(() => { isPurchased && setBtnState(true); }, []); const handleButtonClick = () => { const updatedCartItems = [...storedCartItems]; if (!btnState && !isPurchased) { updatedCartItems.push({ category, id, title, subtitle, image, price }); } else if (isPurchased) { updatedCartItems.splice(indexInCart, 1); } localStorage.setItem("cartItems", JSON.stringify(updatedCartItems)); setBtnState(!btnState); }; return ( <button className={`${ !btnState ? "bg-blue-500 hover:bg-blue-600" : "bg-yellow-300 hover:bg-yellow-400" } py-2 px-8 rounded`} onClick={handleButtonClick} > {!btnState ? "Add to Cart" : "Remove from Cart"} </button> ); }


addToCart コンポーネントは個々の製品プレビュー ページに表示され、ユーザーが製品をショッピング カートに追加できるようになります。


レンダリング時に、 isPurchased関数は、製品が既にカートに追加されているかどうかを検出します。レンダリングされたボタンではない場合は「カートに追加」と表示され、それ以外の場合は「カートから削除」と表示されます。


handleButtonClick関数のクリック機能は、上記の状態に応じて items 配列に製品を追加または削除します。

ページの作成

最後に、チュートリアルの前のセクションで作成したコンポーネントをインポートし、アプリケーションのページ ロジックを作成しましょう。


ホームページ

appディレクトリのpage.tsxを開き、その内容を次のように編集します。


 import Image from "next/image"; import Header from "./components/Header"; import Text from "./components/Text"; import Footer from "./components/Footer"; import { getPages } from "./services/fetchData"; import { PageAPI } from "./interfaces/data"; export default async function Home() { const pages = await getPages(); const getValues = (el: PageAPI) => { const { herotitle, herodescription, heroimage } = el.attributeValues.en_US; return { title: herotitle.value[0].htmlValue, description: herodescription.value[0].htmlValue, image: heroimage.value[0].downloadLink, }; }; const pageContent = getValues(pages[0]); return ( <div className="flex flex-col min-h-screen"> <Header pages={pages} /> <div className="flex flex-row mx-auto max-w-screen-xl"> <div className="flex-1"> <Text className="text-6xl pb-10 text-gray-900" text={pageContent.title} /> <Text className="text-xl pb-8 text-gray-500 text-justify" text={pageContent.description} /> </div> <div className="flex-1 flex justify-end items-center"> <Image src={pageContent.image} alt="Photo by Karsten Winegeart on Unsplash" width={450} height={900} /> </div> </div> <Footer /> </div> ); }


ホーム ページで、まずgetPages関数を呼び出してヘッダーのデータを取得します。


次に、 getValues関数を使用してヒーロー ページ データを取得し、処理を容易にするためにそれらをpageContentオブジェクトに変換します。


次に、インポートされたヘッダーとフッターのコンポーネントをレンダリングし、ヒーローのタイトル、説明、画像に必要な値を渡します。


製品ページ

appディレクトリに新しいフォルダー[category]を作成し、その中にファイルpage.tsxを作成します。


特定のファイル名を使用することは、NextJS がルートを処理し、URL パラメーターにアクセスするために使用するものであるため、重要です。


次のコードをpage.tsxに含めます。


 import Header from "../components/Header"; import Footer from "../components/Footer"; import Card from "../components/Card"; import { getPages, getProducts } from "../services/fetchData"; import { ProductAPI, URLProps } from "../interfaces/data"; export default async function Product({ params }: URLProps) { const { category } = params; const pages = await getPages(); const products = await getProducts(category); const getValues = (products: ProductAPI[]) => { return products.map((el) => { const { producttitle, productsubtitle, productdescription, productimage, productprice, } = el.attributeValues.en_US; return { id: el.id, category: category, title: producttitle.value[0].htmlValue, subtitle: productsubtitle.value[0].htmlValue, description: productdescription.value[0].htmlValue, image: productimage.value[0].downloadLink, price: productprice.value, }; }); }; const productItems = getValues(products.items); return ( <div className="flex flex-col min-h-screen"> <Header pages={pages} /> <div className="mx-auto max-w-screen-xl px-8"> <h2 className="text-4xl text-gray-900 mb-12"> Browse our {category} collection: </h2> <div className="grid gap-x-6 gap-y-10 grid-cols-4 mt-6"> {productItems.map((product) => { return <Card key={product.id} product={product} />; })} </div> </div> <Footer /> </div> ); }


製品ページの場合は、まず URL からcategoryパラメーターを取得します。これをさらにgetProducts関数に渡し、サイトのどのページにアクセスしたかに基づいて製品のどのカテゴリーを取得する必要があるかを記述します。


データを受信したら、処理を容易にするためにページに必要なすべての属性で構成されるオブジェクトの配列productItemsを作成します。


次に、 mapメソッドを介してループし、 componentフォルダーからインポートした Card コンポーネントに props を渡すことで、画面にレンダリングします。


プレビューページ

[category]フォルダー内に、 [productId]という名前の別のフォルダーを作成します。


新しく作成したフォルダーを開き、その中に次のコードを含むファイルpage.tsxを作成します。


 import Header from "../../components/Header"; import Preview from "../../components/Preview"; import AddToCart from "../../components/AddToCart"; import Footer from "../../components/Footer"; import { getPages, getProduct } from "../../services/fetchData"; import { ProductAPI, URLProps } from "../../interfaces/data"; export default async function Product({ params }: URLProps) { const { category, productId } = params; const pages = await getPages(); const product = await getProduct(productId); const getValues = (el: ProductAPI) => { const { producttitle, productsubtitle, productdescription, productimage, productprice, } = el.attributeValues.en_US; return { id: el.id, category: category, title: producttitle.value[0].htmlValue, subtitle: productsubtitle.value[0].htmlValue, description: productdescription.value[0].htmlValue, image: productimage.value[0].downloadLink, price: productprice.value, }; }; const productItem = getValues(product); return ( <div className="flex flex-col min-h-screen"> <Header pages={pages} /> <div className="flex mx-auto max-w-screen-xl"> <div className="flex-1 flex justify-start items-center"> <Preview productItem={productItem}> <AddToCart id={productId} category={category} title={productItem.title} subtitle={productItem.subtitle} description={productItem.description} image={productItem.image} price={productItem.price} /> </Preview> </div> </div> <Footer /> </div> ); }


このページでは、ユーザーが製品ページでカードをクリックすると、個々の製品の詳細を表示できます。


まず URL からproductIdパラメーターを取得し、さらにgetProduct関数に渡して、プレビュー ページで表示される製品に基づいて取得する必要がある製品を指定します。


データを受信したら、Preview コンポーネントに小道具として渡す必要なすべての属性で構成されるオブジェクトproductItemを作成します。


また、 categoryパラメーターも取得します。これは、カート ページにアイテムの有効なリンクを作成できるように、「カートに追加」コンポーネントに渡す必要があるためです。


カートページ

最後に、 appディレクトリに新しいフォルダーcartを作成します。


それを開き、その中に次のコードを使用して新しいファイルpage.tsxを作成します。


 import Header from "../components/Header"; import Order from "../components/Order"; import Footer from "../components/Footer"; import { getPages } from "../services/fetchData"; export default async function Cart() { const pages = await getPages(); return ( <div className="flex flex-col min-h-screen"> <Header pages={pages} /> <div className="container mx-auto max-w-screen-xl px-8"> <h2 className="text-4xl text-gray-900 mb-12">Shopping cart summary:</h2> <Order /> </div> <Footer /> </div> ); }


まず必要なデータを取得し、それを小道具としてヘッダーに渡します。


次に、ナビゲーションを含む Header コンポーネント、ユーザーがカートに追加したすべてのアイテムをリストする Order コンポーネント、そして会社名と著作権情報を含む Footer コンポーネントをレンダリングしました。

テスト

おめでとうございます。機能するプロジェクトが作成されました。


まず、開発者サーバーがまだ実行されているかどうかを確認します。そうでない場合は、 npm run devコマンドを実行して再度起動し、 localhost:3000にアクセスして表示します。


プロジェクトは次のようになっているはずです。


最終プロジェクトのウォークスルー


ご覧のとおり、ホーム セクションのコンテンツは、データ フィールドで指定したホーム属性セットから正常にフェッチされました。


また、OneEntry CMS カタログのすべてのアイテムは、すべての情報が適切に表示された状態で、衣類およびギアのセクションに取得されています。


NextJS のルート処理と製品パラメーターのおかげで、ユーザーは専用ページで各製品を個別にプレビューすることもできます。


また、すべての機能とイベントは期待どおりに動作し、ユーザーはショッピング カートに商品を追加したり、ショッピング カートから商品を削除したりすることができ、合計が計算されます。

結論

このチュートリアルでは、 OneEntry CMSのおかげで、ユーザーが Web サイト ページとそのコンテンツを作成、更新、削除できるだけでなく、使いやすいカタログ インターフェイスで製品を簡単に管理できる e コマース プロジェクトを作成しました。


コードはGitHubで入手できるので、ニーズに合わせて自由に複製して機能を追加してください。さらにメニュー セクションを追加したり、個々のコンポーネントを拡張したり、さらにコンポーネントを追加して新しい機能を実装したりすることもできます。


これが役に立ち、OneEntry CMS をバックエンド ソリューションとして使用する方法、それをアプリケーションのフロントエンドと組み合わせる方法、NextJS、Typescript、Tailwind の最高の機能を使用する方法についての洞察が得られれば幸いです。 。


ニュースレターを購読して、私が発見した最高のリソース、ツール、生産性のヒント、キャリア成長のヒントを必ず受け取ってください。


また、 TwitterLinkedInGitHubで私とつながってください。


ここでも公開されています