From d0943c4c2281e7df95befe0736e56c662d351add Mon Sep 17 00:00:00 2001 From: himaniraghav3 Date: Tue, 17 Feb 2026 15:27:15 +0530 Subject: [PATCH 1/4] PM-3840 Show multiple users in popover --- .../TeamCalendar/TeamCalendar.module.scss | 15 +++++++++++++ .../components/TeamCalendar/TeamCalendar.tsx | 22 ++++++++++++++++--- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/apps/calendar/src/lib/components/TeamCalendar/TeamCalendar.module.scss b/src/apps/calendar/src/lib/components/TeamCalendar/TeamCalendar.module.scss index 58ba914d7..083b22e33 100644 --- a/src/apps/calendar/src/lib/components/TeamCalendar/TeamCalendar.module.scss +++ b/src/apps/calendar/src/lib/components/TeamCalendar/TeamCalendar.module.scss @@ -233,3 +233,18 @@ flex-direction: column; gap: 8px; } + +.moreButton { + all: unset; + cursor: pointer; + color: #374151; + font-size: 13px; + font-weight: 700; + display: inline-block; + padding: 2px 0; +} + +.moreButton:hover { + text-decoration: underline; + cursor: pointer; +} diff --git a/src/apps/calendar/src/lib/components/TeamCalendar/TeamCalendar.tsx b/src/apps/calendar/src/lib/components/TeamCalendar/TeamCalendar.tsx index 7aa3bf2b4..afcffeda9 100644 --- a/src/apps/calendar/src/lib/components/TeamCalendar/TeamCalendar.tsx +++ b/src/apps/calendar/src/lib/components/TeamCalendar/TeamCalendar.tsx @@ -81,6 +81,13 @@ export const TeamCalendar: FC = (props: TeamCalendarProps) => [isMobile], ) + const handleMoreClick = useCallback((e: React.MouseEvent) => { + if (isMobile) return // hover only matters on desktop + const dateKey = e.currentTarget.dataset.dateKey + if (!dateKey) return + setOpenDateKey(dateKey) + }, [isMobile]) + const monthDates = useMemo( () => getMonthDates(currentDate.getFullYear(), currentDate.getMonth()), [currentDate], @@ -132,7 +139,7 @@ export const TeamCalendar: FC = (props: TeamCalendarProps) => const leaveEntry = teamLeaveDates.find(item => item.date === dateKey) const users = leaveEntry?.usersOnLeave ?? [] const sortedUsers = [...users].sort(compareUsersByName) - const displayedUsers = sortedUsers.slice(0, 10) + const displayedUsers = sortedUsers.slice(0, 2) const overflowCount = sortedUsers.length - displayedUsers.length const weekendClass = isWeekend(date) ? styles.weekend : undefined @@ -191,7 +198,16 @@ export const TeamCalendar: FC = (props: TeamCalendarProps) => })} {overflowCount > 0 && (
- {`+${overflowCount} more`} +
)} @@ -202,7 +218,7 @@ export const TeamCalendar: FC = (props: TeamCalendarProps) => })} - {isMobile && openDateKey && (() => { + {openDateKey && (() => { const selectedDate = paddedDates.find(d => d && getDateKey(d) === openDateKey) if (!selectedDate) return undefined From f4d99c6c611596227552c81fc7990c02353c0c5f Mon Sep 17 00:00:00 2001 From: himaniraghav3 Date: Tue, 17 Feb 2026 16:40:01 +0530 Subject: [PATCH 2/4] PM-3878 Make open to work fields required --- .../src/pages/open-to-work/index.tsx | 43 +++++++++++++++++-- .../OpenForGigsModifyModal.tsx | 41 +++++++++++++++++- .../ModifyOpenToWorkModal.tsx | 23 +++++++++- 3 files changed, 101 insertions(+), 6 deletions(-) diff --git a/src/apps/onboarding/src/pages/open-to-work/index.tsx b/src/apps/onboarding/src/pages/open-to-work/index.tsx index 9ebfd75bf..e528028dd 100644 --- a/src/apps/onboarding/src/pages/open-to-work/index.tsx +++ b/src/apps/onboarding/src/pages/open-to-work/index.tsx @@ -1,5 +1,5 @@ import { useNavigate } from 'react-router-dom' -import { FC, MutableRefObject, useEffect, useRef, useState } from 'react' +import { Dispatch, FC, MutableRefObject, SetStateAction, useEffect, useRef, useState } from 'react' import { connect } from 'react-redux' import { toast } from 'react-toastify' import classNames from 'classnames' @@ -12,7 +12,8 @@ import { updateOrCreateMemberTraitsAsync, UserTraitIds, UserTraits } from '~/libs/core' import { OpenToWorkData } from '~/libs/shared/lib/components/modify-open-to-work-modal' -import OpenToWorkForm from '~/libs/shared/lib/components/modify-open-to-work-modal/ModifyOpenToWorkModal' +import OpenToWorkForm, +{ validateOpenToWork } from '~/libs/shared/lib/components/modify-open-to-work-modal/ModifyOpenToWorkModal' import { ProgressBar } from '../../components/progress-bar' import { updateMemberOpenForWork, updatePersonalizations } from '../../redux/actions/member' @@ -46,6 +47,14 @@ export const PageOpenToWorkContent: FC = props => { preferredRoles: [], }) + const [submitAttempted, setSubmitAttempted] = useState(false) + + const [formErrors, setFormErrors]: [ + { [key: string]: string }, + Dispatch> + ] + = useState<{ [key: string]: string }>({}) + useEffect(() => { if (!memberPersonalizationTraits) return @@ -61,6 +70,14 @@ export const PageOpenToWorkContent: FC = props => { })) }, [memberPersonalizationTraits, props.availableForGigs]) + function handleFormChange(nextValue: OpenToWorkData): void { + setFormValue(nextValue) + + if (submitAttempted) { + setFormErrors(validateOpenToWork(nextValue)) + } + } + useEffect(() => { if (!loading && !shouldSavingData.current && !!shouldNavigateTo.current) { navigate(shouldNavigateTo.current) @@ -73,6 +90,16 @@ export const PageOpenToWorkContent: FC = props => { } async function goToNextStep(): Promise { + setSubmitAttempted(true) + + const errors = validateOpenToWork(formValue) + setFormErrors(errors) + + if (Object.keys(errors).length > 0) { + // Don't move to next step + return + } + setLoading(true) const existing = memberPersonalizationTraits?.[0]?.traits?.data?.[0] || {} @@ -118,6 +145,14 @@ export const PageOpenToWorkContent: FC = props => { } } + // reset error when open to work toggle off + useEffect(() => { + if (!formValue.availableForGigs) { + setFormErrors({}) + setSubmitAttempted(false) + } + }, [formValue.availableForGigs]) + return (

@@ -136,8 +171,10 @@ export const PageOpenToWorkContent: FC = props => {

diff --git a/src/apps/profiles/src/member-profile/profile-header/OpenForGigsModifyModal/OpenForGigsModifyModal.tsx b/src/apps/profiles/src/member-profile/profile-header/OpenForGigsModifyModal/OpenForGigsModifyModal.tsx index d185ef003..e5699b7b5 100644 --- a/src/apps/profiles/src/member-profile/profile-header/OpenForGigsModifyModal/OpenForGigsModifyModal.tsx +++ b/src/apps/profiles/src/member-profile/profile-header/OpenForGigsModifyModal/OpenForGigsModifyModal.tsx @@ -13,7 +13,8 @@ import { OpenToWorkData } from '~/libs/shared/lib/components/modify-open-to-work import { updateMemberProfile, } from '~/libs/core/lib/profile/profile-functions/profile-store/profile-xhr.store' -import OpenToWorkForm from '~/libs/shared/lib/components/modify-open-to-work-modal/ModifyOpenToWorkModal' +import OpenToWorkForm, +{ validateOpenToWork } from '~/libs/shared/lib/components/modify-open-to-work-modal/ModifyOpenToWorkModal' import styles from './OpenForGigsModifyModal.module.scss' @@ -37,6 +38,14 @@ const OpenForGigsModifyModal: FC = (props: OpenForG preferredRoles: [], }) + const [submitAttempted, setSubmitAttempted] = useState(false) + + const [formErrors, setFormErrors]: [ + { [key: string]: string }, + Dispatch> + ] + = useState<{ [key: string]: string }>({}) + useEffect(() => { if (!memberPersonalizationTraits) return @@ -55,7 +64,25 @@ const OpenForGigsModifyModal: FC = (props: OpenForG props.profile.availableForGigs, ]) + function handleFormChange(nextValue: OpenToWorkData): void { + setFormValue(nextValue) + + if (submitAttempted) { + setFormErrors(validateOpenToWork(nextValue)) + } + } + function handleOpenForWorkSave(): void { + setSubmitAttempted(true) + + const errors = validateOpenToWork(formValue) + setFormErrors(errors) + + if (Object.keys(errors).length > 0) { + // Don't submit + return + } + setIsSaving(true) const existing = memberPersonalizationTraits?.[0]?.traits?.data?.[0] || {} @@ -93,6 +120,14 @@ const OpenForGigsModifyModal: FC = (props: OpenForG }) } + // reset error when open to work toggle off + useEffect(() => { + if (!formValue.availableForGigs) { + setFormErrors({}) + setSubmitAttempted(false) + } + }, [formValue.availableForGigs]) + return ( = (props: OpenForG ) diff --git a/src/libs/shared/lib/components/modify-open-to-work-modal/ModifyOpenToWorkModal.tsx b/src/libs/shared/lib/components/modify-open-to-work-modal/ModifyOpenToWorkModal.tsx index 808407b06..3c07480a0 100644 --- a/src/libs/shared/lib/components/modify-open-to-work-modal/ModifyOpenToWorkModal.tsx +++ b/src/libs/shared/lib/components/modify-open-to-work-modal/ModifyOpenToWorkModal.tsx @@ -16,6 +16,8 @@ interface OpenToWorkFormProps { value: OpenToWorkData onChange: (value: OpenToWorkData) => void disabled?: boolean + formErrors?: { [key: string]: string } + showErrors?: boolean } export const availabilityOptions = [ @@ -38,6 +40,22 @@ export const preferredRoleOptions: InputMultiselectOption[] = [ { label: 'Enterprise Architect', value: 'ENTERPRISE_ARCHITECT' }, ] +export const validateOpenToWork = (value: OpenToWorkData): { [key: string]: string } => { + if (!value.availableForGigs) return {} + + const errors: { [key: string]: string } = {} + + if (!value.availability) { + errors.availability = 'Availability is required.' + } + + if (!value.preferredRoles || value.preferredRoles.length === 0) { + errors.preferredRoles = 'Select at least one preferred role.' + } + + return errors +} + const OpenToWorkForm: FC = (props: OpenToWorkFormProps) => { function toggleOpenForWork(): void { props.onChange({ @@ -98,7 +116,8 @@ const OpenToWorkForm: FC = (props: OpenToWorkFormProps) => onChange={handleAvailabilityChange} disabled={props.disabled} preventAutoFocus - dirty + dirty={props.showErrors} + error={props.showErrors ? props.formErrors?.availability : undefined} /> = (props: OpenToWorkFormProps) => )} onChange={handleRolesChange} disabled={props.disabled} + dirty={props.showErrors} + error={props.showErrors ? props.formErrors?.preferredRoles : undefined} /> )} From b19e2c8f433142ea002d25898b87c8384265d33f Mon Sep 17 00:00:00 2001 From: himaniraghav3 Date: Wed, 18 Feb 2026 11:53:28 +0530 Subject: [PATCH 3/4] PM-3913 Add profile completeness check for private engagements --- .../AssignmentCard.module.scss | 21 +++++ .../assignment-card/AssignmentCard.tsx | 77 ++++++++++++++----- .../my-assignments/MyAssignmentsPage.tsx | 24 +++++- 3 files changed, 103 insertions(+), 19 deletions(-) diff --git a/src/apps/engagements/src/components/assignment-card/AssignmentCard.module.scss b/src/apps/engagements/src/components/assignment-card/AssignmentCard.module.scss index f718651a3..cca913299 100644 --- a/src/apps/engagements/src/components/assignment-card/AssignmentCard.module.scss +++ b/src/apps/engagements/src/components/assignment-card/AssignmentCard.module.scss @@ -109,3 +109,24 @@ width: 100%; } } + +.applyMessage { + display: flex; + flex-direction: column; + gap: $sp-3; + padding: $sp-4; + border-radius: 8px; +} + +.signInLink { + display: inline-block; + width: fit-content; + font-size: 16px; + font-weight: 600; + color: var(--tc-primary); + text-decoration: none; + + &:hover { + text-decoration: underline; + } +} \ No newline at end of file diff --git a/src/apps/engagements/src/components/assignment-card/AssignmentCard.tsx b/src/apps/engagements/src/components/assignment-card/AssignmentCard.tsx index 27b2ab46e..d85e92bd5 100644 --- a/src/apps/engagements/src/components/assignment-card/AssignmentCard.tsx +++ b/src/apps/engagements/src/components/assignment-card/AssignmentCard.tsx @@ -5,6 +5,7 @@ import remarkFrontmatter from 'remark-frontmatter' import remarkGfm from 'remark-gfm' import { Button, IconSolid } from '~/libs/ui' +import { EnvironmentConfig } from '~/config' import type { Engagement, EngagementAssignment } from '../../lib/models' import { formatDate, formatLocation, truncateText } from '../../lib/utils' @@ -58,6 +59,8 @@ interface AssignmentCardProps { onRejectOffer?: () => void onContactTalentManager: (contactEmail?: string) => void canContactTalentManager?: boolean + profileGateError?: string + profileHandle?: string } const DESCRIPTION_MAX_LENGTH = 160 @@ -154,6 +157,42 @@ const AssignmentCard: FC = (props: AssignmentCardProps) => const showAssignedActions = assignmentStatus === 'assigned' const showOfferActions = assignmentStatus === 'selected' + const renderOfferActions = ( + profileGateError?: string, + onAcceptOffer?: () => void, + onRejectOffer?: () => void, + actionButtonClass?: string, + ): JSX.Element | undefined => { + if ( + profileGateError + || !showOfferActions + || !onAcceptOffer + || !onRejectOffer + ) { + return undefined + } + + return ( + <> +