آموزش ساخت فروشگاه فول استک با جنگو و ری اکت – فصل ۶: احراز هویت کاربر

مدت :

۵ ساعت و ۳۰ دقیقه

قیمت : ۲۹۴,۰۰۰ تومان
LinkedIn
Twitter
Facebook
Telegram

درباره مدرس 

اطلاعات دوره

توضیحات

امروز پس از گذشت حدود ۲ ماه و نیم ضبط و داوری، هم‌رویش مفتخر است که فصل ششم از آموزش ساخت فروشگاه فول استک با جنگو و ری اکت را به شما عرضه کند.

در این فصل ما از ساخت فروشگاه اینترنتی با django به احراز هویت کاربران در سمت فرانت اند می‌پردازیم. از سیستمی که در فصل پنجم برای این کار ساخته بودیم استفاده کردیم. حال بدون اتلاف وقت نگاهی به این آموزش می‌اندازیم تا با کلیات این آموزش آشنا شوید.

 

آموزش-ساخت-فروشگاه-فول-استک-با-جنگو-و-ری-اکت-ریکت-هم-رویش

این آموزش بخشی از بسته جامع ساخت فروشگاه با جنگو و ری اکت (+) است.

شما می‌توانید بسته جامع با با مجموع قیمت کمتر از این لینک (+) تهیه کنید یا این مجموعه را فصل به فصل دریافت و تماشا کنید.

 

فهرست فصل‌‌به‌فصل آموزش ساخت فروشگاه جنگو/ریکت

 

این آموزش در یک نگاه:

از همان درس اول با ساخت نحوه ورود کاربر به سیستم، کار را به صورت عملی شروع کردیم. ریداکس را برای این کار تنظیم و صفحه ورود را ساختیم. پس از آن امکان خروج کاربر را هم ایجاد کردیم. اکنون کاربران امکان ورود و خروج از سیستم را دارند اما کاربران جدید چطور؟

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

صفحه‌ای به اسم پروفایل ایجاد کردیم تا کاربران بتوانند اطلاعات خود را در آن ببینند و یا بروز کنند. در درسی به مدیریت پیام‌ها پرداختیم و تا حدی باگ‌های کوچکی که در سمت فرانت اند داشتیم را بر طرف نمودیم.

در درس فرایند تایید ایمیلی در جنگو به این پرداختیم که اگر که ما بخواهیم تایید ایمیلی یا همان ( Email Verfication ) را بر روی پروژه پیاده سازی کنیم فرایند به چه صورت می‌شود. این فرایند چه تفاوتی با ثبت نام عادی دارد؟

در سه الی چهار درس با یکدیگر تایید ایمیلی را روی فروشگاه اعمال کردیم. از این به بعد کاربران باید برای ثبت نام و استفاده از فروشگاه ایمیل خود را تایید کنند تا حسابشان فعال شود.

در درس ۱۶ یاد گرفتیم که چطور ایمیل را در صورتی که روی میزبان محلی ( Local Host ) هستیم به شکل واقعی ارسال کنیم. از این درس به بعد، به بهتر کردن فروشگاه و برطرف کردن باگ‌های موجود پرداختیم.

به عنوان مثال در درس‌های ۱۷ تا ۱۹ به Access Token‌های مرده ( expired ) در سمت فرانت-اند و تازه سازی آن با استفاده از Refresh Token پرداختیم. در نهایت متوجه باگ احتمالی در jwt شدیم که آن را با ابزار Throttling در جنگو رست رفع کردیم. در نهایت به این فصل پایان دادیم.

 

این آموزش بی‌نظیر است زیرا:
  • این آموزش پروژه محور است.
  • مباحث این آموزش پیشرفته‌تر است.
  • در این آموزش با سیستم JWT که روشی نوین در احراز هویت کاربران است کار می‌کنیم.

 

پیشنیاز

 

کلیدواژگان

آموزش فروشگاه فول استک با جنگو | فول استک با جنگو | آموزش ساخت فروشگاه فول استک | ساخت فروشگاه اینترنتی با جنگو | فروشگاه اینترنتی با جنگو | فروشگاه با جنگو | ساخت فروشگاه اینترنتی با django | ساخت فروشگاه اینترنتی با جنگو | فروشگاه اینترنتی با جنگو | فروشگاه با جنگو | ساخت فروشگاه با ریکت | فروشگاه فول استک با ری اکت

سرفصل‌ها

سرفصل‌ها

درس صفر: این فصل در یک نگاه

  • نگاهی به پیشرفت پروژه در پایان این فصل
  • نگاهی به محتوای تخصصی این فصل
  • نگاهی به مسیر پیش رو

 

درس اول : ریدوسر و اکشن ورود

  • تعریف constanst برای اکشن ورود
  • تعریف تابع ریدوسر با کلید userInfo
  • یاداوری تابع ریداکس تانک
  • تعریف تابع اکشن ورود
  • صدا زدن api ورود با axios.post
  • ذخیره state و پاسخ درخواست در Localstorage
  • JSON.stringify در localStorage

 

درس دوم : ساخت صفحه ورود

  • ایجاد FormContainer برای داشتن یک استایل کلی در فرم ها
  • استفاده از Emmet برای ایجاد سریع فرم ها
  • ساخت فرم با ری اکت بوت استرپ
  • متد onChange در FormControl
  • نحوه گرفتن مقادیر Input ها و پر کردن state
  • متد onSubmit در Form
  • تعریف متغیر redirect در path برای هدایت کردن کاربر

 

درس سوم : اکشن خروج

  • نمایش گزینه‌ها بر حسب وضعیت کاربر ( لاگین بودن یا نبودن)
  • NavDropdown در react-bootstrap
  • صدا زدن تابع در ازای رویداد onClick در NavDropdown.Item
  • تعریف اکشن خروج
  • حذف ایتم از localStorage

 

درس چهارم : ریدوسر و اکشن ثبت نام

  • تعریف constanst برای اکشن ثبت نام
  • یاداوری تابع ریداکس تانک
  • تعریف تابع اکشن ثبت نام
  • صدا زدن api ثبت نام با axios.post
  • ذخیره state و پاسخ درخواست در Localstorage
  • JSON.stringify در localStorage

 

درس پنجم : ساخت صفحه ثبت نام

  • ایجاد FormContainer برای داشتن یک استایل کلی در فرم ها
  • استفاده از Emmet برای ایجاد سریع فرم ها
  • ساخت فرم با ری اکت بوت استرپ
  • متد onChange در FormControl
  • نحوه گرفتن مقادیر Input ها و پر کردن state
  • متد onSubmit در Form

 

درس هفتم : ریدوسر و اکشن پروفایل

  • تعریف constanst برای اکشن مشخصات کاربر
  • تعریف تابع ریدوسر با کلید user
  • یاداوری تابع ریداکس تانک
  • تعریف تابع اکشن مشخصات کاربر
  • صدا زدن api مشخصات کاربر با axios.post
  • ذخیره state و پاسخ درخواست در Localstorage
  • JSON.stringify در localStorage
  • تنظیم header درخواست و قرار دادن توکن

 

درس هشتم : ساخت صفحه پروفایل

  • ایجاد صفحه پروفایل کاربر
  • ایجاد فرم در صفحه پروفایل کاربر
  • نمایش اطلاعات کاربر در فرم
  • متد push در history برای هدایت کردن کاربر
  • دیباگ کردن نمایش اطلاعات کاربر قبلی

 

درس نهم : امکان روزآمد سازی پروفایل

  • تعریف constanst برای اکشن روزآمد سازی پروفایل
  • تعریف تابع ریدوسر روزآمد سازی پروفایل
  • یاداوری تابع ریداکس تانک
  • تعریف تابع اکشن روزآمد سازی پروفایل
  • صدا زدن api روزآمد سازی پروفایل با axios.post
  • ذخیره state و پاسخ درخواست در Localstorage
  • JSON.stringify در localStorage
  • تنظیم header درخواست و قرار دادن توکن
  • متد onSubmit در فرم نمایش مشخصات کاربر
  • نوشتن Handler برای رویداد onSubmit فرم
  • کاربرد e.preventDefault

 

درس دهم : مدیریت پیام‌ها

  • حل کردن باگ‌های ریز نمایشی در پروفایل
  • نمایش صحیح و درست منشا‌ء خطاها
  • نمایش پیام‌های مناسب به کاربر

 

درس یازدهم: فرایند تایید ایمیلی در جنگو

  • درک مراحل و فرایند کلی تایید ایمیلی
  • بررسی دمو ثبت نام عادی ( بدون تایید ایمیلی )
  • بررسی دمو ثبت نام با تایید ایمیلی
  • بررسی کد ثبت نام با تایید ایمیلی

 

درس دوازدهم: اصلاح ویو ثبت نام

  • تغییر ویو ثبت نام برای تایید ایمیلی
  • ایجاد activation link با jwt
  • بررسی حالت های مختلف وضعیت کاربر و عملکرد درست
  • Status ریسپانس ها در جنگو رست

 

درس سیزدهم: ارسال ایمیل فعال سازی

  • ایجاد لینک فعال سازی
  • ارسال ایمیل با کلاس EmailMessage
  • تفاوت EmailMessage با send_mail
  • تنظیم EMAIL_BACKEND
  • بررسی عملکرد با نرم افزار پست من

 

درس چهاردهم: فعال سازی حساب کاربر

  • Decode کردن توکن و استخراج اطلاعات
  • فعال سازی حساب کاربر با اطلاعات به دست امده
  • هدایت کاربر با HttpResponseRedirect

 

درس پانزدهم: اصلاح فرانت اند برای تایید ایمیلی

  • ایجاد تغییرات در اکشن ثبت نام
  • ایجاد تغییرات در صفحه ثبت نام
  • ایجاد تغییرات در هدایت کردن‌ها
  • انجام کاری در ازای بودن ابجکتی که گاهی اوقات وجود دارد و گاهی اوقات نه
  • ایجاد تغییرات در صفحه ورود
  • نمایش درست پیام ها با alert های درست

 

درس شانزدهم : ارسال ایمیل در جنگو به کمک سرورهای گوگل

  • کاربرد Less secure app access در اکانت گوگل
  • تنظیم Email Provider برای استفاده از سرورهای گوگل
  • معرفی Email Provider های دیگر برای استفاده از دیگر سرویس دهنده ها
  • تفاوت TLS و SSL
  • توضیح مفهوم TLS Handshake
  • Cipher suits چیست

 

درس هفدهم: تازه سازی توکن دسترسی ( بک-اند )

  • تنظیم نگاشت refresh token
  • درک ROTATE_REFRESH_TOKENS در تنظیمات JWT
  • درک BLACKLIST_AFTER_ROTATION در تنظیمات JWT
  • کاربرد اپ token_blacklist در JWT

 

درس هجدهم: مفهوم Interceptor‌ها در axios

  • متد create در axios
  • درک مفهوم Interceptore در axios
  • چرایی استفاده از interceptor
  • نصب jwt-decode و dayjs
  • ساخت axios اختصاصی
  • متد unix در dayjs
  • متد diff در dayjs
  • تازه سازی توکن دسترسی قبل از ارسال درخواست کاربر

 

درس نوزدهم: تازه سازی توکن دسترسی ( فرانت-اند )

  • تعریف اکشن به روز کردن توکن‌ها
  • استفاده کردن از axios خودمان
  • چرایی اصلاح کردن اکشن روزآمد سازی پروفایل
  • اصلاح اکشن روزآمد سازی پروفایل
  • علاج از کار افتادن refresh token و هدایت کاربر به صفحه لاگین

 

درس بیستم: مدیریت کردن تعداد درخواست‌ها

  • Throttling چیست
  • پیاده کردن مود User filter در throttling
  • پیاده کردن مود View filter در throttling
  • درک scopeها در throttling
  • اعمال سیاست بر روی ویوها

 

درس بیست و یکم: جمع بندی

نگاه کوتاه به محتوای این فصل
نگاهی به مسیر پیش رو

نظرات (11)

11 دیدگاه برای آموزش ساخت فروشگاه فول استک با جنگو و ری اکت – فصل ۶: احراز هویت کاربر

  1. امین دهقان (خریدار محصول)

    سلام مجدد و تشکر از پاسخ گویی شما
    بله موضوع قبلی حل شد
    این مشکل بنده هم حل شد اگر امکان داشته باشه تو نامگذاری متغییر ها کمی وسواس بیشتر به خرج بدید
    برای مثال شما userLogin رو هم توی انبار و هم توی کلاس تعریف کردید
    و واقعا راه رو برای مهندسی معکوس یا درک دقیق مطلب پیچیده تر می کنه
    مخصوصا برای ما پیر مرد های دات نت کار 🙂
    از وقتی که گذاشتید ممنونم 🙂

    • ابوالفضل حسن زاده

      سلام وقت شما بخیر اقای دهقان . خدا رو شکر که مشکلتون حل شد . بله کاملا درست میفرمایید از این به بعد نامگذاری متغیر ها رو با وسواس و دقت بیشتری انجام میدم . خیلی ممنون از شما ❤❤.

  2. امین دهقان (خریدار محصول)

    سلام روز به خیر
    const userLogin=useSelector(state =>state.userLogin)
    const {error,userInfo,loading}=userLogin
    استاد واقعا من هر کاری کردم هرچقد آموزش رو نگاه کردم حتی مهندسی معکوس کردم متوجه نشدم
    این دو خط رو از کجا آوردید
    میشه خواهش کنم بفرمایید
    error userInfo oading userLogin state منظورتون از تمام این کلمات را بفرمایید؟ سپاس

    • ابوالفضل حسن زاده

      سلام آقای دهقان وقت شما هم بخیر . اگر که منظورتون رو درست متوجه شده باشم ، این کد رو ما در فایل loginScreen.js نوشتیم ( این تا اینجا ). در واقع با این کد ما اون کلیدهایی که در انبار ریداکس تعریف کرده بودیم ( تابع ریدوسر userLoginReducer کلید ها رو اینجا تعریف کردیم )، داریم واکشی میکنیم که ببینیم چه مقدارهایی دارند تا مثلا اگر که loading مقدارش true هست ، یه انیمیشن به نشانه بارگذاری نمایش بدیم . یا مثلا اگر که userInfo مقدار درست داره ( یعنی توکن داره ، اسم و نام کاربری داره و…. ) اون وقت یعنی کاربر لاگین کرده . یا اگر که error مقدار داره یعنی در حین ورود ( درواقع منظور از ورود اینجا دریافت access و refresh توکن هستش ) به مشکل خورده و اون وقت باید یه پیام مناسب بهش نمایش بدیم . state رو هم ما یه متغیر به تابع useSelector میدیم ( شما اینجوری تصور کنید ) بعد این کل انبار ریداکس رو میریزه داخل این متغیر بعد اون موقع با اون کلیدی که توی انبار ذخیرش کردیم داخل فایل store.js به واسطه این متغیر خیلی راحت تر میتونیم صداش بزنیم . اصلا میتونه به این صورت باشه useSelector(x => x.userLogin) هیچ تفاوتی نداره ولی خب ما به جای x ، state رو پاس دادیم تا کدمون تمیز و خوانا باشه . دلیل نوشتنش همین بود .

      پی نوشت :
      مشکل قبلی تون حل شد ؟؟

  3. امین دهقان (خریدار محصول)

    سلام استاد گرامی با تشکر از زحمات شما و تشکر از شما
    این کد مربوط به POST هست که در action نوشتم
    dispatch({type: USER_LOGIN_REQUEST})
    const config={header:{‘Content-type’:’application/json’}}
    const { data } = await axios.post
    (
    ‘http://172.16.45.198/api/Users/UserLogin’,
    {‘UserName’:UserName,’UserPassword’:UserPassword}, config,
    )
    dispatch({
    type:USER_LOGIN_SUCCESS,
    payload :data,
    })
    قبلش عرض میکنم که در بخش GET هیچ مشکلی ندارم
    ولی در این بخش با خطای NETWORK ERROR مواجه میشم
    نکته: من این درخواست رو در پستمن تست کردم و خطایی نداشتم

    • ابوالفضل حسن زاده

      سلام اقای دهقان وقت شما بخیر . اول از همه اینکه خیلی عذرخواهی میکنم جوابتون دیر شد و دوم اینکه ما username password رو که ارسال میکنیم تا access , refresh token دریافت کنیم باید با کلید های username , password باشه . یکی از مشکلات میتونه از اینجا باشه الان شما به این شکل نوشتید {‘UserName’:UserName,’UserPassword’:UserPassword}, باید تبدیل به این حالت بشه {‘username’:username, ‘password’:password} شما این حالت رو هم بررسی کنید اگر درست نشد کدتون رو روی گیت هاب قرار بدید و لینکش رو اینجا قرار بدید تا بررسی کنم . این لینک هم میتونه به شما کمک کنه .

  4. mehdi mahdavi (خریدار محصول)

    سلام تشکر از زحمات شما یه سوال داشتم تااین مرحله پیش اومدم وقتی صفحه را رفرش میکنم یوزر لوگ اوت میشه ممنون میشم راهنمای بفرمایید

    • ابوالفضل حسن زاده

      سلام متشکرم از محبت شما . ببینید زمانی که شما صفحه رو reload یا refresh میکینید ریداکس شما خالی میشه . احتمالا داخل فایل store.js در بخش initialstate متغیریی که برای اولین بار به ریداکس مقدار میده مشکل دارید و یا در جایی که لاگین میکنید و اطلاعات کاربر رو در localstorage ذخیره مکنید مشکل دارید و یا اینکه در هر دو جا درست عمل میکنید ولی در بخش initialstate دارید با نام اشتباه اطلاعات کاربر رو از localstorage میخونید . اگر که اشتباه نکنم در دوره اطلاعات کاربر رو با کلید userInfo ذخیره کردیم . شما این احتمالات رو بررسی کنید باز اگر که مشکل حل نشد بازگو کنید که من بیشتر بررسی کنم.
      ذخیره اطلاعات کاربر در مرورگر
      مقدار دهی اولیه ریداکس از اطلاعات ذخیره شده کاربر در مرورگر

  5. محمدرضا گوشکی (خریدار محصول)

    سلام استاد عزیز خسته نباشید ممنون از دوره عالی
    من هاست پایتونی دارم و در حال حاضر بخش بک اند که جنگو هست رو دیپلوی کرده ام. سوال اینجاست برای دیپلوی کردن بخش فرانت اند پروژه که با ری اکت هست باید هاست جدا تهیه کنم یا روی همان فضا پایتونی میتوان بخش فراند اند را مستقر کرد؟

  6. امین اجاقی (خریدار محصول)

    با سلام و درود خدمت استاد بزرگ و گرامی مصطفی آصفی عزیز استادم که مرا تا مرحله یک برنامه نویس فول استک شدن بسیار یاری رسانده است و من همیشه بدنبال آموزش های شما و همراه بودن با شما در تمام زمان ها هستم و سلام خدمت استاد و دوست و هم شاگردی عزیزم ابوالفضل حسن زاده عزیز و گرامی من پس از کمال در رشد دانشم در برنامه نویسی در مسیر های مختلف بسیار زیباست که بالاخره در یک شرکت که با زبان آنگولار و دات نت کار می کند شغل پیدا کردم یعنی علاوه بر تمام دروسی که با استاد عزیزم مصطفی آصفی و شما و استادان دیگری در همه زمینه های وب گذراندم آنگولار هم یاد گرفتم تا در این شرکت به کار پرداختم و بسیار شادم و شکر گزار خدای بزرگ و متعال و همچنین تمام دروسی که از گذشته شروع کردم را هم مخصوصا دروسی که از هم رویش می آید را دنبا می کنم و دانشم را گسترش می دهم و همینطور این دو دوره ی جدیدی که از این دوره از آموزش ها قرار دادید یعنی 6 و 7 را خریداری کرده ام تا در یک زمان مناسب شروع به یادگیری کنم زیرا هم رویش یک آموزشگاه اینترنتی بسیار مردمی است که با نور خدا و دل هایی بسیار نورانی که از سوی خداست دلی مانند دل پاک و آرام استاد عزیزم مصطفی آصفی که بسیار زیبا و دوستانه و بسیار نیکو در مسیر رشد دانش مردم ایران که خواهران و برادران هستیم این چنین کوشا هستین و از خدای متعال برکات بسیار از آشسمان نیکو هایشان برایتان می طلبم و کمال موفقیت تان از او خواستارم برکت خدا بر شما و عشق زندگیتان و خانواده ها و دوستان و آشناینتان.

  7. iman (خریدار محصول)

    استاد عزیز لطفا برای درگاه پرداخت بحثهای تکنیکی هم که در پروژه واقعی حتما رعایت کنیم رو هم ذکر کنید چون حالت معمولی اش زیاده اصل نکات تکنیکی هست که تو هر آموزشی پیدا نمیشه . مثلا اگر یک سبد خرید داشته باشیم در موبایل و بعد از مدتی با لپ تاپ محصول جدیدی اضافه کنیم . موارد قبلی در سبد خریدمان تعیین تکلیف بشود و از کاربر پرسیده شود به سبد اضافه شود یا موارد قبلی حدف شود. یا از آن مهمتر وقتی یک محصول در سبد داریم و دونفر همزمان درخواست خرید نهایی میزنند و به درگاه پرداخت میروند نفری که زودتر خرید رو زده و به درگاه رفته محصول برایش رزرو شود و در صورت نهایی شدن خرید که تعداد محصول ناموجود شود ولی اگر انصراف از خرید شود محصول از رزور برگشته و دوباره به تعدا محصولات موجود اضافه شود. یا مواردی چون ایجاد تخفیفات دوره ای یا خرید با کپن تخفیف

    • ابوالفضل حسن زاده

      سلام به شما . انشالله چشم حتما . فروشگاه رو که ساختیم در قالب فصل های کوچولو کوچولو این مشکلات رو هم حل میکنیم و اون رو تبدیل به یک فروشگاه حرفه ای و واقعی خواهیم کرد.

  8. رضا (خریدار محصول)

    درود بر شما

    یک نکته ای رو خواستم یاد آور بشم:
    در جایی اومدید و برای تعریف متغیر از var استفاده کردید که این کار منسوخ شده هستش و پیشنهاد شده به دلایل امنیتی و .. استفاده نشه و به جاش از let استفاده بشه.

    و راستی من با یک سری تغییرات کوچیک تونستم پروژه رو روی آخرین ورژن ری اکت یعنی 18 و کتابخونه هاش اجرا کنم. که البته در ورژن جدید ریداکس پیشنهاد شده از ریداکس تولکید استفاده بشه اما خب دستورات قدیمی هم به خوبی کار میکنن.
    “dependencies”: {
    “@hookform/resolvers”: “^2.8.10”,
    “@react-icons/all-files”: “^4.1.0”,
    “@testing-library/jest-dom”: “^5.16.4”,
    “@testing-library/react”: “^13.3.0”,
    “@testing-library/user-event”: “^14.2.0”,
    “axios”: “^0.27.2”,
    “bootstrap”: “^5.1.3”,
    “dayjs”: “^1.11.3”,
    “http-proxy-middleware”: “^2.0.6”,
    “jwt-decode”: “^3.1.2”,
    “react”: “^18.1.0”,
    “react-bootstrap”: “^2.4.0”,
    “react-dom”: “^18.1.0”,
    “react-hook-form”: “^7.32.0”,
    “react-icons”: “^4.4.0”,
    “react-redux”: “^8.0.2”,
    “react-router-bootstrap”: “^0.26.1”,
    “react-router-dom”: “^6.3.0”,
    “react-scripts”: “^5.0.1”,
    “redux”: “^4.2.0”,
    “redux-devtools-extension”: “^2.13.9”,
    “redux-thunk”: “^2.4.1”,
    “web-vitals”: “^2.1.4”,
    “yup”: “^0.32.11”
    },
    “scripts”: {
    “start”: “react-scripts start”,
    “build”: “react-scripts build”,
    “test”: “react-scripts test”,
    “eject”: “react-scripts eject”
    },
    “eslintConfig”: {
    “extends”: [
    “react-app”,
    “react-app/jest”
    ]
    },
    “browserslist”: {
    “production”: [
    “>0.2%”,
    “not dead”,
    “not op_mini all”
    ],
    “development”: [
    “last 1 chrome version”,
    “last 1 firefox version”,
    “last 1 safari version”
    ]
    }
    }

    در پایان من صفحه ی آپدیت پروفایل(profileScreen) رو با react-hook-form ساختم و اینجا قرارش میدم تا دوستان اگه مایل بودن استفاده کنن.

    import React, {useEffect, useState} from ‘react’;
    import {Button, Col, Form, Row} from “react-bootstrap”;
    import * as yup from “yup”;
    import {yupResolver} from “@hookform/resolvers/yup”;
    import {useForm} from “react-hook-form”;
    import {useDispatch, useSelector} from “react-redux”;
    import {getUserDetailAction, updateUserProfileAction, userLogoutAction} from “../actions/userActions”;
    import {useNavigate} from “react-router-dom”;
    import {USER_UPDATE_PROFILE_RESET} from “../constants/userConstans”;
    import Loader from “../components/Loader”;
    import Message from “../components/Message”;

    const ProfileScreen = () => {

    const navigate = useNavigate()

    const [name, setName] = useState(”);
    const [email, setEmail] = useState(”);
    const [successUpdate, setSuccessUpdate] = useState(”)

    const nameRegex = /^[a-zA-Z0-9]+$/;
    const emailRegex = /^\w+([-]?\w+)*@\w+([-]?\w+)*(\.\w{2,3})+$/
    const password = /^(?=.*[0-9])(?=.*[!@#$%^&*])[a-zA-Z0-9!@#$%^&*]{6,16}$/

    const validationSchema = yup.object().shape({
    name: yup.string()
    .required()
    .matches(nameRegex),
    email: yup.string()
    .required()
    .matches(emailRegex),
    password: yup.string()
    .required(‘Password is required’)
    .min(8)
    .matches(password),
    confirmPassword: yup.string()
    .required(‘Confirm Password is required’)
    .oneOf([yup.ref(‘password’)], ‘password must match’)
    });

    const {register, control, handleSubmit, formState: {errors}, reset} = useForm({
    resolver: yupResolver(validationSchema),
    defaultValues: {name: name, email: email}
    })

    const dispatch = useDispatch()
    const userLogin = useSelector(state => state.userLogin)
    const {userInfo} = userLogin

    const userDetails = useSelector(state => state.userDetails)
    const {user} = userDetails
    console.log(‘its user’, user)
    const errorOfUserDetails = userDetails.error
    const loadingOfUserDetails = userDetails.loading

    const userUpdateProfile = useSelector(state => state.userUpdateProfile)
    const {success} = userUpdateProfile
    const errorOfUserUpdateProfile = userUpdateProfile.error
    const loadingOfUserUpdateProfile = userUpdateProfile.loading

    let loading = false
    let error = ”

    if (loadingOfUserDetails || loadingOfUserUpdateProfile) {
    loading = true
    }

    if (errorOfUserDetails) {
    error = errorOfUserDetails
    } else if (errorOfUserUpdateProfile) {
    error = errorOfUserUpdateProfile
    }

    const TE = “Token is invalid or expired”

    useEffect(() => {
    if (errorOfUserDetails === TE || errorOfUserUpdateProfile === TE) {
    dispatch(userLogoutAction())
    navigate(‘/login?redirect=/profile’)
    }
    if (!userInfo) {
    navigate(‘/login’)
    } else {
    if (!user || !user.name || success) {
    dispatch({type: USER_UPDATE_PROFILE_RESET})
    dispatch(getUserDetailAction(‘profile’))
    } else {
    setName(user.name)
    setEmail(user.email)
    }
    }
    reset({name: name, email: email})
    }, [navigate, userInfo, user, dispatch, success, name, email, reset, errorOfUserDetails, errorOfUserUpdateProfile]);

    const submitForm = (data) => {
    const name = data.name
    const email = data.email
    const password = data.password
    dispatch(updateUserProfileAction({
    ‘id’: user._id,
    ‘name’: name,
    ’email’: email,
    ‘password’: password
    }))
    reset({password: ”, confirmPassword: ”})

    setSuccessUpdate(‘Profile Successfully Update’)
    }
    return (

    my profile
    {
    loading ? :
    error ? :
    successUpdate ? :

    }

    FullName

    {errors.name?.type === ‘required’ &&
    fullName is required}
    {errors.name?.type === ‘matches’ &&
    fullName not valid}

    EmailAddress

    {errors.email?.type === ‘required’ &&
    email is required}
    {errors.email?.type === ‘matches’ &&
    Invalid Email Address}

    password

    {errors.password?.type === ‘required’ &&
    {errors.password.message}}
    {errors.password?.type === ‘min’ &&
    minimum 8 characters}
    {errors.password?.type === ‘matches’ &&
    password not secure(use @,CL word,)}

    Confirm Password

    {errors.confirmPassword?.type === ‘required’ &&
    {errors.confirmPassword.message}}
    {errors.confirmPassword?.type === “oneOf” &&
    {errors.confirmPassword.message}}

    Update

    my orders

    );
    };

    export default ProfileScreen;

    • ابوالفضل حسن زاده

      سلام و درود بر شما . خیلی ممنون از یادآوری که کردید و افرین به شما که فراتر از پروژه پیش رفتید . انشالله که دیگر فراگیران هم استفاده کنند.

  9. ایمان نم (خریدار محصول)

    استاد عزیز در فصل های این دوره یک چیز به شدت نیاز هست که ممنون میشم به عنوان یک فصل جداگانه این قابلیت رو قرار بدهید. در انتها بی زحمت این پروژه رو PWA اش رو هم پیاده سازی کنید که بسیار لازم و کاربردی هست.

    • ابوالفضل حسن زاده

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

  10. ایمان (خریدار محصول)

    این فصول باقی مانده رو کی انشا.. انتشار میدید؟ مدرس آقای آسفی هستند یا آقای حسن زاده ؟

    • ابوالفضل حسن زاده

      سلام . انشالله به زودی منتشر میکنیم، بنده تا اخر این پروژه در خدمت شما هستم.

  11. iman (خریدار محصول)

    با سلام . این دوره فصل دیگری هم دارید یا تمام شد؟

    • ابوالفضل حسن زاده

      سلام وقت شما بخیر . دو یا سه فصل دیگر باقی مانده . فصل های ثبت سفارش و پرداخت انلاین و …….

دیدگاه خود را بنویسید

نظرات کاربران در خصوص دوره 

11 دیدگاه برای آموزش ساخت فروشگاه فول استک با جنگو و ری اکت – فصل ۶: احراز هویت کاربر

  1. امین دهقان (خریدار محصول)

    سلام مجدد و تشکر از پاسخ گویی شما
    بله موضوع قبلی حل شد
    این مشکل بنده هم حل شد اگر امکان داشته باشه تو نامگذاری متغییر ها کمی وسواس بیشتر به خرج بدید
    برای مثال شما userLogin رو هم توی انبار و هم توی کلاس تعریف کردید
    و واقعا راه رو برای مهندسی معکوس یا درک دقیق مطلب پیچیده تر می کنه
    مخصوصا برای ما پیر مرد های دات نت کار 🙂
    از وقتی که گذاشتید ممنونم 🙂

    • ابوالفضل حسن زاده

      سلام وقت شما بخیر اقای دهقان . خدا رو شکر که مشکلتون حل شد . بله کاملا درست میفرمایید از این به بعد نامگذاری متغیر ها رو با وسواس و دقت بیشتری انجام میدم . خیلی ممنون از شما ❤❤.

  2. امین دهقان (خریدار محصول)

    سلام روز به خیر
    const userLogin=useSelector(state =>state.userLogin)
    const {error,userInfo,loading}=userLogin
    استاد واقعا من هر کاری کردم هرچقد آموزش رو نگاه کردم حتی مهندسی معکوس کردم متوجه نشدم
    این دو خط رو از کجا آوردید
    میشه خواهش کنم بفرمایید
    error userInfo oading userLogin state منظورتون از تمام این کلمات را بفرمایید؟ سپاس

    • ابوالفضل حسن زاده

      سلام آقای دهقان وقت شما هم بخیر . اگر که منظورتون رو درست متوجه شده باشم ، این کد رو ما در فایل loginScreen.js نوشتیم ( این تا اینجا ). در واقع با این کد ما اون کلیدهایی که در انبار ریداکس تعریف کرده بودیم ( تابع ریدوسر userLoginReducer کلید ها رو اینجا تعریف کردیم )، داریم واکشی میکنیم که ببینیم چه مقدارهایی دارند تا مثلا اگر که loading مقدارش true هست ، یه انیمیشن به نشانه بارگذاری نمایش بدیم . یا مثلا اگر که userInfo مقدار درست داره ( یعنی توکن داره ، اسم و نام کاربری داره و…. ) اون وقت یعنی کاربر لاگین کرده . یا اگر که error مقدار داره یعنی در حین ورود ( درواقع منظور از ورود اینجا دریافت access و refresh توکن هستش ) به مشکل خورده و اون وقت باید یه پیام مناسب بهش نمایش بدیم . state رو هم ما یه متغیر به تابع useSelector میدیم ( شما اینجوری تصور کنید ) بعد این کل انبار ریداکس رو میریزه داخل این متغیر بعد اون موقع با اون کلیدی که توی انبار ذخیرش کردیم داخل فایل store.js به واسطه این متغیر خیلی راحت تر میتونیم صداش بزنیم . اصلا میتونه به این صورت باشه useSelector(x => x.userLogin) هیچ تفاوتی نداره ولی خب ما به جای x ، state رو پاس دادیم تا کدمون تمیز و خوانا باشه . دلیل نوشتنش همین بود .

      پی نوشت :
      مشکل قبلی تون حل شد ؟؟

  3. امین دهقان (خریدار محصول)

    سلام استاد گرامی با تشکر از زحمات شما و تشکر از شما
    این کد مربوط به POST هست که در action نوشتم
    dispatch({type: USER_LOGIN_REQUEST})
    const config={header:{‘Content-type’:’application/json’}}
    const { data } = await axios.post
    (
    ‘http://172.16.45.198/api/Users/UserLogin’,
    {‘UserName’:UserName,’UserPassword’:UserPassword}, config,
    )
    dispatch({
    type:USER_LOGIN_SUCCESS,
    payload :data,
    })
    قبلش عرض میکنم که در بخش GET هیچ مشکلی ندارم
    ولی در این بخش با خطای NETWORK ERROR مواجه میشم
    نکته: من این درخواست رو در پستمن تست کردم و خطایی نداشتم

    • ابوالفضل حسن زاده

      سلام اقای دهقان وقت شما بخیر . اول از همه اینکه خیلی عذرخواهی میکنم جوابتون دیر شد و دوم اینکه ما username password رو که ارسال میکنیم تا access , refresh token دریافت کنیم باید با کلید های username , password باشه . یکی از مشکلات میتونه از اینجا باشه الان شما به این شکل نوشتید {‘UserName’:UserName,’UserPassword’:UserPassword}, باید تبدیل به این حالت بشه {‘username’:username, ‘password’:password} شما این حالت رو هم بررسی کنید اگر درست نشد کدتون رو روی گیت هاب قرار بدید و لینکش رو اینجا قرار بدید تا بررسی کنم . این لینک هم میتونه به شما کمک کنه .

  4. mehdi mahdavi (خریدار محصول)

    سلام تشکر از زحمات شما یه سوال داشتم تااین مرحله پیش اومدم وقتی صفحه را رفرش میکنم یوزر لوگ اوت میشه ممنون میشم راهنمای بفرمایید

    • ابوالفضل حسن زاده

      سلام متشکرم از محبت شما . ببینید زمانی که شما صفحه رو reload یا refresh میکینید ریداکس شما خالی میشه . احتمالا داخل فایل store.js در بخش initialstate متغیریی که برای اولین بار به ریداکس مقدار میده مشکل دارید و یا در جایی که لاگین میکنید و اطلاعات کاربر رو در localstorage ذخیره مکنید مشکل دارید و یا اینکه در هر دو جا درست عمل میکنید ولی در بخش initialstate دارید با نام اشتباه اطلاعات کاربر رو از localstorage میخونید . اگر که اشتباه نکنم در دوره اطلاعات کاربر رو با کلید userInfo ذخیره کردیم . شما این احتمالات رو بررسی کنید باز اگر که مشکل حل نشد بازگو کنید که من بیشتر بررسی کنم.
      ذخیره اطلاعات کاربر در مرورگر
      مقدار دهی اولیه ریداکس از اطلاعات ذخیره شده کاربر در مرورگر

  5. محمدرضا گوشکی (خریدار محصول)

    سلام استاد عزیز خسته نباشید ممنون از دوره عالی
    من هاست پایتونی دارم و در حال حاضر بخش بک اند که جنگو هست رو دیپلوی کرده ام. سوال اینجاست برای دیپلوی کردن بخش فرانت اند پروژه که با ری اکت هست باید هاست جدا تهیه کنم یا روی همان فضا پایتونی میتوان بخش فراند اند را مستقر کرد؟

  6. امین اجاقی (خریدار محصول)

    با سلام و درود خدمت استاد بزرگ و گرامی مصطفی آصفی عزیز استادم که مرا تا مرحله یک برنامه نویس فول استک شدن بسیار یاری رسانده است و من همیشه بدنبال آموزش های شما و همراه بودن با شما در تمام زمان ها هستم و سلام خدمت استاد و دوست و هم شاگردی عزیزم ابوالفضل حسن زاده عزیز و گرامی من پس از کمال در رشد دانشم در برنامه نویسی در مسیر های مختلف بسیار زیباست که بالاخره در یک شرکت که با زبان آنگولار و دات نت کار می کند شغل پیدا کردم یعنی علاوه بر تمام دروسی که با استاد عزیزم مصطفی آصفی و شما و استادان دیگری در همه زمینه های وب گذراندم آنگولار هم یاد گرفتم تا در این شرکت به کار پرداختم و بسیار شادم و شکر گزار خدای بزرگ و متعال و همچنین تمام دروسی که از گذشته شروع کردم را هم مخصوصا دروسی که از هم رویش می آید را دنبا می کنم و دانشم را گسترش می دهم و همینطور این دو دوره ی جدیدی که از این دوره از آموزش ها قرار دادید یعنی 6 و 7 را خریداری کرده ام تا در یک زمان مناسب شروع به یادگیری کنم زیرا هم رویش یک آموزشگاه اینترنتی بسیار مردمی است که با نور خدا و دل هایی بسیار نورانی که از سوی خداست دلی مانند دل پاک و آرام استاد عزیزم مصطفی آصفی که بسیار زیبا و دوستانه و بسیار نیکو در مسیر رشد دانش مردم ایران که خواهران و برادران هستیم این چنین کوشا هستین و از خدای متعال برکات بسیار از آشسمان نیکو هایشان برایتان می طلبم و کمال موفقیت تان از او خواستارم برکت خدا بر شما و عشق زندگیتان و خانواده ها و دوستان و آشناینتان.

  7. iman (خریدار محصول)

    استاد عزیز لطفا برای درگاه پرداخت بحثهای تکنیکی هم که در پروژه واقعی حتما رعایت کنیم رو هم ذکر کنید چون حالت معمولی اش زیاده اصل نکات تکنیکی هست که تو هر آموزشی پیدا نمیشه . مثلا اگر یک سبد خرید داشته باشیم در موبایل و بعد از مدتی با لپ تاپ محصول جدیدی اضافه کنیم . موارد قبلی در سبد خریدمان تعیین تکلیف بشود و از کاربر پرسیده شود به سبد اضافه شود یا موارد قبلی حدف شود. یا از آن مهمتر وقتی یک محصول در سبد داریم و دونفر همزمان درخواست خرید نهایی میزنند و به درگاه پرداخت میروند نفری که زودتر خرید رو زده و به درگاه رفته محصول برایش رزرو شود و در صورت نهایی شدن خرید که تعداد محصول ناموجود شود ولی اگر انصراف از خرید شود محصول از رزور برگشته و دوباره به تعدا محصولات موجود اضافه شود. یا مواردی چون ایجاد تخفیفات دوره ای یا خرید با کپن تخفیف

    • ابوالفضل حسن زاده

      سلام به شما . انشالله چشم حتما . فروشگاه رو که ساختیم در قالب فصل های کوچولو کوچولو این مشکلات رو هم حل میکنیم و اون رو تبدیل به یک فروشگاه حرفه ای و واقعی خواهیم کرد.

  8. رضا (خریدار محصول)

    درود بر شما

    یک نکته ای رو خواستم یاد آور بشم:
    در جایی اومدید و برای تعریف متغیر از var استفاده کردید که این کار منسوخ شده هستش و پیشنهاد شده به دلایل امنیتی و .. استفاده نشه و به جاش از let استفاده بشه.

    و راستی من با یک سری تغییرات کوچیک تونستم پروژه رو روی آخرین ورژن ری اکت یعنی 18 و کتابخونه هاش اجرا کنم. که البته در ورژن جدید ریداکس پیشنهاد شده از ریداکس تولکید استفاده بشه اما خب دستورات قدیمی هم به خوبی کار میکنن.
    “dependencies”: {
    “@hookform/resolvers”: “^2.8.10”,
    “@react-icons/all-files”: “^4.1.0”,
    “@testing-library/jest-dom”: “^5.16.4”,
    “@testing-library/react”: “^13.3.0”,
    “@testing-library/user-event”: “^14.2.0”,
    “axios”: “^0.27.2”,
    “bootstrap”: “^5.1.3”,
    “dayjs”: “^1.11.3”,
    “http-proxy-middleware”: “^2.0.6”,
    “jwt-decode”: “^3.1.2”,
    “react”: “^18.1.0”,
    “react-bootstrap”: “^2.4.0”,
    “react-dom”: “^18.1.0”,
    “react-hook-form”: “^7.32.0”,
    “react-icons”: “^4.4.0”,
    “react-redux”: “^8.0.2”,
    “react-router-bootstrap”: “^0.26.1”,
    “react-router-dom”: “^6.3.0”,
    “react-scripts”: “^5.0.1”,
    “redux”: “^4.2.0”,
    “redux-devtools-extension”: “^2.13.9”,
    “redux-thunk”: “^2.4.1”,
    “web-vitals”: “^2.1.4”,
    “yup”: “^0.32.11”
    },
    “scripts”: {
    “start”: “react-scripts start”,
    “build”: “react-scripts build”,
    “test”: “react-scripts test”,
    “eject”: “react-scripts eject”
    },
    “eslintConfig”: {
    “extends”: [
    “react-app”,
    “react-app/jest”
    ]
    },
    “browserslist”: {
    “production”: [
    “>0.2%”,
    “not dead”,
    “not op_mini all”
    ],
    “development”: [
    “last 1 chrome version”,
    “last 1 firefox version”,
    “last 1 safari version”
    ]
    }
    }

    در پایان من صفحه ی آپدیت پروفایل(profileScreen) رو با react-hook-form ساختم و اینجا قرارش میدم تا دوستان اگه مایل بودن استفاده کنن.

    import React, {useEffect, useState} from ‘react’;
    import {Button, Col, Form, Row} from “react-bootstrap”;
    import * as yup from “yup”;
    import {yupResolver} from “@hookform/resolvers/yup”;
    import {useForm} from “react-hook-form”;
    import {useDispatch, useSelector} from “react-redux”;
    import {getUserDetailAction, updateUserProfileAction, userLogoutAction} from “../actions/userActions”;
    import {useNavigate} from “react-router-dom”;
    import {USER_UPDATE_PROFILE_RESET} from “../constants/userConstans”;
    import Loader from “../components/Loader”;
    import Message from “../components/Message”;

    const ProfileScreen = () => {

    const navigate = useNavigate()

    const [name, setName] = useState(”);
    const [email, setEmail] = useState(”);
    const [successUpdate, setSuccessUpdate] = useState(”)

    const nameRegex = /^[a-zA-Z0-9]+$/;
    const emailRegex = /^\w+([-]?\w+)*@\w+([-]?\w+)*(\.\w{2,3})+$/
    const password = /^(?=.*[0-9])(?=.*[!@#$%^&*])[a-zA-Z0-9!@#$%^&*]{6,16}$/

    const validationSchema = yup.object().shape({
    name: yup.string()
    .required()
    .matches(nameRegex),
    email: yup.string()
    .required()
    .matches(emailRegex),
    password: yup.string()
    .required(‘Password is required’)
    .min(8)
    .matches(password),
    confirmPassword: yup.string()
    .required(‘Confirm Password is required’)
    .oneOf([yup.ref(‘password’)], ‘password must match’)
    });

    const {register, control, handleSubmit, formState: {errors}, reset} = useForm({
    resolver: yupResolver(validationSchema),
    defaultValues: {name: name, email: email}
    })

    const dispatch = useDispatch()
    const userLogin = useSelector(state => state.userLogin)
    const {userInfo} = userLogin

    const userDetails = useSelector(state => state.userDetails)
    const {user} = userDetails
    console.log(‘its user’, user)
    const errorOfUserDetails = userDetails.error
    const loadingOfUserDetails = userDetails.loading

    const userUpdateProfile = useSelector(state => state.userUpdateProfile)
    const {success} = userUpdateProfile
    const errorOfUserUpdateProfile = userUpdateProfile.error
    const loadingOfUserUpdateProfile = userUpdateProfile.loading

    let loading = false
    let error = ”

    if (loadingOfUserDetails || loadingOfUserUpdateProfile) {
    loading = true
    }

    if (errorOfUserDetails) {
    error = errorOfUserDetails
    } else if (errorOfUserUpdateProfile) {
    error = errorOfUserUpdateProfile
    }

    const TE = “Token is invalid or expired”

    useEffect(() => {
    if (errorOfUserDetails === TE || errorOfUserUpdateProfile === TE) {
    dispatch(userLogoutAction())
    navigate(‘/login?redirect=/profile’)
    }
    if (!userInfo) {
    navigate(‘/login’)
    } else {
    if (!user || !user.name || success) {
    dispatch({type: USER_UPDATE_PROFILE_RESET})
    dispatch(getUserDetailAction(‘profile’))
    } else {
    setName(user.name)
    setEmail(user.email)
    }
    }
    reset({name: name, email: email})
    }, [navigate, userInfo, user, dispatch, success, name, email, reset, errorOfUserDetails, errorOfUserUpdateProfile]);

    const submitForm = (data) => {
    const name = data.name
    const email = data.email
    const password = data.password
    dispatch(updateUserProfileAction({
    ‘id’: user._id,
    ‘name’: name,
    ’email’: email,
    ‘password’: password
    }))
    reset({password: ”, confirmPassword: ”})

    setSuccessUpdate(‘Profile Successfully Update’)
    }
    return (

    my profile
    {
    loading ? :
    error ? :
    successUpdate ? :

    }

    FullName

    {errors.name?.type === ‘required’ &&
    fullName is required}
    {errors.name?.type === ‘matches’ &&
    fullName not valid}

    EmailAddress

    {errors.email?.type === ‘required’ &&
    email is required}
    {errors.email?.type === ‘matches’ &&
    Invalid Email Address}

    password

    {errors.password?.type === ‘required’ &&
    {errors.password.message}}
    {errors.password?.type === ‘min’ &&
    minimum 8 characters}
    {errors.password?.type === ‘matches’ &&
    password not secure(use @,CL word,)}

    Confirm Password

    {errors.confirmPassword?.type === ‘required’ &&
    {errors.confirmPassword.message}}
    {errors.confirmPassword?.type === “oneOf” &&
    {errors.confirmPassword.message}}

    Update

    my orders

    );
    };

    export default ProfileScreen;

    • ابوالفضل حسن زاده

      سلام و درود بر شما . خیلی ممنون از یادآوری که کردید و افرین به شما که فراتر از پروژه پیش رفتید . انشالله که دیگر فراگیران هم استفاده کنند.

  9. ایمان نم (خریدار محصول)

    استاد عزیز در فصل های این دوره یک چیز به شدت نیاز هست که ممنون میشم به عنوان یک فصل جداگانه این قابلیت رو قرار بدهید. در انتها بی زحمت این پروژه رو PWA اش رو هم پیاده سازی کنید که بسیار لازم و کاربردی هست.

    • ابوالفضل حسن زاده

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

  10. ایمان (خریدار محصول)

    این فصول باقی مانده رو کی انشا.. انتشار میدید؟ مدرس آقای آسفی هستند یا آقای حسن زاده ؟

    • ابوالفضل حسن زاده

      سلام . انشالله به زودی منتشر میکنیم، بنده تا اخر این پروژه در خدمت شما هستم.

  11. iman (خریدار محصول)

    با سلام . این دوره فصل دیگری هم دارید یا تمام شد؟

    • ابوالفضل حسن زاده

      سلام وقت شما بخیر . دو یا سه فصل دیگر باقی مانده . فصل های ثبت سفارش و پرداخت انلاین و …….

دیدگاه خود را بنویسید