diff --git a/package.json b/package.json index 815e3c5f..517ccc1a 100644 --- a/package.json +++ b/package.json @@ -89,6 +89,7 @@ "react-select-country-list": "^2.2.3", "react-slick": "^0.29.0", "react-top-loading-bar": "^2.3.1", + "react-use": "^17.4.2", "server-only": "^0.0.1", "sharp": "^0.32.6", "slick-carousel": "^1.8.1", @@ -98,6 +99,7 @@ "styled-components": "^5.3.9", "swagger-ui-react": "^4.19.0", "swiper": "^11.0.3", + "swr": "^2.2.4", "test-utils": "^1.1.1", "typescript": "5.0.2", "use-onclickoutside": "^0.4.1", diff --git a/src/components/AreaGraph/index.tsx b/src/components/AreaGraph/index.tsx index 33def241..e94020d8 100644 --- a/src/components/AreaGraph/index.tsx +++ b/src/components/AreaGraph/index.tsx @@ -1,6 +1,6 @@ import dynamic from 'next/dynamic'; -import { userAnalyticsType } from '@/pages/dashboard/new'; +import { userAnalyticsType } from '@/pages/dashboard/components/FreelancerDashboard'; const Chart = dynamic(() => import('react-apexcharts'), { ssr: false, diff --git a/src/components/BriefComponent/index.tsx b/src/components/BriefComponent/index.tsx index fdfdfdc4..22a55419 100644 --- a/src/components/BriefComponent/index.tsx +++ b/src/components/BriefComponent/index.tsx @@ -37,13 +37,13 @@ export default function BriefComponent({ brief }: { brief: any }) { }, [brief]); return ( -
+
router.push(`/briefs/${brief.id}`)} - className='py-9 px-7 max-w-[70%] w-full break-words' + className='py-9 px-7 lg:max-w-[70%] w-full break-words' >

{brief.headline}

-
+

{brief.experience_level} @@ -93,7 +93,7 @@ export default function BriefComponent({ brief }: { brief: any }) { )}

-
+
{ return (
diff --git a/src/components/Briefs/BioInsights.tsx b/src/components/Briefs/BioInsights.tsx index 32363c1c..1a1137b3 100644 --- a/src/components/Briefs/BioInsights.tsx +++ b/src/components/Briefs/BioInsights.tsx @@ -5,7 +5,7 @@ import { Alert, Skeleton, Tooltip } from '@mui/material'; import moment from 'moment'; import Image from 'next/image'; import { useRouter } from 'next/router'; -import React, { useContext, useEffect, useState } from 'react'; +import { useContext, useEffect, useState } from 'react'; import { FaRegShareSquare } from 'react-icons/fa'; import { checkEnvironment } from '@/utils'; @@ -21,7 +21,7 @@ import { saveBriefData, } from '@/redux/services/briefService'; -import { LoginPopupContext, LoginPopupContextType } from '../Layout'; +import { AppContext, AppContextType } from '../Layout'; import CountrySelector from '../Profile/CountrySelector'; type BioInsightsProps = { @@ -55,7 +55,7 @@ const BioInsights = ({ setSuccess, setError, setSuccessTitle, - loadingMain + loadingMain, }: BioInsightsProps) => { const router = useRouter(); const [copied, setCopied] = useState(false); @@ -67,12 +67,9 @@ const BioInsights = ({ const lastApplication: Project = briefApplications[briefApplications?.length - 1]; - const pendingApplciations: Project[] = - !briefApplications?.length - ? [] - : briefApplications?.filter( - (application) => application?.status_id === 1 - ); + const pendingApplciations: Project[] = !briefApplications?.length + ? [] + : briefApplications?.filter((application) => application?.status_id === 1); let applicationCount = '0'; @@ -92,22 +89,26 @@ const BioInsights = ({ else if (isOwnerOfBrief) hint = 'You are not allowed to submit proposal to your own brief'; - const { setShowLoginPopup } = useContext( - LoginPopupContext - ) as LoginPopupContextType; + const { setShowLoginPopup } = useContext(AppContext) as AppContextType; - const acceptedBriefs = allClientBriefs?.acceptedBriefs || [] - const briefsUnderReview = allClientBriefs?.briefsUnderReview || [] - const clientBriefs = [...acceptedBriefs, ...briefsUnderReview] + const acceptedBriefs = allClientBriefs?.acceptedBriefs || []; + const briefsUnderReview = allClientBriefs?.briefsUnderReview || []; + const clientBriefs = [...acceptedBriefs, ...briefsUnderReview]; - const briefWithApplications = allClientBriefs?.briefsUnderReview?.length ? allClientBriefs.briefsUnderReview.filter((brief: Brief) => brief.number_of_applications) : 0 - const hiredCount = allClientBriefs?.acceptedBriefs?.length || 0 + const briefWithApplications = allClientBriefs?.briefsUnderReview?.length + ? allClientBriefs.briefsUnderReview.filter( + (brief: Brief) => brief.number_of_applications + ) + : 0; + const hiredCount = allClientBriefs?.acceptedBriefs?.length || 0; - const hireRate = Math.round((hiredCount / (briefWithApplications.length + hiredCount)) * 100) + const hireRate = Math.round( + (hiredCount / (briefWithApplications.length + hiredCount)) * 100 + ); useEffect(() => { const fetchSavedBriefs = async () => { - if (!brief?.id) return + if (!brief?.id) return; try { if (browsingUser?.id) { @@ -119,14 +120,16 @@ const BioInsights = ({ } setBriefApplications(await getBriefApplications(brief?.id)); } catch (error) { - setError({ message: "Failed to get application data. Please try again. " + error }) + setError({ + message: 'Failed to get application data. Please try again.', + }); } finally { - setLoading(false) + setLoading(false); } - } + }; - fetchSavedBriefs() - }, [brief?.id, browsingUser?.id, setError]) + fetchSavedBriefs(); + }, [brief?.id, browsingUser?.id, setError]); // useEffect(() => { // const setUp = async () => { @@ -171,7 +174,8 @@ const BioInsights = ({ }; const unsaveBrief = async () => { - if (!browsingUser?.id) return setError({ message: "No user information found." }); + if (!browsingUser?.id) + return setError({ message: 'No user information found.' }); await deleteSavedBrief(brief.id, browsingUser?.id); setIsSavedBrief(false); @@ -179,31 +183,30 @@ const BioInsights = ({ setSuccessTitle('Brief Unsaved Successfully'); }; - if (loading || loadingMain) return ( -
- -
- - -
+ if (loading || loadingMain) + return ( +
+ +
+ + +
- - - + + + - - + + - - - -
- ) + + + +
+ ); return ( -
+

Activities on this job @@ -213,10 +216,11 @@ const BioInsights = ({
@@ -238,9 +242,10 @@ const BioInsights = ({ gap-2 !m-0 !px-4 - ${(!canSubmitProposal || isOwnerOfBrief) && - '!bg-gray-300 !text-gray-400 !cursor-not-allowed' - } + ${ + (!canSubmitProposal || isOwnerOfBrief) && + '!bg-gray-300 !text-gray-400 !cursor-not-allowed' + } `} onClick={() => canSubmitProposal && !isOwnerOfBrief && redirectToApply() @@ -256,7 +261,11 @@ const BioInsights = ({
{brief.verified_only && (

- + Only verified freelancer can apply

)} @@ -358,7 +367,9 @@ const BioInsights = ({

- {hireRate > 0 && {hireRate}% hire rate,} + {hireRate > 0 && ( + {hireRate}% hire rate, + )} {allClientBriefs?.briefsUnderReview?.length || 0} open job

@@ -448,8 +459,9 @@ const BioInsights = ({
{`Brief link copied to clipboard`} diff --git a/src/components/Briefs/BriefsList.tsx b/src/components/Briefs/BriefsList.tsx index 85e72386..99c25f81 100644 --- a/src/components/Briefs/BriefsList.tsx +++ b/src/components/Briefs/BriefsList.tsx @@ -1,8 +1,7 @@ import TimeAgo from 'javascript-time-ago'; import en from 'javascript-time-ago/locale/en'; -import Link from 'next/link'; import { useRouter } from 'next/router'; -import React, { useState } from 'react'; +import { useState } from 'react'; import { ProgressBar } from '../ProgressBar'; TimeAgo.addLocale(en); @@ -57,114 +56,104 @@ export const BriefLists = ({ return ( <> -
- { - briefs?.map( - (brief, index) => - index < +
+ {briefs?.map( + (brief, index) => + index < Math.min(Math.max(loadValue, openBriefLimit), briefs.length) && ( -
handleItemClick(brief)} - className={`flex cursor-pointer group hover:bg-imbue-light-purple-hover px-5 py-3 lg:px-10 lg:py-8 justify-between border-b border-b-imbue-light-purple ${briefs.length < 2 && 'last:border-b-0'}`} - > -
-
- - {brief?.headline?.length > 50 - ? `${brief.headline.substring(0, 50)}...` - : brief.headline} - - e.stopPropagation()} - className='text-sm text-imbue-lemon hover:underline ml-4' - > - - View Full Brief - +
handleItemClick(brief)} + className={`flex ${ + brief.project_id && 'flex-col-reverse lg:flex-row' + } cursor-pointer group hover:bg-imbue-light-purple-hover px-9 py-3 lg:px-10 lg:py-8 justify-between border-b border-b-imbue-light-purple ${ + briefs.length < 2 && 'last:border-b-0' + }`} + > +
+ + {brief?.headline?.length > 50 + ? `${brief.headline.substring(0, 50)}...` + : brief.headline} + +

+ Budget ${Number(brief.budget).toLocaleString()} +

+

+ {brief?.description?.length > 500 + ? brief?.description?.substring(0, 500) + '...' + : brief?.description} +

+

+ Created {timeAgo.format(new Date(brief.created))} +

+
+ {brief.project_id ? ( +
+
+ Milestones{' '} + + { + brief.milestones?.filter((m: any) => m?.is_approved) + ?.length + } + /{brief.milestones?.length}
-

- Budget ${Number(brief.budget).toLocaleString()} -

-

- {brief?.description?.length > 500 - ? brief?.description?.substring(0, 500) + '...' - : brief?.description} -

-

- Created {timeAgo.format(new Date(brief.created))} -

+ {/*
+
m?.is_approved + )?.length / + brief.milestones?.length) * + 100 + }%`, + }} + className='h-full rounded-xl Accepted-button absolute' + >
+
+ {brief.milestones?.map((m: any, i: number) => ( +
+ ))} +
+
*/} +
+ it.is_approved === true + ).length + } + /> +
- { - brief.project_id - ? ( -
-

- Milestones{' '} - - { - brief.milestones?.filter((m: any) => m?.is_approved) - ?.length - } - /{brief.milestones?.length} - -

- {/*
-
m?.is_approved - )?.length / - brief.milestones?.length) * - 100 - }%`, - }} - className='h-full rounded-xl Accepted-button absolute' - >
-
- {brief.milestones?.map((m: any, i: number) => ( -
- ))} -
-
*/} -
- it.is_approved === true - ).length - } - /> -
-
) - : ( -
-

- Proposals -

-

- {brief.number_of_applications} -

-
- ) - } -
- ) - )} -
+ ) : ( +
+

+ Proposals +

+

+ {brief.number_of_applications} +

+
+ )} +
+ ) + )} +
{loadValue < briefs.length && (
@@ -197,7 +186,6 @@ export const BriefLists = ({
)} {/* */} -
); diff --git a/src/components/ClientView/Applications/Applications.tsx b/src/components/ClientView/Applications/Applications.tsx index 0dd124a8..ddec46ea 100644 --- a/src/components/ClientView/Applications/Applications.tsx +++ b/src/components/ClientView/Applications/Applications.tsx @@ -1,10 +1,14 @@ +import { useContext } from 'react'; +import useSWR from 'swr' import BackButton from '@/components/BackButton'; import { ApplicationContainer } from '@/components/Briefs/ApplicationContainer'; import ApplicationSkeleton from '@/components/Briefs/ApplicationSkeleton'; import { BriefLists } from '@/components/Briefs/BriefsList'; +import { AppContext, AppContextType } from '@/components/Layout'; -import { Freelancer, Project } from '@/model'; +import { Freelancer } from '@/model'; +import { getBriefApplications } from '@/redux/services/briefService'; interface ClientViewProps { goBack: () => void; @@ -12,8 +16,8 @@ interface ClientViewProps { briefs: any; handleMessageBoxClick: (_userId: number, _freelander: Freelancer) => void; // redirectToBriefApplications: (_applicationId: string) => void; - briefApplications: Project[]; - loadingApplications: boolean; + // briefApplications: Project[]; + // loadingApplications: boolean; } export default function Applications({ @@ -21,10 +25,49 @@ export default function Applications({ briefs, handleMessageBoxClick, // redirectToBriefApplications, - briefApplications, - loadingApplications, + // briefApplications, + // loadingApplications, goBack, }: ClientViewProps) { + // const [briefApplications, setBriefApplications] = useState([]); + // const [loadingApplications, setLoadingApplications] = useState(true); + + // const { user, loading } = useSelector((state: RootState) => state.userState); + + // useEffect(() => { + // const getApplications = async () => { + // if (!briefId || !user.id) return + + // try { + // setLoadingApplications(true); + // const resp = await getBriefApplications(String(briefId)); + + // if (resp.status === 501) + // return router.push('/dashboard'); + + // setBriefApplications(resp); + // setLoadingApplications(false); + // } catch (error) { + // console.log(error); + // setLoadingApplications(false); + // } + // }; + + // briefId && getApplications(); + // }, [briefId, loading, router, user.id]); + + const { setError } = useContext(AppContext) as AppContextType; + + const { + data: briefApplications, + isLoading: loadingApplications, + error + } = useSWR(() => briefId ? `/api/briefs/${briefId}/applications` : null, () => getBriefApplications(String(briefId))) + + if (error) { + setError(error); + } + return (
{briefId ? ( @@ -42,10 +85,10 @@ export default function Applications({ {briefApplications?.map((application: any, index: any) => { return ( ); })} diff --git a/src/components/ClientView/ClientView.tsx b/src/components/ClientView/ClientView.tsx index c4931af6..3784559b 100644 --- a/src/components/ClientView/ClientView.tsx +++ b/src/components/ClientView/ClientView.tsx @@ -1,22 +1,24 @@ import ArrowBackIcon from '@mui/icons-material/ChevronLeft'; import { useRouter } from 'next/router'; -import { useState } from 'react'; +import { Suspense, useState } from 'react'; import { Freelancer, Project } from '@/model'; import Applications from './Applications/Applications'; +// const Applications = lazy(() => import('./Applications/Applications')) import Grants from './Grants/Grants'; import Projects from './Projects/Projects'; +import ApplicationSkeleton from '../Briefs/ApplicationSkeleton'; interface ClientViewProps { GoBack: () => void; briefId: string | string[] | undefined; briefs: any; handleMessageBoxClick: (_userId: number, _freelander: Freelancer) => void; - // redirectToBriefApplications: (_applicationId: string) => void; - briefApplications: Project[]; ongoingGrants: Project[]; - loadingApplications: boolean; + // redirectToBriefApplications: (_applicationId: string) => void; + // briefApplications: Project[]; + // loadingApplications: boolean; } export default function ClientView({ @@ -24,10 +26,10 @@ export default function ClientView({ briefId, briefs, handleMessageBoxClick, - // redirectToBriefApplications, - briefApplications, ongoingGrants, - loadingApplications, + // redirectToBriefApplications, + // briefApplications, + // loadingApplications, }: ClientViewProps) { const [switcher, setSwitcher] = useState('application'); const router = useRouter(); @@ -46,19 +48,19 @@ export default function ClientView({

setSwitcher('application')} - className='text-2xl text-black py-5 border-r text-center w-full ' + className='text-base lg:text-2xl text-black py-3 lg:py-5 border-r text-center w-full ' > Briefs ({briefs?.briefsUnderReview?.length || 0})

setSwitcher('projects')} - className='text-2xl text-black py-5 border-r text-center w-full' + className='text-base lg:text-2xl text-black py-3 lg:py-5 border-r text-center w-full' > Projects({briefs?.acceptedBriefs?.length || 0})

setSwitcher('grants')} - className='text-2xl text-black border-r py-5 text-center w-full' + className='text-base lg:text-2xl text-black py-3 lg:py-5 text-center w-full' > Grants({ongoingGrants?.length || 0})

@@ -102,15 +104,17 @@ export default function ClientView({ {/* */} {switcher === 'application' && ( - }> + + /> + )} {switcher === 'projects' && } {switcher === 'grants' && } diff --git a/src/components/CustomDropDown.tsx b/src/components/CustomDropDown.tsx index 7a46fc0b..50a34a32 100644 --- a/src/components/CustomDropDown.tsx +++ b/src/components/CustomDropDown.tsx @@ -1,7 +1,7 @@ -/* eslint-disable react/display-name */ import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; import { InputAdornment, TextField } from '@mui/material'; -import React, { useState } from 'react'; +import React, { useRef, useState } from 'react'; +import { useClickAway } from 'react-use'; import BriefFilter from './BriefFilter'; @@ -57,26 +57,19 @@ const CustomDropDown = ({ setOptions(newOptions); }; - return ( -
- {/*
- {name} - {'filter-icon'} -
*/} + // to close the dropdown when clicked outside + const dropdownRef = useRef(null); + useClickAway(dropdownRef, () => { + setIsOpen(false) + }); + + return ( +
setIsOpen(true)} - className=' min-w-[13.3rem] w-full' + className='min-w-[13.3rem] w-full' label={name} color='secondary' autoComplete='off' @@ -85,7 +78,10 @@ const CustomDropDown = ({ setIsOpen(!isOpen)} + onClick={(e) => { + e.stopPropagation() + setIsOpen(!isOpen) + }} /> ), @@ -94,7 +90,7 @@ const CustomDropDown = ({ {isOpen && (
= ({ projects }) => { // onClick={() => redirectToApplication(project)} className=' hover:bg-imbue-light-purple cursor-pointer px-9 text-imbue-purple' > -
+
{project.milestones && ( <> diff --git a/src/components/Dashboard/MyClientBriefsView.tsx b/src/components/Dashboard/MyClientBriefsView.tsx index 3b468e82..69f21479 100644 --- a/src/components/Dashboard/MyClientBriefsView.tsx +++ b/src/components/Dashboard/MyClientBriefsView.tsx @@ -1,14 +1,11 @@ /* eslint-disable no-console */ //import ArrowBackIcon from '@mui/icons-material/ArrowBack'; import { useRouter } from 'next/router'; -import React, { useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import { useSelector } from 'react-redux'; import { Freelancer, Project } from '@/model'; -import { - getBriefApplications, - getUserBriefs, -} from '@/redux/services/briefService'; +import { getUserBriefs } from '@/redux/services/briefService'; import { getUsersOngoingGrants } from '@/redux/services/projectServices'; import { RootState } from '@/redux/store/store'; @@ -28,17 +25,14 @@ const MyClientBriefsView = (props: ClientViewProps) => { const { briefId, handleMessageBoxClick } = props; const { user, loading } = useSelector((state: RootState) => state.userState); - + const router = useRouter(); const [briefs, setBriefs] = useState(); - const [briefApplications, setBriefApplications] = useState([]); const [ongoingGrants, setOngoingGrants] = useState([]); - const [loadingApplications, setLoadingApplications] = useState(true); - const router = useRouter(); useEffect(() => { const setUserBriefs = async () => { - if (!user.id) return router.push('/auth/sign-in') + if (!user.id) return router.push('/auth/sign-in'); setBriefs(await getUserBriefs(user?.id)); @@ -50,28 +44,6 @@ const MyClientBriefsView = (props: ClientViewProps) => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [user.id, user?.web3_address, loading]); - useEffect(() => { - const getApplications = async () => { - if (!briefId || !user.id) return - - try { - setLoadingApplications(true); - const resp = await getBriefApplications(String(briefId)); - - if (resp.status === 501) - return router.push('/dashboard'); - - setBriefApplications(resp); - setLoadingApplications(false); - } catch (error) { - console.log(error); - setLoadingApplications(false); - } - }; - - briefId && getApplications(); - }, [briefId, loading, router, user.id]); - const goBack = () => { router.query.briefId = []; router.replace(router, undefined, { shallow: true }); @@ -84,10 +56,10 @@ const MyClientBriefsView = (props: ClientViewProps) => { briefId={briefId} briefs={briefs} handleMessageBoxClick={handleMessageBoxClick} - // redirectToBriefApplications={redirectToBriefApplications} - briefApplications={briefApplications} ongoingGrants={ongoingGrants} - loadingApplications={loadingApplications} + // redirectToBriefApplications={redirectToBriefApplications} + // briefApplications={briefApplications} + // loadingApplications={loadingApplications} />
); diff --git a/src/components/Dashboard/V2/BriefsView.tsx b/src/components/Dashboard/V2/BriefsView.tsx index d82ae5f0..3a9c61bb 100644 --- a/src/components/Dashboard/V2/BriefsView.tsx +++ b/src/components/Dashboard/V2/BriefsView.tsx @@ -579,7 +579,7 @@ const BriefsView = (props: BriefsViewProps) => { }; return ( -
+

Recomended Briefs

diff --git a/src/components/ErrorScreen.tsx b/src/components/ErrorScreen.tsx index ce16b073..9d5e7a54 100644 --- a/src/components/ErrorScreen.tsx +++ b/src/components/ErrorScreen.tsx @@ -22,7 +22,7 @@ const ErrorScreen = (props: ErrorScreenProps) => { onClose={handleClose} aria-labelledby='alert-dialog-title' aria-describedby='alert-dialog-description' - className='p-14 errorDialogue' + className='p-2 lg:p-14 errorDialogue' >
diff --git a/src/components/Filter/FilterModal.tsx b/src/components/Filter/FilterModal.tsx index de263a40..1233efb4 100644 --- a/src/components/Filter/FilterModal.tsx +++ b/src/components/Filter/FilterModal.tsx @@ -33,7 +33,7 @@ const FilterModal = ({ onClick={(e: any) => { e?.stopPropagation(); }} - className='bg-white rounded-2xl md:px-12 px-8 md:py-10 py-5 h-[450px] min-width-1280px:w-[70%] w-[95vw] self-center relative' + className='bg-white rounded-2xl md:px-12 px-8 md:py-10 py-5 h-fit lg:h-[450px] min-width-1280px:w-[70%] w-[95vw] self-center relative' >

Filter by: @@ -72,7 +72,7 @@ const FilterModal = ({ ) })}

*/} -
+
{customDropdownConfigs ?.filter(({ options }: any) => options && options.length > 0) ?.map(({ label, filterType, options }: any) => ( @@ -87,7 +87,7 @@ const FilterModal = ({ ))}
-
+
+ +
+ diff --git a/src/components/Navbar/MenuItems.tsx b/src/components/Navbar/MenuItems.tsx index ee1ae1ed..ceb12e78 100644 --- a/src/components/Navbar/MenuItems.tsx +++ b/src/components/Navbar/MenuItems.tsx @@ -2,79 +2,125 @@ import { ListItemIcon, MenuItem } from '@mui/material'; import Image from 'next/image'; import Link from 'next/link'; import { useRouter } from 'next/router'; -import React from 'react'; +import React, { useContext } from 'react'; +import { BiBuildings } from 'react-icons/bi'; +import { AppContext, AppContextType } from '../Layout'; import defaultProfile from '../../assets/images/profile-image.png'; const MenuItems = ({ user, isFreelancer, setLoginModal, handleClose }: any) => { const router = useRouter(); + const { profileView, setProfileMode } = useContext( + AppContext + ) as AppContextType; const linkItems = [ { - icon: 'face', + icon: ( + dashboard + ), text: 'Dashboard', link: '/dashboard', needAuthentication: true, - duplicate: false, + desktopView: true, }, { - icon: 'work', + icon: ( + + ), text: 'Submit A Brief', link: '/briefs/new', needAuthentication: true, - duplicate: true, + desktopView: false, }, { - icon: 'account_balance', + icon: ( + + ), text: 'Submit A Grant', link: '/grants/new', needAuthentication: true, - duplicate: true, + desktopView: false, }, { - icon: 'search', + icon: ( + + ), text: 'Discover Briefs', link: '/briefs', needAuthentication: false, - duplicate: true, + desktopView: false, }, { - icon: 'groups', + icon: ( + + ), text: 'Discover Freelancers', link: `/freelancers`, needAuthentication: false, - duplicate: true, + desktopView: false, }, + // { + // icon: 'person', + // text: 'Profile', + // link: `/profile/${user?.username}/`, + // needAuthentication: true, + // desktopView: false, + // }, + // { + // icon: isFreelancer ? 'account_circle' : 'group_add', + // text: isFreelancer ? 'Freelancer Profile' : 'Join The Freelancers', + // link: isFreelancer + // ? `/freelancers/${user?.username}/` + // : '/freelancers/new', + // needAuthentication: true, + // desktopView: false, + // }, { - icon: 'person', - text: 'Profile', - link: `/profile/${user?.username}/`, - needAuthentication: true, - duplicate: false, - }, - { - icon: isFreelancer ? 'account_circle' : 'group_add', - text: isFreelancer ? 'Freelancer Profile' : 'Join The Freelancers', - link: isFreelancer - ? `/freelancers/${user?.username}/` - : '/freelancers/new', - needAuthentication: true, - duplicate: false, - }, - { - icon: 'money', + icon: ( + + ), text: 'Transfer Funds', link: '/relay', needAuthentication: true, - duplicate: false, - }, - { - icon: 'logout', - text: user?.username ? 'Sign Out' : 'Sign In', - link: user?.username ? '/logout' : '/login', - needAuthentication: false, - duplicate: false, + desktopView: false, }, + // { + // icon: 'logout', + // text: user?.username ? 'Sign Out' : 'Sign In', + // link: user?.username ? '/logout' : '/login', + // needAuthentication: false, + // desktopView: false, + // }, ]; const navigateToPage = async (link: string, needAuthentication: boolean) => { @@ -142,11 +188,13 @@ const MenuItems = ({ user, isFreelancer, setLoginModal, handleClose }: any) => { )) } */} -
+
handleClose()} + >
{

{user?.display_name || "User Name"}

{ // navigateToPage(linkItems[6].link, user?.id) }} > - Freelancer Profile + {isFreelancer ? 'Freelancer Profile' : 'Join The Freelancers'}

+ { + setProfileMode(profileView == 'client' ? "freelancer" : "client"); + router.push('/dashboard'); + }} + > + + + +

{profileView == 'client' ? "Switch to Freelancer" : "Switch to Hiring"}

+
+ { - user?.id && ( + linkItems.map((item, index) => ( navigateToPage(linkItems[0].link, user?.id)} > - dashboard + {item.icon} -

Dashboard

+

{item.text}

- ) + )) } navigateToPage(linkItems[8].link, user?.id)} + onClick={() => navigateToPage(user?.username ? '/logout' : '/login', user?.id)} > { className='w-[18px] h-[18px]' /> -

{linkItems[8].text}

+

{user?.username ? 'Sign Out' : 'Sign In'}

diff --git a/src/components/Project/ProjectApprovers.tsx b/src/components/Project/ProjectApprovers.tsx index bb9391d5..82e8e6d1 100644 --- a/src/components/Project/ProjectApprovers.tsx +++ b/src/components/Project/ProjectApprovers.tsx @@ -1,7 +1,7 @@ import { Dialog, DialogContent, DialogTitle, Skeleton } from '@mui/material'; import Image from 'next/image'; import { useRouter } from 'next/router'; -import React, { useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import { useSelector } from 'react-redux'; import { Project, User } from '@/model'; @@ -9,217 +9,245 @@ import { getApproverProfiles } from '@/redux/services/projectServices'; import { RootState } from '@/redux/store/store'; type ProjectApproversType = { - approversPreview: User[]; - project: Project; - setApproverPreview: (_value: User[]) => void; - projectOwner: User | undefined; -} + approversPreview: User[]; + project: Project; + setApproverPreview: (_value: User[]) => void; + projectOwner: User | undefined; +}; const ProjectApprovers = (props: ProjectApproversType) => { - const { approversPreview, project, setApproverPreview, projectOwner } = props; - - const { user } = useSelector( - (state: RootState) => state.userState - ); - - const [loading, setLoading] = useState(true); - const [showApproverList, setShowApproverList] = useState(false); - - const router = useRouter() - - useEffect(() => { - const getAndSetApprovers = async () => { - // setting approver list - let approversPreviewList: any = [...approversPreview]; - - try { - // if (project?.approvers?.length) { - // const promises: Promise[] = []; - - // project?.approvers.map((approverAddress: any) => { - // if (approverAddress === user?.web3_address) setIsApprover(true); - - // promises.push(utils.fetchUserByUsernameOrAddress(approverAddress)) - // }) - - // const approversList = await Promise.all(promises) - - // approversList.map((approver, index) => { - // if (approver?.id) { - // approversPreviewList.push(approver); - // } else { - // approversPreviewList.push({ - // id: 0, - // display_name: '', - // profile_photo: '', - // username: '', - // web3_address: project.approvers[index], - // getstream_token: '', - // }); - // } - // }) - - // } else if (approversPreviewList.length === 0 && projectOwner) { - // approversPreviewList.push({ - // id: projectOwner?.id, - // display_name: projectOwner?.display_name, - // profile_photo: projectOwner?.profile_photo, - // username: projectOwner?.username, - // web3_address: projectOwner?.web3_address, - // getstream_token: projectOwner?.getstream_token, - // }); - // } - // setApproverPreview(approversPreviewList); - - if (project?.approvers?.length) { - const approvers = await getApproverProfiles(project.approvers) - approversPreviewList = approvers - } - else { - approversPreviewList = [ - { - id: projectOwner?.id, - display_name: projectOwner?.display_name, - profile_photo: projectOwner?.profile_photo, - username: projectOwner?.username, - web3_address: projectOwner?.web3_address, - getstream_token: projectOwner?.getstream_token, - } - ] - } - - setApproverPreview(approversPreviewList); - } catch (error) { - // eslint-disable-next-line no-console - console.error(error); - } finally { - setLoading(false); - } + const { approversPreview, project, setApproverPreview, projectOwner } = props; + + const { user } = useSelector((state: RootState) => state.userState); + + const [loading, setLoading] = useState(true); + const [showApproverList, setShowApproverList] = useState(false); + + const router = useRouter(); + + useEffect(() => { + const getAndSetApprovers = async () => { + // setting approver list + let approversPreviewList: any = [...approversPreview]; + + try { + // if (project?.approvers?.length) { + // const promises: Promise[] = []; + + // project?.approvers.map((approverAddress: any) => { + // if (approverAddress === user?.web3_address) setIsApprover(true); + + // promises.push(utils.fetchUserByUsernameOrAddress(approverAddress)) + // }) + + // const approversList = await Promise.all(promises) + + // approversList.map((approver, index) => { + // if (approver?.id) { + // approversPreviewList.push(approver); + // } else { + // approversPreviewList.push({ + // id: 0, + // display_name: '', + // profile_photo: '', + // username: '', + // web3_address: project.approvers[index], + // getstream_token: '', + // }); + // } + // }) + + // } else if (approversPreviewList.length === 0 && projectOwner) { + // approversPreviewList.push({ + // id: projectOwner?.id, + // display_name: projectOwner?.display_name, + // profile_photo: projectOwner?.profile_photo, + // username: projectOwner?.username, + // web3_address: projectOwner?.web3_address, + // getstream_token: projectOwner?.getstream_token, + // }); + // } + // setApproverPreview(approversPreviewList); + + if (project?.approvers?.length) { + const approvers = await getApproverProfiles(project.approvers); + approversPreviewList = approvers; + } else { + approversPreviewList = [ + { + id: projectOwner?.id, + display_name: projectOwner?.display_name, + profile_photo: projectOwner?.profile_photo, + username: projectOwner?.username, + web3_address: projectOwner?.web3_address, + getstream_token: projectOwner?.getstream_token, + }, + ]; } - project?.id && getAndSetApprovers() - - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [project.id, setApproverPreview, user?.web3_address, projectOwner?.id, projectOwner?.display_name, projectOwner?.profile_photo, projectOwner?.username, projectOwner?.web3_address, projectOwner?.getstream_token]) - - if (loading || approversPreview?.length === 0) return ( -
- {[1, 1, 1].map((approver: any, index: number) => ( -
- approver.display_name && - router.push(`/profile/${approver.username}`) - } - > - -
- - -
-
- ))} -
- ) - - + setApproverPreview(approversPreviewList); + } catch (error) { + // eslint-disable-next-line no-console + console.error(error); + } finally { + setLoading(false); + } + }; + + project?.id && getAndSetApprovers(); + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + project.id, + setApproverPreview, + user?.web3_address, + projectOwner?.id, + projectOwner?.display_name, + projectOwner?.profile_photo, + projectOwner?.username, + projectOwner?.web3_address, + projectOwner?.getstream_token, + ]); + + if (loading || approversPreview?.length === 0) return ( -
-
-

Approvers

- { - !project.brief_id && ( -

setShowApproverList(true)} - > - see all -

- ) - } +
+ {[1, 1, 1].map((approver: any, index: number) => ( +
+ approver.display_name && + router.push(`/profile/${approver.username}`) + } + > + +
+ +
+
+ ))} +
+ ); - {approversPreview?.length > 0 && ( -
- {approversPreview?.slice(0, 4).map((approver: any, index: number) => ( -
- approver.display_name && - router.push(`/profile/${approver.username}`) - } - > - -
- - { - approver?.display_name.length > 5 - ? approver.display_name.substring(0, 5) + "..." - : approver.display_name - } - -

- {approver?.web3_address?.substring(0, 4) + - '...' + - approver?.web3_address?.substring(44)} -

-
-
- ))} -
- )} - - setShowApproverList(false)} - aria-labelledby="alert-dialog-title" - aria-describedby="alert-dialog-description" - PaperProps={{ className: "w-1/2 pb-2 pr-4 rounded-xl max-h-[70vh] custom-scroll" }} - maxWidth="lg" + return ( +
+
+

Approvers

+ {!project.brief_id && ( +

setShowApproverList(true)} + > + see all +

+ )} +
+ + {approversPreview?.length > 0 && ( +
+ {approversPreview?.slice(0, 4).map((approver: any, index: number) => ( +
+ approver.display_name && + router.push(`/profile/${approver.username}`) + } > - - {"People who can vote for this project"} - - -
- { - approversPreview.map((approver, index) => ( -
approver.username && router.push(`/profile/${approver.username}`)} - > - voter - -
-

{approver.display_name}

-

{approver.web3_address}

-
-
- )) - } -
-
-
+ +
+ + {approver?.display_name.length > 5 + ? approver.display_name.substring(0, 5) + '...' + : approver.display_name} + +

+ {approver?.web3_address?.substring(0, 6) + + '...' + + approver?.web3_address?.substring(42)} +

+
+
+ ))}
- ); + )} + + setShowApproverList(false)} + aria-labelledby='alert-dialog-title' + aria-describedby='alert-dialog-description' + PaperProps={{ + className: 'w-1/2 pb-2 pr-4 rounded-xl max-h-[70vh] custom-scroll', + }} + maxWidth='lg' + > + + {'People who can vote for this project'} + + +
+ {approversPreview.map((approver, index) => ( +
+ approver.username && + router.push(`/profile/${approver.username}`) + } + > + voter + +
+

{approver.display_name}

+

{approver.web3_address}

+
+
+ ))} +
+
+
+
+ ); }; -export default ProjectApprovers; \ No newline at end of file +export default ProjectApprovers; diff --git a/src/components/Project/V2/ExpandableMilestone.tsx b/src/components/Project/V2/ExpandableMilestone.tsx index 5b3ce8bc..1a4253a0 100644 --- a/src/components/Project/V2/ExpandableMilestone.tsx +++ b/src/components/Project/V2/ExpandableMilestone.tsx @@ -365,7 +365,7 @@ const ExpandableMilestone = (props: ExpandableMilestonProps) => { - +
{index + 1} @@ -375,10 +375,12 @@ const ExpandableMilestone = (props: ExpandableMilestonProps) => { {milestone?.name}

-

+

+ Milestone Funding: {milestone.amount} ${Currency[project.currency_id]}

-

+

+ Milestone ends: {moment(milestone.modified).format('MMM Do YY')}

@@ -392,7 +394,7 @@ const ExpandableMilestone = (props: ExpandableMilestonProps) => { {project.first_pending_milestone === milestone.milestone_index && project.project_in_milestone_voting ? (

{

) : (

{ }, [value, votes]) + const mobileView = useMediaQuery('(max-width:480px)'); return (

{ onClose={() => setOpenNoRefundList(false)} aria-labelledby='alert-dialog-title' aria-describedby='alert-dialog-description' - className='p-14 errorDialogue' + className='p-2 lg:p-14 errorDialogue' > { (loading || userLoading) @@ -124,10 +125,20 @@ const NoConfidenceList = (props: VotingListProps) => { approver?.display_name &&

{approver.display_name}

} -

{approver?.web3_address || approver?.approver}

+

+ { + !mobileView + ? approver?.web3_address + : ( + approver?.web3_address?.substring(0, 8) + + '...' + + approver?.web3_address?.substring(39) + ) + } +

-

+

{value === 0 && "Yes"} {value === 1 && "No"} {value === 2 && "Pending"} diff --git a/src/components/Project/VotingList/VotingList.tsx b/src/components/Project/VotingList/VotingList.tsx index ce846e22..9213792c 100644 --- a/src/components/Project/VotingList/VotingList.tsx +++ b/src/components/Project/VotingList/VotingList.tsx @@ -1,4 +1,4 @@ -import { BottomNavigation, BottomNavigationAction, Dialog, DialogTitle } from '@mui/material'; +import { BottomNavigation, BottomNavigationAction, Dialog, DialogTitle, useMediaQuery } from '@mui/material'; import Image from 'next/image'; import React, { useEffect, useState } from 'react'; @@ -30,92 +30,6 @@ const VotingList = (props: VotingListProps) => { const { setOpenVotingList, open, votes, loading } = props const [value, setValue] = React.useState(0); const [list, setList] = useState([]); - // const [votes, setVotes] = useState([]) - // const { user, loading: userLoading } = useSelector( - // (state: RootState) => state.userState - // ); - // const [loading, setLoading] = useState(false); - - // const firstPendingMilestone = props?.firstPendingMilestone >= 0 ? props?.firstPendingMilestone : project?.milestones?.length - 1 - - // useEffect(() => { - // const syncVotes = async () => { - // if (!chainProjectId || !projectId || firstPendingMilestone === undefined) return - - // const imbueApi = await initImbueAPIInfo(); - // const chainService = new ChainService(imbueApi, user); - // const milestoneVotes: any = await chainService.getMilestoneVotes( - // chainProjectId, - // firstPendingMilestone - // ); - - // const votesArray = Object.keys(milestoneVotes) - - // if (votesArray.length > 0) { - // const votes: MilestoneVotes[] = votesArray?.map((key: any) => ({ - // voterAddress: key, - // vote: milestoneVotes[key], - // })) || []; - - // const promises = votes.map(async (v) => await voteOnMilestone(null, v.voterAddress, firstPendingMilestone, v.vote, projectId)) - // await Promise.all(promises) - // const voteResp = await getMillestoneVotes(projectId, firstPendingMilestone) - // setVotes(voteResp) - // setMilestoneVotes(voteResp?.allVoters) - // // const resp = await syncProjectVotes(projectId, firstPendingMilestone, votes) - // } - // } - - // const setVotingList = async () => { - // if (!projectId || firstPendingMilestone === undefined) return - - // setLoading(true) - // try { - // const voteResp = await getMillestoneVotes(projectId, firstPendingMilestone) - // setVotes(voteResp) - // setMilestoneVotes(voteResp?.allVoters) - // // const votersAddressed = voteResp?.map((voter: any) => voter.web3_address) - // syncVotes(); - // } catch (error) { - // // eslint-disable-next-line no-console - // console.error(error); - // } finally { - // setLoading(false) - // } - // } - - // setVotingList() - // }, [user, firstPendingMilestone, setMilestoneVotes, projectId, approvers, chainProjectId]) - - // useEffect(() => { - // const votedYes: User[] = [] - // const votedNo: User[] = [] - // const pendingVote: User[] = [] - - // approvers?.length && approvers?.forEach((approver) => { - // const match = votes.find((voter) => voter?.voterAddress === approver.web3_address) - // if (!approver.web3_address) return - - // if (match) { - // if (match?.vote) votedYes.push(approver) - // else if (!match?.vote) votedNo.push(approver) - // } - // else pendingVote.push(approver) - // }) - - // switch (value) { - // case 0: - // setList(votedYes) - // break; - // case 1: - // setList(votedNo) - // break; - // case 2: - // setList(pendingVote) - // break; - // } - - // }, [value, approvers, user, chainProjectId, firstPendingMilestone, votes]) useEffect(() => { switch (value) { @@ -132,6 +46,7 @@ const VotingList = (props: VotingListProps) => { }, [value, votes]) + const mobileView = useMediaQuery('(max-width:480px)'); return (

{ onClose={() => setOpenVotingList(false)} aria-labelledby='alert-dialog-title' aria-describedby='alert-dialog-description' - className='p-14 errorDialogue' + className='px-2 lg:p-14 errorDialogue' > { (loading) @@ -177,10 +92,20 @@ const VotingList = (props: VotingListProps) => { approver?.display_name &&

{approver.display_name}

} -

{approver?.web3_address || approver?.approver}

+

+ { + !mobileView + ? approver?.web3_address + : ( + approver?.web3_address?.substring(0, 8) + + '...' + + approver?.web3_address?.substring(39) + ) + } +

-

+

{value === 0 && "Yes"} {value === 1 && "No"} {value === 2 && "Pending"} diff --git a/src/components/Review/ReviewModal.tsx b/src/components/Review/ReviewModal.tsx index df9c9ad7..16e9a303 100644 --- a/src/components/Review/ReviewModal.tsx +++ b/src/components/Review/ReviewModal.tsx @@ -98,7 +98,7 @@ const ReviewFormModal = ({ targetUser, project, setShowLoginPopup, setLoading, s onClose={handleClose} aria-labelledby='alert-dialog-title' aria-describedby='alert-dialog-description' - className='p-14 errorDialogue min-h-fit' + className='px-2 lg:p-14 errorDialogue' >

@@ -147,9 +147,9 @@ const ReviewFormModal = ({ targetUser, project, setShowLoginPopup, setLoading, s value={title || ""} /> */} -

Project Title: {action === 'post' ? project?.name : review?.title}

+

Project Title: {action === 'post' ? project?.name : review?.title}

-

Description (optional)

+

Description (optional)

{/* setDescription(e.target.value) diff --git a/src/components/SuccessScreen.tsx b/src/components/SuccessScreen.tsx index 38f7a999..344ab5b6 100644 --- a/src/components/SuccessScreen.tsx +++ b/src/components/SuccessScreen.tsx @@ -23,7 +23,7 @@ const SuccessScreen = (props: SuccessScreenProps) => { onClose={handleClose} aria-labelledby='alert-dialog-title' aria-describedby='alert-dialog-description' - className='p-14 errorDialogue' + className='p-2 lg:p-14 errorDialogue' >
diff --git a/src/components/ValidatableInput.tsx b/src/components/ValidatableInput.tsx index 0428ed6f..c4da4252 100644 --- a/src/components/ValidatableInput.tsx +++ b/src/components/ValidatableInput.tsx @@ -45,15 +45,17 @@ const ValidatableInput = (props: any) => { {...props} inputProps={{ maxLength, + className: "text-xs md:text-base" }} + InputProps={{ className: "p-2 md:p-4" }} onChange={(e) => handleInput(e)} className={'w-full !mb-0'} multiline color='secondary' autoComplete='off' /> -
-

+

+

{error}

{!hideLimit && ( diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 0d811166..8679c26a 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -14,7 +14,6 @@ import '@/styles/stream-chat.css'; import '@/styles/animation.css'; const Layout = dynamic(() => import("@/components/Layout")); - export default function App({ Component, pageProps }: AppProps) { return ( @@ -22,9 +21,9 @@ export default function App({ Component, pageProps }: AppProps) { Imbue - - - + + + ); } diff --git a/src/pages/briefs/[id].tsx b/src/pages/briefs/[id].tsx index 32089c62..2eb2a34f 100644 --- a/src/pages/briefs/[id].tsx +++ b/src/pages/briefs/[id].tsx @@ -2,22 +2,18 @@ import TimeAgo from 'javascript-time-ago'; import en from 'javascript-time-ago/locale/en'; import { useRouter } from 'next/router'; -import React, { useContext, useEffect, useState } from 'react'; +import { useContext, useEffect, useState } from 'react'; import { useSelector } from 'react-redux'; import BioInsights from '@/components/Briefs/BioInsights'; import BioPanel from '@/components/Briefs/BioPanel'; import ClientsHistory from '@/components/Briefs/ClientHistory'; import SimilarProjects from '@/components/Briefs/SimilarBriefs'; -import ErrorScreen from '@/components/ErrorScreen'; -import { LoginPopupContext, LoginPopupContextType } from '@/components/Layout'; +import { AppContext, AppContextType } from '@/components/Layout'; import SuccessScreen from '@/components/SuccessScreen'; import { Brief, Freelancer, User } from '@/model'; -import { - getBrief, - getUserBriefs, -} from '@/redux/services/briefService'; +import { getBrief, getUserBriefs } from '@/redux/services/briefService'; import { getFreelancerProfile } from '@/redux/services/freelancerService'; import { RootState } from '@/redux/store/store'; @@ -66,11 +62,10 @@ const BriefDetails = (): JSX.Element => { const [allClientBriefs, setAllClientBriefs] = useState(); // const [showLoginPopup, setShowLoginPopup] = useState(false); - const { setShowLoginPopup } = useContext( - LoginPopupContext - ) as LoginPopupContextType; + const { setShowLoginPopup, setError } = useContext( + AppContext + ) as AppContextType; const isOwnerOfBrief = browsingUser && browsingUser.id == brief.user_id; - const [error, setError] = useState(); const projectCategories = brief?.industries?.map?.((item) => item?.name); @@ -79,7 +74,7 @@ const BriefDetails = (): JSX.Element => { const id: number = Number(query?.id) || 0; const fetchData = async () => { - if (!id) return + if (!id) return; try { const briefData: Brief | Error | undefined = await getBrief(id); @@ -87,12 +82,10 @@ const BriefDetails = (): JSX.Element => { const targetUserRes = await fetchUser(briefData.user_id); setTargetUser(targetUserRes); setBrief(briefData); - const freelancer = await getFreelancerProfile( - browsingUser?.username - ); + const freelancer = await getFreelancerProfile(browsingUser?.username); const allClientBriefsRes = await getUserBriefs(briefData.user_id); - setAllClientBriefs(allClientBriefsRes) + setAllClientBriefs(allClientBriefsRes); setFreelancer(freelancer); } else { @@ -102,7 +95,7 @@ const BriefDetails = (): JSX.Element => { } catch (error) { setError({ message: error }); } finally { - setLoading(false) + setLoading(false); } }; @@ -158,24 +151,12 @@ const BriefDetails = (): JSX.Element => { loadingMain={loading} />
- + - -
- - -
-
@@ -189,12 +170,6 @@ const BriefDetails = (): JSX.Element => {
- - {/* */}
); }; diff --git a/src/pages/briefs/index.tsx b/src/pages/briefs/index.tsx index 20fd86a8..f7763835 100644 --- a/src/pages/briefs/index.tsx +++ b/src/pages/briefs/index.tsx @@ -15,15 +15,15 @@ import en from 'javascript-time-ago/locale/en'; import dynamic from 'next/dynamic'; import Image from 'next/image'; import { useRouter } from 'next/router'; -import React, { useEffect, useMemo, useState } from 'react'; +import React, { useContext, useEffect, useMemo, useState } from 'react'; import { IoTrashBin } from 'react-icons/io5'; import { useSelector } from 'react-redux'; import { Project } from '@/lib/models'; import { strToIntRange } from '@/utils/helper'; -import ErrorScreen from '@/components/ErrorScreen'; import FilterModal from '@/components/Filter/FilterModal'; +import { AppContext, AppContextType } from '@/components/Layout'; import BackDropLoader from '@/components/LoadingScreen/BackDropLoader'; import { @@ -76,7 +76,8 @@ const Briefs = (): JSX.Element => { const [skills, setSkills] = useState([{ name: '', id: 0 }]); const [myApplications, _setMyApplications] = useState(); - const [error, setError] = useState(); + + const { setError } = useContext(AppContext) as AppContextType const { expRange, @@ -763,7 +764,7 @@ const Briefs = (): JSX.Element => { } /> */} -
+
{
{`${item.experience_level}, ${item.duration}, Posted by ${item.created_by}`}
-
+

{item.description.length > 500 ? `${item.description.substring(0, 500)}...` : item.description} -

+

{item.skills.map((skill: any, skillIndex: any) => ( @@ -924,7 +925,7 @@ const Briefs = (): JSX.Element => {
)} -
+
Proposals Submitted:{' '} @@ -934,7 +935,7 @@ const Briefs = (): JSX.Element => {
-
+
{timeAgo.format(new Date(item?.created))}
@@ -944,26 +945,9 @@ const Briefs = (): JSX.Element => {
- -
- - -
-
-
-
+
{/* starting of the box sections */} -
-
-
-

Projects

-

router.push('/briefs/mybriefs')} - className='bg-imbue-purple px-7 py-2 text-white text-sm rounded-full cursor-pointer' - > - View all -

-
-
-
-
-

Briefs

-
-

- {Briefs?.briefsUnderReview?.length || 0} + +

+
+
+

Projects

+ +

router.push('/briefs/mybriefs')} + className='bg-imbue-purple px-7 py-2 text-white text-sm rounded-full cursor-pointer' + > + View all

-
-
-
-
-

Projects

-
-

- {Briefs?.acceptedBriefs?.length || 0} -

+
+
+
+

Briefs

+
+

+ {Briefs?.briefsUnderReview?.length || 0} +

+
-
-
-
-
-

Grants

-
-

- {Grants?.length || 0} -

+
+
+
+

Projects

+
+

+ {Briefs?.acceptedBriefs?.length || 0} +

+
+
+
+
+
+

Grants

+
+

+ {Grants?.length || 0} +

+
-
-
+
+ +

Grants

-
+
setOpenedOption((prev) => !prev)} @@ -230,14 +262,13 @@ export default function ClientDashboard() {
{options.map((option, index) => (
{ setFilterGrantoptions(option); setOpenedOption(false); @@ -251,52 +282,64 @@ export default function ClientDashboard() {
-
-
-

- {filteredGrants.length} -

+
+

+ {filteredGrants.length} +

+

{filterGrantoptions.name} Grants

+

+ View all +

-

- View all -

-
-
-

Total Spent

-
-
-
-
+ +
+
+
+

Total Spent

+
+
+

{totalSpent}

-

Payout Accounts

+
+

Payout Accounts

+

router.push('/relay')} + className='bg-imbue-purple px-4 lg:px-7 items-center py-1 lg:py-2 text-white text-sm rounded-full cursor-pointer' + > + Get Started +

+
-

router.push('/relay')} - className='bg-imbue-purple px-7 py-2 text-white text-sm rounded-full cursor-pointer' - > - Get Started -

-
-
+ + {/* ending of the box sections */} +
{/* Freelancer recomendations */} -
+
diff --git a/src/pages/dashboard/new.tsx b/src/pages/dashboard/components/FreelancerDashboard.tsx similarity index 71% rename from src/pages/dashboard/new.tsx rename to src/pages/dashboard/components/FreelancerDashboard.tsx index 97ea4347..a3bfca7a 100644 --- a/src/pages/dashboard/new.tsx +++ b/src/pages/dashboard/components/FreelancerDashboard.tsx @@ -1,12 +1,15 @@ /* eslint-disable unused-imports/no-unused-vars */ import { Badge } from '@mui/material'; import { useRouter } from 'next/router'; -import React, { useContext, useEffect, useMemo, useState } from 'react'; +import React, { useContext, useEffect, useMemo, useRef, useState } from 'react'; import { BiChevronDown, BiRightArrowAlt } from 'react-icons/bi'; import { MdOutlineAttachMoney } from 'react-icons/md'; import { useDispatch, useSelector } from 'react-redux'; +import Slider from 'react-slick'; import { Channel, DefaultGenerics } from 'stream-chat'; import 'stream-chat-react/dist/css/v2/index.css'; +import 'slick-carousel/slick/slick.css'; +import 'slick-carousel/slick/slick-theme.css'; import { fetchUser } from '@/utils'; @@ -26,7 +29,6 @@ import { } from '@/redux/services/freelancerService'; import { setUnreadMessage } from '@/redux/slices/userSlice'; import { RootState } from '@/redux/store/store'; - export type DashboardProps = { user: User; isAuthenticated: boolean; @@ -244,6 +246,29 @@ const FreelancerDashboard = (): JSX.Element => { if (user?.id) getProjects(); }, [selectedOption.status_id, user?.id]); + const sliderRef = useRef(null); + + const settings = { + dots: true, + infinite: false, + arrows: false, + slidesToShow: 3, + slidesToScroll: 3, + dotsClass: 'dashboard_slider', + responsive: [ + { + breakpoint: 860, + settings: { + slidesToShow: 1, + slidesToScroll: 1, + dots: true, + variableWidth: true, + } + }, + ] + } + + if (loadingStreamChat || loadingUser) return ; return client ? ( @@ -257,63 +282,66 @@ const FreelancerDashboard = (): JSX.Element => {

{/* starting of the box sections */} -
-
-
-

Projects

- -

router.push('/projects/myprojects')} - > - View all -

-
-
-
-
-

Completed Projects

-
-

- {completedProjects?.length || 0} + + +

+
+
+

Projects

+

router.push('/projects/myprojects')} + > + View all

-
-
-
-
-

Active Projects

-
-

- {activeProjects?.length || 0} -

+
+
+
+

Completed Projects

+
+

+ {completedProjects?.length || 0} +

+
-
-
-
-
-

Pending Projects

-
-

- {pendingProjects?.length || 0} -

+
+
+
+

Active Projects

+
+

+ {activeProjects?.length || 0} +

+
-
-
-
-
-

Grants

-
-

- {grants?.length || 0} -

+
+
+
+

Pending Projects

+
+

+ {pendingProjects?.length || 0} +

+
+
+
+
+
+

Grants

+
+

+ {grants?.length || 0} +

+
-
-
+ +
+

Briefs

-
+
setOpenedOption((prev) => !prev)} @@ -328,14 +356,13 @@ const FreelancerDashboard = (): JSX.Element => {
{options.map((option, index) => (
{ setSelectedOption(option); setOpenedOption(false); @@ -351,10 +378,10 @@ const FreelancerDashboard = (): JSX.Element => {

- {filteredApplications?.length} + {filteredApplications?.length || 0}

-
+

{selectedOption.name} brief

{
-
-
-

Total Earnings

-
router.push('/relay')} - className='px-3 py-0.5 border cursor-pointer text-black border-text-aux-colour rounded-full' - > - + +
+
+
+

Total Earnings

+
router.push('/relay')} + className='px-3 ml-3 py-0.5 border cursor-pointer text-black border-text-aux-colour rounded-full' + > + +
-
-
-
- -

{totalEarnings}

+
+
+ +

{totalEarnings}

+
+

Payout Accounts

-

Payout Accounts

-
+ {/* ending of the box sections */} -
+ +
-
+
{/* Starting of graph */}
diff --git a/src/components/WelcomeModalContent/WelcomeForNewUser.tsx b/src/pages/dashboard/components/WelcomeForNewUser.tsx similarity index 100% rename from src/components/WelcomeModalContent/WelcomeForNewUser.tsx rename to src/pages/dashboard/components/WelcomeForNewUser.tsx diff --git a/src/pages/dashboard/index.tsx b/src/pages/dashboard/index.tsx index 180e3aca..7c262f53 100644 --- a/src/pages/dashboard/index.tsx +++ b/src/pages/dashboard/index.tsx @@ -1,6 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ -/* eslint-disable react-hooks/exhaustive-deps */ - import dynamic from 'next/dynamic'; import { useRouter } from 'next/router'; import React, { useContext, useEffect, useState } from 'react'; @@ -16,15 +13,15 @@ const LoginPopup = dynamic(() => import('@/components/LoginPopup/LoginPopup')); import { Modal } from '@mui/material'; import { AppContext, AppContextType } from '@/components/Layout'; -import WelcomForNewUser from '@/components/WelcomeModalContent/WelcomeForNewUser'; import { Project, User } from '@/model'; import { Brief } from '@/model'; import { setUnreadMessage } from '@/redux/slices/userSlice'; import { RootState } from '@/redux/store/store'; -import ClientDashboard from './ClientDashboard'; -import FreelancerDashboard from './new'; +import ClientDashboard from './components/ClientDashboard'; +import FreelancerDashboard from './components/FreelancerDashboard'; +import WelcomForNewUser from './components/WelcomeForNewUser'; export type DashboardProps = { user: User; @@ -68,7 +65,7 @@ const Dashboard = (): JSX.Element => { } }; setupStreamChat(); - }, [user]); + }, [loadingUser, router, user?.username]); useEffect(() => { if (client && user?.username && !loadingStreamChat) { diff --git a/src/pages/freelancers/index.tsx b/src/pages/freelancers/index.tsx index 6f52c9e6..a88ef8ee 100644 --- a/src/pages/freelancers/index.tsx +++ b/src/pages/freelancers/index.tsx @@ -1,5 +1,3 @@ -/* eslint-disable no-console */ -/* eslint-disable react-hooks/exhaustive-deps */ import VerifiedRoundedIcon from '@mui/icons-material/VerifiedRounded'; import Grid from '@mui/material/Grid'; const TextField = dynamic(() => import("@mui/material/TextField"), { @@ -15,12 +13,12 @@ const ClearIcon = dynamic(() => import("@mui/icons-material/Clear"), { import dynamic from 'next/dynamic'; import Image from 'next/image'; import { useRouter } from 'next/router'; -import React, { useEffect, useState } from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import { strToIntRange } from '@/utils/helper'; -import ErrorScreen from '@/components/ErrorScreen'; import FilterModal from '@/components/Filter/FilterModal'; +import { AppContext, AppContextType } from '@/components/Layout'; import { chevLeftIcon, @@ -44,8 +42,6 @@ import { } from '../../redux/services/freelancerService'; import { FreelancerFilterOption } from '../../types/freelancerTypes'; - - const Freelancers = (): JSX.Element => { const [freelancers, setFreelancers] = useState< Freelancer[] | undefined | any @@ -62,9 +58,10 @@ const Freelancers = (): JSX.Element => { const [pageInput, setPageInput] = useState(1); const [selectedFilterIds, setSlectedFilterIds] = useState>([]); - const [error, setError] = useState() const router = useRouter(); + const { setError } = useContext(AppContext) as AppContextType + const { skillsRangeProps, servicesRangeProps, @@ -457,9 +454,10 @@ const Freelancers = (): JSX.Element => { } if (loading) return ; + return (
-
+
toggleFilter()} { ...{ selectedFilterIds, handleSetId, cancelFilters, customDropdownConfigs, onSearch } @@ -468,7 +466,7 @@ const Freelancers = (): JSX.Element => {
-
+
@@ -499,7 +497,7 @@ const Freelancers = (): JSX.Element => { } /> */} -
+
{ )} -
+
- -
- - -
-
); }; diff --git a/src/pages/freelancers/new.tsx b/src/pages/freelancers/new.tsx index 7c198112..5934db1d 100644 --- a/src/pages/freelancers/new.tsx +++ b/src/pages/freelancers/new.tsx @@ -122,7 +122,7 @@ const Freelancer = (): JSX.Element => { const FreelanceExperience = (
-
+
{stepData[step].content .split('\n') .map((line: string, index: number) => ( @@ -150,7 +150,7 @@ const Freelancer = (): JSX.Element => { const FreelancerHourPerRate = (
-
+
{stepData[step].content.split('\n').map((line, index) => (

{line}

))} @@ -178,7 +178,7 @@ const Freelancer = (): JSX.Element => { const FreelancingGoal = (
-
+
{stepData[step].content.split('\n').map((line, index) => (

{line}

))} @@ -203,7 +203,7 @@ const Freelancer = (): JSX.Element => { // const ImportResume = ( // // TODO: //
- //
+ //
// {stepData[step].content.split('\n').map((line, index) => ( //

{line}

// ))} @@ -226,7 +226,7 @@ const Freelancer = (): JSX.Element => { const TitlePanel = (
-
+
{stepData[step].content.split('\n').map((line, index) => (

{line}

))} @@ -247,7 +247,7 @@ const Freelancer = (): JSX.Element => { // const ExperiencePanel = ( //
- //
+ //
// {stepData[step].content.split('\n').map((line, index) => ( //

{line}

// ))} @@ -257,7 +257,7 @@ const Freelancer = (): JSX.Element => { const EducationPanel = (
-
+
{stepData[step].content.split('\n').map((line, index) => (

{line}

))} @@ -282,7 +282,7 @@ const Freelancer = (): JSX.Element => { const LanguagePanel = (
-
+
{stepData[step].content.split('\n').map((line, index) => (

{line}

))} @@ -313,7 +313,7 @@ const Freelancer = (): JSX.Element => { const SkillsPanel = (
-
+
{stepData[step].content.split('\n').map((line, index) => (

{line}

))} @@ -349,7 +349,7 @@ const Freelancer = (): JSX.Element => { const BioPanel = (
-
+
{stepData[step].content.split('\n').map((line, index) => (

{line}

))} @@ -375,7 +375,7 @@ const Freelancer = (): JSX.Element => { const ServicesPanel = (
-
+
{stepData[step].content.split('\n').map((line, index) => (

{line}

))} @@ -532,9 +532,9 @@ const Freelancer = (): JSX.Element => {
-

+

{stepData[step].heading.replace('{name}', displayName)} -

+

{panels[step] ?? <>}
@@ -549,7 +549,7 @@ const Freelancer = (): JSX.Element => { {step === 0 ? (
@@ -613,7 +612,12 @@ const GrantApplication = (): JSX.Element => {

Payment Address:

{accounts.length == 0 ? ( - + ) : (