Skip to content

Commit 647eb97

Browse files
authored
feat: feedback improvements (#5291)
1 parent 8b6fd32 commit 647eb97

3 files changed

Lines changed: 110 additions & 39 deletions

File tree

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

Lines changed: 100 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,16 @@ import {
66
TypographyColor,
77
TypographyType,
88
} from '../typography/Typography';
9-
import { FlexCol, FlexRow } from '../utilities';
10-
import type { FeedbackClassification } from '../../features/opportunity/types';
9+
import { FlexCol } from '../utilities';
10+
import type {
11+
AnonymousUserContext,
12+
FeedbackClassification,
13+
} from '../../features/opportunity/types';
1114
import {
1215
FeedbackSentiment,
1316
FeedbackUrgency,
1417
} from '../../features/opportunity/types';
18+
import { getRecruiterExperienceLevelLabel } from '../../lib/user';
1519

1620
const getSentimentLabel = (sentiment: FeedbackSentiment): string => {
1721
switch (sentiment) {
@@ -26,7 +30,20 @@ const getSentimentLabel = (sentiment: FeedbackSentiment): string => {
2630
}
2731
};
2832

29-
const getSentimentStyles = (sentiment: FeedbackSentiment): string => {
33+
const getSentimentBorderColor = (sentiment: FeedbackSentiment): string => {
34+
switch (sentiment) {
35+
case FeedbackSentiment.Positive:
36+
return 'border-l-accent-avocado-default';
37+
case FeedbackSentiment.Neutral:
38+
return 'border-l-border-subtlest-tertiary';
39+
case FeedbackSentiment.Negative:
40+
return 'border-l-accent-ketchup-default';
41+
default:
42+
return 'border-l-border-subtlest-tertiary';
43+
}
44+
};
45+
46+
const getSentimentBadgeStyles = (sentiment: FeedbackSentiment): string => {
3047
switch (sentiment) {
3148
case FeedbackSentiment.Positive:
3249
return 'bg-action-upvote-float text-action-upvote-default';
@@ -42,31 +59,66 @@ const getSentimentStyles = (sentiment: FeedbackSentiment): string => {
4259
const getUrgencyLabel = (urgency: FeedbackUrgency): string => {
4360
switch (urgency) {
4461
case FeedbackUrgency.Low:
45-
return 'Low';
62+
return 'Low priority';
4663
case FeedbackUrgency.Medium:
47-
return 'Medium';
64+
return 'Medium priority';
4865
case FeedbackUrgency.High:
49-
return 'High';
66+
return 'High priority';
5067
case FeedbackUrgency.Critical:
5168
return 'Critical';
5269
default:
53-
return 'Unknown';
70+
return '';
5471
}
5572
};
5673

5774
const getUrgencyStyles = (urgency: FeedbackUrgency): string => {
5875
switch (urgency) {
5976
case FeedbackUrgency.Low:
60-
return 'bg-action-upvote-float text-action-upvote-default';
77+
return 'text-text-tertiary';
6178
case FeedbackUrgency.Medium:
62-
return 'bg-overlay-quaternary-onion text-accent-onion-default';
79+
return 'text-accent-onion-default';
6380
case FeedbackUrgency.High:
64-
return 'bg-overlay-quaternary-bacon text-accent-bacon-default';
81+
return 'text-accent-bacon-default';
6582
case FeedbackUrgency.Critical:
66-
return 'bg-action-downvote-float text-action-downvote-default';
83+
return 'text-accent-ketchup-default';
6784
default:
68-
return 'bg-surface-tertiary text-text-secondary';
85+
return 'text-text-tertiary';
86+
}
87+
};
88+
89+
interface UserContextDisplayProps {
90+
userContext: AnonymousUserContext;
91+
}
92+
93+
const UserContextDisplay = ({
94+
userContext,
95+
}: UserContextDisplayProps): ReactElement | null => {
96+
const seniorityLabel = getRecruiterExperienceLevelLabel(
97+
userContext.seniority ?? undefined,
98+
);
99+
100+
const hasAnyData = seniorityLabel || userContext.locationCountry;
101+
102+
if (!hasAnyData) {
103+
return null;
69104
}
105+
106+
const infoParts: string[] = [];
107+
if (seniorityLabel) {
108+
infoParts.push(`${seniorityLabel} experience`);
109+
}
110+
if (userContext.locationCountry) {
111+
infoParts.push(userContext.locationCountry);
112+
}
113+
114+
return (
115+
<Typography
116+
type={TypographyType.Caption1}
117+
color={TypographyColor.Secondary}
118+
>
119+
{infoParts.join(' · ')}
120+
</Typography>
121+
);
70122
};
71123

72124
interface FeedbackCardProps {
@@ -76,44 +128,53 @@ interface FeedbackCardProps {
76128
const FeedbackCard = ({ feedback }: FeedbackCardProps): ReactElement => {
77129
const showSentiment = feedback.sentiment !== FeedbackSentiment.Unspecified;
78130
const showUrgency = feedback.urgency !== FeedbackUrgency.Unspecified;
131+
const hasUserContext = feedback.userContext;
79132

80133
return (
81-
<FlexCol className="gap-4 rounded-16 border border-border-subtlest-tertiary bg-surface-float p-4">
82-
{(showSentiment || showUrgency) && (
83-
<FlexRow className="flex-wrap items-center gap-2">
84-
{showSentiment && (
85-
<span
86-
className={classNames(
87-
'rounded-8 px-2 py-1 typo-footnote',
88-
getSentimentStyles(feedback.sentiment),
89-
)}
90-
>
91-
{getSentimentLabel(feedback.sentiment)}
92-
</span>
93-
)}
94-
{showUrgency && (
95-
<span
96-
className={classNames(
97-
'rounded-8 px-2 py-1 typo-footnote',
98-
getUrgencyStyles(feedback.urgency),
99-
)}
100-
>
101-
{getUrgencyLabel(feedback.urgency)} urgency
102-
</span>
103-
)}
104-
</FlexRow>
134+
<div
135+
className={classNames(
136+
'flex flex-col gap-3 rounded-12 border border-l-4 border-border-subtlest-tertiary bg-background-subtle p-4',
137+
getSentimentBorderColor(feedback.sentiment),
105138
)}
139+
>
140+
{/* Header with sentiment badge and urgency */}
141+
<div className="flex items-center justify-between">
142+
{showSentiment && (
143+
<span
144+
className={classNames(
145+
'rounded-8 px-2 py-0.5 font-bold typo-caption1',
146+
getSentimentBadgeStyles(feedback.sentiment),
147+
)}
148+
>
149+
{getSentimentLabel(feedback.sentiment)}
150+
</span>
151+
)}
152+
{showUrgency && (
153+
<Typography
154+
type={TypographyType.Caption1}
155+
className={getUrgencyStyles(feedback.urgency)}
156+
>
157+
{getUrgencyLabel(feedback.urgency)}
158+
</Typography>
159+
)}
160+
</div>
106161

162+
{/* Feedback quote */}
107163
{feedback.answer && (
108164
<Typography
109165
type={TypographyType.Callout}
110-
color={TypographyColor.Secondary}
166+
color={TypographyColor.Primary}
111167
className="whitespace-pre-wrap break-words"
112168
>
113169
&ldquo;{feedback.answer}&rdquo;
114170
</Typography>
115171
)}
116-
</FlexCol>
172+
173+
{/* User context - seniority and location */}
174+
{hasUserContext && (
175+
<UserContextDisplay userContext={feedback.userContext} />
176+
)}
177+
</div>
117178
);
118179
};
119180

@@ -123,7 +184,7 @@ export interface FeedbackListProps {
123184

124185
export const FeedbackList = ({ feedback }: FeedbackListProps): ReactElement => {
125186
return (
126-
<FlexCol className="gap-4">
187+
<FlexCol className="gap-3">
127188
{feedback.length > 0 ? (
128189
feedback.map((item, index) => (
129190
<FeedbackCard key={item.answer ?? index} feedback={item} />

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,10 @@ export const FEEDBACK_CLASSIFICATION_FRAGMENT = gql`
730730
sentiment
731731
urgency
732732
answer
733+
userContext {
734+
seniority
735+
locationCountry
736+
}
733737
}
734738
`;
735739

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,12 +306,18 @@ export enum FeedbackUrgency {
306306
Critical = 4,
307307
}
308308

309+
export type AnonymousUserContext = {
310+
seniority: string | null;
311+
locationCountry: string | null;
312+
};
313+
309314
export type FeedbackClassification = {
310315
platform: FeedbackPlatform;
311316
category: FeedbackCategory;
312317
sentiment: FeedbackSentiment;
313318
urgency: FeedbackUrgency;
314319
answer?: string;
320+
userContext?: AnonymousUserContext | null;
315321
};
316322

317323
export interface OpportunityFeedbackData {

0 commit comments

Comments
 (0)