Skip to content

Commit 60b5cfe

Browse files
authored
Merge branch 'main' into eng-499-implement-feedback-widget-funneling-submissions-into-linear
2 parents 46e5cdf + a52a732 commit 60b5cfe

File tree

3 files changed

+141
-21
lines changed

3 files changed

+141
-21
lines changed

packages/shared/src/components/cards/common/ActionButtons.tsx

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -147,12 +147,7 @@ const ActionButtons = ({
147147
className,
148148
)}
149149
>
150-
<div
151-
className={classNames(
152-
'flex items-center',
153-
variant === 'grid' && 'flex-1 justify-between',
154-
)}
155-
>
150+
<div className="flex flex-1 items-center justify-between">
156151
<Tooltip
157152
content={isUpvoteActive ? 'Remove upvote' : 'More like this'}
158153
side={variant === 'grid' ? 'bottom' : undefined}

packages/shared/src/features/profile/components/experience/UserExperiencesList.spec.tsx

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ describe('UserExperiencesList', () => {
100100
});
101101

102102
describe('Basic rendering', () => {
103-
it('should render empty fragment when no experiences provided', () => {
103+
it('should render empty state with add button for owner when no experiences provided', () => {
104104
const user = createUser();
105105
renderComponent({
106106
experiences: [],
@@ -109,6 +109,23 @@ describe('UserExperiencesList', () => {
109109
user,
110110
});
111111

112+
// Owner should see empty state with title and add button
113+
expect(screen.getByText('Work Experience')).toBeInTheDocument();
114+
expect(screen.getByText('Add your work experience')).toBeInTheDocument();
115+
expect(
116+
screen.getByRole('link', { name: /add your first work experience/i }),
117+
).toBeInTheDocument();
118+
});
119+
120+
it('should render empty fragment for non-owner when no experiences provided', () => {
121+
const user = createUser({ id: 'otheruser', username: 'otheruser' });
122+
renderComponent({
123+
experiences: [],
124+
title: 'Work Experience',
125+
experienceType: UserExperienceType.Work,
126+
user,
127+
});
128+
112129
expect(screen.queryByText('Work Experience')).not.toBeInTheDocument();
113130
});
114131

@@ -461,13 +478,13 @@ describe('UserExperiencesList', () => {
461478
user,
462479
});
463480

464-
// The edit button is rendered as a link/button with href
465-
const editLinks = screen.getAllByRole('link');
466-
const editButton = editLinks.find((link) =>
467-
link.getAttribute('href')?.includes('settings/profile/experience/work'),
468-
);
469-
expect(editButton).toBeTruthy();
470-
expect(editButton?.getAttribute('href')).toBe(
481+
// The edit button is rendered as a link/button with href to settings page
482+
const editButton = screen.getByRole('link', {
483+
name: 'Edit Work Experience',
484+
});
485+
expect(editButton).toBeInTheDocument();
486+
expect(editButton).toHaveAttribute(
487+
'href',
471488
'https://app.daily.dev/settings/profile/experience/work',
472489
);
473490
});

packages/shared/src/features/profile/components/experience/UserExperiencesList.tsx

Lines changed: 115 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import type { ReactElement } from 'react';
22
import React, { useMemo } from 'react';
3-
import type {
4-
UserExperience,
5-
UserExperienceType,
6-
} from '../../../../graphql/user/profile';
3+
import type { UserExperience } from '../../../../graphql/user/profile';
4+
import { UserExperienceType } from '../../../../graphql/user/profile';
75
import {
86
Typography,
7+
TypographyColor,
98
TypographyTag,
109
TypographyType,
1110
} from '../../../../components/typography/Typography';
@@ -17,13 +16,71 @@ import {
1716
ButtonSize,
1817
ButtonVariant,
1918
} from '../../../../components/buttons/Button';
20-
import { MoveToIcon, EditIcon } from '../../../../components/icons';
19+
import {
20+
MoveToIcon,
21+
PlusIcon,
22+
EditIcon,
23+
JobIcon,
24+
TerminalIcon,
25+
TourIcon,
26+
} from '../../../../components/icons';
27+
import { GraduationIcon } from '../../../../components/icons/Graduation';
28+
import { MedalIcon } from '../../../../components/icons/Medal';
29+
import { VolunteeringIcon } from '../../../../components/icons/Volunteering';
2130
import { IconSize } from '../../../../components/Icon';
2231
import Link from '../../../../components/utilities/Link';
2332
import { useAuthContext } from '../../../../contexts/AuthContext';
2433
import { webappUrl } from '../../../../lib/constants';
2534
import type { PublicProfile } from '../../../../lib/user';
2635
import { useProfilePreview } from '../../../../hooks/profile/useProfilePreview';
36+
import type { IconProps } from '../../../../components/Icon';
37+
38+
const experienceTypeConfig: Record<
39+
UserExperienceType,
40+
{
41+
icon: React.FC<IconProps>;
42+
label: string;
43+
heading: string;
44+
subheading: string;
45+
}
46+
> = {
47+
[UserExperienceType.Work]: {
48+
icon: JobIcon,
49+
label: 'work experience',
50+
heading: 'Add your work experience',
51+
subheading: "Show where you've worked and what you've accomplished",
52+
},
53+
[UserExperienceType.Education]: {
54+
icon: GraduationIcon,
55+
label: 'education',
56+
heading: 'Add your education',
57+
subheading: 'Share your academic background and achievements',
58+
},
59+
[UserExperienceType.Certification]: {
60+
icon: MedalIcon,
61+
label: 'certification',
62+
heading: 'Add your certifications',
63+
subheading: 'Showcase your professional certifications and credentials',
64+
},
65+
[UserExperienceType.OpenSource]: {
66+
icon: TerminalIcon,
67+
label: 'open source contribution',
68+
heading: 'Add your open source work',
69+
subheading: 'Highlight your contributions to open source projects',
70+
},
71+
[UserExperienceType.Project]: {
72+
icon: TourIcon,
73+
label: 'project',
74+
heading: 'Add your projects',
75+
subheading: 'Share your side projects and publications',
76+
},
77+
[UserExperienceType.Volunteering]: {
78+
icon: VolunteeringIcon,
79+
label: 'volunteering experience',
80+
heading: 'Add your volunteering',
81+
subheading: 'Share your community involvement and volunteer work',
82+
},
83+
};
2784

2885
interface UserExperienceListProps<T extends UserExperience> {
2986
experiences: T[];
@@ -75,13 +132,64 @@ export function UserExperienceList<T extends UserExperience>({
75132
[experiences],
76133
);
77134

78-
if (!user || !experiences?.length) {
135+
const hasExperiences = experiences?.length > 0;
136+
137+
if (!user) {
138+
return null;
139+
}
140+
141+
if (!hasExperiences && !isOwner) {
79142
return null;
80143
}
81144

82145
const showMoreUrl = `${webappUrl}${user.username}/${experienceType}`;
83146
const editBaseUrl = `${webappUrl}settings/profile/experience/edit`;
147+
const addUrl = `${editBaseUrl}?type=${experienceType}`;
84148
const settingsUrl = `${webappUrl}settings/profile/experience/${experienceType}`;
149+
const config = experienceTypeConfig[experienceType];
150+
const IconComponent = config.icon;
151+
152+
if (!hasExperiences && isOwner) {
153+
return (
154+
<div className="flex flex-col gap-3 py-4">
155+
{title && (
156+
<Typography tag={TypographyTag.H2} type={TypographyType.Body} bold>
157+
{title}
158+
</Typography>
159+
)}
160+
<div className="flex flex-col items-center gap-4 rounded-16 bg-surface-float p-6">
161+
<div className="flex size-14 items-center justify-center rounded-full bg-overlay-quaternary-cabbage">
162+
<IconComponent size={IconSize.XLarge} />
163+
</div>
164+
<div className="flex flex-col items-center gap-1 text-center">
165+
<Typography
166+
type={TypographyType.Body}
167+
color={TypographyColor.Primary}
168+
bold
169+
>
170+
{config.heading}
171+
</Typography>
172+
<Typography
173+
type={TypographyType.Footnote}
174+
color={TypographyColor.Tertiary}
175+
>
176+
{config.subheading}
177+
</Typography>
178+
</div>
179+
<Link href={addUrl} passHref>
180+
<Button
181+
tag="a"
182+
variant={ButtonVariant.Secondary}
183+
size={ButtonSize.Small}
184+
icon={<PlusIcon />}
185+
>
186+
Add your first {config.label}
187+
</Button>
188+
</Link>
189+
</div>
190+
</div>
191+
);
192+
}
85193

86194
return (
87195
<div className="flex flex-col gap-3 py-4">
@@ -90,7 +198,7 @@ export function UserExperienceList<T extends UserExperience>({
90198
<Typography tag={TypographyTag.H2} type={TypographyType.Body} bold>
91199
{title}
92200
</Typography>
93-
{settingsUrl && isOwner && (
201+
{isOwner && (
94202
<Link href={settingsUrl} passHref>
95203
<Button
96204
tag="a"

0 commit comments

Comments
 (0)