-
-
Next.js + Tailwind CSS + TypeScript Starter
-
- A starter for Next.js, Tailwind CSS, and TypeScript with Absolute
- Import, Seo, Link component, pre-configured with Husky{' '}
-
-
-
- See the repository
-
+
+
+
+ Loading MarketPlace...
-
-
- See all components
-
-
-
- {/* eslint-disable-next-line @next/next/no-img-element */}
-
-
-
-
diff --git a/src/app/purchase-history/layout.tsx b/src/app/purchase-history/layout.tsx
new file mode 100644
index 0000000..13a249d
--- /dev/null
+++ b/src/app/purchase-history/layout.tsx
@@ -0,0 +1,14 @@
+'use client';
+import * as React from 'react';
+
+import PullRefresh from '@/components/PullToRefresh';
+
+export default function Layout({ children }: { children: React.ReactNode }) {
+ return (
+
+ );
+}
diff --git a/src/app/purchase-history/page.tsx b/src/app/purchase-history/page.tsx
new file mode 100644
index 0000000..0df0f55
--- /dev/null
+++ b/src/app/purchase-history/page.tsx
@@ -0,0 +1,126 @@
+'use client';
+import Link from 'next/link';
+import { useEffect } from 'react';
+import { useDispatch, useSelector } from 'react-redux';
+
+import { outfit } from '@/components/FontFamily';
+import Heading from '@/components/heading/Heading';
+
+import Spinner from '@/app/components/Spinner';
+import { getPurchaseHistory } from '@/redux/purchaseHistory/action';
+import { AppDispatch, RootState } from '@/redux/store';
+
+import { LeftArrow, Wallet } from '~/svg';
+
+export type TransectionType = {
+ id: number;
+ credits: number;
+ title: string;
+ author: string;
+ purchasedAt: string;
+};
+
+export type ConsumerCourse = {
+ id: number;
+ purchasedAt?: string;
+ CourseInfo: {
+ title: string;
+ credits: number;
+ providerName?: string;
+ };
+};
+
+const PurchaseHistory = () => {
+ const userId = localStorage.getItem('userId') ?? '';
+ const { walletBalance, purchaseHistory, isLoading, isError } = useSelector(
+ (state: RootState) => state.purchaseHistory
+ );
+ const dispatch: AppDispatch = useDispatch();
+
+ useEffect(() => {
+ dispatch(getPurchaseHistory(userId));
+ }, [dispatch, userId]);
+
+ return (
+
+ {isLoading && (
+
+
+
+ )}
+ {isError && (
+
+ Error...
+
+ )}
+ {!isLoading && !isError && (
+
+ {/* top balance section */}
+
+
+
+
+
+
+
+
+ Wallet Balance
+
+
+
+
+ {walletBalance}
+
+
+
+
+
+
+ {/* heading */}
+
+
+
+ {/* transection details */}
+
+
+ {purchaseHistory?.map((transection: TransectionType) => {
+ const purchasedDate = new Date(transection?.purchasedAt);
+ const formattedDate = purchasedDate.toLocaleDateString('en-US', {
+ month: 'short',
+ day: 'numeric',
+ year: 'numeric',
+ });
+
+ return (
+
+
+
+ {transection?.title}
+
+
+ - {transection?.credits}
+
+
+
+
+ {transection?.author}
+
+
+ {formattedDate}
+
+
+
+
+
+ );
+ })}
+
+
+ )}
+
+ );
+};
+export default PurchaseHistory;
diff --git a/src/app/search/layout.tsx b/src/app/search/layout.tsx
new file mode 100644
index 0000000..6a36c98
--- /dev/null
+++ b/src/app/search/layout.tsx
@@ -0,0 +1,12 @@
+'use client';
+import * as React from 'react';
+
+import PullRefresh from '@/components/PullToRefresh';
+
+export default function Layout({ children }: { children: React.ReactNode }) {
+ return (
+
+ <>{children}>
+
+ );
+}
diff --git a/src/app/search/page.tsx b/src/app/search/page.tsx
new file mode 100644
index 0000000..aa2adf8
--- /dev/null
+++ b/src/app/search/page.tsx
@@ -0,0 +1,275 @@
+'use client';
+import { useState } from 'react';
+import { RxCross1 } from 'react-icons/rx';
+import { useDispatch, useSelector } from 'react-redux';
+import { toast } from 'react-toastify';
+
+import CourseBox from '@/components/Course/CourseBox';
+import CourseCard from '@/components/Course/CourseCard';
+import FilterPage from '@/components/FilterPage';
+import Heading from '@/components/heading/Heading';
+import SearchInput from '@/components/Input/SearchInput';
+import SearchTopbar from '@/components/navbar/SearchTopbar';
+
+import { CourseType } from '@/redux/marketplace/marketplaceReducer';
+import { getSearchCourses } from '@/redux/searchCourses/action';
+import { AppDispatch, RootState } from '@/redux/store';
+
+import { NotFound } from '~/svg';
+
+const getInitialValue2 = () => {
+ return {
+ competencies: '',
+ courseProviders: null,
+ language: null,
+ sortBy: '',
+ };
+};
+
+export type filterObjType = {
+ competencies: string;
+ courseProviders: string[] | null;
+ language: string[] | null;
+ sortBy: string;
+};
+
+export type optionType = {
+ competency: { label: string; value: string }[];
+ language: { label: string; value: string }[];
+ courseProvider: { label: string; value: string }[];
+};
+
+// will filter data
+const filterData = (data: CourseType[], filterObj: filterObjType) => {
+ // if there is no filter applied
+ if (
+ Object.values(filterObj)?.every(
+ (element) => element === null || element === ''
+ )
+ ) {
+ return data;
+ }
+ const { competencies, courseProviders, language, sortBy } = filterObj;
+
+ let filteredResponse = data?.filter((course: CourseType) => {
+ const competencyMatch =
+ !competencies ||
+ course?.competency?.some(
+ (item) => item?.name?.toLowerCase() == competencies?.toLowerCase()
+ );
+ const languageMatch =
+ language?.length === 0 ||
+ !language ||
+ language?.some((lang: string) =>
+ course?.language
+ ?.map((item) => item?.toLowerCase())
+ ?.includes(lang?.toLowerCase())
+ );
+
+ const courseProviderMatch =
+ courseProviders?.length === 0 ||
+ !courseProviders ||
+ courseProviders?.some(
+ (provider: string) =>
+ course?.providerName?.toLowerCase() === provider?.toLowerCase()
+ );
+
+ return competencyMatch && languageMatch && courseProviderMatch;
+ });
+ if (sortBy === 'Low Price') {
+ filteredResponse = filteredResponse.sort((a, b) => a.credits - b.credits);
+ }
+ if (sortBy === 'High Price') {
+ filteredResponse = filteredResponse.sort((a, b) => b.credits - a.credits);
+ }
+ return filteredResponse;
+};
+
+const SearchPage = () => {
+ // will set text to search courses
+ const [searchText, setSearchText] = useState
('');
+
+ // will set data to filter courses
+ const [filterObj, setFilterObj] = useState(getInitialValue2());
+
+ // to dispatch action for api call
+ const dispatch: AppDispatch = useDispatch();
+
+ // will get most popular course data
+ const { mostPopularCourses } = useSelector(
+ (state: RootState) => state?.marketplace
+ );
+
+ // will get searched courses data
+ const { searchCourses } = useSelector(
+ (state: RootState) => state?.searchCourses
+ );
+
+ // this method will apply filter to fetched data
+ const coursesList = filterData(searchCourses, filterObj);
+
+ // will show popular courses initial when true
+ const [showMostPopularCourses, setShowMostPopularCourses] =
+ useState(true);
+
+ // will show filter input fields if true
+ const [isFilterOpen, setFilterOpen] = useState(false);
+
+ // will show filters list in ui
+ const [selectedOption, setSelectedOption] = useState([]);
+
+ // will set all filters applied eg '1 language' '2 course provider' etc
+ const SearchFilterOptions = () => {
+ const { competencies, courseProviders, language, sortBy } = filterObj;
+ setSelectedOption([]);
+ if (competencies) setSelectedOption((prev) => [...prev, competencies]);
+ if (courseProviders?.length)
+ setSelectedOption((prev) => [
+ ...prev,
+ `${courseProviders?.length} course provider`,
+ ]);
+ if (language?.length)
+ setSelectedOption((prev) => [...prev, `${language.length} language`]);
+ if (sortBy) {
+ setSelectedOption((prev) => [...prev, sortBy]);
+ }
+ };
+
+ // will fetch data and hide popular courses
+ const handleSearchCourse = async () => {
+ if (searchText) {
+ dispatch(getSearchCourses(searchText));
+ setShowMostPopularCourses(false);
+ } else {
+ toast.error('please enter course name', {
+ draggable: false,
+ });
+ }
+ };
+
+ // will remove search field
+ const handleCrossIcon = () => {
+ setShowMostPopularCourses(true);
+ setSearchText('');
+ };
+
+ // will remove filters and set the filters to null
+ const handleDeleteSelectedOption = (indexToRemove: number) => {
+ const updatedOptions = [...selectedOption];
+ const removeFilter = updatedOptions.splice(indexToRemove, 1);
+ if (removeFilter?.join()?.includes('High Price' || 'Low Price')) {
+ setFilterObj((pre) => {
+ return {
+ ...pre,
+ sortBy: '',
+ };
+ });
+ } else if (removeFilter?.join()?.includes('language')) {
+ setFilterObj((pre) => {
+ return {
+ ...pre,
+ language: null,
+ };
+ });
+ } else if (removeFilter?.join()?.includes('provider')) {
+ setFilterObj((pre) => {
+ return {
+ ...pre,
+ courseProviders: null,
+ };
+ });
+ } else {
+ setFilterObj((pre) => {
+ return {
+ ...pre,
+ competencies: '',
+ };
+ });
+ }
+ setSelectedOption(updatedOptions);
+ };
+
+ // filter input fields
+ if (isFilterOpen) {
+ return (
+
+ );
+ }
+
+ return (
+
+
+
setFilterOpen(true)}
+ />
+ {selectedOption?.length != 0 && (
+
+
Filtered by:
+ {selectedOption?.map((option, index) => {
+ return (
+
+
{option}
+
handleDeleteSelectedOption(index)}
+ style={{ cursor: 'pointer' }}
+ />
+
+ );
+ })}
+
+ )}
+
+ {/* either */}
+ {showMostPopularCourses ? (
+
+ ) : coursesList?.length != 0 ? (
+
+
+
+
+
+ {coursesList?.map((course: CourseType, index: number) => {
+ return ;
+ })}
+
+
+ ) : (
+
+
+
+
+
+
+ Search not found
+
+
+ Please activate the property name search service that is available
+ correctly
+
+
+
+ )}
+
+ );
+};
+export default SearchPage;
diff --git a/src/components/Competencies.tsx b/src/components/Competencies.tsx
new file mode 100644
index 0000000..8f0c6c5
--- /dev/null
+++ b/src/components/Competencies.tsx
@@ -0,0 +1,19 @@
+import SingleCompetency from '@/components/course-description/SingleCompetency';
+
+import { CompetencyType } from '@/redux/marketplace/marketplaceReducer';
+
+const Competencies = ({ competency }: { competency: CompetencyType[] }) => {
+ return (
+
+ {competency?.map((item) => {
+ return (
+
+ );
+ })}
+
+ );
+};
+export default Competencies;
diff --git a/src/components/Course/CourseBox.tsx b/src/components/Course/CourseBox.tsx
new file mode 100644
index 0000000..904dca2
--- /dev/null
+++ b/src/components/Course/CourseBox.tsx
@@ -0,0 +1,26 @@
+import CourseSlides from '@/components/Course/CourseSlides';
+import Heading from '@/components/heading/Heading';
+import SeeAll from '@/components/heading/SeeAll';
+
+import { CourseType } from '@/redux/marketplace/marketplaceReducer';
+
+const CourseBox = ({
+ heading,
+ CoursesList,
+ handleSeeAllButtonClick,
+}: {
+ heading: string;
+ CoursesList: CourseType[];
+ handleSeeAllButtonClick: string;
+}) => {
+ return (
+
+ );
+};
+export default CourseBox;
diff --git a/src/components/Course/CourseCard.tsx b/src/components/Course/CourseCard.tsx
new file mode 100644
index 0000000..18640c0
--- /dev/null
+++ b/src/components/Course/CourseCard.tsx
@@ -0,0 +1,122 @@
+'use client';
+import Image from 'next/image';
+import { useRouter } from 'next/navigation';
+import { useDispatch } from 'react-redux';
+
+import { outfit } from '@/components/FontFamily';
+
+import { getSaveCourseAndStatus } from '@/redux/coursesDescription/action';
+import { GET_SAVE_COURSE_AND_STATUS_SUCCESS } from '@/redux/coursesDescription/type';
+import { CourseType } from '@/redux/marketplace/marketplaceReducer';
+import { AppDispatch } from '@/redux/store';
+
+import ColoredText from '../heading/ColoredText';
+
+import { EditIcon, Star } from '~/svg';
+
+const CourseCard = ({ courseDetails }: { courseDetails: CourseType }) => {
+ const router = useRouter();
+
+ const dispatch: AppDispatch = useDispatch();
+
+ const handleRoute = () => {
+ const userId = localStorage.getItem('userId') ?? '';
+ dispatch(
+ getSaveCourseAndStatus(userId, courseDetails?.courseId, courseDetails)
+ ).then((res: unknown) => {
+ if (
+ (res as { type?: string })?.type === GET_SAVE_COURSE_AND_STATUS_SUCCESS
+ ) {
+ router.push(`/course-description/${courseDetails?.courseId}`);
+ }
+ });
+ };
+
+ return (
+
+
+
+
+
+
+
+
+ {courseDetails?.title}
+
+
+ {courseDetails?.competency?.map((item, index) => {
+ if (index < 2) {
+ return (
+
+ {item.name} (
+ {item?.levels
+ .map((level) => `L${level.levelNumber}`)
+ .join(', ')}
+ ){index == 1 && '....'}
+
+ );
+ }
+ return null; // Skip rendering for keys beyond the first two and the ellipsis
+ })}
+
+
+ {/* Icon and language list */}
+
+
+
+
+
+ {courseDetails?.providerName}
+
+ {courseDetails?.language?.map((item: string, index: number) => (
+
+ ))}
+
+
+
+
+ Cr. {courseDetails?.credits}
+
+
+ {courseDetails?.avgRating !== null &&
+ courseDetails?.avgRating !== undefined && (
+ <>
+ {courseDetails?.avgRating}{' '}
+
+ {' '}
+
+ >
+ )}
+
+
+
+
+ );
+};
+export default CourseCard;
diff --git a/src/components/Course/CourseSlides.tsx b/src/components/Course/CourseSlides.tsx
new file mode 100644
index 0000000..c9dc35a
--- /dev/null
+++ b/src/components/Course/CourseSlides.tsx
@@ -0,0 +1,39 @@
+import { Swiper, SwiperSlide } from 'swiper/react';
+import 'swiper/css';
+
+import CourseCard from '@/components/Course/CourseCard';
+
+import { CourseType } from '@/redux/marketplace/marketplaceReducer';
+
+import NoCoursesFound from './NoCoursesFound';
+
+const CourseSlides = ({ CoursesList }: { CoursesList: CourseType[] }) => {
+ const spaceBetween = CoursesList?.length > 1 ? -10 : 0;
+
+ return (
+ <>
+ {CoursesList?.length > 0 ? (
+
+
+ {CoursesList?.map((course) => {
+ return (
+
+
+
+ );
+ })}
+
+
+ ) : (
+
+
+
+ )}
+ >
+ );
+};
+export default CourseSlides;
diff --git a/src/components/Course/NoCoursesFound.tsx b/src/components/Course/NoCoursesFound.tsx
new file mode 100644
index 0000000..0bd7c97
--- /dev/null
+++ b/src/components/Course/NoCoursesFound.tsx
@@ -0,0 +1,18 @@
+import React from 'react';
+
+import EmptyBox from '~/svg/emptyBox.svg';
+
+const NoCoursesFound = () => {
+ return (
+
+
+
+
+
+ No courses found.
+
+
+ );
+};
+
+export default NoCoursesFound;
diff --git a/src/components/Course/SingleCompletedCourse.tsx b/src/components/Course/SingleCompletedCourse.tsx
new file mode 100644
index 0000000..4eac770
--- /dev/null
+++ b/src/components/Course/SingleCompletedCourse.tsx
@@ -0,0 +1,68 @@
+'use client';
+import Image from 'next/image';
+import { useState } from 'react';
+import { FaRegStar, FaStar } from 'react-icons/fa';
+import { useDispatch } from 'react-redux';
+
+import {
+ CompletedCourseType,
+ giveFeedbackRating,
+} from '@/redux/completedCourse/action';
+import { AppDispatch } from '@/redux/store';
+
+const SingleCompletedCourse = ({
+ courseDetail,
+}: {
+ courseDetail: CompletedCourseType;
+}) => {
+ const [start, setStar] = useState(parseInt(courseDetail?.rating ?? 0));
+ const dispatch: AppDispatch = useDispatch();
+ const handleClick = (index: number) => {
+ const payload = {
+ courseInfoId: courseDetail.courseInfoId,
+ rating: index,
+ };
+ dispatch(giveFeedbackRating(courseDetail.consumerId, payload));
+ setStar(index);
+ };
+ return (
+
+
+
+
+
+ {courseDetail?.CourseInfo?.title}
+
+
+ {courseDetail?.CourseInfo?.author}
+
+
+
+ {Array.from({ length: start })?.map((_, index) => (
+ handleClick(index + 1)}
+ />
+ ))}
+ {Array.from({ length: 5 - start }).map((_, index) => (
+ handleClick(start + index + 1)}
+ />
+ ))}
+
+
+
+ );
+};
+export default SingleCompletedCourse;
diff --git a/src/components/ErrorScreen/ConnectionCheckWrapper.tsx b/src/components/ErrorScreen/ConnectionCheckWrapper.tsx
new file mode 100644
index 0000000..4dd957a
--- /dev/null
+++ b/src/components/ErrorScreen/ConnectionCheckWrapper.tsx
@@ -0,0 +1,23 @@
+'use client';
+import React from 'react';
+import { Detector } from 'react-detect-offline';
+
+import NoInternetConnectionError from '@/components/ErrorScreen/NoInternetConnectionError';
+
+const ConnectionCheckWrapper = ({
+ children,
+}: {
+ children: React.ReactNode;
+}) => {
+ return (
+
+ (
+ <> {online ? <>{children}> : }>
+ )}
+ />
+
+ );
+};
+
+export default ConnectionCheckWrapper;
diff --git a/src/components/ErrorScreen/NoInternetConnectionError.tsx b/src/components/ErrorScreen/NoInternetConnectionError.tsx
new file mode 100644
index 0000000..602b88a
--- /dev/null
+++ b/src/components/ErrorScreen/NoInternetConnectionError.tsx
@@ -0,0 +1,37 @@
+import React from 'react';
+
+import { outfit } from '@/components/FontFamily';
+
+import NoInternetErrorIcon from '~/svg/NoInternetErrorIcon.svg';
+const NoInternetConnectionError = () => {
+ const handleRetry = () => {
+ window.location.reload();
+ };
+
+ return (
+
+
+
+
+
Oops! no connection
+
+ There seems to be a problem with your network connection. Please
+ check your internet connection.
+
+
+
+
+
+ );
+};
+
+export default NoInternetConnectionError;
diff --git a/src/components/FilterPage.tsx b/src/components/FilterPage.tsx
new file mode 100644
index 0000000..7bd228d
--- /dev/null
+++ b/src/components/FilterPage.tsx
@@ -0,0 +1,114 @@
+'use client';
+import { RxCross2 } from 'react-icons/rx';
+
+import ButtonFill from '@/components/buttons/ButtonFill';
+import { outfit } from '@/components/FontFamily';
+import MultipleButton from '@/components/Input/MultipleButton';
+import MultiSelectTag from '@/components/Input/MultiSelectTag';
+
+import MultiSelectCreatable from './Input/MultiSelectCreatable';
+import SelectTag from './Input/SelectTag';
+import {
+ COMPETENCY_OPTIONS,
+ LANGUAGE_OPTIONS,
+ PROVIDER_OPTIONS,
+} from './options';
+import { filterObjType } from '../app/search/page';
+
+const FilterCourse = ({
+ filterObj,
+ setFilterObj,
+ setFilterOpen,
+ SearchFilterOptions,
+ handleFilterCourse,
+}: {
+ filterObj: filterObjType;
+ setFilterObj: (value: filterObjType) => void;
+ setFilterOpen: (value: boolean) => void;
+ SearchFilterOptions: () => void;
+ handleFilterCourse: () => void;
+}) => {
+ const handleChange = (name: string, value: string[] | string) => {
+ setFilterObj({ ...filterObj, [name]: value });
+ };
+
+ const handleApplyFilter = () => {
+ SearchFilterOptions();
+ setFilterOpen(false);
+ handleFilterCourse();
+ };
+
+ return (
+
+ {/* heading */}
+
+
Filter
+
setFilterOpen(false)}
+ className='cursor-pointer'
+ />
+
+ {/* Competencies*/}
+
+
+ handleChange('competencies', value)}
+ value={filterObj?.competencies ?? ''}
+ options={COMPETENCY_OPTIONS}
+ placeholder='--Select--'
+ />
+
+
+ {/* third party provider*/}
+
+
+
+ handleChange('courseProviders', value)}
+ value={filterObj?.courseProviders ?? []}
+ options={PROVIDER_OPTIONS}
+ placeholder='--Select--'
+ />
+
+ {/*language*/}
+
+
+
+ handleChange('language', value)}
+ value={filterObj?.language ?? []}
+ options={LANGUAGE_OPTIONS}
+ placeholder='--Select--'
+ />
+
+ {/*sort by*/}
+
+
+ handleChange('sortBy', val)}
+ value={filterObj.sortBy}
+ />
+
+
+
+
+ Apply Filters
+
+
+
+ );
+};
+export default FilterCourse;
diff --git a/src/components/FontFamily.ts b/src/components/FontFamily.ts
new file mode 100644
index 0000000..f38c1f4
--- /dev/null
+++ b/src/components/FontFamily.ts
@@ -0,0 +1,18 @@
+import { Outfit, Oxanium, Poppins } from 'next/font/google';
+
+const oxanium = Oxanium({
+ subsets: ['latin'],
+ weight: ['600'],
+});
+
+const outfit = Outfit({
+ subsets: ['latin'],
+ weight: ['600', '500', '400'],
+});
+
+const poppins = Poppins({
+ subsets: ['latin'],
+ weight: ['600'],
+});
+
+export { outfit, oxanium, poppins };
diff --git a/src/components/Input/MultiSelectCreatable.tsx b/src/components/Input/MultiSelectCreatable.tsx
new file mode 100644
index 0000000..45d3d71
--- /dev/null
+++ b/src/components/Input/MultiSelectCreatable.tsx
@@ -0,0 +1,92 @@
+'use client';
+import React from 'react';
+import { ActionMeta, MultiValue } from 'react-select';
+import CreatableSelect from 'react-select/creatable';
+
+export type OptionType = {
+ label: string;
+ value: string;
+};
+
+export type PropsType = {
+ onChange: (value: string[]) => void;
+ value: string[];
+ options: OptionType[];
+ placeholder: string;
+ errorMessage?: string;
+ paddingY?: string;
+ isDisabled?: boolean;
+};
+
+const MultiSelectCreatable = ({
+ options,
+ onChange,
+ value,
+ placeholder,
+ errorMessage = '',
+ paddingY = '',
+ isDisabled = false,
+}: PropsType) => {
+ const handleOption = (
+ evt: MultiValue,
+ action: ActionMeta
+ ) => {
+ if (
+ action.action === 'select-option' ||
+ action.action === 'create-option'
+ ) {
+ onChange(evt?.map((item: OptionType) => item?.value));
+ } else if (action.action === 'remove-value') {
+ if (Array.isArray(value)) {
+ onChange(
+ value?.filter((option) => option !== action?.removedValue?.value)
+ );
+ }
+ }
+ };
+
+ return (
+
+
({
+ label: item,
+ value: item,
+ }))}
+ className='basic-multi-select'
+ classNamePrefix='select'
+ placeholder={placeholder}
+ onChange={(evt, action) => {
+ if (Array.isArray(evt)) {
+ handleOption(evt, action);
+ }
+ }}
+ styles={{
+ input: (base) => ({
+ ...base,
+ 'input:focus': {
+ boxShadow: 'none',
+ },
+ }),
+ control: (baseStyles) => ({
+ ...baseStyles,
+ borderColor: errorMessage ? 'red' : '#E3E7EF',
+ paddingTop: paddingY,
+ paddingBottom: paddingY,
+ borderRadius: '8px',
+ }),
+ }}
+ />
+ {errorMessage && (
+
+ {errorMessage}
+
+ )}
+
+ );
+};
+
+export default MultiSelectCreatable;
diff --git a/src/components/Input/MultiSelectTag.tsx b/src/components/Input/MultiSelectTag.tsx
new file mode 100644
index 0000000..7dd582f
--- /dev/null
+++ b/src/components/Input/MultiSelectTag.tsx
@@ -0,0 +1,83 @@
+'use client';
+import React from 'react';
+import Select, { ActionMeta, MultiValue } from 'react-select';
+export type OptionType = {
+ label: string;
+ value: string;
+};
+export type PropsType = {
+ onChange: (value: string[]) => void;
+ value: string[];
+ options: OptionType[];
+ placeholder: string;
+ errorMessage?: string;
+ paddingY?: string;
+ isDisabled?: boolean;
+};
+const MultiSelectTag = ({
+ options,
+ onChange,
+ value,
+ placeholder,
+ errorMessage = '',
+ paddingY = '',
+ isDisabled = false,
+}: PropsType) => {
+ const handleOption = (
+ evt: MultiValue,
+ action: ActionMeta
+ ) => {
+ if (action.action === 'select-option') {
+ onChange(evt?.map((item: OptionType) => item?.value));
+ } else if (action.action === 'remove-value') {
+ if (Array.isArray(value)) {
+ onChange(
+ value?.filter((option) => option !== action?.removedValue?.value)
+ );
+ }
+ }
+ };
+ return (
+
+
+ );
+};
+export default MultiSelectTag;
diff --git a/src/components/Input/MultipleButton.tsx b/src/components/Input/MultipleButton.tsx
new file mode 100644
index 0000000..7ce29fb
--- /dev/null
+++ b/src/components/Input/MultipleButton.tsx
@@ -0,0 +1,30 @@
+const MultipleButton = ({
+ options,
+ onClick,
+ value,
+}: {
+ options: string[];
+ onClick: (val: string) => void;
+ value: string;
+}) => {
+ return (
+
+ {options.map((c, index) => {
+ return (
+
+ );
+ })}
+
+ );
+};
+export default MultipleButton;
diff --git a/src/components/Input/SearchInput.tsx b/src/components/Input/SearchInput.tsx
new file mode 100644
index 0000000..4ddcc57
--- /dev/null
+++ b/src/components/Input/SearchInput.tsx
@@ -0,0 +1,98 @@
+'use client';
+
+import React, { useState } from 'react';
+import { RxCross1 } from 'react-icons/rx';
+
+import { Filter } from '~/svg';
+
+type PropsType = {
+ onChange: (value: string) => void;
+ value: string;
+ placeholder: string;
+ handleClick: () => void;
+ selectedOptionCount?: number;
+ handleCrossIcon: () => void;
+ handleFilterIcon: () => void;
+};
+
+const SearchInput = ({
+ value,
+ placeholder,
+ onChange,
+ handleClick = () => null,
+ selectedOptionCount,
+ handleCrossIcon,
+ handleFilterIcon,
+}: PropsType) => {
+ const [showCross, setShowCross] = useState(false);
+ const handleKeyDown = (e: React.KeyboardEvent) => {
+ if (e.key === 'Enter') {
+ setShowCross(false);
+ handleClick();
+ }
+ };
+
+ return (
+
+
+
+
+ {
+ onChange(e?.target?.value), setShowCross(true);
+ }}
+ onKeyDown={handleKeyDown}
+ />
+
+
+
+ {value != '' && showCross ? (
+
handleCrossIcon()}
+ className='relative inline-block'
+ >
+
+
+ ) : (
+
handleFilterIcon()}
+ className='relative inline-block cursor-pointer'
+ >
+ {selectedOptionCount != 0 && (
+
+ {selectedOptionCount}
+
+ )}
+
+
+
+
+ )}
+
+
+ );
+};
+
+export default SearchInput;
diff --git a/src/components/Input/SelectTag.tsx b/src/components/Input/SelectTag.tsx
new file mode 100644
index 0000000..c9c90df
--- /dev/null
+++ b/src/components/Input/SelectTag.tsx
@@ -0,0 +1,69 @@
+'use client';
+import React from 'react';
+import Select, { SingleValue } from 'react-select';
+
+export type OptionType = {
+ label: string;
+ value: string;
+};
+
+export type PropsType = {
+ onChange: (value: string) => void;
+ value: string;
+ width?: string;
+ options: OptionType[];
+ placeholder: string;
+ errorMessage?: string;
+ paddingY?: string;
+ isDisabled?: boolean;
+};
+
+const SelectTag = ({
+ options,
+ onChange,
+ width = '100%',
+ value,
+ placeholder,
+ errorMessage = '',
+ paddingY = '',
+ isDisabled = false,
+}: PropsType) => {
+ return (
+
+
+ );
+};
+
+export default SelectTag;
diff --git a/src/components/Input/UnclickableSearchInput.tsx b/src/components/Input/UnclickableSearchInput.tsx
new file mode 100644
index 0000000..54ace5f
--- /dev/null
+++ b/src/components/Input/UnclickableSearchInput.tsx
@@ -0,0 +1,45 @@
+'use client';
+
+import React from 'react';
+
+import { Filter } from '~/svg';
+
+const UnclickableSearchInput = ({ placeholder }: { placeholder: string }) => {
+ return (
+
+ );
+};
+
+export default UnclickableSearchInput;
diff --git a/src/components/Overview.tsx b/src/components/Overview.tsx
new file mode 100644
index 0000000..fc95534
--- /dev/null
+++ b/src/components/Overview.tsx
@@ -0,0 +1,12 @@
+const Overview = ({ about }: { about: string }) => {
+ return (
+
+
+ About Course
+
+
+
{about}
+
+ );
+};
+export default Overview;
diff --git a/src/components/PullToRefresh.tsx b/src/components/PullToRefresh.tsx
new file mode 100644
index 0000000..5b89ad7
--- /dev/null
+++ b/src/components/PullToRefresh.tsx
@@ -0,0 +1,31 @@
+'use client';
+import React from 'react';
+import PullToRefresh from 'react-simple-pull-to-refresh';
+type PropsType = {
+ children: React.ReactElement;
+};
+
+const PullRefresh = ({ children }: PropsType) => {
+ const refreshHandler = async () => {
+ await new Promise((resolve) => {
+ setTimeout(() => {
+ window.location.href = '/';
+ resolve;
+ }, 2000);
+ });
+ };
+
+ return (
+
+ );
+};
+
+export default PullRefresh;
diff --git a/src/components/SwipeSlide.tsx b/src/components/SwipeSlide.tsx
new file mode 100644
index 0000000..8223fe3
--- /dev/null
+++ b/src/components/SwipeSlide.tsx
@@ -0,0 +1,56 @@
+import { outfit, poppins } from '@/components/FontFamily';
+
+import { OnGoingCoursesType } from '@/app/ongoing-courses/page';
+
+const SwipeSlide = ({ course }: { course: OnGoingCoursesType }) => {
+ return (
+
+
+ Ongoing
+
+
+ {course?.CourseInfo?.title}
+
+
+ {course?.CourseInfo?.competency?.length > 0 &&
+ course?.CourseInfo?.competency?.map((item, index) => {
+ if (index < 2) {
+ return (
+
+ {item?.name} (
+ {item?.levels
+ .map((level) => `L${level?.levelNumber}`)
+ .join(', ')}
+ ),{index == 1 && '....'}
+
+ );
+ }
+ return null; // Skip rendering for keys beyond the first two and the ellipsis
+ })}
+
+
+
+ Created by
+
+ {course?.CourseInfo?.providerName}
+
+
+
+ Continue
+
+
+
+ );
+};
+
+export default SwipeSlide;
diff --git a/src/components/SwiperDiv.tsx b/src/components/SwiperDiv.tsx
new file mode 100644
index 0000000..67dff87
--- /dev/null
+++ b/src/components/SwiperDiv.tsx
@@ -0,0 +1,41 @@
+import { useSelector } from 'react-redux';
+import { Pagination } from 'swiper/modules';
+import { Swiper, SwiperSlide } from 'swiper/react';
+import 'swiper/css';
+import 'swiper/css/pagination';
+
+import SwipeSlide from '@/components/SwipeSlide';
+
+import { OnGoingCoursesType } from '@/app/ongoing-courses/page';
+import { RootState } from '@/redux/store';
+
+import NoCoursesFound from './Course/NoCoursesFound';
+
+const SwiperDiv = () => {
+ const { ongoingCourses } = useSelector(
+ (state: RootState) => state?.marketplace
+ );
+ return (
+
+ {ongoingCourses && ongoingCourses.length > 0 ? (
+
+ {ongoingCourses?.map((course: OnGoingCoursesType) => (
+
+
+
+ ))}
+
+ ) : (
+
+
+
+ )}
+
+ );
+};
+export default SwiperDiv;
diff --git a/src/components/buttons/ButtonFill.tsx b/src/components/buttons/ButtonFill.tsx
new file mode 100644
index 0000000..64580bf
--- /dev/null
+++ b/src/components/buttons/ButtonFill.tsx
@@ -0,0 +1,28 @@
+'use client';
+import React from 'react';
+
+export type ButtonType = {
+ onClick: () => void;
+ children: React.ReactNode;
+ classes: string;
+ disabled?: boolean;
+};
+
+const ButtonFill = ({
+ onClick,
+ children,
+ classes,
+ disabled = false,
+}: ButtonType) => {
+ return (
+
+ );
+};
+
+export default ButtonFill;
diff --git a/src/components/buttons/ButtonOutline.tsx b/src/components/buttons/ButtonOutline.tsx
new file mode 100644
index 0000000..76018fe
--- /dev/null
+++ b/src/components/buttons/ButtonOutline.tsx
@@ -0,0 +1,17 @@
+'use client';
+import React from 'react';
+
+import { ButtonType } from '@/components/buttons/ButtonFill';
+
+const ButtonOutline = ({ onClick, children, classes }: ButtonType) => {
+ return (
+
+ );
+};
+
+export default ButtonOutline;
diff --git a/src/components/course-description/SingleCompetency.tsx b/src/components/course-description/SingleCompetency.tsx
new file mode 100644
index 0000000..bfcea80
--- /dev/null
+++ b/src/components/course-description/SingleCompetency.tsx
@@ -0,0 +1,57 @@
+import { useState } from 'react';
+import { BiSolidBarChartAlt2 } from 'react-icons/bi';
+import { MdKeyboardArrowDown, MdKeyboardArrowUp } from 'react-icons/md';
+
+import { outfit, poppins } from '@/components/FontFamily';
+
+import { LevelsType } from '@/redux/marketplace/marketplaceReducer';
+
+type propType = {
+ name: string;
+ levels: LevelsType[];
+};
+
+const SingleCompetency = ({ competency }: { competency: propType }) => {
+ const [open, setOpen] = useState(false);
+ const { name, levels } = competency;
+ return (
+
+
+
+
{name}
+
setOpen(!open)}
+ className='cursor-pointer rounded-sm hover:bg-slate-200'
+ >
+ {open ? (
+
+ ) : (
+
+ )}
+
+
+ {open && (
+
+
+
+ {levels?.map((level) => {
+ return (
+ -
+ • {level?.name}
+
+ );
+ })}
+
+
+ )}
+
+
+ );
+};
+export default SingleCompetency;
diff --git a/src/components/heading/ColoredText.tsx b/src/components/heading/ColoredText.tsx
new file mode 100644
index 0000000..1e6f75b
--- /dev/null
+++ b/src/components/heading/ColoredText.tsx
@@ -0,0 +1,4 @@
+const ColoredText = ({ text, classes }: { text: string; classes: string }) => {
+ return {text}
;
+};
+export default ColoredText;
diff --git a/src/components/heading/Heading.tsx b/src/components/heading/Heading.tsx
new file mode 100644
index 0000000..74a2b66
--- /dev/null
+++ b/src/components/heading/Heading.tsx
@@ -0,0 +1,10 @@
+import { outfit } from '@/components/FontFamily';
+
+const Heading = ({ heading }: { heading: string }) => {
+ return (
+
+ {heading}
+
+ );
+};
+export default Heading;
diff --git a/src/components/heading/SeeAll.tsx b/src/components/heading/SeeAll.tsx
new file mode 100644
index 0000000..0496082
--- /dev/null
+++ b/src/components/heading/SeeAll.tsx
@@ -0,0 +1,22 @@
+import Link from 'next/link';
+
+import { outfit } from '@/components/FontFamily';
+
+const SeeAll = ({
+ heading,
+ redirectTo,
+}: {
+ heading: string;
+ redirectTo: string;
+}) => {
+ return (
+
+
+ {heading}
+
+
+ );
+};
+export default SeeAll;
diff --git a/src/components/navbar/Footer.tsx b/src/components/navbar/Footer.tsx
new file mode 100644
index 0000000..f7bdd2f
--- /dev/null
+++ b/src/components/navbar/Footer.tsx
@@ -0,0 +1,34 @@
+import React from 'react';
+import { AiFillSetting } from 'react-icons/ai';
+import { HiOutlineUserCircle } from 'react-icons/hi';
+
+import { MarketPlaceIcon } from '~/svg';
+
+const Footer = () => {
+ return (
+
+ );
+};
+
+export default Footer;
diff --git a/src/components/navbar/SearchTopbar.tsx b/src/components/navbar/SearchTopbar.tsx
new file mode 100644
index 0000000..34be599
--- /dev/null
+++ b/src/components/navbar/SearchTopbar.tsx
@@ -0,0 +1,30 @@
+import Link from 'next/link';
+import React from 'react';
+import { RxCross1 } from 'react-icons/rx';
+
+import Heading from '@/components/heading/Heading';
+
+const SearchTopbar = ({
+ title,
+ redirectTo,
+}: {
+ title: string;
+ redirectTo: string;
+}) => {
+ return (
+
+
+
+
+ );
+};
+
+export default SearchTopbar;
diff --git a/src/components/navbar/TitleNavbar.tsx b/src/components/navbar/TitleNavbar.tsx
new file mode 100644
index 0000000..58edb32
--- /dev/null
+++ b/src/components/navbar/TitleNavbar.tsx
@@ -0,0 +1,24 @@
+import { useRouter } from 'next/navigation';
+import React from 'react';
+import { MdOutlineKeyboardArrowLeft } from 'react-icons/md';
+
+import Heading from '@/components/heading/Heading';
+
+const TitleNavbar = ({ title }: { title: string }) => {
+ const router = useRouter();
+
+ return (
+
+
+
+
+ );
+};
+
+export default TitleNavbar;
diff --git a/src/components/navbar/TopNavbar.tsx b/src/components/navbar/TopNavbar.tsx
new file mode 100644
index 0000000..9fbcd94
--- /dev/null
+++ b/src/components/navbar/TopNavbar.tsx
@@ -0,0 +1,31 @@
+import Link from 'next/link';
+import React from 'react';
+
+import { oxanium } from '../FontFamily';
+
+import { NotificationBell, Wallet } from '~/svg';
+
+const TopNavbar = () => {
+ return (
+
+ );
+};
+
+export default TopNavbar;
diff --git a/src/components/options.ts b/src/components/options.ts
new file mode 100644
index 0000000..2ca1ecd
--- /dev/null
+++ b/src/components/options.ts
@@ -0,0 +1,72 @@
+export const COMPETENCY_OPTIONS = [
+ {
+ label: 'competency1',
+ value: 'competency1',
+ },
+ {
+ label: 'competency2',
+ value: 'competency2',
+ },
+ {
+ label: 'competency3',
+ value: 'competency3',
+ },
+ {
+ label: 'competency4',
+ value: 'competency4',
+ },
+ {
+ label: 'competency5',
+ value: 'competency5',
+ },
+ {
+ label: 'Backend Engineering',
+ value: 'Backend Engineering',
+ },
+ {
+ label: 'API Development',
+ value: 'API Development',
+ },
+ {
+ label: 'Typescript',
+ value: 'Typescript',
+ },
+];
+
+export const PROVIDER_OPTIONS = [
+ {
+ label: 'my code',
+ value: 'my code',
+ },
+ {
+ label: 'Code',
+ value: 'Code',
+ },
+ {
+ label: 'Udemy',
+ value: 'Udemy',
+ },
+ {
+ label: 'sdf',
+ value: 'sdf',
+ },
+];
+
+export const LANGUAGE_OPTIONS = [
+ {
+ label: 'en',
+ value: 'en',
+ },
+ {
+ label: 'english',
+ value: 'english',
+ },
+ {
+ label: 'hindi',
+ value: 'hindi',
+ },
+ {
+ label: 'marathi',
+ value: 'marathi',
+ },
+];
diff --git a/src/components/popUp/BasicPopup.tsx b/src/components/popUp/BasicPopup.tsx
new file mode 100644
index 0000000..ee385e1
--- /dev/null
+++ b/src/components/popUp/BasicPopup.tsx
@@ -0,0 +1,51 @@
+import { RxCross1 } from 'react-icons/rx';
+
+import { SingleCourseType } from '@/app/course-description/[id]/page';
+
+const BasicPopup = ({
+ setDetailsPopUp,
+ courseDetails,
+}: {
+ setDetailsPopUp: (value: boolean) => void;
+ courseDetails: SingleCourseType;
+}) => {
+ const purchasedDate = new Date(courseDetails?.updatedAt);
+ const formattedDate = purchasedDate.toLocaleDateString('en-US', {
+ month: 'short',
+ day: 'numeric',
+ year: 'numeric',
+ });
+ return (
+
+
+ {/* pop up height and width and position */}
+
+ {/* popUp inside thing */}
+
+
setDetailsPopUp(false)}
+ >
+
+
+
+ {courseDetails?.title}
+
+
+
+ Created by:{' '}
+ {courseDetails?.providerName}
+
+
+ Author: {courseDetails?.author}
+
+
+ Last Updated On:{' '}
+ {formattedDate}
+
+
+
+
+ );
+};
+export default BasicPopup;
diff --git a/src/components/popUp/ButtonPopup.tsx b/src/components/popUp/ButtonPopup.tsx
new file mode 100644
index 0000000..3b2971c
--- /dev/null
+++ b/src/components/popUp/ButtonPopup.tsx
@@ -0,0 +1,47 @@
+import ButtonFill from '@/components/buttons/ButtonFill';
+
+const ButtonPopup = ({
+ setShowPopUp,
+ purchaseCourse,
+ credit,
+}: {
+ setShowPopUp: (value: boolean) => void;
+ purchaseCourse: () => void;
+ credit: number;
+}) => {
+ return (
+
+
+ {/* pop up height and width and position */}
+
+ {/* popUp inside thing */}
+
+
+ Confirm Your Purchase
+
+
+
+ Thank you for choosing us for your learning journey! Cr. {credit}{' '}
+ will be debited from your wallet for this purchase.
+
+
+ setShowPopUp(false)}
+ classes='w-[140px] h-[36px] bg-[#EEF5FF] text-[#385B8B]'
+ >
+ Close
+
+ purchaseCourse()}
+ classes='w-[140px] h-[36px] bg-[#385B8B] text-[#fff]'
+ >
+ Confirm
+
+
+
+
+
+ );
+};
+
+export default ButtonPopup;
diff --git a/src/redux/Provider.tsx b/src/redux/Provider.tsx
new file mode 100644
index 0000000..a7e7c20
--- /dev/null
+++ b/src/redux/Provider.tsx
@@ -0,0 +1,15 @@
+'use client';
+import { Provider } from 'react-redux';
+import { PersistGate } from 'redux-persist/integration/react';
+
+import { persistor, store } from './store';
+
+const ReduxProvider = ({ children }: { children: React.ReactNode }) => {
+ return (
+
+ {children}
+
+ );
+};
+
+export default ReduxProvider;
diff --git a/src/redux/completedCourse/action.ts b/src/redux/completedCourse/action.ts
new file mode 100644
index 0000000..681b6aa
--- /dev/null
+++ b/src/redux/completedCourse/action.ts
@@ -0,0 +1,98 @@
+import { marketBackendUrl } from '@root/config';
+import axios from 'axios';
+import { Dispatch } from 'react';
+import { toast } from 'react-toastify';
+
+import {
+ FEEDBACK_COURSE_REQUEST,
+ FEEDBACK_COURSE_SUCCESS,
+ FEEDBACK_FAILURE,
+ GET_COMPLETED_COURSE_REQUEST,
+ GET_COMPLETED_COURSE_SUCCESS,
+ GET_COMPLETED_FAILURE,
+} from '@/redux/completedCourse/type';
+
+export type CompletedCourseType = {
+ id: string;
+ courseInfoId: number;
+ becknTransactionId: string;
+ consumerId: string;
+ feedback: string;
+ purchasedAt: string;
+ rating: string;
+ status: string;
+ becknMessageId: string;
+ completedAt: string;
+ CourseInfo: {
+ title: string;
+ description: string;
+ credits: number;
+ imageLink: string;
+ language: string[];
+ courseLink: string;
+ providerName: string;
+ author: string;
+ avgRating: number;
+ bppId: string;
+ bppUri: string;
+ providerId: string;
+ competency: {
+ [key: string]: string[];
+ };
+ courseId: string;
+ numberOfPurchases: number;
+ };
+};
+
+type CompletedCourseActionTypes = {
+ type: string;
+ payload?: CompletedCourseType[];
+};
+
+type FeedbackActionTypes = {
+ type: string;
+};
+
+type FeedbackPayloadType = {
+ rating: number;
+ courseInfoId: number;
+};
+
+export const giveFeedbackRating =
+ (userId: string, payload: FeedbackPayloadType) =>
+ (dispatch: Dispatch) => {
+ dispatch({ type: FEEDBACK_COURSE_REQUEST });
+ return axios
+ .patch(
+ `${marketBackendUrl}/api/consumer/${userId}/course/feedback`,
+ payload
+ )
+ .then((res) => {
+ toast.success(res.data.message, {
+ draggable: false,
+ });
+ dispatch({
+ type: FEEDBACK_COURSE_SUCCESS,
+ });
+ })
+ .catch(() => {
+ toast.error('something went wrong', {
+ draggable: false,
+ });
+ dispatch({ type: FEEDBACK_FAILURE });
+ });
+ };
+
+export const getCompletedCourse =
+ (userId: string) => (dispatch: Dispatch) => {
+ dispatch({ type: GET_COMPLETED_COURSE_REQUEST });
+ return axios
+ .get(`${marketBackendUrl}/api/consumer/${userId}/course/purchases`)
+ .then((res) => {
+ dispatch({
+ type: GET_COMPLETED_COURSE_SUCCESS,
+ payload: res?.data?.data.consumerCourses,
+ });
+ })
+ .catch(() => dispatch({ type: GET_COMPLETED_FAILURE }));
+ };
diff --git a/src/redux/completedCourse/completedCourseReducer.ts b/src/redux/completedCourse/completedCourseReducer.ts
new file mode 100644
index 0000000..c5c8034
--- /dev/null
+++ b/src/redux/completedCourse/completedCourseReducer.ts
@@ -0,0 +1,48 @@
+import { CompletedCourseType } from '@/redux/completedCourse/action';
+import {
+ GET_COMPLETED_COURSE_REQUEST,
+ GET_COMPLETED_COURSE_SUCCESS,
+ GET_COMPLETED_FAILURE,
+} from '@/redux/completedCourse/type';
+
+const init = {
+ courses: [],
+ isLoading: false,
+ isError: false,
+};
+
+type actionType = {
+ type: string;
+ payload: CompletedCourseType[];
+};
+
+export const completedCourseReducer = (
+ state = init,
+ { type, payload }: actionType
+) => {
+ switch (type) {
+ case GET_COMPLETED_COURSE_REQUEST: {
+ return {
+ ...state,
+ isLoading: true,
+ };
+ }
+ case GET_COMPLETED_COURSE_SUCCESS: {
+ return {
+ ...state,
+ isLoading: false,
+ courses: payload,
+ };
+ }
+ case GET_COMPLETED_FAILURE: {
+ return {
+ ...state,
+ isLoading: false,
+ isError: true,
+ };
+ }
+ default: {
+ return state;
+ }
+ }
+};
diff --git a/src/redux/completedCourse/type.ts b/src/redux/completedCourse/type.ts
new file mode 100644
index 0000000..90207a9
--- /dev/null
+++ b/src/redux/completedCourse/type.ts
@@ -0,0 +1,7 @@
+export const GET_COMPLETED_COURSE_REQUEST = 'getCompletedCourseRequest';
+export const GET_COMPLETED_COURSE_SUCCESS = 'getCompletedCourseSuccess';
+export const GET_COMPLETED_FAILURE = ' getCompletedCourseFailure';
+
+export const FEEDBACK_COURSE_REQUEST = 'feedbackCourseRequest';
+export const FEEDBACK_COURSE_SUCCESS = 'feedbackCourseSuccess';
+export const FEEDBACK_FAILURE = ' feedbackCourseFailure';
diff --git a/src/redux/coursesDescription/action.ts b/src/redux/coursesDescription/action.ts
new file mode 100644
index 0000000..75803bc
--- /dev/null
+++ b/src/redux/coursesDescription/action.ts
@@ -0,0 +1,208 @@
+import { marketBackendUrl } from '@root/config';
+import axios from 'axios';
+import { Dispatch } from 'react';
+import { toast } from 'react-toastify';
+
+import {
+ GET_SAVE_COURSE_AND_STATUS_FAILURE,
+ GET_SAVE_COURSE_AND_STATUS_REQUEST,
+ GET_SAVE_COURSE_AND_STATUS_SUCCESS,
+ PURCHASE_COURSE_FAILURE,
+ PURCHASE_COURSE_REQUEST,
+ PURCHASE_COURSE_SUCCESS,
+ SAVE_COURSE_FAILURE,
+ SAVE_COURSE_REQUEST,
+ SAVE_COURSE_SUCCESS,
+ UNSAVE_COURSE_FAILURE,
+ UNSAVE_COURSE_REQUEST,
+ UNSAVE_COURSE_SUCCESS,
+} from './type';
+import { CourseType } from '../marketplace/marketplaceReducer';
+
+export type requestCourseType = {
+ courseId: string;
+ bppId?: string;
+ title: string;
+ description: string;
+ credits: number;
+ imageLink: string;
+ language: string[];
+ courseLink: string;
+ providerName: string;
+ providerId?: string;
+ avgRating: number;
+ author: string;
+ competency: {
+ [key: string]: string[];
+ };
+};
+
+type CourseDescriptionActionTypes = {
+ type: string;
+ payload?: {
+ saveCourseStatus?: boolean;
+ purchaseCourseStatus?: boolean;
+ courseLink?: string;
+ };
+};
+
+type SingleCourseActionTypes = {
+ type: string;
+ payload?: {
+ saveCourseStatus: boolean;
+ purchaseCourseStatus: boolean;
+ courseLink: string;
+ singleCourse: CourseType;
+ };
+};
+
+export const removeCourse =
+ (userId: string, courseId: string) =>
+ (dispatch: Dispatch) => {
+ dispatch({ type: UNSAVE_COURSE_REQUEST });
+ return axios
+ .patch(`${marketBackendUrl}/api/consumer/${userId}/course/unsave`, {
+ courseId: courseId,
+ })
+ .then(() =>
+ dispatch({
+ type: UNSAVE_COURSE_SUCCESS,
+ payload: { saveCourseStatus: false },
+ })
+ )
+ .catch(() => {
+ dispatch({ type: UNSAVE_COURSE_FAILURE });
+ toast.error('something went wrong', {
+ draggable: false,
+ });
+ });
+ };
+
+export const saveACourse =
+ (userId: string, payload: requestCourseType) =>
+ (dispatch: Dispatch) => {
+ dispatch({ type: SAVE_COURSE_REQUEST });
+ return axios
+ .post(`${marketBackendUrl}/api/consumer/${userId}/course/save`, {
+ ...payload,
+ providerId: userId,
+ })
+ .then(() =>
+ dispatch({
+ type: SAVE_COURSE_SUCCESS,
+ payload: { saveCourseStatus: true },
+ })
+ )
+ .catch(() => {
+ dispatch({ type: SAVE_COURSE_FAILURE });
+ toast.error('something went wrong', {
+ draggable: false,
+ });
+ });
+ };
+
+// to check course is purchased or not
+const handlePurchaseCourseStatus = async (userId: string, courseId: string) => {
+ const res = await axios.post(
+ `${marketBackendUrl}/api/consumer/${userId}/course/purchase/status`,
+ { courseId: courseId }
+ );
+ return res.data.data;
+};
+
+export const getPurchaseCourseStatus = (userId: string, courseId: string) => {
+ return async (dispatch: Dispatch) => {
+ dispatch({ type: PURCHASE_COURSE_REQUEST });
+ try {
+ const status = await handlePurchaseCourseStatus(userId, courseId);
+
+ return dispatch({
+ type: PURCHASE_COURSE_SUCCESS,
+ payload: {
+ purchaseCourseStatus: status?.purchased,
+ courseLink: status?.courseLink,
+ },
+ });
+ } catch (error) {
+ if (axios.isAxiosError(error)) {
+ dispatch({ type: PURCHASE_COURSE_FAILURE });
+ toast.error(error?.response?.data?.message, {
+ draggable: false,
+ });
+ }
+ }
+ };
+};
+
+export const purchasesACourse = (
+ userId: string,
+ payload: requestCourseType
+) => {
+ return async (dispatch: Dispatch) => {
+ dispatch({ type: PURCHASE_COURSE_REQUEST });
+ try {
+ await axios.post(
+ `${marketBackendUrl}/api/consumer/${userId}/course/purchase`,
+ payload
+ );
+ const status = await handlePurchaseCourseStatus(
+ userId,
+ payload?.courseId
+ );
+ return dispatch({
+ type: PURCHASE_COURSE_SUCCESS,
+ payload: {
+ purchaseCourseStatus: status.purchased,
+ courseLink: status.courseLink,
+ },
+ });
+ } catch (error: unknown) {
+ if (axios.isAxiosError(error)) {
+ dispatch({ type: PURCHASE_COURSE_FAILURE });
+ toast.error(error?.response?.data?.message, {
+ draggable: false,
+ });
+ }
+ }
+ };
+};
+
+// to check course is save or not
+const getSaveCourseStatus = async (userId: string, courseId: string) => {
+ const res = await axios.post(
+ `${marketBackendUrl}/api/consumer/${userId}/course/save/status`,
+ { courseId: courseId }
+ );
+ return res.data.saved;
+};
+
+export const getSaveCourseAndStatus = (
+ userId: string,
+ courseId: string,
+ singleCourse: CourseType
+) => {
+ return async (dispatch: Dispatch) => {
+ dispatch({ type: GET_SAVE_COURSE_AND_STATUS_REQUEST });
+
+ try {
+ const [savedCourseStatus, purchaseCourseStatus] = await Promise.all([
+ getSaveCourseStatus(userId, courseId),
+ handlePurchaseCourseStatus(userId, courseId),
+ ]);
+ return dispatch({
+ type: GET_SAVE_COURSE_AND_STATUS_SUCCESS,
+ payload: {
+ saveCourseStatus: savedCourseStatus,
+ purchaseCourseStatus: purchaseCourseStatus?.purchased,
+ courseLink: purchaseCourseStatus?.courseLink,
+ singleCourse: singleCourse,
+ },
+ });
+ } catch (error) {
+ setTimeout(() => {
+ dispatch({ type: GET_SAVE_COURSE_AND_STATUS_FAILURE });
+ window.location.href = '/error/DataNotFound';
+ }, 5000);
+ }
+ };
+};
diff --git a/src/redux/coursesDescription/courseDescriptionReducer.ts b/src/redux/coursesDescription/courseDescriptionReducer.ts
new file mode 100644
index 0000000..0be35a0
--- /dev/null
+++ b/src/redux/coursesDescription/courseDescriptionReducer.ts
@@ -0,0 +1,129 @@
+import {
+ GET_SAVE_COURSE_AND_STATUS_FAILURE,
+ GET_SAVE_COURSE_AND_STATUS_REQUEST,
+ GET_SAVE_COURSE_AND_STATUS_SUCCESS,
+ PURCHASE_COURSE_FAILURE,
+ PURCHASE_COURSE_REQUEST,
+ PURCHASE_COURSE_SUCCESS,
+ SAVE_COURSE_FAILURE,
+ SAVE_COURSE_REQUEST,
+ SAVE_COURSE_SUCCESS,
+ UNSAVE_COURSE_FAILURE,
+ UNSAVE_COURSE_REQUEST,
+ UNSAVE_COURSE_SUCCESS,
+} from './type';
+import { CourseType } from '../marketplace/marketplaceReducer';
+
+const init = {
+ saveCourseStatus: null,
+ purchaseCourseStatus: null,
+ singleCourse: null,
+ courseLink: null,
+ isLoading: false,
+ isError: false,
+};
+
+type actionType = {
+ type: string;
+ payload: {
+ saveCourseStatus?: boolean;
+ purchaseCourseStatus?: boolean;
+ courseLink?: string;
+ singleCourse?: CourseType;
+ };
+};
+
+export const courseDescriptionReducer = (
+ state = init,
+ { type, payload }: actionType
+) => {
+ switch (type) {
+ case GET_SAVE_COURSE_AND_STATUS_REQUEST: {
+ return {
+ ...state,
+ isLoading: true,
+ };
+ }
+ case GET_SAVE_COURSE_AND_STATUS_SUCCESS: {
+ return {
+ ...state,
+ isLoading: false,
+ saveCourseStatus: payload?.saveCourseStatus,
+ purchaseCourseStatus: payload?.purchaseCourseStatus,
+ singleCourse: payload?.singleCourse,
+ courseLink: payload.courseLink,
+ };
+ }
+ case GET_SAVE_COURSE_AND_STATUS_FAILURE: {
+ return {
+ ...state,
+ isLoading: false,
+ isError: true,
+ };
+ }
+ case SAVE_COURSE_REQUEST: {
+ return {
+ ...state,
+ isLoading: true,
+ };
+ }
+ case SAVE_COURSE_SUCCESS: {
+ return {
+ ...state,
+ isLoading: false,
+ saveCourseStatus: payload?.saveCourseStatus,
+ };
+ }
+ case SAVE_COURSE_FAILURE: {
+ return {
+ ...state,
+ isLoading: false,
+ isError: true,
+ };
+ }
+ case UNSAVE_COURSE_REQUEST: {
+ return {
+ ...state,
+ isLoading: true,
+ };
+ }
+ case UNSAVE_COURSE_SUCCESS: {
+ return {
+ ...state,
+ isLoading: false,
+ saveCourseStatus: payload?.saveCourseStatus,
+ };
+ }
+ case UNSAVE_COURSE_FAILURE: {
+ return {
+ ...state,
+ isLoading: false,
+ isError: true,
+ };
+ }
+ case PURCHASE_COURSE_REQUEST: {
+ return {
+ ...state,
+ isLoading: true,
+ };
+ }
+ case PURCHASE_COURSE_SUCCESS: {
+ return {
+ ...state,
+ isLoading: false,
+ purchaseCourseStatus: payload?.purchaseCourseStatus,
+ courseLink: payload?.courseLink,
+ };
+ }
+ case PURCHASE_COURSE_FAILURE: {
+ return {
+ ...state,
+ isLoading: false,
+ isError: true,
+ };
+ }
+ default: {
+ return state;
+ }
+ }
+};
diff --git a/src/redux/coursesDescription/type.ts b/src/redux/coursesDescription/type.ts
new file mode 100644
index 0000000..44c7bf1
--- /dev/null
+++ b/src/redux/coursesDescription/type.ts
@@ -0,0 +1,15 @@
+export const SAVE_COURSE_REQUEST = 'addCourseRequest';
+export const SAVE_COURSE_SUCCESS = 'addCourseSuccess';
+export const SAVE_COURSE_FAILURE = 'addCourseFailure';
+
+export const UNSAVE_COURSE_REQUEST = 'unSaveCourseRequest';
+export const UNSAVE_COURSE_SUCCESS = 'unSaveCourseSuccess';
+export const UNSAVE_COURSE_FAILURE = 'unSaveCourseFailure';
+
+export const PURCHASE_COURSE_REQUEST = 'purchaseCourseRequest';
+export const PURCHASE_COURSE_SUCCESS = 'purchaseCourseSuccess';
+export const PURCHASE_COURSE_FAILURE = 'purchaseCourseFailure';
+
+export const GET_SAVE_COURSE_AND_STATUS_REQUEST = 'getSaveCourseStatusRequest';
+export const GET_SAVE_COURSE_AND_STATUS_SUCCESS = 'getSaveCourseStatusSuccess';
+export const GET_SAVE_COURSE_AND_STATUS_FAILURE = 'getSaveCourseStatusFailure';
diff --git a/src/redux/marketplace/action.ts b/src/redux/marketplace/action.ts
new file mode 100644
index 0000000..f7755e9
--- /dev/null
+++ b/src/redux/marketplace/action.ts
@@ -0,0 +1,81 @@
+import { marketBackendUrl } from '@root/config';
+import axios from 'axios';
+import { Dispatch } from 'react';
+
+import { CourseType } from './marketplaceReducer';
+import {
+ MARKETPLACE_FAILURE,
+ MARKETPLACE_REQUEST,
+ MARKETPLACE_SUCCESS,
+} from './type';
+
+type marketplaceActionTypes = {
+ type: string;
+ payload?: {
+ mostPopularCourses: CourseType[];
+ recommendedCourses: CourseType[];
+ savedCourses: CourseType[];
+ ongoingCourses: CourseType[];
+ };
+};
+
+export const getSavedCourse = async (userId: string) => {
+ const data = await axios.get(
+ `${marketBackendUrl}/api/consumer/${userId}/course/saved`
+ );
+ return data.data.data.consumerCourses;
+};
+export const getOngoingCourses = async (userId: string) => {
+ const data = await axios.get(
+ `${marketBackendUrl}/api/consumer/${userId}/course/ongoing`
+ );
+ return data.data.data.consumerCourses;
+};
+export const getMostPopularCourses = async () => {
+ // api is not correct for now
+ const data = await axios.get(
+ `${marketBackendUrl}/api/consumer/course/popular?limit=10&offset=0`
+ );
+ return data.data.data.response;
+};
+export const getRecommendedCourses = async (userId: string) => {
+ // api is not correct for now
+ const data = await axios.get(
+ `${marketBackendUrl}/api/consumer/${userId}/course/recommended`
+ );
+ return data.data.data.response;
+};
+
+export const getMarketplaceCourses = (userId: string) => {
+ return async (dispatch: Dispatch) => {
+ dispatch({ type: MARKETPLACE_REQUEST });
+
+ try {
+ const [
+ recommendedCoursesResponse,
+ mostPopularCoursesResponse,
+ ongoingCoursesResponse,
+ savedCourseResponse,
+ ] = await Promise.all([
+ getRecommendedCourses(userId),
+ getMostPopularCourses(),
+ getOngoingCourses(userId),
+ getSavedCourse(userId),
+ ]);
+ return dispatch({
+ type: MARKETPLACE_SUCCESS,
+ payload: {
+ mostPopularCourses: mostPopularCoursesResponse,
+ recommendedCourses: recommendedCoursesResponse,
+ savedCourses: savedCourseResponse,
+ ongoingCourses: ongoingCoursesResponse,
+ },
+ });
+ } catch (error) {
+ setTimeout(() => {
+ dispatch({ type: MARKETPLACE_FAILURE });
+ window.location.href = '/error/DataNotFound';
+ }, 5000);
+ }
+ };
+};
diff --git a/src/redux/marketplace/marketplaceReducer.ts b/src/redux/marketplace/marketplaceReducer.ts
new file mode 100644
index 0000000..514bc7f
--- /dev/null
+++ b/src/redux/marketplace/marketplaceReducer.ts
@@ -0,0 +1,91 @@
+import {
+ MARKETPLACE_FAILURE,
+ MARKETPLACE_REQUEST,
+ MARKETPLACE_SUCCESS,
+} from './type';
+
+export type LevelsType = {
+ id: number | string;
+ levelNumber: number | string;
+ name: string;
+};
+
+export type CompetencyType = {
+ id: number | string;
+ name: string;
+ levels: LevelsType[];
+};
+
+export type CourseType = {
+ courseId: string;
+ title: string;
+ competency: CompetencyType[];
+ created_by: string;
+ lastUpdatedOn: string;
+ avgRating: number;
+ credits: number;
+ language: string[];
+ imageLink: string;
+ imgLink: string;
+ description: string;
+ author?: string;
+ courseLink?: string;
+ bppId?: string;
+ bppUri?: string;
+ providerId?: string;
+ providerName?: string;
+ numberOfPurchases: number;
+};
+
+const init = {
+ mostPopularCourses: null,
+ recommendedCourses: null,
+ savedCourses: null,
+ ongoingCourses: null,
+ isLoading: false,
+ isError: false,
+};
+
+type actionType = {
+ type: string;
+ payload: {
+ mostPopularCourses: CourseType[];
+ recommendedCourses: CourseType[];
+ savedCourses: CourseType[];
+ ongoingCourses: CourseType[];
+ };
+};
+
+export const marketplaceReducer = (
+ state = init,
+ { type, payload }: actionType
+) => {
+ switch (type) {
+ case MARKETPLACE_REQUEST: {
+ return {
+ ...state,
+ isLoading: true,
+ };
+ }
+ case MARKETPLACE_SUCCESS: {
+ return {
+ ...state,
+ isLoading: false,
+ mostPopularCourses: payload.mostPopularCourses,
+ recommendedCourses: payload.recommendedCourses,
+ savedCourses: payload.savedCourses,
+ ongoingCourses: payload.ongoingCourses,
+ };
+ }
+ case MARKETPLACE_FAILURE: {
+ return {
+ ...state,
+ isLoading: false,
+ isError: true,
+ };
+ }
+ default: {
+ return state;
+ }
+ }
+};
diff --git a/src/redux/marketplace/type.ts b/src/redux/marketplace/type.ts
new file mode 100644
index 0000000..fa90fcd
--- /dev/null
+++ b/src/redux/marketplace/type.ts
@@ -0,0 +1,3 @@
+export const MARKETPLACE_REQUEST = 'marketplaceRequest';
+export const MARKETPLACE_SUCCESS = 'marketplaceSuccess';
+export const MARKETPLACE_FAILURE = 'marketplaceFailure';
diff --git a/src/redux/notification/action.ts b/src/redux/notification/action.ts
new file mode 100644
index 0000000..b6e9c4f
--- /dev/null
+++ b/src/redux/notification/action.ts
@@ -0,0 +1,30 @@
+import { marketBackendUrl } from '@root/config';
+import axios from 'axios';
+import { Dispatch } from 'react';
+
+import { NotificationType } from '@/app/notifications/page';
+
+import {
+ GET_NOTIFICATION_FAILURE,
+ GET_NOTIFICATION_REQUEST,
+ GET_NOTIFICATION_SUCCESS,
+} from './type';
+
+type NotificationActionTypes = {
+ type: string;
+ payload?: NotificationType[];
+};
+
+export const getAllNotifications =
+ (userId: string) => (dispatch: Dispatch) => {
+ dispatch({ type: GET_NOTIFICATION_REQUEST });
+ axios
+ .get(`${marketBackendUrl}/api/consumer/${userId}/notifications`)
+ .then((res) =>
+ dispatch({
+ type: GET_NOTIFICATION_SUCCESS,
+ payload: res.data.data.notifications,
+ })
+ )
+ .catch(() => dispatch({ type: GET_NOTIFICATION_FAILURE }));
+ };
diff --git a/src/redux/notification/notificationReducer.ts b/src/redux/notification/notificationReducer.ts
new file mode 100644
index 0000000..2f43002
--- /dev/null
+++ b/src/redux/notification/notificationReducer.ts
@@ -0,0 +1,44 @@
+import { NotificationType } from '@/app/notifications/page';
+
+import {
+ GET_NOTIFICATION_FAILURE,
+ GET_NOTIFICATION_REQUEST,
+ GET_NOTIFICATION_SUCCESS,
+} from './type';
+
+const init = {
+ notificationData: [],
+ isLoading: false,
+ isError: false,
+};
+
+export const notificationReducer = (
+ state = init,
+ { type, payload }: { type: string; payload: NotificationType[] }
+) => {
+ switch (type) {
+ case GET_NOTIFICATION_REQUEST: {
+ return {
+ ...state,
+ isLoading: true,
+ };
+ }
+ case GET_NOTIFICATION_SUCCESS: {
+ return {
+ ...state,
+ isLoading: false,
+ notificationData: payload,
+ };
+ }
+ case GET_NOTIFICATION_FAILURE: {
+ return {
+ ...state,
+ isLoading: false,
+ isError: true,
+ };
+ }
+ default: {
+ return state;
+ }
+ }
+};
diff --git a/src/redux/notification/type.ts b/src/redux/notification/type.ts
new file mode 100644
index 0000000..83460ad
--- /dev/null
+++ b/src/redux/notification/type.ts
@@ -0,0 +1,3 @@
+export const GET_NOTIFICATION_REQUEST = 'getNotificationRequest';
+export const GET_NOTIFICATION_SUCCESS = 'getNotificationSuccess';
+export const GET_NOTIFICATION_FAILURE = 'getNotificationFailure';
diff --git a/src/redux/purchaseHistory/action.ts b/src/redux/purchaseHistory/action.ts
new file mode 100644
index 0000000..d8bf47d
--- /dev/null
+++ b/src/redux/purchaseHistory/action.ts
@@ -0,0 +1,66 @@
+import { marketBackendUrl } from '@root/config';
+import axios from 'axios';
+import { Dispatch } from 'react';
+
+import { ConsumerCourse, TransectionType } from '@/app/purchase-history/page';
+
+import {
+ GET_PURCHASE_HISTORY_FAILURE,
+ GET_PURCHASE_HISTORY_REQUEST,
+ GET_PURCHASE_HISTORY_SUCCESS,
+} from './type';
+
+type PurchaseActionTypes = {
+ type: string;
+ payload?: {
+ walletBalance: number | null;
+ purchaseHistory: TransectionType[];
+ };
+};
+
+export const fetchCredits = async (userId: string) => {
+ const data = await axios.get(
+ `${marketBackendUrl}/api/consumer/${userId}/wallet/credits`
+ );
+ return data.data.data.credits;
+};
+
+export const fetchPurchaseHistory = async (userId: string) => {
+ const data = await axios.get(
+ `${marketBackendUrl}/api/consumer/${userId}/course/purchases`
+ );
+ return data.data.data.consumerCourses;
+};
+
+export const getPurchaseHistory = (userId: string) => {
+ return async (dispatch: Dispatch) => {
+ dispatch({ type: GET_PURCHASE_HISTORY_REQUEST });
+
+ try {
+ const [creditsResponse, purchaseHistoryResponse] = await Promise.all([
+ fetchCredits(userId),
+ fetchPurchaseHistory(userId),
+ ]);
+ const transactionObjArray = purchaseHistoryResponse?.map(
+ (item: ConsumerCourse) => {
+ return {
+ id: item?.id,
+ title: item?.CourseInfo?.title,
+ credits: item?.CourseInfo?.credits,
+ author: item?.CourseInfo?.providerName,
+ purchasedAt: item?.purchasedAt,
+ };
+ }
+ );
+ dispatch({
+ type: GET_PURCHASE_HISTORY_SUCCESS,
+ payload: {
+ walletBalance: creditsResponse,
+ purchaseHistory: transactionObjArray,
+ },
+ });
+ } catch (error) {
+ dispatch({ type: GET_PURCHASE_HISTORY_FAILURE });
+ }
+ };
+};
diff --git a/src/redux/purchaseHistory/purchaseHistoryReducer.ts b/src/redux/purchaseHistory/purchaseHistoryReducer.ts
new file mode 100644
index 0000000..cd185e7
--- /dev/null
+++ b/src/redux/purchaseHistory/purchaseHistoryReducer.ts
@@ -0,0 +1,55 @@
+import { TransectionType } from '@/app/purchase-history/page';
+
+import {
+ GET_PURCHASE_HISTORY_FAILURE,
+ GET_PURCHASE_HISTORY_REQUEST,
+ GET_PURCHASE_HISTORY_SUCCESS,
+} from './type';
+
+const init = {
+ walletBalance: null,
+ purchaseHistory: [],
+ isLoading: false,
+ isError: false,
+};
+
+type actionType = {
+ type: string;
+ payload: {
+ walletBalance: number;
+ purchaseHistory: TransectionType[];
+ };
+};
+
+export const purchaseHistoryReducer = (
+ state = init,
+ { type, payload }: actionType
+) => {
+ switch (type) {
+ case GET_PURCHASE_HISTORY_REQUEST: {
+ return {
+ ...state,
+ isLoading: true,
+ };
+ }
+ case GET_PURCHASE_HISTORY_SUCCESS: {
+ return {
+ ...state,
+ isLoading: false,
+ isError: false,
+ purchaseHistory: payload?.purchaseHistory,
+ walletBalance: payload?.walletBalance,
+ };
+ }
+ case GET_PURCHASE_HISTORY_FAILURE: {
+ return {
+ ...state,
+ isLoading: false,
+ isError: true,
+ };
+ }
+ default: {
+ return state;
+ }
+ }
+};
diff --git a/src/redux/purchaseHistory/type.ts b/src/redux/purchaseHistory/type.ts
new file mode 100644
index 0000000..10cd161
--- /dev/null
+++ b/src/redux/purchaseHistory/type.ts
@@ -0,0 +1,3 @@
+export const GET_PURCHASE_HISTORY_REQUEST = 'getPurchaseHistoryRequest';
+export const GET_PURCHASE_HISTORY_SUCCESS = 'getPurchaseHistorySuccess';
+export const GET_PURCHASE_HISTORY_FAILURE = 'getPurchaseHistoryFailure';
diff --git a/src/redux/searchCourses/action.ts b/src/redux/searchCourses/action.ts
new file mode 100644
index 0000000..b01d668
--- /dev/null
+++ b/src/redux/searchCourses/action.ts
@@ -0,0 +1,58 @@
+import { marketBackendUrl } from '@root/config';
+import axios from 'axios';
+import { Dispatch } from 'react';
+
+import {
+ SEARCH_COURSES_FAILURE,
+ SEARCH_COURSES_REQUEST,
+ SEARCH_COURSES_RESET,
+ SEARCH_COURSES_SUCCESS,
+} from './type';
+import { CourseType } from '../marketplace/marketplaceReducer';
+
+type SearchCoursesActionTypes = {
+ type: string;
+ payload?: CourseType[];
+};
+
+export const getSearchCourses =
+ (searchText: string) =>
+ async (dispatch: Dispatch) => {
+ dispatch({ type: SEARCH_COURSES_REQUEST });
+
+ // when ever user search it will first reset initial value to empty
+ dispatch({
+ type: SEARCH_COURSES_RESET,
+ });
+ try {
+ const response = await axios.get(
+ `${marketBackendUrl}/api/consumer/course/search?searchInput=${searchText}`
+ );
+ {
+ dispatch({
+ type: SEARCH_COURSES_SUCCESS,
+ payload: response?.data?.data?.searchResponse?.courses,
+ });
+ }
+ if (response?.data?.data?.searchResponse?.messageId) {
+ let counter = 0;
+ const intervalId = setInterval(async () => {
+ const res = await axios.get(
+ `${marketBackendUrl}/api/consumer/course/search/poll/${response?.data?.data?.searchResponse?.messageId}`
+ );
+ if (Array.isArray(res.data.data.courses.data)) {
+ dispatch({
+ type: SEARCH_COURSES_SUCCESS,
+ payload: res.data.data.courses.data,
+ });
+ }
+ counter++;
+ if (counter >= 5) {
+ clearInterval(intervalId); // Stop the interval after 5 iterations
+ }
+ }, 5000); // 5-second interval
+ }
+ } catch (error) {
+ dispatch({ type: SEARCH_COURSES_FAILURE });
+ }
+ };
diff --git a/src/redux/searchCourses/searchReducer.ts b/src/redux/searchCourses/searchReducer.ts
new file mode 100644
index 0000000..cb724b9
--- /dev/null
+++ b/src/redux/searchCourses/searchReducer.ts
@@ -0,0 +1,56 @@
+import {
+ SEARCH_COURSES_FAILURE,
+ SEARCH_COURSES_REQUEST,
+ SEARCH_COURSES_RESET,
+ SEARCH_COURSES_SUCCESS,
+} from './type';
+import { CourseType } from '../marketplace/marketplaceReducer';
+
+const init = {
+ searchCourses: [],
+ isLoading: false,
+ isError: false,
+};
+
+type actionType = {
+ type: string;
+ payload: CourseType[];
+};
+
+export const searchCoursesReducer = (
+ state = init,
+ { type, payload }: actionType
+) => {
+ switch (type) {
+ case SEARCH_COURSES_REQUEST: {
+ return {
+ ...state,
+ isLoading: true,
+ };
+ }
+ case SEARCH_COURSES_RESET: {
+ return {
+ ...state,
+ isLoading: false,
+ searchCourses: [],
+ };
+ }
+ case SEARCH_COURSES_SUCCESS: {
+ return {
+ ...state,
+ isLoading: false,
+ searchCourses: [...state.searchCourses, ...payload],
+ };
+ }
+ case SEARCH_COURSES_FAILURE: {
+ return {
+ ...state,
+ isLoading: false,
+ isError: true,
+ };
+ }
+ default: {
+ return state;
+ }
+ }
+};
diff --git a/src/redux/searchCourses/type.ts b/src/redux/searchCourses/type.ts
new file mode 100644
index 0000000..0dfc1b1
--- /dev/null
+++ b/src/redux/searchCourses/type.ts
@@ -0,0 +1,4 @@
+export const SEARCH_COURSES_REQUEST = 'searchCoursesRequest';
+export const SEARCH_COURSES_SUCCESS = 'searchCoursesSuccess';
+export const SEARCH_COURSES_FAILURE = 'searchCoursesFailure';
+export const SEARCH_COURSES_RESET = 'searchCoursesReset';
diff --git a/src/redux/store.ts b/src/redux/store.ts
new file mode 100644
index 0000000..01b0e43
--- /dev/null
+++ b/src/redux/store.ts
@@ -0,0 +1,68 @@
+import { TypedUseSelectorHook, useSelector } from 'react-redux';
+import {
+ AnyAction,
+ applyMiddleware,
+ combineReducers,
+ legacy_createStore,
+ Store,
+} from 'redux';
+import { persistReducer, persistStore } from 'redux-persist';
+import createWebStorage from 'redux-persist/lib/storage/createWebStorage';
+import thunk, { ThunkDispatch } from 'redux-thunk';
+
+import { completedCourseReducer } from '@/redux/completedCourse/completedCourseReducer';
+
+import { courseDescriptionReducer } from './coursesDescription/courseDescriptionReducer';
+import { marketplaceReducer } from './marketplace/marketplaceReducer';
+import { notificationReducer } from './notification/notificationReducer';
+import { purchaseHistoryReducer } from './purchaseHistory/purchaseHistoryReducer';
+import { searchCoursesReducer } from './searchCourses/searchReducer';
+import { userDetailsReducer } from './userDetails/userDetailsReducer';
+
+const createNoopStorage = () => {
+ return {
+ getItem(_key: string) {
+ return Promise.resolve(null);
+ },
+ setItem(_key: string, value: string) {
+ return Promise.resolve(value);
+ },
+ removeItem(_key: string) {
+ return Promise.resolve();
+ },
+ };
+};
+
+const storage =
+ typeof window !== 'undefined'
+ ? createWebStorage('local')
+ : createNoopStorage();
+
+const persistConfig = {
+ key: 'marketplacePersistData',
+ storage,
+};
+
+const rootReducer = combineReducers({
+ notification: notificationReducer,
+ purchaseHistory: purchaseHistoryReducer,
+ marketplace: marketplaceReducer,
+ singleCourse: courseDescriptionReducer,
+ searchCourses: searchCoursesReducer,
+ completedCourse: completedCourseReducer,
+ userDetails: userDetailsReducer,
+});
+
+const persistedReducer = persistReducer(persistConfig, rootReducer);
+
+export type RootState = ReturnType;
+export type AppDispatch = ThunkDispatch;
+
+export const useAppSelector: TypedUseSelectorHook = useSelector;
+
+export const store: Store = legacy_createStore(
+ persistedReducer,
+ applyMiddleware(thunk)
+);
+
+export const persistor = persistStore(store);
diff --git a/src/redux/userDetails/action.ts b/src/redux/userDetails/action.ts
new file mode 100644
index 0000000..c52ebc7
--- /dev/null
+++ b/src/redux/userDetails/action.ts
@@ -0,0 +1,39 @@
+import { marketBackendUrl } from '@root/config';
+import axios from 'axios';
+import { Dispatch } from 'react';
+
+import {
+ USER_DETAILS_FAILURE,
+ USER_DETAILS_REQUEST,
+ USER_DETAILS_SUCCESS,
+} from './type';
+
+export type UserDetailsType = {
+ consumerId: string;
+ name: string;
+ emailId: string;
+ phoneNumber: string;
+ createdAt: string;
+ updatedAt: string;
+ credits: 4960;
+ numberOfPurchasedCourses: 2;
+};
+
+type UserDetailsActionTypes = {
+ type: string;
+ payload?: UserDetailsType;
+};
+
+export const getUserDetails =
+ (userId: string) => (dispatch: Dispatch) => {
+ dispatch({ type: USER_DETAILS_REQUEST });
+ axios
+ .get(`${marketBackendUrl}/api/consumer/${userId}`)
+ .then((res) =>
+ dispatch({
+ type: USER_DETAILS_SUCCESS,
+ payload: res?.data?.data?.consumer,
+ })
+ )
+ .catch(() => dispatch({ type: USER_DETAILS_FAILURE }));
+ };
diff --git a/src/redux/userDetails/type.ts b/src/redux/userDetails/type.ts
new file mode 100644
index 0000000..602fda7
--- /dev/null
+++ b/src/redux/userDetails/type.ts
@@ -0,0 +1,3 @@
+export const USER_DETAILS_REQUEST = 'userDetailsRequest';
+export const USER_DETAILS_SUCCESS = 'userDetailsSuccess';
+export const USER_DETAILS_FAILURE = 'userDetailsFailure';
diff --git a/src/redux/userDetails/userDetailsReducer.ts b/src/redux/userDetails/userDetailsReducer.ts
new file mode 100644
index 0000000..0e4d171
--- /dev/null
+++ b/src/redux/userDetails/userDetailsReducer.ts
@@ -0,0 +1,48 @@
+import { UserDetailsType } from './action';
+import {
+ USER_DETAILS_FAILURE,
+ USER_DETAILS_REQUEST,
+ USER_DETAILS_SUCCESS,
+} from './type';
+
+const init = {
+ userDetails: {},
+ isLoading: false,
+ isError: false,
+};
+
+type actionType = {
+ type: string;
+ payload: UserDetailsType;
+};
+
+export const userDetailsReducer = (
+ state = init,
+ { type, payload }: actionType
+) => {
+ switch (type) {
+ case USER_DETAILS_REQUEST: {
+ return {
+ ...state,
+ isLoading: true,
+ };
+ }
+ case USER_DETAILS_SUCCESS: {
+ return {
+ ...state,
+ isLoading: false,
+ userDetails: payload,
+ };
+ }
+ case USER_DETAILS_FAILURE: {
+ return {
+ ...state,
+ isLoading: false,
+ isError: true,
+ };
+ }
+ default: {
+ return state;
+ }
+ }
+};
diff --git a/tsconfig.json b/tsconfig.json
index f3a4ec1..9028271 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -16,7 +16,8 @@
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"],
- "~/*": ["./public/*"]
+ "~/*": ["./public/*"],
+ "@root/*": ["./*"]
},
"incremental": true,
"plugins": [
@@ -25,7 +26,13 @@
}
]
},
- "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "include": [
+ "next-env.d.ts",
+ "**/*.ts",
+ "**/*.tsx",
+ ".next/types/**/*.ts",
+ "src/app/all-courses"
+ ],
"exclude": ["node_modules"],
"moduleResolution": ["node_modules", ".next", "node"]
}
diff --git a/yarn.lock b/yarn.lock
index 673b38f..ea90eb2 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -155,7 +155,7 @@
dependencies:
"@babel/types" "^7.22.5"
-"@babel/helper-module-imports@^7.22.5":
+"@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.22.5":
"integrity" "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg=="
"resolved" "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz"
"version" "7.22.5"
@@ -1019,12 +1019,12 @@
"core-js-pure" "^3.15.0"
"regenerator-runtime" "^0.13.4"
-"@babel/runtime@^7.10.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
- "integrity" "sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg=="
- "resolved" "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.9.tgz"
- "version" "7.17.9"
+"@babel/runtime@^7.10.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.18.3", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
+ "integrity" "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg=="
+ "resolved" "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz"
+ "version" "7.23.2"
dependencies:
- "regenerator-runtime" "^0.13.4"
+ "regenerator-runtime" "^0.14.0"
"@babel/template@^7.22.5", "@babel/template@^7.3.3":
"integrity" "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw=="
@@ -1234,6 +1234,94 @@
dependencies:
"@cspotcode/source-map-consumer" "0.8.0"
+"@emotion/babel-plugin@^11.11.0":
+ "integrity" "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ=="
+ "resolved" "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz"
+ "version" "11.11.0"
+ dependencies:
+ "@babel/helper-module-imports" "^7.16.7"
+ "@babel/runtime" "^7.18.3"
+ "@emotion/hash" "^0.9.1"
+ "@emotion/memoize" "^0.8.1"
+ "@emotion/serialize" "^1.1.2"
+ "babel-plugin-macros" "^3.1.0"
+ "convert-source-map" "^1.5.0"
+ "escape-string-regexp" "^4.0.0"
+ "find-root" "^1.1.0"
+ "source-map" "^0.5.7"
+ "stylis" "4.2.0"
+
+"@emotion/cache@^11.11.0", "@emotion/cache@^11.4.0":
+ "integrity" "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ=="
+ "resolved" "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz"
+ "version" "11.11.0"
+ dependencies:
+ "@emotion/memoize" "^0.8.1"
+ "@emotion/sheet" "^1.2.2"
+ "@emotion/utils" "^1.2.1"
+ "@emotion/weak-memoize" "^0.3.1"
+ "stylis" "4.2.0"
+
+"@emotion/hash@^0.9.1":
+ "integrity" "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ=="
+ "resolved" "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz"
+ "version" "0.9.1"
+
+"@emotion/memoize@^0.8.1":
+ "integrity" "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA=="
+ "resolved" "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz"
+ "version" "0.8.1"
+
+"@emotion/react@^11.8.1":
+ "integrity" "sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA=="
+ "resolved" "https://registry.npmjs.org/@emotion/react/-/react-11.11.1.tgz"
+ "version" "11.11.1"
+ dependencies:
+ "@babel/runtime" "^7.18.3"
+ "@emotion/babel-plugin" "^11.11.0"
+ "@emotion/cache" "^11.11.0"
+ "@emotion/serialize" "^1.1.2"
+ "@emotion/use-insertion-effect-with-fallbacks" "^1.0.1"
+ "@emotion/utils" "^1.2.1"
+ "@emotion/weak-memoize" "^0.3.1"
+ "hoist-non-react-statics" "^3.3.1"
+
+"@emotion/serialize@^1.1.2":
+ "integrity" "sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA=="
+ "resolved" "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.2.tgz"
+ "version" "1.1.2"
+ dependencies:
+ "@emotion/hash" "^0.9.1"
+ "@emotion/memoize" "^0.8.1"
+ "@emotion/unitless" "^0.8.1"
+ "@emotion/utils" "^1.2.1"
+ "csstype" "^3.0.2"
+
+"@emotion/sheet@^1.2.2":
+ "integrity" "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA=="
+ "resolved" "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz"
+ "version" "1.2.2"
+
+"@emotion/unitless@^0.8.1":
+ "integrity" "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ=="
+ "resolved" "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz"
+ "version" "0.8.1"
+
+"@emotion/use-insertion-effect-with-fallbacks@^1.0.1":
+ "integrity" "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw=="
+ "resolved" "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz"
+ "version" "1.0.1"
+
+"@emotion/utils@^1.2.1":
+ "integrity" "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg=="
+ "resolved" "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz"
+ "version" "1.2.1"
+
+"@emotion/weak-memoize@^0.3.1":
+ "integrity" "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww=="
+ "resolved" "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz"
+ "version" "0.3.1"
+
"@eslint-community/eslint-utils@^4.2.0":
"integrity" "sha512-gB8T4H4DEfX2IV9zGDJPOBgP1e/DbfCPDTtEqUMckpvzS1OYtva8JdFYBqMwYk7xAQ429WGF/UPqn8uQ//h2vQ=="
"resolved" "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.2.0.tgz"
@@ -1266,6 +1354,26 @@
"resolved" "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz"
"version" "8.44.0"
+"@floating-ui/core@^1.4.2":
+ "integrity" "sha512-kK1h4m36DQ0UHGj5Ah4db7R0rHemTqqO0QLvUqi1/mUUp3LuAWbWxdxSIf/XsnH9VS6rRVPLJCncjRzUvyCLXg=="
+ "resolved" "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.0.tgz"
+ "version" "1.5.0"
+ dependencies:
+ "@floating-ui/utils" "^0.1.3"
+
+"@floating-ui/dom@^1.0.1":
+ "integrity" "sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA=="
+ "resolved" "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.3.tgz"
+ "version" "1.5.3"
+ dependencies:
+ "@floating-ui/core" "^1.4.2"
+ "@floating-ui/utils" "^0.1.3"
+
+"@floating-ui/utils@^0.1.3":
+ "integrity" "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A=="
+ "resolved" "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.6.tgz"
+ "version" "0.1.6"
+
"@humanwhocodes/config-array@^0.11.10":
"integrity" "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ=="
"resolved" "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz"
@@ -1557,6 +1665,16 @@
"tiny-glob" "^0.2.9"
"tslib" "^2.4.0"
+"@reduxjs/toolkit@^1.9.7":
+ "integrity" "sha512-t7v8ZPxhhKgOKtU+uyJT13lu4vL7az5aFi4IdoDs/eS548edn2M8Ik9h8fxgvMjGoAUVFSt6ZC1P5cWmQ014QQ=="
+ "resolved" "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.7.tgz"
+ "version" "1.9.7"
+ dependencies:
+ "immer" "^9.0.21"
+ "redux" "^4.2.1"
+ "redux-thunk" "^2.4.2"
+ "reselect" "^4.1.8"
+
"@rushstack/eslint-patch@^1.1.3":
"integrity" "sha512-WiBSI6JBIhC6LRIsB2Kwh8DsGTlbBU+mLRxJmAe3LjHTdkDpwIbEOZgoXBbZilk/vlfjK8i6nKRAvIRn1XaIMw=="
"resolved" "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.1.3.tgz"
@@ -1809,6 +1927,14 @@
dependencies:
"@types/node" "*"
+"@types/hoist-non-react-statics@^3.3.1":
+ "integrity" "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg=="
+ "resolved" "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz"
+ "version" "3.3.5"
+ dependencies:
+ "@types/react" "*"
+ "hoist-non-react-statics" "^3.3.0"
+
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1":
"integrity" "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw=="
"resolved" "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz"
@@ -1876,14 +2002,35 @@
"resolved" "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz"
"version" "15.7.4"
-"@types/react-dom@^18.0.0":
+"@types/react-detect-offline@^2.4.4":
+ "integrity" "sha512-iUqEWeiN63YfNMho8SdltKR0q7WbFXJM5cQXWLKo9rluuYn7kcyzUz2lJnG57MSmESHPC1esRICt2Ve1TlVDAA=="
+ "resolved" "https://registry.npmjs.org/@types/react-detect-offline/-/react-detect-offline-2.4.4.tgz"
+ "version" "2.4.4"
+ dependencies:
+ "@types/react" "*"
+
+"@types/react-dom@^16.8 || ^17.0 || ^18.0", "@types/react-dom@^18.0.0":
"integrity" "sha512-UxeS+Wtj5bvLRREz9tIgsK4ntCuLDo0EcAcACgw3E+9wE8ePDr9uQpq53MfcyxyIS55xJ+0B6mDS8c4qkkHLBg=="
"resolved" "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.2.tgz"
"version" "18.0.2"
dependencies:
"@types/react" "*"
-"@types/react@*", "@types/react@^18.2.15":
+"@types/react-slick@^0.23.11":
+ "integrity" "sha512-52AbNhYN7u0ATqAUvap2MXNG3znUsJslAWgebUHrovvnJ6EG6oic+TZyC82XiwwvijRMyL2V0C2wPJbzsSwllw=="
+ "resolved" "https://registry.npmjs.org/@types/react-slick/-/react-slick-0.23.11.tgz"
+ "version" "0.23.11"
+ dependencies:
+ "@types/react" "*"
+
+"@types/react-transition-group@^4.4.0":
+ "integrity" "sha512-QmQ22q+Pb+HQSn04NL3HtrqHwYMf4h3QKArOy5F8U5nEVMaihBs3SR10WiOM1iwPz5jIo8x/u11al+iEGZZrvg=="
+ "resolved" "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.8.tgz"
+ "version" "4.4.8"
+ dependencies:
+ "@types/react" "*"
+
+"@types/react@*", "@types/react@^16.8 || ^17.0 || ^18.0", "@types/react@^18.2.15":
"integrity" "sha512-oEjE7TQt1fFTFSbf8kkNuc798ahTUzn3Le67/PWjE8MAfYAD/qB7O8hSTcromLFqHCt9bcdOg5GXMokzTjJ5SA=="
"resolved" "https://registry.npmjs.org/@types/react/-/react-18.2.15.tgz"
"version" "18.2.15"
@@ -1907,6 +2054,13 @@
"resolved" "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz"
"version" "2.0.1"
+"@types/swiper@^6.0.0":
+ "integrity" "sha512-QPZRgxZ+ivXXtzV43B3LxpXUIC7FE/EoKM+rtxngmgt2M7eeUYypZhyqZD8UxJtlBcUDw/ATGoVeSNpvBBrz2w=="
+ "resolved" "https://registry.npmjs.org/@types/swiper/-/swiper-6.0.0.tgz"
+ "version" "6.0.0"
+ dependencies:
+ "swiper" "*"
+
"@types/testing-library__jest-dom@^5.9.1":
"integrity" "sha512-Gk9vaXfbzc5zCXI9eYE9BI5BNHEp4D3FWjgqBE/ePGYElLAP+KvxBcsdkwfIVvezs605oiyd/VrpiHe3Oeg+Aw=="
"resolved" "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.1.tgz"
@@ -1914,6 +2068,11 @@
dependencies:
"@types/jest" "*"
+"@types/use-sync-external-store@^0.0.3":
+ "integrity" "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA=="
+ "resolved" "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz"
+ "version" "0.0.3"
+
"@types/yargs-parser@*":
"integrity" "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw=="
"resolved" "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz"
@@ -2240,6 +2399,15 @@
"resolved" "https://registry.npmjs.org/axe-core/-/axe-core-4.4.1.tgz"
"version" "4.4.1"
+"axios@^1.6.0":
+ "integrity" "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg=="
+ "resolved" "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz"
+ "version" "1.6.0"
+ dependencies:
+ "follow-redirects" "^1.15.0"
+ "form-data" "^4.0.0"
+ "proxy-from-env" "^1.1.0"
+
"axobject-query@^2.2.0":
"integrity" "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA=="
"resolved" "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz"
@@ -2280,6 +2448,15 @@
"@types/babel__core" "^7.0.0"
"@types/babel__traverse" "^7.0.6"
+"babel-plugin-macros@^3.1.0":
+ "integrity" "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg=="
+ "resolved" "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz"
+ "version" "3.1.0"
+ dependencies:
+ "@babel/runtime" "^7.12.5"
+ "cosmiconfig" "^7.0.0"
+ "resolve" "^1.19.0"
+
"babel-plugin-polyfill-corejs2@^0.4.4":
"integrity" "sha512-9WeK9snM1BfxB38goUEv2FLnA6ja07UMfazFHzCXUb3NyDZAwfXvQiURQ6guTTMeHcOsdknULm1PDhs4uWtKyA=="
"resolved" "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.4.tgz"
@@ -2491,6 +2668,11 @@
"resolved" "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz"
"version" "1.2.2"
+"classnames@^2.2.5":
+ "integrity" "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw=="
+ "resolved" "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz"
+ "version" "2.3.2"
+
"clean-stack@^2.0.0":
"integrity" "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A=="
"resolved" "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz"
@@ -2533,7 +2715,7 @@
"strip-ansi" "^6.0.0"
"wrap-ansi" "^7.0.0"
-"clsx@^1.2.1":
+"clsx@^1.1.1", "clsx@^1.2.1":
"integrity" "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg=="
"resolved" "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz"
"version" "1.2.1"
@@ -2641,7 +2823,7 @@
"split2" "^3.0.0"
"through2" "^4.0.0"
-"convert-source-map@^1.4.0", "convert-source-map@^1.6.0", "convert-source-map@^1.7.0":
+"convert-source-map@^1.4.0", "convert-source-map@^1.5.0", "convert-source-map@^1.6.0", "convert-source-map@^1.7.0":
"integrity" "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA=="
"resolved" "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz"
"version" "1.8.0"
@@ -2916,6 +3098,14 @@
"resolved" "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.10.tgz"
"version" "0.5.10"
+"dom-helpers@^5.0.1":
+ "integrity" "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA=="
+ "resolved" "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz"
+ "version" "5.2.1"
+ dependencies:
+ "@babel/runtime" "^7.8.7"
+ "csstype" "^3.0.2"
+
"dom-serializer@^2.0.0":
"integrity" "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="
"resolved" "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz"
@@ -3001,6 +3191,11 @@
"graceful-fs" "^4.2.4"
"tapable" "^2.2.0"
+"enquire.js@^2.1.6":
+ "integrity" "sha512-/KujNpO+PT63F7Hlpu4h3pE3TokKRHN26JYmQpPyjkRD/N57R7bPDNojMXdi7uveAKjYB7yQnartCxZnFWr0Xw=="
+ "resolved" "https://registry.npmjs.org/enquire.js/-/enquire.js-2.1.6.tgz"
+ "version" "2.1.6"
+
"entities@^4.2.0", "entities@^4.4.0":
"integrity" "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="
"resolved" "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz"
@@ -3410,6 +3605,11 @@
dependencies:
"to-regex-range" "^5.0.1"
+"find-root@^1.1.0":
+ "integrity" "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
+ "resolved" "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz"
+ "version" "1.1.0"
+
"find-up@^2.1.0":
"integrity" "sha1-RdG35QbHF93UgndaK3eSCjwMV6c= sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ=="
"resolved" "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz"
@@ -3454,6 +3654,11 @@
"resolved" "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz"
"version" "3.1.1"
+"follow-redirects@^1.15.0":
+ "integrity" "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q=="
+ "resolved" "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz"
+ "version" "1.15.3"
+
"form-data@^3.0.0":
"integrity" "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg=="
"resolved" "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz"
@@ -3463,6 +3668,15 @@
"combined-stream" "^1.0.8"
"mime-types" "^2.1.12"
+"form-data@^4.0.0":
+ "integrity" "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww=="
+ "resolved" "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz"
+ "version" "4.0.0"
+ dependencies:
+ "asynckit" "^0.4.0"
+ "combined-stream" "^1.0.8"
+ "mime-types" "^2.1.12"
+
"fraction.js@^4.2.0":
"integrity" "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA=="
"resolved" "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz"
@@ -3713,6 +3927,13 @@
dependencies:
"function-bind" "^1.1.1"
+"hoist-non-react-statics@^3.3.0", "hoist-non-react-statics@^3.3.1", "hoist-non-react-statics@^3.3.2":
+ "integrity" "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw=="
+ "resolved" "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz"
+ "version" "3.3.2"
+ dependencies:
+ "react-is" "^16.7.0"
+
"hosted-git-info@^2.1.4":
"integrity" "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="
"resolved" "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz"
@@ -3776,6 +3997,11 @@
"resolved" "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz"
"version" "5.2.0"
+"immer@^9.0.21":
+ "integrity" "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA=="
+ "resolved" "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz"
+ "version" "9.0.21"
+
"import-fresh@^3.0.0", "import-fresh@^3.2.1":
"integrity" "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw=="
"resolved" "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz"
@@ -4532,6 +4758,13 @@
"resolved" "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz"
"version" "1.0.1"
+"json2mq@^0.2.0":
+ "integrity" "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA=="
+ "resolved" "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz"
+ "version" "0.2.0"
+ dependencies:
+ "string-convert" "^0.2.0"
+
"json5@^1.0.1":
"integrity" "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow=="
"resolved" "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz"
@@ -4790,6 +5023,11 @@
"resolved" "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz"
"version" "2.0.30"
+"memoize-one@^6.0.0":
+ "integrity" "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="
+ "resolved" "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz"
+ "version" "6.0.0"
+
"meow@^8.0.0":
"integrity" "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q=="
"resolved" "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz"
@@ -5365,7 +5603,7 @@
"kleur" "^3.0.3"
"sisteransi" "^1.0.5"
-"prop-types@^15.8.1":
+"prop-types@^15.6.0", "prop-types@^15.6.2", "prop-types@^15.8.1":
"integrity" "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="
"resolved" "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz"
"version" "15.8.1"
@@ -5374,6 +5612,11 @@
"object-assign" "^4.1.1"
"react-is" "^16.13.1"
+"proxy-from-env@^1.1.0":
+ "integrity" "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+ "resolved" "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz"
+ "version" "1.1.0"
+
"psl@^1.1.33":
"integrity" "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ=="
"resolved" "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz"
@@ -5399,7 +5642,12 @@
"resolved" "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz"
"version" "4.0.1"
-"react-dom@^18.0.0", "react-dom@^18.2.0":
+"react-detect-offline@^2.4.5":
+ "integrity" "sha512-sI13NPEKl3uQp95FT5CwrYzH3DnXCwNP6TnY6NRF5gFDM4NU9KDlbtA6HG2dwhDVS0RYQGXwZW/mHbdf8fCnaw=="
+ "resolved" "https://registry.npmjs.org/react-detect-offline/-/react-detect-offline-2.4.5.tgz"
+ "version" "2.4.5"
+
+"react-dom@^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0", "react-dom@^16.10.2 || ^17.0.0 || ^18.0.0", "react-dom@^16.8 || ^17.0 || ^18.0", "react-dom@^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom@^18.0.0", "react-dom@^18.2.0", "react-dom@>=16", "react-dom@>=16.6.0":
"integrity" "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g=="
"resolved" "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz"
"version" "18.2.0"
@@ -5417,12 +5665,82 @@
"resolved" "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
"version" "16.13.1"
+"react-is@^16.7.0":
+ "integrity" "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+ "resolved" "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
+ "version" "16.13.1"
+
"react-is@^17.0.1":
"integrity" "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
"resolved" "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz"
"version" "17.0.2"
-"react@*", "react@^16.5.1 || ^17.0.0 || ^18.0.0", "react@^18.0.0", "react@^18.2.0", "react@>= 16.8.0 || 17.x.x || ^18.0.0-0", "react@>=17.0.0":
+"react-is@^18.0.0":
+ "integrity" "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
+ "resolved" "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz"
+ "version" "18.2.0"
+
+"react-redux@^7.2.1 || ^8.0.2", "react-redux@^8.1.3":
+ "integrity" "sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw=="
+ "resolved" "https://registry.npmjs.org/react-redux/-/react-redux-8.1.3.tgz"
+ "version" "8.1.3"
+ dependencies:
+ "@babel/runtime" "^7.12.1"
+ "@types/hoist-non-react-statics" "^3.3.1"
+ "@types/use-sync-external-store" "^0.0.3"
+ "hoist-non-react-statics" "^3.3.2"
+ "react-is" "^18.0.0"
+ "use-sync-external-store" "^1.0.0"
+
+"react-select@^5.7.7":
+ "integrity" "sha512-HhashZZJDRlfF/AKj0a0Lnfs3sRdw/46VJIRd8IbB9/Ovr74+ZIwkAdSBjSPXsFMG+u72c5xShqwLSKIJllzqw=="
+ "resolved" "https://registry.npmjs.org/react-select/-/react-select-5.7.7.tgz"
+ "version" "5.7.7"
+ dependencies:
+ "@babel/runtime" "^7.12.0"
+ "@emotion/cache" "^11.4.0"
+ "@emotion/react" "^11.8.1"
+ "@floating-ui/dom" "^1.0.1"
+ "@types/react-transition-group" "^4.4.0"
+ "memoize-one" "^6.0.0"
+ "prop-types" "^15.6.0"
+ "react-transition-group" "^4.3.0"
+ "use-isomorphic-layout-effect" "^1.1.2"
+
+"react-simple-pull-to-refresh@^1.3.3":
+ "integrity" "sha512-6qXsa5RtNVmKJhLWvDLIX8UK51HFtCEGjdqQGf+M1Qjrcc4qH4fki97sgVpGEFBRwbY7DiVDA5N5p97kF16DTw=="
+ "resolved" "https://registry.npmjs.org/react-simple-pull-to-refresh/-/react-simple-pull-to-refresh-1.3.3.tgz"
+ "version" "1.3.3"
+
+"react-slick@^0.29.0":
+ "integrity" "sha512-TGdOKE+ZkJHHeC4aaoH85m8RnFyWqdqRfAGkhd6dirmATXMZWAxOpTLmw2Ll/jPTQ3eEG7ercFr/sbzdeYCJXA=="
+ "resolved" "https://registry.npmjs.org/react-slick/-/react-slick-0.29.0.tgz"
+ "version" "0.29.0"
+ dependencies:
+ "classnames" "^2.2.5"
+ "enquire.js" "^2.1.6"
+ "json2mq" "^0.2.0"
+ "lodash.debounce" "^4.0.8"
+ "resize-observer-polyfill" "^1.5.0"
+
+"react-toastify@^9.1.3":
+ "integrity" "sha512-fPfb8ghtn/XMxw3LkxQBk3IyagNpF/LIKjOBflbexr2AWxAH1MJgvnESwEwBn9liLFXgTKWgBSdZpw9m4OTHTg=="
+ "resolved" "https://registry.npmjs.org/react-toastify/-/react-toastify-9.1.3.tgz"
+ "version" "9.1.3"
+ dependencies:
+ "clsx" "^1.1.1"
+
+"react-transition-group@^4.3.0":
+ "integrity" "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g=="
+ "resolved" "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz"
+ "version" "4.4.5"
+ dependencies:
+ "@babel/runtime" "^7.5.5"
+ "dom-helpers" "^5.0.1"
+ "loose-envify" "^1.4.0"
+ "prop-types" "^15.6.2"
+
+"react@*", "react@^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0", "react@^16.10.2 || ^17.0.0 || ^18.0.0", "react@^16.5.1 || ^17.0.0 || ^18.0.0", "react@^16.8 || ^17.0 || ^18.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^16.9.0 || ^17.0.0 || ^18", "react@^18.0.0", "react@^18.2.0", "react@>= 16.8.0 || 17.x.x || ^18.0.0-0", "react@>=16", "react@>=16.6.0", "react@>=16.8.0", "react@>=17.0.0":
"integrity" "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ=="
"resolved" "https://registry.npmjs.org/react/-/react-18.2.0.tgz"
"version" "18.2.0"
@@ -5479,6 +5797,23 @@
"indent-string" "^4.0.0"
"strip-indent" "^3.0.0"
+"redux-persist@^6.0.0":
+ "integrity" "sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ=="
+ "resolved" "https://registry.npmjs.org/redux-persist/-/redux-persist-6.0.0.tgz"
+ "version" "6.0.0"
+
+"redux-thunk@^2.4.2":
+ "integrity" "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q=="
+ "resolved" "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz"
+ "version" "2.4.2"
+
+"redux@^4", "redux@^4 || ^5.0.0-beta.0", "redux@^4.2.1", "redux@>4.0.0":
+ "integrity" "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w=="
+ "resolved" "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz"
+ "version" "4.2.1"
+ dependencies:
+ "@babel/runtime" "^7.9.2"
+
"regenerate-unicode-properties@^10.1.0":
"integrity" "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ=="
"resolved" "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz"
@@ -5496,6 +5831,11 @@
"resolved" "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz"
"version" "0.13.7"
+"regenerator-runtime@^0.14.0":
+ "integrity" "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA=="
+ "resolved" "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz"
+ "version" "0.14.0"
+
"regenerator-transform@^0.15.1":
"integrity" "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg=="
"resolved" "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz"
@@ -5536,6 +5876,16 @@
"resolved" "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz"
"version" "2.1.1"
+"reselect@^4.1.8":
+ "integrity" "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ=="
+ "resolved" "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz"
+ "version" "4.1.8"
+
+"resize-observer-polyfill@^1.5.0":
+ "integrity" "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
+ "resolved" "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz"
+ "version" "1.5.1"
+
"resolve-cwd@^3.0.0":
"integrity" "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg=="
"resolved" "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz"
@@ -5565,7 +5915,7 @@
"resolved" "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz"
"version" "1.1.0"
-"resolve@^1.1.7", "resolve@^1.10.0", "resolve@^1.14.2", "resolve@^1.20.0", "resolve@^1.22.0", "resolve@^1.22.2":
+"resolve@^1.1.7", "resolve@^1.10.0", "resolve@^1.14.2", "resolve@^1.19.0", "resolve@^1.20.0", "resolve@^1.22.0", "resolve@^1.22.2":
"integrity" "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g=="
"resolved" "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz"
"version" "1.22.2"
@@ -5774,6 +6124,11 @@
"buffer-from" "^1.0.0"
"source-map" "^0.6.0"
+"source-map@^0.5.7":
+ "integrity" "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="
+ "resolved" "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz"
+ "version" "0.5.7"
+
"source-map@^0.6.0", "source-map@^0.6.1", "source-map@~0.6.1":
"integrity" "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
"resolved" "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz"
@@ -5846,6 +6201,11 @@
"resolved" "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz"
"version" "0.3.1"
+"string-convert@^0.2.0":
+ "integrity" "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A=="
+ "resolved" "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz"
+ "version" "0.2.1"
+
"string-length@^4.0.1":
"integrity" "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ=="
"resolved" "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz"
@@ -5961,6 +6321,11 @@
dependencies:
"client-only" "0.0.1"
+"stylis@4.2.0":
+ "integrity" "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw=="
+ "resolved" "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz"
+ "version" "4.2.0"
+
"sucrase@^3.32.0":
"integrity" "sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ=="
"resolved" "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz"
@@ -6030,6 +6395,11 @@
"csso" "^5.0.5"
"picocolors" "^1.0.0"
+"swiper@*", "swiper@^11.0.2":
+ "integrity" "sha512-JMHZYdUDG0V5ZdzWJkQicW4F7u4edmS4vlOhciTDhcZokDL2N8EE2uP4INxqIgpiJMoeHlwATqZk2yEAW7F6Dw=="
+ "resolved" "https://registry.npmjs.org/swiper/-/swiper-11.0.2.tgz"
+ "version" "11.0.2"
+
"symbol-tree@^3.2.4":
"integrity" "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="
"resolved" "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz"
@@ -6350,6 +6720,16 @@
dependencies:
"punycode" "^2.1.0"
+"use-isomorphic-layout-effect@^1.1.2":
+ "integrity" "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA=="
+ "resolved" "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz"
+ "version" "1.1.2"
+
+"use-sync-external-store@^1.0.0":
+ "integrity" "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA=="
+ "resolved" "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz"
+ "version" "1.2.0"
+
"util-deprecate@^1.0.1", "util-deprecate@^1.0.2":
"integrity" "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
"resolved" "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"