diff --git a/src/shared/components/challenge-detail/Submissions/index.jsx b/src/shared/components/challenge-detail/Submissions/index.jsx index 796dd3a0d..928892351 100644 --- a/src/shared/components/challenge-detail/Submissions/index.jsx +++ b/src/shared/components/challenge-detail/Submissions/index.jsx @@ -313,8 +313,7 @@ class SubmissionsComponent extends React.Component { isMM() { const { challenge } = this.props; - const trackName = getTrackName(challenge); - return (trackName || '').toLowerCase() === 'data science' || checkIsMM(challenge); + return checkIsMM(challenge); } /** diff --git a/src/shared/containers/challenge-detail/index.jsx b/src/shared/containers/challenge-detail/index.jsx index 752f14302..af21ce89f 100644 --- a/src/shared/containers/challenge-detail/index.jsx +++ b/src/shared/containers/challenge-detail/index.jsx @@ -57,6 +57,7 @@ import { getService } from 'services/contentful'; import { getSubmissionArtifacts as getSubmissionArtifactsService } from 'services/submissions'; import getReviewSummationsService from 'services/reviewSummations'; import { buildMmSubmissionData, buildStatisticsData } from 'utils/mm-review-summations'; +import { appendUtmParamsToUrl } from 'utils/utm'; // import { // getDisplayRecommendedChallenges, // getRecommendedTags, @@ -349,7 +350,11 @@ class ChallengeDetailPageContainer extends React.Component { } = this.props; if (!auth.tokenV3) { const utmSource = communityId || 'community-app-main'; - window.location.href = `${config.URL.AUTH}/member?retUrl=${encodeURIComponent(`${window.location.origin}${window.location.pathname}`)}&utm_source=${utmSource}®Source=challenges`; + window.location.href = appendUtmParamsToUrl( + `${config.URL.AUTH}/member?retUrl=${encodeURIComponent(`${window.location.origin}${window.location.pathname}`)}®Source=challenges`, { + utm_source: utmSource, + }, + ); } else { // Show security reminder to all registrants this.setState({ diff --git a/src/shared/utils/utm.js b/src/shared/utils/utm.js new file mode 100644 index 000000000..529647ca6 --- /dev/null +++ b/src/shared/utils/utm.js @@ -0,0 +1,69 @@ +// UTM cookie configuration constants +const TC_UTM_COOKIE_NAME = 'tc_utm'; + +/** + * Retrieves and parses the tc_utm cookie + * @returns Parsed UTM parameters or null if cookie doesn't exist + */ +export function getUtmCookie() { + try { + const cookies = document.cookie.split(';'); + const cookieStr = cookies.find(cookie => cookie.trim().startsWith(`${TC_UTM_COOKIE_NAME}=`)); + + if (!cookieStr) { + return null; + } + + // handle values that might contain '=' + const cookieValue = decodeURIComponent(cookieStr.split('=').slice(1).join('=')); + return JSON.parse(cookieValue); + } catch (error) { + return null; + } +} + +/** + * Appends UTM parameters from the tc_utm cookie to a given URL + * Only appends parameters that exist in the cookie + * @param url - The base URL to append parameters to + * @returns URL with UTM parameters appended, or original URL if no cookie exists + */ +export function appendUtmParamsToUrl(url, defaultParams = {}) { + if (!url) { + return url; + } + + const utmParams = getUtmCookie(); + + // If there are no cookie params and no defaults, nothing to do + if ( + (!utmParams || Object.keys(utmParams).length === 0) + && (!defaultParams || Object.keys(defaultParams).length === 0) + ) { + return url; + } + + try { + const urlObj = new URL(url, window.location.origin); + const paramNames = ['utm_source', 'utm_medium', 'utm_campaign']; + + paramNames.forEach((param) => { + const cookieVal = utmParams && utmParams[param]; + const defaultVal = defaultParams && defaultParams[param]; + + // Cookie takes precedence and will overwrite existing query param + if (cookieVal) { + urlObj.searchParams.set(param, cookieVal); + } else if (defaultVal) { + // Only apply default if the URL does not already have the param + if (!urlObj.searchParams.has(param)) { + urlObj.searchParams.set(param, defaultVal); + } + } + }); + + return urlObj.toString(); + } catch (error) { + return url; + } +}