Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
8935e60
fix: added work experience description with bullet points support
hentrymartin Feb 12, 2026
c512bb3
PM-3642 #time 2h added work experience description with bullet points…
hentrymartin Feb 12, 2026
9030c30
PM-3642 #time 1.5h added style white listing for list style
hentrymartin Feb 12, 2026
87cb149
PM-3689 #time 2h fixed phone card alignment in phone modal
hentrymartin Feb 12, 2026
b1a463c
fix: lint
hentrymartin Feb 12, 2026
f52a0ff
Merge pull request #1471 from topcoder-platform/pm-3689
hentrymartin Feb 13, 2026
9ff2bf7
Merge pull request #1469 from topcoder-platform/pm-3642
hentrymartin Feb 13, 2026
ef6c7ae
PM-3829 #time 6h ai assisted skill generation for copilot opportunity…
vas3a Feb 13, 2026
05c5e3f
Merge pull request #1474 from topcoder-platform/PM-3829_ai-assisted-s…
vas3a Feb 13, 2026
32f20a7
PM-3855 Enforce profile completeness for engagement application
himaniraghav3 Feb 15, 2026
58dee42
Merge pull request #1475 from topcoder-platform/PM-3855
kkartunov Feb 16, 2026
c950f6d
PM-3829 #time 15min fix skill save
vas3a Feb 16, 2026
ef6a97c
Merge pull request #1477 from topcoder-platform/PM-3829_ai-assisted-s…
vas3a Feb 16, 2026
00049f5
PM-3624 Add mobile view for calendar
himaniraghav3 Feb 16, 2026
83ee9dd
Replace hardcoded colors with vars
himaniraghav3 Feb 16, 2026
e1d9992
Merge pull request #1478 from topcoder-platform/PM-3624
himaniraghav3 Feb 16, 2026
aef27e3
PM-2662 #time 2h allow submissions to be visible in winners tab if su…
hentrymartin Feb 16, 2026
9f4035a
Merge pull request #1479 from topcoder-platform/pm-2662_5
kkartunov Feb 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@
}

.cell {
min-height: 72px;
min-height: 60px;
padding: 6px;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@
}

.userItem {
width: 100%;
border-radius: 8px;
padding: 8px 10px;
font-weight: 700;
Expand Down Expand Up @@ -129,16 +128,16 @@

@media (max-width: 768px) {
.teamCalendar {
padding: 12px;
padding: 5px;
}

.grid {
gap: 8px;
gap: 6px;
}

.cell {
min-height: 96px;
padding: 10px;
min-height: 60px;
padding: 6px;
}

.userItem {
Expand All @@ -148,4 +147,89 @@
.dateNumber {
font-size: 15px;
}

.cellButton {
cursor: pointer;
}
}

.cellButton {
all: unset;
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
gap: 8px;
cursor: default;
}


.countBadge {
align-self: center;
font-size: 12px;
font-weight: 800;
padding: 4px 6px;
border-radius: 999px;
background: #acaeb3;
color: #ffffff;
line-height: 1;
}

.modalRoot {
position: fixed;
inset: 0;
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
}

.backdrop {
position: absolute;
inset: 0;
background: var(--text-secondary);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[⚠️ correctness]
The background color for .backdrop uses a CSS variable --text-secondary. Ensure that this variable is defined and has the intended value, as missing or incorrect variable definitions can lead to unexpected styling.

}

.popover {
position: relative;
z-index: 1;

width: calc(100vw - 32px);
max-width: 420px;
max-height: 70vh;
overflow-y: auto;

background: #fff;
border: 1px solid #e5e7eb;
border-radius: 16px;
box-shadow: 0 24px 64px $tc-black;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[❗❗ correctness]
The box-shadow property uses a variable $tc-black, which is not defined in the provided context. Ensure that this variable is defined elsewhere in the codebase to avoid potential issues with the styling.

}

.popoverHeader {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 14px;
border-bottom: 1px solid #e5e7eb;
}

.popoverTitle {
font-weight: 800;
color: #111827;
}

.closeBtn {
all: unset;
cursor: pointer;
font-size: 18px;
line-height: 1;
padding: 6px 8px;
border-radius: 8px;
}

.popoverBody {
padding: 12px 14px;
display: flex;
flex-direction: column;
gap: 8px;
}
143 changes: 123 additions & 20 deletions src/apps/calendar/src/lib/components/TeamCalendar/TeamCalendar.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { isWeekend } from 'date-fns'
import { FC, useMemo } from 'react'
import { format, isWeekend } from 'date-fns'
import { FC, useCallback, useMemo, useState } from 'react'
import classNames from 'classnames'

import { LoadingSpinner } from '~/libs/ui'
import { useCheckIsMobile } from '~/libs/shared'

import { LeaveStatus, TeamLeaveDate } from '../../models'
import { getDateKey, getMonthDates } from '../../utils'
Expand Down Expand Up @@ -60,6 +61,25 @@ export const TeamCalendar: FC<TeamCalendarProps> = (props: TeamCalendarProps) =>
const currentDate = props.currentDate
const isLoading = props.isLoading
const teamLeaveDates = props.teamLeaveDates
const [openDateKey, setOpenDateKey] = useState<string | undefined>(undefined)

const isMobile: boolean = useCheckIsMobile()

const closePopover = useCallback(() => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[⚠️ performance]
The useCheckIsMobile hook is used to determine if the device is mobile. Ensure that this hook is efficient and does not cause unnecessary re-renders, as it is used in a useCallback dependency array.

setOpenDateKey(undefined)
}, [])

const handleCellClick = useCallback(
(e: React.MouseEvent<HTMLButtonElement>) => {
if (!isMobile) return

const dateKey = e.currentTarget.dataset.dateKey
if (!dateKey) return

setOpenDateKey(prev => (prev === dateKey ? undefined : dateKey))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[⚠️ correctness]
The setOpenDateKey function toggles the state based on the previous value. Consider using a functional update to ensure the state update is based on the latest state, which is more reliable in concurrent rendering scenarios.

},
[isMobile],
)

const monthDates = useMemo(
() => getMonthDates(currentDate.getFullYear(), currentDate.getMonth()),
Expand Down Expand Up @@ -116,44 +136,126 @@ export const TeamCalendar: FC<TeamCalendarProps> = (props: TeamCalendarProps) =>
const overflowCount = sortedUsers.length - displayedUsers.length
const weekendClass = isWeekend(date) ? styles.weekend : undefined

// Mobile popover open/close
const isOpen = openDateKey === dateKey
const leaveCount = sortedUsers.length

return (
<div
key={dateKey}
className={classNames(styles.cell, weekendClass, {
[styles.loading]: isLoading,
[styles.hasLeave]: leaveCount > 0,
})}
>
<span className={styles.dateNumber}>{date.getDate()}</span>
<div className={styles.userList}>
{displayedUsers.length > 0
&& displayedUsers.map((user, userIndex) => {
const isHolidayStatus = user.status === LeaveStatus.WIPRO_HOLIDAY
|| user.status === LeaveStatus.HOLIDAY
{/* Whole cell tappable on mobile */}
<button
type='button'
className={styles.cellButton}
disabled={isLoading}
onClick={handleCellClick}
data-date-key={dateKey}
aria-haspopup='dialog'
aria-expanded={isMobile ? isOpen : undefined}
aria-label={`Open leave list for ${dateKey}`}
>
<span className={styles.dateNumber}>{date.getDate()}</span>

{/* MOBILE: only show count badge */}
{isMobile && leaveCount > 0 && (
<span className={styles.countBadge}>{leaveCount}</span>
)}

{/* DESKTOP: show list in the cell (your existing UI) */}
{!isMobile && (
<div className={styles.userList}>
{displayedUsers.length > 0
&& displayedUsers.map((user, userIndex) => {
const isHolidayStatus
= user.status === LeaveStatus.WIPRO_HOLIDAY
|| user.status === LeaveStatus.HOLIDAY

return (
<div
key={`${dateKey}-${user.userId}-${userIndex.toString()}`}
className={classNames(
styles.userItem,
isHolidayStatus
? styles.userHoliday
: styles.userLeave,
)}
>
{getUserDisplayName(user)}
</div>
)
})}
{overflowCount > 0 && (
<div className={styles.overflowIndicator}>
{`+${overflowCount} more`}
</div>
)}
</div>
)}
</button>
</div>
)
})}
</div>

{isMobile && openDateKey && (() => {
const selectedDate = paddedDates.find(d => d && getDateKey(d) === openDateKey)
if (!selectedDate) return undefined

const selectedEntry = teamLeaveDates.find(item => item.date === openDateKey)
const selectedUsers = [...(selectedEntry?.usersOnLeave ?? [])].sort(compareUsersByName)

return (
<div className={styles.modalRoot}>
<div className={styles.backdrop} onClick={closePopover} />

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[💡 design]
The backdrop div for closing the popover uses an onClick handler. Ensure that this does not interfere with other click events or cause unexpected behavior, especially in complex UI interactions.


<div className={styles.popover} role='dialog' aria-label='Users on leave'>
<div className={styles.popoverHeader}>
<div className={styles.popoverTitle}>
{format(selectedDate, 'EEE, dd MMM yyyy')}
</div>

<button
type='button'
className={styles.closeBtn}
onClick={closePopover}
aria-label='Close'
>
</button>
</div>

<div className={styles.popoverBody}>
{selectedUsers.length === 0 ? (
<div className={styles.emptyState}>No leave</div>
) : (
selectedUsers.map((user, idx) => {
const isHolidayStatus
= user.status === LeaveStatus.WIPRO_HOLIDAY
|| user.status === LeaveStatus.HOLIDAY

return (
<div
key={`${dateKey}-${user.userId}-${userIndex.toString()}`}
key={`${openDateKey}-${user.userId}-${idx.toString()}`}
className={classNames(
styles.userItem,
isHolidayStatus
? styles.userHoliday
: styles.userLeave,
isHolidayStatus ? styles.userHoliday : styles.userLeave,
)}
>
{getUserDisplayName(user)}
</div>
)
})}
{overflowCount > 0 && (
<div className={styles.overflowIndicator}>
{`+${overflowCount} more`}
</div>
})
)}
</div>
</div>
)
})}
</div>
</div>
)
})()}

{isLoading && (
<div className={styles.loadingOverlay}>
Expand All @@ -162,6 +264,7 @@ export const TeamCalendar: FC<TeamCalendarProps> = (props: TeamCalendarProps) =>
)}
</div>
)

}

export default TeamCalendar
Loading
Loading