Skip to content

PM-590 Add start date #1047

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 25, 2025
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
2 changes: 1 addition & 1 deletion src/apps/admin/src/lib/models/MobileTableColumn.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
import { TableColumn } from '~/libs/ui'

export interface MobileTableColumn<T> extends TableColumn<T> {
readonly mobileType?: 'label'
readonly mobileType?: 'label' | 'last-value'
}
5 changes: 3 additions & 2 deletions src/apps/copilots/src/copilots.routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,19 @@ export const childRoutes = [
route: '/',
},
{
authRequired: true,
element: <CopilotsRequests />,
id: 'CopilotRequests',
route: '/requests',
},
{
authRequired: true,
element: <CopilotsRequestForm />,
id: 'CopilotRequestForm',
route: '/requests/new',
},
{
authRequired: true,
element: <CopilotsRequests />,
id: 'CopilotRequestDetails',
route: '/requests/:requestId',
Expand All @@ -54,10 +57,8 @@ export const copilotRoutesMap = childRoutes.reduce((allRoutes, route) => (

export const copilotsRoutes: ReadonlyArray<PlatformRoute> = [
{
authRequired: true,
children: [
...childRoutes,

],
domain: AppSubdomain.copilots,
element: <CopilotsApp />,
Expand Down
12 changes: 12 additions & 0 deletions src/apps/copilots/src/pages/copilot-opportunity-details/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { FC, useEffect, useState } from 'react'
import { useNavigate, useParams } from 'react-router-dom'
import moment from 'moment'

import {
ContentLayout,
Expand Down Expand Up @@ -60,6 +61,17 @@ const CopilotOpportunityDetails: FC<{}> = () => {
<span className={styles.infoValue}>{opportunity?.status}</span>
</div>
</div>
<div className={styles.infoColumn}>
<IconOutline.PlayIcon className={styles.icon} />
<div className={styles.infoText}>
<span className={styles.infoHeading}>Start Date</span>
<span className={styles.infoValue}>
{moment(opportunity?.startDate)

Choose a reason for hiding this comment

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

Consider adding a fallback or default value for opportunity?.startDate in case it is undefined or null to prevent potential runtime errors.

Copy link
Collaborator

Choose a reason for hiding this comment

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

image
@himaniraghav3 checked this - looks like moment will intialize with current datetime in case of undefined startDate.
We fine with that?

.format('MMM D, YYYY')}

</span>
</div>
</div>
<div className={styles.infoColumn}>
<IconOutline.CalendarIcon className={styles.icon} />
<div className={styles.infoText}>
Expand Down
22 changes: 19 additions & 3 deletions src/apps/copilots/src/pages/copilot-opportunity-list/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,20 @@ import {
import { CopilotOpportunity } from '../../models/CopilotOpportunity'
import { copilotRoutesMap } from '../../copilots.routes'
import { CopilotOpportunitiesResponse, useCopilotOpportunities } from '../../services/copilot-opportunities'
import { ProjectTypeLabels } from '../../constants'

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

const tableColumns: TableColumn<CopilotOpportunity>[] = [
{
label: 'Title',
propertyName: 'projectName',
type: 'text',
renderer: (copilotOpportunity: CopilotOpportunity) => (

Choose a reason for hiding this comment

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

Suggestion

Consider using a more descriptive element or adding a class to the <div> for better styling and accessibility. This can help with future styling changes or accessibility improvements.

<div>
{copilotOpportunity.projectName}
</div>
),
type: 'element',
},
{
label: 'Status',
Expand All @@ -33,6 +39,7 @@ const tableColumns: TableColumn<CopilotOpportunity>[] = [
type: 'element',
},
{
isSortable: false,
label: 'Skills Required',
propertyName: 'skills',
renderer: (copilotOpportunity: CopilotOpportunity) => (
Expand All @@ -49,7 +56,12 @@ const tableColumns: TableColumn<CopilotOpportunity>[] = [
{
label: 'Type',
propertyName: 'type',
type: 'text',
renderer: (copilotOpportunity: CopilotOpportunity) => (

Choose a reason for hiding this comment

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

Consider handling cases where copilotOpportunity.projectType might not have a corresponding label in ProjectTypeLabels. This will prevent potential runtime errors if an unexpected projectType value is encountered.

<div className={styles.type}>
{ProjectTypeLabels[copilotOpportunity.projectType]}
</div>
),
type: 'element',
},
{
label: 'Starting Date',
Expand Down Expand Up @@ -80,7 +92,10 @@ const CopilotOpportunityList: FC<{}> = () => {
data: opportunities, isValidating, size, setSize,
}: CopilotOpportunitiesResponse = useCopilotOpportunities()

const tableData = useMemo(() => opportunities, [opportunities])
const tableData = useMemo(() => opportunities.map(opportunity => ({
...opportunity,
type: ProjectTypeLabels[opportunity.projectType] ?? '',

Choose a reason for hiding this comment

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

Consider providing a default value or handling the case where opportunity.projectType might not exist in ProjectTypeLabels to avoid potential issues with undefined values.

})), [opportunities])

function loadMore(): void {
setSize(size + 1)
Expand All @@ -103,6 +118,7 @@ const CopilotOpportunityList: FC<{}> = () => {
moreToLoad={isValidating || opportunities.length > 0}
onLoadMoreClick={loadMore}
onRowClick={handleRowClick}
removeDefaultSort
/>
{opportunitiesLoading && (
<LoadingSpinner overlay />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
.skillsContainer {
display: flex;
flex-wrap: wrap;
overflow-x: auto;

Choose a reason for hiding this comment

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

Consider using overflow: hidden instead of overflow-y: hidden to ensure that any overflow in both directions is handled consistently, unless there is a specific reason to only hide vertical overflow.

overflow-y: hidden;
gap: 8px;

Choose a reason for hiding this comment

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

The removal of max-width: 300px; might affect the layout if there was a reason to constrain the width previously. Please ensure that this change does not negatively impact the design or layout of the page.

}

Expand All @@ -26,4 +28,8 @@

.activeStatus {
color: green;
}
}

.type {
white-space: nowrap;
}
2 changes: 1 addition & 1 deletion src/apps/copilots/src/services/copilot-opportunities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export type CopilotOpportunityResponse = SWRResponse<CopilotOpportunity, Copilot
* @returns {CopilotOpportunityResponse} - The response containing the copilot request data.
*/
export const useCopilotOpportunity = (opportunityId?: string): CopilotOpportunityResponse => {
const url = opportunityId ? buildUrl(`${baseUrl}/copilots/opportunities/${opportunityId}`) : undefined
const url = opportunityId ? buildUrl(`${baseUrl}/copilot/opportunity/${opportunityId}`) : undefined

const fetcher = (urlp: string): Promise<CopilotOpportunity> => xhrGetAsync<CopilotOpportunity>(urlp)
.then(copilotOpportunityFactory)
Expand Down
9 changes: 7 additions & 2 deletions src/libs/ui/lib/components/table/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ const Table: <T extends { [propertyName: string]: any }>(props: TableProps<T>) =

function toggleSort(fieldName: string): void {

const col = props.columns.find(c => c.propertyName === fieldName)

// if sortable is false, we return
if (!col?.isSortable === false) return

Choose a reason for hiding this comment

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

The condition !col?.isSortable === false is confusing and may not work as intended. Consider simplifying it to col?.isSortable === false or !(col?.isSortable) depending on the intended logic.


// if we don't have anything to sort by, we shouldn't be here
if (!sort && !props.removeDefaultSort) {
return
Expand Down Expand Up @@ -129,7 +134,7 @@ const Table: <T extends { [propertyName: string]: any }>(props: TableProps<T>) =

const headerRow: Array<JSX.Element> = displayColumns
.map((col, index) => {
const isSortable: boolean = !!col.propertyName
const isSortable: boolean = !!col.propertyName && col.isSortable !== false

Choose a reason for hiding this comment

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

The condition col.isSortable !== false could be simplified to col.isSortable if isSortable is expected to be a boolean. This would make the code more concise and easier to read.

const isCurrentlySorted: boolean = isSortable && col.propertyName === sort?.fieldName
const colorClass: string = isCurrentlySorted ? 'black-100' : 'black-60'
const sortableClass: string | undefined = isSortable ? styles.sortable : undefined
Expand All @@ -151,7 +156,7 @@ const Table: <T extends { [propertyName: string]: any }>(props: TableProps<T>) =
</Tooltip>
</div>
)}
{!props.disableSorting && (
{!props.disableSorting && isSortable && (

Choose a reason for hiding this comment

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

The addition of isSortable changes the condition for rendering the TableSort component. Ensure that isSortable is properly defined and initialized in the component's logic to avoid potential runtime errors or unintended behavior.

<TableSort
iconClass={colorClass}
isCurrentlySorted={isCurrentlySorted}
Expand Down
1 change: 1 addition & 0 deletions src/libs/ui/lib/components/table/table-column.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ export interface TableColumn<T> {
readonly isExpand?: boolean
readonly colSpan?: number
readonly type: TableCellType
readonly isSortable?: boolean
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,11 @@ export function getSorted<T extends { [propertyName: string]: any }>(

if (sortColumn.type === 'date') {
return sortedData
.sort((a: T, b: T) => sortNumbers(
(a[sort.fieldName] as Date).getTime(),
(b[sort.fieldName] as Date).getTime(),
sort.direction,
))
.sort((a: T, b: T) => {
const aDate = new Date(a[sort.fieldName])

Choose a reason for hiding this comment

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

Consider adding error handling for invalid date strings. Using new Date() without validation can result in 'Invalid Date' objects, which may cause unexpected behavior.

const bDate = new Date(b[sort.fieldName])
return sortNumbers(aDate.getTime(), bDate.getTime(), sort.direction)
})
}

return sortedData
Expand Down