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 all 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
24 changes: 20 additions & 4 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 className={styles.title}>
{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 All @@ -62,7 +74,7 @@ const tableColumns: TableColumn<CopilotOpportunity>[] = [
type: 'text',
},
{
label: 'Hours per week needed',
label: 'Hours/Week',
propertyName: 'numHoursPerWeek',
type: 'number',
},
Expand All @@ -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,7 @@
.skillsContainer {
display: flex;
flex-wrap: wrap;
overflow: auto;

Choose a reason for hiding this comment

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

Combining overflow-x and overflow-y into overflow changes the behavior to allow both horizontal and vertical scrolling. Ensure this change is intentional and does not affect the layout negatively.

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 @@ -11,7 +12,7 @@
color: #333;
padding: 4px 8px;
border-radius: 10px;
white-space: nowrap;
white-space: break-spaces;

Choose a reason for hiding this comment

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

Changing white-space from nowrap to break-spaces may affect the layout and readability of the text. Consider whether this change is necessary and test to ensure it doesn't negatively impact the design, especially in responsive scenarios.

font-size: 14px;
}

Expand All @@ -26,4 +27,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

// 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