diff --git a/.circleci/config.yml b/.circleci/config.yml index 9fcaf7177a..a6edc20235 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -349,8 +349,7 @@ workflows: filters: branches: only: - - develop - - latex_support + - sprig-lib # This is alternate dev env for parallel testing - "build-test": context : org-global @@ -365,6 +364,7 @@ workflows: branches: only: - universal_nav + - marathon_match_submission_download - feat/badges-box # This is beta env for production soft releases - "build-prod-beta": @@ -380,8 +380,6 @@ workflows: branches: only: - develop - - old-mm-fix - - reskin-profile-settings # Production builds are exectuted # when PR is merged to the master # Don't change anything in this configuration diff --git a/__tests__/shared/components/challenge-detail/Submissions/SubmissionRow/SubmissionHistoryRow/__snapshots__/index.jsx.snap b/__tests__/shared/components/challenge-detail/Submissions/SubmissionRow/SubmissionHistoryRow/__snapshots__/index.jsx.snap new file mode 100644 index 0000000000..22c7fa66fe --- /dev/null +++ b/__tests__/shared/components/challenge-detail/Submissions/SubmissionRow/SubmissionHistoryRow/__snapshots__/index.jsx.snap @@ -0,0 +1,83 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Matches shallow shapshot shapshot 1 1`] = ` +
+
+
+
+ SUBMISSION +
+ + 1 + +
+
+
+ FINAL SCORE +
+
+ N/A +
+
+
+
+ PROVISIONAL SCORE +
+
+ 80 +
+
+
+
+ TIME +
+
+ 06 Nov 2017 + + 15:49:35 +
+
+
+
+ Action +
+ +
+
+
+`; diff --git a/__tests__/shared/components/challenge-detail/Submissions/SubmissionRow/SubmissionHistoryRow/index.jsx b/__tests__/shared/components/challenge-detail/Submissions/SubmissionRow/SubmissionHistoryRow/index.jsx new file mode 100755 index 0000000000..cf92205746 --- /dev/null +++ b/__tests__/shared/components/challenge-detail/Submissions/SubmissionRow/SubmissionHistoryRow/index.jsx @@ -0,0 +1,50 @@ +import React from 'react'; +// import ReactDOM from 'react-dom'; +import Renderer from 'react-test-renderer/shallow'; +import TU from 'react-dom/test-utils'; +import SubmissionHistoryRow from 'components/challenge-detail/Submissions/SubmissionRow/SubmissionHistoryRow'; + +const mockData = { + isMM: true, + submission: 1, + finalScore: 80, + provisionalScore: 80, + submissionTime: '2017-11-06T15:49:35.000Z', + isReviewPhaseComplete: false, + status: 'completed', + numWinners: 1, + challengeStatus: 'Completed', + auth: { + tokenV3: 'tokenV3', + }, + submissionId: '1', + isLoggedIn: true, +}; + +describe('Matches shallow shapshot', () => { + test('shapshot 1', () => { + const renderer = new Renderer(); + + renderer.render(( + + )); + expect(renderer.getRenderOutput()).toMatchSnapshot(); + }); +}); + +class Wrapper extends React.Component { + componentDidMount() {} + + render() { + return ; + } +} + +describe('render properly', () => { + test('click', () => { + const instance = TU.renderIntoDocument(()); + const matches = TU.scryRenderedDOMComponentsWithTag(instance, 'button'); + expect(matches).toHaveLength(1); + TU.Simulate.click(matches[0]); + }); +}); diff --git a/__tests__/shared/components/challenge-detail/Winners/Winner/__snapshots__/index.jsx.snap b/__tests__/shared/components/challenge-detail/Winners/Winner/__snapshots__/index.jsx.snap new file mode 100644 index 0000000000..5994402871 --- /dev/null +++ b/__tests__/shared/components/challenge-detail/Winners/Winner/__snapshots__/index.jsx.snap @@ -0,0 +1,73 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Matches shallow shapshot shapshot 1 1`] = ` +
+
+
+ + 1th + +
+
+
+ + +
+
+
+
+
+ $ + 200 +
+
+
+ +
+
+`; diff --git a/__tests__/shared/components/challenge-detail/Winners/Winner/index.jsx b/__tests__/shared/components/challenge-detail/Winners/Winner/index.jsx new file mode 100755 index 0000000000..3dd2be5b9d --- /dev/null +++ b/__tests__/shared/components/challenge-detail/Winners/Winner/index.jsx @@ -0,0 +1,71 @@ +import React from 'react'; +// import ReactDOM from 'react-dom'; +import Renderer from 'react-test-renderer/shallow'; +import TU from 'react-dom/test-utils'; +import Winner from 'components/challenge-detail/Winners/Winner'; + +const mockData = { + isDesign: false, + isMM: true, + prizes: [ + { value: 200, type: 'USD' }, + { value: 100, type: 'USD' }, + ], + submissions: [ + { + placement: 1, + createdBy: 'test', + created: '2017-11-06T15:49:35.000Z', + id: '1', + }, + { + placement: 1, + createdBy: 'test', + created: '2017-12-06T15:49:35.000Z', + id: '2', + }, + { + placement: 1, + createdBy: 'test2', + created: '2017-11-06T15:49:35.000Z', + id: '3', + }, + ], + viewable: false, + winner: { + handle: 'test', + placement: 1, + }, + isLoggedIn: true, + auth: { + tokenV3: 'tokenV3', + }, +}; + +describe('Matches shallow shapshot', () => { + test('shapshot 1', () => { + const renderer = new Renderer(); + + renderer.render(( + + )); + expect(renderer.getRenderOutput()).toMatchSnapshot(); + }); +}); + +class Wrapper extends React.Component { + componentDidMount() {} + + render() { + return ; + } +} + +describe('render properly', () => { + test('click', () => { + const instance = TU.renderIntoDocument(()); + const matches = TU.scryRenderedDOMComponentsWithTag(instance, 'button'); + expect(matches).toHaveLength(1); + TU.Simulate.click(matches[0]); + }); +}); diff --git a/package-lock.json b/package-lock.json index bf07ca0a8e..885b3f2b80 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3510,7 +3510,7 @@ "babel-plugin-transform-object-rest-spread": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz", - "integrity": "sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY=", + "integrity": "sha512-ocgA9VJvyxwt+qJB0ncxV8kb/CjfTcECUY4tQ5VT7nP6Aohzobm8CDFaQ5FHdvZQzLmf0sgDxB8iRXZXxwZcyA==", "dev": true, "requires": { "babel-plugin-syntax-object-rest-spread": "6.13.0", @@ -4196,7 +4196,7 @@ "buffer-fill": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", - "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=" + "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==" }, "buffer-from": { "version": "1.1.1", @@ -4463,8 +4463,7 @@ "ccount": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.0.5.tgz", - "integrity": "sha512-MOli1W+nfbPLlKEhInaxhRdp7KVLFxLN5ykwzHgLsLI3H3gs5jjFAK4Eoj3OzzcxCtumDaI8onoVDeQyWaNTkw==", - "dev": true + "integrity": "sha512-MOli1W+nfbPLlKEhInaxhRdp7KVLFxLN5ykwzHgLsLI3H3gs5jjFAK4Eoj3OzzcxCtumDaI8onoVDeQyWaNTkw==" }, "chain-function": { "version": "1.0.1", @@ -4491,8 +4490,7 @@ "character-entities-html4": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-1.1.4.tgz", - "integrity": "sha512-HRcDxZuZqMx3/a+qrzxdBKBPUpxWEq9xw2OPZ3a/174ihfrQKVsFhqtthBInFy1zZ9GgZyFXOatNujm8M+El3g==", - "dev": true + "integrity": "sha512-HRcDxZuZqMx3/a+qrzxdBKBPUpxWEq9xw2OPZ3a/174ihfrQKVsFhqtthBInFy1zZ9GgZyFXOatNujm8M+El3g==" }, "character-entities-legacy": { "version": "1.1.4", @@ -4768,12 +4766,6 @@ } } }, - "collapse-white-space": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.6.tgz", - "integrity": "sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ==", - "dev": true - }, "collect-all": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/collect-all/-/collect-all-1.0.3.tgz", @@ -6894,7 +6886,7 @@ "decompress-response": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "integrity": "sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==", "requires": { "mimic-response": "1.0.1" } @@ -7059,7 +7051,7 @@ "detect-libc": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==" }, "detect-newline": { "version": "2.1.0", @@ -10287,7 +10279,7 @@ "github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=" + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" }, "glob": { "version": "7.1.6", @@ -10837,6 +10829,40 @@ "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.4.tgz", "integrity": "sha512-gW3sxfynIvZApL4L07wryYF4+C9VvH3AUi7LAnVXV4MneGEgwOByXvFo18BgmTWnm7oHAe874jKbIB1YhHSIzA==" }, + "hast-util-to-html": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-7.1.3.tgz", + "integrity": "sha512-yk2+1p3EJTEE9ZEUkgHsUSVhIpCsL/bvT8E5GzmWc+N1Po5gBw+0F8bo7dpxXR0nu0bQVxVZGX2lBGF21CmeDw==", + "requires": { + "ccount": "1.0.5", + "comma-separated-tokens": "1.0.8", + "hast-util-is-element": "1.1.0", + "hast-util-whitespace": "1.0.4", + "html-void-elements": "1.0.5", + "property-information": "5.5.0", + "space-separated-tokens": "1.1.5", + "stringify-entities": "3.1.0", + "unist-util-is": "4.1.0", + "xtend": "4.0.2" + }, + "dependencies": { + "stringify-entities": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-3.1.0.tgz", + "integrity": "sha512-3FP+jGMmMV/ffZs86MoghGqAoqXAdxLrJP4GUdrDN1aIScYih5tuIO3eF4To5AJZ79KDZ8Fpdy7QJnK8SsL1Vg==", + "requires": { + "character-entities-html4": "1.1.4", + "character-entities-legacy": "1.1.4", + "xtend": "4.0.2" + } + }, + "unist-util-is": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz", + "integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==" + } + } + }, "hast-util-to-text": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-2.0.1.tgz", @@ -10847,6 +10873,11 @@ "unist-util-find-after": "3.0.0" } }, + "hast-util-whitespace": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-1.0.4.tgz", + "integrity": "sha512-I5GTdSfhYfAPNztx2xJRQpG8cuDSNt599/7YUn7Gx/WxNMsG+a835k97TDkFgk123cwjfwINaZknkKkphx/f2A==" + }, "hastscript": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-5.1.2.tgz", @@ -11055,6 +11086,11 @@ "minimist": "1.2.5" } }, + "html-void-elements": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-1.0.5.tgz", + "integrity": "sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w==" + }, "htmlparser2": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", @@ -12084,12 +12120,6 @@ "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true }, - "is-word-character": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.4.tgz", - "integrity": "sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA==", - "dev": true - }, "is-wsl": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", @@ -13506,7 +13536,7 @@ "jsbn": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" + "integrity": "sha1-sBMHyym2GKHtJux56RH4A8TaAEA=" }, "jsdoc": { "version": "3.5.5", @@ -14228,7 +14258,7 @@ "lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" }, "lodash.curry": { "version": "4.1.1", @@ -14278,7 +14308,7 @@ "lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" }, "lodash.isarguments": { "version": "3.1.0", @@ -14295,7 +14325,7 @@ "lodash.isboolean": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" }, "lodash.isequal": { "version": "4.5.0", @@ -14306,7 +14336,7 @@ "lodash.isinteger": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" }, "lodash.ismatch": { "version": "4.4.0", @@ -14317,7 +14347,7 @@ "lodash.isnumber": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" }, "lodash.isplainobject": { "version": "4.0.6", @@ -14327,7 +14357,7 @@ "lodash.isstring": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" }, "lodash.kebabcase": { "version": "4.1.1", @@ -14359,7 +14389,7 @@ "lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" }, "lodash.padend": { "version": "4.6.1", @@ -14516,7 +14546,7 @@ "lru-cache": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz", - "integrity": "sha512-uQw9OqphAGiZhkuPlpFGmdTU2tEuhxTourM/19qGJrxBPHAr/f8BT1a0i/lOclESnGatdJG/UCkP9kZB/Lh1iw==", + "integrity": "sha1-HRdnnAac2l0ECZGgnbwsDbN35V4=", "requires": { "pseudomap": "1.0.2", "yallist": "2.1.2" @@ -14704,6 +14734,37 @@ } } }, + "mdast-util-find-and-replace": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-1.1.1.tgz", + "integrity": "sha512-9cKl33Y21lyckGzpSmEQnIDjEfeeWelN5s1kUW1LwdB0Fkuq2u+4GdqcGEygYxJE8GVqCl0741bYXHgamfWAZA==", + "requires": { + "escape-string-regexp": "4.0.0", + "unist-util-is": "4.1.0", + "unist-util-visit-parents": "3.1.1" + }, + "dependencies": { + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" + }, + "unist-util-is": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz", + "integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==" + }, + "unist-util-visit-parents": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz", + "integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==", + "requires": { + "@types/unist": "2.0.3", + "unist-util-is": "4.1.0" + } + } + } + }, "mdast-util-from-markdown": { "version": "0.8.5", "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz", @@ -14731,6 +14792,71 @@ } } }, + "mdast-util-frontmatter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-frontmatter/-/mdast-util-frontmatter-0.2.0.tgz", + "integrity": "sha512-FHKL4w4S5fdt1KjJCwB0178WJ0evnyyQr5kXTM3wrOVpytD0hrkvd+AOOjU9Td8onOejCkmZ+HQRT3CZ3coHHQ==", + "requires": { + "micromark-extension-frontmatter": "0.2.2" + } + }, + "mdast-util-gfm": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-0.1.2.tgz", + "integrity": "sha512-NNkhDx/qYcuOWB7xHUGWZYVXvjPFFd6afg6/e2g+SV4r9q5XUcCbV4Wfa3DLYIiD+xAEZc6K4MGaE/m0KDcPwQ==", + "requires": { + "mdast-util-gfm-autolink-literal": "0.1.3", + "mdast-util-gfm-strikethrough": "0.2.3", + "mdast-util-gfm-table": "0.1.6", + "mdast-util-gfm-task-list-item": "0.1.6", + "mdast-util-to-markdown": "0.6.5" + } + }, + "mdast-util-gfm-autolink-literal": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-0.1.3.tgz", + "integrity": "sha512-GjmLjWrXg1wqMIO9+ZsRik/s7PLwTaeCHVB7vRxUwLntZc8mzmTsLVr6HW1yLokcnhfURsn5zmSVdi3/xWWu1A==", + "requires": { + "ccount": "1.0.5", + "mdast-util-find-and-replace": "1.1.1", + "micromark": "2.11.4" + } + }, + "mdast-util-gfm-strikethrough": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-0.2.3.tgz", + "integrity": "sha512-5OQLXpt6qdbttcDG/UxYY7Yjj3e8P7X16LzvpX8pIQPYJ/C2Z1qFGMmcw+1PZMUM3Z8wt8NRfYTvCni93mgsgA==", + "requires": { + "mdast-util-to-markdown": "0.6.5" + } + }, + "mdast-util-gfm-table": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-0.1.6.tgz", + "integrity": "sha512-j4yDxQ66AJSBwGkbpFEp9uG/LS1tZV3P33fN1gkyRB2LoRL+RR3f76m0HPHaby6F4Z5xr9Fv1URmATlRRUIpRQ==", + "requires": { + "markdown-table": "2.0.0", + "mdast-util-to-markdown": "0.6.5" + }, + "dependencies": { + "markdown-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz", + "integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==", + "requires": { + "repeat-string": "1.6.1" + } + } + } + }, + "mdast-util-gfm-task-list-item": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-0.1.6.tgz", + "integrity": "sha512-/d51FFIfPsSmCIRNp7E6pozM9z1GYPIkSy1urQ8s/o4TC22BZ7DqfHFWiqBD23bc7J3vV1Fc9O4QIHBlfuit8A==", + "requires": { + "mdast-util-to-markdown": "0.6.5" + } + }, "mdast-util-math": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/mdast-util-math/-/mdast-util-math-0.1.2.tgz", @@ -15023,6 +15149,64 @@ } } }, + "micromark-extension-frontmatter": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/micromark-extension-frontmatter/-/micromark-extension-frontmatter-0.2.2.tgz", + "integrity": "sha512-q6nPLFCMTLtfsctAuS0Xh4vaolxSFUWUWR6PZSrXXiRy+SANGllpcqdXFv2z07l0Xz/6Hl40hK0ffNCJPH2n1A==", + "requires": { + "fault": "1.0.4" + } + }, + "micromark-extension-gfm": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-0.3.3.tgz", + "integrity": "sha512-oVN4zv5/tAIA+l3GbMi7lWeYpJ14oQyJ3uEim20ktYFAcfX1x3LNlFGGlmrZHt7u9YlKExmyJdDGaTt6cMSR/A==", + "requires": { + "micromark": "2.11.4", + "micromark-extension-gfm-autolink-literal": "0.5.7", + "micromark-extension-gfm-strikethrough": "0.6.5", + "micromark-extension-gfm-table": "0.4.3", + "micromark-extension-gfm-tagfilter": "0.3.0", + "micromark-extension-gfm-task-list-item": "0.3.3" + } + }, + "micromark-extension-gfm-autolink-literal": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-0.5.7.tgz", + "integrity": "sha512-ePiDGH0/lhcngCe8FtH4ARFoxKTUelMp4L7Gg2pujYD5CSMb9PbblnyL+AAMud/SNMyusbS2XDSiPIRcQoNFAw==", + "requires": { + "micromark": "2.11.4" + } + }, + "micromark-extension-gfm-strikethrough": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-0.6.5.tgz", + "integrity": "sha512-PpOKlgokpQRwUesRwWEp+fHjGGkZEejj83k9gU5iXCbDG+XBA92BqnRKYJdfqfkrRcZRgGuPuXb7DaK/DmxOhw==", + "requires": { + "micromark": "2.11.4" + } + }, + "micromark-extension-gfm-table": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-0.4.3.tgz", + "integrity": "sha512-hVGvESPq0fk6ALWtomcwmgLvH8ZSVpcPjzi0AjPclB9FsVRgMtGZkUcpE0zgjOCFAznKepF4z3hX8z6e3HODdA==", + "requires": { + "micromark": "2.11.4" + } + }, + "micromark-extension-gfm-tagfilter": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-0.3.0.tgz", + "integrity": "sha512-9GU0xBatryXifL//FJH+tAZ6i240xQuFrSL7mYi8f4oZSbc+NvXjkrHemeYP0+L4ZUT+Ptz3b95zhUZnMtoi/Q==" + }, + "micromark-extension-gfm-task-list-item": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-0.3.3.tgz", + "integrity": "sha512-0zvM5iSLKrc/NQl84pZSjGo66aTGd57C1idmlWmE87lkMcXrTxg1uXa/nXomxJytoje9trP0NDLvw4bZ/Z/XCQ==", + "requires": { + "micromark": "2.11.4" + } + }, "micromark-extension-math": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-0.1.2.tgz", @@ -15085,7 +15269,7 @@ "millisecond": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/millisecond/-/millisecond-0.1.2.tgz", - "integrity": "sha512-BJ8XtxY+woL+5TkP6uS6XvOArm0JVrX2otkgtWZseHpIax0oOOPW3cnwhOjRqbEJg7YRO/BDF7fO/PTWNT3T9Q==" + "integrity": "sha1-bMWtOGJByrjniv+WT4cCjuyS2sU=" }, "mime": { "version": "1.6.0", @@ -15418,7 +15602,7 @@ "mv": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", - "integrity": "sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg==", + "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", "optional": true, "requires": { "mkdirp": "0.5.5", @@ -15429,7 +15613,7 @@ "glob": { "version": "6.0.4", "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", - "integrity": "sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==", + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", "optional": true, "requires": { "inflight": "1.0.6", @@ -15442,7 +15626,7 @@ "rimraf": { "version": "2.4.5", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", - "integrity": "sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==", + "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", "optional": true, "requires": { "glob": "6.0.4" @@ -15704,7 +15888,7 @@ "ncp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", - "integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==", + "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", "optional": true }, "nearley": { @@ -16130,7 +16314,7 @@ "noop-logger": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", - "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=" + "integrity": "sha512-6kM8CLXvuW5crTxsAtva2YLrRrDaiTIkIePWs9moLHqbFWT94WpNFjwS/5dfLfECg5i/lkmw3aoqVidxt23TEQ==" }, "nopt": { "version": "4.0.3", @@ -20511,18 +20695,23 @@ } }, "rehype-katex": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/rehype-katex/-/rehype-katex-4.0.0.tgz", - "integrity": "sha512-0mgBqYugQyIW0eUl6RDOZ28Cat2YzrnWGaYgKCMQnJw6ClmKgLqXBnkDAPGh2mwxvkkKwQOUMUpSLpA5rt7rzA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/rehype-katex/-/rehype-katex-5.0.0.tgz", + "integrity": "sha512-ksSuEKCql/IiIadOHiKRMjypva9BLhuwQNascMqaoGLDVd0k2NlE2wMvgZ3rpItzRKCd6vs8s7MFbb8pcR0AEg==", "requires": { "@types/katex": "0.11.1", "hast-util-to-text": "2.0.1", - "katex": "0.12.0", + "katex": "0.13.24", "rehype-parse": "7.0.1", "unified": "9.2.2", "unist-util-visit": "2.0.3" }, "dependencies": { + "commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==" + }, "is-buffer": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", @@ -20533,6 +20722,14 @@ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==" }, + "katex": { + "version": "0.13.24", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.13.24.tgz", + "integrity": "sha512-jZxYuKCma3VS5UuxOx/rFV1QyGSl3Uy/i0kTJF3HgQ5xMinCQVF8Zd4bMY/9aI9b9A2pjIBOsjSSm68ykTAr8w==", + "requires": { + "commander": "8.3.0" + } + }, "unified": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.2.tgz", @@ -20599,17 +20796,76 @@ } } }, + "rehype-stringify": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-8.0.0.tgz", + "integrity": "sha512-VkIs18G0pj2xklyllrPSvdShAV36Ff3yE5PUO9u36f6+2qJFnn22Z5gKwBOwgXviux4UC7K+/j13AnZfPICi/g==", + "requires": { + "hast-util-to-html": "7.1.3" + } + }, "remark": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/remark/-/remark-10.0.1.tgz", "integrity": "sha512-E6lMuoLIy2TyiokHprMjcWNJ5UxfGQjaMSMhV+f4idM625UjjK4j798+gPs5mfjzDE6vL0oFKVeZM6gZVSVrzQ==", "dev": true, "requires": { - "remark-parse": "6.0.3", "remark-stringify": "6.0.4", "unified": "7.1.0" } }, + "remark-breaks": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/remark-breaks/-/remark-breaks-2.0.2.tgz", + "integrity": "sha512-LsQnPPQ7Fzp9RTjj4IwdEmjPOr9bxe9zYKWhs9ZQOg9hMg8rOfeeqQ410cvVdIK87Famqza1CKRxNkepp2EvUA==", + "requires": { + "unist-util-visit": "2.0.3" + }, + "dependencies": { + "unist-util-is": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz", + "integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==" + }, + "unist-util-visit": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz", + "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==", + "requires": { + "@types/unist": "2.0.3", + "unist-util-is": "4.1.0", + "unist-util-visit-parents": "3.1.1" + } + }, + "unist-util-visit-parents": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz", + "integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==", + "requires": { + "@types/unist": "2.0.3", + "unist-util-is": "4.1.0" + } + } + } + }, + "remark-frontmatter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/remark-frontmatter/-/remark-frontmatter-3.0.0.tgz", + "integrity": "sha512-mSuDd3svCHs+2PyO29h7iijIZx4plX0fheacJcAoYAASfgzgVIcXGYSq9GFyYocFLftQs8IOmmkgtOovs6d4oA==", + "requires": { + "mdast-util-frontmatter": "0.2.0", + "micromark-extension-frontmatter": "0.2.2" + } + }, + "remark-gfm": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-1.0.0.tgz", + "integrity": "sha512-KfexHJCiqvrdBZVbQ6RopMZGwaXz6wFJEfByIuEwGf0arvITHjiKKZ1dpXujjH9KZdm1//XJQwgfnJ3lmXaDPA==", + "requires": { + "mdast-util-gfm": "0.1.2", + "micromark-extension-gfm": "0.3.3" + } + }, "remark-math": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/remark-math/-/remark-math-4.0.0.tgz", @@ -20620,26 +20876,11 @@ } }, "remark-parse": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-6.0.3.tgz", - "integrity": "sha512-QbDXWN4HfKTUC0hHa4teU463KclLAnwpn/FBn87j9cKYJWWawbiLgMfP2Q4XwhxxuuuOxHlw+pSN0OKuJwyVvg==", - "dev": true, + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-9.0.0.tgz", + "integrity": "sha512-geKatMwSzEXKHuzBNU1z676sGcDcFoChMK38TgdHJNAYfFtsfHDQG7MoJAjs6sgYMqyLduCYWDIWZIxiPeafEw==", "requires": { - "collapse-white-space": "1.0.6", - "is-alphabetical": "1.0.4", - "is-decimal": "1.0.4", - "is-whitespace-character": "1.0.4", - "is-word-character": "1.0.4", - "markdown-escapes": "1.0.4", - "parse-entities": "1.2.2", - "repeat-string": "1.6.1", - "state-toggle": "1.0.3", - "trim": "0.0.1", - "trim-trailing-lines": "1.1.3", - "unherit": "1.1.3", - "unist-util-remove-position": "1.1.4", - "vfile-location": "2.0.6", - "xtend": "4.0.2" + "mdast-util-from-markdown": "0.8.5" } }, "remark-rehype": { @@ -21899,7 +22140,7 @@ "simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", "requires": { "is-arrayish": "0.3.2" }, @@ -24821,12 +25062,6 @@ "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=" }, - "trim-trailing-lines": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.3.tgz", - "integrity": "sha512-4ku0mmjXifQcTVfYDfR5lpgV7zVqPg6zV9rdZmwOPqq0+Zq19xDqEgagqVbc4pOOShbncuAOIs59R3+3gcF3ZA==", - "dev": true - }, "trough": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", @@ -25083,15 +25318,6 @@ "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-3.1.0.tgz", "integrity": "sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA==" }, - "unist-util-remove-position": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-1.1.4.tgz", - "integrity": "sha512-tLqd653ArxJIPnKII6LMZwH+mb5q+n/GtXQZo6S6csPRs5zB0u79Yw8ouR3wTw8wxvdJFhpP6Y7jorWdCgLO0A==", - "dev": true, - "requires": { - "unist-util-visit": "1.4.1" - } - }, "unist-util-stringify-position": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", @@ -25390,12 +25616,6 @@ } } }, - "vfile-location": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-2.0.6.tgz", - "integrity": "sha512-sSFdyCP3G6Ka0CEmN83A2YCMKIieHx0EDaj5IDP4g1pa5ZJ4FJDvpO0WODLxo4LUX4oe52gmSCK7Jw4SBghqxA==", - "dev": true - }, "vfile-message": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", diff --git a/package.json b/package.json index ab492a0765..2aa91325e6 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,7 @@ "draft-js-markdown-shortcuts-plugin": "^0.3.0", "draft-js-plugins-editor": "^2.0.3", "express": "^4.16.3", + "fflate": "^0.7.4", "filestack-js": "^1.7.7", "filestack-react": "^2.0.0", "flag-icon-css": "^3.3.0", @@ -121,7 +122,7 @@ "react-infinite-scroller": "^1.1.4", "react-inlinesvg": "^0.8.4", "react-input-mask": "^3.0.0-alpha.2", - "react-markdown": "^6.0.0", + "react-markdown": "^6.0.3", "react-masonry-css": "^1.0.16", "react-paginate": "^6.3.2", "react-player": "^0.24.1", @@ -142,8 +143,14 @@ "redux": "^3.7.2", "redux-actions": "^2.4.0", "redux-promise": "^0.6.0", - "rehype-katex": "^4.0.0", + "rehype-katex": "^5.0.0", + "rehype-raw": "^5.1.0", + "rehype-stringify": "^8.0.0", + "remark-breaks": "^2.0.2", + "remark-frontmatter": "^3.0.0", + "remark-gfm": "^1.0.0", "remark-math": "^4.0.0", + "remark-parse": "^9.0.0", "request-ip": "^2.0.2", "require-context": "^1.1.0", "rss": "^1.2.2", diff --git a/src/client/index.jsx b/src/client/index.jsx index b879711ac2..c08e8fef96 100644 --- a/src/client/index.jsx +++ b/src/client/index.jsx @@ -13,10 +13,15 @@ import { getFreshToken, } from '@topcoder-platform/tc-auth-lib'; import { actions, logger, errors } from 'topcoder-react-lib'; -import { client, redux } from 'topcoder-react-utils'; +import { client, redux, config } from 'topcoder-react-utils'; +import { sprig } from '@sprig-technologies/sprig-browser'; import './styles.scss'; +const Sprig = sprig.configure({ + environmentId: config.SPRIG_ENVIRONMENT_ID, +}); + const { setErrorsStore } = errors; /** @@ -55,6 +60,7 @@ function identify(profile, roles, userIdHash) { integrations: { All: false, Chameleon: true }, }, ); + Sprig('setUserId', profile.handle); } /** diff --git a/src/shared/components/TopcoderFooter/index.jsx b/src/shared/components/TopcoderFooter/index.jsx index 3fc5e604d0..1a28a0abd1 100644 --- a/src/shared/components/TopcoderFooter/index.jsx +++ b/src/shared/components/TopcoderFooter/index.jsx @@ -39,6 +39,7 @@ function TopcoderFooter() { if (footerInitialized.current) { return; } + footerRef.current.id = footerElId.current; footerInitialized.current = true; counter += 1; diff --git a/src/shared/components/challenge-detail/Header/index.jsx b/src/shared/components/challenge-detail/Header/index.jsx index 43ca1bd970..c9ad2a9617 100644 --- a/src/shared/components/challenge-detail/Header/index.jsx +++ b/src/shared/components/challenge-detail/Header/index.jsx @@ -12,7 +12,6 @@ import { isMM } from 'utils/challenge'; import PT from 'prop-types'; import React from 'react'; -import { useMediaQuery } from 'react-responsive'; import { PrimaryButton } from 'topcoder-react-ui-kit'; import { Link } from 'topcoder-react-utils'; import { COMPETITION_TRACKS } from 'utils/tc'; @@ -81,9 +80,7 @@ export default function ChallengeHeader(props) { type, track, } = challenge; - - const desktop = useMediaQuery({ minWidth: 1024 }); - const showDeadlineDetail = desktop || showDeadlineDetailProp; + const showDeadlineDetail = showDeadlineDetailProp; const tags = challenge.tags || []; @@ -148,7 +145,7 @@ export default function ChallengeHeader(props) { const deadlineEnd = moment(nextPhase && phaseEndDate(nextPhase)); const currentTime = moment(); - const timeDiff = getTimeLeft(currentPhases, 'to go'); + const timeDiff = getTimeLeft(currentPhases, 'to go', true); if (!timeDiff.late) { timeDiff.text = timeDiff.text.replace('to go', ''); @@ -172,6 +169,12 @@ export default function ChallengeHeader(props) { const end = phaseEndDate(phase); return moment(end).isAfter(); } + // do not show [Specification Submission, Specification Review, Approval] + // phases for design challenges + if (trackLower === 'design' + && ['Specification Submission', 'Specification Review', 'Approval'].indexOf(phase.name) >= 0) { + return false; + } return true; }); @@ -191,6 +194,7 @@ export default function ChallengeHeader(props) { // condition for 2 round challenge for now let finalStartDate; if (relevantPhases.length === 8 || challenge.timelineTemplateId === 'd4201ca4-8437-4d63-9957-3f7708184b07') { + relevantPhases = _.filter(relevantPhases, p => !(p.name.toLowerCase().includes('checkpoint screening') || p.name.toLowerCase().includes('screening'))); _.map(relevantPhases, (phase) => { if (phase.name === 'Checkpoint Review') { finalStartDate = phase.scheduledEndDate; @@ -443,7 +447,7 @@ export default function ChallengeHeader(props) { (status || '').toLowerCase() === 'active' && (
- {currentPhases && `${currentPhases.name} Ends: `} + {currentPhases && `${currentPhases.name} Ends In: `} {timeDiff.text} diff --git a/src/shared/components/challenge-detail/Header/style.scss b/src/shared/components/challenge-detail/Header/style.scss index a3daf5e533..471d30f915 100644 --- a/src/shared/components/challenge-detail/Header/style.scss +++ b/src/shared/components/challenge-detail/Header/style.scss @@ -635,7 +635,6 @@ border: 0 hidden $tc-white; overflow: hidden; margin: 0 14px 0 5px; - min-height: 96px; .deadlines-overview { background-color: #555; @@ -644,13 +643,14 @@ height: 60px; border-bottom-left-radius: 8px; border-bottom-right-radius: 8px; + display: flex; + padding-right: 16px; &.opened { border-radius: 0 !important; } @include xs-to-md { - display: flex; padding: 0 16px; } diff --git a/src/shared/components/challenge-detail/Specification/SpecificationComponent/index.jsx b/src/shared/components/challenge-detail/Specification/SpecificationComponent/index.jsx index ce3a3aef1f..6360f5f069 100644 --- a/src/shared/components/challenge-detail/Specification/SpecificationComponent/index.jsx +++ b/src/shared/components/challenge-detail/Specification/SpecificationComponent/index.jsx @@ -3,6 +3,13 @@ import React from 'react'; import ReactMarkdown from 'react-markdown'; import remarkMath from 'remark-math'; import rehypeKatex from 'rehype-katex'; +import remarkGfm from 'remark-gfm'; +import remarkParse from 'remark-parse'; +import rehypeStringify from 'rehype-stringify'; +import remarkFrontmatter from 'remark-frontmatter'; +import rehypeRaw from 'rehype-raw'; +import remarkBreaks from 'remark-breaks'; +import style from './styles.scss'; // eslint-disable-next-line import/no-extraneous-dependencies import 'katex/dist/katex.min.css'; @@ -14,8 +21,15 @@ export default function SpecificationComponent({ if (format === 'markdown') { return ( {bodyText} diff --git a/src/shared/components/challenge-detail/Specification/SpecificationComponent/styles.scss b/src/shared/components/challenge-detail/Specification/SpecificationComponent/styles.scss new file mode 100644 index 0000000000..a1be329593 --- /dev/null +++ b/src/shared/components/challenge-detail/Specification/SpecificationComponent/styles.scss @@ -0,0 +1,27 @@ +@import "~styles/mixins"; + +.container { + :global { + table { + display: block; + width: 100%; + overflow: auto; + box-sizing: border-box; + + * { + box-sizing: border-box; + } + + tr { + background-color: #fff; + border-top: 1px solid #c6cbd1; + } + + th, + td { + padding: 6px 13px; + border: 1px solid #dfe2e5; + } + } + } +} diff --git a/src/shared/components/challenge-detail/Specification/styles.scss b/src/shared/components/challenge-detail/Specification/styles.scss index bd21a44f4a..7ecaaf250f 100644 --- a/src/shared/components/challenge-detail/Specification/styles.scss +++ b/src/shared/components/challenge-detail/Specification/styles.scss @@ -169,10 +169,10 @@ $tc-link-visited: #0c4e98; li::before { display: table-cell; - font-weight: 700; text-align: right; content: counter(item) "."; padding: 0 10px 0 0; + white-space: nowrap; } } @@ -352,10 +352,10 @@ $tc-link-visited: #0c4e98; &::before { display: table-cell; - font-weight: 700; text-align: right; content: counter(item) "."; padding: 0 10px 0 0; + white-space: nowrap; } } } diff --git a/src/shared/components/challenge-detail/Submissions/SubmissionRow/SubmissionHistoryRow/index.jsx b/src/shared/components/challenge-detail/Submissions/SubmissionRow/SubmissionHistoryRow/index.jsx index 90cdf96e3d..750458887b 100644 --- a/src/shared/components/challenge-detail/Submissions/SubmissionRow/SubmissionHistoryRow/index.jsx +++ b/src/shared/components/challenge-detail/Submissions/SubmissionRow/SubmissionHistoryRow/index.jsx @@ -6,14 +6,19 @@ import React from 'react'; import PT from 'prop-types'; +import { services } from 'topcoder-react-lib'; import moment from 'moment'; +import { CHALLENGE_STATUS } from 'utils/tc'; import FailedSubmissionTooltip from '../../FailedSubmissionTooltip'; // import Completed from '../../../icons/completed.svg'; import InReview from '../../../icons/in-review.svg'; import Queued from '../../../icons/queued.svg'; +import DownloadIcon from '../../../../SubmissionManagement/Icons/IconSquareDownload.svg'; import './style.scss'; +const { getService } = services.submissions; + export default function SubmissionHistoryRow({ isMM, submission, @@ -22,6 +27,11 @@ export default function SubmissionHistoryRow({ submissionTime, isReviewPhaseComplete, status, + challengeStatus, + auth, + numWinners, + submissionId, + isLoggedIn, }) { const getInitialReviewResult = () => { if (provisionalScore && provisionalScore < 0) return ; @@ -70,6 +80,33 @@ export default function SubmissionHistoryRow({ {moment(submissionTime).format('DD MMM YYYY')} {moment(submissionTime).format('HH:mm:ss')}
+ { + isLoggedIn && isMM + && (numWinners > 0 || challengeStatus === CHALLENGE_STATUS.COMPLETED) && ( +
+
Action
+ +
+ ) + } ); @@ -79,6 +116,7 @@ SubmissionHistoryRow.defaultProps = { finalScore: null, provisionalScore: null, isReviewPhaseComplete: false, + isLoggedIn: false, }; SubmissionHistoryRow.propTypes = { @@ -94,5 +132,10 @@ SubmissionHistoryRow.propTypes = { PT.string, ]), submissionTime: PT.string.isRequired, + challengeStatus: PT.string.isRequired, isReviewPhaseComplete: PT.bool, + auth: PT.shape().isRequired, + numWinners: PT.number.isRequired, + submissionId: PT.string.isRequired, + isLoggedIn: PT.bool, }; diff --git a/src/shared/components/challenge-detail/Submissions/SubmissionRow/SubmissionHistoryRow/style.scss b/src/shared/components/challenge-detail/Submissions/SubmissionRow/SubmissionHistoryRow/style.scss index afe69cc0bb..3a64435b9e 100644 --- a/src/shared/components/challenge-detail/Submissions/SubmissionRow/SubmissionHistoryRow/style.scss +++ b/src/shared/components/challenge-detail/Submissions/SubmissionRow/SubmissionHistoryRow/style.scss @@ -77,7 +77,7 @@ } &.col-3 { - flex: 30; + flex: 32; @include roboto-medium; @@ -100,7 +100,15 @@ text-align: left; &.mm { - flex: 30; + flex: 28; + } + } + + &.center { + text-align: center; + + @include xs-to-sm { + text-align: left; } } } @@ -122,3 +130,14 @@ text-align: left; } } + +button { + cursor: pointer; + border: none; + outline: none; + font: inherit; + color: inherit; + padding: 0; + background: transparent; + text-transform: uppercase; +} diff --git a/src/shared/components/challenge-detail/Submissions/SubmissionRow/index.jsx b/src/shared/components/challenge-detail/Submissions/SubmissionRow/index.jsx index b7e550869f..f4a61b11bb 100644 --- a/src/shared/components/challenge-detail/Submissions/SubmissionRow/index.jsx +++ b/src/shared/components/challenge-detail/Submissions/SubmissionRow/index.jsx @@ -6,7 +6,7 @@ import React from 'react'; import PT from 'prop-types'; import _ from 'lodash'; -import { getRatingLevel } from 'utils/tc'; +import { CHALLENGE_STATUS, getRatingLevel } from 'utils/tc'; import { Modal } from 'topcoder-react-ui-kit'; import IconClose from 'assets/images/icon-close-green.svg'; import moment from 'moment'; @@ -19,8 +19,9 @@ import SubmissionHistoryRow from './SubmissionHistoryRow'; import style from './style.scss'; export default function SubmissionRow({ - isMM, openHistory, member, submissions, score, toggleHistory, + isMM, openHistory, member, submissions, score, toggleHistory, challengeStatus, isReviewPhaseComplete, finalRank, provisionalRank, onShowPopup, rating, viewAsTable, + numWinners, auth, isLoggedIn, }) { const { submissionTime, provisionalScore, status, submissionId, @@ -129,7 +130,10 @@ export default function SubmissionRow({ { openHistory && ( - + 0 || challengeStatus === CHALLENGE_STATUS.COMPLETED) ? style.download : ''}` }} + >

Submission History

@@ -162,7 +166,14 @@ export default function SubmissionRow({ Time
{ - isMM && ( + isMM && (numWinners > 0 || challengeStatus === CHALLENGE_STATUS.COMPLETED) && ( +
+ Action +
+ ) + } + { + isMM && isLoggedIn && (
 
) } @@ -174,17 +185,24 @@ export default function SubmissionRow({ )) }
-
- CLOSE +
+
+ CLOSE +
@@ -201,12 +219,14 @@ SubmissionRow.defaultProps = { finalRank: null, provisionalRank: null, rating: null, + isLoggedIn: false, }; SubmissionRow.propTypes = { isMM: PT.bool.isRequired, openHistory: PT.bool.isRequired, member: PT.string.isRequired, + challengeStatus: PT.string.isRequired, submissions: PT.arrayOf(PT.shape({ provisionalScore: PT.oneOfType([ PT.number, @@ -241,4 +261,7 @@ SubmissionRow.propTypes = { provisionalRank: PT.number, onShowPopup: PT.func.isRequired, viewAsTable: PT.bool.isRequired, + isLoggedIn: PT.bool, + numWinners: PT.number.isRequired, + auth: PT.shape().isRequired, }; diff --git a/src/shared/components/challenge-detail/Submissions/SubmissionRow/style.scss b/src/shared/components/challenge-detail/Submissions/SubmissionRow/style.scss index 67703ed926..6719dc0291 100644 --- a/src/shared/components/challenge-detail/Submissions/SubmissionRow/style.scss +++ b/src/shared/components/challenge-detail/Submissions/SubmissionRow/style.scss @@ -1,5 +1,23 @@ @import "~styles/mixins"; +.modal { + background: #fff; + border-radius: 4; + max-height: 95vh; + overflow: hidden; + padding: 40; + width: 800px; + position: fixed; + top: 0; + left: 0; + transform: translate((calc(50vw - 50%), calc(50vh - 50%))); + z-index: 999; + + &.download { + width: 900px; + } +} + .history { padding-bottom: 10px; border-radius: 8px; @@ -50,11 +68,15 @@ } &.col-4 { - flex: 30; + flex: 32; } &.col-5 { - flex: 30; + flex: 28; + } + + &.center { + text-align: center; } } @@ -121,7 +143,7 @@ } .col-3 { - flex: 30; + flex: 32; } .col-4 { @@ -351,16 +373,22 @@ hr { } } +.close-wrapper { + display: flex; + justify-content: center; + align-items: center; +} + .close-btn { cursor: pointer; display: flex; - justify-content: flex-end; + align-items: center; + justify-content: center; margin-top: 24px; - margin-bottom: 32px; background-color: #137d60; padding: 8px 24px; + width: 91px; border-radius: 24px; - float: right; span { @include roboto-bold; @@ -374,7 +402,6 @@ hr { @include xs-to-sm { border-top: 1px solid #e9e9e9; position: sticky; - margin-bottom: 16px; } } diff --git a/src/shared/components/challenge-detail/Submissions/index.jsx b/src/shared/components/challenge-detail/Submissions/index.jsx index 486427c0c6..8fa9b7d730 100644 --- a/src/shared/components/challenge-detail/Submissions/index.jsx +++ b/src/shared/components/challenge-detail/Submissions/index.jsx @@ -10,17 +10,19 @@ import { isMM as checkIsMM } from 'utils/challenge'; import _ from 'lodash'; import { connect } from 'react-redux'; import { config } from 'topcoder-react-utils'; -import { submission as submissionUtils } from 'topcoder-react-lib'; +import { submission as submissionUtils, services } from 'topcoder-react-lib'; import { isTokenExpired } from '@topcoder-platform/tc-auth-lib'; import cn from 'classnames'; import { Button } from 'topcoder-react-ui-kit'; import DateSortIcon from 'assets/images/icon-date-sort.svg'; import SortIcon from 'assets/images/icon-sort.svg'; +import { getSubmissionId } from 'utils/submissions'; +import { compressFiles } from 'utils/files'; import sortList from 'utils/challenge-detail/sort'; import challengeDetailsActions from 'actions/page/challenge-details'; import LoadingIndicator from 'components/LoadingIndicator'; -import { goToLogin, getRatingLevel } from 'utils/tc'; +import { goToLogin, getRatingLevel, CHALLENGE_STATUS } from 'utils/tc'; import Lock from '../icons/lock.svg'; import ViewAsListActive from '../icons/view-as-list-active.svg'; import ViewAsListInactive from '../icons/view-as-list-inactive.svg'; @@ -32,6 +34,8 @@ import style from './style.scss'; const { getProvisionalScore, getFinalScore } = submissionUtils; +const { getService } = services.submissions; + class SubmissionsComponent extends React.Component { constructor(props) { super(props); @@ -47,6 +51,7 @@ class SubmissionsComponent extends React.Component { finalRankClicked: false, provisionalRankClicked: false, provisionalScoreClicked: false, + downloadingAll: false, }; this.onHandleInformationPopup = this.onHandleInformationPopup.bind(this); this.getSubmissionsSortParam = this.getSubmissionsSortParam.bind(this); @@ -292,6 +297,8 @@ class SubmissionsComponent extends React.Component { challengesUrl, viewAsTable, setViewAsTable, + numWinners, + auth, } = this.props; const { checkpoints, @@ -302,6 +309,7 @@ class SubmissionsComponent extends React.Component { } = challenge; const isMM = this.isMM(); + const isLoggedIn = !_.isEmpty(auth.tokenV3); const isReviewPhaseComplete = this.checkIsReviewPhaseComplete(); const { field, sort } = this.getSubmissionsSortParam(isMM, isReviewPhaseComplete); @@ -319,6 +327,7 @@ class SubmissionsComponent extends React.Component { finalRankClicked, provisionalRankClicked, provisionalScoreClicked, + downloadingAll, } = this.state; const sortOptionClicked = { @@ -593,6 +602,58 @@ class SubmissionsComponent extends React.Component { ) } + { + ((numWinners > 0 || challenge.status === CHALLENGE_STATUS.COMPLETED) + && isMM && isLoggedIn) && ( +
+ +
+ ) + } { isMM && (
@@ -786,6 +847,7 @@ class SubmissionsComponent extends React.Component { isMM={isMM} key={submission.member} {...submission} + challengeStatus={challenge.status} toggleHistory={() => { toggleSubmissionHistory(index); }} openHistory={(submissionHistoryOpen[index.toString()] || false)} isLoadingSubmissionInformation={isLoadingSubmissionInformation} @@ -795,6 +857,9 @@ class SubmissionsComponent extends React.Component { onGetFlagImageFail={onGetFlagImageFail} submissionDetail={submission} viewAsTable={viewAsTable} + numWinners={numWinners} + auth={auth} + isLoggedIn={isLoggedIn} /> )) ) @@ -908,6 +973,7 @@ SubmissionsComponent.propTypes = { type: PT.string.isRequired, tags: PT.arrayOf(PT.string), registrants: PT.any, + status: PT.string.isRequired, phases: PT.any, }).isRequired, toggleSubmissionHistory: PT.func.isRequired, @@ -936,6 +1002,7 @@ SubmissionsComponent.propTypes = { challengesUrl: PT.string.isRequired, viewAsTable: PT.bool.isRequired, setViewAsTable: PT.func.isRequired, + numWinners: PT.number.isRequired, }; function mapDispatchToProps(dispatch) { diff --git a/src/shared/components/challenge-detail/Submissions/style.scss b/src/shared/components/challenge-detail/Submissions/style.scss index 76185c7ff6..3fb513c1fc 100644 --- a/src/shared/components/challenge-detail/Submissions/style.scss +++ b/src/shared/components/challenge-detail/Submissions/style.scss @@ -466,6 +466,39 @@ padding-top: 5px; } +.block-download-all { + display: flex; + justify-content: flex-end; + margin-bottom: 10px; + + .download { + @include roboto-bold; + + font-size: 15px; + line-height: 16px; + color: #137d60; + background-color: transparent; + + &:hover { + color: #0ab88a; + } + + &:disabled { + pointer-events: none; + background-color: transparent; + color: #137d60; + } + + &.MM { + cursor: pointer; + } + + @include xs-to-sm { + font-size: 13px; + } + } +} + .col-arrow { display: flex; padding-left: 5px; diff --git a/src/shared/components/challenge-detail/Winners/Winner/index.jsx b/src/shared/components/challenge-detail/Winners/Winner/index.jsx index f5f7b999ea..d61c04fc82 100644 --- a/src/shared/components/challenge-detail/Winners/Winner/index.jsx +++ b/src/shared/components/challenge-detail/Winners/Winner/index.jsx @@ -1,22 +1,30 @@ import { Avatar } from 'topcoder-react-ui-kit'; import PT from 'prop-types'; +import { services } from 'topcoder-react-lib'; import React, { useEffect, useState } from 'react'; import _ from 'lodash'; import { config } from 'topcoder-react-utils'; import { formatOrdinals, numberWithCommas } from 'utils/challenge-detail/helper'; +import { getMMSubmissionId } from 'utils/submissions'; +import DownloadIcon from '../../../SubmissionManagement/Icons/IconSquareDownload.svg'; import style from './style.scss'; +const { getService } = services.submissions; + function getId(submissions, placement) { return submissions.find(s => s.placement === placement).submissionId; } export default function Winner({ isDesign, + isMM, prizes, submissions, viewable, winner, + isLoggedIn, + auth, }) { const [windowOrigin, setWindowOrigin] = useState(); useEffect(() => { @@ -24,6 +32,7 @@ export default function Winner({ }, []); const submissionId = viewable && getId(submissions, winner.placement); + const mmSubmissionId = isMM && getMMSubmissionId(submissions, winner.handle); let avatarUrl = winner.photoURL; if (avatarUrl) { @@ -69,28 +78,6 @@ export default function Winner({
) } - { - (winner.submissionDownloadLink && viewable) - && ( - - Download - - ) - } - { - /* -
- Submitted on:‌ - ‌{moment(winner.submissionDate).format('MMM DD, YYYY HH:mm')} -
- */ - } @@ -100,6 +87,53 @@ export default function Winner({ {numberWithCommas(prize)} +
+ { + ((!winner.submissionDownloadLink || !viewable) && isMM && isLoggedIn) && ( + + ) + } + { + (winner.submissionDownloadLink && viewable) + && ( + + + + ) + } + { + /* +
+ Submitted on:‌ + ‌{moment(winner.submissionDate).format('MMM DD, YYYY HH:mm')} +
+ */ + } +
); } @@ -110,7 +144,8 @@ Winner.defaultProps = { Winner.propTypes = { isDesign: PT.bool.isRequired, - prizes: PT.arrayOf(PT.number), + isMM: PT.bool.isRequired, + prizes: PT.arrayOf(PT.shape()), submissions: PT.arrayOf(PT.object).isRequired, viewable: PT.bool.isRequired, winner: PT.shape({ @@ -119,4 +154,6 @@ Winner.propTypes = { photoURL: PT.any, submissionDownloadLink: PT.any, }).isRequired, + isLoggedIn: PT.bool.isRequired, + auth: PT.shape().isRequired, }; diff --git a/src/shared/components/challenge-detail/Winners/Winner/style.scss b/src/shared/components/challenge-detail/Winners/Winner/style.scss index 865263cc3a..d63ffe83ff 100644 --- a/src/shared/components/challenge-detail/Winners/Winner/style.scss +++ b/src/shared/components/challenge-detail/Winners/Winner/style.scss @@ -3,7 +3,6 @@ .avatar-prize { display: flex; align-items: center; - margin-bottom: 20px; @include xs-to-sm { margin-bottom: 10px; @@ -66,7 +65,7 @@ display: flex; justify-content: space-between; border-top: 1px solid $tc-gray-10; - padding: 17px 17px 0 17px; + padding: 17px; @include xs-to-sm { padding: 16px 0; @@ -183,16 +182,6 @@ } } - .download { - font-size: 15px; - line-height: 22px; - color: $tc-dark-blue; - - @include xs-to-sm { - font-size: 13px; - } - } - .date { font-size: 15px; line-height: 22px; @@ -221,6 +210,7 @@ .left { display: flex; + flex: 1; } .right { @@ -239,3 +229,13 @@ } } } + +.download-container { + display: flex; + align-items: center; + margin-left: 40px; + + .download { + text-decoration: none; + } +} diff --git a/src/shared/components/challenge-detail/Winners/index.jsx b/src/shared/components/challenge-detail/Winners/index.jsx index efe57fb954..ce850e36c3 100644 --- a/src/shared/components/challenge-detail/Winners/index.jsx +++ b/src/shared/components/challenge-detail/Winners/index.jsx @@ -3,29 +3,90 @@ * Winners tab component. */ -import React from 'react'; +import React, { useState } from 'react'; import PT from 'prop-types'; +import _ from 'lodash'; +import { services } from 'topcoder-react-lib'; +import { CHALLENGE_STATUS } from 'utils/tc'; +import { getMMSubmissionId } from 'utils/submissions'; +import { compressFiles } from 'utils/files'; import Winner from './Winner'; import './style.scss'; +const { getService } = services.submissions; + export default function Winners({ winners, prizes, submissions, viewable, isDesign, + isMM, + isLoggedIn, + auth, + challengeStatus, }) { + const [downloadingAll, setDownloadingAll] = useState(false); return (
+ { + ((winners.length > 0 || challengeStatus === CHALLENGE_STATUS.COMPLETED) + && isMM && isLoggedIn) && ( +
+ +
+ ) + } { winners.map(w => ( )) } @@ -39,12 +100,19 @@ Winners.defaultProps = { submissions: [], viewable: false, isDesign: false, + isMM: false, + isLoggedIn: false, + challengeStatus: '', }; Winners.propTypes = { winners: PT.arrayOf(PT.shape()), - prizes: PT.arrayOf(PT.number), + prizes: PT.arrayOf(PT.shape()), submissions: PT.arrayOf(PT.shape()), viewable: PT.bool, isDesign: PT.bool, + isMM: PT.bool, + isLoggedIn: PT.bool, + challengeStatus: PT.string, + auth: PT.shape().isRequired, }; diff --git a/src/shared/components/challenge-detail/Winners/style.scss b/src/shared/components/challenge-detail/Winners/style.scss index 6e29c3e518..b99774a831 100644 --- a/src/shared/components/challenge-detail/Winners/style.scss +++ b/src/shared/components/challenge-detail/Winners/style.scss @@ -21,3 +21,36 @@ text-align: center; width: 100%; } + +.block-download-all { + display: flex; + justify-content: flex-end; + margin-bottom: 20px; +} + +.download { + @include roboto-bold; + + font-size: 15px; + line-height: 16px; + color: #137d60; + background-color: transparent; + + &:hover { + color: #0ab88a; + } + + &:disabled { + pointer-events: none; + background-color: transparent; + color: #137d60; + } + + &.MM { + cursor: pointer; + } + + @include xs-to-sm { + font-size: 13px; + } +} diff --git a/src/shared/containers/EDU/styles/home.scss b/src/shared/containers/EDU/styles/home.scss index 46cf2c2e60..1dc0c6c6dd 100644 --- a/src/shared/containers/EDU/styles/home.scss +++ b/src/shared/containers/EDU/styles/home.scss @@ -5,6 +5,7 @@ flex: 1; flex-direction: column; width: 100%; + overflow: hidden; .bannerContainer { background-image: linear-gradient(98.81deg, #8b41b0 0%, #ef476f 100%); diff --git a/src/shared/containers/TopcoderHeader/index.jsx b/src/shared/containers/TopcoderHeader/index.jsx index 7ef484133c..074e29e045 100644 --- a/src/shared/containers/TopcoderHeader/index.jsx +++ b/src/shared/containers/TopcoderHeader/index.jsx @@ -1,22 +1,17 @@ /* global tcUniNav */ import React, { useEffect, useMemo, useRef } from 'react'; -import PT from 'prop-types'; import { connect } from 'react-redux'; import { config } from 'topcoder-react-utils'; import LoadingIndicator from 'components/LoadingIndicator'; -import _ from 'lodash'; -import { getInitials, getSubPageConfiguration } from '../../utils/url'; +import { getSubPageConfiguration } from '../../utils/url'; import { SSRPlaceholder } from '../../utils/SSR'; import './styles.scss'; let counter = 0; const headerElIdTmpl = 'uninav-headerNav'; -const TopcoderHeader = ({ auth }) => { +const TopcoderHeader = () => { const uniNavInitialized = useRef(false); - const user = _.get(auth, 'profile') || {}; - const authToken = _.get(auth, 'tokenV3'); - const isAuthenticated = !!authToken; const authURLs = config.HEADER_AUTH_URLS; const headerRef = useRef(); const headerElId = useRef(`${headerElIdTmpl}-${counter}`); @@ -49,15 +44,11 @@ const TopcoderHeader = ({ auth }) => { return type; }, []); - const navigationUserInfo = { - ...user, - initials: getInitials(user.firstName, user.lastName), - }; - useEffect(() => { if (uniNavInitialized.current) { return; } + headerRef.current.id = headerElId.current; uniNavInitialized.current = true; counter += 1; @@ -69,6 +60,7 @@ const TopcoderHeader = ({ auth }) => { type: navType, toolName: getSubPageConfiguration().toolName, toolRoot: getSubPageConfiguration().toolRoot, + user: 'auto', signOut: () => { window.location = `${config.URL.BASE}/logout?ref=nav`; }, @@ -81,23 +73,10 @@ const TopcoderHeader = ({ auth }) => { }); }, [navType]); - useEffect(() => { - tcUniNav('update', headerElId.current, { - user: isAuthenticated ? navigationUserInfo : null, - }); - }, [isAuthenticated, navigationUserInfo]); - return (
); }; -TopcoderHeader.defaultProps = { - auth: {}, -}; - -TopcoderHeader.propTypes = { - auth: PT.shape(), -}; const mapStateToProps = state => ({ auth: state.auth, diff --git a/src/shared/containers/challenge-detail/index.jsx b/src/shared/containers/challenge-detail/index.jsx index 361b414712..3b36784723 100644 --- a/src/shared/containers/challenge-detail/index.jsx +++ b/src/shared/containers/challenge-detail/index.jsx @@ -155,7 +155,7 @@ class ChallengeDetailPageContainer extends React.Component { this.apiService = getService({ spaceName: 'EDU' }); this.state = { thriveArticles: [], - showDeadlineDetail: true, + showDeadlineDetail: false, registrantsSort: { field: '', sort: '', @@ -604,6 +604,7 @@ class ChallengeDetailPageContainer extends React.Component { challengesUrl={challengesUrl} viewAsTable={viewAsTable && isMM} setViewAsTable={value => this.setState({ viewAsTable: value })} + numWinners={isLegacyMM ? 0 : winners.length} /> ) } @@ -659,6 +660,9 @@ class ChallengeDetailPageContainer extends React.Component { viewable={submissionsViewable ? submissionsViewable.value === 'true' : false} submissions={challenge.submissions} isDesign={track.toLowerCase() === 'design'} + isMM={isMM} + isLoggedIn={isLoggedIn} + auth={auth} /> ) } diff --git a/src/shared/containers/timeline-wall/index.jsx b/src/shared/containers/timeline-wall/index.jsx index 6c1e5aca8e..4bdf3f863b 100644 --- a/src/shared/containers/timeline-wall/index.jsx +++ b/src/shared/containers/timeline-wall/index.jsx @@ -24,6 +24,8 @@ import './styles.scss'; const FETCHING_PENDING_APPROVAL_EVENTS_INTERVAL = _.get(config, 'TIMELINE.FETCHING_PENDING_APPROVAL_EVENTS_INTERVAL', 0); const FORUM_LINK = _.get(config, 'TIMELINE.FORUM_LINK', ''); function TimelineWallContainer(props) { + const currentTime = new Date(); + const thisYear = currentTime.getFullYear(); const [tab, setTab] = useState(0); const fetchingApprovalsInterval = useRef(null); const [showRightFilterMobile, setShowRightFilterMobile] = useState(false); @@ -223,7 +225,7 @@ function TimelineWallContainer(props) { type="button" styleName="filter-dropdown hide-desktop show-mobile" > - {selectedFilterValue.year ? selectedFilterValue.year : ''} + {selectedFilterValue.year ? selectedFilterValue.year : thisYear}
diff --git a/src/shared/containers/timeline-wall/modal-event-add/index.jsx b/src/shared/containers/timeline-wall/modal-event-add/index.jsx index c4cde0570c..e4fd5517b9 100644 --- a/src/shared/containers/timeline-wall/modal-event-add/index.jsx +++ b/src/shared/containers/timeline-wall/modal-event-add/index.jsx @@ -3,6 +3,7 @@ import PT from 'prop-types'; import { Modal } from 'topcoder-react-ui-kit'; import IconCloseGreen from 'assets/images/icon-close-green.svg'; import LoadingIndicator from 'components/LoadingIndicator'; +import cn from 'classnames'; import style from './styles.scss'; @@ -17,14 +18,17 @@ function ModalEventAdd({ onCancel={onClose} >
- Confirmation + {uploadResult ? 'Error' : 'Confirmation'}
{ uploading ? ( ) : ( - + { uploadResult || successMessage } diff --git a/src/shared/containers/timeline-wall/modal-event-add/styles.scss b/src/shared/containers/timeline-wall/modal-event-add/styles.scss index 58b03c1cc9..7aed3cb770 100644 --- a/src/shared/containers/timeline-wall/modal-event-add/styles.scss +++ b/src/shared/containers/timeline-wall/modal-event-add/styles.scss @@ -44,6 +44,10 @@ strong { @include roboto-bold; } + + &.error { + color: #ef476f; + } } .separator { diff --git a/src/shared/containers/timeline-wall/styles.scss b/src/shared/containers/timeline-wall/styles.scss index 5d4eb45b15..678e076183 100644 --- a/src/shared/containers/timeline-wall/styles.scss +++ b/src/shared/containers/timeline-wall/styles.scss @@ -86,6 +86,7 @@ border: none; color: white; + &:visited, &:hover { color: white; } diff --git a/src/shared/containers/timeline-wall/timeline-events/right-filter/styles.scss b/src/shared/containers/timeline-wall/timeline-events/right-filter/styles.scss index ce83f8f22d..db8d39a626 100644 --- a/src/shared/containers/timeline-wall/timeline-events/right-filter/styles.scss +++ b/src/shared/containers/timeline-wall/timeline-events/right-filter/styles.scss @@ -3,6 +3,7 @@ .container { display: flex; flex-direction: column; + z-index: 1; @media (max-width: 768px) { position: fixed; @@ -53,16 +54,16 @@ border-top-left-radius: 8px; border-bottom-left-radius: 8px; padding: 10px 0; - position: fixed; - right: 0; - left: 1374px; + position: sticky; + margin-bottom: 80px; + top: 0; max-height: 70%; overflow: scroll; - -ms-overflow-style: none; /* Internet Explorer 10+ */ - scrollbar-width: none; /* Firefox */ + -ms-overflow-style: none; /* Internet Explorer 10+ */ + scrollbar-width: none; /* Firefox */ &::-webkit-scrollbar { - display: none; /* Safari and Chrome */ + display: none; /* Safari and Chrome */ } @media (max-width: 1500px) { @@ -72,6 +73,8 @@ @media (max-width: 768px) { border-radius: 0; width: calc(100% - 10px); + position: relative; + margin-bottom: unset; } span { diff --git a/src/shared/containers/timeline-wall/timeline-events/styles.scss b/src/shared/containers/timeline-wall/timeline-events/styles.scss index d55b957f54..476d845156 100644 --- a/src/shared/containers/timeline-wall/timeline-events/styles.scss +++ b/src/shared/containers/timeline-wall/timeline-events/styles.scss @@ -4,6 +4,7 @@ display: flex; flex-direction: row; justify-content: space-between; + max-width: 1492px; } .left-content { diff --git a/src/shared/reducers/index.js b/src/shared/reducers/index.js index eda5f72328..c27bf01ad8 100644 --- a/src/shared/reducers/index.js +++ b/src/shared/reducers/index.js @@ -20,7 +20,7 @@ import { getCommunityId } from 'server/services/communities'; import { redux, config, isomorphy } from 'topcoder-react-utils'; import { reducer as toastrReducer } from 'react-redux-toastr'; import { reducerFactory } from 'topcoder-react-lib'; -import { getAuthTokens } from 'utils/tc'; +import { getAuthTokens, getM2mToken } from 'utils/tc'; import contentful from './contentful'; import topcoderHeader from './topcoder_header'; @@ -153,6 +153,10 @@ export function factory(req) { const user = _.get(res, 'auth.user'); if (user && isomorphy.isServerSide()) { res.auth.userIdHash = generateUserIdHash(user); + getM2mToken() + .then(((token) => { + res.auth.m2mToken = token; + })); } if (req) { diff --git a/src/shared/utils/challenge-detail/helper.jsx b/src/shared/utils/challenge-detail/helper.jsx index c307e5f66a..ca9f5de982 100644 --- a/src/shared/utils/challenge-detail/helper.jsx +++ b/src/shared/utils/challenge-detail/helper.jsx @@ -55,6 +55,7 @@ export function getEndDate(challenge) { export function getTimeLeft( phase, toGoText = 'to go', + fullText = false, ) { const STALLED_TIME_LEFT_MSG = 'Challenge is currently on hold'; const FF_TIME_LEFT_MSG = 'Winner is working on fixes'; @@ -71,9 +72,9 @@ export function getTimeLeft( if (late) time = -time; let format; - if (time > DAY_MS) format = 'D[d] H[h]'; - else if (time > HOUR_MS) format = 'H[h] m[min]'; - else format = 'm[min] s[s]'; + if (time > DAY_MS) format = fullText ? 'D [day] H [hour]' : 'D[d] H[h]'; + else if (time > HOUR_MS) format = fullText ? 'H [hour] m [minute]' : 'H[h] m[min]'; + else format = fullText ? 'm [minute] s [second]' : 'm[min] s[s]'; time = moment.duration(time).format(format); time = late ? `${time} Past Due` : `${time} ${toGoText}`; diff --git a/src/shared/utils/challenge.js b/src/shared/utils/challenge.js index 22dce73260..ce4679224b 100644 --- a/src/shared/utils/challenge.js +++ b/src/shared/utils/challenge.js @@ -9,7 +9,8 @@ import _ from 'lodash'; */ export function isMM(challenge) { const tags = _.get(challenge, 'tags') || []; - return tags.includes('Marathon Match'); + const isMMType = challenge ? challenge.type === 'Marathon Match' : false; + return tags.includes('Marathon Match') || isMMType; } /** diff --git a/src/shared/utils/files.js b/src/shared/utils/files.js new file mode 100644 index 0000000000..ccbde7fc21 --- /dev/null +++ b/src/shared/utils/files.js @@ -0,0 +1,67 @@ +/* eslint-disable import/prefer-default-export */ +import * as fflate from 'fflate'; + +const fileToU8 = (file, cb) => { + const fr = new FileReader(); + fr.onloadend = () => { + cb(new Uint8Array(fr.result)); + }; + fr.readAsArrayBuffer(file); +}; + +const download = (file, name) => { + const url = URL.createObjectURL(new Blob([file])); + const dl = document.createElement('a'); + dl.download = name || (`compressed-file-${Date.now()}.zip`); + dl.href = url; + dl.click(); + URL.revokeObjectURL(url); +}; + +/** + * Compress list of files + * @param {Array} files list of files + * @param {String} fileName file name + * @param {Function} finish finish callback + */ +export const compressFiles = (files, fileName, finish) => { + // fflate's ZIP API is asynchronous and parallelized (multithreaded) + let left = files.length; + const zipObj = {}; + const ALREADY_COMPRESSED = [ + 'zip', 'gz', 'png', 'jpg', 'jpeg', 'pdf', 'doc', 'docx', 'ppt', 'pptx', + 'xls', 'xlsx', 'heic', 'heif', '7z', 'bz2', 'rar', 'gif', 'webp', 'webm', + 'mp4', 'mov', 'mp3', 'aifc', + ]; + + // Yet again, this is necessary for parallelization. + const processFile = (i) => { + const file = files[i]; + const ext = file.name.slice(file.name.lastIndexOf('.') + 1).toLowerCase(); + fileToU8(file, (buf) => { + // With fflate, we can choose which files we want to compress + zipObj[file.name] = [buf, { + level: ALREADY_COMPRESSED.indexOf(ext) === -1 ? 6 : 0, + }]; + + // If we didn't want to specify options: + // zipObj[file.name] = buf; + + // eslint-disable-next-line no-plusplus + if (!--left) { + fflate.zip(zipObj, { + // If you want to control options for every file, you can do so here + // They are merged with the per-file options (if they exist) + // mem: 9 + }, (err, out) => { + download(out, fileName); + finish(); + }); + } + }); + }; + // eslint-disable-next-line no-plusplus + for (let i = 0; i < files.length; ++i) { + processFile(i); + } +}; diff --git a/src/shared/utils/submissions.js b/src/shared/utils/submissions.js new file mode 100644 index 0000000000..27cd62e800 --- /dev/null +++ b/src/shared/utils/submissions.js @@ -0,0 +1,27 @@ +/* eslint-disable import/prefer-default-export */ + +/** + * Get submission id of marathon match challenge + * @param {Array} submissions list of submission + * @param {String} handle handle + * @returns String or null + */ +export function getMMSubmissionId(submissions, handle) { + const filterSubmissions = handle ? submissions.filter(s => s.createdBy === handle) : submissions; + const sortedSubmissions = filterSubmissions.sort((a, b) => (a.created < b.created ? 1 : -1)); + + return sortedSubmissions.length > 0 ? sortedSubmissions[0].id : null; +} + +/** + * Get submission id of history submissions + * @param {Array} submissions list of history submission + * @returns String or null + */ +export function getSubmissionId(submissions) { + const filterSubmissions = submissions; + const sortedSubmissions = filterSubmissions.sort((a, b) => ( + a.submissionTime < b.submissionTime ? 1 : -1)); + + return sortedSubmissions.length > 0 ? sortedSubmissions[0].submissionId : null; +} diff --git a/src/shared/utils/tc.js b/src/shared/utils/tc.js index e584cc9bde..c223f2818e 100644 --- a/src/shared/utils/tc.js +++ b/src/shared/utils/tc.js @@ -7,8 +7,9 @@ import _ from 'lodash'; import moment from 'moment-timezone'; import { isTokenExpired } from '@topcoder-platform/tc-auth-lib'; import { config, isomorphy } from 'topcoder-react-utils'; +import { services, tc } from 'topcoder-react-lib'; -import { tc } from 'topcoder-react-lib'; +const { api } = services; export const { OLD_COMPETITION_TRACKS, @@ -196,6 +197,14 @@ export function getAuthTokens(req = {}) { return { tokenV2, tokenV3 }; } +/** + * Get M2M Token + * @return {Promise} + */ +export async function getM2mToken() { + return api.getTcM2mToken().then((m2mToken => m2mToken)); +} + /** * At the client side it redirects to Topcoder login, with the current URL used * as the return address. Does nothing at the server side. diff --git a/src/shared/utils/url.js b/src/shared/utils/url.js index 5c7d43ca9c..2e12de0a77 100644 --- a/src/shared/utils/url.js +++ b/src/shared/utils/url.js @@ -190,7 +190,7 @@ export const getSubPageConfiguration = () => { } if (url.includes('/community/arena')) { - toolName = 'SRMs (Arena)'; + toolName = 'Single Round Matches (Arena)'; toolRoot = '/community/arena'; loginRedirect = '/community/arena'; type = 'tool';