diff --git a/apps/portal/prisma/migrations/20221102121331_update_location_for_offers/migration.sql b/apps/portal/prisma/migrations/20221102121331_update_location_for_offers/migration.sql new file mode 100644 index 00000000..06a7d7af --- /dev/null +++ b/apps/portal/prisma/migrations/20221102121331_update_location_for_offers/migration.sql @@ -0,0 +1,21 @@ +/* + Warnings: + + - You are about to drop the column `location` on the `OffersExperience` table. All the data in the column will be lost. + - You are about to drop the column `location` on the `OffersOffer` table. All the data in the column will be lost. + - Added the required column `cityId` to the `OffersOffer` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "OffersExperience" DROP COLUMN "location", +ADD COLUMN "cityId" TEXT; + +-- AlterTable +ALTER TABLE "OffersOffer" DROP COLUMN "location", +ADD COLUMN "cityId" TEXT NOT NULL; + +-- AddForeignKey +ALTER TABLE "OffersExperience" ADD CONSTRAINT "OffersExperience_cityId_fkey" FOREIGN KEY ("cityId") REFERENCES "City"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "OffersOffer" ADD CONSTRAINT "OffersOffer_cityId_fkey" FOREIGN KEY ("cityId") REFERENCES "City"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/apps/portal/public/resumes-logo.svg b/apps/portal/public/resumes-logo.svg new file mode 100644 index 00000000..01606d50 --- /dev/null +++ b/apps/portal/public/resumes-logo.svg @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/apps/portal/src/components/global/AppShell.tsx b/apps/portal/src/components/global/AppShell.tsx index 3c36e0d9..b1191d45 100644 --- a/apps/portal/src/components/global/AppShell.tsx +++ b/apps/portal/src/components/global/AppShell.tsx @@ -1,7 +1,7 @@ import clsx from 'clsx'; import Link from 'next/link'; import { useRouter } from 'next/router'; -import { signIn, signOut, useSession } from 'next-auth/react'; +import { signOut, useSession } from 'next-auth/react'; import type { ReactNode } from 'react'; import { Fragment, useState } from 'react'; import { Menu, Transition } from '@headlessui/react'; @@ -19,12 +19,14 @@ import GoogleAnalytics from './GoogleAnalytics'; import MobileNavigation from './MobileNavigation'; import type { ProductNavigationItems } from './ProductNavigation'; import ProductNavigation from './ProductNavigation'; +import loginPageHref from '../shared/loginPageHref'; type Props = Readonly<{ children: ReactNode; }>; function ProfileJewel() { + const router = useRouter(); const { data: session, status } = useSession(); const isSessionLoading = status === 'loading'; @@ -32,25 +34,20 @@ function ProfileJewel() { return null; } + const loginHref = loginPageHref(); if (session == null) { - return ( - { - event.preventDefault(); - signIn(); - }}> - Sign in + return router.pathname !== loginHref.pathname ? ( + + Log In - ); + ) : null; } const userNavigation = [ { href: '/profile', name: 'Profile' }, { href: '/api/auth/signout', - name: 'Sign out', + name: 'Log out', onClick: (event: MouseEvent) => { event.preventDefault(); signOut(); @@ -139,7 +136,7 @@ export default function AppShell({ children }: Props) { return ( -
+
{/* Narrow sidebar */} {currentProductNavigation.showGlobalNav && (
@@ -186,9 +183,10 @@ export default function AppShell({ children }: Props) { setIsShown={setMobileMenuOpen} /> {/* Content area */} -
-
-
+
+ {/* Navigation Bar */} +
+
- - {/* Main content */} -
- {children} -
+ {/* Main Content */} +
{children}
diff --git a/apps/portal/src/components/global/HomeNavigation.ts b/apps/portal/src/components/global/HomeNavigation.ts index 96739432..d9316ee3 100644 --- a/apps/portal/src/components/global/HomeNavigation.ts +++ b/apps/portal/src/components/global/HomeNavigation.ts @@ -1,22 +1,21 @@ -import type { ProductNavigationItems } from '~/components/global/ProductNavigation'; - -const navigation: ProductNavigationItems = [ - { href: '/offers', name: 'Offers' }, - { href: '/questions', name: 'Question Bank' }, - { - children: [ - { href: '/resumes', name: 'View Resumes' }, - { href: '/resumes/submit', name: 'Submit Resume' }, - ], - href: '#', - name: 'Resumes', - }, -]; +// Not using this for now. +// const navigation: ProductNavigationItems = [ +// { href: '/offers', name: 'Offers' }, +// { href: '/questions', name: 'Question Bank' }, +// { +// children: [ +// { href: '/resumes', name: 'View Resumes' }, +// { href: '/resumes/submit', name: 'Submit Resume' }, +// ], +// href: '#', +// name: 'Resumes', +// }, +// ]; const config = { googleAnalyticsMeasurementID: 'G-DBLZDQ2ZZN', - navigation, - showGlobalNav: true, + navigation: [], + showGlobalNav: false, title: 'Tech Interview Handbook', titleHref: '/', }; diff --git a/apps/portal/src/components/global/ProductNavigation.tsx b/apps/portal/src/components/global/ProductNavigation.tsx index aeff5e80..bdd8efd6 100644 --- a/apps/portal/src/components/global/ProductNavigation.tsx +++ b/apps/portal/src/components/global/ProductNavigation.tsx @@ -35,14 +35,16 @@ export default function ProductNavigation({ - {titleHref !== '/' && - (logo ?? ( - Tech Interview Handbook - ))} +
+ {titleHref !== '/' && + (logo ?? ( + Tech Interview Handbook + ))} +
{title}
diff --git a/apps/portal/src/components/offers/Breadcrumb.tsx b/apps/portal/src/components/offers/Breadcrumb.tsx index 30eb07b4..d303070d 100644 --- a/apps/portal/src/components/offers/Breadcrumb.tsx +++ b/apps/portal/src/components/offers/Breadcrumb.tsx @@ -1,45 +1,50 @@ -export type BreadcrumbStep = { +import clsx from 'clsx'; +import { ChevronRightIcon } from '@heroicons/react/20/solid'; + +export type BreadcrumbStep = Readonly<{ label: string; step?: number; -}; +}>; type BreadcrumbsProps = Readonly<{ currentStep: number; setStep: (nextStep: number) => void; - steps: Array; + steps: ReadonlyArray; }>; -function getPrimaryText(text: string) { - return

{text}

; -} - -function getSlateText(text: string) { - return

{text}

; -} - -function getTextWithLink(text: string, onClickHandler: () => void) { - return ( -

- {text} -

- ); -} - export function Breadcrumbs({ steps, currentStep, setStep }: BreadcrumbsProps) { return ( -
- {steps.map(({ label, step }, index) => ( -
- {step === currentStep - ? getPrimaryText(label) - : step !== undefined - ? getTextWithLink(label, () => setStep(step)) - : getSlateText(label)} - {index !== steps.length - 1 && getSlateText('>')} -
- ))} -
+ ); } diff --git a/apps/portal/src/components/offers/JobTypeTabs.tsx b/apps/portal/src/components/offers/JobTypeTabs.tsx new file mode 100644 index 00000000..8ea87bc6 --- /dev/null +++ b/apps/portal/src/components/offers/JobTypeTabs.tsx @@ -0,0 +1,50 @@ +import clsx from 'clsx'; +import { JobType } from '@prisma/client'; + +import { JobTypeLabel } from './types'; + +type Props = Readonly<{ + onChange: (jobType: JobType) => void; + value: JobType; +}>; + +const tabs = [ + { + label: JobTypeLabel.FULLTIME, + value: JobType.FULLTIME, + }, + { + label: JobTypeLabel.INTERN, + value: JobType.INTERN, + }, +]; + +export default function JobTypeTabs({ value, onChange }: Props) { + return ( +
+ +
+ ); +} diff --git a/apps/portal/src/components/offers/dashboard/DashboardOfferCard.tsx b/apps/portal/src/components/offers/dashboard/DashboardOfferCard.tsx index 748a4142..722263b6 100644 --- a/apps/portal/src/components/offers/dashboard/DashboardOfferCard.tsx +++ b/apps/portal/src/components/offers/dashboard/DashboardOfferCard.tsx @@ -1,5 +1,9 @@ +import { + ArrowTrendingUpIcon, + BuildingOfficeIcon, + MapPinIcon, +} from '@heroicons/react/20/solid'; import { JobType } from '@prisma/client'; -import { HorizontalDivider } from '@tih/ui'; import type { JobTitleType } from '~/components/shared/JobTitles'; import { getLabelForJobTitleType } from '~/components/shared/JobTitles'; @@ -10,12 +14,10 @@ import { formatDate } from '~/utils/offers/time'; import type { UserProfileOffer } from '~/types/offers'; type Props = Readonly<{ - disableTopDivider?: boolean; offer: UserProfileOffer; }>; export default function DashboardProfileCard({ - disableTopDivider, offer: { company, income, @@ -27,29 +29,53 @@ export default function DashboardProfileCard({ }, }: Props) { return ( - <> - {!disableTopDivider && } +
-

+

{getLabelForJobTitleType(title as JobTitleType)} -

-

- {location - ? `Company: ${company.name}, ${location.cityName}` - : `Company: ${company.name}`} -

- {level &&

Level: {level}

} +

+
+ {company?.name && ( +
+
+ )} + {location && ( +
+
+ )} + {level && ( +
+
+ )} +
-

{formatDate(monthYearReceived)}

-

+

{jobType === JobType.FULLTIME ? `${convertMoneyToString(income)} / year` : `${convertMoneyToString(income)} / month`}

+

+ {formatDate(monthYearReceived)} +

- +
); } diff --git a/apps/portal/src/components/offers/dashboard/DashboardProfileCard.tsx b/apps/portal/src/components/offers/dashboard/DashboardProfileCard.tsx index 1fcd6e57..854686eb 100644 --- a/apps/portal/src/components/offers/dashboard/DashboardProfileCard.tsx +++ b/apps/portal/src/components/offers/dashboard/DashboardProfileCard.tsx @@ -1,5 +1,6 @@ import { useRouter } from 'next/router'; -import { ArrowRightIcon, XMarkIcon } from '@heroicons/react/24/outline'; +import { BookmarkSlashIcon } from '@heroicons/react/20/solid'; +import { ArrowRightIcon } from '@heroicons/react/24/outline'; import { Button, useToast } from '@tih/ui'; import { useGoogleAnalytics } from '~/components/global/GoogleAnalytics'; @@ -43,54 +44,52 @@ export default function DashboardProfileCard({ ); function handleRemoveProfile() { + // TODO(offers): Confirm before removal. removeSavedProfileMutation.mutate({ profileId: id }); } return ( -
+
{/* Header */} -
-
-
- -
-
-

{profileName}

- -
- Created at {formatDate(createdAt)} +
+
+
+
+
+ +
+
+

+ {profileName} +

+

+ Created at {formatDate(createdAt)} +

+
-
- -
-
-
- - {/* Offers */} -
- {offers.map((offer: UserProfileOffer, index) => - index === 0 ? ( - +
+
-
+ {/* List of Offers */} +
    + {offers.map((offer: UserProfileOffer) => ( +
  • + +
  • + ))} +
+
diff --git a/apps/portal/src/components/offers/offersSubmission/OffersProfileSave.tsx b/apps/portal/src/components/offers/offersSubmission/OffersProfileSave.tsx index 597c08cc..b72641d8 100644 --- a/apps/portal/src/components/offers/offersSubmission/OffersProfileSave.tsx +++ b/apps/portal/src/components/offers/offersSubmission/OffersProfileSave.tsx @@ -1,7 +1,8 @@ import { signIn, useSession } from 'next-auth/react'; import { useState } from 'react'; import { DocumentDuplicateIcon } from '@heroicons/react/20/solid'; -import { BookmarkSquareIcon, CheckIcon } from '@heroicons/react/24/outline'; +import { BookmarkIcon as BookmarkOutlineIcon } from '@heroicons/react/24/outline'; +import { BookmarkIcon as BookmarkSolidIcon } from '@heroicons/react/24/solid'; import { Button, TextInput, useToast } from '@tih/ui'; import { useGoogleAnalytics } from '~/components/global/GoogleAnalytics'; @@ -73,58 +74,67 @@ export default function OffersProfileSave({ }; return ( -
+
-
+

Save for future edits -

-

We value your privacy.

-

- To keep you offer profile strictly anonymous, only people who have the - link below can edit it. + +

+ We value your privacy

-
-
- +
+

+ To keep your offer profile strictly anonymous, it is not linked to + your user account. Only people who have the link below can edit + it. If you want to edit the profile in future, store the link + somewhere. +

+
+
+ +
+
+
+

+ If you do not want to manually store the link somewhere else, you + can add this offers profile to your user account by clicking the + button below. It will still only be editable by you. +

+
+
-
-

- If you do not want to keep the edit link, you can opt to save this - profile under your account's dashboard. It will still only be editable - by you. -

-
-
diff --git a/apps/portal/src/components/offers/offersSubmission/OffersSubmissionAnalysis.tsx b/apps/portal/src/components/offers/offersSubmission/OffersSubmissionAnalysis.tsx index 77c2fc89..b9070760 100644 --- a/apps/portal/src/components/offers/offersSubmission/OffersSubmissionAnalysis.tsx +++ b/apps/portal/src/components/offers/offersSubmission/OffersSubmissionAnalysis.tsx @@ -15,10 +15,12 @@ export default function OffersSubmissionAnalysis({ return (
- Result + Offer Analysis
{!analysis && ( -

Error generating analysis.

+

+ Error generating analysis. +

)} {analysis && ( -
-
-
+
+
+
+
- -
- {steps[step]} -
{JSON.stringify(formMethods.watch(), null, 2)}
- {step === 0 && ( -
-
- )} - {step === 1 && ( -
-
- )} -
-
+
+ +
+ {steps[step]} + {step === 0 && ( +
+
+ )} + {step === 1 && ( +
+
+ )} +
+
+
diff --git a/apps/portal/src/components/offers/offersSubmission/submissionForm/BackgroundForm.tsx b/apps/portal/src/components/offers/offersSubmission/submissionForm/BackgroundForm.tsx index e9806dc5..bfe5ee5f 100644 --- a/apps/portal/src/components/offers/offersSubmission/submissionForm/BackgroundForm.tsx +++ b/apps/portal/src/components/offers/offersSubmission/submissionForm/BackgroundForm.tsx @@ -21,6 +21,7 @@ import { } from '~/utils/offers/currency/CurrencyEnum'; import FormRadioList from '../../forms/FormRadioList'; +import FormSection from '../../forms/FormSection'; import FormSelect from '../../forms/FormSelect'; import FormTextInput from '../../forms/FormTextInput'; @@ -29,29 +30,26 @@ function YoeSection() { background: BackgroundPostData; }>(); const backgroundFields = formState.errors.background; - return ( - <> -
- Years of Experience (YOE) -
-
-
- -
- -
+ return ( + +
+ +
+ +
+
-
+
- -
- +
+
+
); } @@ -113,38 +111,34 @@ function FullTimeJobFields() { return ( <> -
-
- { - if (option) { - setValue('background.experiences.0.title', option.value); - } - }} - /> -
-
- { - if (option) { - setValue('background.experiences.0.companyId', option.value); - setValue('background.experiences.0.companyName', option.label); - } - }} - /> -
+
+ { + if (option) { + setValue('background.experiences.0.title', option.value); + } + }} + /> + { + if (option) { + setValue('background.experiences.0.companyId', option.value); + setValue('background.experiences.0.companyName', option.label); + } + }} + />
-
+
-
+
-
-
-
-
- { - if (option) { - setValue('background.experiences.0.title', option.value); - } - }} - /> -
-
- { - if (option) { - setValue('background.experiences.0.companyId', option.value); - setValue('background.experiences.0.companyName', option.label); - } - }} - /> -
-
-
- - } - endAddOnType="element" - errorMessage={experiencesField?.monthlySalary?.value?.message} - label="Salary (Monthly)" - placeholder="0.00" - startAddOn="$" - startAddOnType="label" - type="number" - {...register(`background.experiences.0.monthlySalary.value`, { - min: { message: FieldError.NON_NEGATIVE_NUMBER, value: 0 }, - valueAsNumber: true, - })} +
+ { + if (option) { + setValue('background.experiences.0.title', option.value); + } + }} + /> + { + if (option) { + setValue('background.experiences.0.companyId', option.value); + setValue('background.experiences.0.companyName', option.label); + } + }} />
- -
- { - if (option) { - setValue('background.experiences.0.cityId', option.value); - setValue('background.experiences.0.cityName', option.label); - } else { - setValue('background.experiences.0.cityId', ''); - setValue('background.experiences.0.cityName', ''); - } - }} + -
+ } + endAddOnType="element" + errorMessage={experiencesField?.monthlySalary?.value?.message} + label="Salary (Monthly)" + placeholder="0.00" + startAddOn="$" + startAddOnType="label" + type="number" + {...register(`background.experiences.0.monthlySalary.value`, { + min: { message: FieldError.NON_NEGATIVE_NUMBER, value: 0 }, + valueAsNumber: true, + })} + /> + + { + if (option) { + setValue('background.experiences.0.cityId', option.value); + setValue('background.experiences.0.cityName', option.label); + } else { + setValue('background.experiences.0.cityId', ''); + setValue('background.experiences.0.cityName', ''); + } + }} + /> ); @@ -324,85 +308,71 @@ function CurrentJobSection() { }); return ( - <> -
- Current / Previous Job -
-
-
- - - - -
- {watchJobType === JobType.FULLTIME ? ( - - ) : ( - - )} -
- + + + + + + {watchJobType === JobType.FULLTIME ? ( + + ) : ( + + )} + ); } function EducationSection() { const { register } = useFormContext(); return ( - <> -
- Education -
-
-
- - -
- -
- -
-
+ +
+ +
- + + + +
); } export default function BackgroundForm() { return ( -
-
+
+

Help us better gauge your offers -

-
+
+
diff --git a/apps/portal/src/components/offers/offersSubmission/submissionForm/OfferDetailsForm.tsx b/apps/portal/src/components/offers/offersSubmission/submissionForm/OfferDetailsForm.tsx index 6eed3983..7c9e52e6 100644 --- a/apps/portal/src/components/offers/offersSubmission/submissionForm/OfferDetailsForm.tsx +++ b/apps/portal/src/components/offers/offersSubmission/submissionForm/OfferDetailsForm.tsx @@ -10,7 +10,7 @@ import { useFieldArray } from 'react-hook-form'; import { PlusIcon } from '@heroicons/react/20/solid'; import { TrashIcon } from '@heroicons/react/24/outline'; import { JobType } from '@prisma/client'; -import { Button, Dialog } from '@tih/ui'; +import { Button, Dialog, HorizontalDivider } from '@tih/ui'; import CitiesTypeahead from '~/components/shared/CitiesTypeahead'; import CompaniesTypeahead from '~/components/shared/CompaniesTypeahead'; @@ -29,9 +29,11 @@ import { yearOptions, } 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 JobTypeTabs from '../../JobTypeTabs'; import type { OfferFormData } from '../../types'; import { JobTypeLabel } from '../../types'; import { @@ -82,9 +84,9 @@ function FullTimeOfferDetailsForm({ }, [watchCurrency, index, setValue]); return ( -
-
-
+
+ +
+
- -
-
- { - if (option) { - setValue(`offers.${index}.companyId`, option.value); - setValue(`offers.${index}.companyName`, option.label); +
+ { + if (option) { + setValue(`offers.${index}.companyId`, option.value); + setValue(`offers.${index}.companyName`, option.label); + } + }} + /> + { + if (option) { + setValue(`offers.${index}.cityId`, option.value); + setValue(`offers.${index}.cityName`, option.label); + } else { + setValue(`offers.${index}.cityId`, ''); + setValue(`offers.${index}.cityName`, ''); + } + }} + /> +
+ + +
+ + } - }} - /> - { - if (option) { - setValue(`offers.${index}.cityId`, option.value); - setValue(`offers.${index}.cityName`, option.label); - } else { - setValue(`offers.${index}.cityId`, ''); - setValue(`offers.${index}.cityName`, ''); + endAddOnType="element" + errorMessage={ + offerFields?.offersFullTime?.totalCompensation?.value?.message } - }} - /> -
-
- -
-
- - } - endAddOnType="element" - errorMessage={ - offerFields?.offersFullTime?.totalCompensation?.value?.message - } - label="Total Compensation (Annual)" - placeholder="0" - required={true} - startAddOn="$" - startAddOnType="label" - type="number" - {...register( - `offers.${index}.offersFullTime.totalCompensation.value`, - { + label="Total Compensation (Annual)" + placeholder="0" + required={true} + startAddOn="$" + startAddOnType="label" + type="number" + {...register( + `offers.${index}.offersFullTime.totalCompensation.value`, + { + min: { message: FieldError.NON_NEGATIVE_NUMBER, value: 0 }, + required: FieldError.REQUIRED, + valueAsNumber: true, + }, + )} + /> +
+
+ + } + endAddOnType="element" + errorMessage={ + offerFields?.offersFullTime?.baseSalary?.value?.message + } + label="Base Salary (Annual)" + placeholder="0" + startAddOn="$" + startAddOnType="label" + type="number" + {...register(`offers.${index}.offersFullTime.baseSalary.value`, { min: { message: FieldError.NON_NEGATIVE_NUMBER, value: 0 }, - required: FieldError.REQUIRED, valueAsNumber: true, - }, - )} - /> -
-
- - } - endAddOnType="element" - errorMessage={offerFields?.offersFullTime?.baseSalary?.value?.message} - label="Base Salary (Annual)" - placeholder="0" - startAddOn="$" - startAddOnType="label" - type="number" - {...register(`offers.${index}.offersFullTime.baseSalary.value`, { - min: { message: FieldError.NON_NEGATIVE_NUMBER, value: 0 }, - valueAsNumber: true, - })} - /> - - } - endAddOnType="element" - errorMessage={offerFields?.offersFullTime?.bonus?.value?.message} - label="Bonus (Annual)" - placeholder="0" - startAddOn="$" - startAddOnType="label" - type="number" - {...register(`offers.${index}.offersFullTime.bonus.value`, { - min: { message: FieldError.NON_NEGATIVE_NUMBER, value: 0 }, - valueAsNumber: true, - })} - /> -
-
- - } - endAddOnType="element" - errorMessage={offerFields?.offersFullTime?.stocks?.value?.message} - label="Stocks (Annual)" - placeholder="0" - startAddOn="$" - startAddOnType="label" - type="number" - {...register(`offers.${index}.offersFullTime.stocks.value`, { - min: { message: FieldError.NON_NEGATIVE_NUMBER, value: 0 }, - valueAsNumber: true, - })} - /> -
-
+ })} + /> + + } + endAddOnType="element" + errorMessage={offerFields?.offersFullTime?.bonus?.value?.message} + label="Bonus (Annual)" + placeholder="0" + startAddOn="$" + startAddOnType="label" + type="number" + {...register(`offers.${index}.offersFullTime.bonus.value`, { + min: { message: FieldError.NON_NEGATIVE_NUMBER, value: 0 }, + valueAsNumber: true, + })} + /> + + } + endAddOnType="element" + errorMessage={offerFields?.offersFullTime?.stocks?.value?.message} + label="Stocks (Annual)" + placeholder="0" + startAddOn="$" + startAddOnType="label" + type="number" + {...register(`offers.${index}.offersFullTime.stocks.value`, { + min: { message: FieldError.NON_NEGATIVE_NUMBER, value: 0 }, + valueAsNumber: true, + })} + /> +
+
+ -
-
-
-
{index > 0 && ( -
+
)} -
+
); } @@ -324,155 +329,153 @@ function InternshipOfferDetailsForm({ }); return ( -
-
-
- + + { + if (option) { + setValue(`offers.${index}.offersIntern.title`, option.value); + } + }} + /> + +
+ { if (option) { - setValue(`offers.${index}.offersIntern.title`, option.value); + setValue(`offers.${index}.companyId`, option.value); + setValue(`offers.${index}.companyName`, option.label); } }} /> -
-
-
-
- { if (option) { - setValue(`offers.${index}.companyId`, option.value); - setValue(`offers.${index}.companyName`, option.label); + setValue(`offers.${index}.cityId`, option.value); + setValue(`offers.${index}.cityName`, option.label); + } else { + setValue(`offers.${index}.cityId`, ''); + setValue(`offers.${index}.cityName`, ''); } }} />
- { - if (option) { - setValue(`offers.${index}.cityId`, option.value); - setValue(`offers.${index}.cityName`, option.label); - } else { - setValue(`offers.${index}.cityId`, ''); - setValue(`offers.${index}.cityName`, ''); +
+ + +
+ + +
+ + } - }} - /> -
-
- - -
-
- -
-
- - } - endAddOnType="element" - errorMessage={ - offerFields?.offersIntern?.monthlySalary?.value?.message - } - label="Salary (Monthly)" - placeholder="0" - required={true} - startAddOn="$" - startAddOnType="label" - type="number" - {...register(`offers.${index}.offersIntern.monthlySalary.value`, { - min: { message: FieldError.NON_NEGATIVE_NUMBER, value: 0 }, - required: FieldError.REQUIRED, - valueAsNumber: true, - })} - /> -
-
+ endAddOnType="element" + errorMessage={ + offerFields?.offersIntern?.monthlySalary?.value?.message + } + label="Salary (Monthly)" + placeholder="0" + required={true} + startAddOn="$" + startAddOnType="label" + type="number" + {...register(`offers.${index}.offersIntern.monthlySalary.value`, { + min: { message: FieldError.NON_NEGATIVE_NUMBER, value: 0 }, + required: FieldError.REQUIRED, + valueAsNumber: true, + })} + /> +
+
+ -
-
-
-
- {index > 0 && ( -
+ + {index > 0 && ( +
+ +
+
+
+ )}
); } @@ -489,7 +492,7 @@ function OfferDetailsFormArray({ const { append, remove, fields } = fieldArrayValues; return ( -
+
{fields.map((item, index) => { return (
@@ -506,7 +509,7 @@ function OfferDetailsFormArray({ icon={PlusIcon} label="Add another offer" size="lg" - variant="tertiary" + variant="secondary" onClick={() => append( jobType === JobType.FULLTIME @@ -547,40 +550,20 @@ export default function OfferDetailsForm({ jobType === JobType.FULLTIME ? JobTypeLabel.INTERN : JobTypeLabel.FULLTIME; return ( -
-
+
+

Fill in your offer details -

-
-
-
-
-
-
+
+ { + if (newJobType === jobType) { + return; + } + + setDialogOpen(true); + }} + /> +
diff --git a/apps/portal/src/components/offers/profile/ProfileComments.tsx b/apps/portal/src/components/offers/profile/ProfileComments.tsx index 70e388c7..dc0ec114 100644 --- a/apps/portal/src/components/offers/profile/ProfileComments.tsx +++ b/apps/portal/src/components/offers/profile/ProfileComments.tsx @@ -12,6 +12,7 @@ import { import { useGoogleAnalytics } from '~/components/global/GoogleAnalytics'; import ExpandableCommentCard from '~/components/offers/profile/comments/ExpandableCommentCard'; import Tooltip from '~/components/offers/util/Tooltip'; +import loginPageHref from '~/components/shared/loginPageHref'; import { copyProfileLink } from '~/utils/offers/link'; import { trpc } from '~/utils/trpc'; @@ -109,108 +110,115 @@ export default function ProfileComments({ ); } return ( -
-
- {isEditable && ( - -
+
+ +
+
+
+ +

Discussions

+ {isEditable || session?.user?.name ? ( +
+