diff --git a/apps/portal/src/components/offers/Breadcrumb.tsx b/apps/portal/src/components/offers/Breadcrumbs.tsx
similarity index 100%
rename from apps/portal/src/components/offers/Breadcrumb.tsx
rename to apps/portal/src/components/offers/Breadcrumbs.tsx
diff --git a/apps/portal/src/components/offers/EducationFields.ts b/apps/portal/src/components/offers/EducationFields.ts
new file mode 100644
index 00000000..6818b9ab
--- /dev/null
+++ b/apps/portal/src/components/offers/EducationFields.ts
@@ -0,0 +1,16 @@
+import { emptyOption } from './constants';
+
+export const EducationFieldLabels = [
+ 'Business Analytics',
+ 'Computer Science',
+ 'Data Science and Analytics',
+ 'Information Security',
+ 'Information Systems',
+];
+
+export const EducationFieldOptions = [emptyOption].concat(
+ EducationFieldLabels.map((label) => ({
+ label,
+ value: label.replace(/\s+/g, '-').toLowerCase(),
+ })),
+);
diff --git a/apps/portal/src/components/offers/EducationLevels.ts b/apps/portal/src/components/offers/EducationLevels.ts
new file mode 100644
index 00000000..176b2519
--- /dev/null
+++ b/apps/portal/src/components/offers/EducationLevels.ts
@@ -0,0 +1,18 @@
+import { emptyOption } from './constants';
+
+export const EducationLevelLabels = [
+ 'Bachelor',
+ 'Diploma',
+ 'Masters',
+ 'PhD',
+ 'Professional',
+ 'Secondary',
+ 'Self-taught',
+];
+
+export const EducationLevelOptions = [emptyOption].concat(
+ EducationLevelLabels.map((label) => ({
+ label,
+ value: label.replace(/\s+/g, '-').toLowerCase(),
+ })),
+);
diff --git a/apps/portal/src/components/offers/InternshipCycles.ts b/apps/portal/src/components/offers/InternshipCycles.ts
new file mode 100644
index 00000000..1f90539f
--- /dev/null
+++ b/apps/portal/src/components/offers/InternshipCycles.ts
@@ -0,0 +1,18 @@
+import { emptyOption } from './constants';
+
+export const InternshipCycleLabels = [
+ 'Spring',
+ 'Summer',
+ 'Fall',
+ 'Winter',
+ 'Half year',
+ 'Full year',
+ 'Others',
+];
+
+export const InternshipCycleOptions = [emptyOption].concat(
+ InternshipCycleLabels.map((label) => ({
+ label,
+ value: label.replace(/\s+/g, '-').toLowerCase(),
+ })),
+);
diff --git a/apps/portal/src/components/offers/JobTypeTabs.tsx b/apps/portal/src/components/offers/JobTypeTabs.tsx
index 8ea87bc6..22d70ea7 100644
--- a/apps/portal/src/components/offers/JobTypeTabs.tsx
+++ b/apps/portal/src/components/offers/JobTypeTabs.tsx
@@ -1,7 +1,7 @@
import clsx from 'clsx';
import { JobType } from '@prisma/client';
-import { JobTypeLabel } from './types';
+import { JobTypeLabel } from '~/components/offers/constants';
type Props = Readonly<{
onChange: (jobType: JobType) => void;
diff --git a/apps/portal/src/components/offers/Years.ts b/apps/portal/src/components/offers/Years.ts
new file mode 100644
index 00000000..da9ab8e3
--- /dev/null
+++ b/apps/portal/src/components/offers/Years.ts
@@ -0,0 +1,8 @@
+const NUM_YEARS = 5;
+export const FutureYearsOptions = Array.from({ length: NUM_YEARS }, (_, i) => {
+ const year = new Date().getFullYear() + i;
+ return {
+ label: String(year),
+ value: year,
+ };
+});
diff --git a/apps/portal/src/components/offers/constants.ts b/apps/portal/src/components/offers/constants.ts
index d49dca1a..e84dfd4e 100644
--- a/apps/portal/src/components/offers/constants.ts
+++ b/apps/portal/src/components/offers/constants.ts
@@ -1,78 +1,14 @@
-import { EducationBackgroundType } from './types';
+export const HOME_URL = '/offers';
-export const emptyOption = '----';
+export const JobTypeLabel = {
+ FULLTIME: 'Full-time',
+ INTERN: 'Internship',
+};
-export const internshipCycleOptions = [
- {
- label: 'Summer',
- value: 'Summer',
- },
- {
- label: 'Winter',
- value: 'Winter',
- },
- {
- label: 'Spring',
- value: 'Spring',
- },
- {
- label: 'Fall',
- value: 'Fall',
- },
- {
- label: 'Full year',
- value: 'Full year',
- },
-];
-
-export const yearOptions = [
- {
- label: '2021',
- value: 2021,
- },
- {
- label: '2022',
- value: 2022,
- },
- {
- label: '2023',
- value: 2023,
- },
- {
- label: '2024',
- value: 2024,
- },
-];
-
-export const educationLevelOptions = Object.entries(
- EducationBackgroundType,
-).map(([, value]) => ({
- label: value,
- value,
-}));
-
-export const educationFieldOptions = [
- {
- label: 'Computer Science',
- value: 'Computer Science',
- },
- {
- label: 'Information Security',
- value: 'Information Security',
- },
- {
- label: 'Information Systems',
- value: 'Information Systems',
- },
- {
- label: 'Business Analytics',
- value: 'Business Analytics',
- },
- {
- label: 'Data Science and Analytics',
- value: 'Data Science and Analytics',
- },
-];
+export const emptyOption = {
+ label: '',
+ value: '',
+};
export enum FieldError {
NON_NEGATIVE_NUMBER = 'Please fill in a non-negative number in this field.',
diff --git a/apps/portal/src/components/offers/dashboard/DashboardOfferCard.tsx b/apps/portal/src/components/offers/dashboard/DashboardOfferCard.tsx
index 722263b6..dda493ec 100644
--- a/apps/portal/src/components/offers/dashboard/DashboardOfferCard.tsx
+++ b/apps/portal/src/components/offers/dashboard/DashboardOfferCard.tsx
@@ -37,28 +37,28 @@ export default function DashboardProfileCard({
{company?.name && (
-
+
{company.name}
)}
{location && (
-
+
{location.cityName}
)}
{level && (
-
+
diff --git a/apps/portal/src/components/offers/features/LeftTextCard.tsx b/apps/portal/src/components/offers/features/LeftTextCard.tsx
index b4fac765..867b24b7 100644
--- a/apps/portal/src/components/offers/features/LeftTextCard.tsx
+++ b/apps/portal/src/components/offers/features/LeftTextCard.tsx
@@ -2,7 +2,7 @@ import type { StaticImageData } from 'next/image';
import Image from 'next/image';
import type { ReactNode } from 'react';
-import { HOME_URL } from '~/components/offers/types';
+import { HOME_URL } from '../constants';
type LeftTextCardProps = Readonly<{
description: string;
diff --git a/apps/portal/src/components/offers/features/RightTextCard.tsx b/apps/portal/src/components/offers/features/RightTextCard.tsx
index 9ca0f949..028dc57b 100644
--- a/apps/portal/src/components/offers/features/RightTextCard.tsx
+++ b/apps/portal/src/components/offers/features/RightTextCard.tsx
@@ -2,7 +2,7 @@ import type { StaticImageData } from 'next/image';
import Image from 'next/image';
import type { ReactNode } from 'react';
-import { HOME_URL } from '~/components/offers/types';
+import { HOME_URL } from '../constants';
type RightTextCarddProps = Readonly<{
description: string;
diff --git a/apps/portal/src/components/offers/offerAnalysis/OfferProfileCard.tsx b/apps/portal/src/components/offers/offerAnalysis/OfferProfileCard.tsx
index bdf9a1b3..4c7ecca3 100644
--- a/apps/portal/src/components/offers/offerAnalysis/OfferProfileCard.tsx
+++ b/apps/portal/src/components/offers/offerAnalysis/OfferProfileCard.tsx
@@ -9,10 +9,11 @@ import { getLabelForJobTitleType } from '~/components/shared/JobTitles';
import { HorizontalDivider } from '~/../../../packages/ui/dist';
import { convertMoneyToString } from '~/utils/offers/currency';
+import { getCompanyDisplayText } from '~/utils/offers/string';
import { formatDate } from '~/utils/offers/time';
+import { JobTypeLabel } from '../constants';
import ProfilePhotoHolder from '../profile/ProfilePhotoHolder';
-import { JobTypeLabel } from '../types';
import type { AnalysisOffer } from '~/types/offers';
@@ -69,7 +70,7 @@ export default function OfferProfileCard({
{getLabelForJobTitleType(title as JobTitleType)}{' '}
{`(${JobTypeLabel[jobType]})`}
-
{`Company: ${company.name}, ${location}`}
+
{`Company: ${getCompanyDisplayText(company.name, location)}`}
{level &&
Level: {level}
}
diff --git a/apps/portal/src/components/offers/offersSubmission/OffersProfileSave.tsx b/apps/portal/src/components/offers/offersSubmission/OffersProfileSave.tsx
index b72641d8..d494912e 100644
--- a/apps/portal/src/components/offers/offersSubmission/OffersProfileSave.tsx
+++ b/apps/portal/src/components/offers/offersSubmission/OffersProfileSave.tsx
@@ -93,7 +93,6 @@ export default function OffersProfileSave({
(null);
const scrollToTop = () =>
@@ -132,13 +133,7 @@ export default function OffersSubmissionForm({
},
);
- const steps = [
- ,
- ,
- ];
+ const steps = [, ];
const breadcrumbSteps: Array = [
{
@@ -157,14 +152,14 @@ export default function OffersSubmissionForm({
},
];
- const goToNextStep = async (currStep: number) => {
- if (currStep === 0) {
+ const setStepWithValidation = async (nextStep: number) => {
+ if (nextStep === 1) {
const result = await trigger('offers');
if (!result) {
return;
}
}
- setStep(step + 1);
+ setStep(nextStep);
};
const mutationpath =
@@ -175,10 +170,24 @@ export default function OffersSubmissionForm({
const createOrUpdateMutation = trpc.useMutation([mutationpath], {
onError(error) {
console.error(error.message);
+ showToast({
+ title:
+ editProfileId && editToken
+ ? 'Error updating offer profile.'
+ : 'Error creating offer profile',
+ variant: 'failure',
+ });
},
onSuccess(data) {
setParams({ profileId: data.id, token: data.token });
setIsSubmitted(true);
+ showToast({
+ title:
+ editProfileId && editToken
+ ? 'Offer profile updated successfully!'
+ : 'Offer profile created successfully!',
+ variant: 'success',
+ });
},
});
@@ -270,7 +279,7 @@ export default function OffersSubmissionForm({
@@ -288,7 +297,7 @@ export default function OffersSubmissionForm({
label="Next"
variant="primary"
onClick={() => {
- goToNextStep(step);
+ setStepWithValidation(step + 1);
gaEvent({
action: 'offers.profile_submission_navigate_next',
category: 'submission',
diff --git a/apps/portal/src/components/offers/offersSubmission/submissionForm/BackgroundForm.tsx b/apps/portal/src/components/offers/offersSubmission/submissionForm/BackgroundForm.tsx
index bfe5ee5f..d951f088 100644
--- a/apps/portal/src/components/offers/offersSubmission/submissionForm/BackgroundForm.tsx
+++ b/apps/portal/src/components/offers/offersSubmission/submissionForm/BackgroundForm.tsx
@@ -2,12 +2,7 @@ import { useFormContext, useWatch } from 'react-hook-form';
import { JobType } from '@prisma/client';
import { Collapsible, RadioList } from '@tih/ui';
-import {
- educationFieldOptions,
- educationLevelOptions,
- emptyOption,
- FieldError,
-} from '~/components/offers/constants';
+import { FieldError } from '~/components/offers/constants';
import type { BackgroundPostData } from '~/components/offers/types';
import CitiesTypeahead from '~/components/shared/CitiesTypeahead';
import CompaniesTypeahead from '~/components/shared/CompaniesTypeahead';
@@ -20,6 +15,8 @@ import {
CURRENCY_OPTIONS,
} from '~/utils/offers/currency/CurrencyEnum';
+import { EducationFieldOptions } from '../../EducationFields';
+import { EducationLevelOptions } from '../../EducationLevels';
import FormRadioList from '../../forms/FormRadioList';
import FormSection from '../../forms/FormSection';
import FormSelect from '../../forms/FormSelect';
@@ -134,6 +131,9 @@ function FullTimeJobFields() {
if (option) {
setValue('background.experiences.0.companyId', option.value);
setValue('background.experiences.0.companyName', option.label);
+ } else {
+ setValue('background.experiences.0.companyId', '');
+ setValue('background.experiences.0.companyName', '');
}
}}
/>
@@ -343,15 +343,13 @@ function EducationSection() {
diff --git a/apps/portal/src/components/offers/offersSubmission/submissionForm/OfferDetailsForm.tsx b/apps/portal/src/components/offers/offersSubmission/submissionForm/OfferDetailsForm.tsx
index 7c9e52e6..1fc38fe2 100644
--- a/apps/portal/src/components/offers/offersSubmission/submissionForm/OfferDetailsForm.tsx
+++ b/apps/portal/src/components/offers/offersSubmission/submissionForm/OfferDetailsForm.tsx
@@ -22,20 +22,16 @@ import {
defaultFullTimeOfferValues,
defaultInternshipOfferValues,
} from '../OffersSubmissionForm';
-import {
- emptyOption,
- FieldError,
- internshipCycleOptions,
- yearOptions,
-} from '../../constants';
+import { FieldError, JobTypeLabel } from '../../constants';
import FormMonthYearPicker from '../../forms/FormMonthYearPicker';
import FormSection from '../../forms/FormSection';
import FormSelect from '../../forms/FormSelect';
import FormTextArea from '../../forms/FormTextArea';
import FormTextInput from '../../forms/FormTextInput';
+import { InternshipCycleOptions } from '../../InternshipCycles';
import JobTypeTabs from '../../JobTypeTabs';
import type { OfferFormData } from '../../types';
-import { JobTypeLabel } from '../../types';
+import { FutureYearsOptions } from '../../Years';
import {
Currency,
CURRENCY_OPTIONS,
@@ -384,8 +380,7 @@ function InternshipOfferDetailsForm({
display="block"
errorMessage={offerFields?.offersIntern?.internshipCycle?.message}
label="Internship Cycle"
- options={internshipCycleOptions}
- placeholder={emptyOption}
+ options={InternshipCycleOptions}
required={true}
{...register(`offers.${index}.offersIntern.internshipCycle`, {
required: FieldError.REQUIRED,
@@ -395,8 +390,7 @@ function InternshipOfferDetailsForm({
display="block"
errorMessage={offerFields?.offersIntern?.startYear?.message}
label="Internship Year"
- options={yearOptions}
- placeholder={emptyOption}
+ options={FutureYearsOptions}
required={true}
{...register(`offers.${index}.offersIntern.startYear`, {
required: FieldError.REQUIRED,
@@ -522,14 +516,11 @@ function OfferDetailsFormArray({
);
}
-type OfferDetailsFormProps = Readonly<{
- defaultJobType?: JobType;
-}>;
-
-export default function OfferDetailsForm({
- defaultJobType = JobType.FULLTIME,
-}: OfferDetailsFormProps) {
- const [jobType, setJobType] = useState(defaultJobType);
+export default function OfferDetailsForm() {
+ const watchJobType = useWatch({
+ name: `offers.0.jobType`,
+ });
+ const [jobType, setJobType] = useState(watchJobType as JobType);
const [isDialogOpen, setDialogOpen] = useState(false);
const { control } = useFormContext();
const fieldArrayValues = useFieldArray({ control, name: 'offers' });
@@ -576,8 +567,8 @@ export default function OfferDetailsForm({
label="Switch"
variant="primary"
onClick={() => {
- toggleJobType();
setDialogOpen(false);
+ toggleJobType();
}}
/>
}
diff --git a/apps/portal/src/components/offers/profile/EducationCard.tsx b/apps/portal/src/components/offers/profile/EducationCard.tsx
index c885ef5a..9549e423 100644
--- a/apps/portal/src/components/offers/profile/EducationCard.tsx
+++ b/apps/portal/src/components/offers/profile/EducationCard.tsx
@@ -13,12 +13,12 @@ export default function EducationCard({
education: { type, field, startDate, endDate, school },
}: Props) {
return (
-
-
-
-
+
+
+
+
-
+
{field ? `${type ?? 'N/A'}, ${field}` : type ?? `N/A`}
diff --git a/apps/portal/src/components/offers/profile/OfferCard.tsx b/apps/portal/src/components/offers/profile/OfferCard.tsx
index 3d03e1ef..68f1571a 100644
--- a/apps/portal/src/components/offers/profile/OfferCard.tsx
+++ b/apps/portal/src/components/offers/profile/OfferCard.tsx
@@ -1,13 +1,13 @@
import {
- BuildingOffice2Icon,
- ChatBubbleBottomCenterTextIcon,
- CurrencyDollarIcon,
- ScaleIcon,
-} from '@heroicons/react/24/outline';
-import { HorizontalDivider } from '@tih/ui';
+ ArrowTrendingUpIcon,
+ BuildingOfficeIcon,
+ MapPinIcon,
+} from '@heroicons/react/20/solid';
+import { JobTypeLabel } from '~/components/offers/constants';
import type { OfferDisplayData } from '~/components/offers/types';
-import { JobTypeLabel } from '~/components/offers/types';
+
+import { getLocationDisplayText } from '~/utils/offers/string';
type Props = Readonly<{
offer: OfferDisplayData;
@@ -33,33 +33,55 @@ export default function OfferCard({
}: Props) {
function UpperSection() {
return (
-
-
-
-
-
-
-
- {location ? `${companyName}, ${location.cityName}` : companyName}
-
+
+
+
+
+ {jobTitle} {jobType && <>({JobTypeLabel[jobType]})>}
+
+
+ {companyName && (
+
+
+ {companyName}
+
+ )}
+ {location && (
+
+
+ {getLocationDisplayText(location)}
+
+ )}
+ {jobLevel && (
+
+ )}
+
-
-
- {jobLevel ? `${jobTitle}, ${jobLevel}` : jobTitle}{' '}
- {jobType && `(${JobTypeLabel[jobType]})`}
-
+
+ {!duration && receivedMonth && (
+
+ )}
+ {duration && (
+
+
{`${duration} months`}
+
+ )}
- {!duration && receivedMonth && (
-
- )}
- {duration && (
-
-
{`${duration} months`}
-
- )}
);
}
@@ -75,60 +97,69 @@ export default function OfferCard({
}
return (
- <>
-
-
-
- {(totalCompensation || monthlySalary) && (
-
-
-
-
-
-
- {totalCompensation && `TC: ${totalCompensation}`}
- {monthlySalary && `Monthly Salary: ${monthlySalary}`}
-
-
-
- )}
- {(base || stocks || bonus) && totalCompensation && (
-
-
- Base / year: {base ?? 'N/A'} ⋅ Stocks / year:{' '}
- {stocks ?? 'N/A'} ⋅ Bonus / year: {bonus ?? 'N/A'}
-
-
- )}
-
+
+
+ {totalCompensation && (
+
+
-
+ Total Compensation
+
+ -
+ {totalCompensation}
+
+
+ )}
+ {monthlySalary && (
+
+
-
+ Monthly Salary
+
+ - {monthlySalary}
+
+ )}
+ {base && (
+
+
-
+ Base Salary
+
+ - {base}
+
+ )}
+ {stocks && (
+
+
- Stocks
+ - {stocks}
+
+ )}
+ {bonus && (
+
+
- Bonus
+ - {bonus}
+
+ )}
{negotiationStrategy && (
-
-
-
-
-
-
- "{negotiationStrategy}"
-
-
+
+
-
+ Negotiation Strategy
+
+ -
+ {negotiationStrategy}
+
)}
{otherComment && (
-
-
-
-
-
- "{otherComment}"
-
+
+
- Others
+ - {otherComment}
)}
-
- >
+
+
);
}
+
return (
-
+
diff --git a/apps/portal/src/components/offers/profile/ProfileComments.tsx b/apps/portal/src/components/offers/profile/ProfileComments.tsx
index dc0ec114..34ac8b67 100644
--- a/apps/portal/src/components/offers/profile/ProfileComments.tsx
+++ b/apps/portal/src/components/offers/profile/ProfileComments.tsx
@@ -110,10 +110,10 @@ export default function ProfileComments({
);
}
return (
-
-
+
+
-
+
{isEditable && (
@@ -169,57 +169,62 @@ export default function ProfileComments({
-
Discussions
- {isEditable || session?.user?.name ? (
-
);
}
diff --git a/apps/portal/src/components/offers/profile/ProfileDetails.tsx b/apps/portal/src/components/offers/profile/ProfileDetails.tsx
index 3f61060b..7bc5d10c 100644
--- a/apps/portal/src/components/offers/profile/ProfileDetails.tsx
+++ b/apps/portal/src/components/offers/profile/ProfileDetails.tsx
@@ -25,19 +25,19 @@ type ProfileOffersProps = Readonly<{
}>;
function ProfileOffers({ offers }: ProfileOffersProps) {
- if (offers.length !== 0) {
+ if (offers.length === 0) {
return (
- <>
- {offers.map((offer) => (
-
- ))}
- >
+
+
No offers are attached.
+
);
}
+
return (
-
-
-
No offer is attached.
+
+ {offers.map((offer) => (
+
+ ))}
);
}
@@ -49,33 +49,37 @@ type ProfileBackgroundProps = Readonly<{
function ProfileBackground({ background }: ProfileBackgroundProps) {
if (!background?.experiences?.length && !background?.educations?.length) {
return (
-
+
No background information available.
);
}
return (
- <>
+
{background?.experiences?.length > 0 && (
- <>
-
-
-
Work Experience
+
+
+
+
+ Work Experience
+
- >
+
)}
{background?.educations?.length > 0 && (
- <>
-
);
}
@@ -114,7 +118,7 @@ function ProfileAnalysis({
}
return (
-
+
{!analysis ? (
No analysis available.
) : (
@@ -165,12 +169,15 @@ export default function ProfileDetails({
);
}
+
if (selectedTab === ProfileDetailTab.OFFERS) {
return
;
}
+
if (selectedTab === ProfileDetailTab.BACKGROUND) {
return
;
}
+
if (selectedTab === ProfileDetailTab.ANALYSIS) {
return (
-
-
-
+
+
+
+
-
+
{profileName ?? 'anonymous'}
{(experiences[0]?.companyName ||
experiences[0]?.jobLevel ||
experiences[0]?.jobTitle) && (
-
+
@@ -262,7 +262,7 @@ export default function ProfileHeader({
)}
-
+
YOE:
@@ -286,7 +286,7 @@ export default function ProfileHeader({
)}
-
+
-
-
-
+
+ {/*
+

+
*/}
+
+
+
{user?.name ?? 'unknown user'}
-
-
{message}
-
-
{`${timeSinceNow(
- createdAt,
- )} ago`}
- {replyLength > 0 && (
-
+
+
+
+
+ {timeSinceNow(createdAt)} ago
+ {' '}
+ ·{' '}
+ {replyLength > 0 && (
+ <>
+
- )}
- {!disableReply && (
-
-
- )}
- {deletable && (
- <>
-
- {!disableReply && isReplying && (
-
+
+
·{' '}
+ >
+ )}
+ {!disableReply && (
+ <>
+
setIsReplying(!isReplying)}>
+ Reply
+
+
·{' '}
+ >
+ )}
+ {deletable && (
+ <>
+
setIsDialogOpen(true)}>
+ {deleteCommentMutation.isLoading ? 'Deleting...' : 'Delete'}
+
+ {isDialogOpen && (
+
+ )}
+ >
+ )}
+
+ {!disableReply && isReplying && (
+
+
-
- )}
-
+
+
+ )}
-
- >
+
);
}
diff --git a/apps/portal/src/components/offers/profile/comments/ExpandableCommentCard.tsx b/apps/portal/src/components/offers/profile/comments/ExpandableCommentCard.tsx
index 22736196..0eb2d96c 100644
--- a/apps/portal/src/components/offers/profile/comments/ExpandableCommentCard.tsx
+++ b/apps/portal/src/components/offers/profile/comments/ExpandableCommentCard.tsx
@@ -26,18 +26,22 @@ export default function ExpandableCommentCard({
replyLength={comment.replies?.length ?? 0}
token={token}
/>
- {comment.replies && (
-
- {isExpanded &&
- comment.replies.map((reply) => (
-
- ))}
+ {comment.replies && comment.replies.length > 0 && isExpanded && (
+
+
+
+ {comment.replies.map((reply) => (
+ -
+
+
+ ))}
+
+
)}
diff --git a/apps/portal/src/components/offers/table/OffersRow.tsx b/apps/portal/src/components/offers/table/OffersRow.tsx
index 77c7ab44..f80802e4 100644
--- a/apps/portal/src/components/offers/table/OffersRow.tsx
+++ b/apps/portal/src/components/offers/table/OffersRow.tsx
@@ -42,9 +42,9 @@ export default function OfferTableRow({
{convertMoneyToString(income)} |
{jobType === JobType.FULLTIME && (
- {`${baseSalary && convertMoneyToString(baseSalary)} / ${
- bonus && convertMoneyToString(bonus)
- } / ${stocks && convertMoneyToString(stocks)}`}
+ {`${convertMoneyToString(baseSalary)} / ${convertMoneyToString(
+ bonus,
+ )} / ${convertMoneyToString(stocks)}`}
|
)}
{formatDate(monthYearReceived)} |
diff --git a/apps/portal/src/components/offers/table/OffersTable.tsx b/apps/portal/src/components/offers/table/OffersTable.tsx
index 534cd783..b530466b 100644
--- a/apps/portal/src/components/offers/table/OffersTable.tsx
+++ b/apps/portal/src/components/offers/table/OffersTable.tsx
@@ -2,7 +2,7 @@ import clsx from 'clsx';
import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';
import { JobType } from '@prisma/client';
-import { DropdownMenu, Spinner } from '@tih/ui';
+import { DropdownMenu, Spinner, useToast } from '@tih/ui';
import { useGoogleAnalytics } from '~/components/global/GoogleAnalytics';
import OffersTablePagination from '~/components/offers/table/OffersTablePagination';
@@ -66,6 +66,7 @@ export default function OffersTable({
event?.preventDefault();
}, [yoeCategory]);
+ const { showToast } = useToast();
trpc.useQuery(
[
'offers.list',
@@ -81,8 +82,11 @@ export default function OffersTable({
},
],
{
- onError: (err) => {
- alert(err);
+ onError: () => {
+ showToast({
+ title: 'Error loading the page.',
+ variant: 'failure',
+ });
},
onSuccess: (response: GetOffersResponse) => {
setOffers(response.data);
@@ -95,7 +99,7 @@ export default function OffersTable({
function renderFilters() {
return (
-
+
+
{columns.map((header, index) => (
) : (
-
+
{renderHeader()}
{offers.map((offer) => (
@@ -247,9 +251,6 @@ export default function OffersTable({
(offers.length === 0 && (
No data yet🥺
-
- Please try another set of filters.
-
))}
diff --git a/apps/portal/src/components/offers/table/OffersTablePagination.tsx b/apps/portal/src/components/offers/table/OffersTablePagination.tsx
index 9a235901..45dd7831 100644
--- a/apps/portal/src/components/offers/table/OffersTablePagination.tsx
+++ b/apps/portal/src/components/offers/table/OffersTablePagination.tsx
@@ -26,7 +26,7 @@ export default function OffersTablePagination({
Showing
- {` ${startNumber} - ${endNumber} `}
+ {` ${endNumber > 0 ? startNumber : 0} - ${endNumber} `}
{`of `}
diff --git a/apps/portal/src/components/offers/types.ts b/apps/portal/src/components/offers/types.ts
index 0e963a17..b38d1540 100644
--- a/apps/portal/src/components/offers/types.ts
+++ b/apps/portal/src/components/offers/types.ts
@@ -4,27 +4,6 @@ import type { MonthYear } from '~/components/shared/MonthYearPicker';
import type { Location } from '~/types/offers';
-export const HOME_URL = '/offers';
-
-/*
- * Offer Profile
- */
-
-export const JobTypeLabel = {
- FULLTIME: 'Full-time',
- INTERN: 'Internship',
-};
-
-export enum EducationBackgroundType {
- Bachelor = 'Bachelor',
- Diploma = 'Diploma',
- Masters = 'Masters',
- PhD = 'PhD',
- Professional = 'Professional',
- Secondary = 'Secondary',
- SelfTaught = 'Self-taught',
-}
-
export type OffersProfilePostData = {
background: BackgroundPostData;
id?: string;
diff --git a/apps/portal/src/components/resumes/comments/ResumeCommentsList.tsx b/apps/portal/src/components/resumes/comments/ResumeCommentsList.tsx
index 271fdd44..b4af9089 100644
--- a/apps/portal/src/components/resumes/comments/ResumeCommentsList.tsx
+++ b/apps/portal/src/components/resumes/comments/ResumeCommentsList.tsx
@@ -57,7 +57,7 @@ export default function ResumeCommentsList({
}
return (
-
+
{RESUME_COMMENTS_SECTIONS.map(({ label, value }) => {
const comments = commentsQuery.data
? commentsQuery.data.filter((comment: ResumeComment) => {
diff --git a/apps/portal/src/mappers/offers-mappers.ts b/apps/portal/src/mappers/offers-mappers.ts
index 07b37bb0..17355490 100644
--- a/apps/portal/src/mappers/offers-mappers.ts
+++ b/apps/portal/src/mappers/offers-mappers.ts
@@ -87,6 +87,7 @@ const analysisOfferDtoMapper = (
background?.experiences
?.filter((exp) => exp.company != null)
.map((exp) => exp.company?.name ?? '') ?? [],
+ profileId: offer.profileId,
profileName,
title:
offer.jobType === JobType.FULLTIME
@@ -95,7 +96,10 @@ const analysisOfferDtoMapper = (
totalYoe: background?.totalYoe ?? -1,
};
- if (offer.offersFullTime?.totalCompensation) {
+ if (
+ offer.offersFullTime?.totalCompensation &&
+ offer.jobType === JobType.FULLTIME
+ ) {
analysisOfferDto.income.value =
offer.offersFullTime.totalCompensation.value;
analysisOfferDto.income.currency =
@@ -105,7 +109,10 @@ const analysisOfferDtoMapper = (
offer.offersFullTime.totalCompensation.baseValue;
analysisOfferDto.income.baseCurrency =
offer.offersFullTime.totalCompensation.baseCurrency;
- } else if (offer.offersIntern?.monthlySalary) {
+ } else if (
+ offer.offersIntern?.monthlySalary &&
+ offer.jobType === JobType.INTERN
+ ) {
analysisOfferDto.income.value = offer.offersIntern.monthlySalary.value;
analysisOfferDto.income.currency =
offer.offersIntern.monthlySalary.currency;
@@ -369,13 +376,15 @@ export const experienceDtoMapper = (
experience.location != null
? locationDtoMapper(experience.location)
: null,
- monthlySalary: experience.monthlySalary
- ? valuationDtoMapper(experience.monthlySalary)
- : null,
+ monthlySalary:
+ experience.monthlySalary && experience.jobType === JobType.INTERN
+ ? valuationDtoMapper(experience.monthlySalary)
+ : null,
title: experience.title,
- totalCompensation: experience.totalCompensation
- ? valuationDtoMapper(experience.totalCompensation)
- : null,
+ totalCompensation:
+ experience.totalCompensation && experience.jobType === JobType.FULLTIME
+ ? valuationDtoMapper(experience.totalCompensation)
+ : null,
};
return experienceDto;
};
@@ -460,11 +469,11 @@ export const profileOfferDtoMapper = (
location: locationDtoMapper(offer.location),
monthYearReceived: offer.monthYearReceived,
negotiationStrategy: offer.negotiationStrategy,
- offersFullTime: offer.offersFullTime,
- offersIntern: offer.offersIntern,
+ offersFullTime: null,
+ offersIntern: null,
};
- if (offer.offersFullTime) {
+ if (offer.offersFullTime && offer.jobType === JobType.FULLTIME) {
profileOfferDto.offersFullTime = {
baseSalary:
offer.offersFullTime?.baseSalary != null
@@ -485,7 +494,7 @@ export const profileOfferDtoMapper = (
offer.offersFullTime.totalCompensation,
),
};
- } else if (offer.offersIntern) {
+ } else if (offer.offersIntern && offer.jobType === JobType.INTERN) {
profileOfferDto.offersIntern = {
id: offer.offersIntern.id,
internshipCycle: offer.offersIntern.internshipCycle,
@@ -701,30 +710,27 @@ export const dashboardOfferDtoMapper = (
totalYoe: offer.profile.background?.totalYoe ?? -1,
};
- if (offer.offersFullTime) {
+ if (offer.offersFullTime && offer.jobType === JobType.FULLTIME) {
dashboardOfferDto.income = valuationDtoMapper(
offer.offersFullTime.totalCompensation,
);
if (offer.offersFullTime.baseSalary) {
dashboardOfferDto.baseSalary = valuationDtoMapper(
- offer.offersFullTime.baseSalary
+ offer.offersFullTime.baseSalary,
);
}
if (offer.offersFullTime.bonus) {
- dashboardOfferDto.bonus = valuationDtoMapper(
- offer.offersFullTime.bonus
- );
-
+ dashboardOfferDto.bonus = valuationDtoMapper(offer.offersFullTime.bonus);
}
if (offer.offersFullTime.stocks) {
dashboardOfferDto.stocks = valuationDtoMapper(
- offer.offersFullTime.stocks
+ offer.offersFullTime.stocks,
);
}
- } else if (offer.offersIntern) {
+ } else if (offer.offersIntern && offer.jobType === JobType.INTERN) {
dashboardOfferDto.income = valuationDtoMapper(
offer.offersIntern.monthlySalary,
);
@@ -736,12 +742,12 @@ export const dashboardOfferDtoMapper = (
export const getOffersResponseMapper = (
data: Array ,
paging: Paging,
- jobType: JobType
+ jobType: JobType,
) => {
const getOffersResponse: GetOffersResponse = {
data,
jobType,
- paging
+ paging,
};
return getOffersResponse;
};
@@ -817,7 +823,10 @@ const userProfileOfferDtoMapper = (
: offer.offersIntern?.title ?? '',
};
- if (offer.offersFullTime?.totalCompensation) {
+ if (
+ offer.offersFullTime?.totalCompensation &&
+ offer.jobType === JobType.FULLTIME
+ ) {
mappedOffer.income.value = offer.offersFullTime.totalCompensation.value;
mappedOffer.income.currency =
offer.offersFullTime.totalCompensation.currency;
@@ -826,7 +835,10 @@ const userProfileOfferDtoMapper = (
offer.offersFullTime.totalCompensation.baseValue;
mappedOffer.income.baseCurrency =
offer.offersFullTime.totalCompensation.baseCurrency;
- } else if (offer.offersIntern?.monthlySalary) {
+ } else if (
+ offer.offersIntern?.monthlySalary &&
+ offer.jobType === JobType.INTERN
+ ) {
mappedOffer.income.value = offer.offersIntern.monthlySalary.value;
mappedOffer.income.currency = offer.offersIntern.monthlySalary.currency;
mappedOffer.income.id = offer.offersIntern.monthlySalary.id;
diff --git a/apps/portal/src/pages/offers/dashboard.tsx b/apps/portal/src/pages/offers/dashboard.tsx
index 1ae9ea46..bb3f5dc8 100644
--- a/apps/portal/src/pages/offers/dashboard.tsx
+++ b/apps/portal/src/pages/offers/dashboard.tsx
@@ -46,8 +46,8 @@ export default function ProfilesDashboard() {
if (userProfiles.length === 0) {
return (
-
-
+
+
You have not saved any offer profiles yet.
diff --git a/apps/portal/src/pages/offers/features.tsx b/apps/portal/src/pages/offers/features.tsx
index 77ca380c..da0cc28d 100644
--- a/apps/portal/src/pages/offers/features.tsx
+++ b/apps/portal/src/pages/offers/features.tsx
@@ -8,12 +8,12 @@ import {
UsersIcon,
} from '@heroicons/react/24/outline';
+import { HOME_URL } from '~/components/offers/constants';
import offersAnalysis from '~/components/offers/features/images/offers-analysis.png';
import offersBrowse from '~/components/offers/features/images/offers-browse.png';
import offersProfile from '~/components/offers/features/images/offers-profile.png';
import LeftTextCard from '~/components/offers/features/LeftTextCard';
import RightTextCard from '~/components/offers/features/RightTextCard';
-import { HOME_URL } from '~/components/offers/types';
const features = [
{
diff --git a/apps/portal/src/pages/offers/index.tsx b/apps/portal/src/pages/offers/index.tsx
index 9eaeb955..e84173f5 100644
--- a/apps/portal/src/pages/offers/index.tsx
+++ b/apps/portal/src/pages/offers/index.tsx
@@ -59,7 +59,7 @@ export default function OffersHomePage() {
offers.
-
+
Viewing offers for
-
+
(null);
+ const [isValidToken, setIsValidToken] = useState(false);
const pageRef = useRef(null);
const scrollToTop = () =>
pageRef.current?.scrollTo({ behavior: 'smooth', top: 0 });
- // TODO: Check if the token is valid before showing this page
+ const checkToken = trpc.useQuery(
+ ['offers.profile.isValidToken', { profileId: offerProfileId, token }],
+ {
+ onSuccess(data) {
+ setIsValidToken(data);
+ },
+ },
+ );
+
const getAnalysis = trpc.useQuery(
['offers.analysis.get', { profileId: offerProfileId }],
{
@@ -69,7 +79,7 @@ export default function OffersSubmissionResult() {
return (
<>
- {getAnalysis.isLoading && (
+ {(checkToken.isLoading || getAnalysis.isLoading) && (
@@ -77,7 +87,13 @@ export default function OffersSubmissionResult() {
)}
- {!getAnalysis.isLoading && (
+ {checkToken.isSuccess && !isValidToken && (
+
+ )}
+ {getAnalysis.isSuccess && (
diff --git a/apps/portal/src/pages/resumes/[resumeId].tsx b/apps/portal/src/pages/resumes/[resumeId].tsx
index 4e6c9bea..2fea9d4f 100644
--- a/apps/portal/src/pages/resumes/[resumeId].tsx
+++ b/apps/portal/src/pages/resumes/[resumeId].tsx
@@ -236,8 +236,8 @@ export default function ResumeReviewPage() {
{detailsQuery.data.title}
-
-
+
+
{detailsQuery.data.title}
diff --git a/apps/portal/src/pages/resumes/index.tsx b/apps/portal/src/pages/resumes/index.tsx
index 549a7849..7b601e40 100644
--- a/apps/portal/src/pages/resumes/index.tsx
+++ b/apps/portal/src/pages/resumes/index.tsx
@@ -395,7 +395,7 @@ export default function ResumeHomePage() {
leave="transition ease-in-out duration-300 transform"
leaveFrom="translate-x-0"
leaveTo="translate-x-full">
-
+
Quick access
@@ -614,7 +614,7 @@ export default function ResumeHomePage() {
-
+
) : (
-
+
-
+
{getTabTotalPages() > 1 && (
)}
{status === 'authenticated' && (
-
+
diff --git a/apps/portal/src/server/router/offers/offers-profile-router.ts b/apps/portal/src/server/router/offers/offers-profile-router.ts
index 39cd0d4f..cdf0c8b8 100644
--- a/apps/portal/src/server/router/offers/offers-profile-router.ts
+++ b/apps/portal/src/server/router/offers/offers-profile-router.ts
@@ -972,7 +972,19 @@ export const offersProfileRouter = createRouter()
for (const exp of input.background.experiences) {
if (exp.id) {
- // Update existing experience
+ const currentExp = await ctx.prisma.offersExperience.findFirst({
+ where: {
+ id: exp.id
+ }
+ })
+
+ if (!currentExp) {
+ throw new trpc.TRPCError({
+ code: 'NOT_FOUND',
+ message: 'Experience does not exist',
+ });
+ }
+
await ctx.prisma.offersExperience.update({
data: {
companyId: exp.companyId, // TODO: check if can change with connect or whether there is a difference
@@ -984,73 +996,164 @@ export const offersProfileRouter = createRouter()
id: exp.id,
},
});
+ if (currentExp.jobType === exp.jobType) {
+ // Update existing experience
+ if (exp.monthlySalary) {
+ await ctx.prisma.offersExperience.update({
+ data: {
+ monthlySalary: {
+ upsert: {
+ create: {
+ baseCurrency: baseCurrencyString,
+ baseValue: await convert(
+ exp.monthlySalary.value,
+ exp.monthlySalary.currency,
+ baseCurrencyString,
+ ),
+ currency: exp.monthlySalary.currency,
+ value: exp.monthlySalary.value,
+ },
+ update: {
+ baseCurrency: baseCurrencyString,
+ baseValue: await convert(
+ exp.monthlySalary.value,
+ exp.monthlySalary.currency,
+ baseCurrencyString,
+ ),
+ currency: exp.monthlySalary.currency,
+ value: exp.monthlySalary.value,
+ },
+ },
+ },
+ },
+ where: {
+ id: exp.id,
+ },
+ });
+ }
- if (exp.monthlySalary) {
- await ctx.prisma.offersExperience.update({
- data: {
- monthlySalary: {
- upsert: {
- create: {
- baseCurrency: baseCurrencyString,
- baseValue: await convert(
- exp.monthlySalary.value,
- exp.monthlySalary.currency,
- baseCurrencyString,
- ),
- currency: exp.monthlySalary.currency,
- value: exp.monthlySalary.value,
+ if (exp.totalCompensation) {
+ await ctx.prisma.offersExperience.update({
+ data: {
+ totalCompensation: {
+ upsert: {
+ create: {
+ baseCurrency: baseCurrencyString,
+ baseValue: await convert(
+ exp.totalCompensation.value,
+ exp.totalCompensation.currency,
+ baseCurrencyString,
+ ),
+ currency: exp.totalCompensation.currency,
+ value: exp.totalCompensation.value,
+ },
+ update: {
+ baseCurrency: baseCurrencyString,
+ baseValue: await convert(
+ exp.totalCompensation.value,
+ exp.totalCompensation.currency,
+ baseCurrencyString,
+ ),
+ currency: exp.totalCompensation.currency,
+ value: exp.totalCompensation.value,
+ },
},
- update: {
- baseCurrency: baseCurrencyString,
- baseValue: await convert(
- exp.monthlySalary.value,
- exp.monthlySalary.currency,
- baseCurrencyString,
- ),
- currency: exp.monthlySalary.currency,
- value: exp.monthlySalary.value,
+ },
+ },
+ where: {
+ id: exp.id,
+ },
+ });
+ }
+ } else if (exp.jobType === JobType.INTERN) {
+ // Add 1 remove the other
+ if (exp.monthlySalary) {
+ await ctx.prisma.offersExperience.update({
+ data: {
+ monthlySalary: {
+ upsert: {
+ create: {
+ baseCurrency: baseCurrencyString,
+ baseValue: await convert(
+ exp.monthlySalary.value,
+ exp.monthlySalary.currency,
+ baseCurrencyString,
+ ),
+ currency: exp.monthlySalary.currency,
+ value: exp.monthlySalary.value,
+ },
+ update: {
+ baseCurrency: baseCurrencyString,
+ baseValue: await convert(
+ exp.monthlySalary.value,
+ exp.monthlySalary.currency,
+ baseCurrencyString,
+ ),
+ currency: exp.monthlySalary.currency,
+ value: exp.monthlySalary.value,
+ },
},
},
},
- },
- where: {
- id: exp.id,
- },
- });
- }
+ where: {
+ id: exp.id,
+ },
+ });
+ }
- if (exp.totalCompensation) {
await ctx.prisma.offersExperience.update({
data: {
- totalCompensation: {
- upsert: {
- create: {
- baseCurrency: baseCurrencyString,
- baseValue: await convert(
- exp.totalCompensation.value,
- exp.totalCompensation.currency,
- baseCurrencyString,
- ),
- currency: exp.totalCompensation.currency,
- value: exp.totalCompensation.value,
- },
- update: {
- baseCurrency: baseCurrencyString,
- baseValue: await convert(
- exp.totalCompensation.value,
- exp.totalCompensation.currency,
- baseCurrencyString,
- ),
- currency: exp.totalCompensation.currency,
- value: exp.totalCompensation.value,
+ totalCompensation: undefined,
+ totalCompensationId: null,
+ },
+ where: {
+ id: exp.id
+ }
+ })
+ } else if (exp.jobType === JobType.FULLTIME) {
+ if (exp.totalCompensation) {
+ await ctx.prisma.offersExperience.update({
+ data: {
+ totalCompensation: {
+ upsert: {
+ create: {
+ baseCurrency: baseCurrencyString,
+ baseValue: await convert(
+ exp.totalCompensation.value,
+ exp.totalCompensation.currency,
+ baseCurrencyString,
+ ),
+ currency: exp.totalCompensation.currency,
+ value: exp.totalCompensation.value,
+ },
+ update: {
+ baseCurrency: baseCurrencyString,
+ baseValue: await convert(
+ exp.totalCompensation.value,
+ exp.totalCompensation.currency,
+ baseCurrencyString,
+ ),
+ currency: exp.totalCompensation.currency,
+ value: exp.totalCompensation.value,
+ },
+ },
},
},
+ where: {
+ id: exp.id,
+ },
+ });
+ }
+
+ await ctx.prisma.offersExperience.update({
+ data: {
+ monthlySalary: undefined,
+ monthlySalaryId: null
},
- },
- where: {
- id: exp.id,
- },
- });
+ where: {
+ id: exp.id
+ }
+ })
}
} else if (!exp.id) {
// Create new experience
@@ -1581,6 +1684,18 @@ export const offersProfileRouter = createRouter()
for (const offerToUpdate of input.offers) {
if (offerToUpdate.id) {
// Update existing offer
+ const currentOffer = await ctx.prisma.offersOffer.findFirst({
+ where: {
+ id: offerToUpdate.id
+ }
+ })
+
+ if (!currentOffer) {
+ throw new trpc.TRPCError({
+ code: 'NOT_FOUND',
+ message: 'Offer to update does not exist',
+ });
+ }
await ctx.prisma.offersOffer.update({
data: {
comments: offerToUpdate.comments,
@@ -1606,82 +1721,191 @@ export const offersProfileRouter = createRouter()
},
});
- if (offerToUpdate.offersIntern?.monthlySalary != null) {
- await ctx.prisma.offersIntern.update({
- data: {
- internshipCycle:
- offerToUpdate.offersIntern.internshipCycle ?? undefined,
- monthlySalary: {
- upsert: {
- create: {
- baseCurrency: baseCurrencyString,
- baseValue: await convert(
- offerToUpdate.offersIntern.monthlySalary.value,
- offerToUpdate.offersIntern.monthlySalary.currency,
- baseCurrencyString,
- ),
- currency:
- offerToUpdate.offersIntern.monthlySalary.currency,
- value: offerToUpdate.offersIntern.monthlySalary.value,
- },
- update: {
- baseCurrency: baseCurrencyString,
- baseValue: await convert(
- offerToUpdate.offersIntern.monthlySalary.value,
- offerToUpdate.offersIntern.monthlySalary.currency,
- baseCurrencyString,
- ),
- currency:
- offerToUpdate.offersIntern.monthlySalary.currency,
- value: offerToUpdate.offersIntern.monthlySalary.value,
+ if (currentOffer.jobType === offerToUpdate.jobType) {
+ if (offerToUpdate.offersIntern?.monthlySalary != null) {
+ await ctx.prisma.offersIntern.update({
+ data: {
+ internshipCycle:
+ offerToUpdate.offersIntern.internshipCycle ?? undefined,
+ monthlySalary: {
+ upsert: {
+ create: {
+ baseCurrency: baseCurrencyString,
+ baseValue: await convert(
+ offerToUpdate.offersIntern.monthlySalary.value,
+ offerToUpdate.offersIntern.monthlySalary.currency,
+ baseCurrencyString,
+ ),
+ currency:
+ offerToUpdate.offersIntern.monthlySalary.currency,
+ value: offerToUpdate.offersIntern.monthlySalary.value,
+ },
+ update: {
+ baseCurrency: baseCurrencyString,
+ baseValue: await convert(
+ offerToUpdate.offersIntern.monthlySalary.value,
+ offerToUpdate.offersIntern.monthlySalary.currency,
+ baseCurrencyString,
+ ),
+ currency:
+ offerToUpdate.offersIntern.monthlySalary.currency,
+ value: offerToUpdate.offersIntern.monthlySalary.value,
+ },
},
},
+ startYear: offerToUpdate.offersIntern.startYear ?? undefined,
+ title: offerToUpdate.offersIntern.title,
},
- startYear: offerToUpdate.offersIntern.startYear ?? undefined,
- title: offerToUpdate.offersIntern.title,
- },
- where: {
- id: offerToUpdate.offersIntern.id,
- },
- });
- }
+ where: {
+ id: offerToUpdate.offersIntern.id,
+ },
+ });
+ }
- if (offerToUpdate.offersFullTime?.totalCompensation != null) {
- await ctx.prisma.offersFullTime.update({
- data: {
- level: offerToUpdate.offersFullTime.level ?? undefined,
- title: offerToUpdate.offersFullTime.title,
- },
- where: {
- id: offerToUpdate.offersFullTime.id,
- },
- });
- if (offerToUpdate.offersFullTime.baseSalary != null) {
+ if (offerToUpdate.offersFullTime?.totalCompensation != null) {
+ await ctx.prisma.offersFullTime.update({
+ data: {
+ level: offerToUpdate.offersFullTime.level ?? undefined,
+ title: offerToUpdate.offersFullTime.title,
+ },
+ where: {
+ id: offerToUpdate.offersFullTime.id,
+ },
+ });
+ if (offerToUpdate.offersFullTime.baseSalary != null) {
+ await ctx.prisma.offersFullTime.update({
+ data: {
+ baseSalary: {
+ upsert: {
+ create: {
+ baseCurrency: baseCurrencyString,
+ baseValue: await convert(
+ offerToUpdate.offersFullTime.baseSalary.value,
+ offerToUpdate.offersFullTime.baseSalary.currency,
+ baseCurrencyString,
+ ),
+ currency:
+ offerToUpdate.offersFullTime.baseSalary.currency,
+ value: offerToUpdate.offersFullTime.baseSalary.value,
+ },
+ update: {
+ baseCurrency: baseCurrencyString,
+ baseValue: await convert(
+ offerToUpdate.offersFullTime.baseSalary.value,
+ offerToUpdate.offersFullTime.baseSalary.currency,
+ baseCurrencyString,
+ ),
+ currency:
+ offerToUpdate.offersFullTime.baseSalary.currency,
+ value: offerToUpdate.offersFullTime.baseSalary.value,
+ },
+ },
+ },
+ },
+ where: {
+ id: offerToUpdate.offersFullTime.id,
+ },
+ });
+ }
+ if (offerToUpdate.offersFullTime.bonus != null) {
+ await ctx.prisma.offersFullTime.update({
+ data: {
+ bonus: {
+ upsert: {
+ create: {
+ baseCurrency: baseCurrencyString,
+ baseValue: await convert(
+ offerToUpdate.offersFullTime.bonus.value,
+ offerToUpdate.offersFullTime.bonus.currency,
+ baseCurrencyString,
+ ),
+ currency: offerToUpdate.offersFullTime.bonus.currency,
+ value: offerToUpdate.offersFullTime.bonus.value,
+ },
+ update: {
+ baseCurrency: baseCurrencyString,
+ baseValue: await convert(
+ offerToUpdate.offersFullTime.bonus.value,
+ offerToUpdate.offersFullTime.bonus.currency,
+ baseCurrencyString,
+ ),
+ currency: offerToUpdate.offersFullTime.bonus.currency,
+ value: offerToUpdate.offersFullTime.bonus.value,
+ },
+ },
+ },
+ },
+ where: {
+ id: offerToUpdate.offersFullTime.id,
+ },
+ });
+ }
+ if (offerToUpdate.offersFullTime.stocks != null) {
+ await ctx.prisma.offersFullTime.update({
+ data: {
+ stocks: {
+ upsert: {
+ create: {
+ baseCurrency: baseCurrencyString,
+ baseValue: await convert(
+ offerToUpdate.offersFullTime.stocks.value,
+ offerToUpdate.offersFullTime.stocks.currency,
+ baseCurrencyString,
+ ),
+ currency:
+ offerToUpdate.offersFullTime.stocks.currency,
+ value: offerToUpdate.offersFullTime.stocks.value,
+ },
+ update: {
+ baseCurrency: baseCurrencyString,
+ baseValue: await convert(
+ offerToUpdate.offersFullTime.stocks.value,
+ offerToUpdate.offersFullTime.stocks.currency,
+ baseCurrencyString,
+ ),
+ currency:
+ offerToUpdate.offersFullTime.stocks.currency,
+ value: offerToUpdate.offersFullTime.stocks.value,
+ },
+ },
+ },
+ },
+ where: {
+ id: offerToUpdate.offersFullTime.id,
+ },
+ });
+ }
await ctx.prisma.offersFullTime.update({
data: {
- baseSalary: {
+ totalCompensation: {
upsert: {
create: {
baseCurrency: baseCurrencyString,
baseValue: await convert(
- offerToUpdate.offersFullTime.baseSalary.value,
- offerToUpdate.offersFullTime.baseSalary.currency,
+ offerToUpdate.offersFullTime.totalCompensation.value,
+ offerToUpdate.offersFullTime.totalCompensation
+ .currency,
baseCurrencyString,
),
currency:
- offerToUpdate.offersFullTime.baseSalary.currency,
- value: offerToUpdate.offersFullTime.baseSalary.value,
+ offerToUpdate.offersFullTime.totalCompensation
+ .currency,
+ value:
+ offerToUpdate.offersFullTime.totalCompensation.value,
},
update: {
baseCurrency: baseCurrencyString,
baseValue: await convert(
- offerToUpdate.offersFullTime.baseSalary.value,
- offerToUpdate.offersFullTime.baseSalary.currency,
+ offerToUpdate.offersFullTime.totalCompensation.value,
+ offerToUpdate.offersFullTime.totalCompensation
+ .currency,
baseCurrencyString,
),
currency:
- offerToUpdate.offersFullTime.baseSalary.currency,
- value: offerToUpdate.offersFullTime.baseSalary.value,
+ offerToUpdate.offersFullTime.totalCompensation
+ .currency,
+ value:
+ offerToUpdate.offersFullTime.totalCompensation.value,
},
},
},
@@ -1691,30 +1915,151 @@ export const offersProfileRouter = createRouter()
},
});
}
- if (offerToUpdate.offersFullTime.bonus != null) {
+ } else if (currentOffer.jobType === JobType.FULLTIME) {
+ if (offerToUpdate.offersFullTime?.totalCompensation != null) {
+ await ctx.prisma.offersFullTime.update({
+ data: {
+ level: offerToUpdate.offersFullTime.level ?? undefined,
+ title: offerToUpdate.offersFullTime.title,
+ },
+ where: {
+ id: offerToUpdate.offersFullTime.id,
+ },
+ });
+ if (offerToUpdate.offersFullTime.baseSalary != null) {
+ await ctx.prisma.offersFullTime.update({
+ data: {
+ baseSalary: {
+ upsert: {
+ create: {
+ baseCurrency: baseCurrencyString,
+ baseValue: await convert(
+ offerToUpdate.offersFullTime.baseSalary.value,
+ offerToUpdate.offersFullTime.baseSalary.currency,
+ baseCurrencyString,
+ ),
+ currency:
+ offerToUpdate.offersFullTime.baseSalary.currency,
+ value: offerToUpdate.offersFullTime.baseSalary.value,
+ },
+ update: {
+ baseCurrency: baseCurrencyString,
+ baseValue: await convert(
+ offerToUpdate.offersFullTime.baseSalary.value,
+ offerToUpdate.offersFullTime.baseSalary.currency,
+ baseCurrencyString,
+ ),
+ currency:
+ offerToUpdate.offersFullTime.baseSalary.currency,
+ value: offerToUpdate.offersFullTime.baseSalary.value,
+ },
+ },
+ },
+ },
+ where: {
+ id: offerToUpdate.offersFullTime.id,
+ },
+ });
+ }
+ if (offerToUpdate.offersFullTime.bonus != null) {
+ await ctx.prisma.offersFullTime.update({
+ data: {
+ bonus: {
+ upsert: {
+ create: {
+ baseCurrency: baseCurrencyString,
+ baseValue: await convert(
+ offerToUpdate.offersFullTime.bonus.value,
+ offerToUpdate.offersFullTime.bonus.currency,
+ baseCurrencyString,
+ ),
+ currency: offerToUpdate.offersFullTime.bonus.currency,
+ value: offerToUpdate.offersFullTime.bonus.value,
+ },
+ update: {
+ baseCurrency: baseCurrencyString,
+ baseValue: await convert(
+ offerToUpdate.offersFullTime.bonus.value,
+ offerToUpdate.offersFullTime.bonus.currency,
+ baseCurrencyString,
+ ),
+ currency: offerToUpdate.offersFullTime.bonus.currency,
+ value: offerToUpdate.offersFullTime.bonus.value,
+ },
+ },
+ },
+ },
+ where: {
+ id: offerToUpdate.offersFullTime.id,
+ },
+ });
+ }
+ if (offerToUpdate.offersFullTime.stocks != null) {
+ await ctx.prisma.offersFullTime.update({
+ data: {
+ stocks: {
+ upsert: {
+ create: {
+ baseCurrency: baseCurrencyString,
+ baseValue: await convert(
+ offerToUpdate.offersFullTime.stocks.value,
+ offerToUpdate.offersFullTime.stocks.currency,
+ baseCurrencyString,
+ ),
+ currency:
+ offerToUpdate.offersFullTime.stocks.currency,
+ value: offerToUpdate.offersFullTime.stocks.value,
+ },
+ update: {
+ baseCurrency: baseCurrencyString,
+ baseValue: await convert(
+ offerToUpdate.offersFullTime.stocks.value,
+ offerToUpdate.offersFullTime.stocks.currency,
+ baseCurrencyString,
+ ),
+ currency:
+ offerToUpdate.offersFullTime.stocks.currency,
+ value: offerToUpdate.offersFullTime.stocks.value,
+ },
+ },
+ },
+ },
+ where: {
+ id: offerToUpdate.offersFullTime.id,
+ },
+ });
+ }
await ctx.prisma.offersFullTime.update({
data: {
- bonus: {
+ totalCompensation: {
upsert: {
create: {
baseCurrency: baseCurrencyString,
baseValue: await convert(
- offerToUpdate.offersFullTime.bonus.value,
- offerToUpdate.offersFullTime.bonus.currency,
+ offerToUpdate.offersFullTime.totalCompensation.value,
+ offerToUpdate.offersFullTime.totalCompensation
+ .currency,
baseCurrencyString,
),
- currency: offerToUpdate.offersFullTime.bonus.currency,
- value: offerToUpdate.offersFullTime.bonus.value,
+ currency:
+ offerToUpdate.offersFullTime.totalCompensation
+ .currency,
+ value:
+ offerToUpdate.offersFullTime.totalCompensation.value,
},
update: {
baseCurrency: baseCurrencyString,
baseValue: await convert(
- offerToUpdate.offersFullTime.bonus.value,
- offerToUpdate.offersFullTime.bonus.currency,
+ offerToUpdate.offersFullTime.totalCompensation.value,
+ offerToUpdate.offersFullTime.totalCompensation
+ .currency,
baseCurrencyString,
),
- currency: offerToUpdate.offersFullTime.bonus.currency,
- value: offerToUpdate.offersFullTime.bonus.value,
+ currency:
+ offerToUpdate.offersFullTime.totalCompensation
+ .currency,
+ value:
+ offerToUpdate.offersFullTime.totalCompensation.value,
},
},
},
@@ -1724,80 +2069,66 @@ export const offersProfileRouter = createRouter()
},
});
}
- if (offerToUpdate.offersFullTime.stocks != null) {
- await ctx.prisma.offersFullTime.update({
+
+ await ctx.prisma.offersOffer.update({
+ data: {
+ offersIntern: undefined,
+ offersInternId: null
+ },
+ where: {
+ id: offerToUpdate.id
+ }
+ })
+ } else if (currentOffer.jobType === JobType.INTERN) {
+ if (offerToUpdate.offersIntern?.monthlySalary != null) {
+ await ctx.prisma.offersIntern.update({
data: {
- stocks: {
+ internshipCycle:
+ offerToUpdate.offersIntern.internshipCycle ?? undefined,
+ monthlySalary: {
upsert: {
create: {
baseCurrency: baseCurrencyString,
baseValue: await convert(
- offerToUpdate.offersFullTime.stocks.value,
- offerToUpdate.offersFullTime.stocks.currency,
+ offerToUpdate.offersIntern.monthlySalary.value,
+ offerToUpdate.offersIntern.monthlySalary.currency,
baseCurrencyString,
),
currency:
- offerToUpdate.offersFullTime.stocks.currency,
- value: offerToUpdate.offersFullTime.stocks.value,
+ offerToUpdate.offersIntern.monthlySalary.currency,
+ value: offerToUpdate.offersIntern.monthlySalary.value,
},
update: {
baseCurrency: baseCurrencyString,
baseValue: await convert(
- offerToUpdate.offersFullTime.stocks.value,
- offerToUpdate.offersFullTime.stocks.currency,
+ offerToUpdate.offersIntern.monthlySalary.value,
+ offerToUpdate.offersIntern.monthlySalary.currency,
baseCurrencyString,
),
currency:
- offerToUpdate.offersFullTime.stocks.currency,
- value: offerToUpdate.offersFullTime.stocks.value,
+ offerToUpdate.offersIntern.monthlySalary.currency,
+ value: offerToUpdate.offersIntern.monthlySalary.value,
},
},
},
+ startYear: offerToUpdate.offersIntern.startYear ?? undefined,
+ title: offerToUpdate.offersIntern.title,
},
where: {
- id: offerToUpdate.offersFullTime.id,
+ id: offerToUpdate.offersIntern.id,
},
});
}
- await ctx.prisma.offersFullTime.update({
+
+ await ctx.prisma.offersOffer.update({
data: {
- totalCompensation: {
- upsert: {
- create: {
- baseCurrency: baseCurrencyString,
- baseValue: await convert(
- offerToUpdate.offersFullTime.totalCompensation.value,
- offerToUpdate.offersFullTime.totalCompensation
- .currency,
- baseCurrencyString,
- ),
- currency:
- offerToUpdate.offersFullTime.totalCompensation
- .currency,
- value:
- offerToUpdate.offersFullTime.totalCompensation.value,
- },
- update: {
- baseCurrency: baseCurrencyString,
- baseValue: await convert(
- offerToUpdate.offersFullTime.totalCompensation.value,
- offerToUpdate.offersFullTime.totalCompensation
- .currency,
- baseCurrencyString,
- ),
- currency:
- offerToUpdate.offersFullTime.totalCompensation
- .currency,
- value:
- offerToUpdate.offersFullTime.totalCompensation.value,
- },
- },
- },
+ offersFullTime: undefined,
+ offersFullTimeId: null
},
where: {
- id: offerToUpdate.offersFullTime.id,
- },
- });
+ id: offerToUpdate.id
+ }
+ })
}
} else {
// Create new offer
diff --git a/apps/portal/src/types/offers.d.ts b/apps/portal/src/types/offers.d.ts
index 425d654d..559c9f31 100644
--- a/apps/portal/src/types/offers.d.ts
+++ b/apps/portal/src/types/offers.d.ts
@@ -181,6 +181,7 @@ export type AnalysisOffer = {
monthYearReceived: Date;
negotiationStrategy: string;
previousCompanies: Array;
+ profileId: string;
profileName: string;
title: string;
totalYoe: number;
diff --git a/apps/portal/src/utils/offers/currency/CurrencySelector.tsx b/apps/portal/src/utils/offers/currency/CurrencySelector.tsx
index 2ba883b4..2b339933 100644
--- a/apps/portal/src/utils/offers/currency/CurrencySelector.tsx
+++ b/apps/portal/src/utils/offers/currency/CurrencySelector.tsx
@@ -20,7 +20,7 @@ export default function CurrencySelector({
|