Skip to content

[PROD RELEASE] - Feb 5 2026 (Engagements)#7172

Merged
kkartunov merged 65 commits intomasterfrom
develop
Feb 5, 2026
Merged

[PROD RELEASE] - Feb 5 2026 (Engagements)#7172
kkartunov merged 65 commits intomasterfrom
develop

Conversation

@kkartunov
Copy link
Collaborator

No description provided.

himaniraghav3 and others added 30 commits December 5, 2025 14:39
PM-2524 Fix empty submissions tab for DS F2F
…odes

PM-3204 - handle utm codes from cookies
Dockerfile: use ci instead of install
@kkartunov kkartunov requested a review from jmgasper as a code owner February 3, 2026 08:54
@kkartunov kkartunov requested a review from vas3a February 3, 2026 08:54

/* Provide globalThis in older Node runtimes (e.g. Node 10). */
if (typeof globalThis === 'undefined') {
global.globalThis = global;
Copy link

Choose a reason for hiding this comment

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

[💡 maintainability]
Assigning globalThis to global is a workaround for older Node versions. Consider documenting this workaround in a separate documentation file or README to ensure future maintainability and clarity for developers unfamiliar with this context.


/* Provide crypto.sign/crypto.verify in older Node runtimes (e.g. Node 10). */
const crypto = require('crypto');
if (typeof crypto.sign !== 'function') {
Copy link

Choose a reason for hiding this comment

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

[💡 maintainability]
The polyfill for crypto.sign and crypto.verify is a workaround for older Node versions. Ensure that this is well-documented elsewhere in the project documentation to avoid confusion for developers who may not expect this behavior.


/* Provide TextEncoder/TextDecoder in older Node runtimes (e.g. Node 10). */
const { TextDecoder, TextEncoder } = require('util');
if (typeof global.TextEncoder === 'undefined') {
Copy link

Choose a reason for hiding this comment

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

[💡 maintainability]
The polyfill for TextEncoder and TextDecoder is a workaround for older Node versions. Consider documenting this in a separate documentation file or README to ensure future maintainability and clarity for developers unfamiliar with this context.

@@ -1,8 +1,12 @@
module.exports = {
SEGMENT_IO_API_KEY: 'QBtLgV8vCiuRX1lDikbMjcoe9aCHkF6n',
Copy link

Choose a reason for hiding this comment

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

[❗❗ security]
Storing API keys directly in the codebase is a security risk. Consider using environment variables or a secure vault to manage sensitive information.

@@ -1,8 +1,12 @@
module.exports = {
SEGMENT_IO_API_KEY: 'QBtLgV8vCiuRX1lDikbMjcoe9aCHkF6n',
SERVER_API_KEY: '79b2d5eb-c1fd-42c4-9391-6b2c9780d591',
Copy link

Choose a reason for hiding this comment

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

[❗❗ security]
Storing API keys directly in the codebase is a security risk. Consider using environment variables or a secure vault to manage sensitive information.

@@ -1,8 +1,12 @@
module.exports = {
SEGMENT_IO_API_KEY: 'QBtLgV8vCiuRX1lDikbMjcoe9aCHkF6n',
Copy link

Choose a reason for hiding this comment

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

[❗❗ security]
The SEGMENT_IO_API_KEY is hardcoded in the configuration file. Consider using environment variables to manage sensitive information securely.

@@ -1,8 +1,12 @@
module.exports = {
SEGMENT_IO_API_KEY: 'QBtLgV8vCiuRX1lDikbMjcoe9aCHkF6n',
SERVER_API_KEY: '79b2d5eb-c1fd-42c4-9391-6b2c9780d591',
Copy link

Choose a reason for hiding this comment

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

[❗❗ security]
The SERVER_API_KEY is hardcoded in the configuration file. Consider using environment variables to manage sensitive information securely.

@@ -1,3 +1,9 @@
module.exports = {
SERVER_API_KEY: '79b2d5eb-c1fd-42c4-9391-6b2c9780d591',
Copy link

Choose a reason for hiding this comment

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

[❗❗ security]
Storing sensitive information like SERVER_API_KEY directly in the codebase can lead to security vulnerabilities. Consider using environment variables or a secure vault to manage sensitive data.

module.exports = {
SERVER_API_KEY: '79b2d5eb-c1fd-42c4-9391-6b2c9780d591',
API: {
ENGAGEMENTS: 'https://api.topcoder-dev.com/v6/engagements/engagements',
Copy link

Choose a reason for hiding this comment

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

[⚠️ maintainability]
Ensure that the API endpoint https://api.topcoder-dev.com/v6/engagements/engagements is not hardcoded for production use. Consider using environment-specific configurations to manage different API endpoints for development, testing, and production environments.

ENGAGEMENTS: 'https://api.topcoder-dev.com/v6/engagements/engagements',
},
URL: {
ENGAGEMENTS_APP: 'https://engagements.topcoder-dev.com',
Copy link

Choose a reason for hiding this comment

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

[⚠️ maintainability]
The URL https://engagements.topcoder-dev.com should be configurable based on the environment to avoid hardcoding production URLs in the codebase. This will improve the maintainability and flexibility of the application.

return { uuid, page, filters };
}

async function getEngagementsDone(uuid, page, filters, tokenV3) {
Copy link

Choose a reason for hiding this comment

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

[⚠️ correctness]
Consider adding input validation for uuid, page, filters, and tokenV3 to ensure they are of the expected types and values before proceeding with the function logic. This can prevent unexpected errors and improve the robustness of the function.


async function getEngagementsDone(uuid, page, filters, tokenV3) {
try {
const { engagements, meta } = await getEngagements(page, PAGE_SIZE, filters, tokenV3);
Copy link

Choose a reason for hiding this comment

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

[⚠️ performance]
The getEngagements function call should handle potential errors that could arise from network issues or unexpected responses. Consider implementing retry logic or more granular error handling to improve resilience.

} else if (typeof error === 'string') {
message = error;
}
fireErrorMessage('Error Loading Engagements', message);
Copy link

Choose a reason for hiding this comment

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

[💡 maintainability]
The fireErrorMessage function is used here to display an error message, but it might be beneficial to log the error details as well for debugging purposes. Consider adding a logging mechanism to capture the full error stack or details.

value={normalizedTerms}
onChange={(value) => {
onChange(value ? (value.value || '') : '');
onChange(value || null);
Copy link

Choose a reason for hiding this comment

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

[❗❗ correctness]
Changing onChange(value ? (value.value || '') : '') to onChange(value || null) alters the behavior when value is an object with a value property. Previously, it would pass an empty string if value.value was falsy, but now it passes null. Ensure this change is intentional and that downstream logic can handle null appropriately.

}));
const suggestedOptions = (response || [])
.map((skillItem) => {
const label = skillItem && (skillItem.name || skillItem.label || skillItem.title);
Copy link

Choose a reason for hiding this comment

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

[⚠️ correctness]
The mapping logic for label and value is flexible but could lead to unexpected results if skillItem has multiple properties like name, label, and title. Consider explicitly prioritizing these properties to ensure consistent behavior.

.map((skillItem) => {
const label = skillItem && (skillItem.name || skillItem.label || skillItem.title);
if (!label) return null;
const value = skillItem.id || skillItem.skillId || skillItem.value || label;
Copy link

Choose a reason for hiding this comment

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

[⚠️ correctness]
The logic for determining value is potentially problematic. If skillItem has both id and skillId, the choice of id over skillId might not be intentional. Clarify the priority or document the expected structure of skillItem.

onChange={(newSkill) => {
setSkills(newSkill);
onSearch(newSkill);
const nextSkill = newSkill || '';
Copy link

Choose a reason for hiding this comment

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

[⚠️ correctness]
Using newSkill || '' ensures nextSkill is always a string, which is good for consistency. However, if newSkill can be an object, this might mask potential issues. Ensure newSkill is always the expected type before this assignment.


const ProfileHeader = ({ getLookerDone, lookerInfo, info }) => {
const ProfileHeader = ({ info }) => {
const [imageUrl, setimageUrl] = useState();
Copy link

Choose a reason for hiding this comment

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

[💡 readability]
The setimageUrl function should be named setImageUrl to follow camelCase naming conventions, which improves readability and consistency with React's useState convention.

prizeSets,
prizeSet => ((prizeSet && prizeSet.type) || '').toLowerCase() === 'checkpoint',
);
const checkpointPrizeType = _.toUpper(_.get(checkpointPrizes, 'prizes[0].type', 'USD'));
Copy link

Choose a reason for hiding this comment

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

[⚠️ correctness]
Using _.toUpper on a potentially undefined value from _.get could lead to unexpected behavior if checkpointPrizes.prizes[0].type is not a string. Consider providing a default string value to _.get to ensure _.toUpper always receives a string.

numberOfCheckpointsPrizes = checkpointPrizes.prizes.length;
topCheckPointPrize = checkpointPrizes.prizes[0].value;
}
const topCheckPointPrizeDisplay = checkpointPrizeType === 'POINT'
Copy link

Choose a reason for hiding this comment

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

[💡 readability]
The logic for determining topCheckPointPrizeDisplay could be simplified by using a conditional expression directly in the template string. This would improve readability by reducing the number of lines and making the logic more concise.

const { challenge } = this.props;
const trackName = getTrackName(challenge);
return (trackName || '').toLowerCase() === 'data science' || checkIsMM(challenge);
return checkIsMM(challenge);
Copy link

Choose a reason for hiding this comment

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

[❗❗ correctness]
The removal of getTrackName(challenge) and the associated check for 'data science' may change the behavior of the isMM method. Ensure that checkIsMM(challenge) alone is sufficient to determine if the challenge is a marathon match, as this could affect logic that depends on the challenge type.

const past = isPastBucket(activeBucket);
const [currentSelected, setCurrentSelected] = useState(past);
const [isTabClosed, setIsTabClosed] = useState(true);
const pathname = _.get(location, 'pathname', '');
Copy link

Choose a reason for hiding this comment

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

[💡 performance]
Using _.get to access location.pathname is unnecessary here since location is a required prop and pathname is a required field. Direct access would be more straightforward and performant.

}, [activeBucket]);

const onActiveClick = () => {
if (externalTabLabel) {
Copy link

Choose a reason for hiding this comment

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

[⚠️ maintainability]
The check if (history && history.push) is repeated in both onActiveClick and onPastChallengesClick. Consider extracting this logic into a helper function to reduce code duplication and improve maintainability.

{TAB_NAME.PAST_CHALLENGES}
</li>
{TAB_LINKS.map(link => (
<li
Copy link

Choose a reason for hiding this comment

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

[⚠️ correctness]
The key for list items should be unique and stable. Using link.to as a key could cause issues if to is not unique across different links. Consider using a unique identifier for each link if available.

@@ -1,5 +1,10 @@
@import "~styles/mixins";

.item-link {
Copy link

Choose a reason for hiding this comment

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

[⚠️ maintainability]
The .item-link class is defined multiple times with different properties. Consider consolidating these definitions to improve maintainability and avoid potential conflicts.

}
}

.item-link {
Copy link

Choose a reason for hiding this comment

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

[⚠️ maintainability]
The .item-link class is defined multiple times with different properties. Consider consolidating these definitions to improve maintainability and avoid potential conflicts.

@@ -0,0 +1,547 @@
import React from 'react';
import PT from 'prop-types';
import moment from 'moment-timezone';
Copy link

Choose a reason for hiding this comment

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

[⚠️ performance]
Consider using native JavaScript Intl API for timezone handling instead of moment-timezone to reduce bundle size, as moment is a large library and Intl is now widely supported.

<img src={iconBlackSkills} alt="role-icon" /> {getRoleDisplay(role)}
</div>
<div styleName="icon-val">
<img src={iconBlackSkills} alt="skills-icon" /> {limitedSkillsText}
Copy link

Choose a reason for hiding this comment

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

[💡 accessibility]
The alt attribute for the skills icon is set to skills-icon, which is not descriptive for accessibility purposes. Consider using a more descriptive text.

};

EngagementCard.propTypes = {
engagement: PT.shape(),
Copy link

Choose a reason for hiding this comment

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

[⚠️ maintainability]
The engagement prop type is defined as PT.shape(), which does not specify any expected properties. Consider defining the shape of the engagement object to improve type safety and maintainability.


&:disabled,
&:hover:disabled {
background-color: #e9e9e9 !important;
Copy link

Choose a reason for hiding this comment

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

[⚠️ maintainability]
Using !important can make the CSS harder to maintain and override in the future. Consider refactoring to avoid using !important if possible.

const UPDATED_DATE_FIELDS = ['updatedAt', 'updated_at', 'updatedOn', 'updated'];
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;

function getTimestamp(engagement, fields) {
Copy link

Choose a reason for hiding this comment

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

[⚠️ correctness]
The getTimestamp function returns 0 when no valid timestamp is found. This could lead to incorrect sorting if engagements have missing or invalid date fields. Consider handling this case more explicitly, perhaps by returning null or a distinct value that can be checked separately.

<div styleName="cards">
{sortedEngagements.map((engagement, index) => (
<EngagementCard
key={engagement.nanoId || engagement.id || engagement.engagementId || index}
Copy link

Choose a reason for hiding this comment

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

[⚠️ correctness]
The key for EngagementCard falls back to the index if none of the other identifiers are available. This can lead to issues with React's reconciliation process if the order of engagements changes. Consider ensuring that each engagement has a unique identifier to use as a key.

}
}

.filters.loading {
Copy link

Choose a reason for hiding this comment

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

[⚠️ design]
Consider adding a visual indicator (e.g., a spinner) when .filters.loading is active to improve user experience by providing feedback that the interface is in a loading state.

cursor: pointer;

&:disabled {
opacity: 0.6;
Copy link

Choose a reason for hiding this comment

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

[⚠️ accessibility]
Ensure that the opacity change for the disabled state of .load-more-button provides sufficient contrast to meet accessibility standards. This is important for users with visual impairments.

getEngagements,
} = this.props;

const filterChanged = !_.isEqual(prevProps.filter, filter);
Copy link

Choose a reason for hiding this comment

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

[⚠️ performance]
Using _.isEqual from lodash for deep comparison of filter objects is fine, but consider the performance implications if filter becomes large or complex. If performance becomes an issue, you might want to implement a more efficient comparison strategy.


return {
getEngagements: (page, filters, tokenV3) => {
const uuid = shortId();
Copy link

Choose a reason for hiding this comment

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

[⚠️ security]
The shortId library is used to generate UUIDs for requests. Be aware that shortId is not suitable for generating cryptographically secure IDs. If security is a concern, consider using a more robust library like uuid.

};
}

const page = typeof payload.page === 'number' ? payload.page : state.lastRequestedPage;
Copy link

Choose a reason for hiding this comment

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

[⚠️ correctness]
The check for typeof payload.page === 'number' is good for ensuring the type, but consider also checking if payload.page is a valid page number (e.g., non-negative) to prevent potential logical errors.

...state.meta,
...(payload.meta || {}),
};
const totalCount = typeof nextMeta.totalCount === 'number' ? nextMeta.totalCount : null;
Copy link

Choose a reason for hiding this comment

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

[⚠️ correctness]
The variable totalCount is set to null if nextMeta.totalCount is not a number. This could lead to unexpected behavior in the subsequent logic that checks totalCount !== null. Consider defaulting to a safe number or handling this case explicitly.

@@ -0,0 +1,32 @@
import LoadingIndicator from 'components/LoadingIndicator';
import path from 'path';
Copy link

Choose a reason for hiding this comment

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

[💡 maintainability]
The path module is imported but not used in the code. Consider removing it to clean up the imports.

renderPlaceholder={() => <LoadingIndicator />}
renderServer={(renderProps) => {
const p = webpack.resolveWeak('containers/engagement-listing');
const EngagementListing = webpack.requireWeak(path.resolve(__dirname, p));
Copy link

Choose a reason for hiding this comment

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

[⚠️ correctness]
Using path.resolve(__dirname, p) in a server-side rendering context might not behave as expected if the module resolution path is not correctly set up. Ensure that __dirname resolves to the correct directory in all environments where this code will run.


function buildEngagementsUrl(page, pageSize, filters = {}) {
const normalizedPage = Number.isFinite(page) ? Math.max(1, page + 1) : 1;
const url = new URL(engagementsApiUrl);
Copy link

Choose a reason for hiding this comment

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

[💡 maintainability]
Consider using URLSearchParams directly for constructing query parameters instead of appending them one by one. This can improve readability and maintainability.

});

if (!res.ok) {
throw new Error(res.statusText);
Copy link

Choose a reason for hiding this comment

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

[⚠️ maintainability]
Consider including more detailed error handling here, such as logging the error or providing more context in the error message. This can help with debugging and understanding the failure.

});

if (!res.ok) {
throw new Error(res.statusText);
Copy link

Choose a reason for hiding this comment

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

[⚠️ maintainability]
Consider including more detailed error handling here, such as logging the error or providing more context in the error message. This can help with debugging and understanding the failure.

});

if (!res.ok) {
throw new Error(res.statusText);
Copy link

Choose a reason for hiding this comment

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

[⚠️ maintainability]
Consider including more detailed error handling here, such as logging the error or providing more context in the error message. This can help with debugging and understanding the failure.

fullFooter = false;
}

if (url.includes('/engagements')) {
Copy link

Choose a reason for hiding this comment

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

[⚠️ maintainability]
The condition url.includes('/engagements') is very similar to the condition for /challenges above. Consider refactoring to avoid duplication and improve maintainability.

@jmgasper jmgasper changed the title [PROD RELEASE] - Jan 26 [PROD RELEASE] - Feb 5 2026 (Engagements) Feb 4, 2026
@jmgasper jmgasper assigned jmgasper and unassigned jmgasper Feb 4, 2026
@jmgasper jmgasper requested review from jmgasper and removed request for jmgasper February 4, 2026 01:04
@kkartunov kkartunov merged commit 2f73c91 into master Feb 5, 2026
11 of 12 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.

4 participants