これは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 サイトのコンテンツ作成と管理エクスペリエンスの生産性を向上させます。
ランディング ページには見出しタイトルが表示され、ショップの特徴がリストされ、ヒーロー画像が含まれます。
最初のショップセクションは衣料品に特化します。
2 番目のショップ セクションにはギアが含まれます。
各項目には、詳細を含む個別のプレビュー ページがあります。
すでにカートに入っている商品には、削除するオプションがあります。
カートには選択したすべてのアイテムがリストされ、合計が計算されます。
まず、ユーザーは新しいアカウントにサインアップする必要があります。これを行うには、OneEntry ホームページに移動し、電子メール アカウント経由でサインアップします。
その後、ログインすると、OneEntry ダッシュボードが表示されます。
新しいプロジェクトを作成することから始めます。
Study プランを 1 か月間使用するための無料コードを受け取ります。プロジェクトの作成プロセス中にアクティブ化する機会があります。
プロジェクトの作成には数分かかります。準備が完了すると、プロジェクトのステータスが「作業中」に変わり、ステータス インジケーターが緑色になります。
プロジェクトの作成後、CMS ポータルにアクセスしてアプリのデータを作成および保存するためのログイン詳細が記載された電子メールが届きます。
ログイン後、最初のページを作成できるようになります。
[コンテンツ管理] に移動し、[新しいページの作成] をクリックして、ページの種類、ページ タイトル、ページ ULR、メニュー項目の名前など、必要なデータをすべて入力します。
入力するとすべてのデータが自動的に保存されます。
ホーム、衣類、ギア、カートの 4 つの異なるページを作成します。作成すると、ページは以下のスクリーンショットのようになります。
次に、保存するデータ構造を作成する必要があります。 OneEntry CMS では、データの属性を作成することによってこれが実現されます。
「設定」に移動し、水平メニューで「属性」を選択します。名前、マーカー、タイプを指定するホームページの属性セットを作成します。
作成すると、以下のスクリーンショットのようになります。
同様に、衣類とギアに 2 つの個別の属性セットを作成してみましょう。作成すると、結果は以下のスクリーンショットのようになります。
次に、各セットに特定の属性を定義しましょう。
先ほどホーム セクションのワイヤーフレームに含めたコンテンツに基づいて、タイトル、説明、画像を表示したいと思います。
ホームの歯車アイテムをクリックし、以下のリストに示すように属性名、マーカー、および属性タイプを作成します。
次に、戻って「衣類」の歯車アイコンをクリックします。
製品のタイトル、サブタイトル、説明、画像、価格を表示したいため、このページの属性は少し異なります。
属性構造は次のようになります。
次に、同じ構造を使用する Gear ページに対して同じことを行います。
プロジェクトのこの段階では、コンテンツ構造がすでに定義されており、コンテンツ自体の作成を開始する準備ができています。
以前にサイトのすべてのページを作成したコンテンツ管理セクションに移動します。
ホームの編集ボタンをクリックします。その後、水平メニューの「属性」タブをクリックします。
属性セットとして「ホーム」を選択します。これにより、ホームページの設定で以前に作成したすべての属性がロードされます。
次に、ホームページに表示するサンプル データを入力します。
次に、衣類とギアのページにコンテンツを追加しましょう。
ページ タイプとしてカタログを選択したため、左側のメニューから [カタログ] を選択すると、両方のページがそこに表示されるはずです。
ここで、「衣類」の「追加」アイコンをクリックし、いくつかのアイテムを追加します。
まず、追加する製品のヘッダーを追加します。
次に、「属性」タブに切り替え、属性セットとして「衣類」を選択し、必要なデータを入力します。
カタログ メニューに戻り、衣類とギアの両方のアイテムをさらにいくつか表示します。デモ アプリでは、以下のスクリーンショットに示すように 4 つの項目を追加しました。
OneEntry CMS で作成されたデータはすべて保護されているため、アクセスできるようにするにはプライベート トークンを作成する必要があります。
これを行うには、[設定] に移動し、[アプリ トークン] を選択します。アプリ名と有効期限を入力し、「作成」をクリックします。これにより、一意の API キーが生成されます。
アクション リストの表示アイコンをクリックすると、キーが表示されます。チュートリアルの次のセクションで必要になるため、これをクリップボードにコピーします。
チュートリアルのこのセクションでは、コードの操作を開始し、OneEntry CMS で動作するように NextJS プロジェクトを構成します。
ターミナルを開き、コマンドnpx create-next-app@latest
を実行します。
CLI によりセットアップ ウィザードが開始されます。プロジェクトの名前を入力し、以下に示すようにデフォルト値をすべて選択します。
セットアップが完了するまで 1 分ほどかかります。NextJS アプリが作成されると通知が届きます。
その後、コマンドcd winter-sports
を使用してディレクトリを新しく作成したフォルダーに変更し、 npm run dev
を実行して開発者サーバーを起動します。
アクセスするには、ターミナルに表示されるリンクをクリックするか、Web ブラウザを開いてhttp://localhost:3000に手動で移動します。
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 のテキスト入力フィールドから受信したデータのテキスト レンダラーを定義しました。
次に、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.tsx
、 Footer.tsx
、 Text.tsx
、 Card.tsx
、 Preview.tsx
、 Order.tsx
、 AddToCart.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 の最高の機能を使用する方法についての洞察が得られれば幸いです。 。
ニュースレターを購読して、私が発見した最高のリソース、ツール、生産性のヒント、キャリア成長のヒントを必ず受け取ってください。
また、 Twitter 、 LinkedIn 、 GitHubで私とつながってください。
ここでも公開されています