[offers][feat] save to user profile (#462)

pull/464/head
Zhang Ziqing 2 years ago committed by GitHub
parent 3ecc756052
commit e152de2284
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -9,7 +9,9 @@ import { Bars3BottomLeftIcon } from '@heroicons/react/24/outline';
import GlobalNavigation from '~/components/global/GlobalNavigation'; import GlobalNavigation from '~/components/global/GlobalNavigation';
import HomeNavigation from '~/components/global/HomeNavigation'; import HomeNavigation from '~/components/global/HomeNavigation';
import OffersNavigation from '~/components/offers/OffersNavigation'; import OffersNavigation, {
OffersNavigationAuthenticated,
} from '~/components/offers/OffersNavigation';
import QuestionsNavigation from '~/components/questions/QuestionsNavigation'; import QuestionsNavigation from '~/components/questions/QuestionsNavigation';
import ResumesNavigation from '~/components/resumes/ResumesNavigation'; import ResumesNavigation from '~/components/resumes/ResumesNavigation';
@ -105,6 +107,7 @@ function ProfileJewel() {
export default function AppShell({ children }: Props) { export default function AppShell({ children }: Props) {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false); const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const router = useRouter(); const router = useRouter();
const { data: session } = useSession();
const currentProductNavigation: Readonly<{ const currentProductNavigation: Readonly<{
googleAnalyticsMeasurementID: string; googleAnalyticsMeasurementID: string;
@ -120,8 +123,11 @@ export default function AppShell({ children }: Props) {
} }
if (path.startsWith('/offers')) { if (path.startsWith('/offers')) {
if (session == null) {
return OffersNavigation; return OffersNavigation;
} }
return OffersNavigationAuthenticated;
}
if (path.startsWith('/questions')) { if (path.startsWith('/questions')) {
return QuestionsNavigation; return QuestionsNavigation;

@ -5,6 +5,12 @@ const navigation: ProductNavigationItems = [
{ href: '/offers/features', name: 'Features' }, { href: '/offers/features', name: 'Features' },
]; ];
const navigationAuthenticated: ProductNavigationItems = [
{ href: '/offers/submit', name: 'Analyze your offers' },
{ href: '/offers/dashboard', name: 'Your repository' },
{ href: '/offers/features', name: 'Features' },
];
const config = { const config = {
// TODO: Change this to your own GA4 measurement ID. // TODO: Change this to your own GA4 measurement ID.
googleAnalyticsMeasurementID: 'G-34XRGLEVCF', googleAnalyticsMeasurementID: 'G-34XRGLEVCF',
@ -17,4 +23,9 @@ const config = {
titleHref: '/offers', titleHref: '/offers',
}; };
export const OffersNavigationAuthenticated = {
...config,
navigation: navigationAuthenticated,
};
export default config; export default config;

@ -0,0 +1,55 @@
import { JobType } from '@prisma/client';
import { HorizontalDivider } from '@tih/ui';
import type { JobTitleType } from '~/components/shared/JobTitles';
import { getLabelForJobTitleType } from '~/components/shared/JobTitles';
import { convertMoneyToString } from '~/utils/offers/currency';
import { formatDate } from '~/utils/offers/time';
import type { UserProfileOffer } from '~/types/offers';
type Props = Readonly<{
disableTopDivider?: boolean;
offer: UserProfileOffer;
}>;
export default function DashboardProfileCard({
disableTopDivider,
offer: {
company,
income,
jobType,
level,
location,
monthYearReceived,
title,
},
}: Props) {
return (
<>
{!disableTopDivider && <HorizontalDivider />}
<div className="flex items-end justify-between">
<div className="col-span-1 row-span-3">
<p className="font-bold">
{getLabelForJobTitleType(title as JobTitleType)}
</p>
<p>
{location
? `Company: ${company.name}, ${location}`
: `Company: ${company.name}`}
</p>
{level && <p>Level: {level}</p>}
</div>
<div className="col-span-1 row-span-3">
<p className="text-end">{formatDate(monthYearReceived)}</p>
<p className="text-end text-xl">
{jobType === JobType.FULLTIME
? `${convertMoneyToString(income)} / year`
: `${convertMoneyToString(income)} / month`}
</p>
</div>
</div>
</>
);
}

@ -0,0 +1,105 @@
import { useRouter } from 'next/router';
import { ArrowRightIcon, XMarkIcon } from '@heroicons/react/24/outline';
import { Button, useToast } from '@tih/ui';
import DashboardOfferCard from '~/components/offers/dashboard/DashboardOfferCard';
import { formatDate } from '~/utils/offers/time';
import { trpc } from '~/utils/trpc';
import ProfilePhotoHolder from '../profile/ProfilePhotoHolder';
import type { UserProfile, UserProfileOffer } from '~/types/offers';
type Props = Readonly<{
profile: UserProfile;
}>;
export default function DashboardProfileCard({
profile: { createdAt, id, offers, profileName, token },
}: Props) {
const { showToast } = useToast();
const router = useRouter();
const trpcContext = trpc.useContext();
const PROFILE_URL = `/offers/profile/${id}?token=${token}`;
const removeSavedProfileMutation = trpc.useMutation(
'offers.user.profile.removeFromUserProfile',
{
onError: () => {
showToast({
title: `Server error.`,
variant: 'failure',
});
},
onSuccess: () => {
trpcContext.invalidateQueries(['offers.user.profile.getUserProfiles']);
showToast({
title: `Profile removed from your dashboard successfully!`,
variant: 'success',
});
},
},
);
function handleRemoveProfile() {
removeSavedProfileMutation.mutate({ profileId: id });
}
return (
<div className="space-y-4 bg-white px-4 pt-5 sm:px-4">
{/* Header */}
<div className="-ml-4 -mt-2 flex flex-wrap items-center justify-between border-b border-gray-300 pb-4 sm:flex-nowrap">
<div className="flex items-center gap-x-5">
<div>
<ProfilePhotoHolder size="sm" />
</div>
<div className="col-span-10">
<p className="text-xl font-bold">{profileName}</p>
<div className="flex flex-row">
<span>Created at {formatDate(createdAt)}</span>
</div>
</div>
</div>
<div className="flex self-start">
<Button
disabled={removeSavedProfileMutation.isLoading}
icon={XMarkIcon}
isLabelHidden={true}
label="Remove Profile"
size="md"
variant="tertiary"
onClick={handleRemoveProfile}
/>
</div>
</div>
{/* Offers */}
<div>
{offers.map((offer: UserProfileOffer, index) =>
index === 0 ? (
<DashboardOfferCard
key={offer.id}
disableTopDivider={true}
offer={offer}
/>
) : (
<DashboardOfferCard key={offer.id} offer={offer} />
),
)}
</div>
<div className="flex justify-end pt-1">
<Button
disabled={removeSavedProfileMutation.isLoading}
icon={ArrowRightIcon}
isLabelHidden={false}
label="Read full profile"
size="md"
variant="secondary"
onClick={() => router.push(PROFILE_URL)}
/>
</div>
</div>
);
}

@ -10,12 +10,15 @@ import {
} from '@tih/ui'; } from '@tih/ui';
import ExpandableCommentCard from '~/components/offers/profile/comments/ExpandableCommentCard'; import ExpandableCommentCard from '~/components/offers/profile/comments/ExpandableCommentCard';
import Tooltip from '~/components/offers/util/Tooltip';
import { copyProfileLink } from '~/utils/offers/link'; import { copyProfileLink } from '~/utils/offers/link';
import { trpc } from '~/utils/trpc'; import { trpc } from '~/utils/trpc';
import type { OffersDiscussion, Reply } from '~/types/offers'; import type { OffersDiscussion, Reply } from '~/types/offers';
import 'react-popper-tooltip/dist/styles.css';
type ProfileHeaderProps = Readonly<{ type ProfileHeaderProps = Readonly<{
isDisabled: boolean; isDisabled: boolean;
isEditable: boolean; isEditable: boolean;
@ -107,6 +110,7 @@ export default function ProfileComments({
<div className="m-4 h-full"> <div className="m-4 h-full">
<div className="flex-end flex justify-end space-x-4"> <div className="flex-end flex justify-end space-x-4">
{isEditable && ( {isEditable && (
<Tooltip tooltipContent="Copy this link to edit your profile later">
<Button <Button
addonPosition="start" addonPosition="start"
disabled={isDisabled} disabled={isDisabled}
@ -123,7 +127,9 @@ export default function ProfileComments({
}); });
}} }}
/> />
</Tooltip>
)} )}
<Tooltip tooltipContent="Share this profile with your friends">
<Button <Button
addonPosition="start" addonPosition="start"
disabled={isDisabled} disabled={isDisabled}
@ -140,6 +146,7 @@ export default function ProfileComments({
}); });
}} }}
/> />
</Tooltip>
</div> </div>
<h2 className="mt-2 mb-6 text-2xl font-bold">Discussions</h2> <h2 className="mt-2 mb-6 text-2xl font-bold">Discussions</h2>
{isEditable || session?.user?.name ? ( {isEditable || session?.user?.name ? (

@ -1,27 +1,32 @@
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { useState } from 'react'; import { useState } from 'react';
import { import {
BookmarkIcon as BookmarkIconOutline,
BuildingOffice2Icon, BuildingOffice2Icon,
CalendarDaysIcon, CalendarDaysIcon,
PencilSquareIcon, PencilSquareIcon,
TrashIcon, TrashIcon,
} from '@heroicons/react/24/outline'; } from '@heroicons/react/24/outline';
import { Button, Dialog, Spinner, Tabs } from '@tih/ui'; import { BookmarkIcon as BookmarkIconSolid } from '@heroicons/react/24/solid';
import { Button, Dialog, Spinner, Tabs, useToast } from '@tih/ui';
import ProfilePhotoHolder from '~/components/offers/profile/ProfilePhotoHolder'; import ProfilePhotoHolder from '~/components/offers/profile/ProfilePhotoHolder';
import type { BackgroundDisplayData } from '~/components/offers/types'; import type { BackgroundDisplayData } from '~/components/offers/types';
import { JobTypeLabel } from '~/components/offers/types'; import { JobTypeLabel } from '~/components/offers/types';
import { getProfileEditPath } from '~/utils/offers/link'; import { getProfileEditPath } from '~/utils/offers/link';
import { trpc } from '~/utils/trpc';
import type { ProfileDetailTab } from '../constants'; import type { ProfileDetailTab } from '../constants';
import { profileDetailTabs } from '../constants'; import { profileDetailTabs } from '../constants';
import Tooltip from '../util/Tooltip';
type ProfileHeaderProps = Readonly<{ type ProfileHeaderProps = Readonly<{
background?: BackgroundDisplayData; background?: BackgroundDisplayData;
handleDelete: () => void; handleDelete: () => void;
isEditable: boolean; isEditable: boolean;
isLoading: boolean; isLoading: boolean;
isSaved?: boolean;
selectedTab: ProfileDetailTab; selectedTab: ProfileDetailTab;
setSelectedTab: (tab: ProfileDetailTab) => void; setSelectedTab: (tab: ProfileDetailTab) => void;
}>; }>;
@ -31,28 +36,91 @@ export default function ProfileHeader({
handleDelete, handleDelete,
isEditable, isEditable,
isLoading, isLoading,
isSaved = false,
selectedTab, selectedTab,
setSelectedTab, setSelectedTab,
}: ProfileHeaderProps) { }: ProfileHeaderProps) {
const [isDialogOpen, setIsDialogOpen] = useState(false); const [isDialogOpen, setIsDialogOpen] = useState(false);
// Const [saved, setSaved] = useState(isSaved);
const router = useRouter(); const router = useRouter();
const trpcContext = trpc.useContext();
const { offerProfileId = '', token = '' } = router.query; const { offerProfileId = '', token = '' } = router.query;
const { showToast } = useToast();
const handleEditClick = () => { const handleEditClick = () => {
router.push(getProfileEditPath(offerProfileId as string, token as string)); router.push(getProfileEditPath(offerProfileId as string, token as string));
}; };
const saveMutation = trpc.useMutation(
['offers.user.profile.addToUserProfile'],
{
onError: () => {
showToast({
title: `Failed to saved to dashboard!`,
variant: 'failure',
});
},
onSuccess: () => {
// SetSaved(true);
showToast({
title: `Saved to dashboard!`,
variant: 'success',
});
},
},
);
const unsaveMutation = trpc.useMutation(
['offers.user.profile.removeFromUserProfile'],
{
onError: () => {
showToast({
title: `Failed to saved to dashboard!`,
variant: 'failure',
});
},
onSuccess: () => {
// SetSaved(false);
showToast({
title: `Removed from dashboard!`,
variant: 'success',
});
trpcContext.invalidateQueries(['offers.profile.listOne']);
},
},
);
const toggleSaved = () => {
if (isSaved) {
unsaveMutation.mutate({ profileId: offerProfileId as string });
} else {
saveMutation.mutate({
profileId: offerProfileId as string,
token: token as string,
});
}
};
function renderActionList() { function renderActionList() {
return ( return (
<div className="space-x-2"> <div className="flex justify-center space-x-2">
{/* <Button <Tooltip
disabled={isLoading} tooltipContent={
icon={BookmarkSquareIcon} isSaved ? 'Remove from account' : 'Save to your account'
}>
<Button
disabled={
isLoading || saveMutation.isLoading || unsaveMutation.isLoading
}
icon={isSaved ? BookmarkIconSolid : BookmarkIconOutline}
isLabelHidden={true} isLabelHidden={true}
label="Save to user account" isLoading={saveMutation.isLoading || unsaveMutation.isLoading}
label={isSaved ? 'Remove from account' : 'Save to your account'}
size="md" size="md"
variant="tertiary" variant="tertiary"
/> */} onClick={toggleSaved}
/>
</Tooltip>
<Tooltip tooltipContent="Edit profile">
<Button <Button
disabled={isLoading} disabled={isLoading}
icon={PencilSquareIcon} icon={PencilSquareIcon}
@ -62,6 +130,8 @@ export default function ProfileHeader({
variant="tertiary" variant="tertiary"
onClick={handleEditClick} onClick={handleEditClick}
/> />
</Tooltip>
<Tooltip tooltipContent="Delete profile">
<Button <Button
disabled={isLoading} disabled={isLoading}
icon={TrashIcon} icon={TrashIcon}
@ -71,6 +141,7 @@ export default function ProfileHeader({
variant="tertiary" variant="tertiary"
onClick={() => setIsDialogOpen(true)} onClick={() => setIsDialogOpen(true)}
/> />
</Tooltip>
{isDialogOpen && ( {isDialogOpen && (
<Dialog <Dialog
isShown={isDialogOpen} isShown={isDialogOpen}

@ -0,0 +1,42 @@
import type { ReactNode } from 'react';
import { usePopperTooltip } from 'react-popper-tooltip';
import type { Placement } from '@popperjs/core';
type TooltipProps = Readonly<{
children: ReactNode;
placement?: Placement;
tooltipContent: ReactNode;
}>;
export default function Tooltip({
children,
tooltipContent,
placement = 'bottom-start',
}: TooltipProps) {
const {
getTooltipProps,
getArrowProps,
setTooltipRef,
setTriggerRef,
visible,
} = usePopperTooltip({
interactive: true,
placement,
trigger: ['focus', 'hover'],
});
return (
<>
<div ref={setTriggerRef}>{children}</div>
{visible && (
<div
ref={setTooltipRef}
{...getTooltipProps({
className: 'tooltip-container ',
})}>
{tooltipContent}
<div {...getArrowProps({ className: 'tooltip-arrow' })} />
</div>
)}
</>
);
}

@ -35,7 +35,8 @@ import type {
SpecificYoe, SpecificYoe,
UserProfile, UserProfile,
UserProfileOffer, UserProfileOffer,
Valuation} from '~/types/offers'; Valuation,
} from '~/types/offers';
const analysisOfferDtoMapper = ( const analysisOfferDtoMapper = (
offer: OffersOffer & { offer: OffersOffer & {
@ -530,7 +531,7 @@ export const profileDtoMapper = (
user: User | null; user: User | null;
}, },
inputToken: string | undefined, inputToken: string | undefined,
inputUserId: string | null | undefined inputUserId: string | null | undefined,
) => { ) => {
const profileDto: Profile = { const profileDto: Profile = {
analysis: profileAnalysisDtoMapper(profile.analysis), analysis: profileAnalysisDtoMapper(profile.analysis),
@ -547,7 +548,7 @@ export const profileDtoMapper = (
profileDto.editToken = profile.editToken ?? null; profileDto.editToken = profile.editToken ?? null;
profileDto.isEditable = true; profileDto.isEditable = true;
const users = profile.user const users = profile.user;
// TODO: BRYANN UNCOMMENT THIS ONCE U CHANGE THE SCHEMA // TODO: BRYANN UNCOMMENT THIS ONCE U CHANGE THE SCHEMA
// for (let i = 0; i < users.length; i++) { // for (let i = 0; i < users.length; i++) {
@ -558,7 +559,7 @@ export const profileDtoMapper = (
// TODO: REMOVE THIS ONCE U CHANGE THE SCHEMA // TODO: REMOVE THIS ONCE U CHANGE THE SCHEMA
if (users?.id === inputUserId) { if (users?.id === inputUserId) {
profileDto.isSaved = true profileDto.isSaved = true;
} }
} }
@ -645,38 +646,53 @@ export const getOffersResponseMapper = (
return getOffersResponse; return getOffersResponse;
}; };
export const getUserProfileResponseMapper = (res: User & { export const getUserProfileResponseMapper = (
OffersProfile: Array<OffersProfile & { res:
offers: Array<OffersOffer & { | (User & {
OffersProfile: Array<
OffersProfile & {
offers: Array<
OffersOffer & {
company: Company; company: Company;
offersFullTime: (OffersFullTime & { totalCompensation: OffersCurrency }) | null; offersFullTime:
offersIntern: (OffersIntern & { monthlySalary: OffersCurrency }) | null; | (OffersFullTime & { totalCompensation: OffersCurrency })
}>; | null;
}>; offersIntern:
} | null): Array<UserProfile> => { | (OffersIntern & { monthlySalary: OffersCurrency })
| null;
}
>;
}
>;
})
| null,
): Array<UserProfile> => {
if (res) { if (res) {
return res.OffersProfile.map((profile) => { return res.OffersProfile.map((profile) => {
return { return {
createdAt: profile.createdAt, createdAt: profile.createdAt,
id: profile.id, id: profile.id,
offers: profile.offers.map((offer) => { offers: profile.offers.map((offer) => {
return userProfileOfferDtoMapper(offer) return userProfileOfferDtoMapper(offer);
}), }),
profileName: profile.profileName, profileName: profile.profileName,
token: profile.editToken token: profile.editToken,
} };
}) });
} }
return [] return [];
} };
const userProfileOfferDtoMapper = ( const userProfileOfferDtoMapper = (
offer: OffersOffer & { offer: OffersOffer & {
company: Company; company: Company;
offersFullTime: (OffersFullTime & { totalCompensation: OffersCurrency }) | null; offersFullTime:
| (OffersFullTime & { totalCompensation: OffersCurrency })
| null;
offersIntern: (OffersIntern & { monthlySalary: OffersCurrency }) | null; offersIntern: (OffersIntern & { monthlySalary: OffersCurrency }) | null;
}): UserProfileOffer => { },
): UserProfileOffer => {
const mappedOffer: UserProfileOffer = { const mappedOffer: UserProfileOffer = {
company: offersCompanyDtoMapper(offer.company), company: offersCompanyDtoMapper(offer.company),
id: offer.id, id: offer.id,
@ -695,11 +711,10 @@ const userProfileOfferDtoMapper = (
offer.jobType === JobType.FULLTIME offer.jobType === JobType.FULLTIME
? offer.offersFullTime?.title ?? '' ? offer.offersFullTime?.title ?? ''
: offer.offersIntern?.title ?? '', : offer.offersIntern?.title ?? '',
} };
if (offer.offersFullTime?.totalCompensation) { if (offer.offersFullTime?.totalCompensation) {
mappedOffer.income.value = mappedOffer.income.value = offer.offersFullTime.totalCompensation.value;
offer.offersFullTime.totalCompensation.value;
mappedOffer.income.currency = mappedOffer.income.currency =
offer.offersFullTime.totalCompensation.currency; offer.offersFullTime.totalCompensation.currency;
mappedOffer.income.id = offer.offersFullTime.totalCompensation.id; mappedOffer.income.id = offer.offersFullTime.totalCompensation.id;
@ -709,11 +724,9 @@ const userProfileOfferDtoMapper = (
offer.offersFullTime.totalCompensation.baseCurrency; offer.offersFullTime.totalCompensation.baseCurrency;
} else if (offer.offersIntern?.monthlySalary) { } else if (offer.offersIntern?.monthlySalary) {
mappedOffer.income.value = offer.offersIntern.monthlySalary.value; mappedOffer.income.value = offer.offersIntern.monthlySalary.value;
mappedOffer.income.currency = mappedOffer.income.currency = offer.offersIntern.monthlySalary.currency;
offer.offersIntern.monthlySalary.currency;
mappedOffer.income.id = offer.offersIntern.monthlySalary.id; mappedOffer.income.id = offer.offersIntern.monthlySalary.id;
mappedOffer.income.baseValue = mappedOffer.income.baseValue = offer.offersIntern.monthlySalary.baseValue;
offer.offersIntern.monthlySalary.baseValue;
mappedOffer.income.baseCurrency = mappedOffer.income.baseCurrency =
offer.offersIntern.monthlySalary.baseCurrency; offer.offersIntern.monthlySalary.baseCurrency;
} else { } else {
@ -723,5 +736,5 @@ const userProfileOfferDtoMapper = (
}); });
} }
return mappedOffer return mappedOffer;
} };

@ -0,0 +1,95 @@
import { useRouter } from 'next/router';
import { signIn, useSession } from 'next-auth/react';
import { useState } from 'react';
import { Button, Spinner } from '@tih/ui';
import DashboardOfferCard from '~/components/offers/dashboard/DashboardProfileCard';
import { trpc } from '~/utils/trpc';
import type { UserProfile } from '~/types/offers';
export default function ProfilesDashboard() {
const { status } = useSession();
const router = useRouter();
const [userProfiles, setUserProfiles] = useState<Array<UserProfile>>([]);
const userProfilesQuery = trpc.useQuery(
['offers.user.profile.getUserProfiles'],
{
onError: (error) => {
if (error.data?.code === 'UNAUTHORIZED') {
signIn();
}
},
onSuccess: (response: Array<UserProfile>) => {
setUserProfiles(response);
},
},
);
if (status === 'loading' || userProfilesQuery.isLoading) {
return (
<div className="flex h-screen w-screen">
<div className="m-auto mx-auto w-full justify-center">
<Spinner className="m-10" display="block" size="lg" />
</div>
</div>
);
}
if (status === 'unauthenticated') {
signIn();
}
if (userProfiles.length === 0) {
return (
<div className="flex h-screen w-screen">
<div className="m-auto mx-auto w-full justify-center text-xl">
<div className="mb-8 flex w-full flex-row justify-center">
<h2>You have not saved any offer profiles yet.</h2>
</div>
<div className="flex flex-row justify-center">
<Button
label="Submit your offers now!"
size="lg"
variant="primary"
onClick={() => router.push('/offers/submit')}
/>
</div>
</div>
</div>
);
}
return (
<>
{userProfilesQuery.isLoading && (
<div className="flex h-screen w-screen">
<div className="m-auto mx-auto w-full justify-center">
<Spinner className="m-10" display="block" size="lg" />
</div>
</div>
)}
{!userProfilesQuery.isLoading && (
<div className="mt-8 overflow-y-auto">
<h1 className="mx-auto mb-4 w-3/4 text-start text-4xl font-bold text-slate-900">
Your repository
</h1>
<p className="mx-auto w-3/4 text-start text-xl text-slate-900">
Save your offer profiles to respository to easily access and edit
them later.
</p>
<div className="justfy-center mt-8 flex w-screen">
<ul className="mx-auto w-3/4 space-y-3" role="list">
{userProfiles?.map((profile) => (
<li
key={profile.id}
className="overflow-hidden bg-white px-4 py-4 shadow sm:rounded-md sm:px-6">
<DashboardOfferCard key={profile.id} profile={profile} />
</li>
))}
</ul>
</div>
</div>
)}
</>
);
}

@ -188,6 +188,7 @@ export default function OfferProfile() {
handleDelete={handleDelete} handleDelete={handleDelete}
isEditable={isEditable} isEditable={isEditable}
isLoading={getProfileQuery.isLoading} isLoading={getProfileQuery.isLoading}
isSaved={getProfileQuery.data?.isSaved}
selectedTab={selectedTab} selectedTab={selectedTab}
setSelectedTab={setSelectedTab} setSelectedTab={setSelectedTab}
/> />

@ -191,7 +191,7 @@ export type UserProfile = {
offers: Array<UserProfileOffer>; offers: Array<UserProfileOffer>;
profileName: string; profileName: string;
token: string; token: string;
} };
export type UserProfileOffer = { export type UserProfileOffer = {
company: OffersCompany; company: OffersCompany;
@ -202,4 +202,4 @@ export type UserProfileOffer = {
location: string; location: string;
monthYearReceived: Date; monthYearReceived: Date;
title: string; title: string;
} };

Loading…
Cancel
Save