diff --git a/apps/portal/src/components/offers/table/OffersRow.tsx b/apps/portal/src/components/offers/table/OffersRow.tsx
index f7bf4dc6..9e131a7e 100644
--- a/apps/portal/src/components/offers/table/OffersRow.tsx
+++ b/apps/portal/src/components/offers/table/OffersRow.tsx
@@ -1,6 +1,6 @@
import Link from 'next/link';
-import { convertCurrencyToString } from '~/utils/offers/currency';
+import { convertMoneyToString } from '~/utils/offers/currency';
import { formatDate } from '~/utils/offers/time';
import type { DashboardOffer } from '~/types/offers';
@@ -21,7 +21,7 @@ export default function OfferTableRow({
{title} |
{totalYoe} |
-
{convertCurrencyToString(income)} |
+
{convertMoneyToString(income)} |
{formatDate(monthYearReceived)} |
diff --git a/apps/portal/src/pages/offers/profile/[offerProfileId].tsx b/apps/portal/src/pages/offers/profile/[offerProfileId].tsx
index 6bef7241..d6b18e65 100644
--- a/apps/portal/src/pages/offers/profile/[offerProfileId].tsx
+++ b/apps/portal/src/pages/offers/profile/[offerProfileId].tsx
@@ -7,7 +7,8 @@ import ProfileDetails from '~/components/offers/profile/ProfileDetails';
import ProfileHeader from '~/components/offers/profile/ProfileHeader';
import type { BackgroundCard, OfferEntity } from '~/components/offers/types';
-import { convertCurrencyToString } from '~/utils/offers/currency';
+import { convertMoneyToString } from '~/utils/offers/currency';
+import { getProfilePath } from '~/utils/offers/link';
import { formatDate } from '~/utils/offers/time';
import { trpc } from '~/utils/trpc';
@@ -38,7 +39,7 @@ export default function OfferProfile() {
}
// If the profile is not editable with a wrong token, redirect to the profile page
if (!data?.isEditable && token !== '') {
- router.push(`/offers/profile/${offerProfileId}`);
+ router.push(getProfilePath(offerProfileId as string));
}
setIsEditable(data?.isEditable ?? false);
@@ -48,10 +49,8 @@ export default function OfferProfile() {
? data?.offers.map((res: ProfileOffer) => {
if (res.offersFullTime) {
const filteredOffer: OfferEntity = {
- base: convertCurrencyToString(
- res.offersFullTime.baseSalary,
- ),
- bonus: convertCurrencyToString(res.offersFullTime.bonus),
+ base: convertMoneyToString(res.offersFullTime.baseSalary),
+ bonus: convertMoneyToString(res.offersFullTime.bonus),
companyName: res.company.name,
id: res.offersFullTime.id,
jobLevel: res.offersFullTime.level,
@@ -60,12 +59,11 @@ export default function OfferProfile() {
negotiationStrategy: res.negotiationStrategy || '',
otherComment: res.comments || '',
receivedMonth: formatDate(res.monthYearReceived),
- stocks: convertCurrencyToString(res.offersFullTime.stocks),
- totalCompensation: convertCurrencyToString(
+ stocks: convertMoneyToString(res.offersFullTime.stocks),
+ totalCompensation: convertMoneyToString(
res.offersFullTime.totalCompensation,
),
};
-
return filteredOffer;
}
const filteredOffer: OfferEntity = {
@@ -73,7 +71,7 @@ export default function OfferProfile() {
id: res.offersIntern!.id,
jobTitle: res.offersIntern!.title,
location: res.location,
- monthlySalary: convertCurrencyToString(
+ monthlySalary: convertMoneyToString(
res.offersIntern!.monthlySalary,
),
negotiationStrategy: res.negotiationStrategy || '',
@@ -88,46 +86,29 @@ export default function OfferProfile() {
if (data?.background) {
const transformedBackground = {
- educations: [
- {
- endDate: data?.background.educations[0].endDate
- ? formatDate(data.background.educations[0].endDate)
- : '-',
- field: data.background.educations[0].field || '-',
- school: data.background.educations[0].school || '-',
- startDate: data.background.educations[0].startDate
- ? formatDate(data.background.educations[0].startDate)
- : '-',
- type: data.background.educations[0].type || '-',
- },
- ],
- experiences: [
- data.background.experiences &&
- data.background.experiences.length > 0
- ? {
- companyName:
- data.background.experiences[0].company?.name ?? '-',
- duration:
- String(data.background.experiences[0].durationInMonths) ??
- '-',
- jobLevel: data.background.experiences[0].level ?? '',
- jobTitle: data.background.experiences[0].title ?? '-',
- monthlySalary: data.background.experiences[0].monthlySalary
- ? convertCurrencyToString(
- data.background.experiences[0].monthlySalary,
- )
- : '-',
- totalCompensation: data.background.experiences[0]
- .totalCompensation
- ? convertCurrencyToString(
- data.background.experiences[0].totalCompensation,
- )
- : '-',
- }
- : {},
- ],
+ educations: data.background.educations.map((education) => ({
+ endDate: education.endDate ? formatDate(education.endDate) : '-',
+ field: education.field || '-',
+ school: education.school || '-',
+ startDate: education.startDate
+ ? formatDate(education.startDate)
+ : '-',
+ type: education.type || '-',
+ })),
+ experiences: data.background.experiences.map((experience) => ({
+ companyName: experience.company?.name ?? '-',
+ duration: String(experience.durationInMonths) ?? '-',
+ jobLevel: experience.level ?? '',
+ jobTitle: experience.title ?? '-',
+ monthlySalary: experience.monthlySalary
+ ? convertMoneyToString(experience.monthlySalary)
+ : '-',
+ totalCompensation: experience.totalCompensation
+ ? convertMoneyToString(experience.totalCompensation)
+ : '-',
+ })),
profileName: data.profileName,
- specificYoes: data.background.specificYoes ?? [],
+ specificYoes: data.background.specificYoes,
totalYoe: String(data.background.totalYoe) || '-',
};
setBackground(transformedBackground);
diff --git a/apps/portal/src/pages/offers/profile/edit/[offerProfileId].tsx b/apps/portal/src/pages/offers/profile/edit/[offerProfileId].tsx
new file mode 100644
index 00000000..c5b7c15c
--- /dev/null
+++ b/apps/portal/src/pages/offers/profile/edit/[offerProfileId].tsx
@@ -0,0 +1,79 @@
+import { useRouter } from 'next/router';
+import { useState } from 'react';
+
+import OffersSubmissionForm from '~/components/offers/offersSubmission/OffersSubmissionForm';
+import type { OffersProfileFormData } from '~/components/offers/types';
+import { JobType } from '~/components/offers/types';
+
+import { Spinner } from '~/../../../packages/ui/dist';
+import { getProfilePath } from '~/utils/offers/link';
+import { convertToMonthYear } from '~/utils/offers/time';
+import { trpc } from '~/utils/trpc';
+
+export default function OffersEditPage() {
+ const [initialData, setInitialData] = useState();
+ const router = useRouter();
+ const { offerProfileId, token = '' } = router.query;
+
+ const getProfileResult = trpc.useQuery(
+ [
+ 'offers.profile.listOne',
+ { profileId: offerProfileId as string, token: token as string },
+ ],
+ {
+ onError(error) {
+ console.error(error.message);
+ },
+ onSuccess(data) {
+ const { educations, experiences, specificYoes, totalYoe } =
+ data.background!;
+
+ setInitialData({
+ background: {
+ educations,
+ experiences:
+ experiences.length === 0
+ ? [{ jobType: JobType.FullTime }]
+ : experiences,
+ specificYoes,
+ totalYoe,
+ },
+ offers: data.offers.map((offer) => ({
+ comments: offer.comments,
+ companyId: offer.company.id,
+ id: offer.id,
+ jobType: offer.jobType,
+ location: offer.location,
+ monthYearReceived: convertToMonthYear(offer.monthYearReceived),
+ negotiationStrategy: offer.negotiationStrategy,
+ offersFullTime: offer.offersFullTime,
+ offersIntern: offer.offersIntern,
+ })),
+ });
+ },
+ },
+ );
+
+ const profile = getProfileResult.data;
+
+ if (profile && !profile.isEditable) {
+ router.push(getProfilePath(profile.id));
+ }
+
+ return (
+ <>
+ {getProfileResult.isLoading && (
+
+
+
+ )}
+ {!getProfileResult.isLoading && (
+
+ )}
+ >
+ );
+}
diff --git a/apps/portal/src/pages/offers/submit.tsx b/apps/portal/src/pages/offers/submit.tsx
index 6847bbf7..df2015f1 100644
--- a/apps/portal/src/pages/offers/submit.tsx
+++ b/apps/portal/src/pages/offers/submit.tsx
@@ -1,215 +1,5 @@
-import { useRef, useState } from 'react';
-import type { SubmitHandler } from 'react-hook-form';
-import { FormProvider, useForm } from 'react-hook-form';
-import { ArrowLeftIcon, ArrowRightIcon } from '@heroicons/react/20/solid';
-import { Button } from '@tih/ui';
-
-import { Breadcrumbs } from '~/components/offers/Breadcrumb';
-import BackgroundForm from '~/components/offers/offers-submission/BackgroundForm';
-import OfferAnalysis from '~/components/offers/offers-submission/OfferAnalysis';
-import OfferDetailsForm from '~/components/offers/offers-submission/OfferDetailsForm';
-import OfferProfileSave from '~/components/offers/offers-submission/OfferProfileSave';
-import type {
- OfferFormData,
- OffersProfileFormData,
-} from '~/components/offers/types';
-import { JobType } from '~/components/offers/types';
-import type { Month } from '~/components/shared/MonthYearPicker';
-
-import { cleanObject, removeInvalidMoneyData } from '~/utils/offers/form';
-import { getCurrentMonth, getCurrentYear } from '~/utils/offers/time';
-import { trpc } from '~/utils/trpc';
-
-import type { CreateOfferProfileResponse } from '~/types/offers';
-
-const defaultOfferValues = {
- comments: '',
- companyId: '',
- jobType: JobType.FullTime,
- location: '',
- monthYearReceived: {
- month: getCurrentMonth() as Month,
- year: getCurrentYear(),
- },
- negotiationStrategy: '',
-};
-
-export const defaultFullTimeOfferValues = {
- ...defaultOfferValues,
- jobType: JobType.FullTime,
-};
-
-export const defaultInternshipOfferValues = {
- ...defaultOfferValues,
- jobType: JobType.Intern,
-};
-
-const defaultOfferProfileValues = {
- background: {
- educations: [],
- experiences: [{ jobType: JobType.FullTime }],
- specificYoes: [],
- },
- offers: [defaultOfferValues],
-};
-
-type FormStep = {
- component: JSX.Element;
- hasNext: boolean;
- hasPrevious: boolean;
- label: string;
-};
+import OffersSubmissionForm from '~/components/offers/offersSubmission/OffersSubmissionForm';
export default function OffersSubmissionPage() {
- const [formStep, setFormStep] = useState(0);
- const [createProfileResponse, setCreateProfileResponse] =
- useState();
-
- const pageRef = useRef(null);
- const scrollToTop = () =>
- pageRef.current?.scrollTo({ behavior: 'smooth', top: 0 });
- const formMethods = useForm({
- defaultValues: defaultOfferProfileValues,
- mode: 'all',
- });
- const { handleSubmit, trigger } = formMethods;
-
- const formSteps: Array = [
- {
- component: ,
- hasNext: true,
- hasPrevious: false,
- label: 'Offer details',
- },
- {
- component: ,
- hasNext: false,
- hasPrevious: true,
- label: 'Background',
- },
- {
- component: (
-
- ),
- hasNext: true,
- hasPrevious: false,
- label: 'Analysis',
- },
- {
- component: ,
- hasNext: false,
- hasPrevious: false,
- label: 'Save',
- },
- ];
-
- const formStepsLabels = formSteps.map((step) => step.label);
-
- const nextStep = async (currStep: number) => {
- if (currStep === 0) {
- const result = await trigger('offers');
- if (!result) {
- return;
- }
- }
- setFormStep(formStep + 1);
- scrollToTop();
- };
-
- const previousStep = () => {
- setFormStep(formStep - 1);
- scrollToTop();
- };
-
- const generateAnalysisMutation = trpc.useMutation(
- ['offers.analysis.generate'],
- {
- onError(error) {
- console.error(error.message);
- },
- },
- );
-
- const createMutation = trpc.useMutation(['offers.profile.create'], {
- onError(error) {
- console.error(error.message);
- },
- onSuccess(data) {
- generateAnalysisMutation.mutate({
- profileId: data?.id || '',
- });
- setCreateProfileResponse(data);
- setFormStep(formStep + 1);
- scrollToTop();
- },
- });
-
- const onSubmit: SubmitHandler = async (data) => {
- const result = await trigger();
- if (!result) {
- return;
- }
-
- data = removeInvalidMoneyData(data);
-
- const background = cleanObject(data.background);
- background.specificYoes = data.background.specificYoes.filter(
- (specificYoe) => specificYoe.domain && specificYoe.yoe > 0,
- );
- if (Object.entries(background.experiences[0]).length === 1) {
- background.experiences = [];
- }
-
- const offers = data.offers.map((offer: OfferFormData) => ({
- ...offer,
- monthYearReceived: new Date(
- offer.monthYearReceived.year,
- offer.monthYearReceived.month,
- ),
- }));
-
- const postData = { background, offers };
-
- createMutation.mutate(postData);
- };
-
- return (
-
- );
+ return ;
}
diff --git a/apps/portal/src/server/router/offers/offers-analysis-router.ts b/apps/portal/src/server/router/offers/offers-analysis-router.ts
index 25611507..37b0d83b 100644
--- a/apps/portal/src/server/router/offers/offers-analysis-router.ts
+++ b/apps/portal/src/server/router/offers/offers-analysis-router.ts
@@ -216,7 +216,7 @@ export const offersAnalysisRouter = createRouter()
// TODO: Shift yoe out of background to make it mandatory
if (
!overallHighestOffer.profile.background ||
- !overallHighestOffer.profile.background.totalYoe
+ overallHighestOffer.profile.background.totalYoe === undefined
) {
throw new TRPCError({
code: 'BAD_REQUEST',
diff --git a/apps/portal/src/utils/offers/currency/index.tsx b/apps/portal/src/utils/offers/currency/index.tsx
index 8afe5860..373d2984 100644
--- a/apps/portal/src/utils/offers/currency/index.tsx
+++ b/apps/portal/src/utils/offers/currency/index.tsx
@@ -1,6 +1,6 @@
import type { Money } from '~/components/offers/types';
-export function convertCurrencyToString({ currency, value }: Money) {
+export function convertMoneyToString({ currency, value }: Money) {
if (!value) {
return '-';
}
diff --git a/apps/portal/src/utils/offers/link.tsx b/apps/portal/src/utils/offers/link.tsx
new file mode 100644
index 00000000..9e111aad
--- /dev/null
+++ b/apps/portal/src/utils/offers/link.tsx
@@ -0,0 +1,19 @@
+export function getProfileLink(profileId: string, token?: string) {
+ return `${window.location.origin}${getProfilePath(profileId, token)}`;
+}
+
+export function copyProfileLink(profileId: string, token?: string) {
+ // TODO: Add notification
+ navigator.clipboard.writeText(getProfileLink(profileId, token));
+}
+
+export function getProfilePath(profileId: string, token?: string) {
+ if (token) {
+ return `/offers/profile/${profileId}?token=${token}`;
+ }
+ return `/offers/profile/${profileId}`;
+}
+
+export function getProfileEditPath(profileId: string, token: string) {
+ return `/offers/profile/edit/${profileId}?token=${token}`;
+}
diff --git a/apps/portal/src/utils/offers/time.tsx b/apps/portal/src/utils/offers/time.tsx
index 6cd5f16e..4f68adeb 100644
--- a/apps/portal/src/utils/offers/time.tsx
+++ b/apps/portal/src/utils/offers/time.tsx
@@ -32,22 +32,20 @@ export function timeSinceNow(date: Date | number | string) {
export function formatDate(value: Date | number | string) {
const date = new Date(value);
- // Const day = date.toLocaleString('default', { day: '2-digit' });
const month = date.toLocaleString('default', { month: 'short' });
const year = date.toLocaleString('default', { year: 'numeric' });
return `${month} ${year}`;
}
-export function formatMonthYear({ month, year }: MonthYear) {
- const monthString = month < 10 ? month.toString() : `0${month}`;
- const yearString = year.toString();
- return `${monthString}/${yearString}`;
-}
-
export function getCurrentMonth() {
- return getMonth(Date.now());
+ // `getMonth` returns a zero-based month index
+ return getMonth(Date.now()) + 1;
}
export function getCurrentYear() {
return getYear(Date.now());
}
+
+export function convertToMonthYear(date: Date) {
+ return { month: date.getMonth() + 1, year: date.getFullYear() } as MonthYear;
+}
|