[offers][feat] Add token check and refactor UI (#499)

* [offers][fix] Fix and refactor UI

* [offers][feat] Add token check in submission results page

* [offers][refactor] Refactor text display
pull/500/head
Ai Ling 2 years ago committed by GitHub
parent b3eb2f6d6b
commit 230b32be34
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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(),
})),
);

@ -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(),
})),
);

@ -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(),
})),
);

@ -1,7 +1,7 @@
import clsx from 'clsx'; import clsx from 'clsx';
import { JobType } from '@prisma/client'; import { JobType } from '@prisma/client';
import { JobTypeLabel } from './types'; import { JobTypeLabel } from '~/components/offers/constants';
type Props = Readonly<{ type Props = Readonly<{
onChange: (jobType: JobType) => void; onChange: (jobType: JobType) => void;

@ -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,
};
});

@ -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 = [ export const emptyOption = {
{ label: '',
label: 'Summer', value: '',
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 enum FieldError { export enum FieldError {
NON_NEGATIVE_NUMBER = 'Please fill in a non-negative number in this field.', NON_NEGATIVE_NUMBER = 'Please fill in a non-negative number in this field.',

@ -2,7 +2,7 @@ import type { StaticImageData } from 'next/image';
import Image from 'next/image'; import Image from 'next/image';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import { HOME_URL } from '~/components/offers/types'; import { HOME_URL } from '../constants';
type LeftTextCardProps = Readonly<{ type LeftTextCardProps = Readonly<{
description: string; description: string;

@ -2,7 +2,7 @@ import type { StaticImageData } from 'next/image';
import Image from 'next/image'; import Image from 'next/image';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import { HOME_URL } from '~/components/offers/types'; import { HOME_URL } from '../constants';
type RightTextCarddProps = Readonly<{ type RightTextCarddProps = Readonly<{
description: string; description: string;

@ -9,10 +9,11 @@ import { getLabelForJobTitleType } from '~/components/shared/JobTitles';
import { HorizontalDivider } from '~/../../../packages/ui/dist'; import { HorizontalDivider } from '~/../../../packages/ui/dist';
import { convertMoneyToString } from '~/utils/offers/currency'; import { convertMoneyToString } from '~/utils/offers/currency';
import { getCompanyDisplayText } from '~/utils/offers/string';
import { formatDate } from '~/utils/offers/time'; import { formatDate } from '~/utils/offers/time';
import { JobTypeLabel } from '../constants';
import ProfilePhotoHolder from '../profile/ProfilePhotoHolder'; import ProfilePhotoHolder from '../profile/ProfilePhotoHolder';
import { JobTypeLabel } from '../types';
import type { AnalysisOffer } from '~/types/offers'; import type { AnalysisOffer } from '~/types/offers';
@ -69,7 +70,7 @@ export default function OfferProfileCard({
{getLabelForJobTitleType(title as JobTitleType)}{' '} {getLabelForJobTitleType(title as JobTitleType)}{' '}
{`(${JobTypeLabel[jobType]})`} {`(${JobTypeLabel[jobType]})`}
</p> </p>
<p>{`Company: ${company.name}, ${location}`}</p> <p>{`Company: ${getCompanyDisplayText(company.name, location)}`}</p>
{level && <p>Level: {level}</p>} {level && <p>Level: {level}</p>}
</div> </div>
<div className="col-span-1 row-span-3"> <div className="col-span-1 row-span-3">

@ -93,7 +93,6 @@ export default function OffersProfileSave({
<div className="mt-4 flex gap-4"> <div className="mt-4 flex gap-4">
<div className="grow"> <div className="grow">
<TextInput <TextInput
disabled={true}
isLabelHidden={true} isLabelHidden={true}
label="Edit link" label="Edit link"
value={getProfileLink(profileId, token)} value={getProfileLink(profileId, token)}

@ -4,11 +4,11 @@ import type { SubmitHandler } from 'react-hook-form';
import { FormProvider, useForm } from 'react-hook-form'; import { FormProvider, useForm } from 'react-hook-form';
import { ArrowLeftIcon, ArrowRightIcon } from '@heroicons/react/20/solid'; import { ArrowLeftIcon, ArrowRightIcon } from '@heroicons/react/20/solid';
import { JobType } from '@prisma/client'; import { JobType } from '@prisma/client';
import { Button } from '@tih/ui'; import { Button, useToast } from '@tih/ui';
import { useGoogleAnalytics } from '~/components/global/GoogleAnalytics'; import { useGoogleAnalytics } from '~/components/global/GoogleAnalytics';
import type { BreadcrumbStep } from '~/components/offers/Breadcrumb'; import type { BreadcrumbStep } from '~/components/offers/Breadcrumbs';
import { Breadcrumbs } from '~/components/offers/Breadcrumb'; import { Breadcrumbs } from '~/components/offers/Breadcrumbs';
import BackgroundForm from '~/components/offers/offersSubmission/submissionForm/BackgroundForm'; import BackgroundForm from '~/components/offers/offersSubmission/submissionForm/BackgroundForm';
import OfferDetailsForm from '~/components/offers/offersSubmission/submissionForm/OfferDetailsForm'; import OfferDetailsForm from '~/components/offers/offersSubmission/submissionForm/OfferDetailsForm';
import type { import type {
@ -102,8 +102,9 @@ export default function OffersSubmissionForm({
token: editToken, token: editToken,
}); });
const [isSubmitted, setIsSubmitted] = useState(false); const [isSubmitted, setIsSubmitted] = useState(false);
const { event: gaEvent } = useGoogleAnalytics();
const { event: gaEvent } = useGoogleAnalytics();
const { showToast } = useToast();
const router = useRouter(); const router = useRouter();
const pageRef = useRef<HTMLDivElement>(null); const pageRef = useRef<HTMLDivElement>(null);
const scrollToTop = () => const scrollToTop = () =>
@ -132,13 +133,7 @@ export default function OffersSubmissionForm({
}, },
); );
const steps = [ const steps = [<OfferDetailsForm key={0} />, <BackgroundForm key={1} />];
<OfferDetailsForm
key={0}
defaultJobType={initialOfferProfileValues.offers[0].jobType}
/>,
<BackgroundForm key={1} />,
];
const breadcrumbSteps: Array<BreadcrumbStep> = [ const breadcrumbSteps: Array<BreadcrumbStep> = [
{ {
@ -157,14 +152,14 @@ export default function OffersSubmissionForm({
}, },
]; ];
const goToNextStep = async (currStep: number) => { const setStepWithValidation = async (nextStep: number) => {
if (currStep === 0) { if (nextStep === 1) {
const result = await trigger('offers'); const result = await trigger('offers');
if (!result) { if (!result) {
return; return;
} }
} }
setStep(step + 1); setStep(nextStep);
}; };
const mutationpath = const mutationpath =
@ -175,10 +170,24 @@ export default function OffersSubmissionForm({
const createOrUpdateMutation = trpc.useMutation([mutationpath], { const createOrUpdateMutation = trpc.useMutation([mutationpath], {
onError(error) { onError(error) {
console.error(error.message); console.error(error.message);
showToast({
title:
editProfileId && editToken
? 'Error updating offer profile.'
: 'Error creating offer profile',
variant: 'failure',
});
}, },
onSuccess(data) { onSuccess(data) {
setParams({ profileId: data.id, token: data.token }); setParams({ profileId: data.id, token: data.token });
setIsSubmitted(true); setIsSubmitted(true);
showToast({
title:
editProfileId && editToken
? 'Offer profile updated successfully!'
: 'Offer profile created successfully!',
variant: 'success',
});
}, },
}); });
@ -270,7 +279,7 @@ export default function OffersSubmissionForm({
<div className="flex justify-center bg-slate-100 px-4 py-4 sm:px-6 lg:px-8"> <div className="flex justify-center bg-slate-100 px-4 py-4 sm:px-6 lg:px-8">
<Breadcrumbs <Breadcrumbs
currentStep={step} currentStep={step}
setStep={setStep} setStep={setStepWithValidation}
steps={breadcrumbSteps} steps={breadcrumbSteps}
/> />
</div> </div>
@ -288,7 +297,7 @@ export default function OffersSubmissionForm({
label="Next" label="Next"
variant="primary" variant="primary"
onClick={() => { onClick={() => {
goToNextStep(step); setStepWithValidation(step + 1);
gaEvent({ gaEvent({
action: 'offers.profile_submission_navigate_next', action: 'offers.profile_submission_navigate_next',
category: 'submission', category: 'submission',

@ -2,12 +2,7 @@ import { useFormContext, useWatch } from 'react-hook-form';
import { JobType } from '@prisma/client'; import { JobType } from '@prisma/client';
import { Collapsible, RadioList } from '@tih/ui'; import { Collapsible, RadioList } from '@tih/ui';
import { import { FieldError } from '~/components/offers/constants';
educationFieldOptions,
educationLevelOptions,
emptyOption,
FieldError,
} from '~/components/offers/constants';
import type { BackgroundPostData } from '~/components/offers/types'; import type { BackgroundPostData } from '~/components/offers/types';
import CitiesTypeahead from '~/components/shared/CitiesTypeahead'; import CitiesTypeahead from '~/components/shared/CitiesTypeahead';
import CompaniesTypeahead from '~/components/shared/CompaniesTypeahead'; import CompaniesTypeahead from '~/components/shared/CompaniesTypeahead';
@ -20,6 +15,8 @@ import {
CURRENCY_OPTIONS, CURRENCY_OPTIONS,
} from '~/utils/offers/currency/CurrencyEnum'; } from '~/utils/offers/currency/CurrencyEnum';
import { EducationFieldOptions } from '../../EducationFields';
import { EducationLevelOptions } from '../../EducationLevels';
import FormRadioList from '../../forms/FormRadioList'; import FormRadioList from '../../forms/FormRadioList';
import FormSection from '../../forms/FormSection'; import FormSection from '../../forms/FormSection';
import FormSelect from '../../forms/FormSelect'; import FormSelect from '../../forms/FormSelect';
@ -134,6 +131,9 @@ function FullTimeJobFields() {
if (option) { if (option) {
setValue('background.experiences.0.companyId', option.value); setValue('background.experiences.0.companyId', option.value);
setValue('background.experiences.0.companyName', option.label); setValue('background.experiences.0.companyName', option.label);
} else {
setValue('background.experiences.0.companyId', '');
setValue('background.experiences.0.companyName', '');
} }
}} }}
/> />
@ -343,15 +343,13 @@ function EducationSection() {
<FormSelect <FormSelect
display="block" display="block"
label="Education Level" label="Education Level"
options={educationLevelOptions} options={EducationLevelOptions}
placeholder={emptyOption}
{...register(`background.educations.0.type`)} {...register(`background.educations.0.type`)}
/> />
<FormSelect <FormSelect
display="block" display="block"
label="Field" label="Field"
options={educationFieldOptions} options={EducationFieldOptions}
placeholder={emptyOption}
{...register(`background.educations.0.field`)} {...register(`background.educations.0.field`)}
/> />
</div> </div>

@ -22,20 +22,16 @@ import {
defaultFullTimeOfferValues, defaultFullTimeOfferValues,
defaultInternshipOfferValues, defaultInternshipOfferValues,
} from '../OffersSubmissionForm'; } from '../OffersSubmissionForm';
import { import { FieldError, JobTypeLabel } from '../../constants';
emptyOption,
FieldError,
internshipCycleOptions,
yearOptions,
} from '../../constants';
import FormMonthYearPicker from '../../forms/FormMonthYearPicker'; import FormMonthYearPicker from '../../forms/FormMonthYearPicker';
import FormSection from '../../forms/FormSection'; import FormSection from '../../forms/FormSection';
import FormSelect from '../../forms/FormSelect'; import FormSelect from '../../forms/FormSelect';
import FormTextArea from '../../forms/FormTextArea'; import FormTextArea from '../../forms/FormTextArea';
import FormTextInput from '../../forms/FormTextInput'; import FormTextInput from '../../forms/FormTextInput';
import { InternshipCycleOptions } from '../../InternshipCycles';
import JobTypeTabs from '../../JobTypeTabs'; import JobTypeTabs from '../../JobTypeTabs';
import type { OfferFormData } from '../../types'; import type { OfferFormData } from '../../types';
import { JobTypeLabel } from '../../types'; import { FutureYearsOptions } from '../../Years';
import { import {
Currency, Currency,
CURRENCY_OPTIONS, CURRENCY_OPTIONS,
@ -384,8 +380,7 @@ function InternshipOfferDetailsForm({
display="block" display="block"
errorMessage={offerFields?.offersIntern?.internshipCycle?.message} errorMessage={offerFields?.offersIntern?.internshipCycle?.message}
label="Internship Cycle" label="Internship Cycle"
options={internshipCycleOptions} options={InternshipCycleOptions}
placeholder={emptyOption}
required={true} required={true}
{...register(`offers.${index}.offersIntern.internshipCycle`, { {...register(`offers.${index}.offersIntern.internshipCycle`, {
required: FieldError.REQUIRED, required: FieldError.REQUIRED,
@ -395,8 +390,7 @@ function InternshipOfferDetailsForm({
display="block" display="block"
errorMessage={offerFields?.offersIntern?.startYear?.message} errorMessage={offerFields?.offersIntern?.startYear?.message}
label="Internship Year" label="Internship Year"
options={yearOptions} options={FutureYearsOptions}
placeholder={emptyOption}
required={true} required={true}
{...register(`offers.${index}.offersIntern.startYear`, { {...register(`offers.${index}.offersIntern.startYear`, {
required: FieldError.REQUIRED, required: FieldError.REQUIRED,
@ -522,14 +516,11 @@ function OfferDetailsFormArray({
); );
} }
type OfferDetailsFormProps = Readonly<{ export default function OfferDetailsForm() {
defaultJobType?: JobType; const watchJobType = useWatch({
}>; name: `offers.0.jobType`,
});
export default function OfferDetailsForm({ const [jobType, setJobType] = useState(watchJobType as JobType);
defaultJobType = JobType.FULLTIME,
}: OfferDetailsFormProps) {
const [jobType, setJobType] = useState(defaultJobType);
const [isDialogOpen, setDialogOpen] = useState(false); const [isDialogOpen, setDialogOpen] = useState(false);
const { control } = useFormContext(); const { control } = useFormContext();
const fieldArrayValues = useFieldArray({ control, name: 'offers' }); const fieldArrayValues = useFieldArray({ control, name: 'offers' });
@ -576,8 +567,8 @@ export default function OfferDetailsForm({
label="Switch" label="Switch"
variant="primary" variant="primary"
onClick={() => { onClick={() => {
toggleJobType();
setDialogOpen(false); setDialogOpen(false);
toggleJobType();
}} }}
/> />
} }

@ -7,7 +7,11 @@ import {
import { HorizontalDivider } from '@tih/ui'; import { HorizontalDivider } from '@tih/ui';
import type { OfferDisplayData } from '~/components/offers/types'; import type { OfferDisplayData } from '~/components/offers/types';
import { JobTypeLabel } from '~/components/offers/types';
import {
getCompanyDisplayText,
getJobDisplayText,
} from '~/utils/offers/string';
type Props = Readonly<{ type Props = Readonly<{
offer: OfferDisplayData; offer: OfferDisplayData;
@ -35,19 +39,18 @@ export default function OfferCard({
return ( return (
<div className="flex justify-between px-8"> <div className="flex justify-between px-8">
<div className="flex flex-col"> <div className="flex flex-col">
{(companyName || location) && (
<div className="flex flex-row"> <div className="flex flex-row">
<span> <span>
<BuildingOffice2Icon className="mr-3 h-5" /> <BuildingOffice2Icon className="mr-3 h-5" />
</span> </span>
<span className="font-bold"> <span className="font-bold">
{location ? `${companyName}, ${location.cityName}` : companyName} {getCompanyDisplayText(companyName, location)}
</span> </span>
</div> </div>
)}
<div className="ml-8 flex flex-row"> <div className="ml-8 flex flex-row">
<p> <p>{getJobDisplayText(jobTitle, jobLevel, jobType)}</p>
{jobLevel ? `${jobTitle}, ${jobLevel}` : jobTitle}{' '}
{jobType && `(${JobTypeLabel[jobType]})`}
</p>
</div> </div>
</div> </div>
{!duration && receivedMonth && ( {!duration && receivedMonth && (

@ -13,10 +13,10 @@ import { Button, Dialog, Spinner, Tabs, useToast } from '@tih/ui';
import { useGoogleAnalytics } from '~/components/global/GoogleAnalytics'; import { useGoogleAnalytics } from '~/components/global/GoogleAnalytics';
import type { ProfileDetailTab } from '~/components/offers/constants'; import type { ProfileDetailTab } from '~/components/offers/constants';
import { JobTypeLabel } from '~/components/offers/constants';
import { profileDetailTabs } from '~/components/offers/constants'; import { profileDetailTabs } from '~/components/offers/constants';
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 Tooltip from '~/components/offers/util/Tooltip'; import Tooltip from '~/components/offers/util/Tooltip';
import { getProfileEditPath } from '~/utils/offers/link'; import { getProfileEditPath } from '~/utils/offers/link';

@ -2,7 +2,7 @@ import clsx from 'clsx';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { JobType } from '@prisma/client'; 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 { useGoogleAnalytics } from '~/components/global/GoogleAnalytics';
import OffersTablePagination from '~/components/offers/table/OffersTablePagination'; import OffersTablePagination from '~/components/offers/table/OffersTablePagination';
@ -66,6 +66,7 @@ export default function OffersTable({
event?.preventDefault(); event?.preventDefault();
}, [yoeCategory]); }, [yoeCategory]);
const { showToast } = useToast();
trpc.useQuery( trpc.useQuery(
[ [
'offers.list', 'offers.list',
@ -81,8 +82,11 @@ export default function OffersTable({
}, },
], ],
{ {
onError: (err) => { onError: () => {
alert(err); showToast({
title: 'Error loading the page.',
variant: 'failure',
});
}, },
onSuccess: (response: GetOffersResponse) => { onSuccess: (response: GetOffersResponse) => {
setOffers(response.data); setOffers(response.data);
@ -246,7 +250,7 @@ export default function OffersTable({
{!offers || {!offers ||
(offers.length === 0 && ( (offers.length === 0 && (
<div className="py-16 text-lg"> <div className="py-16 text-lg">
<div className="flex justify-center">No data yet🥺</div> <div className="flex justify-center">No data yet 🥺</div>
</div> </div>
))} ))}
</div> </div>

@ -26,7 +26,7 @@ export default function OffersTablePagination({
<div className="mb-2 text-sm font-normal text-slate-500 md:mb-0"> <div className="mb-2 text-sm font-normal text-slate-500 md:mb-0">
Showing Showing
<span className="font-semibold text-slate-900"> <span className="font-semibold text-slate-900">
{` ${startNumber} - ${endNumber} `} {` ${endNumber > 0 ? startNumber : 0} - ${endNumber} `}
</span> </span>
{`of `} {`of `}
<span className="font-semibold text-slate-900"> <span className="font-semibold text-slate-900">

@ -4,27 +4,6 @@ import type { MonthYear } from '~/components/shared/MonthYearPicker';
import type { Location } from '~/types/offers'; 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 = { export type OffersProfilePostData = {
background: BackgroundPostData; background: BackgroundPostData;
id?: string; id?: string;

@ -8,12 +8,12 @@ import {
UsersIcon, UsersIcon,
} from '@heroicons/react/24/outline'; } from '@heroicons/react/24/outline';
import { HOME_URL } from '~/components/offers/constants';
import offersAnalysis from '~/components/offers/features/images/offers-analysis.png'; import offersAnalysis from '~/components/offers/features/images/offers-analysis.png';
import offersBrowse from '~/components/offers/features/images/offers-browse.png'; import offersBrowse from '~/components/offers/features/images/offers-browse.png';
import offersProfile from '~/components/offers/features/images/offers-profile.png'; import offersProfile from '~/components/offers/features/images/offers-profile.png';
import LeftTextCard from '~/components/offers/features/LeftTextCard'; import LeftTextCard from '~/components/offers/features/LeftTextCard';
import RightTextCard from '~/components/offers/features/RightTextCard'; import RightTextCard from '~/components/offers/features/RightTextCard';
import { HOME_URL } from '~/components/offers/types';
const features = [ const features = [
{ {

@ -6,6 +6,7 @@ import { Spinner, useToast } from '@tih/ui';
import { useGoogleAnalytics } from '~/components/global/GoogleAnalytics'; import { useGoogleAnalytics } from '~/components/global/GoogleAnalytics';
import { ProfileDetailTab } from '~/components/offers/constants'; import { ProfileDetailTab } from '~/components/offers/constants';
import { HOME_URL } from '~/components/offers/constants';
import ProfileComments from '~/components/offers/profile/ProfileComments'; import ProfileComments from '~/components/offers/profile/ProfileComments';
import ProfileDetails from '~/components/offers/profile/ProfileDetails'; import ProfileDetails from '~/components/offers/profile/ProfileDetails';
import ProfileHeader from '~/components/offers/profile/ProfileHeader'; import ProfileHeader from '~/components/offers/profile/ProfileHeader';
@ -13,7 +14,6 @@ import type {
BackgroundDisplayData, BackgroundDisplayData,
OfferDisplayData, OfferDisplayData,
} from '~/components/offers/types'; } from '~/components/offers/types';
import { HOME_URL } from '~/components/offers/types';
import type { JobTitleType } from '~/components/shared/JobTitles'; import type { JobTitleType } from '~/components/shared/JobTitles';
import { getLabelForJobTitleType } from '~/components/shared/JobTitles'; import { getLabelForJobTitleType } from '~/components/shared/JobTitles';

@ -1,11 +1,12 @@
import Error from 'next/error';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { useEffect, useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import { ArrowLeftIcon, ArrowRightIcon } from '@heroicons/react/20/solid'; import { ArrowLeftIcon, ArrowRightIcon } from '@heroicons/react/20/solid';
import { EyeIcon } from '@heroicons/react/24/outline'; import { EyeIcon } from '@heroicons/react/24/outline';
import { Button, Spinner } from '@tih/ui'; import { Button, Spinner } from '@tih/ui';
import type { BreadcrumbStep } from '~/components/offers/Breadcrumb'; import type { BreadcrumbStep } from '~/components/offers/Breadcrumbs';
import { Breadcrumbs } from '~/components/offers/Breadcrumb'; import { Breadcrumbs } from '~/components/offers/Breadcrumbs';
import OffersProfileSave from '~/components/offers/offersSubmission/OffersProfileSave'; import OffersProfileSave from '~/components/offers/offersSubmission/OffersProfileSave';
import OffersSubmissionAnalysis from '~/components/offers/offersSubmission/OffersSubmissionAnalysis'; import OffersSubmissionAnalysis from '~/components/offers/offersSubmission/OffersSubmissionAnalysis';
@ -21,12 +22,21 @@ export default function OffersSubmissionResult() {
token = token as string; token = token as string;
const [step, setStep] = useState(0); const [step, setStep] = useState(0);
const [analysis, setAnalysis] = useState<ProfileAnalysis | null>(null); const [analysis, setAnalysis] = useState<ProfileAnalysis | null>(null);
const [isValidToken, setIsValidToken] = useState(false);
const pageRef = useRef<HTMLDivElement>(null); const pageRef = useRef<HTMLDivElement>(null);
const scrollToTop = () => const scrollToTop = () =>
pageRef.current?.scrollTo({ behavior: 'smooth', top: 0 }); 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( const getAnalysis = trpc.useQuery(
['offers.analysis.get', { profileId: offerProfileId }], ['offers.analysis.get', { profileId: offerProfileId }],
{ {
@ -69,7 +79,7 @@ export default function OffersSubmissionResult() {
return ( return (
<> <>
{getAnalysis.isLoading && ( {(checkToken.isLoading || getAnalysis.isLoading) && (
<div className="flex h-screen w-screen"> <div className="flex h-screen w-screen">
<div className="m-auto mx-auto w-screen justify-center font-medium text-slate-500"> <div className="m-auto mx-auto w-screen justify-center font-medium text-slate-500">
<Spinner display="block" size="lg" /> <Spinner display="block" size="lg" />
@ -77,7 +87,13 @@ export default function OffersSubmissionResult() {
</div> </div>
</div> </div>
)} )}
{!getAnalysis.isLoading && ( {checkToken.isSuccess && !isValidToken && (
<Error
statusCode={403}
title="You do not have permissions to access this page"
/>
)}
{getAnalysis.isSuccess && (
<div ref={pageRef} className="w-full"> <div ref={pageRef} className="w-full">
<div className="flex justify-center"> <div className="flex justify-center">
<div className="block w-full max-w-screen-md overflow-hidden rounded-lg sm:shadow-lg md:my-10"> <div className="block w-full max-w-screen-md overflow-hidden rounded-lg sm:shadow-lg md:my-10">

@ -20,7 +20,7 @@ export default function CurrencySelector({
<Select <Select
display="inline" display="inline"
isLabelHidden={true} isLabelHidden={true}
label="Select fruit" label="Currency"
name="" name=""
options={currencyOptions} options={currencyOptions}
value={selectedCurrency} value={selectedCurrency}

@ -0,0 +1,37 @@
import type { JobType } from '@prisma/client';
import { JobTypeLabel } from '~/components/offers/constants';
import type { Location } from '~/types/offers';
function joinWithComma(...strings: Array<string | null | undefined>) {
return strings.filter((value) => !!value).join(', ');
}
function getLocationDisplayText({ cityName, countryName }: Location) {
return cityName === countryName
? cityName
: joinWithComma(cityName, countryName);
}
export function getCompanyDisplayText(
companyName?: string | null,
location?: Location | null,
) {
if (!location) {
return companyName;
}
return joinWithComma(companyName, getLocationDisplayText(location));
}
export function getJobDisplayText(
jobTitle?: string | null,
jobLevel?: string | null,
jobType?: JobType | null,
) {
let jobDisplay = joinWithComma(jobTitle, jobLevel);
if (jobType) {
jobDisplay = jobDisplay.concat(` (${JobTypeLabel[jobType]})`);
}
return jobDisplay;
}
Loading…
Cancel
Save