Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
668b207
Initial engagements
jmgasper Jan 7, 2026
79c1801
Lint updates
jmgasper Jan 9, 2026
b30ba53
API reference fixes
jmgasper Jan 12, 2026
ab32cda
Look and feel updates to better match copilots portal
jmgasper Jan 12, 2026
9c0a249
Build fix
jmgasper Jan 12, 2026
d2af2a4
Build error seen in CircleCI only
jmgasper Jan 12, 2026
8fe159b
Updates to address gig vs. engagement clarifications
jmgasper Jan 13, 2026
937f5b2
Deploy engagements branch
jmgasper Jan 13, 2026
61e17ee
Fixes and updates to engagements list and display. Remove feedback a…
jmgasper Jan 14, 2026
36abcee
Fix for loading loop on root engagements page
jmgasper Jan 14, 2026
c93448d
Merge branch 'engagements' into engagements_dev_merge
jmgasper Jan 14, 2026
972f133
Merge pull request #1400 from topcoder-platform/engagements_dev_merge
jmgasper Jan 14, 2026
56e163f
Updates for assignment management and tracking for members
jmgasper Jan 15, 2026
378c6fb
Various bug fixes - PM-3414, PM-3415, PM-3425, PM-3429
jmgasper Jan 15, 2026
0284011
Further engagements tweaks for QA feedback
jmgasper Jan 16, 2026
3c58224
Better assignment tracking for assigned members
jmgasper Jan 16, 2026
e3ad21d
Merge pull request #1406 from topcoder-platform/dev
kkartunov Jan 16, 2026
de73eee
Use name instead of handle on calendar.
jmgasper Jan 19, 2026
5e559c1
Merge branch 'engagements' of github.com:topcoder-platform/platform-u…
jmgasper Jan 19, 2026
743c155
Lint
jmgasper Jan 19, 2026
bbf72a2
Phone number to applications and QA fixes
jmgasper Jan 20, 2026
0d1b73c
API path fix
jmgasper Jan 20, 2026
100f1ab
Fix conflict errors happening
jmgasper Jan 20, 2026
4798d28
Member experience collecting for engagements, and a bug fix for revie…
jmgasper Jan 20, 2026
3a9256c
Feedback and experience fixes - also fix engagements link in wallet-a…
jmgasper Jan 20, 2026
fb22501
QA fixes
jmgasper Jan 21, 2026
a7f0122
UI and QA fixes
jmgasper Jan 21, 2026
b9bb781
QA fixes
jmgasper Jan 21, 2026
08be028
Better handling of markdown in engagements display
jmgasper Jan 22, 2026
c1eac23
QA and feedback fixes related to PM-3559
jmgasper Jan 22, 2026
05b0475
Better alignment and search for "any" as a country
jmgasper Jan 22, 2026
138b614
Merge branch 'dev' into engagements
jmgasper Jan 22, 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
1 change: 1 addition & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ workflows:
only:
- dev
- mm-final-2025-reveal
- engagements

- deployQa:
context: org-global
Expand Down
1 change: 1 addition & 0 deletions craco.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ module.exports = {
'@profiles': resolve('src/apps/profiles/src'),
'@wallet': resolve('src/apps/wallet/src'),
'@walletAdmin': resolve('src/apps/wallet-admin/src'),
'@engagements': resolve('src/apps/engagements/src'),

'@platform': resolve('src/apps/platform/src'),
// aliases used in SCSS files
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@
"webpack-merge": "^5.10.0"
},
"resolutions": {
"@types/react": "18.0.35",
"@types/react": "18.3.27",
"string-width": "4.2.0",
"node-fetch": "2.6.7",
"nth-check": "2.0.1",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable max-len */
export const newsletters: Array<{ id: string, name: string, desc: string }> = [
{
desc: 'A weekly summary of available ways to earn, including gig work, challenges, and Thrive articles.',
desc: 'A weekly summary of available ways to earn, including engagements, challenges, and Thrive articles.',
id: 'd0c48e9da3',
name: 'Work Opportunities',
},
Expand Down
20 changes: 11 additions & 9 deletions src/apps/calendar/src/CalendarApp.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { FC, useContext, useEffect, useMemo } from 'react'
import { Outlet, Routes } from 'react-router-dom'

import { routerContext, RouterContextData } from '~/libs/core'
import { ProfileProvider, routerContext, RouterContextData } from '~/libs/core'

import { CalendarContextProvider, Layout, SWRConfigProvider } from './lib'
import { toolTitle } from './calendar-app.routes'
Expand All @@ -19,14 +19,16 @@ const CalendarApp: FC = () => {
}, [])

return (
<CalendarContextProvider>
<SWRConfigProvider>
<Layout>
<Outlet />
<Routes>{childRoutes}</Routes>
</Layout>
</SWRConfigProvider>
</CalendarContextProvider>
<ProfileProvider>
<CalendarContextProvider>
<SWRConfigProvider>
<Layout>
<Outlet />
<Routes>{childRoutes}</Routes>
</Layout>
</SWRConfigProvider>
</CalendarContextProvider>
</ProfileProvider>
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { addMonths, endOfMonth, startOfMonth, subMonths } from 'date-fns'
import { FC, useCallback, useContext, useEffect, useMemo, useState } from 'react'

import { useProfileContext } from '~/libs/core'
import { Button } from '~/libs/ui'

import { Calendar, CalendarLegend, MonthNavigation } from '../../lib/components'
Expand All @@ -12,6 +13,7 @@ import styles from './PersonalCalendarPage.module.scss'

const PersonalCalendarPage: FC = () => {
const calendarContext = useContext(CalendarContext)
const profileContext = useProfileContext()
const [currentDate, setCurrentDate] = useState<Date>(new Date())
const [selectedDates, setSelectedDates] = useState<Set<string>>(new Set())
const leaveDatesState = useFetchLeaveDates()
Expand Down Expand Up @@ -107,14 +109,35 @@ const PersonalCalendarPage: FC = () => {
return `${count} date${count > 1 ? 's' : ''} selected`
}, [selectedDates])

const profile = profileContext.profile
const displayName = useMemo(() => {
const firstName = profile?.firstName?.trim()
const lastName = profile?.lastName?.trim()
const fullName = [firstName, lastName]
.filter(Boolean)
.join(' ')

return (
fullName
|| profile?.handle
|| calendarContext.loginUserInfo?.handle
|| 'Your calendar'
)
}, [
calendarContext.loginUserInfo?.handle,
profile?.firstName,
profile?.handle,
profile?.lastName,
])

const errorMessage = actionError || (error ? 'Something went wrong. Please try again.' : '')

return (
<div className={styles.page}>
<header className={styles.header}>
<p className={styles.subtitle}>Welcome back</p>
<h2 className={styles.title}>
{calendarContext.loginUserInfo?.handle ?? 'Your calendar'}
{displayName}
</h2>
</header>

Expand Down
1 change: 1 addition & 0 deletions src/apps/engagements/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './src'
24 changes: 24 additions & 0 deletions src/apps/engagements/src/EngagementsApp.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { FC } from 'react'
import { useContext } from 'react'
import { Outlet, Routes } from 'react-router-dom'

import type { RouterContextData } from '~/libs/core'
import { routerContext } from '~/libs/core'

import { toolTitle } from './engagements.routes'
import { EngagementsSwr } from './lib'

const EngagementsApp: FC<{}> = () => {
const { getChildRoutes }: RouterContextData = useContext(routerContext)

return (
<EngagementsSwr>
<Outlet />
<Routes>
{getChildRoutes(toolTitle)}
</Routes>
</EngagementsSwr>
)
}

export default EngagementsApp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
@import '@libs/ui/styles/includes';

.card {
display: grid;
gap: 12px;
padding: 18px;
border-radius: 16px;
border: 1px solid $black-10;
background: $tc-white;
transition: box-shadow 0.2s ease, transform 0.2s ease, border-color 0.2s ease;
}

.clickable {
cursor: pointer;
}

.clickable:hover {
border-color: $turq-160;
box-shadow: 0 12px 30px rgba($black-100, 0.08);
transform: translateY(-2px);
}

.header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 12px;
}

.titleRow {
display: flex;
align-items: center;
gap: 8px;
}

.titleIcon {
width: 18px;
height: 18px;
color: $teal-100;
}

.title {
margin: 0;
font-size: 1rem;
color: $black-100;
}

.titleLink {
color: $turq-160;
text-decoration: none;
font-weight: 600;
}

.titleLink:hover,
.titleLink:focus {
color: $turq-140;
text-decoration: underline;
}

.metaRow {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 12px;
}

.metaItem {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 0.85rem;
color: $black-80;
}

.metaIcon {
width: 16px;
height: 16px;
color: $teal-100;
}

.actions {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}

.viewDetailsButton {
margin-left: auto;
padding: 0;
font-weight: 600;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { FC, MouseEvent, useCallback } from 'react'
import { Link, useNavigate } from 'react-router-dom'
import classNames from 'classnames'

import { Button, IconSolid } from '~/libs/ui'

import type { Application } from '../../lib/models'
import { formatDate } from '../../lib/utils'
import { rootRoute } from '../../engagements.routes'
import { ApplicationStatusBadge } from '../application-status-badge'
import { StatusBadge } from '../status-badge'

import styles from './ApplicationCard.module.scss'

interface ApplicationCardProps {
application: Application
onClick?: () => void
}

const ApplicationCard: FC<ApplicationCardProps> = (props: ApplicationCardProps) => {
const navigate = useNavigate()
const application = props.application
const onClick = props.onClick

const canNavigate = Boolean(application.engagement?.nanoId)

const engagementTitle = application.engagement?.title ?? 'Engagement details unavailable'
const engagementStatus = application.engagement?.status
const engagementLink = application.engagement?.nanoId
? `${rootRoute}/${application.engagement.nanoId}`
: undefined

const handleOpenDetails = useCallback((event: MouseEvent<HTMLButtonElement>) => {
event.stopPropagation()
onClick?.()
}, [onClick])

const handleTitleClick = useCallback((event: MouseEvent<HTMLAnchorElement>) => {
event.stopPropagation()
}, [])

const handleCardClick = useCallback(() => {
if (!application.engagement?.nanoId) {
return
}

navigate(`${rootRoute}/${application.engagement.nanoId}`)
}, [navigate, application.engagement?.nanoId])

return (
<div
className={classNames(
styles.card,
canNavigate && styles.clickable,
)}
onClick={handleCardClick}
>
<div className={styles.header}>
<div className={styles.titleRow}>
<IconSolid.BriefcaseIcon className={styles.titleIcon} />
<h3 className={styles.title}>
{engagementLink ? (
<Link
to={engagementLink}
className={styles.titleLink}
onClick={handleTitleClick}
>
{engagementTitle}
</Link>
) : (
engagementTitle
)}
</h3>
</div>
<ApplicationStatusBadge status={application.status} size='sm' />
</div>
<div className={styles.metaRow}>
<div className={styles.metaItem}>
<IconSolid.CalendarIcon className={styles.metaIcon} />
<span>{`Applied ${formatDate(application.createdAt)}`}</span>
</div>
{engagementStatus && (
<StatusBadge status={engagementStatus} size='sm' />
)}
</div>
<div className={styles.actions}>
{onClick && (
<Button
label='View Application'
onClick={handleOpenDetails}
link
className={styles.viewDetailsButton}
/>
)}
</div>
</div>
)
}

export default ApplicationCard
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as ApplicationCard } from './ApplicationCard'
Loading
Loading