Skip to content

Commit b1e79a7

Browse files
authored
refactor: update recruiter logo and analyze page (#5312)
1 parent 3055b80 commit b1e79a7

File tree

7 files changed

+96
-193
lines changed

7 files changed

+96
-193
lines changed

packages/shared/src/components/Logo.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,6 @@ const DevPlusIcon = dynamic(() =>
1919
),
2020
);
2121

22-
const LogoRecruiterSvg = dynamic(() =>
23-
import(
24-
/* webpackChunkName: "logoRecruiterSvg" */ '../svg/LogoRecruiterSvg'
25-
).then((mod) => mod.LogoRecruiterSvg),
26-
);
27-
2822
export enum LogoPosition {
2923
Absolute = 'absolute',
3024
Relative = 'relative',
@@ -152,7 +146,11 @@ export default function Logo({
152146
fallback={LogoText}
153147
/>
154148
)}
155-
{isRecruiter && !compact && <LogoRecruiterSvg />}
149+
{isRecruiter && !compact && (
150+
<span className="hidden rounded-6 border border-accent-cabbage-subtler bg-accent-cabbage-flat px-1.5 py-0.5 font-bold uppercase tracking-wider text-accent-cabbage-default typo-caption2 laptop:inline-block">
151+
Recruiter
152+
</span>
153+
)}
156154
</a>
157155
</LinkWithTooltip>
158156
);

packages/shared/src/components/recruiter/AnalyzeStatusBar.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ import { IconSize } from '../Icon';
1212

1313
const LOADING_STEPS = [
1414
'Analyzing your job description (this may take a minute)',
15-
'Mapping skills, requirements, and intent',
16-
'Scanning the daily.dev network...',
15+
'Extracting skills and requirements',
16+
'Finding matches in our community...',
1717
];
1818

19-
const COMPLETE_MESSAGE = 'Your hiring edge is ready';
19+
const COMPLETE_MESSAGE = 'Your analysis is ready';
2020

2121
type AnalyzeStatusBarProps = {
2222
loadingStep: number;

packages/shared/src/components/recruiter/Header.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ export interface RecruiterHeaderProps {
2424
}
2525

2626
export const RecruiterHeader = ({
27-
title = 'Your potential reach',
28-
subtitle = "See how many developers match your role and what they're interested in.",
27+
title = 'Reach analysis',
28+
subtitle = 'See who in our community fits your role',
2929
headerButton,
3030
}: RecruiterHeaderProps) => {
3131
return (

packages/shared/src/features/opportunity/components/analyze/AnalyzeContent.tsx

Lines changed: 0 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -7,32 +7,20 @@ import {
77
import { useOpportunityPreviewContext } from '../../context/OpportunityPreviewContext';
88
import { OpportunityPreviewStatus } from '../../types';
99
import { JobInfo } from './JobInfo';
10-
import { apiUrl } from '../../../../lib/config';
1110
import { ReachHeroSection } from './ReachHeroSection';
12-
import { InsightCard } from './InsightCard';
13-
import { Chip } from '../../../../components/cards/common/PostTags';
1411
import { PlusUserIcon } from '../../../../components/icons/PlusUser';
1512
import { IconSize } from '../../../../components/Icon';
1613

1714
type AnalyzeContentProps = {
1815
loadingStep: number;
1916
};
2017

21-
const iconSize = 24;
22-
2318
export const AnalyzeContent = ({ loadingStep }: AnalyzeContentProps) => {
2419
const data = useOpportunityPreviewContext();
2520
const isReady = data?.result?.status === OpportunityPreviewStatus.READY;
2621
const totalCount = data?.result?.totalCount ?? 0;
27-
const tags = data?.result?.tags ?? [];
28-
const companies = data?.result?.companies ?? [];
29-
30-
// Mock engagement stat - in production this would come from the API
31-
const avgTimePerWeek = '4.2 hrs';
3222

3323
const isError = data?.result?.status === OpportunityPreviewStatus.ERROR;
34-
const showAggregation =
35-
!isError && loadingStep >= 2 && (isReady || tags.length > 0);
3624
const showReachHero = !isError && loadingStep >= 2;
3725

3826
return (
@@ -60,71 +48,6 @@ export const AnalyzeContent = ({ loadingStep }: AnalyzeContentProps) => {
6048
</div>
6149
)}
6250

63-
{/* Candidate Insights */}
64-
{showAggregation && (
65-
<div className="grid gap-4 tablet:grid-cols-3">
66-
{/* Tags */}
67-
<InsightCard
68-
label="Interested in"
69-
tooltip="Topics these developers actively read and engage with on daily.dev"
70-
isLoading={!isReady}
71-
>
72-
<div className="flex flex-wrap gap-1.5">
73-
{tags.map((tag) => (
74-
<Chip key={tag} className="!my-0">
75-
#{tag}
76-
</Chip>
77-
))}
78-
</div>
79-
</InsightCard>
80-
81-
{/* Companies */}
82-
<InsightCard
83-
label="Currently working at"
84-
tooltip="Companies where matched developers currently work"
85-
isLoading={!isReady}
86-
>
87-
<div className="flex flex-wrap gap-1.5">
88-
{companies.slice(0, 4).map((company) => (
89-
<Chip key={company.name} className="!my-0 gap-1.5">
90-
<img
91-
src={`${apiUrl}/icon?url=${encodeURIComponent(
92-
company.favicon || '',
93-
)}&size=${iconSize}`}
94-
className="size-4 rounded-full bg-surface-float object-contain"
95-
alt={company.name}
96-
/>
97-
<span>{company.name}</span>
98-
</Chip>
99-
))}
100-
</div>
101-
</InsightCard>
102-
103-
{/* Platform Engagement */}
104-
<InsightCard
105-
label="Weekly active time"
106-
tooltip="How much time these developers spend on daily.dev each week"
107-
isLoading={!isReady}
108-
>
109-
<div className="flex flex-col">
110-
<Typography
111-
type={TypographyType.Title2}
112-
bold
113-
className="text-accent-cabbage-default"
114-
>
115-
{avgTimePerWeek}
116-
</Typography>
117-
<Typography
118-
type={TypographyType.Caption1}
119-
color={TypographyColor.Tertiary}
120-
>
121-
avg. per candidate
122-
</Typography>
123-
</div>
124-
</InsightCard>
125-
</div>
126-
)}
127-
12851
{/* Job Summary */}
12952
<div className="rounded-16 border border-border-subtlest-tertiary bg-background-default p-4">
13053
<Typography

packages/shared/src/features/opportunity/components/analyze/JobInfo.tsx

Lines changed: 77 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,38 @@ import {
55
TypographyType,
66
} from '../../../../components/typography/Typography';
77
import { useOpportunityPreviewContext } from '../../context/OpportunityPreviewContext';
8-
import { SeniorityLevel } from '../../protobuf/opportunity';
8+
import { SeniorityLevel, SalaryPeriod } from '../../protobuf/opportunity';
9+
import { LocationType } from '../../protobuf/util';
910
import { seniorityLevelMap } from '../../common';
1011
import { ElementPlaceholder } from '../../../../components/ElementPlaceholder';
1112
import { Chip } from '../../../../components/cards/common/PostTags';
13+
import type { Salary } from '../../types';
14+
15+
const locationTypeMap: Record<LocationType, string | null> = {
16+
[LocationType.UNSPECIFIED]: null,
17+
[LocationType.REMOTE]: 'Remote',
18+
[LocationType.OFFICE]: 'On-site',
19+
[LocationType.HYBRID]: 'Hybrid',
20+
};
21+
22+
const salaryPeriodMap: Record<SalaryPeriod, string> = {
23+
[SalaryPeriod.UNSPECIFIED]: 'year',
24+
[SalaryPeriod.ANNUAL]: 'year',
25+
[SalaryPeriod.MONTHLY]: 'month',
26+
[SalaryPeriod.WEEKLY]: 'week',
27+
[SalaryPeriod.DAILY]: 'day',
28+
[SalaryPeriod.HOURLY]: 'hour',
29+
};
30+
31+
const formatSalary = (salary?: Salary): string | null => {
32+
if (!salary?.min || !salary?.max) {
33+
return null;
34+
}
35+
const min = salary.min / 1000;
36+
const max = salary.max / 1000;
37+
const period = salaryPeriodMap[salary.period ?? SalaryPeriod.UNSPECIFIED];
38+
return `$${min}k - $${max}k/${period}`;
39+
};
1240

1341
type JobInfoProps = {
1442
loadingStep: number;
@@ -33,8 +61,10 @@ export const JobInfo = ({ loadingStep }: JobInfoProps) => {
3361
);
3462
}
3563

64+
const { locations, meta, title, tldr, keywords } = opportunity;
65+
3666
const locationString =
37-
opportunity.locations
67+
locations
3868
?.map((item) =>
3969
[
4070
item.location?.city,
@@ -44,51 +74,68 @@ export const JobInfo = ({ loadingStep }: JobInfoProps) => {
4474
.filter(Boolean)
4575
.join(', '),
4676
)
47-
.join(' · ') || 'Location not specified';
77+
.join(' · ') || null;
4878

4979
const seniorityLabel =
50-
seniorityLevelMap[
51-
opportunity.meta?.seniorityLevel ?? SeniorityLevel.UNSPECIFIED
52-
];
80+
seniorityLevelMap[meta?.seniorityLevel ?? SeniorityLevel.UNSPECIFIED];
81+
82+
const workArrangement =
83+
locationTypeMap[locations?.[0]?.type ?? LocationType.UNSPECIFIED];
84+
85+
const salaryRange = formatSalary(meta?.salary);
86+
87+
// Build meta items array for clean rendering
88+
const metaItems = [
89+
locationString,
90+
seniorityLabel !== 'N/A' ? seniorityLabel : null,
91+
workArrangement,
92+
salaryRange,
93+
].filter(Boolean);
5394

5495
return (
5596
<div className="flex flex-col gap-4">
5697
{/* Title and meta */}
5798
<div>
5899
<Typography type={TypographyType.Title3} bold>
59-
{opportunity.title}
100+
{title}
60101
</Typography>
61-
<div className="mt-1 flex flex-wrap items-center gap-x-2 gap-y-1">
62-
<Typography
63-
type={TypographyType.Footnote}
64-
color={TypographyColor.Tertiary}
65-
>
66-
{locationString}
67-
</Typography>
68-
{seniorityLabel && (
69-
<>
70-
<span className="text-text-quaternary">·</span>
71-
<Typography
72-
type={TypographyType.Footnote}
73-
color={TypographyColor.Tertiary}
74-
>
75-
{seniorityLabel}
76-
</Typography>
77-
</>
78-
)}
79-
</div>
102+
{metaItems.length > 0 && (
103+
<div className="mt-1 flex flex-wrap items-center gap-x-2 gap-y-1">
104+
{metaItems.map((item, index) => (
105+
<React.Fragment key={item}>
106+
{index > 0 && <span className="text-text-quaternary">·</span>}
107+
<Typography
108+
type={TypographyType.Footnote}
109+
color={TypographyColor.Tertiary}
110+
>
111+
{item}
112+
</Typography>
113+
</React.Fragment>
114+
))}
115+
</div>
116+
)}
80117
</div>
81118

119+
{/* TLDR */}
120+
{tldr && (
121+
<Typography
122+
type={TypographyType.Body}
123+
color={TypographyColor.Secondary}
124+
>
125+
<span className="font-bold text-text-primary">TLDR</span> {tldr}
126+
</Typography>
127+
)}
128+
82129
{/* Tech stack */}
83-
{opportunity.keywords && opportunity.keywords.length > 0 && (
130+
{keywords && keywords.length > 0 && (
84131
<div className="flex flex-wrap gap-1.5">
85-
{opportunity.keywords.slice(0, 10).map((tag) => (
132+
{keywords.slice(0, 10).map((tag) => (
86133
<Chip key={tag.keyword} className="!my-0">
87134
#{tag.keyword}
88135
</Chip>
89136
))}
90-
{opportunity.keywords.length > 10 && (
91-
<Chip className="!my-0">+{opportunity.keywords.length - 10}</Chip>
137+
{keywords.length > 10 && (
138+
<Chip className="!my-0">+{keywords.length - 10}</Chip>
92139
)}
93140
</div>
94141
)}

packages/shared/src/features/opportunity/components/analyze/ReachHeroSection.tsx

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ type ReachHeroSectionProps = {
1111
isLoading: boolean;
1212
};
1313

14-
const PASSIVE_PERCENTAGE = 30;
15-
1614
export const ReachHeroSection = ({
1715
totalCount,
1816
isLoading,
@@ -58,29 +56,32 @@ export const ReachHeroSection = ({
5856
return (
5957
<div className="rounded-16 border border-border-subtlest-tertiary bg-background-default p-6">
6058
<div className="flex flex-col items-center">
59+
{/* Label */}
60+
<Typography
61+
type={TypographyType.Body}
62+
color={TypographyColor.Secondary}
63+
>
64+
Potential reach
65+
</Typography>
6166
{/* Hero number */}
6267
<Typography type={TypographyType.Tera} bold className="tabular-nums">
6368
{animatedCount.toLocaleString()}
6469
</Typography>
6570
<Typography
6671
type={TypographyType.Title3}
6772
color={TypographyColor.Primary}
68-
className="mt-1"
6973
>
7074
developers matched
7175
</Typography>
7276

73-
{/* Exclusive stat */}
77+
{/* Community differentiator */}
7478
<div className="mt-5 flex animate-fade-slide-up items-center gap-2 rounded-10 bg-surface-float px-3 py-2">
7579
<div className="size-2 animate-pulse rounded-full bg-status-success" />
7680
<Typography
7781
type={TypographyType.Callout}
7882
color={TypographyColor.Secondary}
7983
>
80-
<span className="font-bold text-text-primary">
81-
{PASSIVE_PERCENTAGE}%
82-
</span>{' '}
83-
are passively open to new opportunities
84+
Active in our community
8485
</Typography>
8586
</div>
8687
</div>

0 commit comments

Comments
 (0)