Skip to content

Commit db28a1d

Browse files
authored
feat: organization can be null and inline create (#5123)
1 parent 781be91 commit db28a1d

5 files changed

Lines changed: 107 additions & 72 deletions

File tree

packages/shared/src/components/opportunity/OpportunityEditModal/OpportunityEditOrganizationModal.tsx

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import Textarea from '../../fields/Textarea';
1414
import { Button, ButtonSize, ButtonVariant } from '../../buttons/Button';
1515
import { labels } from '../../../lib';
1616
import {
17+
opportunityCreateOrganizationSchema,
1718
opportunityEditOrganizationSchema,
1819
SocialMediaType,
1920
} from '../../../lib/schema/opportunity';
@@ -345,6 +346,12 @@ export const OpportunityEditOrganizationModal = ({
345346
...clearOrganizationImageMutationOptions(),
346347
});
347348

349+
// if there was no organization we use create schema to require name
350+
const editSchema =
351+
opportunity && !opportunity.organization
352+
? opportunityCreateOrganizationSchema
353+
: opportunityEditOrganizationSchema;
354+
348355
const {
349356
register,
350357
control,
@@ -354,26 +361,27 @@ export const OpportunityEditOrganizationModal = ({
354361
setValue,
355362
watch,
356363
} = useForm({
357-
resolver: zodResolver(opportunityEditOrganizationSchema),
364+
resolver: zodResolver(editSchema),
358365
defaultValues: async () => {
359366
const opportunityData = await promise;
360367

361368
// Merge all link types into a single array
362-
const customLinks = opportunityData.organization.customLinks || [];
363-
const socialLinks = opportunityData.organization.socialLinks || [];
364-
const pressLinks = opportunityData.organization.pressLinks || [];
369+
const customLinks = opportunityData.organization?.customLinks || [];
370+
const socialLinks = opportunityData.organization?.socialLinks || [];
371+
const pressLinks = opportunityData.organization?.pressLinks || [];
365372
const allLinks = [...customLinks, ...socialLinks, ...pressLinks];
366373

367374
return {
368375
organization: {
369-
website: opportunityData.organization.website || '',
370-
description: opportunityData.organization.description || '',
371-
perks: opportunityData.organization.perks || [],
372-
founded: opportunityData.organization.founded || undefined,
373-
location: opportunityData.organization.location || '',
374-
category: opportunityData.organization.category || '',
375-
size: opportunityData.organization.size || undefined,
376-
stage: opportunityData.organization.stage || undefined,
376+
name: opportunityData.organization?.name || '',
377+
website: opportunityData.organization?.website || '',
378+
description: opportunityData.organization?.description || '',
379+
perks: opportunityData.organization?.perks || [],
380+
founded: opportunityData.organization?.founded || undefined,
381+
location: opportunityData.organization?.location || '',
382+
category: opportunityData.organization?.category || '',
383+
size: opportunityData.organization?.size || undefined,
384+
stage: opportunityData.organization?.stage || undefined,
377385
links: allLinks,
378386
},
379387
};
@@ -389,7 +397,7 @@ export const OpportunityEditOrganizationModal = ({
389397

390398
await mutateAsync({
391399
id,
392-
payload: data as z.infer<typeof opportunityEditOrganizationSchema>,
400+
payload: data as z.infer<typeof editSchema>,
393401
organizationImage: organizationImageFile || undefined,
394402
});
395403
} catch (originalError) {
@@ -521,6 +529,17 @@ export const OpportunityEditOrganizationModal = ({
521529
fileSizeLimitMB={5}
522530
/>
523531
</div>
532+
{editSchema === opportunityCreateOrganizationSchema && (
533+
<TextField
534+
{...register('organization.name')}
535+
type="text"
536+
inputId="organizationName"
537+
label="Company name"
538+
fieldType="secondary"
539+
valid={!errors.organization?.name}
540+
hint={errors.organization?.name?.message}
541+
/>
542+
)}
524543
<TextField
525544
{...register('organization.website')}
526545
type="url"

packages/shared/src/features/opportunity/components/OpportunityMatchList.tsx

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,15 @@ export const OpportunityMatchList = ({
8484
passHref
8585
>
8686
<a className="flex items-center gap-3 p-2">
87-
<SourceAvatar
88-
source={{
89-
image: opportunity.organization.image,
90-
handle: opportunity.organization.name,
91-
}}
92-
size={ProfileImageSize.Large}
93-
/>
87+
{!!opportunity.organization && (
88+
<SourceAvatar
89+
source={{
90+
image: opportunity.organization.image,
91+
handle: opportunity.organization.name,
92+
}}
93+
size={ProfileImageSize.Large}
94+
/>
95+
)}
9496
<FlexCol className="min-w-0 flex-1 gap-1">
9597
<Typography
9698
type={TypographyType.Body}
@@ -104,7 +106,7 @@ export const OpportunityMatchList = ({
104106
type={TypographyType.Footnote}
105107
color={TypographyColor.Tertiary}
106108
>
107-
{opportunity.organization.name}{' '}
109+
{opportunity.organization?.name || 'N/A'}{' '}
108110
<DateFormat date={match.createdAt} />
109111
</Typography>
110112
<div

packages/shared/src/features/opportunity/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ export type Opportunity = {
9090
state: OpportunityState;
9191
title: string;
9292
tldr: string;
93-
organization: Organization;
93+
organization?: Organization;
9494
content: {
9595
overview: OpportunityContentBlock;
9696
};

packages/shared/src/lib/schema/opportunity.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ export const opportunityEditInfoSchema = z.object({
104104
),
105105
),
106106
}),
107+
organization: z.object({
108+
name: z.string().nonempty().max(60),
109+
}),
107110
});
108111

109112
export const createOpportunityEditContentSchema = ({
@@ -207,3 +210,10 @@ export const opportunityEditRecruiterSchema = z.object({
207210
bio: z.string().max(2000).optional(),
208211
}),
209212
});
213+
214+
export const opportunityCreateOrganizationSchema =
215+
opportunityEditOrganizationSchema.extend({
216+
organization: opportunityEditOrganizationSchema.shape.organization.extend({
217+
name: z.string().nonempty().max(60),
218+
}),
219+
});

packages/webapp/pages/jobs/[id]/index.tsx

Lines changed: 54 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -406,30 +406,32 @@ const JobPage = (): ReactElement => {
406406
});
407407
}}
408408
/>
409-
<div className="flex items-center">
410-
<SourceAvatar
411-
source={{
412-
image: opportunity.organization.image,
413-
handle: opportunity.organization.name,
414-
}}
415-
size={ProfileImageSize.Medium}
416-
/>
409+
{!!opportunity.organization && (
410+
<div className="flex items-center">
411+
<SourceAvatar
412+
source={{
413+
image: opportunity.organization.image,
414+
handle: opportunity.organization.name,
415+
}}
416+
size={ProfileImageSize.Medium}
417+
/>
417418

418-
<Typography
419-
bold
420-
type={TypographyType.Callout}
421-
color={TypographyColor.Primary}
422-
>
423-
{opportunity.organization.name}{' '}
424419
<Typography
425-
tag={TypographyTag.Span}
426-
color={TypographyColor.Tertiary}
427-
className="font-normal"
420+
bold
421+
type={TypographyType.Callout}
422+
color={TypographyColor.Primary}
428423
>
429-
Verified job
424+
{opportunity.organization.name}{' '}
425+
<Typography
426+
tag={TypographyTag.Span}
427+
color={TypographyColor.Tertiary}
428+
className="font-normal"
429+
>
430+
Verified job
431+
</Typography>
430432
</Typography>
431-
</Typography>
432-
</div>
433+
</div>
434+
)}
433435
{/* Recruiter */}
434436
{!!opportunity.recruiters?.[0] && (
435437
<div className="flex items-center gap-2">
@@ -704,7 +706,7 @@ const JobPage = (): ReactElement => {
704706
}}
705707
/>
706708

707-
{opportunity.organization.website && (
709+
{!!opportunity.organization?.website && (
708710
<Link href={opportunity.organization.website} passHref>
709711
<Button
710712
tag="a"
@@ -721,34 +723,36 @@ const JobPage = (): ReactElement => {
721723
)}
722724
</div>
723725
{/* Company information */}
724-
<div className="flex px-4">
725-
<SourceAvatar
726-
source={{
727-
image: opportunity.organization.image,
728-
handle: opportunity.organization.name,
729-
}}
730-
size={ProfileImageSize.Large}
731-
/>
726+
{!!opportunity.organization && (
727+
<div className="flex px-4">
728+
<SourceAvatar
729+
source={{
730+
image: opportunity.organization.image,
731+
handle: opportunity.organization.name,
732+
}}
733+
size={ProfileImageSize.Large}
734+
/>
732735

733-
<div className="flex flex-shrink flex-col flex-wrap">
734-
<Typography
735-
type={TypographyType.Body}
736-
color={TypographyColor.Primary}
737-
>
738-
{opportunity.organization.name}
739-
</Typography>
740-
<Typography
741-
type={TypographyType.Footnote}
742-
color={TypographyColor.Tertiary}
743-
>
744-
{companyStageMap[opportunity.organization.stage]}{' '}
745-
{opportunity.organization.category}
746-
</Typography>
736+
<div className="flex flex-shrink flex-col flex-wrap">
737+
<Typography
738+
type={TypographyType.Body}
739+
color={TypographyColor.Primary}
740+
>
741+
{opportunity.organization.name}
742+
</Typography>
743+
<Typography
744+
type={TypographyType.Footnote}
745+
color={TypographyColor.Tertiary}
746+
>
747+
{companyStageMap[opportunity.organization.stage]}{' '}
748+
{opportunity.organization.category}
749+
</Typography>
750+
</div>
747751
</div>
748-
</div>
752+
)}
749753

750754
{/* SoMe Links */}
751-
{opportunity.organization.socialLinks?.length > 0 && (
755+
{opportunity.organization?.socialLinks?.length > 0 && (
752756
<div className="flex gap-2 px-4">
753757
{opportunity.organization.socialLinks.map(
754758
({ link, socialType }) => (
@@ -786,7 +790,7 @@ const JobPage = (): ReactElement => {
786790
Founded
787791
</Typography>
788792
<Typography type={TypographyType.Footnote} bold>
789-
{opportunity.organization.founded}
793+
{opportunity.organization?.founded || 'N/A'}
790794
</Typography>
791795

792796
<Typography
@@ -796,7 +800,7 @@ const JobPage = (): ReactElement => {
796800
HQ
797801
</Typography>
798802
<Typography type={TypographyType.Footnote} bold>
799-
{opportunity.organization.location}
803+
{opportunity.organization?.location || 'N/A'}
800804
</Typography>
801805

802806
<Typography
@@ -806,13 +810,13 @@ const JobPage = (): ReactElement => {
806810
Employees
807811
</Typography>
808812
<Typography type={TypographyType.Footnote} bold>
809-
{companySizeMap[opportunity.organization.size]}
813+
{companySizeMap[opportunity.organization?.size] || 'N/A'}
810814
</Typography>
811815
</div>
812816
</SimpleTooltip>
813817

814818
{/* Description */}
815-
{opportunity.organization.description && (
819+
{!!opportunity.organization?.description && (
816820
<SimpleTooltip
817821
content={
818822
canEdit ? labels.opportunity.companyInfoEditNotice : undefined
@@ -830,7 +834,7 @@ const JobPage = (): ReactElement => {
830834
)}
831835

832836
{/* Perks & Benefits */}
833-
{opportunity.organization.perks && (
837+
{opportunity.organization?.perks?.length > 0 && (
834838
<div className="flex flex-col gap-2 px-4">
835839
<Typography bold type={TypographyType.Callout}>
836840
Perks & Benefits

0 commit comments

Comments
 (0)