اسکرول


اسکرول🔗

توضیح تصویر

پروژه‌ی اولیه🔗

فایل پروژه را می‌توانید از طریق این این لینک دانلود کنید.

ساختار پروژه به شکل زیر است:

initital_project/
├─ index.html
└─ styles.css
Plain text

جزئیات پیاده‌سازی🔗

  1. حرکت از یک سکشن به سکشنِ دیگر باید حتماً به‌صورت نرم انجام شود.
  2. کلاس dot را طوری بنویسید که :
    • طول و عرض آن 12 پیکسل باشد.
    • رنگ بک‌گراند آن gray باشد.
    • کاملا به صورت دایره کامل دیده بشود.
    • دیسپلی آن block باشد.
  3. کلاس dot-navigation را طوری بنویسید که :
    • پوزیشن آن کاملا ثابت باشد.
    • از بالا فاصله 50 درصدی داشته باشد.
    • از راست 20 پیکسل فاصله داشته باشد.
    • دیسپلی آن فلکس باشد.
    • مسیر آن ستونی باشد.
    • فاصله آیتم‌های داخل آن از هم 10 پیکسل باشد.
  4. توجه داشته باشید که نباید در صفحه، اسکرولِ افقی مشاهده کنیم.
  5. اسنپ‌اسکرول باید کاملاً به‌صورت عمودی پیاده‌سازی بشود.
  6. کاری کنید که هر سکشن با شروعِ اسکرول، از بالا شروع شود.
  7. نوعِ snap scroll باید به‌گونه‌ای تنظیم شود که در جهتِ عمودی باشد و مرورگر به نزدیک‌ترین نقطه‌ی snap بچسبد.

نکات🔗

  1. در فایل styles.css پس از کامنت کدهای خود را بنویسید.
  2. در نهایت باید چنین نتیجه‌ای داشته باشیم، 3 صفحه به همین شکل که مثل ویدئو بالا به نرمی اسکرول میخورند و با کلیک بر روی نقاط به سکشن های متفاوت میریم :

توضیح تصویر

آنچه باید آپلود کنید🔗

فایلی که آپلود خواهید کرد باید دارای فرمت .zip باشد و دارای ساختار زیر باشد:

answer/
└─ styles.css
Plain text

موفق باشید 🌟

پورت


پورت🔗

توضیح تصویر

پروژه‌ی اولیه🔗

فایلِ پروژه را می‌توانید از طریق این این لینک دانلود کنید.

ساختار پروژه به شکلِ زیر است:

initial_project/
├─ index.html
├─ main.js
└─ styles.css
Plain text

جزئیات پیاده‌سازی🔗

  1. همان‌طور که مشاهده می‌کنید، همه‌ی پورت‌ها شامل یک مقدار مشخص هستند که در فایلِ index.html می‌توانید تگ‌‌های مربوطه برا ببینید.

  2. در کنارِ هر عدد، یک آیکونِ ویرایش (همان مداد) مشاهده می‌کنید که وقتی روی آن کلیک می‌کنیم، تگی که مقدار در آن ذخیره شده بود باید از دیدِ کاربر محو شده و به‌جای آن، یک input نمایش داده شود که مقدارِ آن، همان مقدارِ موجود در تگِ مربوطه بوده؛ همچنین یک دکمه‌ی «ثبت» هم در زیرِ هر input قرار دارد.

  3. حالا با وارد کردنِ مقدارِ جدید در input و زدنِ دکمه‌ی ثبت، input محو شده و دوباره همان تگی که وظیفه‌ی نمایش مقدار را داشت، نمایش داده می‌شود.

نکات🔗

  1. شما صرفا باید در فایل main.js تغییرات ایجاد کرده و کد بنویسید.
  2. هر باکس، اطلاعاتِ مخصوصِ خودش را دارد.
  3. در نظر داشته باشید که هر input فقط یک‌بار باز می‌شود.
  4. تگ‌های اچ‌تی‌ام‌ای دارای آیدی با نام‌های واضح و مفهومی هستند.

آنچه باید آپلود کنید🔗

فایلی که آپلود خواهید کرد باید دارای فرمت .zip باشد و دارای ساختارِ زیر باشد:

answer/
└─ main.js
Plain text

موفق باشید 🌟

ریسپانسیو


ریسپانسیو🔗

توضیح تصویر

پروژه‌ٔ اولیه🔗

فایل پروژه را می‌توانید از طریق این این لینک دانلود کنید.

ساختار پروژه به شکل زیر است:

initial_project/
├─ index.html
└─ styles.css
Plain text

جزئیات پیاده‌سازی🔗

بخش: flex-section🔗

ویژگی > 800px ≤ 800px
display - flex
flex-direction - column
gap 3px 10px
padding 8px 5px
flex 1 1

توضیح تصویر

توضیح تصویر

بخش: box1🔗

ویژگی > 800px ≤ 800px
display flex flex
flex-direction - column
justify-content center center
align-items center center

بخش: .box1 .sub-box (بالای 800px)🔗

بخش ویژگی مقدار
فرزند اول flex 1
- height 300px
فرزند دوم flex 1
- height 200px

بخش: .box1 .sub-box1 ( زیر 800px)🔗

ویژگی مقدار
background-color rgb(255, 0, 0)
border-radius 12px
width 250px

بخش: .box1 .sub-box2 (زیر 800px)🔗

ویژگی مقدار
background-color rgb(0, 0, 255)
border-radius 10px
width 150px

توضیح تصویر

بخش: box2🔗

ویژگی > 800px ≤ 800px
display flex flex

بخش: .left-nested-box و .right-nested-box🔗

ویژگی > 800px ≤ 800px
padding 10px / 20px همان مقدار
display flex همان مقدار
flex-direction column همان مقدار
gap 10px / 2px همان مقدار
border-radius فقط در ≤800px (8px برای همه عناصر) 8px

توضیح تصویر

بخش: box3🔗

ویژگی > 800px ≤ 800px
display flex flex
flex-direction - column

بخش: .box3 .small-box🔗

موقعیت در DOM ویژگی > 800px ≤ 800px
همه .small-box flex 1 1
فرزند اول width 50px
- border-radius 5px
- background #4682b4
فرزند سوم width 200px
- border-radius 20px
- background #4682b4

بخش: big-box🔗

ویژگی > 800px ≤ 800px
flex 2 1
height 169px -
width - 100px
border-radius - 10px
background - #ffa07a

توضیح تصویر

بخش: grid-section🔗

ویژگی > 800px ≤ 800px
تعداد ستون های هر سطح 1 4

توضیح تصویر

توضیح تصویر

بخش: hamburger و nav-links🔗

کلاس > 800px ≤ 800px
hamburger معلوم نباشد معلوم باشد
nav-links معلوم باشد معلوم نباشد

توضیح تصویر

توضیح تصویر

نکات پیاده‌سازی🔗

  1. در بالا هم ویدئو و هم تصویر 2 حالت تمام سکشن‌ها موجود است.
  2. شما باید طبق اطلاعاتی که در جداول وجود دارد استایل‌دهی کنید.

آنچه باید آپلود کنید🔗

فایلی که آپلود خواهید کرد باید دارای فرمت .zip باشد و دارای ساختار زیر باشد:

answer/
└─ styles.css
Plain text

موفق باشید 🌟

کارت محصول


کارت محصول🔗

توضیح تصویر

پروژه‌ٔ اولیه🔗

فایل پروژه را می‌توانید از طریق این این لینک دانلود کنید.

ساختار پروژه به شکل زیر است:

initial-project/
├─ api/
│  └─ getProduct.js
├─ assets/
│  ├─ spinner.svg
│  └─ watch.png
├─ js/
│  └─ cardAnimation.js
├─ index.html
├─ main.js
└─ style.css
Plain text

جزئیات پیاده‌سازی🔗

  1. در فایل getProduct.js، تابعی با همین نام وجود دارد که یک Promise برمی‌گرداند، که شامل مشخصات محصولات است.
  2. تابع showToast را باید با هدف نمایش toaster به کاربر کامل کنید. تگ مورد نظر که دارای آیدی toast است را از فایل HTML به‌دست بیاورید.
  • توجه داشته باشید ۳ ثانیه پس از نمایش toaster، باید از بین برود.
  1. تابع renderProduct باید اطلاعات محصول را در فرمت زیر و در تگی که دارای آیدی product-card است قرار دهد:
<div class="card-header">
  <img class="product-image" src="<!-- آدرس عکس -->" alt="<!-- آدرس عکس -->" />
  <div class="badge <!-- کلاس بر اساس وضعیت موجودی -->">
    <!-- وضعیت موجودی محصول -->
  </div>
</div>
<div class="card-body">
  <h2 class="product-title"><!-- عنوان محصول --></h2>
  <p class="product-brand">
    برند:
    <!-- برند محصول -->
  </p>
  <p class="product-category">
    دسته‌بندی:
    <!-- دسته‌بندی محصول -->
  </p>
  <p class="product-price"><!-- قیمت محصول --></p>
  <p class="product-description"><!-- توضیحات محصول --></p>
  <div class="product-rating">
    <span><!-- امتیاز محصول -->
    </span>
    |
    <span>
      فروش:
      <!-- فروش محصول -->
      عدد
    </span>
  </div>
</div>
HTML

تصویر محصول کامل به شکل زیر است :

توضیح تصویر

  1. در تابع renderEror در صورتی که دریافت اطلاعات محصول با مشکل مواجه شد، محتوای داخلی product-card به این شکل خواهد بود:
    <p style="color: var(--error-color); font-weight: bold;">خطا در بارگذاری اطلاعات</p>
    <button id="retry-btn" class="retry-btn">تلاش مجدد</button>
    HTML
  • همچنین دکمه‌ای با آیدی retry-btn وجود دارد که باید برای آن یک event کلیک تعریف کنید تا با کلیک روی آن، سعی دوباره برای دریافت اطلاعات انجام شود (تابع نوشته‌شده برای این کار را call کنید).
  1. تابع loadProduct را باید طوری کامل کنید، که هنگام لودینگ، محتوای زیر در product-card نمایش داده شود:
    <img src="assets/spinner.svg" alt="در حال بارگذاری..." class="spinner">
    Plain text

همچنین در این تابع باید تلاش برای دریافت اطلاعات انجام شود. در صورت موفقیت، باید renderProduct را call کنید و در صورت شکست، showToast را نمایش دهید و همچنین renderError را فراخوانی کنید.

  1. در پایان، هم تابع اصلی که تمام فرایند ها را به درستی اجرا می‌کند فراخوانی

نکات🔗

  1. در صورت نیاز برای نمایش {کارت، toaster , ... } از کلاس show و برای محو کردن آن‌ها از کلاس hidden که در فایل CSS از قبل نوشته شده‌اند استفاده بکنید.
  2. در بخش جزئیات پیاده‌سازی، یک‌سری <!-- --> کامنت HTML را مشاهده می‌کنید که باید در آن قسمت‌ها اطلاعات مربوط به محصول را قرار دهید.
  3. متن ارور باید در toaster نمایش داده شود.
  4. برای استفاده از getProduct.js از import استفاده کنید.
  • اگر با خطا رو به رو شویم :

توضیح تصویر

  • اگر محصول موجود یا ناموجود باشد :

توضیح تصویر

آنچه باید آپلود کنید🔗

فایلی که آپلود خواهید کرد باید دارای فرمت .zip باشد و دارای ساختار زیر باشد:

answer/
├─ main.js
Plain text

موفق باشید 🌟

جدول‌ اطلاعات


جدول‌ اطلاعات🔗

توضیح تصویر

پروژه‌ٔ اولیه🔗

فایل پروژه را می‌توانید از طریق این این لینک دانلود کنید.

ساختار پروژه به شکل زیر است:

initial_project/
├─ js/
│  └─ data.js
├─ index.html
├─ main.js
└─ styles.css
Plain text

در فایل data.js، تابعی وجود دارد که اطلاعات لیستی از ماشین‌ها در آن موجود است.

جزئیات پیاده‌سازی🔗

۱. اینپوت، سلکتور و تیبل را می‌توانید از طریق آیدی‌های زیر بدست آورید:

  • searchInput
  • yearFilter
  • carsTable

۲. این خط را مشاهده کنید:

fetchCarsData().then()
JavaScript

با ادامه دادن آن، اطلاعات ماشین‌ها را به‌دست آورید.

۳. تابع renderCars را به‌گونه‌ای پیاده‌سازی کنید که:

  • اگر لیست خالی بود، emptyListString به‌عنوان محتوای بدنهٔ تیبل نمایش داده شود.

توضیح تصویر

  • اگر لیست شامل آیتم بود، برای هر آیتم یک ردیف جدول با ۴ ستون زیر نمایش داده شود:
<td><!-- نام ماشین --></td>
<td><!-- مدل ماشین --></td>
<td><!-- سال ساخت ماشین --></td>
<td><!-- رنگ ماشین --></td>     
HTML
  1. تابع createYearFilterList را طوری کامل کنید که :
  • منوی کشویی سال، بر اساس داده‌های ماشین‌ها ساخته شود.
  • داده‌های ماشین شامل سال ساخت است.
  • سال‌ها به‌صورت نزولی مرتب شوند

توضیح تصویر

توضیح تصویر

۵. تابع applyFilters را طوری کامل کنید که:

  • بر اساس محتوای سرچ‌شده یا سال انتخاب‌شده یا ترکیب هر دو، محتوای تیبل را فیلتر کند.
    searchInput.addEventListener("input");
    JavaScript
  • ادامه این ایونت را به‌گونه‌ای بنویسید که ۲.۵ ثانیه پس از توقف تایپ کاربر، عملیات فیلتر انجام و تیبل آپدیت شود.
  1. این خط را مشاهده کنید :
    yearFilter.addEventListener("change");
    JavaScript
  • ادامه این ایونت را به‌گونه‌ای بنویسید که ۱ ثانیه پس از انتخاب سال، عملیات فیلتر انجام و تیبل آپدیت شود.

نکات🔗

۱. هر زمان که کاربر شروع به تایپ کرد یا سالی را انتخاب نمود، فارغ از بازه‌های زمانی، باید فوراً اسپینر لودینگ با استفاده از تابع showSpinner نمایش داده شود. ۲. بازه‌های زمانی ذکرشده حتماً باید رعایت شوند. در غیر این صورت، هدف برنامه محقق نخواهد شد. ۳. تغییرات فیلترها فقط باید در بدنهٔ تیبل اعمال شوند.

آنچه باید آپلود کنید🔗

فایلی که آپلود خواهید کرد باید دارای فرمت .zip باشد و دارای ساختار زیر باشد:

answer/
└─ main.js
Plain text

موفق باشید 🌟

احراز هویت


احراز هویت🔗

توضیح تصویر

پروژه‌ٔ اولیه🔗

فایل پروژه را ‌می‌توانید از طریق این این لینک دانلود کنید.

ساختار پروژه به شکل زیر است:

initital_project/
├─ css/
│  └─ styles.css
├─ app.js
├─ dashboard.html
├─ index.html
├─ login.html
└─ register.html
Plain text

جزئیات پیاده‌سازی🔗

اقدامات لازم برای app.js🔗

  1. همان‌طور که مشاهده می‌کنید، در ابتدای فایل کامنتی با عنوان Validation و در پایین آن تابعی با نام isAuthentication وجود دارد که شما باید آن را طوری کامل کنید که بررسی کند کاربر احراز هویت (authentication) را انجام داده است یا خیر. پس باید مقداری را بر اساس مقادیر موجود در لوکال‌استورج به ما برگرداند.

تابع registerUser:🔗

  1. ورودی این تابع یک آبجکت به نام user است که باید دارای سه مقدار باشد: username، password و role.
  2. این تابع یک Promise برمی‌گرداند که ممکن است با موفقیت اجرا شود (resolve) یا با خطا مواجه شود (reject)، بنابراین هر دو حالت باید در نظر گرفته شوند.
  3. در تابع، مقدار username را پاک‌سازی و استاندارد می‌کنیم تا مقایسه‌ای دقیق و یکسان برای بررسی تکراری بودن انجام شود.
  4. سپس لیست کاربران موجود از localStorage خوانده می‌شود و در آرایه‌ای به نام users قرار می‌گیرد. اگر مقدار موجود در localStorage آرایه نبود یا خطایی رخ داد، آرایه خالی در نظر گرفته می‌شود.
  5. بررسی می‌شود که آیا کاربری با همین username (پس از پاک‌سازی) قبلاً ثبت شده یا خیر. اگر چنین کاربری وجود داشت، عملیات با پیام زیر رد می‌شود: "این نام کاربری قبلاً ثبت شده. لطفاً نام دیگری انتخاب کنید."
  6. اگر نام کاربری تکراری نبود، اطلاعات کاربر جدید (شامل username، password و role) به آرایه users اضافه می‌شود.
  7. سپس آرایه به‌روزرسانی‌شده دوباره در localStorage ذخیره می‌شود. در صورتی که عملیات ذخیره‌سازی با خطا مواجه شود، تابع با پیام زیر رد می‌شود:

"خطا در ذخیره‌سازی. دوباره تلاش کنید."

  1. در نهایت، اگر همه مراحل با موفقیت انجام شد، Promise با موفقیت (resolve) پایان می‌یابد.

توضیح تصویر

توضیح تصویر

تابع loginUser🔗

  1. ورودی تابع یک آبجکت به نام creds است که شامل مقادیر username و password از loginForm می‌باشد.
  2. خروجی تابع، یک پرامیس است که در صورت موفقیت، آبجکتی شامل سه مقدار accessToken، refreshToken و role را بازمی‌گرداند. این مقادیر باید در localStorage ذخیره شوند.
  3. مانند تابع ثبت‌نام (registerUser)، در ابتدا باید username را پالایش کنید (حذف فاصله‌ها و تبدیل به حروف کوچک). سپس لیست کاربران (users) از localStorage خوانده می‌شود. در صورت بروز خطا یا نبود لیست معتبر، یک آرایه خالی جایگزین می‌شود.
  4. سپس بررسی می‌کنید که آیا کاربری با username برابر و password منطبق وجود دارد یا خیر. اگر چنین کاربری یافت نشد، reject با پیام زیر صدا زده می‌شود و تابع خاتمه می‌یابد: "نام کاربری یا رمز عبور اشتباه است."
  5. در صورت تطابق اطلاعات، باید مقادیر accessToken و refreshToken را با این فرمت تولید کنید:
    const accessToken = "access-" + Date.now() + "-" + Math.random().toString(36).substr(2);
    const refreshToken = "refresh-" + Date.now() + "-" + Math.random().toString(36).substr(2);
    JavaScript
  6. سپس این مقادیر به همراه role کاربر، در localStorage ذخیره می‌شوند.
  7. مرحله ۶ باید در یک بلوک try/catch انجام شود. در صورت بروز خطا هنگام ذخیره‌سازی، تابع با reject و پیام زیر خاتمه می‌یابد: "خطا در فرآیند ورود. دوباره تلاش کنید."
  8. در نهایت، اگر تمام مراحل با موفقیت انجام شود، resolve فراخوانی می‌شود و آبجکت زیر بازگردانده می‌شود:
    { accessToken, refreshToken, role: found.role }
    JavaScript

توضیح تصویر

توضیح تصویر

رویداد سابمیت برای LoginForm🔗

  1. ابتدا باید از رفتار پیش‌فرض فرم جلوگیری شود تا از بارگذاری مجدد صفحه جلوگیری شود.
  2. مقادیر ورودی مربوط به نام کاربری (loginUsername) و رمز عبور (loginPassword) از عناصر HTML دریافت می‌شوند.
  3. بررسی می‌شود که هر دو فیلد مقدار داشته باشند. در صورتی که یکی از آن‌ها خالی باشد، پیام خطای «لطفاً همهٔ فیلدها را کامل کنید.» با استفاده از تابع showToast نمایش داده شده و ادامه‌ی اجرای تابع متوقف می‌شود.
  4. اگر هر دو فیلد مقدار داشته باشند، یک شیء شامل username و password ساخته شده و به تابع loginUser ارسال می‌شود.
  5. در صورتی که عملیات ورود موفقیت‌آمیز باشد، پیام «ورود با موفقیت انجام شد!» با استفاده از showToast نمایش داده می‌شود و پس از گذشت یک ثانیه کاربر به صفحه‌ی dashboard.html هدایت می‌شود.
  6. اگر عملیات ورود با شکست مواجه شود، پیام خطای مربوطه از طریق showToast(err, "error") نمایش داده می‌شود.

رویداد سابمیت برای RegisterForm🔗

فرایند ثبت‌نام بسیار مشابه ورود است با تفاوت‌هایی جزئی:

  1. مقدارهای نام کاربری (registerUsername)، رمز عبور (registerPassword) و نقش (role) از عناصر HTML گرفته می‌شوند.
  2. بررسی می‌شود که هر سه فیلد مقدار داشته باشند. در غیر این صورت، پیام «لطفاً همهٔ فیلدها را کامل کنید.» نمایش داده شده و روند ارسال متوقف می‌شود.
  3. اگر فیلدها کامل باشند، یک شیء شامل username، password و role ساخته می‌شود و به تابع registerUser ارسال می‌گردد.
  4. در صورت موفقیت، پیام «ثبت‌نام با موفقیت انجام شد!» نمایش داده شده و پس از یک ثانیه کاربر به صفحه‌ی login.html منتقل می‌شود.
  5. در صورت بروز خطا در ثبت‌نام، پیام خطا با استفاده از showToast(err, "error") نمایش داده خواهد شد.

بررسی و نمایش محتوای داشبورد (Dashboard)🔗

  1. در ابتدا، مقادیر accessToken، refreshToken و role از localStorage خوانده می‌شوند.
  2. اگر هر یک از این مقادیر در دسترس نباشند، یعنی کاربر احراز هویت نشده یا اطلاعات ناقص است، کاربر باید به صفحه‌ی اصلی (index.html) هدایت شود.
  3. اگر مقادیر مورد نیاز وجود داشته باشند، بسته به مقدار role، محتوای مناسب برای داشبورد انتخاب و در عنصر dashboard-content نمایش داده می‌شود:
    • در صورتی که مقدار نقش admin باشد، محتوای مدیریتی (adminContent) نمایش داده می‌شود.
    • در غیر این صورت، محتوای مخصوص کاربر عادی (userContent) قرار داده خواهد شد.
  4. پس از نمایش محتوای مناسب، عنصر مربوط به دکمه خروج با شناسه logout-btn پیدا می‌شود. در صورتی که وجود داشته باشد، یک رویداد کلیک برای آن تعریف می‌شود که هنگام کلیک:
    • مقادیر accessToken، refreshToken و role را از localStorage حذف می‌کند.
    • سپس کاربر را به صفحه‌ی اصلی (index.html) بازمی‌گرداند.

توضیح تصویر

توضیح تصویر

توضیح تصویر

کنترل مسیرهای دسترسی بر اساس وضعیت احراز هویت🔗

برای هدایت صحیح کاربر به صفحات مجاز، بر اساس مسیر فعلی و وضعیت احراز هویت، شرایط زیر بررسی می‌شود:

  1. اگر کاربر در مسیر dashboard.html باشد ولی احراز هویت نشده باشد، باید به صفحه‌ی login.html منتقل شود.
  2. اگر کاربر در صفحه‌ی login.html یا register.html باشد ولی قبلاً احراز هویت شده باشد، باید مستقیماً به صفحه‌ی dashboard.html منتقل شود.
  3. اگر کاربر در صفحه‌ی اصلی (index.html یا ریشه /) باشد:
    • در صورت احراز هویت، به داشبورد هدایت می‌شود.
    • در غیر این صورت، به صفحه‌ی ورود (login.html) منتقل می‌شود.

هدف از این کنترل‌ها، جلوگیری از دسترسی غیرمجاز به بخش‌های محافظت‌شده‌ی سایت و همچنین جلوگیری از ورود کاربران لاگین‌شده به صفحات ورود و ثبت‌نام است. این کار تجربه‌ی کاربری را بهبود می‌دهد و ساختار امنیتی سمت کلاینت را تقویت می‌کند.

نکات پیاده‌سازی🔗

  1. چهار صفحه اچ‌تی‌ام‌ال داریم که به صورت کامل نوشته شده‌اند و نیازی نیست که چیزی در آن‌ها بنویسید.
  2. همه صفحات به app.js متصل هستند.
  3. مقادیری که باید در لوکال‌استورج ذخیره بشوند :
  • accessToken
  • refreshToken
  • role
  1. در ابتدای فایل تابعی مشاهده می‌کنید به نام showToast که به صورت کامل نوشته شده است و لازم نیست که چیزی را در آن بنوسید و در آینده‌ی نزدیک باید از آن استفاده بکنید.
  2. تمام فرایند‌ها به طور کامل در ویدئو‌ها نشان داده شده است.
  3. برای استانداردسازی یوزنیم در واقع باید به حروف و فاصله‌ها توجه بکنید.

آنچه باید آپلود بکنید🔗

فایلی که آپلود خواهید کرد باید دارای فرمت .Zip باشد و دارای ساختار زیر باشد:

answer/
├─ app.js
Plain text

موفق باشید 🌟