From b0503b8e03cf5149e4cc03d16a90c473eb1e86ba Mon Sep 17 00:00:00 2001 From: sweetdev108 <138671430+smashing9@users.noreply.github.com> Date: Mon, 3 Jun 2024 11:16:04 -0700 Subject: [PATCH] init --- .eslintrc.json | 3 + .gitignore | 36 + .hintrc | 14 + README.md | 1 + app/bookings/layout.tsx | 7 + app/bookings/page.tsx | 9 + app/calendar/layout.tsx | 5 + app/calendar/page.tsx | 59 + app/create-password/layout.tsx | 9 + app/create-password/page.tsx | 73 + app/dashboard/layout.tsx | 9 + app/dashboard/page.tsx | 28 + app/favicon.ico | Bin 0 -> 25931 bytes app/forgot-password/layout.tsx | 9 + app/forgot-password/page.tsx | 72 + app/globals.css | 125 + app/help/layout.tsx | 5 + app/help/page.tsx | 18 + app/job-posting/layout.tsx | 9 + app/job-posting/page.tsx | 9 + app/layout.tsx | 46 + app/messages/layout.tsx | 5 + app/messages/page.tsx | 26 + app/otp-verify/layout.tsx | 9 + app/otp-verify/page.tsx | 74 + app/page.tsx | 37 + app/revenue/layout.tsx | 5 + app/revenue/page.tsx | 18 + app/schedule/layout.tsx | 7 + app/schedule/page.tsx | 46 + app/signin/layout.tsx | 9 + app/signin/page.tsx | 107 + app/signup/layout.tsx | 9 + app/signup/page.tsx | 121 + components/bookings/DTBooking.tsx | 93 + components/bookings/MBBooking.tsx | 71 + components/bookings/index.tsx | 168 + .../create-password/CreatePasswordForm.tsx | 151 + components/custom/CardTemplate.tsx | 45 + components/custom/Checkbox.tsx | 109 + components/custom/EmptyAvatar.tsx | 17 + components/custom/Input.tsx | 103 + components/custom/Loading.tsx | 9 + components/custom/MonthPick.tsx | 51 + components/custom/Select.tsx | 59 + components/custom/StatusBadge.tsx | 57 + components/custom/buttons/Button.tsx | 69 + components/custom/buttons/TabButton.tsx | 26 + .../calendars/BCalendar/AddEventDialog.tsx | 404 ++ .../calendars/BCalendar/DelEventDialog.tsx | 82 + .../custom/calendars/BCalendar/EventItem.tsx | 10 + .../custom/calendars/BCalendar/WeekDay.tsx | 29 + .../custom/calendars/BCalendar/index.tsx | 188 + components/custom/calendars/SCalendar.tsx | 107 + components/dashboard/AvatarCard.tsx | 193 + components/dashboard/BookingsCard.tsx | 119 + components/dashboard/CalendarCard.tsx | 29 + .../CompleteProfileCard/AddLicenseNum.tsx | 157 + .../BackgroundCheckDialog.tsx | 349 + .../BackgroundDescDialog.tsx | 201 + .../DentalTrainingDialog.tsx | 191 + .../CompleteProfileCard/LanguagesDialog.tsx | 275 + .../CompleteProfileCard/ListSkillsDialog.tsx | 281 + .../UploadProfilePicDialog.tsx | 334 + .../CompleteProfileCard/VerifyEmailDialog.tsx | 133 + .../VerifyPhoneDialog/OTPForm.tsx | 88 + .../VerifyPhoneDialog/PhoneForm.tsx | 76 + .../VerifyPhoneDialog/index.tsx | 75 + .../dashboard/CompleteProfileCard/index.tsx | 159 + .../JobPostingCard/JPCardTemplate.tsx | 178 + .../JobPostingCard/JobApplyDialog.tsx | 174 + .../JobPostingCard/JobDescriptionDialog.tsx | 69 + components/dashboard/JobPostingCard/index.tsx | 113 + .../forgot-password/ForgotPasswordForm.tsx | 90 + components/help/index.tsx | 102 + components/job-posting/JobPostingPage.tsx | 221 + components/job-posting/JobTabItem.tsx | 429 ++ components/layout/AuthLayout/LeftSection.tsx | 18 + components/layout/AuthLayout/index.tsx | 47 + .../DashboardLayout/DPLayout/Header.tsx | 246 + .../DPLayout/Navbar/NavItem.tsx | 36 + .../DashboardLayout/DPLayout/Navbar/index.tsx | 63 + .../layout/DashboardLayout/DPLayout/index.tsx | 29 + .../DashboardLayout/MBLayout/Header.tsx | 223 + .../MBLayout/Navbar/NavItem.tsx | 34 + .../DashboardLayout/MBLayout/Navbar/index.tsx | 56 + .../layout/DashboardLayout/MBLayout/index.tsx | 15 + components/layout/DashboardLayout/MsgMenu.tsx | 74 + .../layout/DashboardLayout/NotiDialog.tsx | 145 + .../layout/DashboardLayout/NotiMenu.tsx | 118 + .../ProfileDialogue/StatusItem.tsx | 49 + .../DashboardLayout/ProfileDialogue/index.tsx | 400 ++ .../AccountSettingDialog.tsx | 158 + .../ChangePasswordDialog.tsx | 201 + .../DeleteAccountDialog.tsx | 82 + .../AddBillingCardDialog.tsx | 160 + .../BillingInfoDialog/index.tsx | 232 + .../BookingSettingDialog/MoneyDialog.tsx | 164 + .../BookingSettingDialog/TimeDialog.tsx | 173 + .../BookingSettingDialog/TravelDialog.tsx | 146 + .../BookingSettingDialog/index.tsx | 289 + .../EditProfileDialog/EditProfile.tsx | 312 + .../ProfileMenuDialogs/EventsDialg/index.tsx | 132 + .../LogoutDialog/LogOutDialogue.tsx | 78 + .../NotificationDialog/index.tsx | 276 + .../ProfileMenuDialogs/OfficeDialog/index.tsx | 481 ++ .../ProfileDialog/EditProfileDialog.tsx | 409 ++ .../ProfileDialog/index.tsx | 514 ++ .../layout/DashboardLayout/RecommendAlert.tsx | 53 + .../layout/DashboardLayout/ReferDialog.tsx | 233 + .../layout/DashboardLayout/UserMenu.tsx | 74 + components/layout/DashboardLayout/index.tsx | 58 + components/messages/Chat/ReceivedMessage.tsx | 39 + components/messages/Chat/SentMessage.tsx | 36 + components/messages/Chat/index.tsx | 203 + components/messages/DP.tsx | 57 + components/messages/MB.tsx | 26 + components/messages/UserList/Item.tsx | 50 + components/messages/UserList/index.tsx | 63 + components/messages/tempData.ts | 153 + components/otp-verify/OTPVerifyForm.tsx | 127 + components/revenue/BankItem.tsx | 64 + components/revenue/Dialogue.tsx | 164 + components/revenue/DirectDepositTab.tsx | 230 + components/revenue/EarningTab.tsx | 36 + components/revenue/TaxInfoTab.tsx | 247 + components/revenue/index.tsx | 53 + components/schedule/AddDialog.tsx | 304 + components/schedule/DTExceptionItem.tsx | 99 + components/schedule/DeleteDialog.tsx | 93 + components/schedule/EditDialog.tsx | 329 + components/schedule/Exceptions.tsx | 116 + components/schedule/MBExceptionItem.tsx | 95 + components/schedule/TypicalItem.tsx | 201 + components/schedule/TypicalWeek.tsx | 247 + components/schedule/index.tsx | 78 + components/signin/SigninForm.tsx | 145 + components/signup/FirstStepSignup.tsx | 110 + components/signup/SecondStepSignup.tsx | 190 + components/signup/Signup.tsx | 156 + components/signup/ThirdStepSignUp.tsx | 223 + contexts/AuthContext.tsx | 90 + contexts/CalendarContext.tsx | 24 + contexts/LoadingContext.tsx | 19 + contexts/ScheduleContext.tsx | 30 + contexts/UserContext.tsx | 33 + hooks/useLoading.ts | 6 + libraries/emailTemplates/emailTemplates.tsx | 5870 +++++++++++++++++ libraries/formik.ts | 3 + libraries/iconify-react.ts | 5 + libraries/material-tailwind.ts | 86 + libraries/moment.ts | 3 + libraries/react-big-calendar.ts | 5 + libraries/react-calendar.ts | 3 + libraries/react-circular-progressbar.ts | 5 + libraries/react-phone-number-input.ts | 13 + libraries/react-spinners.ts | 3 + libraries/react-toastify.ts | 3 + libraries/usehooks-ts.ts | 7 + libraries/uuid.ts | 3 + libraries/yup.ts | 3 + next.config.js | 15 + package-lock.json | 5747 ++++++++++++++++ package.json | 49 + postcss.config.js | 6 + public/assets/images/Group.png | Bin 0 -> 5847 bytes public/assets/images/avatar-brooklyn.png | Bin 0 -> 2550 bytes public/assets/images/avatar-cameron.png | Bin 0 -> 3167 bytes public/assets/images/avatar-guy.png | Bin 0 -> 3640 bytes public/assets/images/avatar-jane.png | Bin 0 -> 3883 bytes public/assets/images/avatar-lg.png | Bin 0 -> 8470 bytes public/assets/images/avatar-wade.png | Bin 0 -> 3199 bytes public/assets/images/avatar.png | Bin 0 -> 3107 bytes public/assets/images/bank1.png | Bin 0 -> 2957 bytes public/assets/images/bank2.png | Bin 0 -> 1851 bytes public/assets/images/bank3.png | Bin 0 -> 3066 bytes public/assets/images/bankCard.png | Bin 0 -> 7371 bytes public/assets/images/bankLogo1.png | Bin 0 -> 4387 bytes public/assets/images/bankLogo2.png | Bin 0 -> 4736 bytes public/assets/images/calendar.png | Bin 0 -> 59690 bytes public/assets/images/chat-avatar-1.png | Bin 0 -> 5639 bytes public/assets/images/chat-avatar-2.png | Bin 0 -> 5779 bytes public/assets/images/chat-avatar-3.png | Bin 0 -> 4790 bytes public/assets/images/chat-avatar-4.png | Bin 0 -> 5966 bytes public/assets/images/chat-avatar-5.png | Bin 0 -> 4652 bytes public/assets/images/chat-avatar-6.png | Bin 0 -> 5542 bytes public/assets/images/comment-of-calendar.png | Bin 0 -> 14837 bytes public/assets/images/initialLogo.png | Bin 0 -> 1246 bytes public/assets/images/letterLogo.png | Bin 0 -> 1882 bytes public/assets/images/letter_logo_purple.png | Bin 0 -> 832 bytes public/assets/images/logo-white.png | Bin 0 -> 1120 bytes public/assets/images/logo.png | Bin 0 -> 1036 bytes public/assets/images/logo_mb.png | Bin 0 -> 1785 bytes public/assets/images/logo_purple.png | Bin 0 -> 583 bytes public/assets/images/map.png | Bin 0 -> 101720 bytes public/assets/images/office1.png | Bin 0 -> 5628 bytes public/assets/images/office2.png | Bin 0 -> 4563 bytes public/assets/images/office3.png | Bin 0 -> 5142 bytes public/assets/images/office4.png | Bin 0 -> 5396 bytes public/assets/images/org_bmc_dental.png | Bin 0 -> 5032 bytes public/assets/images/org_gj_dental.png | Bin 0 -> 3396 bytes .../images/org_hulen_crossing_dental.png | Bin 0 -> 5653 bytes public/assets/images/org_marvel_dental.png | Bin 0 -> 5626 bytes .../images/org_oakheights_family_dental.png | Bin 0 -> 3437 bytes public/assets/images/revenueTab1.svg | 170 + public/assets/images/revenueTab2.svg | 238 + public/assets/images/scheduleImg.png | Bin 0 -> 16662 bytes public/assets/images/socialLink1.png | Bin 0 -> 1978 bytes public/assets/images/socialLink2.png | Bin 0 -> 1526 bytes public/assets/images/socialLink3.png | Bin 0 -> 1619 bytes public/assets/images/socialLink4.png | Bin 0 -> 1794 bytes public/next.svg | 1 + .../templateImages/background-successful1.png | Bin 0 -> 8532 bytes public/templateImages/bank-confirmation1.png | Bin 0 -> 14532 bytes public/templateImages/bank-verification.png | Bin 0 -> 9520 bytes public/templateImages/booking-confirmed1.png | Bin 0 -> 15395 bytes public/templateImages/booking-reject1.png | Bin 0 -> 13977 bytes public/templateImages/deactivate1.png | Bin 0 -> 14057 bytes public/templateImages/dental-logo.png | Bin 0 -> 3382 bytes public/templateImages/device1.png | Bin 0 -> 4439 bytes public/templateImages/doctor1.png | Bin 0 -> 24577 bytes public/templateImages/doctor2.png | Bin 0 -> 27577 bytes public/templateImages/doctor3.png | Bin 0 -> 30019 bytes public/templateImages/doctor4.png | Bin 0 -> 27469 bytes public/templateImages/email-1.png | Bin 0 -> 4768 bytes public/templateImages/facebook1.png | Bin 0 -> 1652 bytes public/templateImages/forgot1.png | Bin 0 -> 16547 bytes public/templateImages/gallery.png | Bin 0 -> 1277 bytes public/templateImages/instagram1.png | Bin 0 -> 1989 bytes public/templateImages/invitation1.png | Bin 0 -> 12267 bytes public/templateImages/invoice-thanks.png | Bin 0 -> 18095 bytes public/templateImages/invoice1.png | Bin 0 -> 6013 bytes public/templateImages/job-accept1.png | Bin 0 -> 20735 bytes public/templateImages/job-posted1.png | Bin 0 -> 13741 bytes public/templateImages/job-reject1.png | Bin 0 -> 20327 bytes public/templateImages/link1.png | Bin 0 -> 823 bytes public/templateImages/linkedin1.png | Bin 0 -> 1743 bytes public/templateImages/message-recieved1.png | Bin 0 -> 7936 bytes public/templateImages/new-dentist1.png | Bin 0 -> 8892 bytes public/templateImages/new-job1.png | Bin 0 -> 12267 bytes public/templateImages/notification1.png | Bin 0 -> 17347 bytes public/templateImages/password1.png | Bin 0 -> 6985 bytes public/templateImages/payment-office1.png | Bin 0 -> 18087 bytes public/templateImages/payment-successful1.png | Bin 0 -> 15098 bytes public/templateImages/postion-closed1.png | Bin 0 -> 15826 bytes public/templateImages/profile1.png | Bin 0 -> 10591 bytes public/templateImages/rewards1.png | Bin 0 -> 8335 bytes public/templateImages/short-listed1.png | Bin 0 -> 22168 bytes public/templateImages/twitter1.png | Bin 0 -> 2018 bytes public/templateImages/user-image1.png | Bin 0 -> 15165 bytes public/templateImages/user-profile1.png | Bin 0 -> 15136 bytes public/templateImages/welcome1.png | Bin 0 -> 11272 bytes public/vercel.svg | 1 + tailwind.config.ts | 23 + tsconfig.json | 33 + utils/api.ts | 43 + utils/constants.ts | 295 + utils/functions.ts | 34 + utils/interfaces.ts | 281 + utils/tempData.ts | 490 ++ utils/types.ts | 10 + utils/validation.ts | 28 + 262 files changed, 30293 insertions(+) create mode 100644 .eslintrc.json create mode 100644 .gitignore create mode 100644 .hintrc create mode 100644 README.md create mode 100644 app/bookings/layout.tsx create mode 100644 app/bookings/page.tsx create mode 100644 app/calendar/layout.tsx create mode 100644 app/calendar/page.tsx create mode 100644 app/create-password/layout.tsx create mode 100644 app/create-password/page.tsx create mode 100644 app/dashboard/layout.tsx create mode 100644 app/dashboard/page.tsx create mode 100644 app/favicon.ico create mode 100644 app/forgot-password/layout.tsx create mode 100644 app/forgot-password/page.tsx create mode 100644 app/globals.css create mode 100644 app/help/layout.tsx create mode 100644 app/help/page.tsx create mode 100644 app/job-posting/layout.tsx create mode 100644 app/job-posting/page.tsx create mode 100644 app/layout.tsx create mode 100644 app/messages/layout.tsx create mode 100644 app/messages/page.tsx create mode 100644 app/otp-verify/layout.tsx create mode 100644 app/otp-verify/page.tsx create mode 100644 app/page.tsx create mode 100644 app/revenue/layout.tsx create mode 100644 app/revenue/page.tsx create mode 100644 app/schedule/layout.tsx create mode 100644 app/schedule/page.tsx create mode 100644 app/signin/layout.tsx create mode 100644 app/signin/page.tsx create mode 100644 app/signup/layout.tsx create mode 100644 app/signup/page.tsx create mode 100644 components/bookings/DTBooking.tsx create mode 100644 components/bookings/MBBooking.tsx create mode 100644 components/bookings/index.tsx create mode 100644 components/create-password/CreatePasswordForm.tsx create mode 100644 components/custom/CardTemplate.tsx create mode 100644 components/custom/Checkbox.tsx create mode 100644 components/custom/EmptyAvatar.tsx create mode 100644 components/custom/Input.tsx create mode 100644 components/custom/Loading.tsx create mode 100644 components/custom/MonthPick.tsx create mode 100644 components/custom/Select.tsx create mode 100644 components/custom/StatusBadge.tsx create mode 100644 components/custom/buttons/Button.tsx create mode 100644 components/custom/buttons/TabButton.tsx create mode 100644 components/custom/calendars/BCalendar/AddEventDialog.tsx create mode 100644 components/custom/calendars/BCalendar/DelEventDialog.tsx create mode 100644 components/custom/calendars/BCalendar/EventItem.tsx create mode 100644 components/custom/calendars/BCalendar/WeekDay.tsx create mode 100644 components/custom/calendars/BCalendar/index.tsx create mode 100644 components/custom/calendars/SCalendar.tsx create mode 100644 components/dashboard/AvatarCard.tsx create mode 100644 components/dashboard/BookingsCard.tsx create mode 100644 components/dashboard/CalendarCard.tsx create mode 100644 components/dashboard/CompleteProfileCard/AddLicenseNum.tsx create mode 100644 components/dashboard/CompleteProfileCard/BackgroundCheckDialog.tsx create mode 100644 components/dashboard/CompleteProfileCard/BackgroundDescDialog.tsx create mode 100644 components/dashboard/CompleteProfileCard/DentalTrainingDialog.tsx create mode 100644 components/dashboard/CompleteProfileCard/LanguagesDialog.tsx create mode 100644 components/dashboard/CompleteProfileCard/ListSkillsDialog.tsx create mode 100644 components/dashboard/CompleteProfileCard/UploadProfilePicDialog.tsx create mode 100644 components/dashboard/CompleteProfileCard/VerifyEmailDialog.tsx create mode 100644 components/dashboard/CompleteProfileCard/VerifyPhoneDialog/OTPForm.tsx create mode 100644 components/dashboard/CompleteProfileCard/VerifyPhoneDialog/PhoneForm.tsx create mode 100644 components/dashboard/CompleteProfileCard/VerifyPhoneDialog/index.tsx create mode 100644 components/dashboard/CompleteProfileCard/index.tsx create mode 100644 components/dashboard/JobPostingCard/JPCardTemplate.tsx create mode 100644 components/dashboard/JobPostingCard/JobApplyDialog.tsx create mode 100644 components/dashboard/JobPostingCard/JobDescriptionDialog.tsx create mode 100644 components/dashboard/JobPostingCard/index.tsx create mode 100644 components/forgot-password/ForgotPasswordForm.tsx create mode 100644 components/help/index.tsx create mode 100644 components/job-posting/JobPostingPage.tsx create mode 100644 components/job-posting/JobTabItem.tsx create mode 100644 components/layout/AuthLayout/LeftSection.tsx create mode 100644 components/layout/AuthLayout/index.tsx create mode 100644 components/layout/DashboardLayout/DPLayout/Header.tsx create mode 100644 components/layout/DashboardLayout/DPLayout/Navbar/NavItem.tsx create mode 100644 components/layout/DashboardLayout/DPLayout/Navbar/index.tsx create mode 100644 components/layout/DashboardLayout/DPLayout/index.tsx create mode 100644 components/layout/DashboardLayout/MBLayout/Header.tsx create mode 100644 components/layout/DashboardLayout/MBLayout/Navbar/NavItem.tsx create mode 100644 components/layout/DashboardLayout/MBLayout/Navbar/index.tsx create mode 100644 components/layout/DashboardLayout/MBLayout/index.tsx create mode 100644 components/layout/DashboardLayout/MsgMenu.tsx create mode 100644 components/layout/DashboardLayout/NotiDialog.tsx create mode 100644 components/layout/DashboardLayout/NotiMenu.tsx create mode 100644 components/layout/DashboardLayout/ProfileDialogue/StatusItem.tsx create mode 100644 components/layout/DashboardLayout/ProfileDialogue/index.tsx create mode 100644 components/layout/DashboardLayout/ProfileMenuDialogs/AccountSettingDialog/AccountSettingDialog.tsx create mode 100644 components/layout/DashboardLayout/ProfileMenuDialogs/AccountSettingDialog/ChangePasswordDialog.tsx create mode 100644 components/layout/DashboardLayout/ProfileMenuDialogs/AccountSettingDialog/DeleteAccountDialog.tsx create mode 100644 components/layout/DashboardLayout/ProfileMenuDialogs/BillingInfoDialog/AddBillingCardDialog.tsx create mode 100644 components/layout/DashboardLayout/ProfileMenuDialogs/BillingInfoDialog/index.tsx create mode 100644 components/layout/DashboardLayout/ProfileMenuDialogs/BookingSettingDialog/MoneyDialog.tsx create mode 100644 components/layout/DashboardLayout/ProfileMenuDialogs/BookingSettingDialog/TimeDialog.tsx create mode 100644 components/layout/DashboardLayout/ProfileMenuDialogs/BookingSettingDialog/TravelDialog.tsx create mode 100644 components/layout/DashboardLayout/ProfileMenuDialogs/BookingSettingDialog/index.tsx create mode 100644 components/layout/DashboardLayout/ProfileMenuDialogs/EditProfileDialog/EditProfile.tsx create mode 100644 components/layout/DashboardLayout/ProfileMenuDialogs/EventsDialg/index.tsx create mode 100644 components/layout/DashboardLayout/ProfileMenuDialogs/LogoutDialog/LogOutDialogue.tsx create mode 100644 components/layout/DashboardLayout/ProfileMenuDialogs/NotificationDialog/index.tsx create mode 100644 components/layout/DashboardLayout/ProfileMenuDialogs/OfficeDialog/index.tsx create mode 100644 components/layout/DashboardLayout/ProfileMenuDialogs/ProfileDialog/EditProfileDialog.tsx create mode 100644 components/layout/DashboardLayout/ProfileMenuDialogs/ProfileDialog/index.tsx create mode 100644 components/layout/DashboardLayout/RecommendAlert.tsx create mode 100644 components/layout/DashboardLayout/ReferDialog.tsx create mode 100644 components/layout/DashboardLayout/UserMenu.tsx create mode 100644 components/layout/DashboardLayout/index.tsx create mode 100644 components/messages/Chat/ReceivedMessage.tsx create mode 100644 components/messages/Chat/SentMessage.tsx create mode 100644 components/messages/Chat/index.tsx create mode 100644 components/messages/DP.tsx create mode 100644 components/messages/MB.tsx create mode 100644 components/messages/UserList/Item.tsx create mode 100644 components/messages/UserList/index.tsx create mode 100644 components/messages/tempData.ts create mode 100644 components/otp-verify/OTPVerifyForm.tsx create mode 100644 components/revenue/BankItem.tsx create mode 100644 components/revenue/Dialogue.tsx create mode 100644 components/revenue/DirectDepositTab.tsx create mode 100644 components/revenue/EarningTab.tsx create mode 100644 components/revenue/TaxInfoTab.tsx create mode 100644 components/revenue/index.tsx create mode 100644 components/schedule/AddDialog.tsx create mode 100644 components/schedule/DTExceptionItem.tsx create mode 100644 components/schedule/DeleteDialog.tsx create mode 100644 components/schedule/EditDialog.tsx create mode 100644 components/schedule/Exceptions.tsx create mode 100644 components/schedule/MBExceptionItem.tsx create mode 100644 components/schedule/TypicalItem.tsx create mode 100644 components/schedule/TypicalWeek.tsx create mode 100644 components/schedule/index.tsx create mode 100644 components/signin/SigninForm.tsx create mode 100644 components/signup/FirstStepSignup.tsx create mode 100644 components/signup/SecondStepSignup.tsx create mode 100644 components/signup/Signup.tsx create mode 100644 components/signup/ThirdStepSignUp.tsx create mode 100644 contexts/AuthContext.tsx create mode 100644 contexts/CalendarContext.tsx create mode 100644 contexts/LoadingContext.tsx create mode 100644 contexts/ScheduleContext.tsx create mode 100644 contexts/UserContext.tsx create mode 100644 hooks/useLoading.ts create mode 100644 libraries/emailTemplates/emailTemplates.tsx create mode 100644 libraries/formik.ts create mode 100644 libraries/iconify-react.ts create mode 100644 libraries/material-tailwind.ts create mode 100644 libraries/moment.ts create mode 100644 libraries/react-big-calendar.ts create mode 100644 libraries/react-calendar.ts create mode 100644 libraries/react-circular-progressbar.ts create mode 100644 libraries/react-phone-number-input.ts create mode 100644 libraries/react-spinners.ts create mode 100644 libraries/react-toastify.ts create mode 100644 libraries/usehooks-ts.ts create mode 100644 libraries/uuid.ts create mode 100644 libraries/yup.ts create mode 100644 next.config.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 postcss.config.js create mode 100644 public/assets/images/Group.png create mode 100644 public/assets/images/avatar-brooklyn.png create mode 100644 public/assets/images/avatar-cameron.png create mode 100644 public/assets/images/avatar-guy.png create mode 100644 public/assets/images/avatar-jane.png create mode 100644 public/assets/images/avatar-lg.png create mode 100644 public/assets/images/avatar-wade.png create mode 100644 public/assets/images/avatar.png create mode 100644 public/assets/images/bank1.png create mode 100644 public/assets/images/bank2.png create mode 100644 public/assets/images/bank3.png create mode 100644 public/assets/images/bankCard.png create mode 100644 public/assets/images/bankLogo1.png create mode 100644 public/assets/images/bankLogo2.png create mode 100644 public/assets/images/calendar.png create mode 100644 public/assets/images/chat-avatar-1.png create mode 100644 public/assets/images/chat-avatar-2.png create mode 100644 public/assets/images/chat-avatar-3.png create mode 100644 public/assets/images/chat-avatar-4.png create mode 100644 public/assets/images/chat-avatar-5.png create mode 100644 public/assets/images/chat-avatar-6.png create mode 100644 public/assets/images/comment-of-calendar.png create mode 100644 public/assets/images/initialLogo.png create mode 100644 public/assets/images/letterLogo.png create mode 100644 public/assets/images/letter_logo_purple.png create mode 100644 public/assets/images/logo-white.png create mode 100644 public/assets/images/logo.png create mode 100644 public/assets/images/logo_mb.png create mode 100644 public/assets/images/logo_purple.png create mode 100644 public/assets/images/map.png create mode 100644 public/assets/images/office1.png create mode 100644 public/assets/images/office2.png create mode 100644 public/assets/images/office3.png create mode 100644 public/assets/images/office4.png create mode 100644 public/assets/images/org_bmc_dental.png create mode 100644 public/assets/images/org_gj_dental.png create mode 100644 public/assets/images/org_hulen_crossing_dental.png create mode 100644 public/assets/images/org_marvel_dental.png create mode 100644 public/assets/images/org_oakheights_family_dental.png create mode 100644 public/assets/images/revenueTab1.svg create mode 100644 public/assets/images/revenueTab2.svg create mode 100644 public/assets/images/scheduleImg.png create mode 100644 public/assets/images/socialLink1.png create mode 100644 public/assets/images/socialLink2.png create mode 100644 public/assets/images/socialLink3.png create mode 100644 public/assets/images/socialLink4.png create mode 100644 public/next.svg create mode 100644 public/templateImages/background-successful1.png create mode 100644 public/templateImages/bank-confirmation1.png create mode 100644 public/templateImages/bank-verification.png create mode 100644 public/templateImages/booking-confirmed1.png create mode 100644 public/templateImages/booking-reject1.png create mode 100644 public/templateImages/deactivate1.png create mode 100644 public/templateImages/dental-logo.png create mode 100644 public/templateImages/device1.png create mode 100644 public/templateImages/doctor1.png create mode 100644 public/templateImages/doctor2.png create mode 100644 public/templateImages/doctor3.png create mode 100644 public/templateImages/doctor4.png create mode 100644 public/templateImages/email-1.png create mode 100644 public/templateImages/facebook1.png create mode 100644 public/templateImages/forgot1.png create mode 100644 public/templateImages/gallery.png create mode 100644 public/templateImages/instagram1.png create mode 100644 public/templateImages/invitation1.png create mode 100644 public/templateImages/invoice-thanks.png create mode 100644 public/templateImages/invoice1.png create mode 100644 public/templateImages/job-accept1.png create mode 100644 public/templateImages/job-posted1.png create mode 100644 public/templateImages/job-reject1.png create mode 100644 public/templateImages/link1.png create mode 100644 public/templateImages/linkedin1.png create mode 100644 public/templateImages/message-recieved1.png create mode 100644 public/templateImages/new-dentist1.png create mode 100644 public/templateImages/new-job1.png create mode 100644 public/templateImages/notification1.png create mode 100644 public/templateImages/password1.png create mode 100644 public/templateImages/payment-office1.png create mode 100644 public/templateImages/payment-successful1.png create mode 100644 public/templateImages/postion-closed1.png create mode 100644 public/templateImages/profile1.png create mode 100644 public/templateImages/rewards1.png create mode 100644 public/templateImages/short-listed1.png create mode 100644 public/templateImages/twitter1.png create mode 100644 public/templateImages/user-image1.png create mode 100644 public/templateImages/user-profile1.png create mode 100644 public/templateImages/welcome1.png create mode 100644 public/vercel.svg create mode 100644 tailwind.config.ts create mode 100644 tsconfig.json create mode 100644 utils/api.ts create mode 100644 utils/constants.ts create mode 100644 utils/functions.ts create mode 100644 utils/interfaces.ts create mode 100644 utils/tempData.ts create mode 100644 utils/types.ts create mode 100644 utils/validation.ts diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..bffb357 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fd3dbb5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,36 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js +.yarn/install-state.gz + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/.hintrc b/.hintrc new file mode 100644 index 0000000..4d37573 --- /dev/null +++ b/.hintrc @@ -0,0 +1,14 @@ +{ + "extends": [ + "development" + ], + "hints": { + "axe/forms": [ + "default", + { + "select-name": "off", + "label": "off" + } + ] + } +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..34396f1 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# dental_frontend diff --git a/app/bookings/layout.tsx b/app/bookings/layout.tsx new file mode 100644 index 0000000..4aa13e8 --- /dev/null +++ b/app/bookings/layout.tsx @@ -0,0 +1,7 @@ +import { ReactNode } from "react"; + +export default function BookingLayout({ children }: { children: ReactNode }) { + return ( +
{children}
+ ); +} diff --git a/app/bookings/page.tsx b/app/bookings/page.tsx new file mode 100644 index 0000000..fcf18f6 --- /dev/null +++ b/app/bookings/page.tsx @@ -0,0 +1,9 @@ +import BookingCard from "@/components/bookings"; + +export default function Booking() { + return ( + <> + + + ); +} diff --git a/app/calendar/layout.tsx b/app/calendar/layout.tsx new file mode 100644 index 0000000..959965d --- /dev/null +++ b/app/calendar/layout.tsx @@ -0,0 +1,5 @@ +import { ILayoutProps } from "@/utils/interfaces"; + +export default function CalendarLayout({ children }: ILayoutProps) { + return
{children}
; +} diff --git a/app/calendar/page.tsx b/app/calendar/page.tsx new file mode 100644 index 0000000..d35b65a --- /dev/null +++ b/app/calendar/page.tsx @@ -0,0 +1,59 @@ +"use client"; +import { useEffect } from "react"; +import { Card, CardBody } from "@/libraries/material-tailwind"; +import { toast } from "@/libraries/react-toastify"; +import api from "@/utils/api"; +import { getErrorMessage } from "@/utils/functions"; +import useLoading from "@/hooks/useLoading"; +import { useCalendar } from "@/contexts/CalendarContext"; +import BCalendar from "@/components/custom/calendars/BCalendar"; +import { IEvent } from "@/utils/interfaces"; +import { useUser } from "@/contexts/UserContext"; + +let isFirstLoad = true; + +export default function CalendarPage() { + const { setIsLoading } = useLoading(); + const { setEvents } = useCalendar(); + const {userData} = useUser(); + + useEffect(() => { + if(!userData || userData?.userType == 1) return ; + if (isFirstLoad) { + setIsLoading(true); + api + .post("/calendar/events/list") + .then((res) => { + const processedData: Array = res.data.map( + (dataItem: { + id: string; + title: string; + doubleBooked: boolean; + date: string; + }) => ({ + id: dataItem.id, + title: dataItem.title, + doubleBooked: dataItem.doubleBooked, + start: new Date(`${dataItem.date}T00:00:00.00Z`), + end: new Date(`${dataItem.date}T23:59:59.00Z`), + }) + ); + setEvents(processedData); + setIsLoading(false); + }) + .catch((err) => { + toast.error(getErrorMessage(err)); + setIsLoading(false); + }); + } + isFirstLoad = false; + }, []); + + return ( + + + + + + ); +} diff --git a/app/create-password/layout.tsx b/app/create-password/layout.tsx new file mode 100644 index 0000000..0a409ce --- /dev/null +++ b/app/create-password/layout.tsx @@ -0,0 +1,9 @@ +import { ReactNode } from "react"; + +interface IProps { + children: ReactNode; +} + +export default function CreatePasswordLayout({ children }: IProps) { + return children; +} diff --git a/app/create-password/page.tsx b/app/create-password/page.tsx new file mode 100644 index 0000000..710b81e --- /dev/null +++ b/app/create-password/page.tsx @@ -0,0 +1,73 @@ +"use client"; +import Image from "next/image"; +import LeftSection from "@/components/layout/AuthLayout/LeftSection"; +import { Typography } from "@/libraries/material-tailwind"; +import CreatePasswordForm from "@/components/create-password/CreatePasswordForm"; +import Loading from "@/components/custom/Loading"; +import { useState } from "react"; + +export default function CreatePasswordPage() { + const [loading, setLoading] = useState(false); + + return ( + <> + +
+ + + Dental Jobs + +
+
+ + +
+ + Habitasse leo mi enim condimentum rhoncus. Sed non tortor gravida . + +
+
+ {loading ? ( + + ) : ( +
+ + Create New Password + + +
+ )} +
+ + ); +} diff --git a/app/dashboard/layout.tsx b/app/dashboard/layout.tsx new file mode 100644 index 0000000..4bdc668 --- /dev/null +++ b/app/dashboard/layout.tsx @@ -0,0 +1,9 @@ +import { ReactNode } from "react"; + +export default function DashboardLayout({ children }: { children: ReactNode }) { + return ( +
+ {children} +
+ ); +} diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx new file mode 100644 index 0000000..8a14c4f --- /dev/null +++ b/app/dashboard/page.tsx @@ -0,0 +1,28 @@ +"use client"; +import AvatarCard from "@/components/dashboard/AvatarCard"; +import BookingsCard from "@/components/dashboard/BookingsCard"; +import CalendarCard from "@/components/dashboard/CalendarCard"; +import CompleteProfileCard from "@/components/dashboard/CompleteProfileCard"; +import JobPostingCard from "@/components/dashboard/JobPostingCard"; +import { useUser } from "@/contexts/UserContext"; +import { useEffect } from "react"; + +export default function DashboardPage() { + const { userData } = useUser(); + + useEffect(() => { + if (!userData || userData?.userType == 1) return; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ( + <> + + + + + + + + ); +} diff --git a/app/favicon.ico b/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..718d6fea4835ec2d246af9800eddb7ffb276240c GIT binary patch literal 25931 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 LrJugUA?W`A8`#=m literal 0 HcmV?d00001 diff --git a/app/forgot-password/layout.tsx b/app/forgot-password/layout.tsx new file mode 100644 index 0000000..4cbd743 --- /dev/null +++ b/app/forgot-password/layout.tsx @@ -0,0 +1,9 @@ +import { ReactNode } from "react"; + +interface IProps { + children: ReactNode; +} + +export default function ForgotPasswordLayout({ children }: IProps) { + return children; +} diff --git a/app/forgot-password/page.tsx b/app/forgot-password/page.tsx new file mode 100644 index 0000000..593640e --- /dev/null +++ b/app/forgot-password/page.tsx @@ -0,0 +1,72 @@ +"use client"; +import { useState } from "react"; +import Image from "next/image"; +import LeftSection from "@/components/layout/AuthLayout/LeftSection"; +import { Typography } from "@/libraries/material-tailwind"; +import ForgotPasswordForm from "@/components/forgot-password/ForgotPasswordForm"; +import Loading from "@/components/custom/Loading"; + +export default function ForgotPasswordPage() { + const [loading, setLoading] = useState(false); + return ( + <> + +
+ + + Dental Jobs + +
+
+ + +
+ + Habitasse leo mi enim condimentum rhoncus. Sed non tortor gravida . + +
+
+ {loading ? ( + + ) : ( +
+ + Forgot Password? + + +
+ )} +
+ + ); +} diff --git a/app/globals.css b/app/globals.css new file mode 100644 index 0000000..3c37cf5 --- /dev/null +++ b/app/globals.css @@ -0,0 +1,125 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +:root { + --foreground-rgb: 0, 0, 0; + --background-start-rgb: 214, 219, 220; + --background-end-rgb: 255, 255, 255; +} + +@media (prefers-color-scheme: dark) { + :root { + --foreground-rgb: 255, 255, 255; + --background-start-rgb: 0, 0, 0; + --background-end-rgb: 0, 0, 0; + } +} + +body { + color: rgb(var(--foreground-rgb)); + background: linear-gradient( + to bottom, + transparent, + rgb(var(--background-end-rgb)) + ) + rgb(var(--background-start-rgb)); +} + +/* width */ +::-webkit-scrollbar { + width: 7px; +} + +/* Track */ +::-webkit-scrollbar-track { + background: none; +} + +::-webkit-scrollbar-track:hover { + background: none; +} + +/* Handle */ +::-webkit-scrollbar-thumb { + border-radius: 16px; + border: 5px solid #8032ff; +} + +/* Handle on hover */ +::-webkit-scrollbar-thumb:hover { + border: 5px solid #6627cc; +} + +/* set button(top and bottom of the scrollbar) */ +::-webkit-scrollbar-button { + display: none; +} + +.react-calendar__month-view__weekdays abbr { + text-decoration: none; +} + +.react-calendar__tile { + border-radius: 9999px; +} + +.react-calendar__tile { + @media (max-width: 500px) { + padding: 10px 3px; + } +} + +.react-calendar__tile--now { + background: none !important; + border: 2px solid #8032ff !important; + color: #8032ff !important; + font-weight: 700 !important; +} + +.react-calendar__month-view__days__day { + color: #7a6899; +} + +.rbc-header { + padding: 0 !important; + border: none !important; +} + +.rbc-month-view { + border: none !important; +} + +.rbc-day-bg { + border: 1px solid #f6f4f9; + border-radius: 0.5rem; +} + +.rbc-month-row + .rbc-month-row { + border: none !important; +} + +.rbc-date-cell { + padding-top: 7px !important; + padding-right: 7px !important; + color: #7a6899 !important; +} + +.rbc-event { + background-color: transparent !important; +} + +.rbc-date-cell.rbc-now.rbc-current { + color: #8032ff !important; +} + +.tooltip-center-small-screen { + @media (max-width: 500px) { + position: fixed; + top: 10%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 1000; + width: 82%; + } +} diff --git a/app/help/layout.tsx b/app/help/layout.tsx new file mode 100644 index 0000000..5dca850 --- /dev/null +++ b/app/help/layout.tsx @@ -0,0 +1,5 @@ +import { ReactNode } from "react"; + +export default function DashboardLayout({ children }: { children: ReactNode }) { + return
{children}
; +} diff --git a/app/help/page.tsx b/app/help/page.tsx new file mode 100644 index 0000000..6b42f8a --- /dev/null +++ b/app/help/page.tsx @@ -0,0 +1,18 @@ +"use client" + +import HelpPage from "@/components/help"; +import { useUser } from "@/contexts/UserContext"; +import { useEffect } from "react"; + +export default function Dashboard() { + const {userData} = useUser(); + + useEffect(() => { + if(!userData || userData?.userType == 1) return ; + }, []) + return ( + <> + + + ); +} diff --git a/app/job-posting/layout.tsx b/app/job-posting/layout.tsx new file mode 100644 index 0000000..ef37db8 --- /dev/null +++ b/app/job-posting/layout.tsx @@ -0,0 +1,9 @@ +import { ReactNode } from "react"; + +export default function JobPostingLayout({ + children, +}: { + children: ReactNode; +}) { + return
{children}
; +} diff --git a/app/job-posting/page.tsx b/app/job-posting/page.tsx new file mode 100644 index 0000000..e4920e5 --- /dev/null +++ b/app/job-posting/page.tsx @@ -0,0 +1,9 @@ +import JobPostingPage from "@/components/job-posting/JobPostingPage"; + +export default function Booking() { + return ( + <> + + + ); +} diff --git a/app/layout.tsx b/app/layout.tsx new file mode 100644 index 0000000..35c76ee --- /dev/null +++ b/app/layout.tsx @@ -0,0 +1,46 @@ +/* eslint-disable @next/next/no-page-custom-font */ +import type { Metadata } from "next"; +import { ThemeProvider } from "@/libraries/material-tailwind"; +import "react-circular-progressbar/dist/styles.css"; +import "react-calendar/dist/Calendar.css"; +import "react-big-calendar/lib/css/react-big-calendar.css"; +import "react-toastify/dist/ReactToastify.css"; +import "swiper/css"; +import "react-phone-number-input/style.css"; +import "./globals.css"; +import Home from "./page"; +import { ToastContainer } from "@/libraries/react-toastify"; + +export const metadata: Metadata = { + title: "Dental", + description: "Dental Job Portal", +}; + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + + + + + + + + {children} + + + + + ); +} diff --git a/app/messages/layout.tsx b/app/messages/layout.tsx new file mode 100644 index 0000000..429dd00 --- /dev/null +++ b/app/messages/layout.tsx @@ -0,0 +1,5 @@ +import { ReactNode } from "react"; + +export default function MessagesLayout({ children }: { children: ReactNode }) { + return <>{children}; +} diff --git a/app/messages/page.tsx b/app/messages/page.tsx new file mode 100644 index 0000000..241518e --- /dev/null +++ b/app/messages/page.tsx @@ -0,0 +1,26 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { TEMP_USERS } from "@/components/messages/tempData"; +import { IUserItem } from "@/utils/interfaces"; +import DP from "@/components/messages/DP"; +import MB from "@/components/messages/MB"; +import { useUser } from "@/contexts/UserContext"; + +export type TUserItem = IUserItem | null; + +export default function MessagesPage() { + const [selectedUser, setSelectedUser] = useState(null); + const {userData} = useUser(); + + useEffect(() => { + if(!userData || userData?.userType == 1) return ; + }, []) + + return ( + <> + + + + ); +} diff --git a/app/otp-verify/layout.tsx b/app/otp-verify/layout.tsx new file mode 100644 index 0000000..e8061ad --- /dev/null +++ b/app/otp-verify/layout.tsx @@ -0,0 +1,9 @@ +import { ReactNode } from "react"; + +interface IProps { + children: ReactNode; +} + +export default function OTPVerifyLayout({ children }: IProps) { + return children; +} diff --git a/app/otp-verify/page.tsx b/app/otp-verify/page.tsx new file mode 100644 index 0000000..f25431c --- /dev/null +++ b/app/otp-verify/page.tsx @@ -0,0 +1,74 @@ +"use client"; +import Image from "next/image"; +import LeftSection from "@/components/layout/AuthLayout/LeftSection"; +import OTPVerifyForm from "@/components/otp-verify/OTPVerifyForm"; +import { Typography } from "@/libraries/material-tailwind"; +import { useState } from "react"; +import Loading from "@/components/custom/Loading"; + +export default function OTPVerifyPage() { + const [loading, setLoading] = useState(false); + + return ( + <> + +
+ + + Dental Jobs + +
+
+ + +
+ + Habitasse leo mi enim condimentum rhoncus. Sed non tortor gravida . + +
+ +
+ {loading ? ( + + ) : ( +
+ + OTP Verification + + +
+ )} +
+ + ); +} diff --git a/app/page.tsx b/app/page.tsx new file mode 100644 index 0000000..8e6e76e --- /dev/null +++ b/app/page.tsx @@ -0,0 +1,37 @@ +"use client"; +import { ReactNode } from "react"; +import { usePathname } from "next/navigation"; +import { LoadingProvider } from "@/contexts/LoadingContext"; +import { CalendarProvider } from "@/contexts/CalendarContext"; +import { ScheduleProvider } from "@/contexts/ScheduleContext"; +import { isAuthLayout } from "@/utils/functions"; +import AuthLayout from "@/components/layout/AuthLayout"; +import DashboardLayout from "@/components/layout/DashboardLayout"; +import { UserProvider } from "@/contexts/UserContext"; +import { AuthProvider } from "@/contexts/AuthContext"; + +interface IProps { + children: ReactNode; +} + +export default function Home({ children }: IProps) { + const pathname = usePathname(); + + return ( + + + + + + {isAuthLayout(pathname) ? ( + {children} + ) : ( + {children} + )} + + + + + + ); +} diff --git a/app/revenue/layout.tsx b/app/revenue/layout.tsx new file mode 100644 index 0000000..2f47813 --- /dev/null +++ b/app/revenue/layout.tsx @@ -0,0 +1,5 @@ +import { ReactNode } from "react"; + +export default function RevenueLayout({ children }: { children: ReactNode }) { + return
{children}
; +} diff --git a/app/revenue/page.tsx b/app/revenue/page.tsx new file mode 100644 index 0000000..66c7e5d --- /dev/null +++ b/app/revenue/page.tsx @@ -0,0 +1,18 @@ +"use client" +import RevenuePage from "@/components/revenue"; +import { useUser } from "@/contexts/UserContext"; +import { useEffect } from "react"; + +export default function Revenue() { + const {userData} = useUser(); + + useEffect(() => { + if(!userData || userData?.userType == 1) return ; + }, []) + + return ( + <> + + + ); +} diff --git a/app/schedule/layout.tsx b/app/schedule/layout.tsx new file mode 100644 index 0000000..93264e5 --- /dev/null +++ b/app/schedule/layout.tsx @@ -0,0 +1,7 @@ +import { ReactNode } from "react"; + +export default function ScheduleLayout({ children }: { children: ReactNode }) { + return ( +
{children}
+ ); +} diff --git a/app/schedule/page.tsx b/app/schedule/page.tsx new file mode 100644 index 0000000..61ceeea --- /dev/null +++ b/app/schedule/page.tsx @@ -0,0 +1,46 @@ +"use client"; +import { useEffect } from "react"; +import { toast } from "@/libraries/react-toastify"; +import api from "@/utils/api"; +import { getErrorMessage } from "@/utils/functions"; +import { useSchedule } from "@/contexts/ScheduleContext"; +import Schedule from "@/components/schedule"; +import useLoading from "@/hooks/useLoading"; +import { useUser } from "@/contexts/UserContext"; + +let isFirstLoad = true; + +export default function SchedulePage() { + const { setWeekAvailabilities } = useSchedule(); + const { setIsLoading } = useLoading(); + const { userData } = useUser(); + + useEffect(() => { + if (!userData || userData?.userType == 1) return; + if (isFirstLoad) { + (async () => { + setIsLoading(true); + await api + .get("/schedule/get/availability") + .then((res) => { + const { data } = res.data; + setWeekAvailabilities(data?.daysAvailable || []); + }) + .catch((err) => { + toast.error(getErrorMessage(err)); + }); + + await api.get("/schedule/get/blocked/dates").then((res) => { + const { data } = res.data; + if (data) { + } + }); + setIsLoading(false); + })(); + } + isFirstLoad = false; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ; +} diff --git a/app/signin/layout.tsx b/app/signin/layout.tsx new file mode 100644 index 0000000..f3f572d --- /dev/null +++ b/app/signin/layout.tsx @@ -0,0 +1,9 @@ +import { ReactNode } from "react"; + +interface IProps { + children: ReactNode; +} + +export default function SigninLayout({ children }: IProps) { + return <>{children}; +} diff --git a/app/signin/page.tsx b/app/signin/page.tsx new file mode 100644 index 0000000..ccbf1f0 --- /dev/null +++ b/app/signin/page.tsx @@ -0,0 +1,107 @@ +"use client"; +import Image from "next/image"; +import Link from "next/link"; +import LeftSection from "@/components/layout/AuthLayout/LeftSection"; +import { Typography } from "@/libraries/material-tailwind"; +import SigninForm from "@/components/signin/SigninForm"; +import { PATH_MAPPER } from "@/utils/constants"; +import { useState } from "react"; +import Loading from "@/components/custom/Loading"; + +export default function SigninPage() { + const [loading, setLoading] = useState(false); + return ( + <> + +
+ + + Dental Jobs + +
+
+ + +
+ + Habitasse leo mi enim condimentum rhoncus. Sed non tortor gravida . + +
+ +
+ {loading ? ( + + ) : ( + <> +
+ + +
+
+ + Login + + + + Haven't created your free account? +
+ + Sign up Dental Office + {" "} + or{" "} + + Dental Professional + +
+
+ + )} +
+ + ); +} diff --git a/app/signup/layout.tsx b/app/signup/layout.tsx new file mode 100644 index 0000000..aaa5681 --- /dev/null +++ b/app/signup/layout.tsx @@ -0,0 +1,9 @@ +import { ReactNode } from "react"; + +interface IProps { + children: ReactNode; +} + +export default function SignupLayout({ children }: IProps) { + return <>{children}; +} diff --git a/app/signup/page.tsx b/app/signup/page.tsx new file mode 100644 index 0000000..1a51665 --- /dev/null +++ b/app/signup/page.tsx @@ -0,0 +1,121 @@ +"use client"; +import Image from "next/image"; +import Link from "next/link"; +import { redirect } from "next/navigation"; +import { useEffect, useState } from "react"; +import LeftSection from "@/components/layout/AuthLayout/LeftSection"; +import Signup from "@/components/signup/Signup"; +import { Typography } from "@/libraries/material-tailwind"; +import { L_STORAGE_AUTH_TOKEN } from "@/utils/constants"; + +export default function SignupPage() { + const [authToken, setAuthToken] = useState(""); + + useEffect(() => { + setAuthToken( + localStorage.getItem(L_STORAGE_AUTH_TOKEN) || + sessionStorage.getItem(L_STORAGE_AUTH_TOKEN) || + "" + ); + }, []); + + if (authToken) return redirect("/dashboard"); + + return ( + <> + +
+ + +
+
+ + + Habitasse leo mi enim condimentum rhoncus. Sed non tortor gravida . + + +
+
+ +
+
+ + +
+
+ + Professional Registration + +
+ + If you are a dental practice looking to hire, click{" "} + + here + {" "} + to register. + + + + Please enter your full name and contact information. We will never + sell your information and it will only be used as part of our + service. In order to approve your profile, we will need to verify + your contact information. We may also contact you to help you + complete your application. If you would like to set up a meeting + now, please{" "} + + Schedule a time + {" "} + to speak with us. + +
+ + +
+
+ + ); +} diff --git a/components/bookings/DTBooking.tsx b/components/bookings/DTBooking.tsx new file mode 100644 index 0000000..93fa170 --- /dev/null +++ b/components/bookings/DTBooking.tsx @@ -0,0 +1,93 @@ +"use client"; + +import React from "react"; +import { Typography, ListItem } from "@/libraries/material-tailwind"; +import { TEMP_BOOKING } from "@/utils/tempData"; +import { IconButton } from "@/libraries/material-tailwind"; +import { Icon } from "@/libraries/iconify-react"; +import { ICON_MAPPER } from "@/utils/constants"; +import StatusBadge from "../custom/StatusBadge"; +import Link from "next/link"; + +interface IProps { + filterBookings: Function; +} + +export default function DTBooking({ filterBookings }: IProps) { + return ( +
+
+ + Date & Time + + + Booking with + + + Hourly Rate + + + Message + +
+
+ {filterBookings().map((b: any, i: number) => ( + + + {b.bookedDateTime.slice(0, 10)} {b.bookedDateTime.slice(11, 19)} + + + {b.bookingWith} + + + {b.hourlyRate} + + + + + + + + + {/* {b.status} */} + + ))} +
+
+ ); +} diff --git a/components/bookings/MBBooking.tsx b/components/bookings/MBBooking.tsx new file mode 100644 index 0000000..8591f27 --- /dev/null +++ b/components/bookings/MBBooking.tsx @@ -0,0 +1,71 @@ +"use client"; + +import React from "react"; +import { Typography, ListItem } from "@/libraries/material-tailwind"; +import { IconButton } from "@/libraries/material-tailwind"; +import { Icon } from "@/libraries/iconify-react"; +import { ICON_MAPPER } from "@/utils/constants"; +import StatusBadge from "../custom/StatusBadge"; +import Link from "next/link"; + +interface IProps { + filterBookings: Function; +} + +export default function MBBooking({ filterBookings }: IProps) { + return ( + <> + {filterBookings().map((b: any) => ( + +
+
+ + {b.label} + + + + + + + {/* {b.status} */} +
+
+ + Date and Time + + + {b.bookedDateTime.slice(0, 10)}-{b.bookedDateTime.slice(11, 17)} + +
+
+ + Hourly Rate + + + {b.hourlyRate} + +
+
+
+ ))} + + ); +} diff --git a/components/bookings/index.tsx b/components/bookings/index.tsx new file mode 100644 index 0000000..8a1b627 --- /dev/null +++ b/components/bookings/index.tsx @@ -0,0 +1,168 @@ +"use client"; + +import React, { useState, useRef, useEffect } from "react"; +import { IComponent, IBooking } from "@/utils/interfaces"; +import CardTemplate from "../custom/CardTemplate"; +import TabButton from "../custom/buttons/TabButton"; +import { IconButton, Tooltip } from "@/libraries/material-tailwind"; +import { Icon } from "@/libraries/iconify-react"; +import { TEMP_BOOKING } from "@/utils/tempData"; +import CalendarCard from "../dashboard/CalendarCard"; +import DTBooking from "./DTBooking"; +import MBBooking from "./MBBooking"; +import api from "@/utils/api"; +import { useUser } from "@/contexts/UserContext"; + +export default function BookingCard({ className = "" }: IComponent) { + const [activeTab, setActiveTab] = useState("Today"); + const [data, setData] = useState([]); + const [isTooltipOpen, setIsTooltipOpen] = useState(false); + const [isSmallScreen, setIsSmallScreen] = useState(false); + const tooltipRef = useRef(null); + const iconButtonRef = useRef(null); + const calendarRef = useRef(null); + const pageNo: number = 1; + const { userData } = useUser(); + + const toggleTooltip = () => { + setIsTooltipOpen(!isTooltipOpen); + }; + + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if ( + (!tooltipRef.current || + !tooltipRef.current.contains(event.target as Node)) && + (!iconButtonRef.current || + !iconButtonRef.current.contains(event.target as Node)) && + (!calendarRef.current || + !calendarRef.current.contains(event.target as Node)) + ) { + setIsTooltipOpen(false); + } + }; + document.addEventListener("mousedown", handleClickOutside); + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, []); + + useEffect(() => { + const checkScreenSize = () => { + setIsSmallScreen(window.innerWidth < 640); + }; + checkScreenSize(); + window.addEventListener("resize", checkScreenSize); + return () => window.removeEventListener("resize", checkScreenSize); + }, []); + + const getData = async () => { + let res: any = await api.post(`/jobs/professional/bookings/${pageNo}`); + setData(res?.data); + }; + + useEffect(() => { + if (!userData || userData?.userType == 1) return; + getData(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + function filterBookings() { + const currentDate = new Date().toDateString(); + switch (activeTab) { + case "Today": + return data.filter((booking) => { + const bookedDate = new Date(booking.bookedDateTime).toDateString(); + return bookedDate === currentDate; + }); + case "Past": + return data.filter((booking) => { + const bookedDate = new Date(booking.bookedDateTime).toDateString(); + return bookedDate < currentDate; + }); + case "Future": + return data.filter((booking) => { + const bookedDate = new Date(booking.bookedDateTime).toDateString(); + return bookedDate >= currentDate; + }); + default: + return data; + } + } + + return ( + + + + + ) : null + } + className={`bg-white py-3 px-4 ${ + isSmallScreen ? "tooltip-center-small-screen" : "" + }`} + animate={{ + mount: { scale: 1, y: 0 }, + unmount: { scale: 0, y: 25 }, + }} + open={isTooltipOpen} + dismiss={{ + itemPress: false, + }} + > + + + + + + } + > +
+
+ setActiveTab("Today")} + > + Today + + setActiveTab("Past")} + > + Past + + setActiveTab("Future")} + > + Future + +
+
+ +
+
+ +
+
+
+ ); +} diff --git a/components/create-password/CreatePasswordForm.tsx b/components/create-password/CreatePasswordForm.tsx new file mode 100644 index 0000000..eba7867 --- /dev/null +++ b/components/create-password/CreatePasswordForm.tsx @@ -0,0 +1,151 @@ +"use client"; + +import { FormHTMLAttributes, useState } from "react"; +import * as yup from "yup"; +import Input from "@/components/custom/Input"; +import { IconButton } from "@/libraries/material-tailwind"; +import { Icon } from "@/libraries/iconify-react"; +import { + ICON_MAPPER, + VALIDATION_DISMATCH_PASSWORDS, + VALIDATION_REQUIRED_FIELD, +} from "@/utils/constants"; +import { useFormik } from "formik"; +import Button from "@/components/custom/buttons/Button"; +import api from "@/utils/api"; +import { useAuth } from "@/contexts/AuthContext"; +import { toast } from "react-toastify"; +import { useRouter } from "next/navigation"; +import { getErrorMessage } from "@/utils/functions"; + +type TPasswordType = "text" | "password"; + +const validationSchema = yup.object().shape({ + password: yup.string().required(VALIDATION_REQUIRED_FIELD), + confPassword: yup + .string() + .oneOf([yup.ref("password", undefined)], VALIDATION_DISMATCH_PASSWORDS) + .required(VALIDATION_REQUIRED_FIELD), +}); + +interface IProps extends FormHTMLAttributes { + setLoading: (value: boolean | ((prev: boolean) => boolean)) => void; +} + +export default function CreatePasswordForm({ + className = "", + setLoading, +}: IProps) { + const router = useRouter(); + const { refId } = useAuth(); + + const [passwordType, setPasswordType] = useState("password"); + const [confPasswordType, setConfPasswordType] = + useState("password"); + + const formik = useFormik({ + initialValues: { + password: "", + confPassword: "", + }, + validationSchema, + onSubmit: ({ password }) => { + setLoading(true); + + api + .post("/update/forgot/password", { + password, + refId, + }) + .then((res) => { + const { success } = res.data; + setLoading(false); + if (success) { + toast.success("The password has been updated."); + router.push("/signin"); + } + }) + .catch((err) => { + setLoading(false); + toast.error(getErrorMessage(err)); + }); + }, + }); + + return ( +
+
+ + setPasswordType((prev) => + prev === "text" ? "password" : "text" + ) + } + variant="text" + > + + + } + onChange={formik.handleChange} + value={formik.values.password} + error={formik.touched.password && formik.errors.password} + /> + + setConfPasswordType((prev) => + prev === "text" ? "password" : "text" + ) + } + variant="text" + > + + + } + onChange={formik.handleChange} + value={formik.values.confPassword} + error={formik.touched.confPassword && formik.errors.confPassword} + /> +
+ + +
+ ); +} diff --git a/components/custom/CardTemplate.tsx b/components/custom/CardTemplate.tsx new file mode 100644 index 0000000..e9ed658 --- /dev/null +++ b/components/custom/CardTemplate.tsx @@ -0,0 +1,45 @@ +import { ReactNode } from "react"; +import { IComponent } from "@/utils/interfaces"; +import { Card, CardBody, Typography } from "@/libraries/material-tailwind"; + +interface IProps extends IComponent { + title: string; + children: ReactNode; + actions?: ReactNode; +} + +export default function CardTemplate({ + className = "", + title = "", + children, + actions, +}: IProps) { + return ( + + +
+ + {title} + + {title === "Hi, James Mann!" ? ( + + Public Profile + + ) : ( + "" + )} + {actions} +
+ {children} +
+
+ ); +} diff --git a/components/custom/Checkbox.tsx b/components/custom/Checkbox.tsx new file mode 100644 index 0000000..c6b0a5f --- /dev/null +++ b/components/custom/Checkbox.tsx @@ -0,0 +1,109 @@ +"use client"; + +import { + CheckboxProps, + Checkbox as MTCheckbox, + Typography, +} from "@/libraries/material-tailwind"; +import { IComponent } from "@/utils/interfaces"; +import { TColor } from "@/utils/types"; +import { useEffect, useState } from "react"; + +interface IProps extends IComponent { + color: TColor; + label: CheckboxProps["label"]; + labelProps?: CheckboxProps["labelProps"]; + name?: CheckboxProps["name"]; + checked?: CheckboxProps["checked"]; + onChange?: CheckboxProps["onChange"]; +} + +export default function Checkbox({ + className: propClassName = "", + color = "primary", + label, + name, + checked, + onChange, +}: IProps) { + const [classNameOfIconProps, setClassNameOfIconProps] = + useState("bg-primary"); + const [className, setClassName] = useState( + "border-primary checked:border-primary checked:bg-primary", + ); + + useEffect(() => { + switch (color) { + case "secondary": + setClassNameOfIconProps("bg-secondary"); + return setClassName( + "border-secondary checked:border-secondary checked:bg-secondary", + ); + + case "success": + setClassNameOfIconProps("bg-success"); + return setClassName( + "border-success checked:border-success checked:bg-success", + ); + + case "info": + setClassNameOfIconProps("bg-info"); + return setClassName("border-info checked:border-info checked:bg-info"); + + case "warning": + setClassNameOfIconProps("bg-warning"); + return setClassName( + "border-warning checked:border-warning checked:bg-warning", + ); + + case "error": + setClassNameOfIconProps("bg-error"); + return setClassName( + "border-error checked:border-error checked:bg-error", + ); + + case "dark": + setClassNameOfIconProps("bg-dark"); + return setClassName("border-dark checked:border-dark checked:bg-dark"); + + case "lightDark": + setClassNameOfIconProps("bg-lightDark"); + return setClassName( + "border-lightDark checked:border-lightDark checked:bg-lightDark", + ); + + default: + setClassNameOfIconProps("bg-primary"); + return setClassName( + "border-primary checked:border-primary checked:bg-primary", + ); + } + }, [color]); + + return ( + + {label} + + } + labelProps={{ + className: "flex-1", + }} + name={name} + checked={checked} + onChange={onChange} + /> + ); +} diff --git a/components/custom/EmptyAvatar.tsx b/components/custom/EmptyAvatar.tsx new file mode 100644 index 0000000..66c3ec9 --- /dev/null +++ b/components/custom/EmptyAvatar.tsx @@ -0,0 +1,17 @@ +import { AllHTMLAttributes } from "react"; +import { Icon } from "@/libraries/iconify-react"; +import { ICON_MAPPER } from "@/utils/constants"; + +export default function EmptyAvatar({ + className = "", + onClick, +}: AllHTMLAttributes) { + return ( +
+ +
+ ); +} diff --git a/components/custom/Input.tsx b/components/custom/Input.tsx new file mode 100644 index 0000000..4242825 --- /dev/null +++ b/components/custom/Input.tsx @@ -0,0 +1,103 @@ +import { type ReactNode, type InputHTMLAttributes, useState } from "react"; +import { Typography } from "@/libraries/material-tailwind"; + +interface IProps extends InputHTMLAttributes { + label?: string; + id?: string; + type?: string; + name?: string; + className?: string; + children?: ReactNode | string; + classNameOfInput?: string; + startAdornment?: ReactNode; + endAdornment?: ReactNode; + error?: string | boolean; +} + +export default function Input({ + label = "", + id = "", + type = "", + name = "", + className = "", + classNameOfInput = "", + startAdornment, + endAdornment, + error, + ...others +}: IProps) { + const [showPassword, setShowPassword] = useState(false); + const [focused, setFocused] = useState(false); + return ( +
+ {!!label && ( + + )} + +
+ {startAdornment ? ( +
+ {startAdornment} + setFocused(true)} + onBlur={() => setFocused(false)} + /> +
+ ) : ( + setFocused(true)} + onBlur={() => setFocused(false)} + /> + )} + + {!!endAdornment && ( +
setShowPassword(true)} + onMouseUp={() => setShowPassword(false)} + > + {endAdornment} +
+ )} +
+ + {!!error && ( + + {error} + + )} +
+ ); +} diff --git a/components/custom/Loading.tsx b/components/custom/Loading.tsx new file mode 100644 index 0000000..a9004f8 --- /dev/null +++ b/components/custom/Loading.tsx @@ -0,0 +1,9 @@ +import { BarLoader } from "@/libraries/react-spinners"; + +export default function Loading() { + return ( +
+ +
+ ); +} diff --git a/components/custom/MonthPick.tsx b/components/custom/MonthPick.tsx new file mode 100644 index 0000000..2d671d5 --- /dev/null +++ b/components/custom/MonthPick.tsx @@ -0,0 +1,51 @@ +import { AllHTMLAttributes, useMemo } from "react"; +import { IconButton, Typography } from "@/libraries/material-tailwind"; +import { Icon } from "@/libraries/iconify-react"; +import { ICON_MAPPER } from "@/utils/constants"; + +interface IProps extends AllHTMLAttributes { + pickPrevMonth: () => void; + pickNextMonth: () => void; + date: Date; +} + +export default function MonthPick({ + pickPrevMonth, + pickNextMonth, + date, + className = "", +}: IProps) { + const monthYear = useMemo(() => { + const formatter = new Intl.DateTimeFormat("en-us", { + year: "numeric", + month: "long", + }); + return formatter.format(date).replaceAll(".", ""); + }, [date]); + + return ( +
+ + + + + {monthYear} + + + + +
+ ); +} diff --git a/components/custom/Select.tsx b/components/custom/Select.tsx new file mode 100644 index 0000000..a606ec9 --- /dev/null +++ b/components/custom/Select.tsx @@ -0,0 +1,59 @@ +import { ReactNode, SelectHTMLAttributes, useState } from "react"; +import { Icon } from "@/libraries/iconify-react"; +import { ICON_MAPPER } from "@/utils/constants"; + +interface IProps extends SelectHTMLAttributes { + children: ReactNode; + available?: boolean; + iconClassName?: string; + label?: string; + error?: string | boolean; +} + +export default function Select({ + className = "", + available, + children, + iconClassName = "", + label, + error, + id, + ...others +}: IProps) { + const [opened, setOpened] = useState(false); + + return ( +
+ {!!label && ( + + )} +
+ + +
+
+ ); +} diff --git a/components/custom/StatusBadge.tsx b/components/custom/StatusBadge.tsx new file mode 100644 index 0000000..db33924 --- /dev/null +++ b/components/custom/StatusBadge.tsx @@ -0,0 +1,57 @@ +import { ReactNode, useMemo } from "react"; +import { STATUS_MAPPER } from "@/utils/constants"; +import { IComponent } from "@/utils/interfaces"; + +interface IProps extends IComponent { + status: string; + isDefault?: boolean; + beta?: string; + children: ReactNode; +} + +export default function StatusBadge({ + className = "", + status = "", + isDefault = false, + children, +}: IProps) { + const { colorClassName, fontWeightClassName } = useMemo(() => { + let colorClass = ""; + let fontWeightClass = "font-semibold"; + + if (isDefault) { + colorClass = "bg-lightDark text-lightDark"; + fontWeightClass = "font-normal"; + } else { + if (status === STATUS_MAPPER.pending) { + colorClass = "bg-error text-error"; + } else if (status === STATUS_MAPPER.ongoing) { + colorClass = "bg-primary text-primary"; + } else if (status === STATUS_MAPPER.completed) { + colorClass = "bg-success text-success"; + } else if (status === STATUS_MAPPER.eligible) { + colorClass = "bg-success text-success"; + fontWeightClass = "font-normal"; + } else if (status === STATUS_MAPPER.available) { + colorClass = "bg-success text-success"; + fontWeightClass = "font-normal"; + } else if (status === STATUS_MAPPER.notAvailable) { + colorClass = "bg-error text-error"; + fontWeightClass = "font-normal"; + } else if (status === STATUS_MAPPER.notVerified) { + colorClass = "bg-error text-error"; + fontWeightClass = "font-normal"; + } + } + + return { colorClassName: colorClass, fontWeightClassName: fontWeightClass }; + }, [status, isDefault]); + + return ( +
+ {children} +
+ ); +} diff --git a/components/custom/buttons/Button.tsx b/components/custom/buttons/Button.tsx new file mode 100644 index 0000000..af40260 --- /dev/null +++ b/components/custom/buttons/Button.tsx @@ -0,0 +1,69 @@ +"use client"; +import { ButtonHTMLAttributes, useMemo } from "react"; +import { Button as MTButton } from "@/libraries/material-tailwind"; +import { TColor, TVariant } from "@/utils/types"; + +interface IProps extends ButtonHTMLAttributes { + variant: TVariant; + color: TColor; +} + +export default function Button({ + variant, + color, + className = "", + children, + onClick, + type, + disabled = false, +}: IProps) { + const _className = useMemo(() => { + let defaultClassName = ""; + if (variant === "filled") { + if (color === "primary") return "border border-primary bg-primary"; + if (color === "secondary") return "border border-secondary bg-secondary"; + if (color === "success") return "border border-success bg-success"; + if (color === "warning") return "border border-warning bg-warning"; + if (color === "error") return "border border-error bg-error"; + if (color === "dark") return "border border-dark bg-dark"; + if (color === "lightDark") return "border border-lightDark bg-lightDark"; + } + + if (variant === "outlined") { + defaultClassName = "bg-transparent border"; + + if (color === "primary") + return `${defaultClassName} border-primary text-primary`; + if (color === "secondary") + return `${defaultClassName} border-secondary text-secondary`; + if (color === "success") + return `${defaultClassName} border-success text-success`; + if (color === "warning") + return `${defaultClassName} border-warning text-warning`; + if (color === "error") + return `${defaultClassName} border-error text-error`; + if (color === "dark") return `${defaultClassName} border-dark text-dark`; + if (color === "lightDark") + return `${defaultClassName} border-lightDark text-lightDark`; + } + + if (variant === "text") { + if (color === "secondary") return "text-secondary"; + } + + return defaultClassName; + }, [variant, color]); + + return ( + + {children} + + ); +} diff --git a/components/custom/buttons/TabButton.tsx b/components/custom/buttons/TabButton.tsx new file mode 100644 index 0000000..a807ff1 --- /dev/null +++ b/components/custom/buttons/TabButton.tsx @@ -0,0 +1,26 @@ +"use client"; + +import { ButtonHTMLAttributes, ReactNode } from "react"; + +interface IProps extends ButtonHTMLAttributes { + children: ReactNode; + isActive?: boolean; +} + +export default function TabButton({ + className = "", + children, + onClick, + isActive = false, +}: IProps) { + return ( + + ); +} diff --git a/components/custom/calendars/BCalendar/AddEventDialog.tsx b/components/custom/calendars/BCalendar/AddEventDialog.tsx new file mode 100644 index 0000000..687a24d --- /dev/null +++ b/components/custom/calendars/BCalendar/AddEventDialog.tsx @@ -0,0 +1,404 @@ +import Link from "next/link"; +import { ChangeEvent, useMemo, useState } from "react"; +import { Swiper, SwiperSlide } from "swiper/react"; +import { Dialog, DialogBody, Typography } from "@/libraries/material-tailwind"; +import type { DialogProps } from "@/libraries/material-tailwind"; +import { SCHEDULE_TIMES } from "@/utils/constants"; +import api from "@/utils/api"; +import { getErrorMessage } from "@/utils/functions"; +import moment from "@/libraries/moment"; +import { Radio } from "@/libraries/material-tailwind"; +import { toast } from "@/libraries/react-toastify"; +import { useCalendar } from "@/contexts/CalendarContext"; +import Select from "@/components/custom/Select"; +import Button from "@/components/custom/buttons/Button"; +import MonthPick from "@/components/custom/MonthPick"; + +interface IProps { + opened: boolean; + setOpened: Function; + date: Date; + size: DialogProps["size"]; +} + +type TAvailiability = "unavailable" | "custom" | "typical"; +type TDateWeek = { + date: number; + weekDay: string; +}; + +export default function AddEventDialog({ + opened, + setOpened, + date, + size, +}: IProps) { + const { setEvents } = useCalendar(); + + const [fromTime, setFromTime] = useState(SCHEDULE_TIMES[0]); + const [toTime, setToTime] = useState(SCHEDULE_TIMES[1]); + const [fromDate, setFromDate] = useState(new Date()); + const [toDate, setToDate] = useState(new Date()); + const [availability, setAvailibility] = + useState("unavailable"); + + const fromDateWeeks = useMemo>(() => { + const maxDate = new Date( + fromDate.getFullYear(), + fromDate.getMonth() + 1, + 0 + ).getDate(); + const dates = []; + + for (let i = 1; i <= maxDate; i += 1) { + dates.push({ + date: i, + weekDay: moment( + new Date(fromDate.getFullYear(), fromDate.getMonth(), i) + ).format("ddd"), + }); + } + + return dates; + }, [fromDate]); + + const toDateWeeks = useMemo>(() => { + const maxDate = new Date( + toDate.getFullYear(), + toDate.getMonth() + 1, + 0 + ).getDate(); + const dates = []; + + for (let i = 1; i <= maxDate; i += 1) { + dates.push({ + date: i, + weekDay: moment( + new Date(toDate.getFullYear(), toDate.getMonth(), i) + ).format("ddd"), + }); + } + + return dates; + }, [toDate]); + + const handleFrom = (e: ChangeEvent) => { + setFromTime(e.target.value); + }; + + const handleTo = (e: ChangeEvent) => { + setToTime(e.target.value); + }; + + const handler = () => { + setOpened(!opened); + }; + + const pickPrevMonthOfFromDate = () => { + setFromDate( + (currentDate) => + new Date(currentDate.getFullYear(), currentDate.getMonth() - 1, 1) + ); + }; + + const pickNextMonthOfFromDate = () => { + setFromDate( + (currentDate) => + new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 1) + ); + }; + + const pickPrevMonthOfToDate = () => { + setToDate( + (currentDate) => + new Date(currentDate.getFullYear(), currentDate.getMonth() - 1, 1) + ); + }; + + const pickNextMonthOfToDate = () => { + setToDate( + (currentDate) => + new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 1) + ); + }; + + const handleSave = () => { + if (availability === "unavailable") { + const day = moment(date).format("YYYY-MM-DD"); + + api + .post("/calendar/set/dates/block", { + startDate: day, + endDate: day, + time: [ + { + start: "00:00:00", + end: "23:59:59", + }, + ], + type: 1, + blockid: "", + }) + .then((res) => { + setEvents((prev) => [ + ...prev, + { + ...res.data.data, + type: "unavailable", + blockId: res.data.data.blockid ? `${res.data.data.blockid}` : "", + }, + ]); + toast.success("Success: An unavailable event has been added."); + setOpened(!opened); + }) + .catch((err) => { + toast.error(getErrorMessage(err)); + }); + } else if (availability === "custom") { + const startDate = new Date(`${moment(fromDate).format("L")} ${fromTime}`); + const endDate = new Date(`${moment(toDate).format("L")} ${toTime}`); + + if (startDate >= endDate) { + toast.error("Error: Invalid dates."); + } else { + // Should be considered later. + api + .post("/calendar/save/events", { + startDate: moment(startDate).format("YYYY-MM-DD"), + endDate: moment(endDate).format("YYYY-MM-DD"), + time: [ + { + start: moment(startDate).format("HH:mm:ss"), + end: moment(endDate).format("HH:mm:ss"), + }, + ], + type: 1, + blockid: "", + }) + .then((res) => { + setEvents((prev) => [ + ...prev, + { ...res.data.data, type: "available" }, + ]); + toast.success("Success: An event has been added."); + setOpened(!opened); + }) + .catch((err) => { + toast.error(getErrorMessage(err)); + }); + } + } + }; + + return ( + + +
+ + Exception to availability + + + Date: {moment(date).format("ll")} + +
+ + {availability === "custom" && ( +
+
+ From + +
+ + {fromDateWeeks.map((dw) => ( + + setFromDate( + new Date( + fromDate.getFullYear(), + fromDate.getMonth(), + dw.date + ) + ) + } + > +
+ {dw.date} + {dw.weekDay} +
+
+ ))} +
+ +
+ To + +
+ + {toDateWeeks.map((dw) => ( + + setToDate( + new Date(toDate.getFullYear(), toDate.getMonth(), dw.date) + ) + } + > +
+ {dw.date} + {dw.weekDay} +
+
+ ))} +
+
+ )} + +
+ + Set to unavailable + + } + className="border-primary checked:border-primary checked:before:border-primary before:bg-primary checked:before:bg-primary text-primary" + iconProps={{ + className: "text-primary", + }} + checked={availability === "unavailable"} + onChange={() => setAvailibility("unavailable")} + /> + +
+ +
+ + Set to available at these items + + +
+ + From: + + } + className="border-primary checked:border-primary checked:before:border-primary before:bg-primary checked:before:bg-primary text-primary" + iconProps={{ + className: "text-primary", + }} + checked={availability === "custom"} + onChange={() => setAvailibility("custom")} + /> + + + To: + + +
+
+ +
+ + + Use availability as set in{" "} + + Typical Availability + + + } + className="border-primary checked:border-primary checked:before:border-primary before:bg-primary checked:before:bg-primary text-primary" + iconProps={{ + className: "text-primary", + }} + checked={availability === "typical"} + onChange={() => setAvailibility("typical")} + /> +
+
+ + +
+
+
+ ); +} diff --git a/components/custom/calendars/BCalendar/DelEventDialog.tsx b/components/custom/calendars/BCalendar/DelEventDialog.tsx new file mode 100644 index 0000000..44cf86c --- /dev/null +++ b/components/custom/calendars/BCalendar/DelEventDialog.tsx @@ -0,0 +1,82 @@ +import { AllHTMLAttributes } from "react"; +import type { DialogProps } from "@/libraries/material-tailwind"; +import { Dialog, DialogBody, Typography } from "@/libraries/material-tailwind"; +import { Icon } from "@/libraries/iconify-react"; +import moment from "@/libraries/moment"; +import { ICON_MAPPER } from "@/utils/constants"; +import { IEvent } from "@/utils/interfaces"; +import Button from "@/components/custom/buttons/Button"; +import api from "@/utils/api"; +import { toast } from "react-toastify"; +import { getErrorMessage } from "@/utils/functions"; +import { useCalendar } from "@/contexts/CalendarContext"; + +interface IProps extends AllHTMLAttributes { + opened: boolean; + setOpened: (value: boolean | ((prev: boolean) => boolean)) => void; + dialogSize: DialogProps["size"]; + event: IEvent; +} + +export default function DelEventDialog({ + opened, + setOpened, + dialogSize, + event, +}: IProps) { + const { setEvents } = useCalendar(); + + const handleDelete = () => { + api + .get(`/calendar/unset/dates/block/${event.id}`) + .then((res) => { + setEvents((prev) => { + const newEvents = [...prev]; + newEvents.splice( + newEvents.findIndex((item) => item.id === event.id), + 1 + ); + return newEvents; + }); + toast.success("Success. The event has been deleted."); + setOpened(false); + }) + .catch((err) => { + toast.error(getErrorMessage(err)); + }); + }; + + return ( + + +
+ +
+ + + Are you sure want to delete? +
This exception from{" "} + + {" "} + {moment(event.start).format("MM/DD/YYYY hh:mm a")} to{" "} + {moment(event.end).format("MM/DD/YYYY hh:mm a")} + + . +
+ +
+ + +
+
+
+ ); +} diff --git a/components/custom/calendars/BCalendar/EventItem.tsx b/components/custom/calendars/BCalendar/EventItem.tsx new file mode 100644 index 0000000..fc71abc --- /dev/null +++ b/components/custom/calendars/BCalendar/EventItem.tsx @@ -0,0 +1,10 @@ +import type { EventProps } from "@/libraries/react-big-calendar"; +import { IEvent } from "@/utils/interfaces"; + +export default function EventItem({ title }: EventProps) { + return ( +
+

{title}

+
+ ); +} diff --git a/components/custom/calendars/BCalendar/WeekDay.tsx b/components/custom/calendars/BCalendar/WeekDay.tsx new file mode 100644 index 0000000..a48a27b --- /dev/null +++ b/components/custom/calendars/BCalendar/WeekDay.tsx @@ -0,0 +1,29 @@ +import { Typography } from "@/libraries/material-tailwind"; +import type { HeaderProps } from "@/libraries/react-big-calendar"; + +const today = new Date(); + +export default function WeekDay({ date, label }: HeaderProps) { + return ( +
+ + {label} + + + {label[0]} + +
+ ); +} diff --git a/components/custom/calendars/BCalendar/index.tsx b/components/custom/calendars/BCalendar/index.tsx new file mode 100644 index 0000000..c499336 --- /dev/null +++ b/components/custom/calendars/BCalendar/index.tsx @@ -0,0 +1,188 @@ +"use client"; + +import { AllHTMLAttributes, useMemo, useState } from "react"; +import { + BigCalendar, + EventProps, + momentLocalizer, +} from "@/libraries/react-big-calendar"; +import moment from "@/libraries/moment"; +import type { DialogProps } from "@/libraries/material-tailwind"; +import { + Dialog, + DialogBody, + IconButton, + Menu, + MenuHandler, + MenuList, +} from "@/libraries/material-tailwind"; +import { Icon } from "@/libraries/iconify-react"; +import { useMediaQuery } from "@/libraries/usehooks-ts"; +import { ICON_MAPPER, SCREEN_MAPPER } from "@/utils/constants"; +import WeekDay from "@/components/custom/calendars/BCalendar/WeekDay"; +import EventItem from "@/components/custom/calendars/BCalendar/EventItem"; +import AddEventDialog from "@/components/custom/calendars/BCalendar/AddEventDialog"; +import SCalendar from "@/components/custom/calendars/SCalendar"; +import { useCalendar } from "@/contexts/CalendarContext"; +import { IEvent } from "@/utils/interfaces"; + +interface IProps extends AllHTMLAttributes { + title?: string; +} + +const localizer = momentLocalizer(moment); +const currentDate = new Date(); + +export default function BCalendar({ title = "", className = "" }: IProps) { + const isMd = useMediaQuery(`(max-width: ${SCREEN_MAPPER.md})`); + + const { events } = useCalendar(); + + const [date, setDate] = useState(currentDate); + const [sCalendarOpened, setSCalendarOpened] = useState(false); + const [addEventDialogOpened, setAddEventDialogOpened] = + useState(false); + const [sCalDialogOpened, setSCalDialogOpened] = useState(false); + + const dialogSize = useMemo(() => { + if (isMd) return "xl"; + return "xs"; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const handleSCalendarOpened = () => { + setSCalendarOpened((prev) => !prev); + }; + + const gotoNeighborMonth = (toNext: boolean) => { + const monthOffset = toNext ? 1 : -1; + setDate( + (prev) => new Date(prev.getFullYear(), prev.getMonth() + monthOffset, 1) + ); + }; + + const gotoNeighborDate = (toNext: boolean) => { + const dateOffset = toNext ? 1 : -1; + setDate( + (prev) => + new Date( + prev.getFullYear(), + prev.getMonth(), + prev.getDate() + dateOffset + ) + ); + }; + + const onClickDate = (dt: Date) => { + setDate(dt); + setAddEventDialogOpened(true); + }; + + return ( + <> +
+
+

+ {title || moment(date).format("MMMM, YYYY")} +

+ +
+ gotoNeighborDate(false)} + > + + + + + +
+ + + {date.getDate() === currentDate.getDate() + ? "Today" + : moment(date).format("l")} + +
+
+ + + +
+ + gotoNeighborDate(true)} + > + + +
+ + setSCalDialogOpened(true)} + > + + +
+ + <>, + month: { + header: WeekDay, + event: (props: EventProps) => , + }, + }} + onNavigate={onClickDate} + /> +
+ + + + + + + + + + ); +} diff --git a/components/custom/calendars/SCalendar.tsx b/components/custom/calendars/SCalendar.tsx new file mode 100644 index 0000000..cf3f7a3 --- /dev/null +++ b/components/custom/calendars/SCalendar.tsx @@ -0,0 +1,107 @@ +import { AllHTMLAttributes, ForwardedRef, forwardRef, useMemo } from "react"; +import { ICON_MAPPER, PATH_MAPPER, SHORT_WEEKDAYS } from "@/utils/constants"; +import { Icon } from "@/libraries/iconify-react"; +import { IconButton } from "@/libraries/material-tailwind"; +import { Calendar } from "@/libraries/react-calendar"; +import Button from "@/components/custom/buttons/Button"; +import Link from "next/link"; + +interface IProps extends AllHTMLAttributes { + date: Date; + gotoNeighborMonth: (toNext: boolean) => void; + isAddSchedule: boolean; +} + +const SCalendar = forwardRef( + ( + { className = "", date, gotoNeighborMonth, isAddSchedule = true }: IProps, + ref: ForwardedRef, + ) => { + const monthYear = useMemo(() => { + const formatter = new Intl.DateTimeFormat("en-us", { + year: "numeric", + month: "long", + }); + return formatter.format(date).replaceAll(".", ""); + }, [date]); + + return ( +
+ {isAddSchedule ? ( +
+
Calendar
+
+ gotoNeighborMonth(false)} + > + + +

{monthYear}

+ gotoNeighborMonth(true)} + > + + +
+
+ ) : ( +
+ gotoNeighborMonth(false)} + > + + +

{monthYear}

+ gotoNeighborMonth(true)} + > + + +
+ )} + +
+ SHORT_WEEKDAYS[date.getDay()]} + calendarType="gregory" + value={date} + /> + {isAddSchedule ? ( + + + + ) : ( + + )} +
+
+ ); + }, +); + +SCalendar.displayName = "SCalendar"; + +export default SCalendar; diff --git a/components/dashboard/AvatarCard.tsx b/components/dashboard/AvatarCard.tsx new file mode 100644 index 0000000..5d144df --- /dev/null +++ b/components/dashboard/AvatarCard.tsx @@ -0,0 +1,193 @@ +"use client"; + +import React, { useState, useEffect } from "react"; +import { IComponent } from "@/utils/interfaces"; +import { COLOR_MAPPER, ICON_MAPPER } from "@/utils/constants"; +import { Avatar, Typography } from "@/libraries/material-tailwind"; +import { CircularProgressbarWithChildren } from "@/libraries/react-circular-progressbar"; +import { Icon } from "@/libraries/iconify-react"; +import CardTemplate from "@/components/custom/CardTemplate"; +import { useUser } from "@/contexts/UserContext"; +import EmptyAvatar from "../custom/EmptyAvatar"; +import api from "@/utils/api"; +import { error } from "console"; +import { toast } from "react-toastify"; + +export default function AvatarCard({ className = "" }: IComponent) { + const { userData } = useUser(); + + const [selectPermanentJob, setSelectPermanentJob] = useState(false); + const [selectTempJob, setSelectTempJob] = useState(false); + + const handleSetJobType = (type: string): void => { + let jobTypes: string[] = []; + + if (type == "fulltime") { + !selectPermanentJob && jobTypes.push("permanent"); + selectTempJob && jobTypes.push("temporary"); + } else { + selectPermanentJob && jobTypes.push("permanent"); + !selectTempJob && jobTypes.push("temporary"); + } + + api + .post("/professional/dashboard/jobtype", { + jobType: jobTypes, + }) + .then((res) => { + const { success } = res.data; + if (success) { + toast.success("Succeed in setting a jobType"); + } + }) + .catch((err) => { + toast.error("Failed in setting a jobType."); + }); + }; + + const handlePermanentJobClick = () => { + setSelectPermanentJob(!selectPermanentJob); + handleSetJobType("fulltime"); + }; + + const handleTempJobClick = () => { + setSelectTempJob(!selectTempJob); + handleSetJobType("temporary"); + }; + + useEffect(() => { + api + .get("/professional/dashboard/jobtype") + .then((res) => { + let jobTypes: string[] = res.data.jobType; + jobTypes?.forEach((type) => { + type == "permanent" && setSelectPermanentJob(true); + type == "temporary" && setSelectTempJob(true); + }); + }) + .catch((error) => { + toast.error("Failed in getting a jobType."); + }); + }, []); + + return ( + +
+
+
+ + {userData?.avatar ? ( + + ) : ( + + )} + +
+ +
+ + 40% Completed + +
+
+ +
+ + Welcome to Dental Jobs! + + + Habitasse leo mi enim condimentum rhoncus. Sed non tortor gravida + pulvinar tempus purus. Feugiat quam aliquam. + +
+ +
+ + Availablility + + +
+
+
+ + + Permanent Job + +
+ +
+ +
+
+ + + Temporary Job + +
+ +
+
+
+
+
+ ); +} diff --git a/components/dashboard/BookingsCard.tsx b/components/dashboard/BookingsCard.tsx new file mode 100644 index 0000000..d928168 --- /dev/null +++ b/components/dashboard/BookingsCard.tsx @@ -0,0 +1,119 @@ +"use client"; + +import React, { useEffect, useState } from "react"; +import Link from "next/link"; +import { IBooking, IComponent } from "@/utils/interfaces"; +import { PATH_MAPPER } from "@/utils/constants"; +import CardTemplate from "@/components/custom/CardTemplate"; +import TabButton from "@/components/custom/buttons/TabButton"; +import { List, ListItem, Typography } from "@/libraries/material-tailwind"; +import StatusBadge from "@/components/custom/StatusBadge"; +import moment from "@/libraries/moment"; +import api from "@/utils/api"; +import { toast } from "@/libraries/react-toastify"; +import { getErrorMessage } from "@/utils/functions"; + + +interface IBookings { + id: number; + title: string; + bookedAt: string; + status: string; +} + +export default function BookingsCard({ className = "" }: IComponent) { + const [activeTab, setActiveTab] = useState("Today"); + const [bookings, setBookings] = useState(); + + function filterBookings() { + switch (activeTab) { + case "Today": + return bookings; + case "Past": + return bookings?.filter((booking) => booking.status === "Completed"); + case "Future": + return bookings?.filter((booking) => booking.status === "Pending"); + default: + return bookings; + } + } + + + useEffect(() => { + api + .post("/jobs/professional/dashboard/bookings") + .then((res) => { + setBookings(res.data); + }) + .catch((err) => { + toast.error(getErrorMessage(err)); + }); + }, []); + + return ( + + See All + + } + > +
+
+ setActiveTab("Today")} + > + Today + + setActiveTab("Future")} + > + Future + + setActiveTab("Past")} + > + Past + +
+ + + {filterBookings()?.map((b, i) => ( + +
+ + {b.title} + + + {moment(b.bookedAt).format("llll")} + +
+ + {b.status} +
+ ))} +
+
+
+ ); +} diff --git a/components/dashboard/CalendarCard.tsx b/components/dashboard/CalendarCard.tsx new file mode 100644 index 0000000..ccbcffa --- /dev/null +++ b/components/dashboard/CalendarCard.tsx @@ -0,0 +1,29 @@ +"use client"; +import { AllHTMLAttributes, useState } from "react"; +import { Card, CardBody } from "@/libraries/material-tailwind"; +import SCalendar from "@/components/custom/calendars/SCalendar"; + +export default function CalendarCard({ + className = "", +}: AllHTMLAttributes) { + const [date, setDate] = useState(new Date()); + + const gotoNeighborMonth = (toNext: boolean) => { + const monthOffset = toNext ? 1 : -1; + setDate( + (prev) => new Date(prev.getFullYear(), prev.getMonth() + monthOffset, 1), + ); + }; + + return ( + + + + + + ); +} diff --git a/components/dashboard/CompleteProfileCard/AddLicenseNum.tsx b/components/dashboard/CompleteProfileCard/AddLicenseNum.tsx new file mode 100644 index 0000000..e707f44 --- /dev/null +++ b/components/dashboard/CompleteProfileCard/AddLicenseNum.tsx @@ -0,0 +1,157 @@ +import React, { useState } from "react"; +import Button from "@/components/custom/buttons/Button"; +import { IComponent } from "@/utils/interfaces"; +import { Icon } from "@/libraries/iconify-react"; +import { ICON_MAPPER } from "@/utils/constants"; + +import { + Dialog, + DialogBody, + IconButton, + Typography, + MTInput, +} from "@/libraries/material-tailwind"; +import type { DialogProps } from "@/libraries/material-tailwind"; +import api from "@/utils/api"; +import { toast } from "react-toastify"; +import { getErrorMessage } from "@/utils/functions"; +import { useUser } from "@/contexts/UserContext"; + +interface IProps extends IComponent { + addLicenseNumDialog: boolean; + setAddLicenseNumDialog: Function; + size: DialogProps["size"]; +} + +export default function AddLicenseDialog({ + addLicenseNumDialog, + setAddLicenseNumDialog, + size = "md", +}: IProps) { + const handler = () => { + setAddLicenseNumDialog(!addLicenseNum); + }; + + const [loading, setLoading] = useState(false); + const [licenseNum, setLicenseNum] = useState(); + const { userData, setUserData } = useUser(); + + const handleChange = (e: any) => { + setLicenseNum(e.target.value); + }; + + const addLicenseNum = (result: boolean) => { + setLoading(true); + api + .post("/user/save/profile/license", { result, licenseNum }) + .then((res) => { + setLoading(false); + let tempUserData = userData; + Object(tempUserData?.verifyData).licenseAdded = true; + setUserData(tempUserData); + toast.success("Successfully added"); + }) + .catch((err) => { + setLoading(false); + toast.error(getErrorMessage(err)); + }); + setAddLicenseNumDialog(!addLicenseNum); + }; + + return ( + + +
+ + + +
+
+
+ + Add Your License Number + + +
+
+ + License/Certification + + + Our promise to dental jobs is presenting verified professionals. + (To be equal, we verify dental offices, too). + + + Are you licensed or certified? + + + If licensed, give me your license number. + + +
+ +
+
+ + +
+
+
+
+
+ ); +} diff --git a/components/dashboard/CompleteProfileCard/BackgroundCheckDialog.tsx b/components/dashboard/CompleteProfileCard/BackgroundCheckDialog.tsx new file mode 100644 index 0000000..efc6bfe --- /dev/null +++ b/components/dashboard/CompleteProfileCard/BackgroundCheckDialog.tsx @@ -0,0 +1,349 @@ +"use client"; + +import React, { useState } from 'react' +import { IComponent } from '@/utils/interfaces'; +import { DialogProps } from '@material-tailwind/react' +import { + Dialog, + DialogBody, + IconButton, + Typography, + MTInput, + } from "@/libraries/material-tailwind"; +import Loading from '@/components/custom/Loading'; +import { Icon } from '@/libraries/iconify-react'; +import { ICON_MAPPER } from '@/utils/constants'; +import Button from '@/components/custom/buttons/Button'; +import api from '@/utils/api'; +import { toast } from '@/libraries/react-toastify'; +import { getErrorMessage } from '@/utils/functions'; + +const LIST_CRIMINAL_SEARCH = [ + { + id: 1, + label: "Records with less-than-misdemeanor severity", + }, + { + id: 2, + label: "Records when you were under 18", + }, + { + id: 3, + label: "Misdemeanor:", + }, + { + id: 4, + label: "Deferred / alternative adjudication records", + }, + { + id: 5, + label: "Marijuana possession records", + }, + { + id: 6, + label: "Vehicles & traffic records", + }, + { + id: 7, + label: "Public nuisance records", + }, + { + id: 8, + label: "Alcohol & Tobacco", + }, + { + id: 9, + label: "Marijuana Possession/Use", + }, + { + id: 10, + label: "Driving under the Influence (DUI)", + }, +] + +interface IProps extends IComponent { + backgroundCheck: boolean; + setBackgroundCheck: Function; + size: DialogProps["size"]; + } + +export default function BackgroundCheckDialog({ + backgroundCheck, + setBackgroundCheck, + size = "md" +} : IProps) { + + const handler = () => { + setBackgroundCheck(!backgroundCheck); + }; + + let loading = false; + + const [cardNumber, setCardNumber] = useState(""); + const [expiry, setExpiry] = useState(""); + const [pin, setPin] = useState(""); + + const handleChange = (e: any) => { + const { id, value } = e.target; + + if( id === "cardNum"){ + setCardNumber(value); + } else if ( id === "expDate"){ + setExpiry(value); + } else { + setPin(value); + } + }; + + const handleSaved = () => { + api + .post("/membership/save/credit/card", { + cardNumber: cardNumber, + expiry: expiry, + pin: pin + } ) + .then((res) => { + toast.success("Saved successfully."); + }) + .catch((err) => { + toast.error(getErrorMessage(err)); + }); + + setCardNumber(""); + setExpiry(""); + setPin(""); + }; + + return ( + + {loading ? ( + + ) : ( + +
+ + + +
+
+
+ + Complete A Background Check + +
+
+ + Increase your marketability with a background check badge + +
+
+ + James Mann + +
+ +
+ + +

Unlock the background check badge for your profile

+
+ + +

Increase the number of offices you are eligible to work for

+
+ + +

Includes:

+
+ +
+ + +

SSN Trace

+
+ + +

National Criminal Search

+
+ +
+ {LIST_CRIMINAL_SEARCH.map((e) =>( + +
  • {e.label}
  • +
    + ) + )} +
    + + + +

    Sex Offender Search

    +
    + + +

    Global Watchlist Search

    +
    + + +

    Terrorist Watchlist Search

    +
    + +
    + + +

    CLEAR checks are valid for 1 year from the date of issue

    +
    + +
    + + $15.00 + +
    +
    + + By continuing, you agree to a background check and are purchasing the Background Check Badge for your Cloud + Dentistry profile. The Background Check Badge will only be displayed if your background check is deemed CLEAR + according to the rules above. No refunds are issued for failed or incomplete background checks. + +
    +
    +
    +
    + + Payment Details + + + Card Number + + +
    +
    + + Expiry Date + + +
    +
    + + PIN + + +
    +
    +
    + +
    +
    +
    + )} +
    + ) +} + diff --git a/components/dashboard/CompleteProfileCard/BackgroundDescDialog.tsx b/components/dashboard/CompleteProfileCard/BackgroundDescDialog.tsx new file mode 100644 index 0000000..bd936f2 --- /dev/null +++ b/components/dashboard/CompleteProfileCard/BackgroundDescDialog.tsx @@ -0,0 +1,201 @@ +"use client"; + +import React, { ChangeEvent, useEffect, useState } from "react"; +import Button from "@/components/custom/buttons/Button"; +import { IComponent } from "@/utils/interfaces"; +import { Icon } from "@/libraries/iconify-react"; +import { ICON_MAPPER } from "@/utils/constants"; +import { + Dialog, + DialogBody, + IconButton, + Typography, + Textarea, +} from "@/libraries/material-tailwind"; +import type { DialogProps } from "@/libraries/material-tailwind"; +import api from "@/utils/api"; +import Loading from "@/components/custom/Loading"; +import { toast } from "react-toastify"; +import { useUser } from "@/contexts/UserContext"; + +interface IProps extends IComponent { + backgroundDesc: boolean; + setBackgroundDesc: Function; + size: DialogProps["size"]; +} + +export default function BackgroundDescDialog({ + backgroundDesc, + setBackgroundDesc, + size = "md", +}: IProps) { + const handler = () => { + setBackgroundDesc(!backgroundDesc); + }; + + const [loading, setLoading] = useState(false); + const [description, setDescription] = useState(""); + const { userData, setUserData } = useUser(); + const handleChange = (e: ChangeEvent) => { + setDescription(e.target.value); + }; + + useEffect(() => { + setLoading(true); + api + .get("/user/get/profile/description") + .then((res) => { + setLoading(false); + setDescription(res.data.description); + }) + .catch((err) => { + setLoading(false); + console.log(err); + }); + }, []); + + const handleSave = () => { + setLoading(true); + api + .post("/user/save/profile/description", { description: description }) + .then((res) => { + setLoading(false); + toast.success("Saved successfully."); + let tempUserData = userData; + Object(tempUserData?.verifyData).backgroundDescAdded = true; + setUserData(tempUserData); + setBackgroundDesc(!backgroundDesc); + }) + .catch((err) => { + setLoading(false); + console.log(err); + }); + }; + + return ( + + {loading ? ( + + ) : ( + +
    + + + +
    +
    +
    + + Background Description + + +
    +
    + + Share what makes you great! Include your years of experience, + why you love what you do, your skills and the practice + management systems you use, and everything else that makes you + great at your job. The more descriptive, the better - this is + what offices see. + +