Skip to content

[PROD RELEASE] - Q2 #1027

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 192 commits into from
Jun 18, 2025
Merged

[PROD RELEASE] - Q2 #1027

merged 192 commits into from
Jun 18, 2025

Conversation

kkartunov
Copy link
Collaborator

Includes work on:

  • Copilot portal
  • Bugs & Fixes for served apps
  • Review app initial code base
  • Admin app initial code base

vas3a and others added 30 commits January 13, 2025 17:42
PM-505 Fix default values on onboarding form
PM-579 QR feedback on copilot request form
V5 challenge management
@@ -43,6 +74,62 @@ const Table: <T extends { [propertyName: string]: any }>(props: TableProps<T>) =
const [sortedData, setSortedData]: [ReadonlyArray<T>, Dispatch<SetStateAction<ReadonlyArray<T>>>]
= useState<ReadonlyArray<T>>(props.data)

useEffect(() => {
if (_.isEmpty(colWidthInput) && colWidthInput) {

Choose a reason for hiding this comment

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

The condition _.isEmpty(colWidthInput) && colWidthInput seems contradictory. If colWidthInput is empty, it cannot simultaneously be truthy. Consider revising this condition to ensure it behaves as intended.

}
}, 10)
}
// eslint-disable-next-line react-hooks/exhaustive-deps

Choose a reason for hiding this comment

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

Avoid disabling ESLint rules inline. Instead, consider refactoring the code to adhere to the rules or configure the rules appropriately in the ESLint configuration file.

}

screenWidthBkRef.current = screenWidth
// eslint-disable-next-line react-hooks/exhaustive-deps

Choose a reason for hiding this comment

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

Avoid disabling ESLint rules inline. Instead, consider refactoring the code to adhere to the rules or configure the rules appropriately in the ESLint configuration file.

const isCurrentlySorted: boolean = isSortable && col.propertyName === sort?.fieldName
const colorClass: string = isCurrentlySorted ? 'black-100' : 'black-60'
const sortableClass: string | undefined = isSortable ? styles.sortable : undefined
const columnId = `column-id-${col.columnId}-`

Choose a reason for hiding this comment

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

The columnId variable is being used as a class name in line 204. Ensure that col.columnId does not contain any characters that could lead to invalid class names or unintended styling issues.

const isCurrentlySorted: boolean = isSortable && col.propertyName === sort?.fieldName
const colorClass: string = isCurrentlySorted ? 'black-100' : 'black-60'
const sortableClass: string | undefined = isSortable ? styles.sortable : undefined
const columnId = `column-id-${col.columnId}-`
const colWidth = colWidthInput?.[columnId]

Choose a reason for hiding this comment

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

The colWidthInput object is being accessed with columnId as a key. Ensure that colWidthInput is properly initialized and contains the expected keys to avoid potential undefined values.

data={sorted}
allRows={sortedData}
onRowClick={props.onRowClick}
columns={props.columns}

Choose a reason for hiding this comment

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

The colWidthInput variable is used here, but it is not clear from the diff if it has been defined or initialized. Ensure that colWidthInput is properly defined and initialized before use.

<div className={styles['table-wrap']}>
<table className={styles.table}>
<div className={classNames(styles['table-wrap'], props.className)}>
<table ref={tableRef} className={styles.table}>

Choose a reason for hiding this comment

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

The tableRef is being added as a ref to the table element. Ensure that tableRef is defined and initialized correctly, and that it is being used appropriately elsewhere in the component.

'TableCell_blockCell',
)}
onClick={onClick}
style={props.style}

Choose a reason for hiding this comment

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

Using inline styles with style={props.style} can lead to performance issues and make it harder to maintain the code. Consider using a CSS class instead to apply styles.

}, [props.columns, props.showExpand])
// get the cells in the row
const cells: Array<JSX.Element> = displayColumns.map((col, colIndex) => {
const columnId = `column-id-${col.columnId}-`

Choose a reason for hiding this comment

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

The columnId variable is constructed with a trailing hyphen (-). Ensure that this is intentional and that the props.colWidth object keys are structured to match this format. If not, this could lead to unexpected behavior or undefined values.

// get the cells in the row
const cells: Array<JSX.Element> = displayColumns.map((col, colIndex) => {
const columnId = `column-id-${col.columnId}-`
const colWidth = props.colWidth?.[columnId]

Choose a reason for hiding this comment

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

The colWidth variable is being accessed using a dynamic key. Ensure that props.colWidth is properly initialized and contains the expected keys to avoid potential runtime errors.

<td colSpan={displayColumns.length}>
<div className={styles.blockExpandContainer}>
{expandColumns.map((col, colIndex) => {
const columnId = `column-id-${col.columnId}-`

Choose a reason for hiding this comment

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

The variable columnId is constructed using a template string, but it includes a trailing hyphen (-). Ensure that this is intentional and that it aligns with the expected keys in props.colWidth.

col.className,
'TableCell_blockExpandValue',
)}
style={colWidth ? { width: `${colWidth}px` } : {}}

Choose a reason for hiding this comment

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

The inline style for width is conditionally applied based on colWidth. Consider verifying that colWidth is always a valid number to prevent potential rendering issues. If colWidth can be zero or negative, additional validation might be necessary.


import { Sort } from '../../../../../../apps/gamification-admin/src/game-lib/pagination'
import { IconOutline } from '../../svgs'
import { Button } from '../../button'

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

Choose a reason for hiding this comment

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

The import statement for styles is added, but it is not used anywhere in the current diff. Ensure that styles is utilized in the component or remove the import if it is unnecessary.


function handleClick(): void {
props.toggleSort(props.propertyName as string)
}

return (
<Button
className={props.iconClass}
className={classNames(props.iconClass, 'TableSort', styles.btnSort)}

Choose a reason for hiding this comment

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

The addition of styles.btnSort to the className prop suggests that there is a new style being applied. Ensure that styles.btnSort is defined and correctly imported in this file to avoid any potential runtime errors.

}

export function useWindowSize(): WindowSize {
const [windowSize, setWindowSize]: [WindowSize, Dispatch<SetStateAction<WindowSize>>] = useState({

Choose a reason for hiding this comment

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

Consider initializing the windowSize state with the current window dimensions instead of zeros. This will ensure that the initial state reflects the actual window size, which may prevent unnecessary re-renders.


// Remove event listener on cleanup
return () => window.removeEventListener('resize', handleResize)
}, [handleResize]) // Empty array ensures that effect is only run on mount

Choose a reason for hiding this comment

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

The comment 'Empty array ensures that effect is only run on mount' is misleading because the dependency array is not empty; it contains handleResize. Consider updating the comment to accurately reflect the dependency array's contents.

version "11.1.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-11.1.0.tgz#9549028be1753bb934fc96e2bca09bb4105ae912"
integrity sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==

uuid@^8.3.2:

Choose a reason for hiding this comment

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

There are two different versions of the 'uuid' package specified: '^11.1.0' and '^8.3.2'. Consider consolidating to a single version if possible to avoid potential conflicts or inconsistencies.

authRequired: true,
element: <CopilotsRequestForm />,
id: 'CopilotRequestForm',
rolesRequired: [UserRole.administrator, UserRole.projectManager] as UserRole[],

Choose a reason for hiding this comment

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

Consider checking if the rolesRequired array is used consistently across the application to ensure that only users with the specified roles can access this route. If this is a new addition, verify that the role-checking logic is implemented correctly in the route handling.

@@ -0,0 +1,572 @@
import { FC, useContext, useMemo, useState } from 'react'
import { bind, debounce, isEmpty } from 'lodash'

Choose a reason for hiding this comment

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

The debounce function from lodash is imported but not used in the code. Consider removing it if it's not needed to keep the code clean and maintainable.

InputRadio, InputSelect, InputSelectReact, InputText, InputTextarea } from '~/libs/ui'
import { InputSkillSelector } from '~/libs/shared'

import { getProjects } from '../../services/projects'

Choose a reason for hiding this comment

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

The import statement for useProjects has been removed and replaced with getProjects. Ensure that the getProjects function is correctly used in the code, and if useProjects was used elsewhere, make sure to update those references as well.

const CopilotRequestForm: FC<{}> = () => {
const { profile }: ProfileContextData = useContext(profileContext)

const [formValues, setFormValues] = useState<any>({})

Choose a reason for hiding this comment

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

Consider specifying a more specific type for formValues instead of using any to improve type safety.


const [formValues, setFormValues] = useState<any>({})
const [isFormChanged, setIsFormChanged] = useState(false)
const [formErrors, setFormErrors] = useState<any>({})

Choose a reason for hiding this comment

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

Consider specifying a more specific type for formErrors instead of using any to improve type safety.

const [isFormChanged, setIsFormChanged] = useState(false)
const [formErrors, setFormErrors] = useState<any>({})
const [existingCopilot, setExistingCopilot] = useState<string>('')
const [paymentType, setPaymentType] = useState<string>('')

Choose a reason for hiding this comment

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

Consider initializing existingCopilot with a more descriptive default value or handling the case where it might be an empty string.

const [formErrors, setFormErrors] = useState<any>({})
const [existingCopilot, setExistingCopilot] = useState<string>('')
const [paymentType, setPaymentType] = useState<string>('')

Choose a reason for hiding this comment

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

Consider initializing paymentType with a more descriptive default value or handling the case where it might be an empty string.

label: string;
value: string;
}>> {
const response = await getProjects(inputValue)

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 the getProjects call to manage potential API failures gracefully.

setFormErrors(updatedFormErrors)
}

const debouncedProjectSearch = useMemo(() => debounce((inputValue: string, callback: (options: any[]) => void) => {

Choose a reason for hiding this comment

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

The debounce function is being used here, but it's not clear if it has been imported from a utility library like lodash. Ensure that debounce is correctly imported to avoid runtime errors.

setFormErrors(updatedFormErrors)
}

const debouncedProjectSearch = useMemo(() => debounce((inputValue: string, callback: (options: any[]) => void) => {

Choose a reason for hiding this comment

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

Consider adding a dependency array to the useMemo hook if there are any dependencies that should trigger a recalculation of the debounced function. Currently, it is set to an empty array, which means the function will only be created once and never updated.

tabIndex={0}
value={formValues.projectId || ''}
onChange={handleProjectSelect}
loadOptions={debouncedProjectSearch}

Choose a reason for hiding this comment

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

The async prop is not a valid prop for the InputSelectReact component. If this is intended to be an asynchronous select, ensure that the component supports this functionality or use a compatible component.

value={formValues.projectId || ''}
onChange={handleProjectSelect}
loadOptions={debouncedProjectSearch}
async

Choose a reason for hiding this comment

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

Consider renaming debouncedProjectSearch to something more descriptive, such as fetchProjectOptions, to clarify its purpose in loading options asynchronously.

})
}

export const getProjects = (search?: string, filter?: any): Promise<Project[]> => {

Choose a reason for hiding this comment

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

Consider specifying a more precise type instead of any for the filter parameter to improve type safety and maintainability.

}

export const getProjects = (search?: string, filter?: any): Promise<Project[]> => {
const params = { name: `"${search}"`, ...filter }

Choose a reason for hiding this comment

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

Ensure that the search parameter is properly sanitized to prevent potential injection attacks, especially since it is being directly included in the params object.


export const getProjects = (search?: string, filter?: any): Promise<Project[]> => {
const params = { name: `"${search}"`, ...filter }
const url = buildUrl(baseUrl, params)

Choose a reason for hiding this comment

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

Verify that the buildUrl function correctly handles and encodes the params object to prevent malformed URLs or security vulnerabilities.

@@ -8,6 +8,8 @@ import {
useRef,
} from 'react'
import { find } from 'lodash'
import AsyncCreatable from 'react-select/async-creatable'

Choose a reason for hiding this comment

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

Consider checking if the AsyncCreatable component is necessary for your use case. If not, it might be better to remove unused imports to keep the code clean.

@@ -8,6 +8,8 @@ import {
useRef,
} from 'react'
import { find } from 'lodash'
import AsyncCreatable from 'react-select/async-creatable'
import AsyncSelect from 'react-select/async'

Choose a reason for hiding this comment

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

Ensure that AsyncSelect is used in the component. If it is not used, consider removing it to avoid unnecessary imports.

@@ -31,7 +34,8 @@ interface InputSelectReactProps {
readonly label?: string
readonly name: string
readonly onChange: (event: ChangeEvent<HTMLInputElement>) => void
readonly options: OptionsOrGroups<unknown, GroupBase<unknown>>
readonly onInputChange?: (newValue: string) => void
readonly options?: OptionsOrGroups<unknown, GroupBase<unknown>>

Choose a reason for hiding this comment

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

Suggestion

Making options optional (options?:) could lead to potential issues if the component relies on this prop being present. Consider adding validation or default handling within the component to manage cases where options might be undefined. This will help prevent runtime errors if the component attempts to access properties of options without checking for its presence.

@@ -41,6 +45,8 @@ interface InputSelectReactProps {
readonly onBlur?: (event: FocusEvent<HTMLInputElement>) => void
readonly openMenuOnClick?: boolean
readonly openMenuOnFocus?: boolean
readonly async?: boolean
readonly loadOptions?: (inputValue: string, callback: (option: any) => void) => void

Choose a reason for hiding this comment

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

Consider specifying the type for the option parameter in the loadOptions callback to improve type safety and clarity.

props.creatable ? CreatableSelect : ReactSelect
), [props.creatable])
const Input = useMemo(() => {
if (props.async) {

Choose a reason for hiding this comment

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

Consider adding a type annotation for the Input constant to ensure type safety and improve code readability.

@@ -137,7 +152,9 @@ const InputSelectReact: FC<InputSelectReactProps> = props => {
styles.select,
)
}
loadOptions={props.loadOptions}

Choose a reason for hiding this comment

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

Ensure that loadOptions is a function and properly handles errors or edge cases when fetching options. Consider adding validation or error handling logic if necessary.

fix(PM-1273): show apply copilot only for projects where user is not a member
startDate: Date,
tzRestrictions: 'yes' | 'no',
createdAt: Date,
members: Array<number>,

Choose a reason for hiding this comment

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

Consider using number[] instead of Array<number> for consistency and readability, as it is more commonly used in TypeScript.

&& copilotApplications
&& copilotApplications.length === 0
&& opportunity?.status === 'active'
&& !isAlreadyMemberOfTheProject ? applyCopilotOpportunityButton : undefined

Choose a reason for hiding this comment

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

Consider renaming isAlreadyMemberOfTheProject to isNotAlreadyMemberOfTheProject or adjusting the logic to improve readability, as the current negation can be confusing.

@jmgasper jmgasper merged commit a39a297 into master Jun 18, 2025
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants