خسته از هرچی که بود، خسته از هرچی که هست...
فرانتیوم امروز واقعاً از این وضعیت خسته شده بود! با خودش گفت: «چرا برای یه خرید ساده باید توی این گرمای تابستون، حتماً تا فروشگاه برم؟ اگه میتونستم خیلی راحت برم توی سایت، محصولم رو ببینم، آدرسم رو وارد کنم و تموم! چی میشد؟»
فرانتیوم فهمید که غر زدن فایده نداره. وقتشه خودش دستبهکار بشه و فروشگاه آنلاین خودش رو راه بندازه!

پروژهی اولیه
برای دانلود پروژهی اولیه روی این لینک کلیک کنید.
ساختار فایلها
little_shop
├─ api/
│ ├─ getAllProducts.js
│ └─ getProductById.js
├─ data/
│ └─ mockProducts.js
├─ images/
│ ├─ headphone.webp
│ ├─ laptop.jpg
│ └─ s25.png
├─ scripts/
│ ├─ checkout.js
│ ├─ index.js
│ └─ productDetail.js
├─ styles/
│ ├─ spinner.css
│ └─ style.css
├─ checkout.html
├─ index.html
├─ productDetail.html
└─ success.html
جزئیات پیادهسازی
در گیفهای زیر میتوانید فرایندهای مختلف برنامه را مشاهده کنید:
- صفحهی لیست محصولات

- صفحهی جزئیات محصول

- رفتن به صفحهی تکمیل پرداخت

- فرایند استپر

طبق توضیحات زیر، باید فایلهای موجود در دایرکتوری api را ایجاد و تکمیل کنید.
پیادهسازی دایرکتوری api
apiدر فایل mockProducts، یک آرایه از محصولات تعریف شده است.
پیادهسازی پرامیسها
در این بخش، باید در دایرکتوری api دو فایل جاوااسکریپت با نامهای زیر ایجاد کنید:
-
getAllProducts.js -
getProductById.js
پیادهسازی فایل getAllProducts
getAllProductsدر این فایل باید تابعی پیادهسازی شود که تمام محصولات را در صورت resolve شدن، بازگرداند. در صورتی که reject شود، باید شیئی شامل موارد زیر بازگردانده شود:
-
status: عدد401 -
data: آرایهای خالی[] -
message: یک رشته با محتوای زیر
محصولی یافت نشد، لطفا دوباره تلاش بکنید.
جزئیات پیادهسازی
-
این تابع باید با استفاده از Promise پیادهسازی شود.
-
برای شبیهسازی تاخیر پاسخ سرور، از
setTimeoutبا مدت زمان ۸۰۰ میلیثانیه استفاده کنید. -
درون این تاخیر، یک مقدار تصادفی با
Math.random()تولید میشود:- اگر مقدار تصادفی کمتر از
0.5باشد، Promise با دادههایmockProductsresolve شود. - در غیر این صورت، Promise با شیء خطا (دارای
status،dataوmessage) reject شود.
- اگر مقدار تصادفی کمتر از
پیادهسازی فایل getProductById
getProductByIdدر این فایل باید تابعی پیادهسازی شود که محصول مورد نظر را با استفاده از id همان محصول پیدا کند.
در صورت resolve شدن، محصول پیدا شده بازگردانده میشود.
در صورت reject شدن، باید شیئی شامل موارد زیر بازگردانده شود:
status: عدد404message: یک رشته با محتوای زیر:
محصول مورد نظر یافت نشد.
جزئیات پیادهسازی
-
این تابع باید با استفاده از Promise پیادهسازی شود.
-
برای شبیهسازی تاخیر پاسخ سرور، از
setTimeoutبا مدت زمان ۸۰۰ میلیثانیه استفاده کنید. -
درون این تاخیر، یک مقدار تصادفی با
Math.random()تولید میشود:- اگر مقدار تصادفی کمتر از
0.5باشد و محصولی باidمورد نظر پیدا شود، Promise با محصول مورد نظر resolve شود. - در غیر این صورت، Promise با شیء خطا (دارای
statusوmessage) reject شود.
- اگر مقدار تصادفی کمتر از
- نکته: این منطق به شما کمک میکند رفتار واقعی یک درخواست شبکه (موفق یا ناموفق بودن پاسخ سرور) را شبیهسازی کنید.
طبق توضیحات زیر، باید فایلهای موجود در دایرکتوری scripts را تکمیل کنید.
- توجه: در فایلها، کامنتهای راهنما درج شدهاند که به شما در تکمیل توابع کمک میکنند.
پیادهسازی فایل index.js
index.jsدر این فایل هدف شما این است که محصولات را از getAllProducts دریافت کرده، نمایش دهید و وضعیت بارگذاری و خطا را مدیریت کنید.
- ابتدا باید سه المنت را از صفحه
index.htmlدریافت کنید:
spinner: برای نمایش لودر در هنگام بارگذاری.errorBox: برای نمایش پیام خطا.productsContainer: برای نمایش لیست محصولات.
پیادهسازی تابع renderProducts
renderProductsاین تابع مسئول نمایش محصولات است.
ورودی تابع یک آرایه از محصولات است و خروجی آن افزودن کارتهای محصولات به productsContainer میباشد.
وظایف تابع:
-
برای هر محصول یک کارت محصول بسازید.
-
اطلاعات نمایش داده شده برای هر محصول شامل موارد زیر باشد:
-
تصویر (
image) با کلاسcard-img -
عنوان محصول (
title) -
توضیحات محصول (
description) -
قیمت محصول (
price) با فرمتtoLocaleString()و واحد تومان -
دستهبندی محصول (
category) -
وضعیت موجودی (
storage) با کلاسهایinstockیاoutstockو متن مناسب:-
موجود در انبار →
موجود در انبار -
ناموجود →
ناموجود
-
-
-
هر کارت باید روی کلیک کاربر، به صفحه جزئیات محصول (
productDetail.html) باidهمان محصول هدایت شود.
ساختار کارت محصول به شکل زیر است
<div class="card"">
<img src="${p.image}" alt="${p.title}" class="card-img"/>
<h2>${p.title}</h2>
<p>${p.description}</p>
<span class="price">قیمت: ${p.price.toLocaleString()} تومان</span>
<span class="category">دستهبندی: ${p.category}</span>
<span class="${p.storage ? "instock" : "outstock"}">
${p.storage ? "موجود در انبار" : "ناموجود"}
</span>
</div>
تابع loadProducts
این تابع مسئول مدیریت بارگذاری و نمایش خطا است و از async/await برای دریافت محصولات استفاده میکند.
وظایف تابع:
-
هنگام شروع بارگذاری:
-
نمایش
spinner→ حذف کلاسhidden -
مخفی کردن
errorBoxوproductsContainer→ افزودن کلاسhidden
-
-
فراخوانی تابع
getAllProducts()و دریافت آرایه محصولات -
نمایش محصولات با استفاده از
renderProducts -
پس از بارگذاری:
-
مخفی کردن
spinner -
اگر آرایه محصولات خالی بود → نمایش پیام خطا
"هیچ محصولی یافت نشد." -
در غیر این صورت → نمایش
productsContainer
-
-
مدیریت خطا:
-
مخفی کردن
spinner -
نمایش پیام خطا از
err.messageدرerrorBox
-
نکته: فراموش نکنید که تابع loadProducts را فراخوانی کنید!
نکته: در داخل تابع loadProducts باید از try/catch برای مدیریت خطاها استفاده شود.
فایل productDetail.js
productDetail.jsاین فایل مسئول نمایش جزئیات یک محصول است که با استفاده از شناسه (id) محصول، اطلاعات آن را از API دریافت کرده و در صفحه نمایش میدهد.
- ابتدا باید سه المنت را از صفحه
productDetail.htmlدریافت کنید:
spinner: برای نمایش لودر در هنگام بارگذاری.errorBox: برای نمایش پیام خطا.productDetail: برای نمایش محصول.
تابع getIdFromUrl
getIdFromUrlاین تابع مسئول استخراج شناسه محصول (id) از URL صفحه است.
تابع renderProduct
renderProductاین تابع وظیفه نمایش جزئیات محصول در صفحه را دارد.
وظایف تابع:
-
مخفی کردن
errorBoxو پاک کردن پیامهای خطا -
نمایش
productDetail -
افزودن محتوای محصول شامل:
- تصویر محصول (
product.image) با جایگزین پیشفرض"images/placeholder.png" - عنوان محصول (
product.title) - توضیحات محصول (
product.description) - قیمت محصول (
product.price.toLocaleString()) - دستهبندی محصول (
product.category) - وضعیت موجودی محصول (
product.storage)
- تصویر محصول (
-
افزودن دکمه خرید (
buy-btn) که با کلیک کاربر به صفحهcheckout.htmlبا همانidهدایت شود.
ساختار جزئیات هر محصول:
<img src="${product.image || "images/placeholder.png"}" alt="${
product.title
}" />
<h2>${product.title}</h2>
<p class="description">${product.description}</p>
<p><strong>قیمت:</strong> ${product.price.toLocaleString()} تومان</p>
<p><strong>دستهبندی:</strong> ${product.category}</p>
<p><strong>وضعیت:</strong> ${
product.storage ? "✅ موجود در انبار" : "❌ ناموجود"
}</p>
<button
class="buy-btn"
onclick="window.location.href='checkout.html?id=${encodeURIComponent(
product.id
)}'">
خرید محصول
</button>
تابع loadProduct
loadProductاین تابع مسئول مدیریت بارگذاری محصول و نمایش خطا است و از async/await استفاده میکند.
وظایف تابع:
-
هنگام شروع بارگذاری:
- نمایش
spinner - مخفی کردن
errorBoxوproductDetail - پاک کردن محتوای قبلی
productDetail
- نمایش
-
دریافت
idاز URL باgetIdFromUrl() -
فراخوانی
getProductById(id)برای دریافت محصول -
در صورت موفقیت، فراخوانی
renderProduct(product) -
در صورت خطا، نمایش پیام خطا در
errorBox -
در نهایت، مخفی کردن
spinner
نکته: فراموش نکنید که تابع loadProduct را فراخوانی کنید!
نکته: در داخل تابع loadProduct باید از try/catch برای مدیریت خطاها استفاده شود.
فایل checkout.js
checkout.js- این فایل مسئول مدیریت فرم چندمرحلهای پرداخت (Checkout) است و شامل منطق تغییر مراحل، اعتبارسنجی فیلدها و نمایش خلاصه سفارش است. المانهایی که باید با آنها کار کنید به شکل زیر است:
const steps = document.querySelectorAll(".step"); // مراحل پروگرس
const forms = document.querySelectorAll(".form-step"); // فرمهای هر مرحله
const nextBtns = document.querySelectorAll(".next"); // دکمههای مرحله بعد
const prevBtns = document.querySelectorAll(".prev"); // دکمههای مرحله قبل
const lines = document.querySelectorAll(".line"); // خطوط پروگرس
تابع getIdFromUrl
getIdFromUrlاین id برای دریافت اطلاعات محصول از API در مرحله خلاصه استفاده میشود.
- در صورت نبودن
idدر URL، اطلاعات محصول در مرحله خلاصه با علامت "—" نمایش داده میشود.
تابع showStep
این تابع مرحله جاری فرم را نمایش میدهد:
- فرم مربوط به مرحله جاری فعال میشود (
active) - مراحل تکمیل شده و خطوط پروگرس نیز فعال میشوند
- اگر مرحله آخر (Step 3) باشد، تابع
fillSummaryفراخوانی میشود
کلاسهای CSS:
| کلاس | کاربرد |
|---|---|
active |
نمایش فرم یا مرحله فعال |
line active |
رنگ و پر شدن خطوط پروگرس |
- نکته: مرحله بعد تنها زمانی قابل نمایش است که تمام ورودیهای مرحله جاری پر شده باشند.
اعتبارسنجی validateStep
validateStepتابع validateStep(index):
- بررسی همه ورودیها، سلکتها و تکستاریاهایی که
requiredهستند - اگر ورودی خالی باشد، کلاس
errorاضافه میشود و فوکوس روی آن قرار میگیرد - اگر ورودی پر باشد، کلاس
errorحذف میشود - اگر همه ورودیها پر باشند،
trueبرمیگرداند و اجازه رفتن به مرحله بعد داده میشود
کلاس CSS:
-
errorنمایش ورودی با خطا (مثلاً رنگ قرمز) -
توجه: کاربر نمیتواند به مرحله بعد برود تا زمانی که تمام فیلدهای الزامی پر شده باشند.
توضیح مراحل فرم
| مرحله | شرح |
|---|---|
| Step 1: اطلاعات شخصی | ورودیها: name، lastname، code، phone (تمامی فیلدها required) |
| Step 2: اطلاعات آدرس | ورودیها: province، city، address (تمامی فیلدها required) |
| Step 3: خلاصه خرید | نمایش اطلاعات جمعآوریشده از مراحل ۱ و ۲ و اطلاعات محصول از API. دکمه submit برای ثبت نهایی |
- ** توجه:** مرحله ۳ صرفاً نمایش اطلاعات است و ورودی جدیدی ندارد.
پر کردن خلاصه سفارش fillSummary
fillSummary-
اطلاعات شخصی و آدرس از فرمهای مراحل قبل خوانده میشوند
-
اطلاعات محصول با استفاده از
getProductByIdدریافت میشوند -
اطلاعات در المنتهای خلاصه (
summary-*) نمایش داده میشوند
مثال المنتها:
document.getElementById("summary-name").textContent = ...;
document.getElementById("summary-product").textContent = ...;
document.getElementById("summary-price").textContent = ...;
- توجه: در صورت خطای دریافت محصول، پیام مناسب نمایش داده شود.
دکمههای Next و Prev
-
دکمهی Next: ابتدا
validateStep(currentStep)اجرا میشود و فقط در صورت اعتبارسنجی موفق، به مرحله بعد میرود -
دکمهی Prev: بدون اعتبارسنجی به مرحله قبل برمیگردد
کلیک روی مراحل Stepper
- کاربر میتواند فقط به مراحل تکمیل شده یا فعلی برود
- اگر مرحله بالاتر را انتخاب کند، ابتدا اعتبارسنجی همه مراحل قبل انجام میشود
دکمهی submit
- بررسی اعتبارسنجی مرحله آخر
- در صورت موفقیت، هدایت به صفحه
success.html
مدیریت ورودیها
- هر ورودی با
requiredهنگام تغییر مقدار، کلاسerrorحذف میشود - جلوگیری از نمایش خطا پس از اصلاح ورودی توسط کاربر
کلاسهای مهم CSS
| کلاس | کاربرد |
|---|---|
active |
فرم یا مرحله فعال |
error |
ورودی اشتباه |
line active |
پر شدن خطوط پروگرس |
next / prev |
دکمههای ناوبری بین مراحل |
submit |
دکمه ثبت نهایی |
summary-* |
نمایش اطلاعات جمعآوری شده |
آنچه باید آپلود کنید
- توجه: پس از اعمال تغییرات، فایلهای داخل
scriptsوapiرا Zip کرده و آپلود کنید. همانند پروژه اولیه در فایل زیپ شده نباید کد در پوشهی دیگری قرار بگیرد در غیر این صورت سیستم داوری فایل را شناسایی نکرده و نمرهای دریافت نخواهید کرد.
ساختار مناسب برای آپلود پاسخ
answer/
├─ api/
│ ├─ getAllProducts.js
│ └─ getProductById.js
└─ scripts/
├─ checkout.js
├─ index.js
└─ productDetail.js
ارسال پاسخ برای این سؤال