# اسکرول

# پروژهی اولیه
فایل پروژه را میتوانید از طریق این [این لینک](/contest/assignments/85280/download_problem_initial_project/291489/) دانلود کنید.
ساختار پروژه به شکل زیر است:
```
initital_project/
├─ index.html
└─ styles.css
```
# جزئیات پیادهسازی
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
```
موفق باشید 🌟
اسکرول
# پورت

# پروژهی اولیه
فایلِ پروژه را میتوانید از طریق این [این لینک](/contest/assignments/85280/download_problem_initial_project/291490/) دانلود کنید.
ساختار پروژه به شکلِ زیر است:
```
initial_project/
├─ index.html
├─ main.js
└─ styles.css
```
# جزئیات پیادهسازی
1. همانطور که مشاهده میکنید، همهی پورتها شامل یک مقدار مشخص هستند که در فایلِ `index.html` میتوانید تگهای مربوطه برا ببینید.
2. در کنارِ هر عدد، یک آیکونِ ویرایش (همان مداد) مشاهده میکنید که وقتی روی آن کلیک میکنیم، تگی که مقدار در آن ذخیره شده بود باید از دیدِ کاربر محو شده و بهجای آن، یک input نمایش داده شود که مقدارِ آن، همان مقدارِ موجود در تگِ مربوطه بوده؛ همچنین یک دکمهی «ثبت» هم در زیرِ هر input قرار دارد.
3. حالا با وارد کردنِ مقدارِ جدید در input و زدنِ دکمهی ثبت، input محو شده و دوباره همان تگی که وظیفهی نمایش مقدار را داشت، نمایش داده میشود.
# نکات
1. شما صرفا باید در فایل **main.js** تغییرات ایجاد کرده و کد بنویسید.
2. هر باکس، اطلاعاتِ مخصوصِ خودش را دارد.
3. در نظر داشته باشید که هر input فقط یکبار باز میشود.
4. تگهای اچتیامای دارای آیدی با نامهای واضح و مفهومی هستند.
# آنچه باید آپلود کنید
فایلی که آپلود خواهید کرد باید دارای فرمت `.zip` باشد و دارای ساختارِ زیر باشد:
```
answer/
└─ main.js
```
موفق باشید 🌟
# کارت محصول
_szd6.gif)
# پروژهٔ اولیه
فایل پروژه را میتوانید از طریق این [این لینک](/contest/assignments/85280/download_problem_initial_project/291492/) دانلود کنید.
ساختار پروژه به شکل زیر است:
```plaintext
initial-project/
├─ api/
│ └─ getProduct.js
├─ assets/
│ ├─ spinner.svg
│ └─ watch.png
├─ js/
│ └─ cardAnimation.js
├─ index.html
├─ main.js
└─ style.css
```
# جزئیات پیادهسازی
1. در فایل `getProduct.js`، تابعی با همین نام وجود دارد که یک Promise برمیگرداند، که شامل مشخصات محصولات است.
2. تابع `showToast` را باید با هدف نمایش toaster به کاربر کامل کنید. تگ مورد نظر که دارای آیدی `toast` است را از فایل HTML بهدست بیاورید.
+ توجه داشته باشید ۳ ثانیه پس از نمایش toaster، باید از بین برود.
3. تابع `renderProduct` باید اطلاعات محصول را در فرمت زیر و در تگی که دارای آیدی `product-card` است قرار دهد:
```html
<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>
```
تصویر محصول کامل به شکل زیر است :

4. در تابع `renderEror` در صورتی که دریافت اطلاعات محصول با مشکل مواجه شد، محتوای داخلی `product-card` به این شکل خواهد بود:
```html
<p style="color: var(--error-color); font-weight: bold;">خطا در بارگذاری اطلاعات</p>
<button id="retry-btn" class="retry-btn">تلاش مجدد</button>
```
+ همچنین دکمهای با آیدی `retry-btn` وجود دارد که باید برای آن یک event کلیک تعریف کنید تا با کلیک روی آن، سعی دوباره برای دریافت اطلاعات انجام شود (تابع نوشتهشده برای این کار را call کنید).
5. تابع `loadProduct` را باید طوری کامل کنید، که هنگام لودینگ، محتوای زیر در `product-card` نمایش داده شود:
```
<img src="assets/spinner.svg" alt="در حال بارگذاری..." class="spinner">
```
همچنین در این تابع باید تلاش برای دریافت اطلاعات انجام شود. در صورت موفقیت، باید `renderProduct` را call کنید و در صورت شکست، `showToast` را نمایش دهید و همچنین `renderError` را فراخوانی کنید.
6. در پایان، هم تابع اصلی که تمام فرایند ها را به درستی اجرا میکند فراخوانی
# نکات
1. در صورت نیاز برای نمایش {کارت، toaster , ... } از کلاس `show` و برای محو کردن آنها از کلاس `hidden` که در فایل CSS از قبل نوشته شدهاند استفاده بکنید.
2. در بخش جزئیات پیادهسازی، یکسری `<!-- -->` کامنت HTML را مشاهده میکنید که باید در آن قسمتها اطلاعات مربوط به محصول را قرار دهید.
3. متن ارور باید در `toaster` نمایش داده شود.
4. برای استفاده از `getProduct.js` از `import` استفاده کنید.
+ اگر با خطا رو به رو شویم :
_l6lb.png)
+ اگر محصول موجود یا ناموجود باشد :

# آنچه باید آپلود کنید
فایلی که آپلود خواهید کرد باید دارای فرمت `.zip` باشد و دارای ساختار زیر باشد:
```plaintext
answer/
├─ main.js
```
موفق باشید 🌟
کارت محصول
# جدول اطلاعات

# پروژهٔ اولیه
فایل پروژه را میتوانید از طریق این [این لینک](/contest/assignments/85280/download_problem_initial_project/291493/) دانلود کنید.
ساختار پروژه به شکل زیر است:
```
initial_project/
├─ js/
│ └─ data.js
├─ index.html
├─ main.js
└─ styles.css
```
در فایل `data.js`، تابعی وجود دارد که اطلاعات لیستی از ماشینها در آن موجود است.
# جزئیات پیادهسازی
۱. اینپوت، سلکتور و تیبل را میتوانید از طریق آیدیهای زیر بدست آورید:
+ `searchInput`
+ `yearFilter`
+ `carsTable`
۲. این خط را مشاهده کنید:
```js
fetchCarsData().then()
```
با ادامه دادن آن، اطلاعات ماشینها را بهدست آورید.
۳. تابع `renderCars` را بهگونهای پیادهسازی کنید که:
+ اگر لیست خالی بود، `emptyListString` بهعنوان محتوای بدنهٔ تیبل نمایش داده شود.

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


۵. تابع `applyFilters` را طوری کامل کنید که:
+ بر اساس محتوای سرچشده یا سال انتخابشده یا ترکیب هر دو، محتوای تیبل را فیلتر کند.
```js
searchInput.addEventListener("input");
```
+ ادامه این ایونت را بهگونهای بنویسید که **۲.۵ ثانیه پس از توقف تایپ کاربر**، عملیات فیلتر انجام و تیبل آپدیت شود.
6. این خط را مشاهده کنید :
```js
yearFilter.addEventListener("change");
```
+ ادامه این ایونت را بهگونهای بنویسید که **۱ ثانیه پس از انتخاب سال**، عملیات فیلتر انجام و تیبل آپدیت شود.
# نکات
۱. هر زمان که کاربر شروع به تایپ کرد یا سالی را انتخاب نمود، **فارغ از بازههای زمانی**، باید فوراً **اسپینر لودینگ** با استفاده از تابع `showSpinner` نمایش داده شود.
۲. بازههای زمانی ذکرشده **حتماً باید رعایت شوند**. در غیر این صورت، هدف برنامه محقق نخواهد شد.
۳. تغییرات فیلترها فقط باید در **بدنهٔ تیبل** اعمال شوند.
# آنچه باید آپلود کنید
فایلی که آپلود خواهید کرد باید دارای فرمت `.zip` باشد و دارای ساختار زیر باشد:
```
answer/
└─ main.js
```
موفق باشید 🌟
جدول اطلاعات
# احراز هویت

## پروژهٔ اولیه
فایل پروژه را میتوانید از طریق این [این لینک](/contest/assignments/85280/download_problem_initial_project/291494/) دانلود کنید.
ساختار پروژه به شکل زیر است:
```plaintext
initital_project/
├─ css/
│ └─ styles.css
├─ app.js
├─ dashboard.html
├─ index.html
├─ login.html
└─ register.html
```
## جزئیات پیادهسازی
### اقدامات لازم برای `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` ذخیره میشود. در صورتی که عملیات ذخیرهسازی با خطا مواجه شود، تابع با پیام زیر رد میشود:
**"خطا در ذخیرهسازی. دوباره تلاش کنید."**
8. در نهایت، اگر همه مراحل با موفقیت انجام شد، 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` را با این فرمت تولید کنید:
```js
const accessToken = "access-" + Date.now() + "-" + Math.random().toString(36).substr(2);
const refreshToken = "refresh-" + Date.now() + "-" + Math.random().toString(36).substr(2);
```
6. سپس این مقادیر به همراه `role` کاربر، در `localStorage` ذخیره میشوند.
7. مرحله ۶ باید در یک بلوک `try/catch` انجام شود. در صورت بروز خطا هنگام ذخیرهسازی، تابع با `reject` و پیام زیر خاتمه مییابد:
**"خطا در فرآیند ورود. دوباره تلاش کنید."**
8. در نهایت، اگر تمام مراحل با موفقیت انجام شود، `resolve` فراخوانی میشود و آبجکت زیر بازگردانده میشود:
```js
{ accessToken, refreshToken, role: found.role }
```


### رویداد سابمیت برای 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
4. در ابتدای فایل تابعی مشاهده میکنید به نام showToast که به صورت کامل نوشته شده است و لازم نیست که چیزی را در آن بنوسید و در آیندهی نزدیک باید از آن استفاده بکنید.
5. تمام فرایندها به طور کامل در ویدئوها نشان داده شده است.
6. برای استانداردسازی یوزنیم در واقع باید به حروف و فاصلهها توجه بکنید.
## آنچه باید آپلود بکنید
فایلی که آپلود خواهید کرد باید دارای فرمت **.Zip** باشد و دارای ساختار زیر باشد:
```plaintext
answer/
├─ app.js
```
موفق باشید 🌟