diff --git a/package.json b/package.json index 6b495774f..b8bea93e0 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,6 @@ "@storybook/react": "7.6.10", "@stripe/react-stripe-js": "1.13.0", "@stripe/stripe-js": "1.41.0", - "@tanstack/react-table": "^8.11.7", "apexcharts": "^3.36.0", "axios": "^1.7.9", "browser-cookies": "^1.2.0", @@ -46,17 +45,10 @@ "express": "^4.21.2", "express-fileupload": "^1.4.0", "express-interceptor": "^1.2.0", - "fflate": "^0.7.4", - "filestack-react": "^2.0.0", - "focus-trap-react": "^6.0.0", - "get-parameter-names": "^0.3.0", "highcharts": "^10.3.3", "highcharts-react-official": "^3.2.0", "highlight.js": "^11.6.0", "html2canvas": "^1.4.1", - "isomorphic-fetch": "^2.2.1", - "joi": "^17.4.0", - "katex": "^0.16.4", "lodash": "^4.17.21", "markdown-it": "^13.0.2", "marked": "4.1.1", @@ -73,14 +65,12 @@ "react-color": "^2.13.8", "react-contenteditable": "^3.3.6", "react-css-super-themr": "^2.2.0", - "react-date-range": "^1.1.3", "react-datepicker": "^4.14.1", "react-dom": "^18.2.0", "react-dropzone": "^11.3.2", "react-elastic-carousel": "^0.11.5", "react-gtm-module": "^2.0.11", "react-helmet": "^6.1.0", - "react-html-parser": "^2.0.2", "react-markdown": "8.0.6", "react-otp-input": "^3.1.1", "react-popper": "^2.3.0", @@ -92,7 +82,6 @@ "react-scripts": "5.0.1", "react-select": "^5.8.0", "react-spinners": "^0.13.6", - "react-stickynode": "^1.4.1", "react-toastify": "^9.0.8", "react-tooltip": "5.11.1", "redux": "^4.2.0", @@ -101,21 +90,15 @@ "redux-promise": "^0.6.0", "redux-promise-middleware": "^6.1.3", "redux-thunk": "^2.4.1", - "rehype-katex": "^6.0.2", - "rehype-raw": "^6.1.1", - "rehype-stringify": "^9.0.3", "remark-breaks": "^3.0.2", "remark-frontmatter": "^4.0.1", "remark-gfm": "^3.0.1", - "remark-math": "^5.1.1", "remove": "^0.1.5", "sanitize-html": "^2.12.1", "sass": "^1.79.0", - "shortid": "^2.2.16", "styled-components": "^5.3.6", "swr": "^1.3.0", "tc-auth-lib": "topcoder-platform/tc-auth-lib#1.0.27", - "turndown": "^4.0.2", "typescript": "^4.8.4", "universal-navigation": "https://github.com/topcoder-platform/universal-navigation#9fc50d938be7182", "uuid": "^9.0.0" diff --git a/src/apps/earn/README.md b/src/apps/earn/README.md deleted file mode 100644 index 6cf398fdf..000000000 --- a/src/apps/earn/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Instructions for Running the Earn Tool Locally - -## Earn Config \ No newline at end of file diff --git a/src/apps/earn/index.ts b/src/apps/earn/index.ts deleted file mode 100644 index 6f39cd49b..000000000 --- a/src/apps/earn/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './src' diff --git a/src/apps/earn/src/EarnApp.tsx b/src/apps/earn/src/EarnApp.tsx deleted file mode 100644 index d25ffcfa0..000000000 --- a/src/apps/earn/src/EarnApp.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { FC, useContext } from 'react' -import { Outlet, Routes } from 'react-router-dom' -import { Provider } from 'react-redux' - -import { routerContext, RouterContextData } from '~/libs/core' - -import { toolTitle } from './earn.routes' -import store from './store' - -const EarnApp: FC<{}> = () => { - - const { getChildRoutes }: RouterContextData = useContext(routerContext) - - return ( - - - - {getChildRoutes(toolTitle)} - - - ) -} - -export default EarnApp diff --git a/src/apps/earn/src/actions/auth.js b/src/apps/earn/src/actions/auth.js deleted file mode 100644 index 45cec7a91..000000000 --- a/src/apps/earn/src/actions/auth.js +++ /dev/null @@ -1,111 +0,0 @@ -/** - * @module "actions.auth" - * @desc Actions related to Topcoder authentication system. - */ - -import { createActions } from "redux-actions"; -import _ from "lodash"; -import { decodeToken, readCookie } from "../utils/token"; -import { getApiV3, getApiV5 } from "../services/challenge-api"; -import { setErrorIcon, ERROR_ICON_TYPES } from "../utils/errors"; -import { TOKEN_COOKIE_KEYS } from "../constants/index"; -import { getAuthUserTokens } from "../utils/auth"; - -/** - * Helper method that checks for HTTP error response v5 and throws Error in this case. - * @param {Object} res HTTP response object - * @return {Object} API JSON response object - * @private - */ -async function checkErrorV5(res) { - if (!res.ok) { - if (res.status === 403) { - setErrorIcon(ERROR_ICON_TYPES.API, "Auth0", res.statusText); - } - throw new Error(res.statusText); - } - const jsonRes = await res.json(); - if (jsonRes.message) { - throw new Error(res.message); - } - return { - result: jsonRes, - headers: res.headers, - }; -} - -/** - * @static - * @desc Creates an action that loads Topcoder user profile from v3 API. - * @param {String} userTokenV3 v3 authentication token. - * @return {Action} - */ -function loadProfileDone(userTokenV3) { - if (!userTokenV3) return Promise.resolve(null); - const user = decodeToken(userTokenV3); - const apiV3 = getApiV3(userTokenV3); - const apiV5 = getApiV5(userTokenV3); - return Promise.all([ - apiV3 - .get(`/members/${user.handle}`) - .then((res) => res.json()) - .then((res) => (res.result.status === 200 ? res.result.content : {})), - apiV5 - .get(`/groups?memberId=${user.userId}&membershipType=user`) - .then(checkErrorV5) - .then((res) => res.result || []), - ]).then(([profile, groups]) => ({ ...profile, groups })); -} - -/** - * @static - * @desc Creates an action that sets Topcoder v2 authentication token. - * @param {String} tokenV2 Topcoder v2 authentication token. - * @return {Action} - */ -function setTcTokenV2(tokenV2) { - return tokenV2; -} - -/** - * @static - * @desc Creates an action that decodes Topcoder v3 authentication token, - * to get user object, and then writes both the token and the user object into - * Redux store. - * @param {String} tokenV3 Topcoder v3 authentication token. - * @return {Action} - */ -function setTcTokenV3(tokenV3) { - return tokenV3; -} - -async function setAuthDone() { - const { tokenV3 } = await getAuthUserTokens(); - const user = tokenV3 ? decodeToken(tokenV3) : null; - return user; -} - -/** - * @static - * @desc Check token cookies to find if a user is logged out: - * This is because all the token cookies are cleared if a user is logged out. - * @return {Action} - */ -function checkIsLoggedOut() { - const tokenKeys = Object.keys(TOKEN_COOKIE_KEYS); - const isLoggedOut = _.every( - tokenKeys, - (k) => readCookie(TOKEN_COOKIE_KEYS[k]) === undefined - ); - return { isLoggedOut }; -} - -export default createActions({ - AUTH: { - LOAD_PROFILE: loadProfileDone, - SET_TC_TOKEN_V2: setTcTokenV2, - SET_TC_TOKEN_V3: setTcTokenV3, - SET_AUTH_DONE: setAuthDone, - CHECK_IS_LOGGED_OUT: checkIsLoggedOut, - }, -}); diff --git a/src/apps/earn/src/actions/challenge-listing/filter-panel.js b/src/apps/earn/src/actions/challenge-listing/filter-panel.js deleted file mode 100644 index 8f907f5ae..000000000 --- a/src/apps/earn/src/actions/challenge-listing/filter-panel.js +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Actions related to the header filter panel. - */ - - import _ from 'lodash'; - import { createActions } from 'redux-actions'; - - export default createActions({ - CHALLENGE_LISTING: { - FILTER_PANEL: { - /* Expands / collapses the filter panel. */ - SET_EXPANDED: _.identity, - - /* Updates text in the search bar, without applying it to the active - * challenge filter. The text will be set to the filter when Enter is - * pressed. */ - SET_SEARCH_TEXT: _.identity, - - /* Shows / hides the modal with track switches (for mobile view only). */ - SHOW_TRACK_MODAL: _.identity, - }, - }, - }); - \ No newline at end of file diff --git a/src/apps/earn/src/actions/challenge-listing/index.js b/src/apps/earn/src/actions/challenge-listing/index.js deleted file mode 100644 index a878ba764..000000000 --- a/src/apps/earn/src/actions/challenge-listing/index.js +++ /dev/null @@ -1,612 +0,0 @@ -/** - * Challenge listing actions. - */ - - import _ from 'lodash'; - import { createActions } from 'redux-actions'; - import { decodeToken } from "@earn/utils/token"; - import { processSRM } from '@earn/utils/tc'; - import { getService } from '@earn/services/challenges'; - import { getReviewOpportunitiesService } from '@earn/services/reviewOpportunities'; - import { BUCKETS } from '@earn/utils/challenge-listing/buckets'; - import SORT from '@earn/utils/challenge-listing/sort'; - import { fireErrorMessage } from "@earn/utils/errors"; - -/** - * The maximum number of challenges to fetch in a single API call. - */ -const PAGE_SIZE = 10; - -/** - * The maximum number of review opportunities to fetch in a single API call. - */ -const REVIEW_OPPORTUNITY_PAGE_SIZE = 1000; - -/** - * Private. Loads from the backend all challenges matching some conditions. - * @param {Function} getter Given params object of shape { limit, offset } - * loads from the backend at most "limit" challenges, skipping the first - * "offset" ones. Returns loaded challenges as an array. - * @param {Number} page Optional. Next page of challenges to load. - * @param {Array} prev Optional. Challenges loaded so far. - */ -// function getAll(getter, page = 0, prev) { -// /* Amount of challenges to fetch in one API call. 50 is the current maximum -// * amount of challenges the backend returns, event when the larger limit is -// * explicitely required. */ - -// return getter({ -// perPage: PAGE_SIZE, -// page: page + 1, -// }).then(({ challenges: chunk }) => { -// if (!chunk.length) return prev || []; -// return getAll(getter, 1 + page, prev ? prev.concat(chunk) : chunk); -// }); -// } - -function getChallengeTypesInit() {} -/** - * Gets possible challenge types. - * @return {Promise} - */ -function getChallengeTypesDone() { - return getService() - .getChallengeTypes() - .then(res => res.sort((a, b) => a.name.localeCompare(b.name))); -} - -/** - * Gets possible challenge tags (technologies). - * @return {Promise} - */ -function getChallengeTagsDone() { - return getService() - .getChallengeTags() - .then(res => res.map(item => item.name) - .sort((a, b) => a.localeCompare(b))); -} - -/** - * Notifies about reloading of all active challenges. The UUID is stored in the - * state, and only challenges fetched by getAllActiveChallengesDone action with - * the same UUID will be accepted into the state. - * @param {String} uuid - * @return {String} - */ -function getActiveChallengesInit(uuid, page, frontFilter) { - return { uuid, page, frontFilter }; -} - -function getOpenForRegistrationChallengesInit(uuid, page, frontFilter) { - return { uuid, page, frontFilter }; -} - -function getMyChallengesInit(uuid, page, frontFilter) { - return { uuid, page, frontFilter }; -} - -function getAllChallengesInit(uuid, page, frontFilter) { - return { uuid, page, frontFilter }; -} - -function getMyPastChallengesInit(uuid, page, frontFilter) { - return { uuid, page, frontFilter }; -} - -/** - * Get all challenges and match with user challenges - * @param {String} uuid progress id - * @param {String} tokenV3 token v3 - * @param {Object} filter filter object - * @param {number} page start page - */ -// function getAllActiveChallengesWithUsersDone(uuid, tokenV3, filter, page = 0) { -// const service = getService(tokenV3); -// const calls = [ -// getAll(params => service.getChallenges(filter, params), page), -// ]; -// let user; -// if (tokenV3) { -// user = decodeToken(tokenV3).userId; - -// const newFilter = _.mapKeys(filter, (value, key) => { -// if (key === 'tag') return 'technologies'; - -// return key; -// }); - -// // Handle any errors on this endpoint so that the non-user specific challenges -// // will still be loaded. -// calls.push(getAll(params => service.getUserChallenges(user, newFilter, params) -// .catch(() => ({ challenges: [] }))), page); -// } -// return Promise.all(calls).then(([ch, uch]) => { -// /* uch array contains challenges where the user is participating in -// @@ -111,8 +124,8 @@ function getAllActiveChallengesDone(uuid, tokenV3) { -// * challenges in an efficient way. */ -// if (uch) { -// const map = {}; -// uch.forEach((item) => { map[item.id] = item; }); -// ch.forEach((item) => { -// if (map[item.id]) { -// /* It is fine to reassing, as the array we modifying is created just -// * above within the same function. */ -// /* eslint-disable no-param-reassign */ -// item.users[user] = true; -// item.userDetails = map[item.id].userDetails; -// /* eslint-enable no-param-reassign */ -// } -// }); -// } - -// return { uuid, challenges: ch, ...filter }; -// }); -// } - -/** TODO: Inspect if the 2 actions bellow can be removed? - * They do duplicate what is done in `getActiveChallengesDone` but fetch all challenges - * which was refactored in listing-improve - */ -// function getAllActiveChallengesInit(uuid) { -// return uuid; -// } -// function getAllActiveChallengesDone(uuid, tokenV3) { -// const filter = { status: 'Active' }; -// return getAllActiveChallengesWithUsersDone(uuid, tokenV3, filter); -// } - -// function getAllUserChallengesInit(uuid) { -// return uuid; -// } - -// function getAllUserChallengesDone(uuid, tokenV3) { -// const memberId = decodeToken(tokenV3).userId; -// const filter = { status: 'Active', memberId }; -// return getAllActiveChallengesWithUsersDone(uuid, tokenV3, filter); -// } - -/** - * Gets 1 page of active challenges (including marathon matches) from the backend. - * Once this action is completed any active challenges saved to the state before - * will be dropped, and the newly fetched ones will be stored there. - * Loading of all challenges wil start in background. - * @param {String} uuid - * @param {Number} page - * @param {Object} backendFilter Backend filter to use. - * @param {String} tokenV3 Optional. Topcoder auth token v3. Without token only - * public challenges will be fetched. With the token provided, the action will - * also fetch private challenges related to this user. - * @param {Object} frontFilter - - * @return {Promise} - */ -function getActiveChallengesDone(uuid, page, backendFilter, tokenV3, frontFilter = {}) { - const { sorts } = frontFilter; - const sortObj = SORT[sorts[BUCKETS.ONGOING]]; - const filter = { - backendFilter, - frontFilter: { - ...frontFilter, - status: 'Active', - currentPhaseName: 'Submission', - registrationEndDateEnd: new Date().toISOString(), - perPage: PAGE_SIZE, - page: page + 1, - sortBy: sortObj.field ? sortObj.field : sorts[BUCKETS.ONGOING], - sortOrder: sortObj.order, - }, - }; - delete filter.frontFilter.sorts; - const service = getService(tokenV3); - return service.getChallenges(filter).then(ch => ({ - uuid, - challenges: ch.challenges, - meta: ch.meta, - frontFilter, - })); - // const calls = [ - // service.getChallenges(filter, { - // perPage: PAGE_SIZE, - // page: page + 1, - // }), - // ]; - // let user; - // if (tokenV3) { - // user = decodeToken(tokenV3).userId; - - // // Handle any errors on this endpoint so that the non-user specific challenges - // // will still be loaded. - // calls.push(service.getUserChallenges(user, filter, {}) - // .catch(() => ({ challenges: [] }))); - // } - // return Promise.all(calls).then(([ch]) => ({ - // uuid, - // challenges: ch.challenges, - // meta: ch.meta, - // frontFilter, - // })); -} - -/** - * Gets open for registration challenges - * @param {String} uuid - * @param {Number} page - * @param {Object} backendFilter Backend filter to use. - * @param {String} tokenV3 Optional. Topcoder auth token v3. Without token only - * public challenges will be fetched. With the token provided, the action will - * also fetch private challenges related to this user. - * @param {Object} frontFilter - * @param {boolean} recommended recommended toggle is on or off - * @param {String} handle user handle - - * @return {Promise} - */ -function getOpenForRegistrationChallengesDone(uuid, page, backendFilter, - tokenV3, frontFilter = {}, recommended = false, handle) { - const { sorts } = frontFilter; - const sortObj = SORT[sorts[BUCKETS.OPEN_FOR_REGISTRATION]]; - const filter = { - backendFilter, - frontFilter: { - ...frontFilter, - status: 'Active', - currentPhaseName: 'Registration', - perPage: PAGE_SIZE, - page: page + 1, - sortBy: sortObj && sortObj.field ? sortObj.field : sorts[BUCKETS.OPEN_FOR_REGISTRATION], - sortOrder: sortObj ? sortObj.order : 'asc', - }, - }; - delete filter.frontFilter.sorts; - const service = getService(tokenV3); - if (recommended) { - return service.getRecommendedChallenges(filter, handle).then(ch => ({ - uuid, - openForRegistrationChallenges: ch.challenges, - meta: ch.meta, - frontFilter, - })); - } - - return service.getChallenges(filter).then(ch => ({ - uuid, - openForRegistrationChallenges: ch.challenges, - meta: ch.meta, - frontFilter, - })); -} - -function getMyChallengesDone(uuid, page, backendFilter, tokenV3, frontFilter = {}) { - const userId = decodeToken(tokenV3).userId.toString(); - const { sorts } = frontFilter; - const sortObj = SORT[sorts[BUCKETS.MY]]; - const filter = { - backendFilter, - frontFilter: { - ...frontFilter, - status: 'Active', - memberId: userId, - perPage: PAGE_SIZE, - page: page + 1, - sortBy: sortObj.field ? sortObj.field : sorts[BUCKETS.MY], - sortOrder: sortObj.order, - }, - }; - delete filter.frontFilter.sorts; - const service = getService(tokenV3); - return service.getChallenges(filter).then(ch => ({ - uuid, - myChallenges: ch.challenges, - meta: ch.meta, - frontFilter, - })); -} - -function getAllChallengesDone(uuid, page, backendFilter, tokenV3, frontFilter = {}) { - const { sorts } = frontFilter; - const sortObj = SORT[sorts[BUCKETS.ALL]]; - const filter = { - backendFilter, - frontFilter: { - ...frontFilter, - status: 'Active', - perPage: PAGE_SIZE, - page: page + 1, - sortBy: sortObj.field ? sortObj.field : sorts[BUCKETS.ALL], - sortOrder: sortObj.order, - }, - }; - delete filter.frontFilter.sorts; - const service = getService(tokenV3); - return service.getChallenges(filter).then(ch => ({ - uuid, - allChallenges: ch.challenges, - meta: ch.meta, - frontFilter, - })); -} - -function getMyPastChallengesDone(uuid, page, backendFilter, tokenV3, frontFilter = {}) { - const userId = decodeToken(tokenV3).userId.toString(); - const { sorts } = frontFilter; - const sortObj = SORT[sorts[BUCKETS.MY_PAST]]; - const filter = { - backendFilter, - frontFilter: { - ...frontFilter, - status: 'Completed', - memberId: userId, - perPage: PAGE_SIZE, - page: page + 1, - sortBy: sortObj.field ? sortObj.field : sorts[BUCKETS.MY_PAST], - sortOrder: sortObj.order, - }, - }; - delete filter.frontFilter.sorts; - const service = getService(tokenV3); - return service.getChallenges(filter).then(ch => ({ - uuid, - myPastChallenges: ch.challenges, - meta: ch.meta, - frontFilter, - })); -} - -function getTotalChallengesCountInit(uuid) { - return { uuid }; -} - -function getTotalChallengesCountDone(uuid, tokenV3, frontFilter = {}) { - const filter = { - backendFilter: {}, - frontFilter: { - ...frontFilter, - status: 'Active', - isLightweight: true, - perPage: 1, - }, - }; - delete filter.frontFilter.sorts; - const service = getService(tokenV3); - return service.getChallenges(filter).then(ch => ({ - uuid, - meta: ch.meta, - })); -} - -/** - * Init loading of all challenges - * @param {String} uuid - */ -// function getRestActiveChallengesInit(uuid) { -// return { uuid }; -// } - -/** - * Loading all challenges - * @param {String} uuid progress id - * @param {String} tokenV3 token v3 - */ -// function getRestActiveChallengesDone(uuid, tokenV3, filter) { -// const mergedFilter = { -// ...filter, -// status: 'Active', -// }; -// return getAllActiveChallengesWithUsersDone(uuid, tokenV3, mergedFilter, 1); -// } - -/** - * Prepare for getting all recommended challenges - * @param {String} uuid progress id - */ -// function getAllRecommendedChallengesInit(uuid) { -// return uuid; -// } -/** - * Get all recommended challenges - * @param {String} uuid progress id - * @param {String} tokenV3 token v3 - * @param {*} recommendedTags recommended tags - */ -// function getAllRecommendedChallengesDone(uuid, tokenV3, recommendedTags) { -// const filter = { -// status: 'Active', -// ...(!_.isEmpty(recommendedTags) && { tag: recommendedTags }), -// }; -// return getAllActiveChallengesWithUsersDone(uuid, tokenV3, filter); -// } - -/** - * Notifies the state that we are about to load the specified page of past - * challenges. - * @param {String} uuid - * @param {Number} page - * @param {Object} frontFilter - * @return {Object} - */ -function getPastChallengesInit(uuid, page, frontFilter) { - return { uuid, page, frontFilter }; -} - -/** - * Gets the specified page of past challenges (including MMs). - * @param {Number} page Page of challenges to fetch. - * @param {Object} filter Backend filter to use. - * @param {String} tokenV3 Optional. Topcoder auth token v3. - * @param {Object} frontFilter Optional. Original frontend filter. - * @param {Object} - */ -function getPastChallengesDone(uuid, page, backendFilter, tokenV3, frontFilter = {}) { - const { sorts } = frontFilter; - const sortObj = SORT[sorts[BUCKETS.ALL_PAST]]; - const filter = { - backendFilter, - frontFilter: { - ...frontFilter, - status: 'Completed', - perPage: PAGE_SIZE, - page: page + 1, - sortBy: sortObj.field ? sortObj.field : sorts[BUCKETS.ALL_PAST], - sortOrder: sortObj.order, - }, - }; - delete filter.frontFilter.sorts; - const service = getService(tokenV3); - return service.getChallenges(filter).then(ch => ({ - uuid, - pastChallenges: ch.challenges, - frontFilter, - meta: ch.meta, - })); -} - -/** - * Action to get a list of currently open Review Opportunities using V3 API - * @param {String} uuid Unique identifier for init/donen instance from shortid module - * @param {Number} page Page of review opportunities to fetch. - * @param {String} tokenV3 Optional. Topcoder auth token v3. - * @return {Object} Action object - */ -function getReviewOpportunitiesDone(uuid, page, tokenV3) { - return getReviewOpportunitiesService(tokenV3) - .getReviewOpportunities(REVIEW_OPPORTUNITY_PAGE_SIZE, page * REVIEW_OPPORTUNITY_PAGE_SIZE) - .then(loaded => ({ uuid, loaded })) - .catch((error) => { - fireErrorMessage('Error Getting Review Opportunities', error.content || error); - return Promise.reject(error); - }); -} - -/** - * Payload creator for the action that inits the loading of SRMs. - * @param {String} uuid - * @return {String} - */ -function getSrmsInit(uuid) { - return uuid; -} - -/** - * Payload creator for the action that loads SRMs. - * @param {String} uuid - * @param {String} handle - * @param {Object} params - * @param {String} tokenV3 - */ -function getSrmsDone(uuid, handle, params, tokenV3) { - const service = getService(tokenV3); - const promises = [service.getSrms(params)]; - if (handle) { - promises.push(service.getUserSrms(handle, params)); - } - return Promise.all(promises).then((data) => { - let srms = data[0]; - const userSrms = data[1]; - const userSrmsMap = {}; - _.forEach(userSrms, (srm) => { - userSrmsMap[srm.id] = srm; - }); - srms = _.map(srms, (srm) => { - if (userSrmsMap[srm.id]) { - return processSRM(srm); - } - return srm; - }); - return { uuid, data: srms }; - }); -} - -/** - * Payload creator for the action that initialize user registered challenges. - * @param {String} uuid - * @return {String} -// */ -// function getUserChallengesInit(uuid) { -// return { uuid }; -// } - -// /** -// * Payload creator for the action that loads user registered challenges. -// * @param {String} userId -// * @return {String} -// */ -// function getUserChallengesDone(userId, tokenV3) { -// const service = getService(tokenV3); - -// return service.getUserResources(userId) -// .then(item => item) -// .catch((error) => { -// fireErrorMessage('Error Getting User Challenges', error.content || error); -// return Promise.reject(error); -// }); -// } - -export default createActions({ - CHALLENGE_LISTING: { - DROP_CHALLENGES: _.noop, - DROP_ACTIVE_CHALLENGES: _.noop, - DROP_OPEN_FOR_REGISTRATION_CHALLENGES: _.noop, - DROP_MY_CHALLENGES: _.noop, - DROP_ALL_CHALLENGES: _.noop, - DROP_PAST_CHALLENGES: _.noop, - DROP_MY_PAST_CHALLENGES: _.noop, - DROP_RECOMMENDED_CHALLENGES: _.noop, - - // GET_ALL_ACTIVE_CHALLENGES_INIT: getAllActiveChallengesInit, - // GET_ALL_ACTIVE_CHALLENGES_DONE: getAllActiveChallengesDone, - - // GET_ALL_USER_CHALLENGES_INIT: getAllUserChallengesInit, - // GET_ALL_USER_CHALLENGES_DONE: getAllUserChallengesDone, - - // GET_ALL_RECOMMENDED_CHALLENGES_INIT: getAllRecommendedChallengesInit, - // GET_ALL_RECOMMENDED_CHALLENGES_DONE: getAllRecommendedChallengesDone, - - GET_ALL_CHALLENGES_INIT: getAllChallengesInit, - GET_ALL_CHALLENGES_DONE: getAllChallengesDone, - - GET_ACTIVE_CHALLENGES_INIT: getActiveChallengesInit, - GET_ACTIVE_CHALLENGES_DONE: getActiveChallengesDone, - - GET_OPEN_FOR_REGISTRATION_CHALLENGES_INIT: getOpenForRegistrationChallengesInit, - GET_OPEN_FOR_REGISTRATION_CHALLENGES_DONE: getOpenForRegistrationChallengesDone, - - GET_MY_CHALLENGES_INIT: getMyChallengesInit, - GET_MY_CHALLENGES_DONE: getMyChallengesDone, - - GET_MY_PAST_CHALLENGES_INIT: getMyPastChallengesInit, - GET_MY_PAST_CHALLENGES_DONE: getMyPastChallengesDone, - - // GET_REST_ACTIVE_CHALLENGES_INIT: getRestActiveChallengesInit, - // GET_REST_ACTIVE_CHALLENGES_DONE: getRestActiveChallengesDone, - - GET_CHALLENGE_TYPES_INIT: getChallengeTypesInit, - GET_CHALLENGE_TYPES_DONE: getChallengeTypesDone, - - GET_TOTAL_CHALLENGES_COUNT_INIT: getTotalChallengesCountInit, - GET_TOTAL_CHALLENGES_COUNT_DONE: getTotalChallengesCountDone, - - GET_CHALLENGE_TAGS_INIT: _.noop, - GET_CHALLENGE_TAGS_DONE: getChallengeTagsDone, - - GET_PAST_CHALLENGES_INIT: getPastChallengesInit, - GET_PAST_CHALLENGES_DONE: getPastChallengesDone, - - GET_REVIEW_OPPORTUNITIES_INIT: (uuid, page) => ({ uuid, page }), - GET_REVIEW_OPPORTUNITIES_DONE: getReviewOpportunitiesDone, - - GET_SRMS_INIT: getSrmsInit, - GET_SRMS_DONE: getSrmsDone, - - // GET_USER_CHALLENGES_INIT: getUserChallengesInit, - // GET_USER_CHALLENGES_DONE: getUserChallengesDone, - - EXPAND_TAG: id => id, - - /* Pass in community ID. */ - SELECT_COMMUNITY: _.identity, - - SET_FILTER: _.identity, - - SET_SORT: (bucket, sort) => ({ bucket, sort }), - }, -}); diff --git a/src/apps/earn/src/actions/challenge-listing/sidebar.js b/src/apps/earn/src/actions/challenge-listing/sidebar.js deleted file mode 100644 index cc7d6dbca..000000000 --- a/src/apps/earn/src/actions/challenge-listing/sidebar.js +++ /dev/null @@ -1,159 +0,0 @@ -/** - * Actions for the sidebar. - */ - - import _ from 'lodash'; - import { createActions } from 'redux-actions'; - // import { services } from 'topcoder-react-lib'; - - // const { getUserSettingsService } = services.userSetting; - - /** - * Changes name of the specified filter (but does not save it to the backend). - * @param {String} index - * @param {String} name - */ - // function changeFilterName(index, name) { - // return { index, name }; - // } - - /** - * Deletes saved filter. - * @param {String} id - * @param {Object} tokenV2 - * @return {Promise} - */ - // function deleteSavedFilter(id, tokenV2) { - // return getUserSettingsService(tokenV2) - // .deleteFilter(id).then(() => id); - // } - - /** - * Handles drag move event. - * @param {Object} dragEvent ReactJS onDrag event. - * @param {Object} dragState - * - * NOTE: This code is just taken from the previous version of the code. It has] - * some flaws, but it is not the main problem for now. - * - * NOTE: This implementation of dragging has a flaw: if you take an item and - * drug it down, you'll see that it is correctly moved down the list, but its - * highlighting (at least in Chrome) remains in the original position. Compare - * to the situation, when you drag an item upward the list: the highlighting - * moves properly with the item. This is related to the way ReactJS interacts - * with DOM, and, most probably, it is just easier to adopt some 3-rd party - * Drag-n-Drop library, then to find out a work-around. - */ - // function dragSavedFilterMove(dragEvent, dragState) { - /* For a reason not clear to me, shortly after starting to drag a filter, - * and also when the user releases the mouse button, thus ending the drag, - * this handler gets an event with 'screenY' position equal 0. This breaks - * the dragging handling, which works just fine otherwise. Hence, this simple - * fix of the issue, until the real problem is figured out. - */ - // if (!dragEvent.screenY) return dragState; - - // /* Calculation of the target position of the dragged item inside the filters - // * array. */ - // const shift = (dragEvent.screenY - dragState.y) / dragEvent.target.offsetHeight; - // const index = Math.round(dragState.startIndex + shift); - // if (index === dragState.index) return dragState; - // return { ...dragState, currentIndex: index }; - // } - - /** - * Initializes drag of a filter item. - * @param {Number} index - * @param {Object} dragEvent - * @return {Object} - */ - // function dragSavedFilterStart(index, dragEvent) { - // return { - // currentIndex: index, - // startIndex: index, - // y: dragEvent.screenY, - // }; - // } - - // function getSavedFilters(tokenV2) { - // return getUserSettingsService(tokenV2).getFilters(); - // } - - /** - * After changing filter name with changeFilterName(..) this action can be used - * to reset filter name to the one last saved into API. No API call is made, - * as the last saved name is kept inside the state. - * @param {String} index - */ - // function resetFilterName(index) { - // return index; - // } - - /** - * Saves filter to the backend. - * @param {String} name - * @param {Object} filter Filter state. - * @param {String} tokenV2 - * @return {Promise} - */ - // function saveFilter(name, filter, tokenV2) { - // return getUserSettingsService(tokenV2) - // .saveFilter(name, filter); - // } - - /** - * Updates all saved filters (basically to update their ordering in the - * backend). - * @param {Array} savedFilters - * @param {String} tokenV2 - */ - // function updateAllSavedFilters(savedFilters, tokenV2) { - // const service = getUserSettingsService(tokenV2); - // savedFilters.forEach(filter => service.updateFilter(filter.id, filter.name, filter.filter)); - // } - - /** - * Saves updated fitler to the backend. - * @param {Object} filter - * @param {String} tokenV2 - * @return {Promise} - */ - // function updateSavedFilter(filter, tokenV2) { - // return getUserSettingsService(tokenV2) - // .updateFilter(filter.id, filter.name, filter.filter); - // } - - export default createActions({ - CHALLENGE_LISTING: { - SIDEBAR: { - // CHANGE_FILTER_NAME: changeFilterName, - - // DELETE_SAVED_FILTER: deleteSavedFilter, - - // DRAG_SAVED_FILTER_MOVE: dragSavedFilterMove, - // DRAG_SAVED_FILTER_START: dragSavedFilterStart, - - // GET_SAVED_FILTERS: getSavedFilters, - - // RESET_FILTER_NAME: resetFilterName, - - // SAVE_FILTER_DONE: saveFilter, - - // SAVE_FILTER_INIT: _.noop, - - /* Pass in the bucket type. */ - SELECT_BUCKET: (bucket, expanding = false) => ({ bucket, expanding }), - SELECT_BUCKET_DONE: _.noop, - - /* Pass in the index of filter inside savedFilters array. */ - // SELECT_SAVED_FILTER: _.identity, - - /* Pass in true/false to enable/disable. */ - // SET_EDIT_SAVED_FILTERS_MODE: _.identity, - - // UPDATE_ALL_SAVED_FILTERS: updateAllSavedFilters, - // UPDATE_SAVED_FILTER: updateSavedFilter, - }, - }, - }); - \ No newline at end of file diff --git a/src/apps/earn/src/actions/challenge.js b/src/apps/earn/src/actions/challenge.js deleted file mode 100644 index bb0770780..000000000 --- a/src/apps/earn/src/actions/challenge.js +++ /dev/null @@ -1,442 +0,0 @@ -/** - * @module "actions.challenge" - * @desc Actions related to Topcoder challenges APIs. - */ - -import _ from "lodash"; -import config from "../config"; -import { createActions } from "redux-actions"; -import { decodeToken } from "../utils/token"; -import { getService as getChallengesService } from "../services/challenges"; -import { getService as getSubmissionService } from "../services/submissions"; -import { getApi } from "../services/challenge-api"; -import * as submissionUtil from "../utils/submission"; -import challenge from "../reducers/challenge"; - -const { PAGE_SIZE } = config; - -/** - * Private. Loads from the backend all data matching some conditions. - * @param {Function} getter Given params object of shape { limit, offset } - * loads from the backend at most "limit" data, skipping the first - * "offset" ones. Returns loaded data as an array. - * @param {Number} page Optional. Next page of data to load. - * @param {Number} perPage Optional. The size of the page content to load. - * @param {Array} prev Optional. data loaded so far. - */ -function getAll(getter, page = 1, perPage = PAGE_SIZE, prev) { - /* Amount of submissions to fetch in one API call. 50 is the current maximum - * amount of submissions the backend returns, event when the larger limit is - * explicitely required. */ - return getter({ - page, - perPage, - }).then((res) => { - if (res.length === 0) { - return prev || res; - } - // parse submissions - let current = []; - if (prev) { - current = prev.concat(res); - } else { - current = res; - } - return getAll(getter, 1 + page, perPage, current); - }); -} - -/** - * @static - * @desc Creates an action that drops from Redux store all checkpoints loaded - * before. - * @return {Action} - */ -function dropCheckpoints() {} - -/** - * @static - * @desc Creates an action that drops from Redux store all challenge results - * loaded before. - * @return {Action} - */ -function dropResults() {} - -/** - * @static - * @desc Creates an action that signals beginning of challenge details loading. - * @param {Number|String} challengeId Challenge ID - * @return {Action} - */ -function getDetailsInit(challengeId) { - return _.toString(challengeId); -} - -/** - * @static - * @desc Creates an action that loads challenge details. - * @param {Number|String} challengeId Challenge ID. - * @param {String} tokenV3 Topcoder v3 auth token. - * @param {String} tokenV2 Topcoder v2 auth token. - * @return {Action} - */ -function getDetailsDone({challengeId, tokenV3, tokenV2}) { - const service = getChallengesService(tokenV3, tokenV2); - const v3Promise = service.getChallengeDetails(challengeId); - return v3Promise; -} - -/** - * @static - * @desc Creates an action that signals beginning of user submissions loading. - * @param {String} challengeId Challenge ID. - * @return {Action} - */ -function getSubmissionsInit(challengeId) { - /* As a safeguard, we enforce challengeId to be string (in case somebody - * passes in a number, by mistake). */ - return _.toString(challengeId); -} - -/** - * @static - * @desc Creates an action that loads user's submissions to the specified - * challenge. - * @param {String} challengeId Challenge ID. - * @param {String} tokenV3 Topcoder auth token v3. - * @return {Action} - */ -function getSubmissionsDone({challengeId, tokenV3}) { - const user = decodeToken(tokenV3); - const submissionsService = getSubmissionService(tokenV3); - const filters = { - challengeId, - memberId: user.userId, - }; - return submissionsService.getSubmissions(filters) - .then(submissions => ({ - challengeId: _.toString(challengeId), - submissions, - })) - .catch((error) => { - const err = { challengeId: _.toString(challengeId), error }; - throw err; - }); -} - -/** - * @static - * @desc Creates an action that signals beginning of Marathon Match submissions loading. - * @param {String} challengeId Challenge ID. - * @return {Action} - */ -function getMMSubmissionsInit(challengeId) { - /* As a safeguard, we enforce challengeId to be string (in case somebody - * passes in a number, by mistake). */ - return _.toString(challengeId); -} - - -/** - * @static - * @desc Creates an action that loads Marathon Match submissions to the specified - * challenge. - * @param {String} challengeId Challenge ID. - * @param {Array} registrants The array of register. - * @param {String} tokenV3 Topcoder auth token v3. - * @return {Action} - */ -function getMMSubmissionsDone(challengeId, tokenV3) { - const filter = { challengeId }; - const submissionsService = getSubmissionService(tokenV3); - - // TODO: Move those numbers to configs - return getAll(params => submissionsService.getSubmissions(filter, params), 1, 500) - .then((submissions) => { - const finalSubmissions = submissionUtil.processMMSubmissions(submissions); - return { - challengeId, - submissions: finalSubmissions, - tokenV3, - }; - }); -} - -/** - * @static - * @desc Creates an action that signals beginning of registration for a - * challenge. - * @return {Action} - */ -function registerInit() { -} - -/** - * @static - * @desc Creates an action that registers user for a challenge. - * @param {Object} auth An object that holds auth tokens. You can directly pass - * here the `auth` segment of Redux store. - * @param [auth.tokenV2]{String} Topcoder auth token v2. - * @param [auth.tokenV3]{String} Topcoder auth token v3. - * @param {String} challengeId Challenge ID. - * @return {Action} - */ -function registerDone(auth, challengeId) { - return getChallengesService(auth.tokenV3) - .register(challengeId) - /* As a part of registration flow we silently update challenge details, - * reusing for this purpose the corresponding action handler. */ - // Uses a delay to allow API time to update - .then(() => new Promise( - resolve => setTimeout( - () => resolve(getDetailsDone({challengeId, tokenV3: auth.tokenV3, tokenV2: auth.tokenV2})), - config.CHALLENGE_DETAILS_REFRESH_DELAY, - ), - )); -} - -/** - * @static - * @desc Creates an action that signals beginning of user unregistration from a - * challenge. - * @return {Action} - */ -function unregisterInit() {} - -/** - * @static - * @desc Creates an action that unregisters user from a challenge. - * @param {Object} auth Object that holds Topcoder auth tokens. - * @param {String} [auth.tokenV2] v2 token. - * @param {String} [auth.tokenV3] v3 token. - * @param {String} challengeId Challenge ID. - * @return {Action} - */ -function unregisterDone(auth, challengeId) { - return getChallengesService(auth.tokenV3) - .unregister(challengeId) - /* As a part of unregistration flow we silently update challenge details, - * reusing for this purpose the corresponding action handler. */ - // Uses a delay to allow API time to update - .then(() => new Promise( - resolve => setTimeout( - () => resolve(getDetailsDone({challengeId, tokenV3: auth.tokenV3, tokenV2: auth.tokenV2})), - config.CHALLENGE_DETAILS_REFRESH_DELAY, - ), - )); -} - -/** - * @static - * @desc Creates an action that signals beginning of challenge results loading. - * @param {Number|String} challengeId Challenge ID - * @return {Action} - */ -function loadResultsInit(challengeId) { - return _.toString(challengeId); -} - -/** - * @static - * @desc Creates an action that loads challenge results. - * @param {Object} auth Object that holds Topcoder auth tokens. - * @param {String} [auth.tokenV2] v2 token. - * @param {String} [auth.tokenV3] v3 token. - * @param {Number|String} challengeId Challenge ID. Should match the one passed - * in the previous {@link module:actions.challenge.loadResultsInit} call. - * @param {String} type Challenge type. - * @return {Action} - */ -function loadResultsDone(auth, challengeId, type) { - return getApi('V2') - .fetch(`/${type}/challenges/result/${challengeId}`) - .then(response => response.json()) - .then(response => ({ - challengeId: _.toString(challengeId), - results: response.results, - })); -} - -/** - * @static - * @desc Creates an action that signals beginning of challenge checkpoints data - * loading. - * @return {Action} - */ -function fetchCheckpointsInit() {} - -/** - * @static - * @desc Creates an action that loads challenge checkpoints data. - * @param {String} tokenV2 Topcoder v2 auth token. - * @param {String} challengeId Challenge ID. - */ -function fetchCheckpointsDone(tokenV2, challengeId) { - const endpoint = `/design/challenges/checkpoint/${challengeId}`; - return getApi('V2').fetch(endpoint) - .then((response) => { - if (response.status !== 200) { - throw response.status; - } else { - return response.json(); - } - }) - .then((response) => { - // Expanded key is used for UI expand/collapse. - response.checkpointResults.forEach((checkpoint, index) => { - response.checkpointResults[index].expanded = false; - }); - return { - challengeId: String(challengeId), - checkpoints: response, - }; - }) - .catch(error => ({ - error, - challengeId: String(challengeId), - })); -} - -/** - * @static - * @desc Creates an action that Toggles checkpoint details panel in the Topcoder - * Submission Management Page. - * @todo This is UI action relevant to a specific page in specific app. Must be - * moved back to Community App. - * @param {Number} id Checkpoint ID. - * @param {Boolean} open Target state: `true` to expand, `false` to collapse the - * details. - * @return {Action} - */ -function toggleCheckpointFeedback(id, open) { - return { id, open }; -} - -/** - * @static - * @desc Creates an action that signals beginning of challenge details update. - * @todo No idea, why we have this action. This functionality should be covered - * by {@link module:actions.challenge.getDetailsInit} and - * {@link module:actions.challenge.getDetailsDone}. We need to refactor this. - * @param {String} uuid UUID of the operation (the same should be passed into - * the corresponding {@link module:actions.challenge.updateChallengeDone}). - * @return {Action} - */ -function updateChallengeInit(uuid) { - return uuid; -} - -/** - * @static - * @desc Creates an action that updates challenge details. - * @todo No idea, why we have this action. This functionality should be covered - * by {@link module:actions.challenge.getDetailsInit} and - * {@link module:actions.challenge.getDetailsDone}. We need to refactor this. - * @param {String} uuid Operation UUID. Should match the one passed into the - * previous {@link module:actions.challenge.updateChallengeInit} call. - * @param {Object} challenge Challenge data. - * @param {String} tokenV3 Topcoder v3 auth token. - * @return {Action} - */ -function updateChallengeDone(uuid, challenge, tokenV3) { - return getChallengesService(tokenV3).updateChallenge(challenge) - .then(res => ({ uuid, res })); -} - -/** - * @static - * @desc Creates an action that signals beginning of getting count of user's active challenges. - * @return {Action} - */ -function getActiveChallengesCountInit() {} - -/** - * @static - * @desc Creates an action that gets count of user's active challenges from the backend. - * @param {String} handle Topcoder user handle. - * @param {String} tokenV3 Optional. Topcoder auth token v3. Without token only - * public challenges will be counted. With the token provided, the action will - * also count private challenges related to this user. - * @return {Action} - */ -function getActiveChallengesCountDone(handle, tokenV3) { - return getChallengesService(tokenV3).getActiveChallengesCount(handle); -} - -/** - * @static - * @desc Creates an action that gets submission information by submission id - * @param {String} submissionId The submission id - * @return {Action} - */ -function getSubmissionInformationInit(challengeId, submissionId) { - return { challengeId: _.toString(challengeId), submissionId: _.toString(submissionId) }; -} - -/** - * @static - * @desc Creates an action that gets submission information from the backend. - * @param {String} submissionId The submission id - * @param {String} tokenV3 Topcoder auth token v3. - * @return {Action} - */ -function getSubmissionInformationDone(challengeId, submissionId, tokenV3) { - const filter = { challengeId }; - const submissionsService = getSubmissionService(tokenV3); - - return getAll(params => submissionsService.getSubmissions(filter, params), 1, 500) - .then((submissions) => { - const submission = _.find(submissions, { id: submissionId }); - _.remove(submission.review, review => review.typeId === config.AV_SCAN_SCORER_REVIEW_TYPE_ID); - return { submissionId, submission }; - }); -} - -/** - * @static - * @desc Creates an action that signals beginning of fetching challenge statistics - * @return {Action} - */ -function fetchChallengeStatisticsInit() {} - -/** - * @static - * @desc Creates an action that gets challenge statistics from the backend. - * @param {String} challengeId The challenge id - * @param {String} tokenV3 Topcoder auth token v3. - * @return {Action} - */ -function fetchChallengeStatisticsDone(challengeId, tokenV3) { - const challengeService = getChallengesService(tokenV3); - return challengeService.getChallengeStatistics(challengeId); -} - -export default createActions({ - CHALLENGE: { - DROP_CHECKPOINTS: dropCheckpoints, - DROP_RESULTS: dropResults, - FETCH_CHECKPOINTS_INIT: fetchCheckpointsInit, - FETCH_CHECKPOINTS_DONE: fetchCheckpointsDone, - GET_DETAILS_INIT: getDetailsInit, - GET_DETAILS_DONE: getDetailsDone, - GET_SUBMISSIONS_INIT: getSubmissionsInit, - GET_SUBMISSIONS_DONE: getSubmissionsDone, - LOAD_RESULTS_INIT: loadResultsInit, - LOAD_RESULTS_DONE: loadResultsDone, - REGISTER_INIT: registerInit, - REGISTER_DONE: registerDone, - TOGGLE_CHECKPOINT_FEEDBACK: toggleCheckpointFeedback, - UNREGISTER_INIT: unregisterInit, - UNREGISTER_DONE: unregisterDone, - UPDATE_CHALLENGE_INIT: updateChallengeInit, - UPDATE_CHALLENGE_DONE: updateChallengeDone, - GET_ACTIVE_CHALLENGES_COUNT_INIT: getActiveChallengesCountInit, - GET_ACTIVE_CHALLENGES_COUNT_DONE: getActiveChallengesCountDone, - GET_MM_SUBMISSIONS_INIT: getMMSubmissionsInit, - GET_MM_SUBMISSIONS_DONE: getMMSubmissionsDone, - GET_SUBMISSION_INFORMATION_INIT: getSubmissionInformationInit, - GET_SUBMISSION_INFORMATION_DONE: getSubmissionInformationDone, - FETCH_CHALLENGE_STATISTICS_INIT: fetchChallengeStatisticsInit, - FETCH_CHALLENGE_STATISTICS_DONE: fetchChallengeStatisticsDone, - }, -}); diff --git a/src/apps/earn/src/actions/challenges.js b/src/apps/earn/src/actions/challenges.js deleted file mode 100644 index 780839e74..000000000 --- a/src/apps/earn/src/actions/challenges.js +++ /dev/null @@ -1,140 +0,0 @@ -import { createActions } from "redux-actions"; -import _ from "lodash"; -import service from "../services/challenges"; -import * as util from "../utils/challenge"; -import * as constants from "../constants"; -import { decodeToken } from "tc-auth-lib"; -import { getAuthUserTokens } from "../utils/auth"; - -async function doGetChallenges(filter, cancellationSignal) { - return service.getChallenges(filter, cancellationSignal); -} - -async function getAllActiveChallenges(filter, signal) { - const allActiveFilter = { - ...util.createChallengeCriteria(filter), - ...util.createAllActiveChallengeCriteria(), - }; - return doGetChallenges(allActiveFilter, signal); -} - -async function getOpenForRegistrationChallenges(filter, signal) { - const openForRegistrationFilter = { - ...util.createChallengeCriteria(filter), - ...util.createOpenForRegistrationChallengeCriteria(), - }; - return doGetChallenges(openForRegistrationFilter, signal); -} - -async function getClosedChallenges(filter, signal) { - const closedFilter = { - ...util.createChallengeCriteria(filter), - ...util.createClosedChallengeCriteria(), - }; - return doGetChallenges(closedFilter, signal); -} - -async function getMyChallenges(filter, signal) { - const { tokenV3 } = await getAuthUserTokens(); - const user = decodeToken(tokenV3); - - const myFilter = { - ...util.createChallengeCriteria(filter), - ...util.createMyChallengeCriteria(user.userId), - }; - return doGetChallenges(myFilter, signal); -} - -async function getReviewOpportunities(filter, signal) { - const reviewOpportunities = await service.getReviewOpportunities(signal); - - //Convert from the reviewOpportunity into a challenge object for display in the UI - const challenges = reviewOpportunities.result.content.map((item) => { - let challenge={}; - challenge.id = item.id; - challenge.name = item.challenge.title; - challenge.legacy = {}; - challenge.legacy.id = item.challenge.id; - challenge.track = item.challenge.track; - challenge.overview = {}; - challenge.overview.totalPrizes = item.payments[0].payment; - challenge.reviewPayment = item.payments[0].payment; - challenge.type = "Contest Review"; - challenge.tags = item.challenge.technologies; - challenge.openPositions = item.openPositions; - challenge.numOfSubmissions = item.submissions; - - return challenge; - }) - return challenges; -} - -async function getOpenForRegistrationCount(filter, signal) { - const openForRegistrationCountCriteria = { - ...util.createChallengeCriteria(filter), - ...util.createOpenForRegistrationCountCriteria(), - }; - return doGetChallenges(openForRegistrationCountCriteria, signal); -} - -async function getChallenges(filter, signal) { - const ALL_ACTIVE_CHALLENGES_BUCKET = constants.FILTER_BUCKETS[0]; - const OPEN_FOR_REGISTRATION_BUCKET = constants.FILTER_BUCKETS[1]; - const CLOSED_CHALLENGES = constants.FILTER_BUCKETS[2]; - const MY_CHALLENGES = constants.FILTER_BUCKETS[3]; - const REVIEW_OPPORTUNITIES = constants.FILTER_BUCKETS[4]; - - let challenges; - let total; - let openForRegistrationCount; - - const getChallengesByBucket = async (f) => { - const promises = []; - switch (f.bucket) { - case ALL_ACTIVE_CHALLENGES_BUCKET: - promises.push(getAllActiveChallenges(f, signal)); - break; - case OPEN_FOR_REGISTRATION_BUCKET: - promises.push(getOpenForRegistrationChallenges(f, signal)); - break; - case CLOSED_CHALLENGES: - promises.push(getClosedChallenges(f, signal)); - break; - case MY_CHALLENGES: - promises.push(getMyChallenges(f, signal)); - break; - case REVIEW_OPPORTUNITIES: - promises.push(getReviewOpportunities(f, signal)); - break; - default: - return [util.createEmptyResult(), 0]; - } - promises.push(getOpenForRegistrationCount(f, signal)); - return Promise.all(promises).then((result) => [ - result[0], - result[1].meta.total, - ]); - }; - - if (!util.checkRequiredFilterAttributes(filter)) { - return { - challenges: [], - total: 0, - openForRegistrationCount: 0, - }; - } - - [challenges, openForRegistrationCount] = await getChallengesByBucket(filter); - if(challenges.meta) - total = challenges.meta.total; - else - total = 0 - - return { challenges, total, openForRegistrationCount }; -} - -export default createActions({ - GET_CHALLENGES_INIT: _.noop(), - GET_CHALLENGES_DONE: getChallenges, - GET_CHALLENGES_FAILURE: _.noop, -}); diff --git a/src/apps/earn/src/actions/errors.js b/src/apps/earn/src/actions/errors.js deleted file mode 100644 index f42857512..000000000 --- a/src/apps/earn/src/actions/errors.js +++ /dev/null @@ -1,22 +0,0 @@ -/** - * @module "actions.errors" - * @desc Actions related to the standard application-wide error handling and - * messaging. - * - * Pending to be documented. You are not supposed to use them directly anyway. - * - * @todo This module does not belong to `topcoder-react-lib`, it will be moved - * to `topcoder-react-utils` soon. - */ -import _ from "lodash"; -import { createActions } from "redux-actions"; - -export default createActions({ - ERRORS: { - CLEAR_ERROR: _.noop, - NEW_ERROR: (title, details) => ({ title, details }), - CLEAR_ALL_ERROR_ICONS: _.noop, - SET_ERROR_ICON: (id, title, message) => ({ id, title, message }), - CLEAR_ERROR_ICON: (id) => ({ id }), - }, -}); diff --git a/src/apps/earn/src/actions/filter.js b/src/apps/earn/src/actions/filter.js deleted file mode 100644 index 442e2be58..000000000 --- a/src/apps/earn/src/actions/filter.js +++ /dev/null @@ -1,34 +0,0 @@ -import { createActions } from "redux-actions"; -import * as utils from "../utils"; - -function updateFilter(partialUpdate) { - return partialUpdate; -} - -function clearChallengeFilter(defaultFilter) { - return defaultFilter; -} - -function updateChallengeQuery(filter) { - const params = utils.challenge.createChallengeParams(filter); - utils.url.updateQuery(params); - return params; -} - -function updateGigFilter(partialUpdate) { - return partialUpdate; -} - -function updateGigQuery(filter) { - const params = utils.myGig.createGigParams(filter); - utils.url.updateQuery(params); - return params; -} - -export default createActions({ - UPDATE_FILTER: updateFilter, - CLEAR_CHALLENGE_FILTER: clearChallengeFilter, - UPDATE_CHALLENGE_QUERY: updateChallengeQuery, - UPDATE_GIG_FILTER: updateGigFilter, - UPDATE_GIG_QUERY: updateGigQuery, -}); diff --git a/src/apps/earn/src/actions/gig-apply/creators.js b/src/apps/earn/src/actions/gig-apply/creators.js deleted file mode 100644 index d15e1b4a9..000000000 --- a/src/apps/earn/src/actions/gig-apply/creators.js +++ /dev/null @@ -1,42 +0,0 @@ -import { createActions } from "redux-actions"; -import * as ACTION_TYPE from "./types"; - -const actions = createActions( - {}, - ACTION_TYPE.INIT_PROFILE_DATA, - ACTION_TYPE.RESET_APPLICATION, - ACTION_TYPE.RESET_FORM, - ACTION_TYPE.SEND_APPLICATION_ERROR, - ACTION_TYPE.SEND_APPLICATION_PENDING, - ACTION_TYPE.SEND_APPLICATION_SUCCESS, - ACTION_TYPE.SET_AGREED_DURATION, - ACTION_TYPE.SET_AGREED_TERMS, - ACTION_TYPE.SET_AGREED_TIMEZONE, - ACTION_TYPE.SET_CITY, - ACTION_TYPE.SET_COUNTRY, - ACTION_TYPE.SET_PAYMENT, - ACTION_TYPE.SET_PHONE, - ACTION_TYPE.SET_REFERRAL, - ACTION_TYPE.SET_RESUME, - ACTION_TYPE.SET_SKILLS, - ACTION_TYPE.TOUCH_AGREED_DURATION, - ACTION_TYPE.TOUCH_AGREED_TERMS, - ACTION_TYPE.TOUCH_AGREED_TIMEZONE, - ACTION_TYPE.TOUCH_CITY, - ACTION_TYPE.TOUCH_PAYMENT, - ACTION_TYPE.TOUCH_PHONE, - ACTION_TYPE.TOUCH_RESUME, - ACTION_TYPE.TOUCH_SKILLS, - ACTION_TYPE.TOUCH_REFERRAL, - ACTION_TYPE.VALIDATE_CITY, - ACTION_TYPE.VALIDATE_COUNTRY, - ACTION_TYPE.VALIDATE_PAYMENT, - ACTION_TYPE.VALIDATE_PHONE, - ACTION_TYPE.VALIDATE_RESUME, - ACTION_TYPE.VALIDATE_SKILLS, - ACTION_TYPE.VALIDATE_REFERRAL, - ACTION_TYPE.VALIDATE_UNTOUCHED, - { prefix: "GIG-APPLY", namespace: "--" } -); - -export default actions; diff --git a/src/apps/earn/src/actions/gig-apply/effectors.js b/src/apps/earn/src/actions/gig-apply/effectors.js deleted file mode 100644 index 4e970bd1e..000000000 --- a/src/apps/earn/src/actions/gig-apply/effectors.js +++ /dev/null @@ -1,73 +0,0 @@ -import store from "../../store"; - -import * as applySelectors from "../../reducers/gig-apply/selectors"; -import * as detailsSelectors from "../../reducers/gig-details/selectors"; -import * as gigsSelectors from "../../reducers/gigs/selectors"; -import * as lookupSelectors from "../../reducers/lookupSelectors"; -import * as myGigsSelectors from "../../reducers/my-gigs/selectors"; -import * as detailsEffectors from "../../actions/gig-details/effectors"; -import * as applyServices from "../../services/gig-apply"; -import { composeApplication } from "../../utils/gig-apply"; -import lookupActions from "../lookup"; -import applyActions from "./creators"; - -/** - * Loads gig details and countries. Must be called after the user's profile - * has loaded. - */ -export const loadInitialData = async (externalId) => { - const { dispatch, getState } = store; - const promises = []; - let details = detailsSelectors.getDetails(getState()); - if (!details) { - promises.push(detailsEffectors.loadDetails(store, externalId)); - } - let countryByCode = lookupSelectors.getCountryByCode(getState()); - if (!countryByCode) { - promises.push(dispatch(lookupActions.lookup.getAllCountries())); - } - try { - await Promise.all(promises); - } catch (error) { - console.error(error); - } - const state = getState(); - const profile = myGigsSelectors.getProfile(state); - countryByCode = lookupSelectors.getCountryByCode(state); - const country = countryByCode[profile.country]?.name || ""; - const skillsByName = gigsSelectors.getSkillsByName(state); - const skillNames = profile.skill?.split(",") || []; - let skills = null; - for (let name of skillNames) { - let skill = skillsByName[name]; - if (skill) { - // initialize it with empty array as long as we have at least one skill - if (!skills) skills = []; - skills.push(skill); - } - } - dispatch(applyActions.initProfileData({ country, profile, skills })); -}; - -export const sendApplication = async () => { - const { dispatch, getState } = store; - const state = getState(); - dispatch(applyActions.validateUntouched()); - const isFormValid = applySelectors.getIsFormValid(state); - - if (!isFormValid) { - // stop the application by return directly - return; - } - const { jobExternalId } = detailsSelectors.getDetails(state); - const formData = composeApplication(state); - dispatch(applyActions.sendApplicationPending()); - let data = null; - try { - data = await applyServices.sendApplication(jobExternalId, formData); - } catch (error) { - dispatch(applyActions.sendApplicationError(error.toString())); - return; - } - dispatch(applyActions.sendApplicationSuccess(data)); -}; diff --git a/src/apps/earn/src/actions/gig-apply/types.js b/src/apps/earn/src/actions/gig-apply/types.js deleted file mode 100644 index fae9997b4..000000000 --- a/src/apps/earn/src/actions/gig-apply/types.js +++ /dev/null @@ -1,33 +0,0 @@ -export const INIT_PROFILE_DATA = "INIT_PROFILE_DATA"; -export const RESET_APPLICATION = "RESET_APPLICATION"; -export const RESET_FORM = "RESET_FORM"; -export const SEND_APPLICATION_ERROR = "SEND_APPLICATION_ERROR"; -export const SEND_APPLICATION_PENDING = "SEND_APPLICATION_PENDING"; -export const SEND_APPLICATION_SUCCESS = "SEND_APPLICATION_SUCCESS"; -export const SET_AGREED_DURATION = "SET_AGREED_DURATION"; -export const SET_AGREED_TERMS = "SET_AGREED_TERMS"; -export const SET_AGREED_TIMEZONE = "SET_AGREED_TIMEZONE"; -export const SET_CITY = "SET_CITY"; -export const SET_COUNTRY = "SET_COUNTRY"; -export const SET_PAYMENT = "SET_PAYMENT"; -export const SET_PHONE = "SET_PHONE"; -export const SET_REFERRAL = "SET_REFERRAL"; -export const SET_RESUME = "SET_RESUME"; -export const SET_SKILLS = "SET_SKILLS"; -export const TOUCH_AGREED_DURATION = "TOUCH_AGREED_DURATION"; -export const TOUCH_AGREED_TERMS = "TOUCH_AGREED_TERMS"; -export const TOUCH_AGREED_TIMEZONE = "TOUCH_AGREED_TIMEZONE"; -export const TOUCH_CITY = "TOUCH_CITY"; -export const TOUCH_PAYMENT = "TOUCH_PAYMENT"; -export const TOUCH_PHONE = "TOUCH_PHONE"; -export const TOUCH_RESUME = "TOUCH_RESUME"; -export const TOUCH_SKILLS = "TOUCH_SKILLS"; -export const TOUCH_REFERRAL = "TOUCH_REFERRAL"; -export const VALIDATE_CITY = "VALIDATE_CITY"; -export const VALIDATE_COUNTRY = "VALIDATE_COUNTRY"; -export const VALIDATE_PAYMENT = "VALIDATE_PAYMENT"; -export const VALIDATE_PHONE = "VALIDATE_PHONE"; -export const VALIDATE_RESUME = "VALIDATE_RESUME"; -export const VALIDATE_SKILLS = "VALIDATE_SKILLS"; -export const VALIDATE_REFERRAL = "VALIDATE_REFERRAL"; -export const VALIDATE_UNTOUCHED = "VALIDATE_UNTOUCHED"; diff --git a/src/apps/earn/src/actions/gig-details/creators.js b/src/apps/earn/src/actions/gig-details/creators.js deleted file mode 100644 index 8d2a96077..000000000 --- a/src/apps/earn/src/actions/gig-details/creators.js +++ /dev/null @@ -1,14 +0,0 @@ -import { createActions } from "redux-actions"; -import * as ACTION_TYPE from "./types"; - -const actions = createActions( - {}, - ACTION_TYPE.LOAD_DETAILS_ERROR, - ACTION_TYPE.LOAD_DETAILS_PENDING, - ACTION_TYPE.LOAD_DETAILS_SUCCESS, - ACTION_TYPE.RESET_DETAILS, - ACTION_TYPE.SET_CANCEL_RESET, - { prefix: "GIG-DETAILS", namespace: "--" } -); - -export default actions; diff --git a/src/apps/earn/src/actions/gig-details/effectors.js b/src/apps/earn/src/actions/gig-details/effectors.js deleted file mode 100644 index e3c3767b0..000000000 --- a/src/apps/earn/src/actions/gig-details/effectors.js +++ /dev/null @@ -1,54 +0,0 @@ -import { tokenGetAsync } from "~/libs/core"; - -import * as gigsSelectors from "../../reducers/gigs/selectors"; -import * as selectors from "../../reducers/gig-details/selectors"; -import * as services from "../../services/gig-details"; -import { loadSkills } from "../gigs/effectors"; -import { isAbort } from "../../utils/fetch"; - -import actions from "./creators"; - -export const loadDetails = async (store, externalId) => { - const { dispatch, getState } = store; - const skillsPromise = loadSkills(store); - const { token: tokenV3 } = await tokenGetAsync(); - - const [promise, controller] = services.fetchGig(externalId, tokenV3); - dispatch(actions.loadDetailsPending(controller)); - let details = null; - try { - details = await promise; - } catch (error) { - if (!isAbort(error)) { - dispatch(actions.loadDetailsError(error.toString())); - } - return; - } - try { - await skillsPromise; - } catch (error) { - // This should never be reachable but just in case. - console.error(error); - } - const skillsById = gigsSelectors.getSkillsById(getState()); - if (details.skills?.length && skillsById) { - const skills = []; - for (let id of details.skills) { - let skill = skillsById[id]; - if (skill) { - skills.push(skill); - } - } - details.skills = skills; - } - dispatch(actions.loadDetailsSuccess(details)); -}; - -export const resetDetails = ({ dispatch, getState }) => { - const { abortController, cancelReset } = selectors.getStateSlice(getState()); - if (cancelReset) { - return; - } - abortController?.abort(); - dispatch(actions.resetDetails()); -}; diff --git a/src/apps/earn/src/actions/gig-details/types.js b/src/apps/earn/src/actions/gig-details/types.js deleted file mode 100644 index 1cba85ae2..000000000 --- a/src/apps/earn/src/actions/gig-details/types.js +++ /dev/null @@ -1,5 +0,0 @@ -export const LOAD_DETAILS_ERROR = "LOAD_DETAILS_ERROR"; -export const LOAD_DETAILS_PENDING = "LOAD_DETAILS_PENDING"; -export const LOAD_DETAILS_SUCCESS = "LOAD_DETAILS_SUCCESS"; -export const RESET_DETAILS = "RESET_DETAILS"; -export const SET_CANCEL_RESET = "SET_CANCEL_RESET"; diff --git a/src/apps/earn/src/actions/gigs/creators.js b/src/apps/earn/src/actions/gigs/creators.js deleted file mode 100644 index e48071da6..000000000 --- a/src/apps/earn/src/actions/gigs/creators.js +++ /dev/null @@ -1,31 +0,0 @@ -import { createActions } from "redux-actions"; -import * as ACTION_TYPE from "./types"; - -const actions = createActions( - {}, - ACTION_TYPE.ADD_SKILL, - ACTION_TYPE.LOAD_GIGS_SPECIAL_ERROR, - ACTION_TYPE.LOAD_GIGS_SPECIAL_SUCCESS, - ACTION_TYPE.LOAD_PAGE_SUCCESS, - ACTION_TYPE.LOAD_PAGE_ERROR, - ACTION_TYPE.LOAD_PAGE_PENDING, - ACTION_TYPE.LOAD_SKILLS_ERROR, - ACTION_TYPE.LOAD_SKILLS_SUCCESS, - ACTION_TYPE.REMOVE_SKILL, - ACTION_TYPE.RESET_FILTERS, - ACTION_TYPE.SET_LOCATION, - ACTION_TYPE.SET_PAGE_NUMBER, - ACTION_TYPE.SET_PAGE_SIZE, - ACTION_TYPE.SET_PAYMENT_MAX, - ACTION_TYPE.SET_PAYMENT_MAX_VALUE, - ACTION_TYPE.SET_PAYMENT_MIN, - ACTION_TYPE.SET_PAYMENT_MIN_VALUE, - ACTION_TYPE.SET_SKILLS, - ACTION_TYPE.SET_SORTING, - ACTION_TYPE.SET_TITLE, - ACTION_TYPE.UPDATE_STATE_FROM_QUERY, - ACTION_TYPE.UPDATE_FILTERED_SPECIAL_GIGS, - { prefix: "GIGS", namespace: "--" } -); - -export default actions; diff --git a/src/apps/earn/src/actions/gigs/effectors.js b/src/apps/earn/src/actions/gigs/effectors.js deleted file mode 100644 index d30cb95c7..000000000 --- a/src/apps/earn/src/actions/gigs/effectors.js +++ /dev/null @@ -1,163 +0,0 @@ -import actions from "./creators"; -import * as selectors from "../../reducers/gigs/selectors"; -import * as services from "../../services/gigs"; -import lookupServices from "../../services/lookup"; -import { isAbort } from "../../utils/fetch"; -import { makeQueryFromState } from "../../reducers/gigs/urlQuery"; -import { sortByName } from "../../utils/misc"; - -/** - * Loads the specified gigs' page. If page number is not provided the current - * page number from current state is used. All relevant gigs' filters are loaded - * from the current state to construct the request query. - * - * @returns {Promise} - */ -export const loadGigsPage = async ({ dispatch, getState }) => { - const gigsState = selectors.getStateSlice(getState()); - // If there's an ongoing request we just cancel it since the data that comes - // with its response will not correspond to application's current state, - // namely filters and sorting. - gigsState.abortController?.abort(); - const { filters, sorting, pagination } = gigsState; - const { location, paymentMax, paymentMin, skills, title } = filters; - const { pageNumber, pageSize } = pagination; - const { sortBy, sortOrder } = sorting; - const [promise, abortController] = services.fetchGigs({ - location, - pageNumber, - pageSize, - paymentMax, - paymentMin, - skills, - sortBy, - sortOrder, - title, - featured: false, - }); - dispatch(actions.loadPagePending(abortController)); - let gigs, pageCount, totalCount; - try { - const { data, pagination } = await promise; - gigs = data; - pageCount = pagination.pageCount; - totalCount = pagination.totalCount; - } catch (error) { - // If request was cancelled by the next call to loadGigsPage - // there's nothing more to do. - if (!isAbort(error)) { - dispatch(actions.loadPageError(error.message)); - } - return; - } - dispatch(actions.loadPageSuccess({ gigs, pageCount, totalCount })); - dispatch(actions.updateFilteredSpecialGigs()); -}; - -/** - * Loads promo (hotlist) gigs. - * - * @param {Object} store redux store object - * @returns {Promise} - */ -export const loadGigsSpecial = async ({ dispatch }) => { - let gigsSpecial = null; - const [promise] = services.fetchGigs({ - pageNumber: 1, - special: true, - }); - try { - let { data } = await promise; - gigsSpecial = data; - } catch (error) { - dispatch(actions.loadGigsSpecialError(error.toString())); - console.error(error); - return; - } - dispatch(actions.loadGigsSpecialSuccess(gigsSpecial)); -}; - -/** - * Loads skills and special (featured + hotlist) gigs. - * - * @param {Object} store redux store object - */ -export const loadInitialData = async (store) => { - const { dispatch } = store; - let skillsPromise = loadSkills(store); - let gigsSpecial = null; - const [promise] = services.fetchGigs({ - pageNumber: 1, - special: true, - }); - try { - let { data } = await promise; - gigsSpecial = data; - } catch (error) { - dispatch(actions.loadGigsSpecialError(error.toString())); - console.error(error); - return; - } - // skills must be present in the store before we can process special gigs - await skillsPromise; - dispatch(actions.loadGigsSpecialSuccess(gigsSpecial)); -}; - -/** - * Loads all gigs' skills. - * - * @param {Object} store redux store - * @returns {Promise} - */ -export const loadSkills = async ({ dispatch, getState }) => { - const hasSkills = selectors.getHasSkills(getState()); - if (hasSkills) { - return; - } - const pageSize = 1e3; - let skills = null; - try { - const response = await lookupServices.getPaginatedSkills(1, pageSize); - const totalPages = +response.meta?.totalPages || 0; - const promises = [Promise.resolve(response)]; - for (let p = 2; p <= totalPages; p++) { - promises.push(lookupServices.getPaginatedSkills(p, pageSize)); - } - skills = (await Promise.all(promises)).flat().sort(sortByName); - } catch (error) { - dispatch(actions.loadSkillsError(error.toString())); - console.error(error); - return; - } - dispatch(actions.loadSkillsSuccess(skills)); -}; - -/** - * Updates state from current query (which may be empty) and then updates - * URL query by replacing URL in history. - * - * @param {Object} store redux store object - * @param {Object} options.mountLocation location object - */ -export const updateStateAndQuery = ({ dispatch, getState }, options) => { - const location = options ? options.mountLocation : window.location; - const isGigsLocation = location.pathname === window.location.pathname; - - dispatch(actions.updateStateFromQuery(location.search)); - const query = makeQueryFromState(selectors.getStateSlice(getState())); - - if (isGigsLocation) { - window.history.replaceState(null, "", `${location.pathname}?${query}`); - } -}; - -/** - * Updates URL query from current state by pushing new URL into history. - * - * @param {Object} store redux store object - */ -export const updateUrlQuery = ({ getState }) => { - const location = window.location; - const query = makeQueryFromState(selectors.getStateSlice(getState())); - window.history.pushState(null, "", `${location.pathname}?${query}`); -}; diff --git a/src/apps/earn/src/actions/gigs/types.js b/src/apps/earn/src/actions/gigs/types.js deleted file mode 100644 index 401ba029b..000000000 --- a/src/apps/earn/src/actions/gigs/types.js +++ /dev/null @@ -1,22 +0,0 @@ -export const ADD_SKILL = "ADD_SKILL"; -export const LOAD_GIGS_SPECIAL_ERROR = "LOAD_GIGS_SPECIAL_ERROR"; -export const LOAD_GIGS_SPECIAL_SUCCESS = "LOAD_GIGS_SPECIAL_SUCCESS"; -export const LOAD_PAGE_ERROR = "LOAD_PAGE_ERROR"; -export const LOAD_PAGE_PENDING = "LOAD_PAGE_PENDING"; -export const LOAD_PAGE_SUCCESS = "LOAD_PAGE_SUCCESS"; -export const LOAD_SKILLS_ERROR = "LOAD_SKILLS_ERROR"; -export const LOAD_SKILLS_SUCCESS = "LOAD_SKILLS_SUCCESS"; -export const REMOVE_SKILL = "REMOVE_SKILL"; -export const RESET_FILTERS = "RESET_FILTERS"; -export const SET_LOCATION = "SET_LOCATION"; -export const SET_PAGE_NUMBER = "SET_PAGE_NUMBER"; -export const SET_PAGE_SIZE = "SET_PAGE_SIZE"; -export const SET_PAYMENT_MAX = "SET_PAYMENT_MAX"; -export const SET_PAYMENT_MAX_VALUE = "SET_PAYMENT_MAX_VALUE"; -export const SET_PAYMENT_MIN = "SET_PAYMENT_MIN"; -export const SET_PAYMENT_MIN_VALUE = "SET_PAYMENT_MIN_VALUE"; -export const SET_SKILLS = "SET_SKILLS"; -export const SET_SORTING = "SET_SORTING"; -export const SET_TITLE = "SET_TITLE"; -export const UPDATE_STATE_FROM_QUERY = "UPDATE_STATE_FROM_QUERY"; -export const UPDATE_FILTERED_SPECIAL_GIGS = "UPDATE_FILTERED_SPECIAL_GIGS"; diff --git a/src/apps/earn/src/actions/index.js b/src/apps/earn/src/actions/index.js deleted file mode 100644 index 4b9e943ef..000000000 --- a/src/apps/earn/src/actions/index.js +++ /dev/null @@ -1,29 +0,0 @@ -import auth from "./auth"; -import challenge from "./challenge.js"; -import challengeListing from "./challenge-listing"; -import challenges from "./challenges"; -import errors from "./errors"; -import filter from "./filter"; -import lookup from "./lookup"; -import myGigs from "./my-gigs"; -import page from "./page/challenge-details"; -import submission from "./submission"; -import submissionManagement from "./submissionManagement"; -import smp from "./smp"; - -export const actions = { - challenges, - filter, - myGigs, - submission, - submissionManagement, - ...smp, - ...challenge, - ...challengeListing, - ...auth, - ...lookup, - ...page, - ...errors, -}; - -export default actions; diff --git a/src/apps/earn/src/actions/lookup.js b/src/apps/earn/src/actions/lookup.js deleted file mode 100644 index e1ba99ff6..000000000 --- a/src/apps/earn/src/actions/lookup.js +++ /dev/null @@ -1,199 +0,0 @@ -/** - * @module "actions.lookup" - * @desc Actions related to lookup data. - */ - import { createActions } from "redux-actions"; - import service, { getService } from "../services/lookup"; - - async function getTags() { - return service.getTags(); - } - - async function getCommunityList() { - return service.getCommunityList(); - } - /* - * device api PAGE_SIZE - */ - export const PAGE_SIZE = 100; - - /** - * @static - * @desc Creates an action that signals beginning of getting all deveice types - * @return {Action} - */ - function getTypesInit() {} - - /** - * @static - * @desc Creates an action that get all deveice types - * @return {Action} - */ - function getTypesDone() { - return getService().getTypes(); - } - - /** - * @static - * @desc Creates an action that signals beginning of getting all manufacturers - * @return {Action} - */ - function getManufacturersInit() {} - - /** - * @static - * @desc Creates an action that get all deveice manufacturers - * @param {String} type - * @return {Action} - */ - function getManufacturersDone(type) { - return getService().getManufacturers(type); - } - - /** - * @static - * @desc Creates an action that signals beginning of getting models - * @param {Number} page - * @return {Action} - */ - function getModelsInit(page) { - return { - page, - }; - } - - /** - * @static - * @desc Creates an action that get all deveice models - * @param {Number} page - * @param {String} manufacturer - * @param {String} type - * @return {Action} - */ - function getModelsDone(page, type, manufacturer) { - return getService().getDevices(page, PAGE_SIZE, type, manufacturer); - } - - /** - * @static - * @desc Creates an action that signals beginning of getting operation systems - * - * @param {Number} page - * @return {Action} - */ - function getOsesInit(page) { - return { - page, - }; - } - - /** - * @static - * @desc Creates an action that get all operation systems - * @param {Number} page - * @param {String} manufacturer - * @param {String} type - * @param {String} model - * @return {Action} - */ - function getOsesDone(page, type, manufacturer, model) { - return getService().getDevices(page, PAGE_SIZE, type, manufacturer, model); - } - - /** - * @static - * @desc Creates an action that signals beginning of getting all skill tags. - * @return {Action} - */ - function getSkillTagsInit() {} - - /** - * @static - * @desc Creates an action that gets all skill tags. - * @return {Action} - */ - function getSkillTagsDone() { - const params = { - filter: { - domain: "SKILLS", - status: "APPROVED", - }, - limit: { - limit: 1000, - }, - }; - return getService().getTags(params); - } - - /** - * @static - * @desc Creates an action that signals beginning of getting all countries. - * @return {Action} - */ - function getCountriesInit() {} - - /** - * @static - * @desc Creates an action that gets all countries. - * @return {Action} - */ - function getCountriesDone() { - return getService().getCountries(); - } - - /** - * @static - * @desc Creates an action that signals beginning of getting all review types. - * @return {Action} - */ - function getReviewTypesInit() {} - - /** - * @static - * @desc Creates an action that gets all review types. - * @param {String} tokenV3 Optional. Auth token for Topcoder API v3. - * @return {Action} - */ - function getReviewTypesDone(tokenV3) { - return getService(tokenV3).getReviewTypes(); - } - - /** - * @static - * @desc Creates an action that signals beginning of getting all countries api version 5. - * @return {Action} - */ - function getAllCountriesInit() {} - - /** - * @static - * @desc Creates an action that gets all countries api version 5. - * @param {String} tokenV3 Optional. Auth token for Topcoder API v3. - * @return {Action} - */ - function getAllCountries(tokenV3) { - return getService(tokenV3).getAllCountries(); - } - - export default createActions({ - LOOKUP: { - GET_TAGS_DONE: getTags, - GET_COMMUNITY_LIST_DONE: getCommunityList, - GET_TYPES_INIT: getTypesInit, - GET_TYPES_DONE: getTypesDone, - GET_MANUFACTURERS_INIT: getManufacturersInit, - GET_MANUFACTURERS_DONE: getManufacturersDone, - GET_MODELS_INIT: getModelsInit, - GET_MODELS_DONE: getModelsDone, - GET_OSES_INIT: getOsesInit, - GET_OSES_DONE: getOsesDone, - GET_SKILL_TAGS_INIT: getSkillTagsInit, - GET_SKILL_TAGS_DONE: getSkillTagsDone, - GET_COUNTRIES_INIT: getCountriesInit, - GET_COUNTRIES_DONE: getCountriesDone, - GET_REVIEW_TYPES_INIT: getReviewTypesInit, - GET_REVIEW_TYPES_DONE: getReviewTypesDone, - GET_ALL_COUNTRIES_INIT: getAllCountriesInit, - GET_ALL_COUNTRIES: getAllCountries, - }, - }); diff --git a/src/apps/earn/src/actions/my-gigs.js b/src/apps/earn/src/actions/my-gigs.js deleted file mode 100644 index 661cfd527..000000000 --- a/src/apps/earn/src/actions/my-gigs.js +++ /dev/null @@ -1,78 +0,0 @@ -import { createActions } from "redux-actions"; -import _ from "lodash"; -import { - PER_PAGE, - CHECKING_GIG_TIMES, - DELAY_CHECK_GIG_TIME, -} from "../constants"; -import service from "../services/my-gigs"; - -async function getMyActiveGigs( - status = "active_jobs", - page = 1, - perPage = PER_PAGE -) { - return service.getMyGigs(status, page, perPage); -} - -async function getMyOpenGigs( - status = "open_jobs", - page = 1, - perPage = PER_PAGE -) { - return service.getMyGigs(status, page, perPage); -} - -async function getMyCompletedGigs( - status = "completed_jobs", - page = 1, - perPage = PER_PAGE -) { - return service.getMyGigs(status, page, perPage); -} - -async function getMyArchivedGigsDone( - status = "archived_jobs", - page = 1, - perPage = PER_PAGE -) { - return service.getMyGigs(status, page, perPage); -} - -async function getProfile() { - return service.getProfile(); -} - -async function updateProfile(profile) { - return service.updateProfile(profile); -} - -async function startCheckingGigs(externalId) { - let i = 0; - while (i < CHECKING_GIG_TIMES) { - const res = await service.startCheckingGigs(externalId); - if (res && !res.synced) { - await new Promise((resolve) => { - setTimeout(() => { - resolve(); - }, DELAY_CHECK_GIG_TIME); - }); - i++; - continue; - } else { - return {}; - } - } - return {}; -} - -export default createActions({ - GET_MY_ACTIVE_GIGS: getMyActiveGigs, - GET_MY_OPEN_GIGS: getMyOpenGigs, - GET_MY_COMPLETED_GIGS: getMyCompletedGigs, - GET_MY_ARCHIVED_GIGS: getMyArchivedGigsDone, - GET_PROFILE: getProfile, - UPDATE_PROFILE: updateProfile, - UPDATE_PROFILE_RESET: _.noop, - START_CHECKING_GIGS: startCheckingGigs, -}); diff --git a/src/apps/earn/src/actions/page/challenge-details.js b/src/apps/earn/src/actions/page/challenge-details.js deleted file mode 100644 index 3bf4ce082..000000000 --- a/src/apps/earn/src/actions/page/challenge-details.js +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Actions related to the UI state of challenge details page. - */ - import _ from 'lodash'; - import { createActions } from 'redux-actions'; - - /** - * String values of valid tab names. - */ - export const TABS = { - DETAILS: 'details', - REGISTRANTS: 'registrants', - CHECKPOINTS: 'checkpoints', - SUBMISSIONS: 'submissions', - MY_SUBMISSIONS: 'my submissions', - WINNERS: 'winners', - CHALLENGE_FORUM: 'challenge_forum', - MM_DASHBOARD: 'dashboard', - }; - - /* Holds valid values for the specs tab state. */ - export const SPECS_TAB_STATES = { - EDIT: 'EDIT', - VIEW: 'VIEW', - SAVING: 'SAVING', - }; - - /** - * Creates action that switches the page to the specified content tab. - * @param {String} tab One of `TAB` values. - * @return {Action} - */ - function selectTab(tab) { - return tab; - } - - /** - * Creates action that toggle the submission history. - * @param {Number} index of subbmission history. - * @return {Action} - */ - function toggleSubmissionHistory(index) { - return index; - } - - /** - * Sets the state of specs tab. - * @param {String} state One of SPECS_TAB_STATES keys. - * @return {String} - */ - function setSpecsTabState(state) { - return state; - } - - /** - * Toggles checkpoint feedback. If second argument is provided, it - * will just open / close the checkpoint depending on its value being - * true or false. - * @param {Number} id - * @param {Boolean} open - * @return {Object} - */ - function toggleCheckpointFeedback(id, open = false) { - return { id, open }; - } - - /** - * Creates action that toggle the submission testcase.. - * @param {Number} index of submission testcase. - * @return {Action} - */ - function toggleSubmissionTestCase(index) { - return index; - } - - export default createActions({ - PAGE: { - CHALLENGE_DETAILS: { - SELECT_TAB: selectTab, - SET_SPECS_TAB_STATE: setSpecsTabState, - TOGGLE_CHECKPOINT_FEEDBACK: toggleCheckpointFeedback, - SUBMISSIONS: { - TOGGLE_SUBMISSION_HISTORY: toggleSubmissionHistory, - TOGGLE_SUBMISSION_TESTCASE: toggleSubmissionTestCase, - CLEAR_SUBMISSION_TESTCASE_OPEN: _.identity, - }, - }, - }, - }); - \ No newline at end of file diff --git a/src/apps/earn/src/actions/page/index.js b/src/apps/earn/src/actions/page/index.js deleted file mode 100644 index f72a18973..000000000 --- a/src/apps/earn/src/actions/page/index.js +++ /dev/null @@ -1,5 +0,0 @@ -import _ from "lodash"; -import challengeDetails from "./challenge-details"; -import submission_management from "./submission_management.js"; - -export default _.merge({}, challengeDetails, submission_management); diff --git a/src/apps/earn/src/actions/page/submission.js b/src/apps/earn/src/actions/page/submission.js deleted file mode 100644 index f2a8f1f68..000000000 --- a/src/apps/earn/src/actions/page/submission.js +++ /dev/null @@ -1,57 +0,0 @@ -/** - * actions.page.challenge-details.submission - * - * Description: - * Contains the Redux Actions for updating the Submission page UI - * and for for uploading submissions to back end - */ -import _ from 'lodash'; -import { createActions } from 'redux-actions'; -import config from '@earn/config'; -import { getApi } from "@earn/services/lib/api"; - -/** - * Payload creator for the action that actually performs submission operation. - * @param {String} tokenV3 - * @param {String} tokenV2 - * @param {String} submissionId - * @param {Object} body Data to submit. - * @param {String} track Competition track of the challenge where we submit. - * @param {Function} progress The callback to trigger with updates on the - * submission progress. - * @return Promise - */ -function submitDone(tokenV3, tokenV2, submissionId, body, track, progress) { - const api = getApi('V5', tokenV3); - const url = '/submissions/'; - return api.upload(url, { - body, - method: 'POST', - }, progress).then((res) => { - const jres = JSON.parse(res); - return jres; - }, (err) => { - throw err; - }); -} - -/** - * Export Actions for usage by Redux - */ -export default createActions({ - PAGE: { - SUBMISSION: { - SUBMIT_INIT: _.noop, - SUBMIT_DONE: submitDone, - SUBMIT_RESET: _.noop, - UPLOAD_PROGRESS: percent => percent, - SET_AGREED: agreed => agreed, - SET_FILE_PICKER_ERROR: (id, error) => ({ id, error }), - SET_FILE_PICKER_FILE_NAME: (id, fileName) => ({ id, fileName }), - SET_FILE_PICKER_UPLOAD_PROGRESS: (id, progress) => ({ id, progress }), - SET_FILE_PICKER_DRAGGED: (id, dragged) => ({ id, dragged }), - UPDATE_NOTES_LENGTH: length => length, - SET_SUBMISSION_FILESTACK_DATA: data => data, - }, - }, -}); diff --git a/src/apps/earn/src/actions/page/submission_management.js b/src/apps/earn/src/actions/page/submission_management.js deleted file mode 100644 index 08bc034d3..000000000 --- a/src/apps/earn/src/actions/page/submission_management.js +++ /dev/null @@ -1,12 +0,0 @@ -import _ from 'lodash'; -import { createActions } from 'redux-actions'; - -export default createActions({ - PAGE: { - SUBMISSION_MANAGEMENT: { - SHOW_DETAILS: _.identity, - CANCEL_DELETE: _.noop, - CONFIRM_DELETE: _.identity, - }, - }, -}); diff --git a/src/apps/earn/src/actions/smp.js b/src/apps/earn/src/actions/smp.js deleted file mode 100644 index ff3cded55..000000000 --- a/src/apps/earn/src/actions/smp.js +++ /dev/null @@ -1,50 +0,0 @@ -/** - * @module "actions.smp" - * @desc Actions related to *My Submissions Management* page. - */ - -import _ from "lodash"; -import { createActions } from "redux-actions"; -import { getApi } from "../services/challenge-api"; - -/** - * @static - * @desc Creates an action that signals beginning of submission download. - * @return {Action} - */ -function deleteSubmissionInit() {} - -/** - * @static - * @desc Creates an action that deletes user's submission to a challenge. - * @param {String} tokenV3 Topcoder v3 auth token. - * @param {Number|String} submissionId Submission ID. - * @return {Action} - */ -function deleteSubmissionDone(tokenV3, submissionId) { - return getApi("V5", tokenV3) - .delete(`/submissions/${submissionId}`) - .then(() => submissionId); -} - -/** - * @static - * @todo At this moment we don't need any special JS code to download - * submissions: we get them from legacy Topcoder Studio API, which is - * authenticated by cookies, and can be done with a simple link in - * the component. Soon we'll migrate to use the new TC API instead, and - * then we'll decide, whether we need operate downloads in JS, or can we - * just remove this action. - * @return {Action} - */ -function downloadSubmission(tokens, type, submissionId) { - _.noop(tokens, type, submissionId); -} - -export default createActions({ - SMP: { - DELETE_SUBMISSION_DONE: deleteSubmissionDone, - DELETE_SUBMISSION_INIT: deleteSubmissionInit, - DOWNLOAD_SUBMISSION: downloadSubmission, - }, -}); diff --git a/src/apps/earn/src/actions/submission.js b/src/apps/earn/src/actions/submission.js deleted file mode 100644 index 9a67f8763..000000000 --- a/src/apps/earn/src/actions/submission.js +++ /dev/null @@ -1,36 +0,0 @@ -/** - * actions.page.challenge-details.submission - * - * Description: - * Contains the Redux Actions for updating the Submission page UI - * and for for uploading submissions to back end - */ -import _ from "lodash"; -import { createActions } from "redux-actions"; -import service from "../services/submission"; - -/** - * Payload creator for the action that actually performs submission operation. - * @param {Object} data Data to submit. - * @param {Function} onProgress The callback to trigger with updates on the - * submission progress. - * @return Promise - */ -function submitDone(data, onProgress) { - return service.submit(data, onProgress); -} - -export default createActions({ - SUBMIT: { - SUBMIT_INIT: _.noop, - SUBMIT_DONE: submitDone, - SUBMIT_RESET: _.noop, - UPLOAD_PROGRESS: (percent) => percent, - SET_AGREED: (agreed) => agreed, - SET_FILE_PICKER_ERROR: (id, error) => ({ id, error }), - SET_FILE_PICKER_FILE_NAME: (id, fileName) => ({ id, fileName }), - SET_FILE_PICKER_UPLOAD_PROGRESS: (id, progress) => ({ id, progress }), - SET_FILE_PICKER_DRAGGED: (id, dragged) => ({ id, dragged }), - SET_SUBMISSION_FILESTACK_DATA: (data) => data, - }, -}); diff --git a/src/apps/earn/src/actions/submissionManagement.js b/src/apps/earn/src/actions/submissionManagement.js deleted file mode 100644 index 3d541f142..000000000 --- a/src/apps/earn/src/actions/submissionManagement.js +++ /dev/null @@ -1,42 +0,0 @@ -import _ from "lodash"; -import { createActions } from "redux-actions"; -import service from "../services/submission"; -import { decodeToken } from "tc-auth-lib"; -import { triggerDownload } from "../utils"; -import { getAuthUserTokens } from "../utils/auth"; - -function deleteSubmissionDone(submissionId) { - return service.deleteSubmission(submissionId); -} - -async function downloadSubmissionDone(track, submissionId) { - const blob = await service.downloadSubmission(track, submissionId); - triggerDownload( - `submission-${track.toLowerCase()}-${submissionId}.zip`, - blob - ); -} - -async function getMySubmissionsDone(challengeId) { - const { tokenV3 } = await getAuthUserTokens(); - const user = decodeToken(tokenV3); - const filters = { - challengeId, - memberId: user.userId, - }; - - return service.getSubmissions(filters); -} - -export default createActions({ - MY_SUBMISSIONS: { - SHOW_DETAILS: _.identity, - CANCEL_DELETE: _.noop, - CONFIRM_DELETE: _.identity, - DELETE_SUBMISSION_INIT: _.noop, - DELETE_SUBMISSION_DONE: deleteSubmissionDone, - DOWNLOAD_SUBMISSION_DONE: downloadSubmissionDone, - GET_MY_SUBMISSIONS_INIT: _.noop, - GET_MY_SUBMISSIONS_DONE: getMySubmissionsDone, - }, -}); diff --git a/src/apps/earn/src/actions/submission_management.js b/src/apps/earn/src/actions/submission_management.js deleted file mode 100644 index f114d67f1..000000000 --- a/src/apps/earn/src/actions/submission_management.js +++ /dev/null @@ -1,10 +0,0 @@ -import _ from 'lodash'; -import { createActions } from 'redux-actions'; - -export default createActions({ - SUBMISSION_MANAGEMENT: { - SHOW_DETAILS: _.identity, - CANCEL_DELETE: _.noop, - CONFIRM_DELETE: _.identity, - }, -}); diff --git a/src/apps/earn/src/actions/terms.js b/src/apps/earn/src/actions/terms.js deleted file mode 100644 index 181276db3..000000000 --- a/src/apps/earn/src/actions/terms.js +++ /dev/null @@ -1,251 +0,0 @@ -/** - * Terms specific actions. - */ - -import _ from "lodash"; -import { createActions } from "redux-actions"; -import { getService } from "../services/terms"; -import config from "../config"; - -/** - * Payload creator for TERMS/GET_TERMS_DONE action, - * which fetch terms of the specified entity. - * @param {Object} entity entity object - * @param {String} entity.type entity type ['challenge'||'community'] - * @param {String} entity.id entity id - * @param {Object} tokens object with tokenV2 and tokenV3 properties - * @param {Boolean} mockAgreed if true, then all terms will be mocked as agreed - * this only makes effect if MOCK_TERMS_SERVICE is true - * and the only purpose of this param is testing terms - * @return {Promise} - */ -function getTermsDone(entity, tokens, mockAgreed) { - const service = getService(tokens.tokenV3); - let termsPromise; - - // if mockAgreed=true passed, then we create an array of 10 true which we pass to the - // terms service methods. - // when terms service is mocked by setting MOCK_TERMS_SERVICE=true - // it will make all terms to have agreed status (actually only first 10 will be agreed, - // but we will hardly have even more then 3 terms per entity) - const mockAgreedArray = mockAgreed - ? Array(10 + 1) - .join("1") - .split("") - .map(() => true) - : []; - - switch (entity.type) { - case "challenge": { - termsPromise = service.getChallengeTerms(entity.terms, mockAgreedArray); - break; - } - case "community": { - termsPromise = service.getCommunityTerms( - entity.id, - tokens.tokenV3, - mockAgreedArray - ); - break; - } - case "reviewOpportunity": { - termsPromise = service.getReviewOpportunityTerms( - entity.reviewOpportunityTerms - ); - break; - } - default: - throw new Error( - `Entity type '${entity.type}' is not supported by getTermsDone.` - ); - } - - return termsPromise.then((res) => ({ entity, terms: res })); -} - -/** - * Payload creator for TERMS/GET_TERM_DETAILS_INIT action, - * which marks that we are about to fetch details of the specified term. - * If any details for another term are currently being fetched, - * they will be silently discarded. - * @param {Number|String} termId - * @return {String} - */ -function getTermDetailsInit(termId) { - return _.toString(termId); -} - -/** - * Payload creator for TERMS/GET_TERM_DETAILS_DONE action, - * which fetch details of the specified term. - * @param {Number|String} termId - * @param {String} tokenV3 - * @return {Promise} - */ -function getTermDetailsDone(termId, tokenV3) { - const service = getService(tokenV3); - return service - .getTermDetails(termId) - .then((details) => ({ termId, details })); -} - -/** - * Payload creator for TERMS/GET_DOCU_SIGN_URL_INIT - * @param {Number|String} templateId id of document template to sign - * @return {String} string format of the id - */ -function getDocuSignUrlInit(templateId) { - return _.toString(templateId); -} - -/** - * Payload creator for TERMS/GET_DOCU_SIGN_URL_DONE - * which generate the url of DoduSign term - * @param {Number|String} templateId id of document template to sign - * @param {String} returnUrl callback url after finishing singing - * @param {String} tokenV3 auth token - * @return {Promise} promise of request result - */ -function getDocuSignUrlDone(templateId, returnUrl, tokenV3) { - const service = getService(tokenV3); - return service - .getDocuSignUrl(templateId, returnUrl) - .then((resp) => ({ templateId, docuSignUrl: resp.recipientViewUrl })); -} - -/** - * Payload creator for TERMS/AGREE_TERM_INIT - * @param {Number|String} termId id of term - * @return {String} string format of the id - */ -function agreeTermInit(termId) { - return _.toString(termId); -} - -/** - * Payload creator for TERMS/AGREE_TERM_DONE - * @param {Number|String} termId id of term - * @param {String} tokenV3 auth token - * @return {Promise} promise of request result - */ -function agreeTermDone(termId, tokenV3) { - const service = getService(tokenV3); - return service - .agreeTerm(termId) - .then((resp) => ({ termId, success: resp.success })); -} - -/** - * Payload creator for TERMS/CHECK_STATUS_DONE - * which will check if all terms of specified entity have been agreed, - * - * NOTE: - * As in some reason backend does not saves immediately that DocuSign term has been agreed - * In case not all terms were agreed we try again after some delay. - * Maximum quantity attempts and delay between attempts are configured in - * MAX_ATTEMPTS and TIME_OUT - * - * TODO: - * Looks like the bug described above was caused by server caching responses - * at least for getTermDetails which is used by getCommunityTerms. - * To fix it I've added nocache random value param in the terms service - * for getTermDetails and it looks like works so we get results immediately. - * This still have to be tested for challenges as they use another endpoint - * in method getChallengeTerms. - * Also terms which use third part service DocuSign has to be also tested prior - * to removing multiple checks. - * In case their agreed status is updated immediately, this code - * has to simplified and don't make several attempts, only one. - * - * @param {Object} entity entity object - * @param {String} entity.type entity type ['challenge'||'community'] - * @param {String} entity.id entity id - * @param {Object} tokens object with tokenV2 and tokenV3 properties - * - * @return {Promise} promise of request result - */ -function checkStatusDone(entity, tokens) { - // timeout between checking status attempts - const TIME_OUT = 5000; - - // maximum attempts to check status - const MAX_ATTEMPTS = 5; - - // we set this flag for getTermsDone when MOCK_TERMS_SERVICE is true - // so that checkStatusDone resolves to all terms agreed when mocking - const mockAgreed = config.MOCK_TERMS_SERVICE; - - /** - * Promisified setTimeout - * @param {Number} timeout timeout in milliseconds - * @return {Promise} resolves after timeout - */ - const delay = (timeout) => - new Promise((resolve) => { - setTimeout(resolve, timeout); - }); - - /** - * Makes attempt to check status - * @param {Number} maxAttempts maximum number of attempts to perform - * @return {Promise} resolves to the list of term objects - */ - const checkStatus = (maxAttempts) => - getTermsDone(entity, tokens, mockAgreed).then((res) => { - const allAgreed = _.every(res.terms, "agreed"); - - // if not all terms are agreed and we still have some attempts to try - if (!allAgreed && maxAttempts > 1) { - return delay(TIME_OUT).then(() => checkStatus(maxAttempts - 1)); - } - - return res.terms; - }); - - return checkStatus(MAX_ATTEMPTS); -} - -/** - * Payload creator for the action that opens the specified terms modal. - * @param {String} modalInstanceUuid ID of the terms modal instance to be - * opened. Any other instances of terms modals present in the page will be - * closed automatically by this action, as it is not safe to open multiple - * modals, (and makes no sense in current implementation). - * @param {???} selectedTerm Optional. Selected term. It was not documented by - * author of related code, thus the exact value is not clear. - * @return {Object} Action payload. - */ -function openTermsModal(modalInstanceUuid, selectedTerm) { - return { modalInstanceUuid, selectedTerm }; -} - -/** - * Payload creator for the action that closes the specified terms modal. - * @param {String} modalInstanceUuid ID of the terms modal instance to be - * closed. If another terms modal is open, it won't be affected. - * @return {String} Action payload. - */ -function closeTermsModal(modalInstanceUuid) { - return modalInstanceUuid; -} - -export default createActions({ - TERMS: { - GET_TERMS_INIT: _.identity, - GET_TERMS_DONE: getTermsDone, - GET_TERM_DETAILS_INIT: getTermDetailsInit, - GET_TERM_DETAILS_DONE: getTermDetailsDone, - GET_DOCU_SIGN_URL_INIT: getDocuSignUrlInit, - GET_DOCU_SIGN_URL_DONE: getDocuSignUrlDone, - AGREE_TERM_INIT: agreeTermInit, - AGREE_TERM_DONE: agreeTermDone, - - OPEN_TERMS_MODAL: openTermsModal, - CLOSE_TERMS_MODAL: closeTermsModal, - - SELECT_TERM: _.identity, - SIGN_DOCU: _.identity, - CHECK_STATUS_INIT: _.noop, - CHECK_STATUS_DONE: checkStatusDone, - }, -}); diff --git a/src/apps/earn/src/actions/user/creators.js b/src/apps/earn/src/actions/user/creators.js deleted file mode 100644 index 14aa5d151..000000000 --- a/src/apps/earn/src/actions/user/creators.js +++ /dev/null @@ -1,15 +0,0 @@ -import { createActions } from "redux-actions"; -import * as ACTION_TYPE from "./types"; - -const actions = createActions( - {}, - ACTION_TYPE.LOAD_PROFILE_ERROR, - ACTION_TYPE.LOAD_PROFILE_PENDING, - ACTION_TYPE.LOAD_PROFILE_SUCCESS, - ACTION_TYPE.LOAD_REFERRAL_DATA_ERROR, - ACTION_TYPE.LOAD_REFERRAL_DATA_PENDING, - ACTION_TYPE.LOAD_REFERRAL_DATA_SUCCESS, - { prefix: "USER", namespace: "--" } -); - -export default actions; diff --git a/src/apps/earn/src/actions/user/effectors.js b/src/apps/earn/src/actions/user/effectors.js deleted file mode 100644 index 5a0449eba..000000000 --- a/src/apps/earn/src/actions/user/effectors.js +++ /dev/null @@ -1,75 +0,0 @@ -import { profileGetLoggedInAsync } from "~/libs/core"; - -import * as selectors from "../../reducers/user/selectors"; -import { fetchReferralData } from "../../services/referral"; -import { delay } from "../../utils/misc"; - -import actions from "./creators"; - -/** - * Loads user's referral data. - * - * @param {Object} store redux store object - * @param {boolean} [forceReload] force data reload even if it is already present - * @returns {Promise} - */ -export const loadReferralData = async ( - { dispatch, getState }, - forceReload = false -) => { - const state = getState(); - const referralData = selectors.getReferralData(state); - if (referralData && !forceReload) { - return; - } - const profile = selectors.getProfile(state); - if (!profile) { - console.error( - "No profile data provided when trying to fetch referral data." - ); - return; - } - let data = null; - try { - data = await fetchReferralData(profile); - } catch (error) { - dispatch(actions.loadReferralDataError(error.toString())); - return; - } - dispatch(actions.loadReferralDataSuccess(data)); -}; - -/** - * Tries to load user's profile. - * - * @param {Object} store redux store object - * @param {boolean} [forceReload] force data reload even if it is already present - * @returns {Promise} - */ -export const loadProfile = async ( - { dispatch, getState }, - forceReload = false -) => { - let profile = selectors.getProfile(getState()); - if (profile && !forceReload) { - return; - } - let error = null; - dispatch(actions.loadProfilePending()); - for (let i = 0; i < 3; i++) { - try { - profile = await profileGetLoggedInAsync(); - } catch (err) { - error = err; - } - if (profile) { - break; - } - await delay(1000); - } - if (error) { - dispatch(actions.loadProfileError(error.toString())); - } else { - dispatch(actions.loadProfileSuccess(profile)); - } -}; diff --git a/src/apps/earn/src/actions/user/types.js b/src/apps/earn/src/actions/user/types.js deleted file mode 100644 index 1ae4310a9..000000000 --- a/src/apps/earn/src/actions/user/types.js +++ /dev/null @@ -1,6 +0,0 @@ -export const LOAD_PROFILE_PENDING = "LOAD_PROFILE_PENDING"; -export const LOAD_PROFILE_ERROR = "LOAD_PROFILE_ERROR"; -export const LOAD_PROFILE_SUCCESS = "LOAD_PROFILE_SUCCESS"; -export const LOAD_REFERRAL_DATA_ERROR = "LOAD_REFERRAL_DATA_ERROR"; -export const LOAD_REFERRAL_DATA_PENDING = "LOAD_REFERRAL_DATA_PENDING"; -export const LOAD_REFERRAL_DATA_SUCCESS = "LOAD_REFERRAL_DATA_SUCCESS"; diff --git a/src/apps/earn/src/assets/icons/IconCloudDownload.svg b/src/apps/earn/src/assets/icons/IconCloudDownload.svg deleted file mode 100644 index b17e3bc52..000000000 --- a/src/apps/earn/src/assets/icons/IconCloudDownload.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/IconMinimalDown.svg b/src/apps/earn/src/assets/icons/IconMinimalDown.svg deleted file mode 100644 index 919a0ae0b..000000000 --- a/src/apps/earn/src/assets/icons/IconMinimalDown.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - diff --git a/src/apps/earn/src/assets/icons/IconMinimalLeft.svg b/src/apps/earn/src/assets/icons/IconMinimalLeft.svg deleted file mode 100644 index 287bf690e..000000000 --- a/src/apps/earn/src/assets/icons/IconMinimalLeft.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - diff --git a/src/apps/earn/src/assets/icons/IconMinimalRight.svg b/src/apps/earn/src/assets/icons/IconMinimalRight.svg deleted file mode 100644 index d2905c94b..000000000 --- a/src/apps/earn/src/assets/icons/IconMinimalRight.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - diff --git a/src/apps/earn/src/assets/icons/IconMinimalUp.svg b/src/apps/earn/src/assets/icons/IconMinimalUp.svg deleted file mode 100644 index 9bf3e2d5a..000000000 --- a/src/apps/earn/src/assets/icons/IconMinimalUp.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - diff --git a/src/apps/earn/src/assets/icons/IconShare.svg b/src/apps/earn/src/assets/icons/IconShare.svg deleted file mode 100644 index 2c731ab66..000000000 --- a/src/apps/earn/src/assets/icons/IconShare.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - diff --git a/src/apps/earn/src/assets/icons/IconSquareDownload.svg b/src/apps/earn/src/assets/icons/IconSquareDownload.svg deleted file mode 100644 index 9ef5ceed7..000000000 --- a/src/apps/earn/src/assets/icons/IconSquareDownload.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/IconTrashSimple.svg b/src/apps/earn/src/assets/icons/IconTrashSimple.svg deleted file mode 100644 index 2b1e18cf4..000000000 --- a/src/apps/earn/src/assets/icons/IconTrashSimple.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - diff --git a/src/apps/earn/src/assets/icons/arrow-down.svg b/src/apps/earn/src/assets/icons/arrow-down.svg deleted file mode 100644 index bcfb1072d..000000000 --- a/src/apps/earn/src/assets/icons/arrow-down.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/arrow-prev.svg b/src/apps/earn/src/assets/icons/arrow-prev.svg deleted file mode 100644 index 0cfb59fda..000000000 --- a/src/apps/earn/src/assets/icons/arrow-prev.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - diff --git a/src/apps/earn/src/assets/icons/arrow-right.svg b/src/apps/earn/src/assets/icons/arrow-right.svg deleted file mode 100644 index a75d8d921..000000000 --- a/src/apps/earn/src/assets/icons/arrow-right.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/apps/earn/src/assets/icons/arrow.svg b/src/apps/earn/src/assets/icons/arrow.svg deleted file mode 100644 index 5a6667ff4..000000000 --- a/src/apps/earn/src/assets/icons/arrow.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - 2F48C66D-6E88-40BD-8F0F-1ABB211BC6D0 - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/banner-chevron-up.svg b/src/apps/earn/src/assets/icons/banner-chevron-up.svg deleted file mode 100644 index 96c5c3d89..000000000 --- a/src/apps/earn/src/assets/icons/banner-chevron-up.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - banner chevron - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/card-view.svg b/src/apps/earn/src/assets/icons/card-view.svg deleted file mode 100644 index a25ef8005..000000000 --- a/src/apps/earn/src/assets/icons/card-view.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/checkmark-large.png b/src/apps/earn/src/assets/icons/checkmark-large.png deleted file mode 100644 index d3c90d8c6..000000000 Binary files a/src/apps/earn/src/assets/icons/checkmark-large.png and /dev/null differ diff --git a/src/apps/earn/src/assets/icons/checkmark-medium.png b/src/apps/earn/src/assets/icons/checkmark-medium.png deleted file mode 100644 index 454fd20e7..000000000 Binary files a/src/apps/earn/src/assets/icons/checkmark-medium.png and /dev/null differ diff --git a/src/apps/earn/src/assets/icons/checkmark-small.png b/src/apps/earn/src/assets/icons/checkmark-small.png deleted file mode 100644 index fdcd176f2..000000000 Binary files a/src/apps/earn/src/assets/icons/checkmark-small.png and /dev/null differ diff --git a/src/apps/earn/src/assets/icons/checkpoint-small.svg b/src/apps/earn/src/assets/icons/checkpoint-small.svg deleted file mode 100644 index 81f1073b6..000000000 --- a/src/apps/earn/src/assets/icons/checkpoint-small.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/checkpoint.svg b/src/apps/earn/src/assets/icons/checkpoint.svg deleted file mode 100644 index 5f748e847..000000000 --- a/src/apps/earn/src/assets/icons/checkpoint.svg +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/close-gray.svg b/src/apps/earn/src/assets/icons/close-gray.svg deleted file mode 100644 index d773daa00..000000000 --- a/src/apps/earn/src/assets/icons/close-gray.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/close.svg b/src/apps/earn/src/assets/icons/close.svg deleted file mode 100644 index 26c8dfbb8..000000000 --- a/src/apps/earn/src/assets/icons/close.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/dropdown-arrow.png b/src/apps/earn/src/assets/icons/dropdown-arrow.png deleted file mode 100644 index 40edda675..000000000 Binary files a/src/apps/earn/src/assets/icons/dropdown-arrow.png and /dev/null differ diff --git a/src/apps/earn/src/assets/icons/find-work-green.svg b/src/apps/earn/src/assets/icons/find-work-green.svg deleted file mode 100644 index fb4743f1a..000000000 --- a/src/apps/earn/src/assets/icons/find-work-green.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - 1D5681BC-C33F-4401-A8C9-A5A6F99E8CA7 - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/find-work.svg b/src/apps/earn/src/assets/icons/find-work.svg deleted file mode 100644 index 1af6abbaa..000000000 --- a/src/apps/earn/src/assets/icons/find-work.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - DAD5564D-C4AF-448B-9138-756872CA4CB8 - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/icon-calendar-blue.svg b/src/apps/earn/src/assets/icons/icon-calendar-blue.svg deleted file mode 100644 index 84fbbc9dd..000000000 --- a/src/apps/earn/src/assets/icons/icon-calendar-blue.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - Fill 81 Copy - Created with Sketch. - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/icon-calendar-medium.svg b/src/apps/earn/src/assets/icons/icon-calendar-medium.svg deleted file mode 100644 index 26c0a56e8..000000000 --- a/src/apps/earn/src/assets/icons/icon-calendar-medium.svg +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/src/apps/earn/src/assets/icons/icon-calendar.svg b/src/apps/earn/src/assets/icons/icon-calendar.svg deleted file mode 100644 index b75a6fb03..000000000 --- a/src/apps/earn/src/assets/icons/icon-calendar.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - Fill 81 Copy - Created with Sketch. - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/icon-corner-left-green.svg b/src/apps/earn/src/assets/icons/icon-corner-left-green.svg deleted file mode 100644 index 0b69acfa2..000000000 --- a/src/apps/earn/src/assets/icons/icon-corner-left-green.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - diff --git a/src/apps/earn/src/assets/icons/icon-cross.png b/src/apps/earn/src/assets/icons/icon-cross.png deleted file mode 100644 index 75604cde4..000000000 Binary files a/src/apps/earn/src/assets/icons/icon-cross.png and /dev/null differ diff --git a/src/apps/earn/src/assets/icons/icon-facebook-gray.svg b/src/apps/earn/src/assets/icons/icon-facebook-gray.svg deleted file mode 100644 index fd976e999..000000000 --- a/src/apps/earn/src/assets/icons/icon-facebook-gray.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/src/apps/earn/src/assets/icons/icon-gear-blue.svg b/src/apps/earn/src/assets/icons/icon-gear-blue.svg deleted file mode 100644 index 6705fa5ba..000000000 --- a/src/apps/earn/src/assets/icons/icon-gear-blue.svg +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/src/apps/earn/src/assets/icons/icon-globe-simple.svg b/src/apps/earn/src/assets/icons/icon-globe-simple.svg deleted file mode 100644 index c726e199b..000000000 --- a/src/apps/earn/src/assets/icons/icon-globe-simple.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - diff --git a/src/apps/earn/src/assets/icons/icon-globe.png b/src/apps/earn/src/assets/icons/icon-globe.png deleted file mode 100644 index d324710cd..000000000 Binary files a/src/apps/earn/src/assets/icons/icon-globe.png and /dev/null differ diff --git a/src/apps/earn/src/assets/icons/icon-hourglass.svg b/src/apps/earn/src/assets/icons/icon-hourglass.svg deleted file mode 100644 index 9a154d2d0..000000000 --- a/src/apps/earn/src/assets/icons/icon-hourglass.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - diff --git a/src/apps/earn/src/assets/icons/icon-linkedin-gray.svg b/src/apps/earn/src/assets/icons/icon-linkedin-gray.svg deleted file mode 100644 index 6653b4d77..000000000 --- a/src/apps/earn/src/assets/icons/icon-linkedin-gray.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - diff --git a/src/apps/earn/src/assets/icons/icon-location-crimson.svg b/src/apps/earn/src/assets/icons/icon-location-crimson.svg deleted file mode 100644 index d993f5f6f..000000000 --- a/src/apps/earn/src/assets/icons/icon-location-crimson.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - diff --git a/src/apps/earn/src/assets/icons/icon-location-mark.svg b/src/apps/earn/src/assets/icons/icon-location-mark.svg deleted file mode 100644 index 19c17a8a9..000000000 --- a/src/apps/earn/src/assets/icons/icon-location-mark.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - diff --git a/src/apps/earn/src/assets/icons/icon-magnifier.svg b/src/apps/earn/src/assets/icons/icon-magnifier.svg deleted file mode 100644 index 657f19399..000000000 --- a/src/apps/earn/src/assets/icons/icon-magnifier.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - diff --git a/src/apps/earn/src/assets/icons/icon-mark.png b/src/apps/earn/src/assets/icons/icon-mark.png deleted file mode 100644 index 8b7b43c57..000000000 Binary files a/src/apps/earn/src/assets/icons/icon-mark.png and /dev/null differ diff --git a/src/apps/earn/src/assets/icons/icon-next.svg b/src/apps/earn/src/assets/icons/icon-next.svg deleted file mode 100644 index 467efa406..000000000 --- a/src/apps/earn/src/assets/icons/icon-next.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/icon-number-one.png b/src/apps/earn/src/assets/icons/icon-number-one.png deleted file mode 100644 index 29c75b631..000000000 Binary files a/src/apps/earn/src/assets/icons/icon-number-one.png and /dev/null differ diff --git a/src/apps/earn/src/assets/icons/icon-number-three.png b/src/apps/earn/src/assets/icons/icon-number-three.png deleted file mode 100644 index 9b3eb12c3..000000000 Binary files a/src/apps/earn/src/assets/icons/icon-number-three.png and /dev/null differ diff --git a/src/apps/earn/src/assets/icons/icon-number-two.png b/src/apps/earn/src/assets/icons/icon-number-two.png deleted file mode 100644 index 4947bef6a..000000000 Binary files a/src/apps/earn/src/assets/icons/icon-number-two.png and /dev/null differ diff --git a/src/apps/earn/src/assets/icons/icon-payment.svg b/src/apps/earn/src/assets/icons/icon-payment.svg deleted file mode 100644 index cdc79ef20..000000000 --- a/src/apps/earn/src/assets/icons/icon-payment.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - -$ - diff --git a/src/apps/earn/src/assets/icons/icon-prev.svg b/src/apps/earn/src/assets/icons/icon-prev.svg deleted file mode 100644 index 3e90c4708..000000000 --- a/src/apps/earn/src/assets/icons/icon-prev.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/icon-sad-face.svg b/src/apps/earn/src/assets/icons/icon-sad-face.svg deleted file mode 100644 index b9b685c03..000000000 --- a/src/apps/earn/src/assets/icons/icon-sad-face.svg +++ /dev/null @@ -1,20 +0,0 @@ - - - - 838C3DF4-2CF1-42A3-BC49-21142AF2003C@2x - Created with sketchtool. - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/icon-tick-circled.svg b/src/apps/earn/src/assets/icons/icon-tick-circled.svg deleted file mode 100644 index 5d80b5f34..000000000 --- a/src/apps/earn/src/assets/icons/icon-tick-circled.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/icon-twitter-gray.svg b/src/apps/earn/src/assets/icons/icon-twitter-gray.svg deleted file mode 100644 index ca0065e2b..000000000 --- a/src/apps/earn/src/assets/icons/icon-twitter-gray.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - diff --git a/src/apps/earn/src/assets/icons/info.svg b/src/apps/earn/src/assets/icons/info.svg deleted file mode 100644 index 937844107..000000000 --- a/src/apps/earn/src/assets/icons/info.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/list-view.svg b/src/apps/earn/src/assets/icons/list-view.svg deleted file mode 100644 index ea5bd2186..000000000 --- a/src/apps/earn/src/assets/icons/list-view.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/logo_topcoder.svg b/src/apps/earn/src/assets/icons/logo_topcoder.svg deleted file mode 100644 index 3fa9d3aa6..000000000 --- a/src/apps/earn/src/assets/icons/logo_topcoder.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/long-arrow-next.svg b/src/apps/earn/src/assets/icons/long-arrow-next.svg deleted file mode 100644 index 05de31982..000000000 --- a/src/apps/earn/src/assets/icons/long-arrow-next.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/apps/earn/src/assets/icons/menu-chevron-up.svg b/src/apps/earn/src/assets/icons/menu-chevron-up.svg deleted file mode 100644 index cfdb74002..000000000 --- a/src/apps/earn/src/assets/icons/menu-chevron-up.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - CE319284-3A67-4756-B6AD-2D4164352632 - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/my-work-green.svg b/src/apps/earn/src/assets/icons/my-work-green.svg deleted file mode 100644 index 3b944366f..000000000 --- a/src/apps/earn/src/assets/icons/my-work-green.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - 9178F574-02BA-4DFF-A4C4-93CE92BDE405 - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/my-work.svg b/src/apps/earn/src/assets/icons/my-work.svg deleted file mode 100644 index b679bbba3..000000000 --- a/src/apps/earn/src/assets/icons/my-work.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - C087C319-6EC3-4D78-A758-D1B398901B5D - - - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/not-found-recommended.png b/src/apps/earn/src/assets/icons/not-found-recommended.png deleted file mode 100644 index 728a77b96..000000000 Binary files a/src/apps/earn/src/assets/icons/not-found-recommended.png and /dev/null differ diff --git a/src/apps/earn/src/assets/icons/not-found.png b/src/apps/earn/src/assets/icons/not-found.png deleted file mode 100644 index a11d6a87b..000000000 Binary files a/src/apps/earn/src/assets/icons/not-found.png and /dev/null differ diff --git a/src/apps/earn/src/assets/icons/note.svg b/src/apps/earn/src/assets/icons/note.svg deleted file mode 100644 index 5edeea9af..000000000 --- a/src/apps/earn/src/assets/icons/note.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/registrant.svg b/src/apps/earn/src/assets/icons/registrant.svg deleted file mode 100644 index 1e337b068..000000000 --- a/src/apps/earn/src/assets/icons/registrant.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/ribbon-icon.svg b/src/apps/earn/src/assets/icons/ribbon-icon.svg deleted file mode 100644 index 92bef26e0..000000000 --- a/src/apps/earn/src/assets/icons/ribbon-icon.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/robot-embarassed.svg b/src/apps/earn/src/assets/icons/robot-embarassed.svg deleted file mode 100644 index 0d974364a..000000000 --- a/src/apps/earn/src/assets/icons/robot-embarassed.svg +++ /dev/null @@ -1,20 +0,0 @@ - - - robot-embarresed - Created with Sketch. - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/robot-happy.svg b/src/apps/earn/src/assets/icons/robot-happy.svg deleted file mode 100644 index 8567e7369..000000000 --- a/src/apps/earn/src/assets/icons/robot-happy.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/search.svg b/src/apps/earn/src/assets/icons/search.svg deleted file mode 100644 index 2ae8f711a..000000000 --- a/src/apps/earn/src/assets/icons/search.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/submission.svg b/src/apps/earn/src/assets/icons/submission.svg deleted file mode 100644 index 2ddc02eb2..000000000 --- a/src/apps/earn/src/assets/icons/submission.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/track-des.svg b/src/apps/earn/src/assets/icons/track-des.svg deleted file mode 100644 index fee5c04d2..000000000 --- a/src/apps/earn/src/assets/icons/track-des.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - AC4D4B5E-93F9-4F74-A2B8-9AE81B345347 - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/track-dev.svg b/src/apps/earn/src/assets/icons/track-dev.svg deleted file mode 100644 index f30f2f2d1..000000000 --- a/src/apps/earn/src/assets/icons/track-dev.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - 3D983778-B2BF-4CB8-B636-1C9EDCAA0EFD - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/track-ds.svg b/src/apps/earn/src/assets/icons/track-ds.svg deleted file mode 100644 index e676bfd80..000000000 --- a/src/apps/earn/src/assets/icons/track-ds.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - DE0551FC-97AC-4CB0-9254-C3C58445CA86 - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/track-qa.svg b/src/apps/earn/src/assets/icons/track-qa.svg deleted file mode 100644 index 18db8e841..000000000 --- a/src/apps/earn/src/assets/icons/track-qa.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - 37873904-C546-401A-A25C-FFDA30980031 - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/icons/update-success.svg b/src/apps/earn/src/assets/icons/update-success.svg deleted file mode 100644 index a721c4f61..000000000 --- a/src/apps/earn/src/assets/icons/update-success.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/images/IconSquareDownload.svg b/src/apps/earn/src/assets/images/IconSquareDownload.svg deleted file mode 100644 index 9ef5ceed7..000000000 --- a/src/apps/earn/src/assets/images/IconSquareDownload.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/images/arrow-down.svg b/src/apps/earn/src/assets/images/arrow-down.svg deleted file mode 100644 index d53eedd1a..000000000 --- a/src/apps/earn/src/assets/images/arrow-down.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - 5D558F9B-43EB-41DF-905D-8862EFEE8959 - Created with sketchtool. - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/images/arrow-left.svg b/src/apps/earn/src/assets/images/arrow-left.svg deleted file mode 100644 index 29f5eee2f..000000000 --- a/src/apps/earn/src/assets/images/arrow-left.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - AB093E0F-1C19-4224-9B2F-A8D7A5A76AD0 - Created with sketchtool. - - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/images/arrow-next.svg b/src/apps/earn/src/assets/images/arrow-next.svg deleted file mode 100644 index 288a45180..000000000 --- a/src/apps/earn/src/assets/images/arrow-next.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/images/arrow-prev-green.svg b/src/apps/earn/src/assets/images/arrow-prev-green.svg deleted file mode 100644 index c0c10c4df..000000000 --- a/src/apps/earn/src/assets/images/arrow-prev-green.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/apps/earn/src/assets/images/arrow-prev.svg b/src/apps/earn/src/assets/images/arrow-prev.svg deleted file mode 100644 index 4c17ae49f..000000000 --- a/src/apps/earn/src/assets/images/arrow-prev.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/apps/earn/src/assets/images/arrow-right-green.svg b/src/apps/earn/src/assets/images/arrow-right-green.svg deleted file mode 100644 index 2c28fcca4..000000000 --- a/src/apps/earn/src/assets/images/arrow-right-green.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/apps/earn/src/assets/images/blob-purple.svg b/src/apps/earn/src/assets/images/blob-purple.svg deleted file mode 100644 index cd1d5d93c..000000000 --- a/src/apps/earn/src/assets/images/blob-purple.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/images/blob-yellow.svg b/src/apps/earn/src/assets/images/blob-yellow.svg deleted file mode 100644 index 98ff027a5..000000000 --- a/src/apps/earn/src/assets/images/blob-yellow.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - 4E19AD87-834F-4DB6-AFFB-EA530C3ACC5D - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/images/calendar.svg b/src/apps/earn/src/assets/images/calendar.svg deleted file mode 100644 index d14c5596e..000000000 --- a/src/apps/earn/src/assets/images/calendar.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/apps/earn/src/assets/images/celebrate.svg b/src/apps/earn/src/assets/images/celebrate.svg deleted file mode 100644 index be86c1da7..000000000 --- a/src/apps/earn/src/assets/images/celebrate.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - Combined Shape - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/images/completed.svg b/src/apps/earn/src/assets/images/completed.svg deleted file mode 100644 index 8b638020e..000000000 --- a/src/apps/earn/src/assets/images/completed.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - C23ADC25-9150-4E3C-9043-97A704EBE690 - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/images/default-user-avatar.svg b/src/apps/earn/src/assets/images/default-user-avatar.svg deleted file mode 100644 index 4a80a0465..000000000 --- a/src/apps/earn/src/assets/images/default-user-avatar.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/images/ico-user-default.png b/src/apps/earn/src/assets/images/ico-user-default.png deleted file mode 100644 index 6454881d6..000000000 Binary files a/src/apps/earn/src/assets/images/ico-user-default.png and /dev/null differ diff --git a/src/apps/earn/src/assets/images/ico-user-default.svg b/src/apps/earn/src/assets/images/ico-user-default.svg deleted file mode 100644 index ed6ce1a7b..000000000 --- a/src/apps/earn/src/assets/images/ico-user-default.svg +++ /dev/null @@ -1,20 +0,0 @@ - - - - ico-user-default - Created with Sketch. - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/images/icon-arrow-down-black.svg b/src/apps/earn/src/assets/images/icon-arrow-down-black.svg deleted file mode 100644 index 869b8991c..000000000 --- a/src/apps/earn/src/assets/images/icon-arrow-down-black.svg +++ /dev/null @@ -1,9 +0,0 @@ - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/images/icon-arrow-down.svg b/src/apps/earn/src/assets/images/icon-arrow-down.svg deleted file mode 100644 index b8814cfb9..000000000 --- a/src/apps/earn/src/assets/images/icon-arrow-down.svg +++ /dev/null @@ -1,9 +0,0 @@ - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/images/icon-arrow-up-black.svg b/src/apps/earn/src/assets/images/icon-arrow-up-black.svg deleted file mode 100644 index 3b6328bbb..000000000 --- a/src/apps/earn/src/assets/images/icon-arrow-up-black.svg +++ /dev/null @@ -1,9 +0,0 @@ - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/images/icon-arrow-up.svg b/src/apps/earn/src/assets/images/icon-arrow-up.svg deleted file mode 100644 index cae45cce0..000000000 --- a/src/apps/earn/src/assets/images/icon-arrow-up.svg +++ /dev/null @@ -1,9 +0,0 @@ - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/images/icon-calendar-2-active.svg b/src/apps/earn/src/assets/images/icon-calendar-2-active.svg deleted file mode 100644 index e03bcbecf..000000000 --- a/src/apps/earn/src/assets/images/icon-calendar-2-active.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/apps/earn/src/assets/images/icon-calendar-2.svg b/src/apps/earn/src/assets/images/icon-calendar-2.svg deleted file mode 100644 index c8f954975..000000000 --- a/src/apps/earn/src/assets/images/icon-calendar-2.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/apps/earn/src/assets/images/icon-close-green.svg b/src/apps/earn/src/assets/images/icon-close-green.svg deleted file mode 100644 index 101c548ea..000000000 --- a/src/apps/earn/src/assets/images/icon-close-green.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/apps/earn/src/assets/images/icon-date-sort.svg b/src/apps/earn/src/assets/images/icon-date-sort.svg deleted file mode 100644 index 1defb57d7..000000000 --- a/src/apps/earn/src/assets/images/icon-date-sort.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/apps/earn/src/assets/images/icon-sort-mobile.svg b/src/apps/earn/src/assets/images/icon-sort-mobile.svg deleted file mode 100644 index d5c9466c8..000000000 --- a/src/apps/earn/src/assets/images/icon-sort-mobile.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/apps/earn/src/assets/images/icon-sort.svg b/src/apps/earn/src/assets/images/icon-sort.svg deleted file mode 100644 index 8919cf0ed..000000000 --- a/src/apps/earn/src/assets/images/icon-sort.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/apps/earn/src/assets/images/icon-time-active.svg b/src/apps/earn/src/assets/images/icon-time-active.svg deleted file mode 100644 index cf1202494..000000000 --- a/src/apps/earn/src/assets/images/icon-time-active.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/apps/earn/src/assets/images/icon-time.svg b/src/apps/earn/src/assets/images/icon-time.svg deleted file mode 100644 index 5e535625f..000000000 --- a/src/apps/earn/src/assets/images/icon-time.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/apps/earn/src/assets/images/icon-verified.svg b/src/apps/earn/src/assets/images/icon-verified.svg deleted file mode 100644 index ac4a28ac7..000000000 --- a/src/apps/earn/src/assets/images/icon-verified.svg +++ /dev/null @@ -1,37 +0,0 @@ - - - Group 6 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/apps/earn/src/assets/images/nav-active-item-blue.svg b/src/apps/earn/src/assets/images/nav-active-item-blue.svg deleted file mode 100644 index 459058553..000000000 --- a/src/apps/earn/src/assets/images/nav-active-item-blue.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/apps/earn/src/assets/images/nav-active-item.svg b/src/apps/earn/src/assets/images/nav-active-item.svg deleted file mode 100644 index 5ff4ba65b..000000000 --- a/src/apps/earn/src/assets/images/nav-active-item.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/apps/earn/src/assets/images/open-graph/challenges/._06-Big-Prize.png b/src/apps/earn/src/assets/images/open-graph/challenges/._06-Big-Prize.png deleted file mode 100644 index 127aed183..000000000 Binary files a/src/apps/earn/src/assets/images/open-graph/challenges/._06-Big-Prize.png and /dev/null differ diff --git a/src/apps/earn/src/assets/images/open-graph/challenges/._09-First2Finish.png b/src/apps/earn/src/assets/images/open-graph/challenges/._09-First2Finish.png deleted file mode 100644 index 8a66ae8d6..000000000 Binary files a/src/apps/earn/src/assets/images/open-graph/challenges/._09-First2Finish.png and /dev/null differ diff --git a/src/apps/earn/src/assets/images/open-graph/challenges/02-Design-Preview.png b/src/apps/earn/src/assets/images/open-graph/challenges/02-Design-Preview.png deleted file mode 100644 index 18ddf6c07..000000000 Binary files a/src/apps/earn/src/assets/images/open-graph/challenges/02-Design-Preview.png and /dev/null differ diff --git a/src/apps/earn/src/assets/images/open-graph/challenges/03-Development.png b/src/apps/earn/src/assets/images/open-graph/challenges/03-Development.png deleted file mode 100644 index 11d31928d..000000000 Binary files a/src/apps/earn/src/assets/images/open-graph/challenges/03-Development.png and /dev/null differ diff --git a/src/apps/earn/src/assets/images/open-graph/challenges/04-Data-Science.png b/src/apps/earn/src/assets/images/open-graph/challenges/04-Data-Science.png deleted file mode 100644 index 5f58f3e14..000000000 Binary files a/src/apps/earn/src/assets/images/open-graph/challenges/04-Data-Science.png and /dev/null differ diff --git a/src/apps/earn/src/assets/images/open-graph/challenges/05-QA.png b/src/apps/earn/src/assets/images/open-graph/challenges/05-QA.png deleted file mode 100644 index a476e36c8..000000000 Binary files a/src/apps/earn/src/assets/images/open-graph/challenges/05-QA.png and /dev/null differ diff --git a/src/apps/earn/src/assets/images/open-graph/challenges/06-Big-Prize.png b/src/apps/earn/src/assets/images/open-graph/challenges/06-Big-Prize.png deleted file mode 100644 index 8fedf92a1..000000000 Binary files a/src/apps/earn/src/assets/images/open-graph/challenges/06-Big-Prize.png and /dev/null differ diff --git a/src/apps/earn/src/assets/images/open-graph/challenges/09-First2Finish.png b/src/apps/earn/src/assets/images/open-graph/challenges/09-First2Finish.png deleted file mode 100644 index 07993a41a..000000000 Binary files a/src/apps/earn/src/assets/images/open-graph/challenges/09-First2Finish.png and /dev/null differ diff --git a/src/apps/earn/src/assets/images/open-graph/challenges/Design-First2Finish.png b/src/apps/earn/src/assets/images/open-graph/challenges/Design-First2Finish.png deleted file mode 100644 index 887800120..000000000 Binary files a/src/apps/earn/src/assets/images/open-graph/challenges/Design-First2Finish.png and /dev/null differ diff --git a/src/apps/earn/src/assets/images/open-graph/challenges/Design-Task.png b/src/apps/earn/src/assets/images/open-graph/challenges/Design-Task.png deleted file mode 100644 index f2e2fb18e..000000000 Binary files a/src/apps/earn/src/assets/images/open-graph/challenges/Design-Task.png and /dev/null differ diff --git a/src/apps/earn/src/assets/images/open-graph/challenges/Development-First2Finish.png b/src/apps/earn/src/assets/images/open-graph/challenges/Development-First2Finish.png deleted file mode 100644 index f3f2ceee8..000000000 Binary files a/src/apps/earn/src/assets/images/open-graph/challenges/Development-First2Finish.png and /dev/null differ diff --git a/src/apps/earn/src/assets/images/open-graph/challenges/Development-Task.png b/src/apps/earn/src/assets/images/open-graph/challenges/Development-Task.png deleted file mode 100644 index 20cbac13f..000000000 Binary files a/src/apps/earn/src/assets/images/open-graph/challenges/Development-Task.png and /dev/null differ diff --git a/src/apps/earn/src/assets/images/open-graph/challenges/MM-Challenge.png b/src/apps/earn/src/assets/images/open-graph/challenges/MM-Challenge.png deleted file mode 100644 index b747546df..000000000 Binary files a/src/apps/earn/src/assets/images/open-graph/challenges/MM-Challenge.png and /dev/null differ diff --git a/src/apps/earn/src/assets/images/open-graph/challenges/QA-First2Finish.png b/src/apps/earn/src/assets/images/open-graph/challenges/QA-First2Finish.png deleted file mode 100644 index e3d439288..000000000 Binary files a/src/apps/earn/src/assets/images/open-graph/challenges/QA-First2Finish.png and /dev/null differ diff --git a/src/apps/earn/src/assets/images/open-graph/challenges/QA-Task.png b/src/apps/earn/src/assets/images/open-graph/challenges/QA-Task.png deleted file mode 100644 index 0c94fdc20..000000000 Binary files a/src/apps/earn/src/assets/images/open-graph/challenges/QA-Task.png and /dev/null differ diff --git a/src/apps/earn/src/assets/images/progress-bar-mid.svg b/src/apps/earn/src/assets/images/progress-bar-mid.svg deleted file mode 100644 index 89bb89a9e..000000000 --- a/src/apps/earn/src/assets/images/progress-bar-mid.svg +++ /dev/null @@ -1,35 +0,0 @@ - - - 950E0BBC-2C06-4F3E-A318-363E03868587@2x - - - - - - - - - - - - - - - - - - - - - - You are here - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/images/progress-bar-mobile.svg b/src/apps/earn/src/assets/images/progress-bar-mobile.svg deleted file mode 100644 index f3bc32823..000000000 --- a/src/apps/earn/src/assets/images/progress-bar-mobile.svg +++ /dev/null @@ -1,37 +0,0 @@ - - - 4C8194C6-558E-4D9A-B419-CA20B73B5E59@2x - - - - - - - - - - - - - - - - - - - - - - - - You are here - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/images/progress-bar.svg b/src/apps/earn/src/assets/images/progress-bar.svg deleted file mode 100644 index 31d0152d6..000000000 --- a/src/apps/earn/src/assets/images/progress-bar.svg +++ /dev/null @@ -1,35 +0,0 @@ - - - E3BD476A-D75E-46B7-B760-74F655D7902F - - - - - - - - - - - - - - - - - - - - - - You are here - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/images/social.png b/src/apps/earn/src/assets/images/social.png deleted file mode 100644 index f64e59923..000000000 Binary files a/src/apps/earn/src/assets/images/social.png and /dev/null differ diff --git a/src/apps/earn/src/assets/images/social/icon_email.svg b/src/apps/earn/src/assets/images/social/icon_email.svg deleted file mode 100644 index e03bb5c54..000000000 --- a/src/apps/earn/src/assets/images/social/icon_email.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/src/apps/earn/src/assets/images/social/icon_facebook.svg b/src/apps/earn/src/assets/images/social/icon_facebook.svg deleted file mode 100644 index 548ec26a8..000000000 --- a/src/apps/earn/src/assets/images/social/icon_facebook.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/apps/earn/src/assets/images/social/icon_plus.svg b/src/apps/earn/src/assets/images/social/icon_plus.svg deleted file mode 100644 index dbb6e5ce5..000000000 --- a/src/apps/earn/src/assets/images/social/icon_plus.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/apps/earn/src/assets/images/social/icon_print.svg b/src/apps/earn/src/assets/images/social/icon_print.svg deleted file mode 100644 index aa0b1e8ea..000000000 --- a/src/apps/earn/src/assets/images/social/icon_print.svg +++ /dev/null @@ -1,29 +0,0 @@ - - - - icon print - Created with Sketch. - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/images/social/icon_twitter.svg b/src/apps/earn/src/assets/images/social/icon_twitter.svg deleted file mode 100644 index d08c276e0..000000000 --- a/src/apps/earn/src/assets/images/social/icon_twitter.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/apps/earn/src/assets/images/tco19_logo_black.svg b/src/apps/earn/src/assets/images/tco19_logo_black.svg deleted file mode 100644 index 02102ed27..000000000 --- a/src/apps/earn/src/assets/images/tco19_logo_black.svg +++ /dev/null @@ -1,39 +0,0 @@ - - - - Topcoder Open 2019 - Created with sketchtool. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/images/thinking-face-laptop-tablet.svg b/src/apps/earn/src/assets/images/thinking-face-laptop-tablet.svg deleted file mode 100644 index 809839b5e..000000000 --- a/src/apps/earn/src/assets/images/thinking-face-laptop-tablet.svg +++ /dev/null @@ -1,433 +0,0 @@ - - - - - - - - - - - diff --git a/src/apps/earn/src/assets/images/thinking-face-mobile.svg b/src/apps/earn/src/assets/images/thinking-face-mobile.svg deleted file mode 100644 index cb818ced0..000000000 --- a/src/apps/earn/src/assets/images/thinking-face-mobile.svg +++ /dev/null @@ -1,433 +0,0 @@ - - - - - - - - - - - diff --git a/src/apps/earn/src/assets/images/tick-big.png b/src/apps/earn/src/assets/images/tick-big.png deleted file mode 100644 index 8bd83bc41..000000000 Binary files a/src/apps/earn/src/assets/images/tick-big.png and /dev/null differ diff --git a/src/apps/earn/src/assets/images/tooltip-arrow.svg b/src/apps/earn/src/assets/images/tooltip-arrow.svg deleted file mode 100644 index a6bda5162..000000000 --- a/src/apps/earn/src/assets/images/tooltip-arrow.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/apps/earn/src/assets/images/tooltip-info.svg b/src/apps/earn/src/assets/images/tooltip-info.svg deleted file mode 100644 index bb9b227af..000000000 --- a/src/apps/earn/src/assets/images/tooltip-info.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/apps/earn/src/assets/images/update-success-big.svg b/src/apps/earn/src/assets/images/update-success-big.svg deleted file mode 100644 index ba202e104..000000000 --- a/src/apps/earn/src/assets/images/update-success-big.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/src/apps/earn/src/assets/images/upload-fail.svg b/src/apps/earn/src/assets/images/upload-fail.svg deleted file mode 100644 index e402a5bb4..000000000 --- a/src/apps/earn/src/assets/images/upload-fail.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/apps/earn/src/assets/images/upload-loading.svg b/src/apps/earn/src/assets/images/upload-loading.svg deleted file mode 100644 index f96a787eb..000000000 --- a/src/apps/earn/src/assets/images/upload-loading.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/apps/earn/src/assets/images/upload-success.svg b/src/apps/earn/src/assets/images/upload-success.svg deleted file mode 100644 index 088cb2148..000000000 --- a/src/apps/earn/src/assets/images/upload-success.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/apps/earn/src/components/AccessDenied/index.jsx b/src/apps/earn/src/components/AccessDenied/index.jsx deleted file mode 100644 index 103530bd2..000000000 --- a/src/apps/earn/src/components/AccessDenied/index.jsx +++ /dev/null @@ -1,69 +0,0 @@ -import PT from "prop-types"; -import _ from "lodash"; - -import { EnvironmentConfig } from "~/config"; -import { LinkButton } from "~/libs/ui"; -import { ReactComponent as TopcoderLogo } from "@earn/assets/icons/logo_topcoder.svg"; - -import { ACCESS_DENIED_REASON } from "../../constants"; -import { styled as styledCss } from "../../utils"; - -import styles from "./styles.module.scss"; -const styled = styledCss(styles) - -const AccessDenied = ({ cause, redirectLink, children }) => { - const retUrl = encodeURIComponent(window.location.href); - switch (cause) { - case ACCESS_DENIED_REASON.NOT_AUTHENTICATED: { - return ( -
- -
- You must be authenticated to access this page. -
-
- - Log In Here - -
-
- ); - } - case ACCESS_DENIED_REASON.NOT_AUTHORIZED: - return ( -
- -
You are not authorized to access this page.
- {children} -
- ); - case ACCESS_DENIED_REASON.HAVE_NOT_SUBMITTED_TO_THE_CHALLENGE: - return ( -
- -
You have not submitted to this challenge
- Back to the challenge -
- ); - default: - return
; - } -}; - -AccessDenied.defaultProps = { - cause: ACCESS_DENIED_REASON.NOT_AUTHENTICATED, - redirectLink: "", - children: null, -}; - -AccessDenied.propTypes = { - cause: PT.oneOf(_.toArray(ACCESS_DENIED_REASON)), - redirectLink: PT.string, - children: PT.node, -}; - -export default AccessDenied; diff --git a/src/apps/earn/src/components/AccessDenied/styles.module.scss b/src/apps/earn/src/components/AccessDenied/styles.module.scss deleted file mode 100644 index 41e351b8f..000000000 --- a/src/apps/earn/src/components/AccessDenied/styles.module.scss +++ /dev/null @@ -1,48 +0,0 @@ -@import '@earn/styles/variables'; -@import '@earn/styles/mixins'; - -.access-denied { - @include tc-body-lg; - - padding-top: 100px; - text-align: center; - width: 100%; -} - -.msg { - padding-top: 48px; - - a { - cursor: pointer; - } -} - -.joinNow { - &, - &:hover, - &:visited { - color: $tc-light-blue; - text-decoration: underline; - } -} - -.policy { - font-weight: bold; - margin-right: 24px; - - &, - &:hover, - &:visited { - color: $tc-light-blue; - text-decoration: underline; - } -} - -.copyright { - @include roboto-regular; - - color: $tc-gray-60; - font-size: 12pt; - margin-top: 128px; - text-transform: uppercase; -} diff --git a/src/apps/earn/src/components/Avatar/index.jsx b/src/apps/earn/src/components/Avatar/index.jsx deleted file mode 100644 index 9e74f6467..000000000 --- a/src/apps/earn/src/components/Avatar/index.jsx +++ /dev/null @@ -1,12 +0,0 @@ -import { ReactComponent as DefaultUserAvatar } from '@earn/assets/images/default-user-avatar.svg'; -import React from 'react'; -import { themr } from 'react-css-super-themr'; -import ProtoAvatar from '../ProtoAvatar'; - -import theme from './style.scss'; - -function Avatar(props) { - return ; -} - -export default themr('Avatar', theme)(Avatar); \ No newline at end of file diff --git a/src/apps/earn/src/components/Avatar/style.scss b/src/apps/earn/src/components/Avatar/style.scss deleted file mode 100644 index 843c97d37..000000000 --- a/src/apps/earn/src/components/Avatar/style.scss +++ /dev/null @@ -1,5 +0,0 @@ -.avatar { - border-radius: 16px; - height: 32px; - width: 32px; - } \ No newline at end of file diff --git a/src/apps/earn/src/components/Banner/index.jsx b/src/apps/earn/src/components/Banner/index.jsx deleted file mode 100644 index 96ac9b552..000000000 --- a/src/apps/earn/src/components/Banner/index.jsx +++ /dev/null @@ -1,48 +0,0 @@ -import { useState } from "react"; -import { ReactComponent as BannerChevronUp} from "../../assets/icons/banner-chevron-up.svg"; -import classNames from 'classnames' - -import styles from "./styles.scss"; - -export const Banner = () => { - const [isExpanded, setIsExpanded] = useState(false); - const header = - "Welcome to our BETA work listings site - Tell us what you think!"; - - return ( -
-

- {header} - - setIsExpanded(!isExpanded)} - > - - -

- - {isExpanded && ( -
-

- Welcome to the Beta version of the new Challenge Listings. During - this Beta phase, we will be fine-tuning the platform based on - feedback we receive from you, our community members. -

-

NOTE THAT THIS IS NOT THE FINAL VERSION OF THE SITE.

-

- You may encounter occasional broken links or error messages. If so, - please let us know! This is what the Beta phase is intended for, and - your feedback will enable us to greatly improve the new site.{" "} -

-

You can click on the Feedback button on page.

-

Thank you!

-
- )} -
- ); -}; - -export default Banner; diff --git a/src/apps/earn/src/components/Banner/styles.scss b/src/apps/earn/src/components/Banner/styles.scss deleted file mode 100644 index 45711f32c..000000000 --- a/src/apps/earn/src/components/Banner/styles.scss +++ /dev/null @@ -1,65 +0,0 @@ -@import "@earn/styles/variables"; -@import "@earn/styles/mixins"; - -.banner { - display: flex; - justify-content: flex-start; - flex-wrap: wrap; - - width: 100%; - background: linear-gradient(90deg, $tc-blue 0%, $tc-turquoise 100%); - border-radius: 10px; - margin-bottom: 22px; - color: $tc-white; - padding-left: 28px; - - .header { - display: flex; - width: 100%; - justify-content: space-between; - flex-direction: row; - align-items: center; - @include roboto-bold; - line-height: 25px; - min-height: 50px; - font-size: 20px; - text-transform: uppercase; - } - - .chevron { - margin-right: 20px; - margin-top: 5px; - - &.expanded { - transform: rotate(180deg); - } - - &:hover { - cursor: pointer; - } - } - - .content { - display: flex; - flex-direction: column; - justify-content: flex-start; - font-size: 16px; - margin-bottom: 24px; - width: 85%; - - h3 { - font-size: 15px; - font-weight: bold; - margin-top: 15px; - } - - p { - margin-top: 15px; - } - - a { - text-decoration: underline; - } - - } -} diff --git a/src/apps/earn/src/components/Checkbox/index.jsx b/src/apps/earn/src/components/Checkbox/index.jsx deleted file mode 100644 index 6325007b5..000000000 --- a/src/apps/earn/src/components/Checkbox/index.jsx +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Checkbox component. - */ -import React, { useRef, useState, useEffect } from "react"; -import PT from "prop-types"; -import _ from "lodash"; - -import config from "../../config"; -import iconCheckL from "../../assets/icons/checkmark-large.png"; -import iconCheckM from "../../assets/icons/checkmark-medium.png"; -import iconCheckS from "../../assets/icons/checkmark-small.png"; - -import styles from "./styles.scss"; -import { styled as styledCss } from "@earn/utils"; -const styled = styledCss(styles) - -function Checkbox({ checked, onChange, size, errorMsg }) { - const [checkedInternal, setCheckedInternal] = useState(checked); - let sizeStyle = size === "lg" ? "lgSize" : null; - const imgSrc = - size === "xs" ? iconCheckS : size === "sm" ? iconCheckM : iconCheckL; - if (!sizeStyle) { - sizeStyle = size === "xs" ? "xsSize" : "smSize"; - } - const delayedOnChange = useRef( - _.debounce((q, cb) => cb(q), config.GUIKIT.DEBOUNCE_ON_CHANGE_TIME) // eslint-disable-line no-undef - ).current; - - useEffect(() => { - setCheckedInternal(checked); - }, [checked]); - - return ( - - ); -} - -Checkbox.defaultProps = { - checked: false, - onChange: () => {}, - size: "sm", - errorMsg: "", -}; - -Checkbox.propTypes = { - checked: PT.bool, - onChange: PT.func, - size: PT.oneOf(["xs", "sm", "lg"]), - errorMsg: PT.string, -}; - -export default Checkbox; diff --git a/src/apps/earn/src/components/Checkbox/styles.scss b/src/apps/earn/src/components/Checkbox/styles.scss deleted file mode 100644 index 1f78c4dcf..000000000 --- a/src/apps/earn/src/components/Checkbox/styles.scss +++ /dev/null @@ -1,117 +0,0 @@ -@import "@earn/styles/variables"; -@import "@earn/styles/GUIKit/default"; - -/* Create a custom checkbox */ -.checkmark { - position: absolute; - top: 0; - left: 0; - background-color: $tc-white; - border: 1px solid $gui-kit-gray-30; - - &.haveError { - border: 2px solid #ef476f; - } - - /* Create the checkmark/indicator (hidden when not checked) */ - .after { - position: absolute; - display: none; - left: 50%; - top: 50%; - } -} - -/* The container */ -.container { - display: block; - position: relative; - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - - // lg size - &.lgSize { - width: 25px; - height: 25px; - - .checkmark { - width: 25px; - height: 25px; - border-radius: 4px; - - .after { - margin-left: -9px; - margin-top: -7px; - } - } - } - - // sm size - &.smSize { - width: 20px; - height: 20px; - - .checkmark { - width: 20px; - height: 20px; - border-radius: 3px; - - .after { - margin-left: -7px; - margin-top: -6px; - } - } - } - - // xs size - &.xsSize { - width: 15px; - height: 15px; - - .checkmark { - width: 15px; - height: 15px; - border-radius: 2px; - - .after { - margin-left: -6px; - margin-top: -4px; - } - } - } - - /* Hide the browser's default checkbox */ - input { - position: absolute; - opacity: 0; - cursor: pointer; - height: 0; - width: 0; - - /* When the checkbox is checked, add a blue background */ - &:checked ~ .checkmark { - background-color: $gui-kit-level-2; - border: none; - box-shadow: inset 0 1px 2px 0 rgba(0, 0, 0, 0.29); - - /* Show the checkmark when checked */ - .after { - display: block; - } - } - } -} - -.errorMessage { - display: block; - - @include errorMessage; - - position: absolute; - white-space: nowrap; - margin: 35px 0 0 0; - color: #ef476f; -} diff --git a/src/apps/earn/src/components/CurrencyField/index.jsx b/src/apps/earn/src/components/CurrencyField/index.jsx deleted file mode 100644 index f5779ec3c..000000000 --- a/src/apps/earn/src/components/CurrencyField/index.jsx +++ /dev/null @@ -1,152 +0,0 @@ -import { useCallback, useEffect, useState } from "react"; -import PT from "prop-types"; -import debounce from "lodash/debounce"; -import { - convertNumberStringToNumber, - integerFormatter, - isValidNumberString, -} from "../../utils/gigs/formatting"; -import { DEBOUNCE_ON_CHANGE_TIME } from "../../constants/index.js"; -import { PAYMENT_MAX_VALUE } from "../../constants/gigs"; -import { isNumber } from "lodash"; - -import styles from "./styles.scss"; - -/** - * Displays currency input field. - * - * @param {Object} props component properties - * @param {string} [props.className] class name added to root element - * @param {string} props.currency currency abbreviation - * @param {string} props.id id for input element - * @param {string} props.label field label - * @param {number} [props.maxValue] maximum value - * @param {number} [props.minValue] minimum value - * @param {string} props.name name for input element - * @param {(v: string) => void} props.onChange function called when input value changes - * @param {(v: number) => void} props.onCommit function called after some delay - * when input value changes and it is valid - * @param {boolean} [props.required] whether the field required non-empty value - * @param {*} props.value input value - * @returns {JSX.Element} - */ -const CurrencyField = ({ - className, - currency, - id, - label, - maxValue, - minValue, - name, - rangeError, - onError, - onChange, - onCommit, - required = false, - value, -}) => { - const [error, setError] = useState(""); - - // eslint-disable-next-line react-hooks/exhaustive-deps - const checkValue = useCallback( - debounce( - (value) => { - if (!value) { - if (required) { - setError("Required"); - } - return; - } - let num = convertNumberStringToNumber(value); - let min = isNumber(minValue) - ? minValue - : convertNumberStringToNumber(minValue); - let max = isNumber(maxValue) - ? maxValue - : convertNumberStringToNumber(maxValue); - if (isValidNumberString(value) && !isNaN(num)) { - setError(""); - if (num < min || num > max) { - onError("Please put in a valid range."); - } else { - onError(""); - onCommit(num); - } - } else { - onError(""); - setError("Invalid format"); - } - }, - DEBOUNCE_ON_CHANGE_TIME, - { leading: false } - ), - [maxValue, minValue, onCommit, required] - ); - - const onChangeValue = useCallback( - (event) => { - let value = event.target.value; - setError(""); - if (value && isValidNumberString(value)) { - let num = convertNumberStringToNumber(value); - if (!isNaN(num)) { - value = integerFormatter.format(num); - } - } - onChange(value); - checkValue(value); - }, - [checkValue, onChange] - ); - - useEffect(() => { - checkValue(value); - }, [checkValue, value]); - - return ( -
- {label && ( - - )} -
- - {currency} - {error && {error}} -
-
- ); -}; - -CurrencyField.defaultProps = { - maxValue: PAYMENT_MAX_VALUE, - minValue: 0, -}; - -CurrencyField.propTypes = { - className: PT.string, - currency: PT.string.isRequired, - id: PT.string.isRequired, - label: PT.string, - rangeError: PT.string, - maxValue: PT.oneOfType([PT.number, PT.string]), - minValue: PT.oneOfType([PT.number, PT.string]), - name: PT.string.isRequired, - onError: PT.func.isRequired, - onChange: PT.func.isRequired, - onCommit: PT.func.isRequired, - required: PT.bool, - value: PT.oneOfType([PT.number, PT.string]).isRequired, -}; - -export default CurrencyField; diff --git a/src/apps/earn/src/components/CurrencyField/styles.scss b/src/apps/earn/src/components/CurrencyField/styles.scss deleted file mode 100644 index fed55f9d9..000000000 --- a/src/apps/earn/src/components/CurrencyField/styles.scss +++ /dev/null @@ -1,70 +0,0 @@ -@import '../../styles/variables'; -@import '../../styles/mixins'; - -.container { - position: relative; -} - -.label { - z-index: 2; - position: absolute; - left: 9px; - top: 0; - padding: 0 7px; - font-size: 12px; - line-height: 12px; - transform: translateY(-50%); - color: $control-border-color; - background-color: #fff; -} - -.field { - z-index: 1; - position: relative; - display: flex; - align-items: center; - border: 1px solid $control-border-color; - border-radius: $control-border-radius; - height: 40px; - background-color: #fff; - - &.has-errors { - border-color: #EF476F; - } -} - -input.input { - flex: 1 1 0; - margin: 0; - border: none !important; - padding: 8px 0 8px 15px; - height: 22px; - font-size: 14px; - line-height: 22px; - background: none; - outline: none !important; - box-shadow: none !important; - width: 60px; -} - -.currency { - flex: 0 0 auto; - margin: 0 10px 0 6px; - font-size: 13px; - line-height: 22px; - text-transform: uppercase; - color: $control-border-color; -} - -.error { - display: block; - position: absolute; - left: 15px; - top: 100%; - margin-top: 2px; - font-size: 12px; - line-height: 17px; - white-space: nowrap; - color: $body-color; - background-color: #fff; -} diff --git a/src/apps/earn/src/components/DateRangePicker/DateInput/index.jsx b/src/apps/earn/src/components/DateRangePicker/DateInput/index.jsx deleted file mode 100644 index 881715340..000000000 --- a/src/apps/earn/src/components/DateRangePicker/DateInput/index.jsx +++ /dev/null @@ -1,167 +0,0 @@ -import React, { useRef, useEffect, useState } from "react"; -import PT from "prop-types"; -import _ from "lodash"; -import TextInput from "../../TextInput"; -import { ReactComponent as CalendarIcon} from "../../../assets/icons/icon-calendar.svg"; - -import styles from "./styles.scss"; -import { styled as styledCss } from "../../../utils"; -const styled = styledCss(styles) - -const DateInput = ({ - id, - isStartDateActive, - startDateString, - onStartDateChange, - onStartDateFocus, - isEndDateActive, - endDateString, - onEndDateChange, - onEndDateFocus, - error, - onClickCalendarIcon, - onStartEndDateChange, - placeholder, - enterToSubmit, -}) => { - const ref = useRef(null); - const [focused, setFocused] = useState(false); - - let rangeText; - if (startDateString && endDateString) { - rangeText = `${startDateString} - ${endDateString}`; - } else { - rangeText = `${startDateString}${endDateString}`; - } - - useEffect(() => { - const inputElement = ref.current.querySelector("input"); - const onFocus = () => setFocused(true); - const onBlur = () => setFocused(false); - - inputElement.addEventListener("focus", onFocus); - inputElement.addEventListener("blur", onBlur); - - return () => { - inputElement.removeEventListener("focus", onFocus); - inputElement.removeEventListener("blur", onBlur); - }; - }, []); - - const latestPropsRef = useRef(null); - latestPropsRef.current = { onStartDateFocus, onEndDateFocus }; - - useEffect(() => { - const inputElement = ref.current.querySelector("input"); - - let caretPosition; - if (inputElement.selectionDirection === "forward") { - caretPosition = inputElement.selectionEnd; - } else { - caretPosition = inputElement.selectionStart; - } - - if (caretPosition < 14) { - latestPropsRef.current.onStartDateFocus(); - } else { - latestPropsRef.current.onEndDateFocus(); - } - }, [focused]); - - const onChangeRangeText = (value) => { - let [newStartDateString = "", newEndDateString = ""] = value - .trim() - .split("-"); - newStartDateString = newStartDateString.trim(); - newEndDateString = newEndDateString.trim(); - - if ( - newStartDateString !== startDateString && - newEndDateString !== endDateString - ) { - const event = { - startDateString: newStartDateString, - endDateString: newEndDateString, - }; - onStartEndDateChange(event); - onStartDateFocus(); - } else if (newStartDateString !== startDateString) { - onStartDateFocus(); - onStartDateChange(newStartDateString); - } else if (newEndDateString !== endDateString) { - onEndDateFocus(); - onEndDateChange(newEndDateString); - if (newEndDateString === "") { - onStartDateFocus(); - } - } - }; - - const onChangeRangeTextDebounced = useRef(_.debounce((f) => f(), 1000)); - - const onClickIcon = (event) => { - event.stopPropagation(); - - const inputElement = ref.current.querySelector("input"); - - let caretPosition; - if (inputElement.selectionDirection === "forward") { - caretPosition = inputElement.selectionEnd; - } else { - caretPosition = inputElement.selectionStart; - } - - if (caretPosition < 14 || caretPosition === rangeText.length) { - onClickCalendarIcon("start"); - } else { - onClickCalendarIcon("end"); - } - }; - - const label = startDateString ? "From" : endDateString ? "To" : "From"; - - return ( -
-
- { - if (!enterToSubmit) { - onChangeRangeTextDebounced.current(() => - onChangeRangeText(value) - ); - } - }} - onEnterKey={(value) => { - onChangeRangeText(value); - }} - placeholder={placeholder} - /> - -
-
{error}
-
- ); -}; - -DateInput.propTypes = { - id: PT.string, - isStartDateActive: PT.bool, - startDateString: PT.string, - onStartDateChange: PT.func, - onStartDateFocus: PT.func, - isEndDateActive: PT.bool, - endDateString: PT.string, - onEndDateChange: PT.func, - onEndDateFocus: PT.func, - error: PT.string, - onClickCalendarIcon: PT.func, - onStartEndDateChange: PT.func, - placeholder: PT.string, -}; - -export default DateInput; diff --git a/src/apps/earn/src/components/DateRangePicker/DateInput/styles.scss b/src/apps/earn/src/components/DateRangePicker/DateInput/styles.scss deleted file mode 100644 index 8b637437b..000000000 --- a/src/apps/earn/src/components/DateRangePicker/DateInput/styles.scss +++ /dev/null @@ -1,49 +0,0 @@ -@import "@earn/styles/variables"; -@import "@earn/styles/mixins"; - -.container { - &.isError { - input { - border: 1px solid $tc-level-5; - } - - .errorHint { - display: block; - color: $tc-level-5; - font-size: 12px; - padding: 4px 0; - height: 20px; - } - } -} - -.date-range-input { - margin-top: -12px; - font-size: $font-size-sm; - - @include textinput-show-label; -} - -.input-group { - position: relative; - - .icon { - position: absolute; - top: 22px; - right: 14px; - display: block; - cursor: pointer; - appearance: none; - background: none; - border: none; - padding: 0; - } - - input { - padding-right: 46px !important; - } -} - -.errorHint { - display: none; -} diff --git a/src/apps/earn/src/components/DateRangePicker/helpers.js b/src/apps/earn/src/components/DateRangePicker/helpers.js deleted file mode 100644 index df745c982..000000000 --- a/src/apps/earn/src/components/DateRangePicker/helpers.js +++ /dev/null @@ -1,136 +0,0 @@ -import { useEffect, useRef, useState } from "react"; -import moment from "moment"; - -/** - * Check whether the dates are same - * @param {Date} date1. - * @param {Date} date2. - * @return {boolean} - */ -export function isSameDay(date1, date2) { - if (!date1 || !date2) return false; - return moment(date1).isSame(moment(date2), "day"); -} - -/** - * Check whether the date1 is occur before date2 - * @param {Date} date1. - * @param {Date} date2. - * @return {boolean} - */ -export function isBeforeDay(date1, date2) { - if (!date1 || !date2) return false; - return moment(date1).isBefore(moment(date2), "day"); -} - -/** - * Check whether the date1 is occur after date2 - * @param {Date} date1. - * @param {Date} date2. - * @return {boolean} - */ -export function isAfterDay(date1, date2) { - if (!date1 || !date2) return false; - return moment(date1).isAfter(moment(date2), "day"); -} - -const staticRangeHandler = { - range: {}, - isSelected(range) { - const definedRange = this.range(); - return ( - isSameDay(range.startDate, definedRange.startDate) && - isSameDay(range.endDate, definedRange.endDate) - ); - }, -}; - -/** - * Create defined date ranges - * @return {object[]} list of defined ranges - */ -export function createStaticRanges() { - const today = moment(); - const endOfToday = today.set({ - hour: 23, - minute: 59, - second: 59, - millisecond: 999, - }); - const pastWeek = endOfToday.clone().subtract(1, "week"); - const pastMonth = endOfToday.clone().subtract(1, "month"); - const past6Months = endOfToday.clone().subtract(6, "month"); - const pastYear = endOfToday.clone().subtract(1, "year"); - - const ranges = [ - { - label: "Past Week", - range: () => ({ - startDate: pastWeek.startOf("day").toDate(), - endDate: endOfToday.toDate(), - }), - }, - { - label: "Past Month", - range: () => ({ - startDate: pastMonth.startOf("day").toDate(), - endDate: endOfToday.toDate(), - }), - }, - { - label: "Past 6 Months", - range: () => ({ - startDate: past6Months.startOf("day").toDate(), - endDate: endOfToday.toDate(), - }), - }, - { - label: "Past Year", - range: () => ({ - startDate: pastYear.startOf("day").toDate(), - endDate: endOfToday.toDate(), - }), - }, - ]; - - return ranges.map((range) => ({ ...staticRangeHandler, ...range })); -} - -/** - * React hook for checking if the click is from outside the reference - * @param {boolean} initialIsVisible true if visible and false if hidden - */ -export function useComponentVisible(initialIsVisible) { - const [isComponentVisible, setIsComponentVisible] = useState( - initialIsVisible - ); - const ref = useRef(null); - - const handleHideDropdown = (event) => { - if (event.key === "Escape") { - setIsComponentVisible(false); - } - }; - - const handleClickOutside = (event) => { - if (ref.current && !ref.current.contains(event.target)) { - setIsComponentVisible(false); - } - }; - - useEffect(() => { - document.addEventListener("keydown", handleHideDropdown, true); - document.addEventListener("click", handleClickOutside, true); - return () => { - document.removeEventListener("keydown", handleHideDropdown, true); - document.removeEventListener("click", handleClickOutside, true); - }; - }); - - return { ref, isComponentVisible, setIsComponentVisible }; -} - -export default { - useComponentVisible, - createStaticRanges, -}; diff --git a/src/apps/earn/src/components/DateRangePicker/index.jsx b/src/apps/earn/src/components/DateRangePicker/index.jsx deleted file mode 100644 index fbd840320..000000000 --- a/src/apps/earn/src/components/DateRangePicker/index.jsx +++ /dev/null @@ -1,651 +0,0 @@ -import React, { useState, useEffect } from "react"; -import moment from "moment"; -import { DateRangePicker as ReactDateRangePicker } from "react-date-range"; -import PropTypes from "prop-types"; - -import styles from "./style.scss"; -import { styled as styledCss } from "@earn/utils"; - -import DateInput from "./DateInput"; - -import { - useComponentVisible, - createStaticRanges, - isSameDay, - isAfterDay, - isBeforeDay, -} from "./helpers"; - -const styled = styledCss(styles); - -function DateRangePicker(props) { - const { id, range, onChange, placeholder, enterToSubmit = false } = props; - - const [rangeString, setRangeString] = useState({ - startDateString: "", - endDateString: "", - }); - const [activeDate, setActiveDate] = useState(null); - const [preview, setPreview] = useState(null); - const [focusedRange, setFocusedRange] = useState([0, 0]); - const [errors, setErrors] = useState({ - startDate: undefined, - endDate: undefined, - }); - - const { - ref: calendarRef, - isComponentVisible, - setIsComponentVisible, - } = useComponentVisible(false); - - const isStartDateFocused = focusedRange[1] === 0; - const isEndDateFocused = focusedRange[1] === 1; - - useEffect(() => { - setRangeString({ - startDateString: range.startDate - ? moment(range.startDate).format("MMM D, YYYY") - : "", - endDateString: range.endDate - ? moment(range.endDate).format("MMM D, YYYY") - : "", - }); - }, [range]); - - /** - * Handle end date change on user input - * After user input the end date via keyboard, validate it then update the range state - * @param {Object} e Input Event. - */ - const onEndDateChange = (value) => { - const endDateString = value; - const endDate = moment(endDateString, "MMM D, YYYY", true); - const startDate = moment(rangeString.startDateString, "MMM D, YYYY", true); - - if (endDate.isValid() && isBeforeDay(endDate, startDate)) { - setErrors({ - ...errors, - endDate: "Range Error", - }); - } else if (endDate.isValid()) { - onChange({ - endDate: endDate.toDate(), - startDate: range.startDate, - }); - - setErrors({ - ...errors, - endDate: "", - }); - - setRangeString({ - ...rangeString, - endDateString: endDate.format("MMM D, YYYY"), - }); - } else if (endDateString === "") { - onChange({ - endDate: null, - startDate: range.startDate, - }); - - setErrors({ - ...errors, - endDate: "", - }); - } else { - setErrors({ - ...errors, - endDate: "Invalid End Date Format", - }); - - setRangeString({ - ...rangeString, - endDateString, - }); - } - }; - - /** - * Handle start date change on user input - * After user input the start date via keyboard, validate it then update the range state - * @param {Object} e Input Event. - */ - const onStartDateChange = (value) => { - const startDateString = value; - const startDate = moment(startDateString, "MMM D, YYYY", true); - const endDate = moment(rangeString.endDateString, "MMM D, YYYY", true); - - if ( - startDate.isValid() && - endDate.isValid() && - isAfterDay(startDate, endDate) - ) { - setErrors({ - ...errors, - startDate: "Range Error", - }); - } else if (startDate.isValid()) { - onChange({ - endDate: range.endDate, - startDate: startDate.toDate(), - }); - - setErrors({ - ...errors, - startDate: "", - }); - - setRangeString({ - ...rangeString, - startDateString: startDate.format("MMM D, YYYY"), - }); - } else if (startDateString === "") { - onChange({ - endDate: range.endDate, - startDate: null, - }); - - setErrors({ - ...errors, - startDate: "", - }); - } else { - setErrors({ - ...errors, - startDate: "Invalid Start Date Format", - }); - - setRangeString({ - ...rangeString, - startDateString, - }); - } - }; - - const onStartEndDateChange = ({ startDateString, endDateString }) => { - const startDate = moment(startDateString, "MMM D, YYYY", true); - const endDate = moment(endDateString, "MMM D, YYYY", true); - - if ( - startDate.isValid() && - endDate.isValid() && - isBeforeDay(endDate, startDate) - ) { - setErrors({ - ...errors, - endDate: "Range Error", - }); - } else if (startDate.isValid() && endDate.isValid()) { - onChange({ - endDate: endDate.toDate(), - startDate: startDate.toDate(), - }); - setErrors({ - startDate: "", - endDate: "", - }); - } else if (startDate.isValid()) { - onChange({ - endDate: null, - startDate: startDate.toDate(), - }); - setErrors({ - ...errors, - endDate: "Invalid End Date Format", - }); - } else if (endDate.isValid()) { - onChange({ - endDate: endDate.toDate(), - startDate: null, - }); - setErrors({ - ...errors, - startDate: "Invalid Start Date Format", - }); - } else if (startDateString === "" && endDateString === "") { - onChange({ - endDate: null, - startDate: null, - }); - setErrors({ - startDate: "", - endDate: "", - }); - } else if (startDateString === "") { - onChange({ - endDate: endDate.toDate(), - startDate: null, - }); - - setErrors({ - ...errors, - startDate: "", - }); - } else if (endDateString === "") { - onChange({ - endDate: null, - startDate: startDate.toDate(), - }); - - setErrors({ - ...errors, - endDate: "", - }); - } else { - onChange({ - endDate: null, - startDate: null, - }); - setErrors({ - startDate: "Invalid Start Date Format", - endDate: "Invalid End Date Format", - }); - } - }; - - /** - * Trigger to open calendar modal on calendar icon in start date input - */ - const onIconClickStartDate = () => { - const calendarIcon = document.querySelector(id); - if (calendarIcon) { - calendarIcon.blur(); - } - setFocusedRange([0, 0]); // set current focused input to start date - setActiveDate(null); - setIsComponentVisible(true); - setPreview(null); - }; - - /** - * Trigger to open calendar modal on calendar icon in end date input - */ - const onIconClickEndDate = () => { - const calendarIcon = document.querySelector(id); - if (calendarIcon) { - calendarIcon.blur(); - } - setFocusedRange([0, 1]); // set current focused input to end date - setActiveDate(null); - setIsComponentVisible(true); - setPreview(null); - }; - - const onReset = (presetRange) => { - let newStartDate; - let newEndDate; - - if (presetRange) { - newStartDate = presetRange.startDate; - newEndDate = presetRange.endDate; - } - - setFocusedRange([0, 0]); - - setErrors({ - startDate: "", - endDate: "", - }); - - setRangeString({ - startDateString: newStartDate - ? moment(newStartDate).format("MMM D, YYYY") - : "", - endDateString: newEndDate ? moment(newEndDate).format("MMM D, YYYY") : "", - }); - - onChange({ - startDate: newStartDate ? moment(newStartDate) : null, - endDate: newEndDate ? moment(newEndDate) : null, - }); - - setIsComponentVisible(false); - }; - - /** - * Event handler on date selection changes - * @param {Object} newRange nnew range that has endDate and startDate data - */ - const onDateRangePickerChange = (newRange) => { - let newEndDate = newRange.endDate; - let newStartDate = newRange.startDate; - const isUseKeyPress = focusedRange[0] !== 0; - - if (isUseKeyPress) { - setFocusedRange([0, focusedRange[1]]); - } - - let shouldCloseCalendar = true; - let shouldOpenNextCalendar = false; - - // User is active on start date calendar modal - if ( - isStartDateFocused && - (isUseKeyPress || isSameDay(newStartDate, newEndDate)) - ) { - if (range.endDate && isAfterDay(newStartDate, range.endDate)) return; - newEndDate = range.endDate; - shouldCloseCalendar = false; - shouldOpenNextCalendar = true; - setErrors({ - ...errors, - startDate: "", - }); - } else if ( - isEndDateFocused && - (isUseKeyPress || isSameDay(newEndDate, newStartDate)) - ) { - if (range.startDate && isBeforeDay(newEndDate, range.startDate)) return; - newStartDate = range.startDate; - setErrors({ - ...errors, - endDate: "", - }); - } else { - setErrors({ - startDate: "", - endDate: "", - }); - } - - // Emit the payload - - setRangeString({ - startDateString: newStartDate - ? moment(newStartDate).format("MMM D, YYYY") - : "", - endDateString: newEndDate ? moment(newEndDate).format("MMM D, YYYY") : "", - }); - - onChange({ - startDate: newStartDate, - endDate: newEndDate ? moment(newEndDate).endOf("day").toDate() : null, - }); - - if (shouldOpenNextCalendar) { - setFocusedRange([0, 1]); - } - if (shouldCloseCalendar) { - setIsComponentVisible(false); - } - }; - - /** - * Event handler on preview change - * @param {Date} date current date which user hover - */ - const onPreviewChange = (date) => { - if (!(date instanceof Date)) { - setPreview(null); - - // --- - // workaround for fixing issue 132: - // - set the active range's background to transparent color - // to prevent the calendar auto focusing on the day of today by default when no - // start date nor end date are set. - // - does not set focus on the selection range when mouse leaves. - // --- - - // setActiveDate(null); - // if (range.startDate || range.endDate) { - // setFocusedRange([0, focusedRange[1]]); - // } - return; - } - - if (isStartDateFocused && date) { - setPreview({ - startDate: date, - endDate: range.endDate || date, - }); - } else if (isEndDateFocused && date) { - setPreview({ - startDate: range.startDate || date, - endDate: date, - }); - } - - setActiveDate(date); - setFocusedRange([1, focusedRange[1]]); - }; - - /** - * Event handler for user keypress - * @param {Event} e Keyboard event - */ - const handleKeyDown = (e) => { - let currentActiveDate = activeDate; - if (!currentActiveDate) { - currentActiveDate = moment().startOf("month").toDate(); - - if (isStartDateFocused && range.startDate) { - currentActiveDate = range.startDate; - } else if (isEndDateFocused && (range.startDate || range.endDate)) { - currentActiveDate = range.endDate || range.startDate; - } - } - - switch (e.key) { - case "Down": - case "ArrowDown": - currentActiveDate = moment(currentActiveDate).add(7, "days").toDate(); - onPreviewChange(currentActiveDate); - break; - case "Up": - case "ArrowUp": - currentActiveDate = moment(currentActiveDate) - .subtract(7, "days") - .toDate(); - onPreviewChange(currentActiveDate); - break; - case "Left": - case "ArrowLeft": - currentActiveDate = moment(currentActiveDate) - .subtract(1, "days") - .toDate(); - onPreviewChange(currentActiveDate); - break; - case "Right": - case "ArrowRight": - currentActiveDate = moment(currentActiveDate).add(1, "days").toDate(); - onPreviewChange(currentActiveDate); - break; - case "Enter": - if (activeDate) { - onDateRangePickerChange({ - startDate: isStartDateFocused ? activeDate : range.startDate, - endDate: isEndDateFocused ? activeDate : range.endDate, - }); - } - break; - case "Esc": - case "Escape": - setIsComponentVisible(false); - break; - default: - return; // Quit when this doesn't handle the key event. - } - - e.preventDefault(); - }; - - /** - * User Effect for listening to keypress event - */ - useEffect(() => { - if (isComponentVisible) { - document.addEventListener("keydown", handleKeyDown, true); - } else { - document.removeEventListener("keydown", handleKeyDown, true); - } - - return () => { - document.removeEventListener("keydown", handleKeyDown, true); - }; - }); - - /** - * Focus the calendar to the given date, - * so for example, if the user click menu for end date it will open the calendar - * and focus it to current end date - */ - const getShownDate = () => { - if (activeDate) { - return activeDate; - } - - if (isStartDateFocused) { - if (preview) return preview.startDate; - return range.startDate || moment().toDate(); - } - if (preview) return preview.endDate; - return range.endDate || moment().toDate(); - }; - - /** - * Disable the days that cannot be selected - */ - const disabledDay = (date) => { - if (isStartDateFocused) { - return range.endDate ? moment(date).isAfter(range.endDate, "day") : false; - } - return range.startDate - ? moment(date).isBefore(range.startDate, "day") - : false; - }; - - /** - * Get Date Ranges - */ - const getRanges = () => { - if (activeDate) { - return [ - { - ...range, - key: "selection", - color: "#0AB88A", - }, - { - startDate: activeDate, - endDate: activeDate, - key: "active", - color: preview ? "#D8FDD8" : "#D8FDD800", - }, - ]; - } - return [ - { - ...range, - key: "selection", - color: "#0AB88A", - }, - ]; - }; - - /** - * Check whether the preview invalid - */ - const isInvalidPreview = () => { - if (!preview) return false; - if (isStartDateFocused) { - return isAfterDay(preview.startDate, range.endDate); - } - return isBeforeDay(preview.endDate, range.startDate); - }; - - const className = `${(focusedRange[1] === 1 && styles.endDate) || ""} ${ - (range.startDate && range.endDate && styles.isRange) || "" - } ${(isInvalidPreview() && styles.isInvalidPreview) || ""} ${ - ((errors.startDate || errors.endDate) && styles.isErrorInput) || "" - }`; - - return ( -
-
- setFocusedRange([0, 0])} - isEndDateActive={focusedRange[1] === 1 && isComponentVisible} - endDateString={rangeString.endDateString} - onEndDateChange={onEndDateChange} - onEndDateFocus={() => setFocusedRange([0, 1])} - error={errors.startDate || errors.endDate} - onClickCalendarIcon={(event) => { - event === "start" ? onIconClickStartDate() : onIconClickEndDate(); - }} - onStartEndDateChange={onStartEndDateChange} - placeholder={placeholder} - enterToSubmit={enterToSubmit} - /> -
-
- {isComponentVisible && ( -
- { - if (!preview) { - onReset(item.selection || item.active); - } else { - onDateRangePickerChange(item.selection || item.active); - } - }} - dateDisplayFormat="MM/dd/yyyy" - showDateDisplay={false} - staticRanges={createStaticRanges()} - inputRanges={[]} - moveRangeOnFirstSelection={false} - initialFocusedRange={[0, 1]} - showMonthArrow={false} - ranges={getRanges()} - disabledDay={disabledDay} - shownDate={getShownDate()} - preview={preview} - onPreviewChange={onPreviewChange} - /> -
- - -
-
- )} -
-
- ); -} - -// It use https://www.npmjs.com/package/react-date-range internally -// Check the docs for further options - -DateRangePicker.propTypes = { - id: PropTypes.string, - range: PropTypes.object.isRequired, - onChange: PropTypes.func.isRequired, - placeholder: PropTypes.string, -}; - -DateRangePicker.defaultProps = { - id: "input-date-range-calendar-icon", - placeholder: "Date range", -}; - -export default DateRangePicker; diff --git a/src/apps/earn/src/components/DateRangePicker/style.scss b/src/apps/earn/src/components/DateRangePicker/style.scss deleted file mode 100644 index b00f99541..000000000 --- a/src/apps/earn/src/components/DateRangePicker/style.scss +++ /dev/null @@ -1,463 +0,0 @@ -@import "@earn/styles/variables"; -@import "@earn/styles/mixins"; - -$green: #D8FDD8; -$darkGreen: #0AB88A;; - -@mixin phone { - @media (max-width: #{$screen-sm - 1px}) { - @content; - } -} - -@mixin tablet { - @media (min-width: #{$screen-sm}) and (max-width: #{$screen-lg - 1px}) { - @content; - } -} - -.isErrorInput { - :global .rdrDateRangePickerWrapper { - margin-top: -20px !important; - } -} - -.isRange { - :global { - .rdrDay { - .rdrInRange { - background: $green !important; - } - - .rdrStartEdge, - .rdrEndEdge { - background: $green !important; - } - } - } -} - -.isInvalidPreview { - :global { - .rdrDayInPreview, - .rdrDayStartPreview, - .rdrDayEndPreview, - .rdrStartEdge.rdrEndEdge { - border: none !important; - } - } -} - -.dateRangePicker { - display: block; - position: relative; - color: $tc-black; - - :global { - @include phone { - .rdrDateRangePickerWrapper { - position: relative !important; - width: 100% !important; - flex-direction: column-reverse; - align-items: center; - justify-content: flex-end; - padding: 0 20px; - border-radius: 0 !important; - - .rdrDefinedRangesWrapper { - .rdrStaticRanges { - display: inline-flex; - flex-direction: row; - justify-content: space-around; - flex-wrap: wrap; - margin-top: 10px !important; - width: calc(100% - 40px); - - .rdrStaticRange { - width: 50%; - } - - .rdrStaticRangeLabel { - font-size: 14px; - } - - > button:hover .rdrStaticRangeLabel { - background-color: $green; - } - } - } - - .rdrDateRangeWrapper { - width: 100%; - - .rdrMonthAndYearWrapper { - padding-top: 0; - - .rdrMonthAndYearPickers select { - font-size: 16px; - } - } - - .rdrMonth { - width: 100%; - } - - .rdrDayNumber { - font-size: 15px; - } - - .rdrDayToday .rdrDayNumber span:after { - bottom: -4px; - left: calc(50% + 2px); - } - - .rdrMonths { - .rdrWeekDay { - font-size: 15px; - margin-bottom: 10px; - } - - .rdrDay { - .rdrDayStartPreview { - left: 17%; - } - - .rdrDayEndPreview { - right: 17%; - } - } - } - } - } - } - - .rdrDateRangePickerWrapper { - z-index: 15; - position: relative; - // background: $tc-white; - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; - overflow: hidden; - width: 455px; - - .rdrDefinedRangesWrapper { - width: 100%; - border: none; - - .rdrStaticRanges { - margin-top: 60px; - - .rdrStaticRange { - border: none; - } - - button { - color: $tc-gray-80; - - @include roboto-regular; - } - - button:hover .rdrStaticRangeLabel { - background-color: $green; - } - } - - .rdrInputRanges { - display: none; - } - } - } - - .rdrMonthAndYearPickers select { - background: url("data:image/svg+xml;utf8,") no-repeat right 8px center; - - option { - background: $white; - } - - &:hover { - background-color: transparent; - } - } - - .rdrMonths { - border-top: 1px solid $tc-gray-20; - border-bottom: 1px solid $tc-gray-20; - - .rdrWeekDay { - font-weight: bold; - color: #484848; - } - - .rdrDayDisabled { - background: transparent; - - .rdrDayInPreview, - .rdrDayStartPreview, - .rdrDayEndPreview, - .rdrStartEdge.rdrEndEdge { - filter: none !important; - -webkit-filter: none !important; - } - } - - .rdrDayPassive { - .rdrInRange, - .rdrStartEdge, - .rdrEndEdge, - .rdrSelected, - .rdrDayStartPreview, - .rdrDayInPreview, - .rdrDayEndPreview { - display: block; - } - - .rdrInRange ~ .rdrDayNumber span { - opacity: 0.5; - } - } - - .rdrDayEndOfMonth .rdrInRange, - .rdrDayEndOfMonth .rdrStartEdge, - .rdrDayEndOfWeek .rdrInRange, - .rdrDayEndOfWeek .rdrStartEdge { - right: 0; - } - - .rdrDayStartOfMonth .rdrInRange, - .rdrDayStartOfMonth .rdrEndEdge, - .rdrDayStartOfWeek .rdrInRange, - .rdrDayStartOfWeek .rdrEndEdge { - left: 0; - } - - .rdrDayStartOfWeek { - .rdrDayInPreview { - left: 0; - border-radius: 0 !important; - border-left: none; - } - } - - .rdrDayEndOfWeek { - .rdrDayInPreview { - right: 0; - border-radius: 0 !important; - border-right: none; - } - } - - .rdrDayStartOfMonth, - .rdrDayEndOfMonth { - .rdrDayInPreview { - border-radius: 0; - border-left: 0; - border-right: 0; - } - } - - .rdrDay { - margin-bottom: 6px; - - .rdrInRange { - color: transparent; - background: transparent; - - & ~ .rdrDayNumber span { - color: $tc-black; - } - } - - .rdrDayStartPreview { - border-radius: 18px 0 0 18px; - height: 36px; - left: 4px; - border-right: none; - } - - .rdrDayEndPreview { - border-radius: 0 18px 18px 0; - height: 36px; - right: 4px; - border-left: none; - } - - .rdrDayStartPreview.rdrDayEndPreview, - .rdrDayHovered { - border-radius: 50%; - width: 36px; - height: 36px; - left: 4px; - border: 1px solid $green; - } - - .rdrSelected, - .rdrInRange, - .rdrStartEdge, - .rdrEndEdge { - top: 0; - bottom: 0; - border-radius: 0; - } - - .rdrDayInPreview, - .rdrDayStartPreview, - .rdrDayEndPreview { - border-color: $green; - top: 0; - bottom: 0; - } - - .rdrDayStartPreview, - .rdrDayEndPreview { - & ~ .rdrDayNumber span { - color: $tc-black; - } - } - - .rdrStartEdge { - background: transparent; - left: 50%; - - &::after { - content: ""; - position: absolute; - top: 0; - left: -18px; - border-radius: 50%; - width: 36px; - height: 36px; - background: currentColor; - z-index: 0; - } - } - - .rdrEndEdge { - background: transparent; - right: 50%; - - &::after { - content: ""; - position: absolute; - top: 0; - right: -18.2px; - border-radius: 50%; - width: 36px; - height: 36px; - background: currentColor; - z-index: 0; - } - } - - .rdrStartEdge.rdrEndEdge ~ .rdrDayNumber span { - color: $tc-black; - } - - .rdrDayNumber { - top: 0; - bottom: 0; - } - } - - .rdrDayStartOfWeek, - .rdrDayEndOfWeek { - border-radius: 0; - } - - .rdrDayToday .rdrDayNumber span:after { - bottom: -4px; - left: calc(50% + 2px); - background: $darkGreen !important; - } - } - } -} - -.dateInputWrapper { - position: relative; - text-align: left; -} - -.calendar-container { - @include roboto-regular; - - position: absolute; - top: calc(100% + 8px); -} - -.calendar-inner-container { - position: absolute; - padding: 0 18px 0 0; - text-align: right; - border: 1px solid $tc-gray-30; - border-radius: 4px; - background: $tc-white; - z-index: 10; - - @include phone { - width: 100%; - position: fixed; - top: 0; - left: 0; - right: 20px; - bottom: 0; - z-index: 15; - padding: 187px 0 0; - border: 0; - border-radius: 0; - background: rgba(#2A2A2A, 0.6); - - @include down(320px) { - padding: 40px 0 0; - } - - .calendar-footer { - margin: 0 20px; - padding: 20px 0; - } - } - - .calendar-footer { - background: $tc-white; - } - - .calendar-button { - @include roboto-bold; - - width: 71px; - height: 24px; - line-height: 22px; - margin: 9px 4px 16px; - padding: 0; - font-size: 10.5px; - text-align: center; - background: transparent; - border: 1px solid #ccc; - border-radius: 2px; - - @include phone { - width: 79px; - height: 26px; - line-height: 27px; - font-size: 12px; - margin: 0 12px 0; - } - } -} - -@include tablet { - .calendar-container, - .calendar-inner-container { - right: 0; - } - - :global { - .rdrDateRangePickerWrapper { - .calendar-container, - .calendar-inner-container { - right: 62px; - left: auto; - } - } - } -} diff --git a/src/apps/earn/src/components/Dropdown/index.jsx b/src/apps/earn/src/components/Dropdown/index.jsx deleted file mode 100644 index f4b9d2d6b..000000000 --- a/src/apps/earn/src/components/Dropdown/index.jsx +++ /dev/null @@ -1,199 +0,0 @@ -/** - * Dropdown component. - */ -import React, { useState, useRef, useEffect } from "react"; -import PT from "prop-types"; -import _ from "lodash"; -import ReactSelect, { components } from "react-select"; - -import config from "../../config"; - -import styles from "./styles.scss"; -import { styled as styledCss } from "@earn/utils"; -const styled = styledCss(styles) - -const Menu = (props) => { - return ( - - {props.children} - - ); -}; -const MenuList = (props) => { - return ( - - {props.children} - - ); -}; - -const CustomOption = (props) => { - return ( - - {props.children} - - ); -}; - -const ValueContainer = ({ children, ...props }) => ( - - {children} - -); - -const Input = (props) => { - return ( - - ); -}; - -const SingleValue = ({ children, ...props }) => ( - - {children} - -); - -const ControlComponent = (props) => ( - -); -const IndicatorSeparator = () => { - return null; -}; - -function Dropdown({ - className, - options, - label, - required, - placeholder, - onChange, - errorMsg, - searchable, - size, -}) { - const [internalOptions, setInternalOptions] = useState(options); - const selectedOption = _.find(internalOptions, { selected: true }); - const [focused, setFocused] = useState(false); - const delayedOnChange = useRef( - _.debounce((q, cb) => cb(q), config.GUIKIT.DEBOUNCE_ON_CHANGE_TIME) // eslint-disable-line no-undef - ).current; - const sizeStyle = size === "lg" ? "lgSize" : "xsSize"; - useEffect(() => { - setInternalOptions(options); - }, [options]); - return ( -
setFocused(true)} - onBlurCapture={() => setFocused(false)} - className={ - styled( - className, - 'dropdownContainer', - 'container', - sizeStyle, - selectedOption && "haveValue", - errorMsg && "haveError", - focused && "isFocused" - ) - } - > -
- ({ - value: o.label, - label: o.label, - }))} - value={selectedOption} - components={{ - Menu, - MenuList, - Option: CustomOption, - ValueContainer, - SingleValue, - IndicatorSeparator, - Input, - Control: ControlComponent, - }} - onChange={(value) => { - if (value) { - const newOptions = internalOptions.map((o) => ({ - selected: value.label === o.label, - label: o.label, - value: o.value, - })); - setInternalOptions(newOptions); - delayedOnChange(_.cloneDeep(newOptions), onChange); - } - }} - placeholder={`${placeholder}${placeholder && required ? " *" : ""}`} - clearable={false} - /> -
- {label ? ( - - {label} - {required ?  * : null} - - ) : null} - {errorMsg ? ( - - {errorMsg} - - ) : null} -
- ); -} - -Dropdown.defaultProps = { - placeholder: '', - label: '', - required: false, - onChange: () => {}, - errorMsg: '', - searchable: true, - size: 'lg', -}; - -Dropdown.propTypes = { - options: PT.arrayOf( - PT.shape({ - label: PT.string, - selected: PT.bool, - }) - ).isRequired, - placeholder: PT.string, - label: PT.string, - required: PT.bool, - onChange: PT.func, - errorMsg: PT.string, - size: PT.oneOf(["xs", "lg"]), -}; - -export default Dropdown; diff --git a/src/apps/earn/src/components/Dropdown/styles.scss b/src/apps/earn/src/components/Dropdown/styles.scss deleted file mode 100644 index c844913b8..000000000 --- a/src/apps/earn/src/components/Dropdown/styles.scss +++ /dev/null @@ -1,260 +0,0 @@ -@import "@earn/styles/variables"; -@import "@earn/styles/GUIKit/default"; - -.label { - @include textInputLabel; -} - -.relative { - position: relative; -} - -.errorMessage { - @include errorMessage; -} - -.iconDropdown { - position: absolute; - top: 50%; - right: 16px; - pointer-events: none; - margin-top: -4px; -} - -.container { - position: relative; - display: flex; - flex-direction: column; - padding-top: 12px; - - &.haveValue .label, - &.haveError .label, - &.isFocused .label { - display: flex; - } - - &.isFocused { - .label { - color: $gui-kit-level-2; - } - - .iconDropdown { - transform: scale(1, -1); - } - } - - &.haveError .label, - &.haveError.isFocused .label { - color: $gui-kit-level-5; - } - - :global { - // @import '~react-select/dist/react-select'; - - width: 100%; - - .Select-control { - margin: 0; - padding: 0; - border: none; - border-radius: 6px !important; - height: 52px; - outline: none !important; - box-shadow: inset 0 0 0 1px $gui-kit-gray-30 !important; - } - - .Select-input { - padding: 0; - margin: 0; - display: flex; - - input { - font-size: 16px; - padding: 0; - height: 22px; - line-height: 22px; - - &:focus { - border: none; - box-shadow: none; - } - } - } - - .Select-value { - .Select-value-label { - height: 22px; - line-height: 22px; - font-size: 16px; - color: $gui-kit-gray-90 !important; - position: absolute; - right: 0; - top: 0; - bottom: 0; - left: 15px; - height: 100%; - display: flex; - align-items: center; - margin: 0; - } - } - - .Select-placeholder, - .Select-value, - .Select-input { - padding: 0 15px !important; - height: 100% !important; - display: flex !important; - align-items: center !important; - } - - .Select-placeholder { - color: $gui-kit-gray-30; - opacity: 1; - text-transform: none; - font-size: 16px; - } - - .Select-multi-value-wrapper { - width: 100% !important; - height: 100% !important; - } - - .Select-arrow-zone { - padding-right: 15px !important; - - .Select-arrow { - background-image: none; - border: none; - width: 15px; - height: 9px; - background-size: 15px 9px; - top: 0 !important; - border-width: 0 !important; - opacity: 0; - pointer-events: none; - } - } - - .Select { - &.is-open { - .Select-arrow-zone { - .Select-arrow { - transform: scale(1, -1); - } - } - } - - &:not(.is-searchable) { - .Select-input { - display: none !important; - } - } - } - - .Select-menu-outer { - top: calc(100% + 2px) !important; - border: 1px solid $gui-kit-gray-30 !important; - border-radius: 0 !important; - max-height: 269px; - z-index: 7; - margin: 0; - - .Select-menu { - padding: 9px 0; - max-height: 269px; - - .Select-option { - padding: 0 15px !important; - font-size: 16px !important; - line-height: 30px !important; - color: $gui-kit-gray-90 !important; - background-color: transparent !important; - text-decoration: none !important; - - &.is-selected { - font-weight: bold !important; - } - - &:hover { - background-color: #229173 !important; - color: $tc-white !important; - } - } - } - } - } - - &.haveError { - :global { - .Select-control { - box-shadow: inset 0 0 0 2px $gui-kit-level-5 !important; - } - } - } - - // lg size - &.lgSize { - :global { - .Select-control { - height: 52px; - } - - .Select-input { - input { - height: 52px; - } - } - } - } - - // xs size - &.xsSize { - padding-top: 0; - - &.haveValue .label, - &.isFocused .label { - margin-top: -12px; - } - - :global { - .Select-control { - height: 40px; - } - - .Select-input { - input { - font-size: 14px; - height: 40px; - } - } - - .Select-value { - .Select-value-label { - font-size: 14px; - color: $gui-kit-gray-90 !important; - } - } - - .Select-placeholder { - font-size: 14px; - } - - .Select-menu-outer { - * { - font-size: 14px !important; - } - - .Select-menu { - .Select-option { - font-size: 14px !important; - } - } - } - } - - .errorMessage { - @include errorMessageXs; - } - } -} diff --git a/src/apps/earn/src/components/DropdownTerms/index.jsx b/src/apps/earn/src/components/DropdownTerms/index.jsx deleted file mode 100644 index d47070913..000000000 --- a/src/apps/earn/src/components/DropdownTerms/index.jsx +++ /dev/null @@ -1,214 +0,0 @@ -/** - * Dropdown terms component. - */ -import React, { useState, useRef, useEffect } from "react"; -import PT from "prop-types"; -import _ from "lodash"; -import Select from "react-select"; - -import config from "../../config"; -import iconDown from "../../assets/icons/dropdown-arrow.png"; - -import styles from "./styles.scss"; -import { styled as styledCss } from "@earn/utils"; -const styled = styledCss(styles) - -function DropdownTerms({ - terms, - placeholder, - label, - required, - onChange, - errorMsg, - addNewOptionPlaceholder, - size, -}) { - const [internalTerms, setInternalTerms] = useState(terms); - const selectedOption = _.filter(internalTerms, { selected: true }).map( - (o) => ({ - value: o.label, - label: o.label, - }) - ); - const [focused, setFocused] = useState(false); - const delayedOnChange = useRef( - _.debounce((q, cb) => cb(q), config.GUIKIT.DEBOUNCE_ON_CHANGE_TIME) // eslint-disable-line no-undef - ).current; - - const containerRef = useRef(null); - let inputFieldRef = useRef(null); - const latestPropsRef = useRef(null); - latestPropsRef.current = { addNewOptionPlaceholder }; - - useEffect(() => { - const selectInput = containerRef.current.getElementsByClassName( - "Select-input" - ); - if (selectInput && selectInput.length) { - inputFieldRef.current = selectInput[0].getElementsByTagName("input"); - inputFieldRef.current[0].placeholder = focused - ? latestPropsRef.current.addNewOptionPlaceholder - : ""; - inputFieldRef.current[0].style.border = "none"; - inputFieldRef.current[0].style.boxShadow = "none"; - selectInput[0].style.borderTop = "none"; - } - }, [focused, selectedOption]); - useEffect(() => { - setInternalTerms(terms); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [terms && terms.length]); - - const CustomReactSelectRow = React.forwardRef( - ({ className, option, children, onSelect }, ref) => - children ? ( - { - event.preventDefault(); - event.stopPropagation(); - onSelect(option, event); - }} - title={option.title} - tabIndex={-1} - > - {children} - - ) : null - ); - - CustomReactSelectRow.defaultProps = { - children: null, - className: "", - onSelect: () => {}, - }; - - CustomReactSelectRow.propTypes = { - children: PT.node, - className: PT.string, - onSelect: PT.func, - option: PT.object.isRequired, - }; - - return ( -
-
- this.setState({ editURL: this.inputURL.value })} - ref={(node) => { - this.inputURL = node; - }} - className={styled("url")} - tabIndex="0" - value={st.editURL} - /> -
-
- Size%: - - this.setState({ size: _.clamp(this.inputSize.value, 0, 100) }) - } - ref={(node) => { - this.inputSize = node; - }} - className={styled("size")} - tabIndex="-1" - value={st.size} - /> -
-
-
- - -
- {st.previewURL ? ( -
-
- {st.description} -
- ) : null} - -
- ); - } -} - -EditModal.defaultProps = { - description: "", - onSave: _.noop, - onCancel: _.noop, - size: 100, - src: "http://", -}; - -EditModal.propTypes = { - description: PT.string, - onSave: PT.func, - onCancel: PT.func, - size: PT.number, - src: PT.string, -}; diff --git a/src/apps/earn/src/components/Editor/Image/EditModal/style.scss b/src/apps/earn/src/components/Editor/Image/EditModal/style.scss deleted file mode 100644 index c3a1aa5ac..000000000 --- a/src/apps/earn/src/components/Editor/Image/EditModal/style.scss +++ /dev/null @@ -1,65 +0,0 @@ -@import "@earn/styles/mixins"; - -.buttons-container { - display: inline-block; - margin-left: 31px; - margin-top: $base-unit * 2; -} - -// Competing against some globals -.container { - @include tc-body-md; - - input { - display: inline-block; - height: $base-unit * 6; - margin: 0; - padding: 0; - margin-left: $base-unit * 1; - vertical-align: middle; - - &.size { - width: 50px; - } - - &.url { - width: 500px; - } - } - - z-index: 1000; -} - -.modalContainer { - top: 30%; - left: 50%; - transform: translate(-50%); - height: auto; - width: 720px; -} - -.modalOverlay { - pointer-events: bounding-box; -} - -.fields-container { - display: flex; - flex-direction: row; - justify-content: space-between; -} - -.field { - display: inline-block; -} - -.preview { - display: block; -} - -.size { - width: 50px; -} - -.url { - width: 100%; -} diff --git a/src/apps/earn/src/components/Editor/Image/Popup/index.jsx b/src/apps/earn/src/components/Editor/Image/Popup/index.jsx deleted file mode 100644 index e01b842d3..000000000 --- a/src/apps/earn/src/components/Editor/Image/Popup/index.jsx +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Popup Component for Link Decorators - */ -import _ from "lodash"; -import PT from "prop-types"; -import React from "react"; - - -import EditModal from "../EditModal"; - -import styles from "./style.scss"; -import { styled as styledCss } from "@earn/utils"; -import { Button } from "~/libs/ui"; -const styled = styledCss(styles) - -export default class Popup extends React.Component { - constructor(props) { - super(props); - this.state = { - editing: props.triggerModal, - }; - } - - render() { - const { onEdit, size, src } = this.props; - const { editing } = this.state; - const renderDisplay = () => ( -
- -
- ); - - const renderEdit = () => ( -
- this.setState({ editing: false })} - onSave={(newSrc, newSize) => { - this.setState({ editing: false }); - onEdit(newSrc, newSize); - }} - /> -
- ); - - return
{editing ? renderEdit() : renderDisplay()}
; - } -} - -Popup.defaultProps = { - size: 100, - src: "http://", - onEdit: _.noop, - triggerModal: false, -}; - -Popup.propTypes = { - onEdit: PT.func, - size: PT.number, - src: PT.string, - triggerModal: PT.bool, -}; diff --git a/src/apps/earn/src/components/Editor/Image/Popup/style.scss b/src/apps/earn/src/components/Editor/Image/Popup/style.scss deleted file mode 100644 index 7fb680869..000000000 --- a/src/apps/earn/src/components/Editor/Image/Popup/style.scss +++ /dev/null @@ -1,6 +0,0 @@ -@import "@earn/styles/mixins"; - -.edit { - height: $base-unit * 4; - width: $base-unit * 4; -} diff --git a/src/apps/earn/src/components/Editor/Image/index.jsx b/src/apps/earn/src/components/Editor/Image/index.jsx deleted file mode 100644 index 5fefc0d78..000000000 --- a/src/apps/earn/src/components/Editor/Image/index.jsx +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Draft Decorator Component - * Renders images within the draft component including a popup edit button and modal - */ -import PT from "prop-types"; - -import { Tooltip } from "~/libs/ui"; - -import Popup from "./Popup"; - -const Image = ({ children, contentState, entityKey, updateEntityData }) => { - const { description, size, src, triggerModal } = contentState - .getEntity(entityKey) - .getData(); - - const popup = ( - { - updateEntityData(entityKey, { src: newSrc, size: newSize }); - }} - size={size} - src={src} - triggerModal={triggerModal} - /> - ); - - return ( - - - {description} - - {children} - - ); -}; - -Image.propTypes = { - contentState: PT.shape().isRequired, - children: PT.node.isRequired, - entityKey: PT.string.isRequired, - updateEntityData: PT.func.isRequired, -}; - -export default Image; diff --git a/src/apps/earn/src/components/Editor/Link/Popup/index.jsx b/src/apps/earn/src/components/Editor/Link/Popup/index.jsx deleted file mode 100644 index 6f6f77a50..000000000 --- a/src/apps/earn/src/components/Editor/Link/Popup/index.jsx +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Popup Component for Link Decorators - */ -import _ from "lodash"; -import PT from "prop-types"; -import React from "react"; - -import styles from "./style.scss"; -import { styled as styledCss } from "@earn/utils"; -import { Button } from "~/libs/ui"; -const styled = styledCss(styles) - -export default class Popup extends React.Component { - constructor(props) { - super(props); - this.state = { - href: props.href, - editing: false, - }; - } - - handleDone() { - const { onEdit } = this.props; - const { href } = this.state; - onEdit(href); - this.setState({ editing: false }); - } - - render() { - const { editing, href } = this.state; - const renderDisplay = () => ( -
- - {href} - - -
- ); - - const renderEdit = () => ( -
- this.setState({ href: this.node.value })} - onKeyUp={(e) => { - if (e.keyCode === 13) { - this.handleDone(); - } - }} - ref={(node) => { - this.node = node; - }} - /> - -
- ); - - return ( -
- {editing ? renderEdit() : renderDisplay()} -
- ); - } -} - -Popup.defaultProps = { - href: "", - onEdit: _.noop, -}; - -Popup.propTypes = { - href: PT.string, - onEdit: PT.func, -}; diff --git a/src/apps/earn/src/components/Editor/Link/Popup/style.scss b/src/apps/earn/src/components/Editor/Link/Popup/style.scss deleted file mode 100644 index 8fb4ddd90..000000000 --- a/src/apps/earn/src/components/Editor/Link/Popup/style.scss +++ /dev/null @@ -1,26 +0,0 @@ -@import "@earn/styles/mixins"; - -.edit { - height: $base-unit * 4; - width: $base-unit * 4; -} - -// Competing against some globals -.container { - a { - display: inline-block; - min-width: $base-unit * 24; - padding-left: $base-unit * 4; - vertical-align: middle; - } - - input[type="text"] { - display: inline-block; - height: $base-unit * 6; - margin: 0; - padding: 0; - margin-left: $base-unit * 1; - width: $base-unit * 48; - vertical-align: middle; - } -} diff --git a/src/apps/earn/src/components/Editor/Link/index.jsx b/src/apps/earn/src/components/Editor/Link/index.jsx deleted file mode 100644 index d36a082cf..000000000 --- a/src/apps/earn/src/components/Editor/Link/index.jsx +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Draft Decorator Component - * Renders links within the draft component including a popup Tooltip to edit - */ -import PT from "prop-types"; - -import { Tooltip } from "~/libs/ui"; - -import Popup from "./Popup"; - -const Link = ({ children, contentState, entityKey, updateEntityData }) => { - const { href, triggerPopup } = contentState.getEntity(entityKey).getData(); - - const popup = ( - updateEntityData(entityKey, { href: updated })} - /> - ); - - return ( - - - - {children} - - - - ); -}; - -Link.defaultProps = { - children: null, -}; - -Link.propTypes = { - children: PT.node, - contentState: PT.shape().isRequired, - entityKey: PT.string.isRequired, - updateEntityData: PT.func.isRequired, -}; - -export default Link; diff --git a/src/apps/earn/src/components/Editor/MarkdownEditor/BlockWrapper.jsx b/src/apps/earn/src/components/Editor/MarkdownEditor/BlockWrapper.jsx deleted file mode 100644 index 257fa80c6..000000000 --- a/src/apps/earn/src/components/Editor/MarkdownEditor/BlockWrapper.jsx +++ /dev/null @@ -1,37 +0,0 @@ -import PT from "prop-types"; -import React from "react"; - -import { EditorBlock } from "draft-js"; - -export default function BlockWrapper(props) { - const { blockProps } = props; - const { type } = blockProps; - const types = type.split("-"); - const thisType = (types[0] || "unstyled").split(":"); - - const leadingLi = - thisType[0] === "li" && !types.slice(1).some((x) => x === "li"); - - if (thisType[0] !== "unstyled") { - const child = BlockWrapper({ - ...props, - blockProps: { - type: types.slice(1).join("-"), - }, - }); - let className = thisType[0]; - if (thisType[1]) className += ` md-syntax-level-${thisType[1]}`; - if (leadingLi) className += " leadingLi"; - if (thisType[0] === "hr") { - return
{child}
; - } - return React.createElement(thisType[0], { className }, child); - } - return ; -} - -BlockWrapper.propTypes = { - blockProps: PT.shape({ - type: PT.string.isRequired, - }).isRequired, -}; diff --git a/src/apps/earn/src/components/Editor/MarkdownEditor/index.jsx b/src/apps/earn/src/components/Editor/MarkdownEditor/index.jsx deleted file mode 100644 index 015d46a62..000000000 --- a/src/apps/earn/src/components/Editor/MarkdownEditor/index.jsx +++ /dev/null @@ -1,127 +0,0 @@ -/** - * Markdown editor. - */ - -import PT from "prop-types"; -import React from "react"; -import Turndown from "turndown"; - -import { ContentState, convertFromHTML, EditorState } from "draft-js"; - -import BlockWrapper from "./BlockWrapper"; -import Connector from "../Connector"; -import GenericEditor from "../GenericEditor"; -import MdUtils from "./md-utils"; - -import ImageEditor from "../Image/EditModal"; - -import styles from "./style.scss"; -import { styled as styledCss } from "../../../utils"; -const styled = styledCss(styles) - -const GUI_STATES = { - IMAGE_EDIT_MODAL: "IMAGE_EDIT_MODAL", - REGULAR: "REGULAR", -}; - -export default class MarkdownEditor extends React.Component { - constructor(props) { - super(props); - this.mdUtils = new MdUtils(); - this.state = { - editor: EditorState.createEmpty(this.mdUtils), - gui: GUI_STATES.REGULAR, - }; - this.turndown = new Turndown(); - } - - componentDidMount() { - const { initialContent } = this.props; - if (initialContent) { - let state = initialContent.replace(/\n/g, "
"); - state = convertFromHTML(state); - state = ContentState.createFromBlockArray( - state.contentBlocks, - state.entityMap - ); - state = EditorState.createWithContent(state, this.mdUtils); - this.onChange(state); - } - } - - onChange(newState) { - const { connector } = this.props; - this.mdUtils.parse(newState.getCurrentContent()); - if (connector && connector.previewer) { - connector.previewer.setVisible(true); - connector.previewer.setContent(this.mdUtils.getHtml()); - } - if (this.editor) { - const selfState = this.mdUtils.highlight(newState); - if (selfState !== newState) { - this.setState({ editor: selfState }); - } - } - } - - getHtml() { - return this.mdUtils.getHtml(); - } - - setHtml(html) { - let state = this.turndown.turndown(html); - state = state.replace(/\n/g, "
"); - state = convertFromHTML(state); - state = ContentState.createFromBlockArray( - state.contentBlocks, - state.entityMap - ); - state = EditorState.createWithContent(state, this.mdUtils); - this.onChange(state); - } - - insertImage() { - setImmediate(() => this.setState({ gui: GUI_STATES.IMAGE_EDIT_MODAL })); - } - - render() { - const { connector, id } = this.props; - const st = this.state; - return ( -
- {st.gui === GUI_STATES.IMAGE_EDIT_MODAL ? ( - this.setState({ gui: GUI_STATES.REGULAR })} - /> - ) : null} - ({ - component: BlockWrapper, - editable: true, - props: { type: block.getType() }, - })} - connector={connector} - decorator={this.mdUtils} - editorState={st.editor} - id={id} - onChange={(state) => this.onChange(state)} - ref={(node) => { - this.editor = node; - }} - /> -
- ); - } -} - -MarkdownEditor.defaultProps = { - connector: new Connector(), - id: null, - initialContent: null, -}; - -MarkdownEditor.propTypes = { - connector: PT.shape(), - id: PT.string, - initialContent: PT.string, -}; diff --git a/src/apps/earn/src/components/Editor/MarkdownEditor/inlineWrapperFactory.jsx b/src/apps/earn/src/components/Editor/MarkdownEditor/inlineWrapperFactory.jsx deleted file mode 100644 index fe0684052..000000000 --- a/src/apps/earn/src/components/Editor/MarkdownEditor/inlineWrapperFactory.jsx +++ /dev/null @@ -1,35 +0,0 @@ -import PT from "prop-types"; -import React from "react"; - -function InlineWrapper({ children, hrefs, key }) { - if (!key || key === "text") { - return {children}; - } - if (key === "mdSyntax") { - return {children}; - } - if (key === "inlineCode") { - return {children}; - } - const keys = key.split("-"); - const child = InlineWrapper({ - children, - key: keys.slice(1).join("-"), - }); - - if (keys[0].startsWith("a:")) { - return {children}; - } - - return React.createElement(keys[0], {}, child); -} - -InlineWrapper.propTypes = { - children: PT.node.isRequired, - hrefs: PT.shape.isRequired, - key: PT.string.isRequired, -}; - -export default function inlineWrapperFactory(key, hrefs) { - return ({ children }) => InlineWrapper({ children, hrefs, key }); -} diff --git a/src/apps/earn/src/components/Editor/MarkdownEditor/md-utils.js b/src/apps/earn/src/components/Editor/MarkdownEditor/md-utils.js deleted file mode 100644 index 23689f398..000000000 --- a/src/apps/earn/src/components/Editor/MarkdownEditor/md-utils.js +++ /dev/null @@ -1,456 +0,0 @@ -/** - * Markdown utilities for DraftJS editor. - * - * There are plenty of Markdown-related plugins for DraftJS in Internet, but it - * looks like nobody made it right so far; thus here we go with the correct - * solution. - */ - -import _ from "lodash"; -import Markdown from "markdown-it"; - -import { EditorState, SelectionState, Modifier } from "draft-js"; - -import { List } from "immutable"; - -import shortId from "shortid"; - -import inlineWrapperFactory from "./inlineWrapperFactory"; - -import "./style.scss"; - -/** - * Counts the specified characters in the given string. - * @param {String} string - * @param {String} char - * @return {Number} - */ -function count(string, char) { - let pos = -1; - let res = 0; - for (;;) { - const c = string[(pos += 1)]; - if (c === char) res += 1; - else if (!c) return res; - } -} - -/** - * Finds position of the n-th occurance of the specified character in the given - * string, and returns it. Returns -1, if not found. - * @param {String} string - * @param {String} char - * @param {Number} n - * @return {Number} - */ -function findNth(string, char, n) { - let pos = -1; - let res = n; - for (;;) { - const c = string[(pos += 1)]; - if (c === char) res -= 1; - if (!res) return pos; - if (!c) return -1; - } -} - -/* Internal. */ -function newSelector(key, pos = 0, end, endKey) { - return SelectionState.createEmpty(key).merge({ - anchorOffset: pos, - focusKey: endKey === undefined ? key : endKey, - focusOffset: end === undefined ? pos : end, - }); -} - -/* Internal. */ -function insertChar(content, key, pos, char) { - return Modifier.insertText(content, newSelector(key, pos), char); -} - -/* Internal. */ -function mergeBlockData(content, key, data) { - return Modifier.mergeBlockData(content, newSelector(key), data); -} - -/* Internal. */ -function removeChar(content, key, pos) { - return Modifier.removeRange(content, newSelector(key, pos, pos + 1)); -} - -/* Internal. */ -function removeLastChar(content, key, pos) { - const s = newSelector(key, pos, 0, content.getKeyAfter(key)); - return Modifier.removeRange(content, s); -} - -/* Internal. */ -function splitBlock(content, key, pos) { - return Modifier.splitBlock(content, newSelector(key, pos)); -} - -/* Internal. */ -function setBlockType(content, key, type) { - const block = content.getBlockForKey(key); - if (block.getType() === type) return content; - return Modifier.setBlockType(content, newSelector(key, 0), type); -} - -export default class MdUtils { - /** - * Constructs a new MdUtils instance. - * @param {ContentState} contentState Optional. If provided, constructor will - * automatically call parse(contentState) in the end of initialization. - */ - constructor(contentState) { - this.markdown = new Markdown(); - this.markdown.disable(["table"]); - this.tokens = []; - if (contentState) this.parse(contentState); - } - - /** - * Private. - * - * Merges and/or splits DraftJS blocks to ensure that the current block - * contains exactly the specified number of lines. - * - * @param {Number} numLines - */ - alignLines(numLines) { - for (;;) { - const text = this.content.getBlockForKey(this.key).getText(); - const numLinesInBlock = 1 + count(text, "\n"); - if (numLinesInBlock < numLines) { - const nextKey = this.content.getKeyAfter(this.key); - if (!nextKey) return; - this.content = removeLastChar(this.content, this.key, text.length); - this.content = insertChar(this.content, this.key, text.length, "\n"); - if (this.selection.getAnchorKey() === nextKey) { - this.selection = this.selection.merge({ - anchorKey: this.key, - anchorOffset: this.selection.getAnchorOffset() + text.length + 1, - }); - } - if (this.selection.getFocusKey() === nextKey) { - this.selection = this.selection.merge({ - focusKey: this.key, - focusOffset: this.selection.getFocusOffset() + text.length + 1, - }); - } - } else if (numLinesInBlock > numLines) { - const splitPoint = findNth(text, "\n", numLines); - this.content = removeChar(this.content, this.key, splitPoint); - this.content = splitBlock(this.content, this.key, splitPoint); - if ( - this.selection.getAnchorKey() === this.key && - this.selection.getAnchorOffset() > splitPoint - ) { - this.selection = this.selection.merge({ - anchorKey: this.content.getKeyAfter(this.key), - anchorOffset: this.selection.getAnchorOffset() - splitPoint - 1, - }); - } - if ( - this.selection.getFocusKey() === this.key && - this.selection.getFocusOffset() > splitPoint - ) { - this.selection = this.selection.merge({ - focusKey: this.content.getKeyAfter(this.key), - focusOffset: this.selection.getFocusOffset() - splitPoint - 1, - }); - } - } else return; - } - } - - getDecorations(block) { - _.noop(this); - const res = block.getData().get("decorations") || List(); - return res.setSize(block.getLength()); - } - - getComponentForKey(key) { - _.noop(this); - return inlineWrapperFactory(key, this.hrefs); - } - - getPropsForKey() { - _.noop(this); - return {}; - } - - /** - * Highlights Markdown syntax in the given DraftJS state. - * @param {EditorState} state DraftJS EditorState that holds a plain text with - * a valid Markdown markup, previously loaded into this MdUtils instance via - * parse(..) method. - * @return {EditorState} Resulting state with the proper formatting and - * styling of the markup. - */ - highlight(state) { - this.blockTypes = []; - this.content = state.getCurrentContent(); - this.decorations = {}; - this.decoreLevel = 0; - this.endLines = []; - this.hrefs = {}; - this.key = this.content.getFirstBlock().getKey(); - this.selection = state.getSelection(); - this.styleLine = 0; - this.tokenId = 0; - this.tokens.forEach(() => this.highlightNextToken()); - while (this.key) { - this.content = setBlockType(this.content, this.key, "unstyled"); - this.highlightInline(); - this.key = this.content.getKeyAfter(this.key); - } - const res = EditorState.push(state, this.content, "custom"); - return EditorState.acceptSelection(res, this.selection); - } - - /** - * Private. - * - * Highlights inline Markdown syntax in the current DraftJS block. - */ - highlightInline(subTokens) { - let pos = 0; - const text = this.content.getBlockForKey(this.key).getText(); - const res = new Array(text.length); - res.fill("mdSyntax"); - - if (subTokens) { - const styles = []; - subTokens.forEach((st) => { - switch (st.type) { - case "link_open": { - const id = shortId().replace(/-/g, ":"); - [[, this.hrefs[id]]] = st.attrs; - styles.push(`a:${id}`); - break; - } - - case "em_open": - case "strong_open": - styles.push(st.tag); - break; - - case "s_open": - styles.push("strike"); - break; - - case "em_close": - case "link_close": - case "s_close": - case "strong_close": - styles.pop(); - break; - - case "code_inline": { - if (!st.content.length) break; - let style = styles.join("-"); - if (!style) style = "inlineCode"; - else style = `${style}-inlineCode`; - pos = text.indexOf(st.content, pos); - const end = pos + st.content.length; - while (pos < end) { - res[pos] = style; - pos += 1; - } - break; - } - - case "text": { - if (!st.content.length) break; - pos = text.indexOf(st.content, pos); - const end = pos + st.content.length; - const style = styles.join("-") || "text"; - while (pos < end) { - res[pos] = style; - pos += 1; - } - if (styles.length && _.last(styles).startsWith("a:")) { - pos += 3 + _.last(styles).slice(2).length; - } - break; - } - - default: - } - }); - } - - let i = text.length - 1; - while (i >= 0 && text[i] === " ") { - res[i] = "text"; - i -= 1; - } - while (i >= 0 && res[i] === "mdSyntax") i -= 1; - if (i < 0) res.fill("mdSyntax"); - - const decorations = List(res); - this.content = mergeBlockData(this.content, this.key, { decorations }); - } - - /** - * Private. - * - * Highlights Markdown syntax in the specified number of lines, starting from - * the first non-styled line. - * - * @param {Number} numLines - */ - highlightLines(numLines) { - this.alignLines(numLines); - const type = this.blockTypes.join("-") || "unstyled"; - this.content = setBlockType(this.content, this.key, type); - } - - /** - * Private. - * - * Highlights all Markdown syntax between the last highlighted DraftJS block - * and the current MarkdownIt token. - */ - highlightNextToken() { - const token = this.tokens[this.tokenId]; - - /* If token opens a new range, we: - * - Style any block before this token line; - * - Remember the range of this token, and the line of the first non-styled - * block. */ - if (token.map) { - const linesBefore = token.map[0] - this.styleLine; - if (linesBefore) { - this.highlightLines(linesBefore); - this.highlightInline(); - [this.styleLine] = token.map; - this.key = this.content.getKeyAfter(this.key); - } - } - - switch (token.type) { - case "blockquote_open": - case "bullet_list_open": - this.decoreLevel += 1; - this.blockTypes.push(`${token.tag}:${this.decoreLevel}`); - this.endLines.push(token.map[1]); - break; - - case "ordered_list_open": - this.decoreLevel += 2; - this.blockTypes.push(`${token.tag}:${this.decoreLevel}`); - this.endLines.push(token.map[1]); - break; - - case "heading_open": - case "list_item_open": - case "paragraph_open": - this.blockTypes.push(token.tag); - this.endLines.push(token.map[1]); - break; - - case "blockquote_close": - case "bullet_list_close": - case "heading_close": - case "list_item_close": - case "ordered_list_close": - case "paragraph_close": { - const linesBefore = _.last(this.endLines) - this.styleLine; - if (linesBefore) { - this.highlightLines(linesBefore); - this.highlightInline(); - this.key = this.content.getKeyAfter(this.key); - this.styleLine = _.last(this.endLines); - } - this.blockTypes.pop(); - this.endLines.pop(); - - switch (token.type) { - case "blockquote_close": - case "bullet_list_close": - this.decoreLevel -= 1; - break; - case "ordered_list_close": - this.decoreLevel -= 2; - break; - default: - } - - break; - } - - case "fence": { - this.blockTypes.push(token.tag); - let numLines = token.map[1] - token.map[0]; - const subTokens = [{ type: "text", content: token.content }]; - const isClosed = 1 + count(token.content, "\n") < numLines; - if (!isClosed) { - let i = 1 + this.tokenId; - while (i < this.tokens.length && !this.tokens[i].map) i += 1; - if ( - i === this.tokens.length || - this.tokens[i].map[0] > token.map[1] - ) { - numLines += 1; - } - } - this.highlightLines(numLines); - this.highlightInline(subTokens); - this.key = this.content.getKeyAfter(this.key); - [, this.styleLine] = token.map; - this.blockTypes.pop(); - break; - } - - case "code_block": - case "hr": { - this.blockTypes.push(token.tag); - this.highlightLines(token.map[1] - token.map[0]); - this.highlightInline(token.children); - this.key = this.content.getKeyAfter(this.key); - [, this.styleLine] = token.map; - this.blockTypes.pop(); - break; - } - - case "inline": - this.highlightLines(token.map[1] - token.map[0]); - this.highlightInline(token.children); - this.key = this.content.getKeyAfter(this.key); - [, this.styleLine] = token.map; - break; - - default: - } - - this.tokenId += 1; - } - - /** - * Returns HTML representation of the Markdown markup previously loaded by - * parse(..) method of MdUtils. - * @return {String} - */ - getHtml() { - if (!this.html) { - this.html = this.markdown.renderer.render(this.tokens, this.env); - } - return this.html; - } - - /** - * Parses the given DraftJS state. The state should contain a plain text with - * Markdown markup. After the parse you can call other methods of MdUtils to - * generate corresponding HTML markup, or DraftJS state for rendered Markdown - * representation, or DraftJS decorator for Markdown syntax highlighting in - * the original state. - * @param {ContentState} state - */ - parse(contentState) { - delete this.html; - this.env = {}; - this.tokens = this.markdown.parse(contentState.getPlainText(), this.env); - } -} diff --git a/src/apps/earn/src/components/Editor/MarkdownEditor/style.scss b/src/apps/earn/src/components/Editor/MarkdownEditor/style.scss deleted file mode 100644 index 24c5e4fe3..000000000 --- a/src/apps/earn/src/components/Editor/MarkdownEditor/style.scss +++ /dev/null @@ -1,148 +0,0 @@ -@import "@earn/styles/mixins"; - -/* Styling of Markdown syntax highlighting. */ -.container { - @include tc-body-md; - - overflow: hidden; - - a { - color: $tc-dark-blue-110; - text-decoration: underline; - } - - code { - background: $tc-gray-neutral-light; - border: 1px solid $tc-gray-20; - border-radius: 6px; - display: block; - font-family: "Roboto Mono", monospace; - padding: 15px 20px; - white-space: pre-wrap; - - &:global.inline { - background: $tc-gray-10; - border: none; - border-radius: 0; - display: inline; - padding: 0 5px; - } - } - - em { - font-style: italic; - } - strong { - font-weight: bold; - } - - /* This styling leads to some artefacts :( */ - :global .hr { - width: 100%; - - > div > span > span { - border-top: 1px solid firebrick; - display: inline-block; - height: 0; - line-height: 0; - width: 100%; - } - } - - :global { - li { - display: inline-block; - position: relative; - - &.leadingLi { - margin-top: 10px; - } - - &::before { - border-left: 3px solid $tc-gray-40; - content: ""; - height: 100%; - position: absolute; - } - } - - $offset: -15; - - @for $level from 0 to 10 { - ol.md-syntax-level-#{$level} > li::before { - left: #{$offset}px; - } - $offset: $offset + 18; - } - - $offset: -5; - - @for $level from 0 to 10 { - ul.md-syntax-level-#{$level} > li::before { - left: #{$offset}px; - } - $offset: $offset + 18; - } - - .blockquote { - position: relative; - - &::before { - border-left: 3px solid $tc-light-blue; - content: ""; - height: 100%; - position: absolute; - } - } - - $offset: -15; - - @for $level from 0 to 10 { - .blockquote.md-syntax-level-#{$level}::before { - left: #{$offset}px; - } - $offset: $offset + 18; - } - - div, - .h1, - .h2, - .h3, - .h4, - .h5, - .h6, - p { - @include tc-body-md; - - margin: 0 !important; - text-transform: none; - } - - .h1 .text { - @include tc-heading-xl; - } - .h2 .text { - @include tc-heading-lg; - } - .h3 .text { - @include tc-heading-md; - } - .h4 .text { - @include tc-heading-sm; - } - .h5 .text { - @include tc-heading-xs; - } - .h6 .text { - @include tc-heading-xs; - } - - .mdSyntax { - @include tc-body-md; - - color: firebrick; - font-family: "Roboto Mono", monospace; - font-weight: bold; - } - } -} diff --git a/src/apps/earn/src/components/Editor/MultiEditor.jsx b/src/apps/earn/src/components/Editor/MultiEditor.jsx deleted file mode 100644 index d5b236874..000000000 --- a/src/apps/earn/src/components/Editor/MultiEditor.jsx +++ /dev/null @@ -1,164 +0,0 @@ -/** - * The MultiEditor component combines together WysiwygEditor and Markdown editor - * allowing to easily switch between them. - */ - -import PT from "prop-types"; -import React from "react"; -import Turndown from "turndown"; - -import { OrderedSet } from "immutable"; - -import Connector from "./Connector"; -import MarkdownEditor from "./MarkdownEditor"; -import WysiwygEditor from "."; - -export const MODES = { - MARKDOWN: "MARKDOWN", - WYSIWYG: "WYSIWYG", -}; - -export default class MultiEditor extends React.Component { - constructor(props) { - super(props); - this.fakeConnector = new Connector(); - this.fakeConnector.setToolbar(this); - this.id = props.id; - this.state = { - mode: props.initialMode, - }; - this.turndown = new Turndown(); - } - - componentDidMount() { - const { connector } = this.props; - if (connector) { - connector.addEditor(this); - this.fakeConnector.setPreviewer(connector.previewer); - } - } - - componentWillReceiveProps({ connector, id }) { - const { connector: prevConnector } = this.props; - this.id = id; - if (connector !== prevConnector) { - if (prevConnector) prevConnector.removeEditor(this); - if (connector) { - connector.addEditor(this); - this.fakeConnector.setPreviewer(connector.previewer); - } - } - } - - componentWillUnmount() { - const { connector } = this.props; - if (connector) connector.removeEditor(this); - } - - onFocusedEditorChanged(state) { - const { connector } = this.props; - if (connector) connector.setFocusedEditor(this, state); - } - - getHtml() { - return this.editor.getHtml(); - } - - setHtml(html) { - this.editor.setHtml(html); - } - - setMode(value) { - const { mode } = this.state; - if (value === mode) return; - const { connector } = this.props; - const state = this.editor.getHtml(); - this.setState({ mode: value }, () => { - this.editor.setHtml(state); - if (connector) connector.setFocusedEditor(this, this.editor.state.editor); - }); - } - - applyBlockStyle(type) { - const { mode } = this.state; - if (mode === MODES.WYSIWYG) this.editor.applyBlockStyle(type); - } - - applyColorStyle(type, color) { - const { mode } = this.state; - if (mode === MODES.WYSIWYG) { - this.editor.applyColorStyle(type, color); - } - } - - focus() { - const { mode } = this.state; - if (mode === MODES.WYSIWYG) this.editor.focus(); - } - - insertImage(src, triggerModal) { - const { mode } = this.state; - switch (mode) { - case MODES.WYSIWYG: - return this.editor.insertImage(src, triggerModal); - case MODES.MARKDOWN: - return this.editor.insertImage(); - default: - return undefined; - } - } - - insertLink(title, href, triggerPopup) { - const { mode } = this.state; - if (mode === MODES.WYSIWYG) { - this.editor.insertLink(title, href, triggerPopup); - } - } - - toggleInlineStyle(styleName) { - const { mode } = this.state; - if (mode === MODES.WYSIWYG) { - return this.editor.toggleInlineStyle(styleName); - } - return OrderedSet(); - } - - render() { - const { mode } = this.state; - switch (mode) { - case MODES.MARKDOWN: - return ( - { - if (node) this.editor = node; - }} - /> - ); - case MODES.WYSIWYG: { - return ( - { - if (node) this.editor = node; - }} - /> - ); - } - default: - throw new Error("Unknown mode"); - } - } -} - -MultiEditor.defaultProps = { - connector: null, - id: null, - initialMode: MODES.WYSIWYG, -}; - -MultiEditor.propTypes = { - connector: PT.shape(), - id: PT.string, - initialMode: PT.oneOf(Object.values(MODES)), -}; diff --git a/src/apps/earn/src/components/Editor/Previewer/index.jsx b/src/apps/earn/src/components/Editor/Previewer/index.jsx deleted file mode 100644 index 526885fb4..000000000 --- a/src/apps/earn/src/components/Editor/Previewer/index.jsx +++ /dev/null @@ -1,54 +0,0 @@ -import PT from "prop-types"; -import React from "react"; - -import styles from "./style.scss"; -import { styled as styledCss } from "../../../utils"; -const styled = styledCss(styles) - -export default class Previewer extends React.Component { - constructor(props) { - super(props); - if (props.connector) props.connector.setPreviewer(this); - this.state = { - content: props.initialContent, - visible: false, - }; - } - - setContent(content) { - setImmediate(() => this.setState({ content })); - } - - setVisible(newVisible) { - const { visible } = this.state; - if (newVisible === visible) return; - setImmediate(() => this.setState({ visible: newVisible })); - } - - render() { - const { content, visible } = this.state; - return ( -
- {visible ? ( -
-
Rendering Preview
-
-
- ) : null} -
- ); - } -} - -Previewer.defaultProps = { - connector: null, - initialContent: "", -}; - -Previewer.propTypes = { - connector: PT.shape(), - initialContent: PT.string, -}; diff --git a/src/apps/earn/src/components/Editor/Previewer/style.scss b/src/apps/earn/src/components/Editor/Previewer/style.scss deleted file mode 100644 index d1776dade..000000000 --- a/src/apps/earn/src/components/Editor/Previewer/style.scss +++ /dev/null @@ -1,25 +0,0 @@ -@import "@earn/styles/mixins"; - -.container { - background: white; - overflow: hidden; - position: relative; -} - -.content { - border: 1px solid $tc-gray-40; - height: 33vh; - margin: 0 0 10px; - padding-top: 25px; -} - -.title { - @include tc-label-xs; - - background: $tc-gray-neutral-dark; - color: $tc-gray-40; - left: 1px; - position: absolute; - padding: 5px 10px; - top: 1px; -} diff --git a/src/apps/earn/src/components/Editor/Toolbar/ColorPicker/index.jsx b/src/apps/earn/src/components/Editor/Toolbar/ColorPicker/index.jsx deleted file mode 100644 index 4bc508922..000000000 --- a/src/apps/earn/src/components/Editor/Toolbar/ColorPicker/index.jsx +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Component - * Implements a color picker dropdown populated with the colors found in utils/editor - */ -import _ from "lodash"; -import PT from "prop-types"; -import React from "react"; -import * as Colors from "react-color"; - -import { EDITOR_COLOR_MAP } from "../../../../utils/editor"; - -import styles from "./style.scss"; -import { styled as styledCss } from "@earn/utils"; -const styled = styledCss(styles) - -const ColorPicker = ({ onChange, style, visible }) => ( -
- {visible ? ( - { - e.preventDefault(); - onChange(_.findKey(EDITOR_COLOR_MAP, (value) => value === hex)); - }} - className={styled("color-picker")} - /> - ) : null} -
-); - -ColorPicker.defaultProps = { - onChange: _.noop, - style: "", - visible: false, -}; - -ColorPicker.propTypes = { - onChange: PT.func, - style: PT.string, - visible: PT.bool, -}; - -export default ColorPicker; diff --git a/src/apps/earn/src/components/Editor/Toolbar/ColorPicker/style.scss b/src/apps/earn/src/components/Editor/Toolbar/ColorPicker/style.scss deleted file mode 100644 index a3272ea00..000000000 --- a/src/apps/earn/src/components/Editor/Toolbar/ColorPicker/style.scss +++ /dev/null @@ -1,7 +0,0 @@ -// The react-color component has inline styles which we need to overwrite -.color-picker { - display: inline-flex !important; - flex-wrap: nowrap !important; - position: absolute !important; - width: auto !important; -} diff --git a/src/apps/earn/src/components/Editor/Toolbar/index.jsx b/src/apps/earn/src/components/Editor/Toolbar/index.jsx deleted file mode 100644 index 587f11dd5..000000000 --- a/src/apps/earn/src/components/Editor/Toolbar/index.jsx +++ /dev/null @@ -1,298 +0,0 @@ -/** - * Component - * Implements a Toolbar that can control multiple components - */ -import _ from "lodash"; -import PT from "prop-types"; -import React from "react"; -import Sticky from "react-stickynode"; - -import { Button } from "~/libs/ui"; -import Select from "../../Select"; -import { EDITOR_BLOCK_STYLE_MAP } from "../../../utils/editor"; - -import { RichUtils } from "draft-js"; - -import ColorPicker from "./ColorPicker"; -import Connector from "../Connector"; -import MultiEditor, { MODES } from "../MultiEditor"; - -import styles from "./style.scss"; -import { styled as styledCss } from "../../../utils"; -const styled = styledCss(styles) - -/** - * Component class, provides a Toolbar that can control multiple Editor components - * connected to it via the Connector class - */ -export default class Toolbar extends React.Component { - constructor(props) { - super(props); - this.state = { - block: null, - editor: null, - // markdown: false, - pickingTextColor: false, - // pickingHighlightColor: false, - - BOLD: false, - ITALIC: false, - }; - } - - componentDidMount() { - const { connector } = this.props; - connector.setToolbar(this); - } - - componentWillReceiveProps({ connector: newConnector }) { - const { connector } = this.props; - const prevConnector = connector; - if (newConnector !== prevConnector) { - if (prevConnector) prevConnector.setToolbar(null); - if (newConnector) newConnector.setToolbar(this); - } - } - - componentWillUnmount() { - const { connector } = this.props; - connector.setToolbar(null); - } - - onFocusedEditorChanged(newState) { - const { connector } = this.props; - const editor = connector.focusedEditor; - if (editor) { - const inlineStyle = newState.getCurrentInlineStyle(); - const block = RichUtils.getCurrentBlockType(newState); - this.setState({ - editor, - block, - BOLD: inlineStyle.has("BOLD"), - // INLINE_CODE: inlineStyle.has('CODE'), - ITALIC: inlineStyle.has("ITALIC"), - UNDERLINE: inlineStyle.has("UNDERLINE"), - STRIKETHROUGH: inlineStyle.has("STRIKETHROUGH"), - }); - } else { - this.setState({ - block: "unstyled", - editor: null, - BOLD: false, - // INLINE_CODE: false, - ITALIC: false, - UNDERLINE: false, - STRIKETHROUGH: false, - }); - } - } - - render() { - const st = this.state; - const disableStyling = !st.editor; - const { connector, nodeId, onSave } = this.props; - - const createStyleButton = (label, name, active, className) => ( - - ); - - return ( - -
- {connector.focusedEditor instanceof MultiEditor ? ( -
- { - st.editor.applyBlockStyle(value); - this.setState({ block: value }); - }} - onFocus={(e) => e.preventDefault()} - options={_.map(EDITOR_BLOCK_STYLE_MAP, (label, value) => ({ - label, - value, - }))} - placeholder="Block Style" - value={st.editor ? st.block : null} - /> -
- - {/* I guess, we gonna drop the inline Markdown option. Just for - * a case, let's keep the button code around for a bit longer. */ - /* - - */} -
-
- ); - } -} - -Toolbar.defaultProps = { - connector: new Connector(), - onSave: _.noop, - nodeId: null, -}; - -Toolbar.propTypes = { - connector: PT.instanceOf(Connector), - onSave: PT.func, - nodeId: PT.string, -}; diff --git a/src/apps/earn/src/components/Editor/Toolbar/style.scss b/src/apps/earn/src/components/Editor/Toolbar/style.scss deleted file mode 100644 index 07db930cc..000000000 --- a/src/apps/earn/src/components/Editor/Toolbar/style.scss +++ /dev/null @@ -1,81 +0,0 @@ -@import "@earn/styles/mixins"; - -@mixin button { - margin: 0 1px; - padding: 0 10px; -} - -.basic { - @include button; -} - -.bold { - @include button; - - font-weight: bold; -} - -.container { - background: $tc-gray-neutral-light; - padding: 10px; - width: 100%; -} - -.highlight-color-picker { - display: inline; - position: absolute; - transform: translate(-56px, 44px); -} - -.inlineCode { - @include button; - @include roboto-mono-regular; -} - -.italic { - @include button; - - font-style: italic; -} - -.select { - @include tc-label-md; -} - -.select-wrapper { - @include button; - - display: inline-block; - vertical-align: middle; - width: 250px; -} - -.save { - @include button; -} - -.separator { - display: inline-block; - height: 30px; - margin: 0 5px; - vertical-align: middle; - width: 1px; -} - -.strikethrough { - @include button; - - text-decoration: line-through; -} - -.text-color-picker { - display: inline; - position: absolute; - transform: translate(-44px, 44px); -} - -.underline { - @include button; - - text-decoration: underline; -} diff --git a/src/apps/earn/src/components/Editor/index.jsx b/src/apps/earn/src/components/Editor/index.jsx deleted file mode 100644 index bf12218ea..000000000 --- a/src/apps/earn/src/components/Editor/index.jsx +++ /dev/null @@ -1,306 +0,0 @@ -/** - * Content editor based on DraftJS. - * - * DraftJS is not Redux-friendly, thus our editor uses local state, unlike most - * of our code. Technically, it is possible to keep its state in Redux store, - * but it will have performance drawback, as it will demand constant conversions - * between the Redux state segment and the internal state of the editor. - */ -import _ from "lodash"; -import PT from "prop-types"; -import React from "react"; - -import { - ContentState, - convertFromHTML, - EditorState, - Modifier, - RichUtils, -} from "draft-js"; -import "draft-js/dist/Draft.css"; - -import Editor from "draft-js-plugins-editor"; -import createMarkdownShortcutsPlugin from "draft-js-markdown-shortcuts-plugin"; - -import { EDITOR_COLOR_MAP, editorStateToHTML } from "../../utils/editor"; - -import Connector from "./Connector"; -import createCustomPlugin from "./plugin"; - -import styles from "./style.scss"; - -export default class EditorWrapper extends React.Component { - constructor(props) { - super(props); - this.id = props.id; - - this.state = { - editor: EditorState.createEmpty(), - markdown: false, - }; - - // Each Editor needs its own instance of plugins - this.markdownPlugin = createMarkdownShortcutsPlugin(); - // We need to inject the EditorWrapper into the plugin so that it can - // modify state.editorState - this.customPlugin = createCustomPlugin({ - editor: this, - }); - } - - componentDidMount() { - const { connector, initialContent } = this.props; - connector.addEditor(this); - if (initialContent) { - let editorState = convertFromHTML(initialContent); - editorState = ContentState.createFromBlockArray( - editorState.contentBlocks, - editorState.entityMap - ); - editorState = EditorState.createWithContent(editorState); - this.initialContent = editorState.getCurrentContent(); - setImmediate(() => this.setState({ editor: editorState })); - } - } - - componentWillReceiveProps({ connector, id }) { - const { connector: prevConnector } = this.props; - this.id = id; - if (connector !== prevConnector) { - if (prevConnector) prevConnector.removeEditor(this); - if (connector) connector.addEditor(this); - } - } - - componentWillUnmount() { - const { connector } = this.props; - connector.removeEditor(this); - } - - getHtml() { - const { editor } = this.state; - return editorStateToHTML(editor.getCurrentContent()); - } - - setHtml(html) { - let state = convertFromHTML(html); - state = ContentState.createFromBlockArray( - state.contentBlocks, - state.entityMap - ); - state = EditorState.createWithContent(state); - setImmediate(() => this.setState({ editor: state })); - } - - /** - * Sets the block type/style at the current selection. Type map can be found in utils/editor. - * Only one block type/style can be applied, this will replace the previous. - * @param {String} type The new block style - */ - applyBlockStyle(type) { - const { editor } = this.state; - let editorState = editor; - editorState = RichUtils.toggleBlockType(editorState, type); - this.setState({ editorState }); // eslint-disable-line - } - - /** - * Sets the color at the current selection for the specified category. - * Type map can be found in utils/editor. - * @param {String} type Category, TEXT or HIGHLIGHT - * @param {String} color The new color name - */ - applyColorStyle(type, color) { - let { editor: editorState } = this.state; - let contentState = editorState.getCurrentContent(); - - const sel = editorState.getSelection(); - - // Clear any existing colors - contentState = _.reduce( - EDITOR_COLOR_MAP, - (state, value, name) => - Modifier.removeInlineStyle(state, sel, `${type}_${name}`), - contentState - ); - - editorState = EditorState.push( - editorState, - contentState, - "change-inline-style" - ); - - // Apply new color - editorState = RichUtils.toggleInlineStyle(editorState, `${type}_${color}`); - - this.setState({ editor: editorState }); - } - - focus() { - if (this.node) this.node.focus(); - } - - /** - * Inserts a new image at current cursor selection. - * @param {String} src The default src - * @param {Boolean} triggerModal Whether to trigger the img selection/resize modal on creation - */ - insertImage(src, triggerModal) { - let { editor: editorState } = this.state; - let contentState = editorState.getCurrentContent(); - - // If the user has a range selected, it needs to be collapsed before insertText will work - // This sets the starting and ending range to the same position, - // which is equivalent to just a cursor/caret - let sel = editorState.getSelection(); - const startKey = sel.getStartKey(); - const startOffset = sel.getStartOffset(); - sel = sel.merge({ - anchorKey: startKey, - anchorOffset: startOffset, - focusKey: startKey, - focusOffset: startOffset, - }); - - contentState = contentState.createEntity("IMG", "SEGMENTED", { - src, - triggerModal, - }); - const key = contentState.getLastCreatedEntityKey(); - - // Using insertText so that images behave in an inline fashion - contentState = Modifier.insertText(contentState, sel, " ", null, key); - - editorState = EditorState.push( - editorState, - contentState, - "insert-characters" - ); - - this.setState({ editor: editorState }); - } - - /** - * Inserts a new link at current cursor, or applies to selected text - * @param {String} title Default title to display for the link, if no text is selected in range - * @param {String} href The href - * @param {Boolean} triggerPopup Whether to trigger the popup on creation - */ - insertLink(title, href, triggerPopup) { - let { editor: editorState } = this.state; - let contentState = editorState.getCurrentContent(); - - const sel = editorState.getSelection(); - - contentState = contentState.createEntity("LINK", "MUTABLE", { - href, - triggerPopup, - }); - const key = contentState.getLastCreatedEntityKey(); - - // Selection is a just the cursor, insert new link - if (sel.isCollapsed()) { - // Inserts a space at the cursor, needed so that the user can 'escape' - // from the link entity by clicking after the link, or pressing right arrow - contentState = Modifier.insertText(contentState, sel, " ", null, null); - // Because selection hasn't been updated, this will insert the link *before* - // the newly created space. - contentState = Modifier.insertText(contentState, sel, title, null, key); - - editorState = EditorState.push( - editorState, - contentState, - "insert-characters" - ); - } else { - // Selection is a range, keep the text but make it a link - editorState = RichUtils.toggleLink(editorState, sel, key); - } - - this.setState({ editor: editorState }); - } - - /** - * Toggle an inline text style on/off - * @param {String} styleName Name of the style - * @return {String} The resulting style of the selection - */ - toggleInlineStyle(styleName) { - const { editor } = this.state; - const editorState = RichUtils.toggleInlineStyle(editor, styleName); - this.setState({ editor: editorState }); - return editorState.getCurrentInlineStyle(); - } - - render() { - const { connector, theme } = this.props; - - const st = this.state; - - let containerStyles = styles.container; - if (st.editor.getSelection().getHasFocus()) { - containerStyles += ` ${styles.focused}`; - } - if (theme.container) { - containerStyles += ` ${theme.container}`; - } - - return ( -
this.focus()} - onKeyPress={() => this.focus()} - onFocus={() => this.focus()} - role="button" - tabIndex={0} - > - { - const editorState = RichUtils.handleKeyCommand(state, command); - if (editorState) { - connector.setFocusedEditor(this, editorState); - this.setState({ editor: editorState }); - return true; - } - return false; - }} - onChange={(newState) => { - const p = _.get(this, "props.connector.previewer"); - if (p) p.setVisible(false); - - const hasFocus = newState.getSelection().getHasFocus(); - if ( - !connector.modified && - this.initialContent && - this.initialContent !== newState.getCurrentContent() - ) { - connector.modified = true; - } - connector.setFocusedEditor(hasFocus ? this : null, newState); - this.setState({ editor: newState }); - }} - plugins={[st.markdown ? this.markdownPlugin : {}, this.customPlugin]} - ref={(node) => { - this.node = node; - }} - spellCheck - /> -
- ); - } -} - -EditorWrapper.defaultProps = { - connector: new Connector(), - id: null, - initialContent: null, - theme: {}, -}; - -EditorWrapper.propTypes = { - connector: PT.instanceOf(Connector), - id: PT.string, - initialContent: PT.string, - theme: PT.shape(), -}; diff --git a/src/apps/earn/src/components/Editor/plugin.jsx b/src/apps/earn/src/components/Editor/plugin.jsx deleted file mode 100644 index 2941806cd..000000000 --- a/src/apps/earn/src/components/Editor/plugin.jsx +++ /dev/null @@ -1,102 +0,0 @@ -/** - * Custom DraftJS plugin. - * - * Facilitates the extra functionality required for component. - * Including: Images, Links, and the Note block. - * - * In the future, it should also be possible to validate allowed block elements here, - * and also substitute them for the nearest available option when HTML is rendered. - */ -import _ from "lodash"; -import React from "react"; -import { Map } from "immutable"; -import { EditorState } from "draft-js"; -import { EDITOR_COLOR_MAP } from "../../utils/editor"; - -import Image from "./Image"; -import Link from "./Link"; - -/** - * This is based on the strategy that draft-js-markdown-shortcuts-plugin uses - * so it will work on images and links created with markdown - * - * @param {String} type The block type to create the strategy for, ex. 'IMG', 'LINK' - * @return {Function} The strategy function - */ -const createStrategy = (type) => (contentBlock, callback, contentState) => { - contentBlock.findEntityRanges((metadata) => { - const key = metadata.getEntity(); - return key !== null && contentState.getEntity(key).getType() === type; - }, callback); -}; - -/** - * Creates a custom plugin instance - * - * @param {Object} config Config object, standard interface for Draft JS plugins - * @return {Object} Object representing the Custom Plugin instance, is passed to the - */ -export default ({ editor }) => { - // Store the editor in the closure - const updateEntityData = (key, data) => { - let editorState = editor.state.editor; - - editorState = EditorState.push( - editorState, - editorState.getCurrentContent().replaceEntityData(key, data), - "change-block-data" - ); - - // Force re-render for new data - editorState = EditorState.forceSelection( - editorState, - editorState.getSelection() - ); - - editor.setState({ editorState }); - }; - - const inlineStyles = {}; - - _.forIn(EDITOR_COLOR_MAP, (value, name) => { - inlineStyles[`TEXT_${name}`] = { color: value }; - inlineStyles[`HIGHLIGHT_${name}`] = { background: value }; - }); - - return { - // Provides custom html element rendering for block types - blockRenderMap: Map({ - // draft-js and draft-js-markdown-shortcuts-plugin use inconsistent rendering of - // code-blocks, so we override both of them - "code-block": { - element: "span", - wrapper: , - }, - note: { - element: "span", - wrapper:
, - }, - }), - // Provides custom styling for inline elements (mainly text) - customStyleMap: { - CODE: { - background: "#fafafb", - fontFamily: '"Roboto Mono", monospace', - }, - ...inlineStyles, - }, - // Provides custom component rendering for images and links - decorators: [ - { - strategy: createStrategy("LINK"), - component: Link, - props: { updateEntityData }, - }, - { - strategy: createStrategy("IMG"), - component: Image, - props: { updateEntityData }, - }, - ], - }; -}; diff --git a/src/apps/earn/src/components/Editor/style.scss b/src/apps/earn/src/components/Editor/style.scss deleted file mode 100644 index d3dea4cb3..000000000 --- a/src/apps/earn/src/components/Editor/style.scss +++ /dev/null @@ -1,71 +0,0 @@ -@import "@earn/styles/mixins"; - -:global { - // Has a default z-index of 0 which interferes with component - .DraftEditor-editorContainer { - z-index: auto; - } - - // Draft.js sets some list-related globals that need to be undone, - // and these can't be changed using the blockStyleFn mechanism - .public-DraftStyleDefault-orderedListItem.public-DraftStyleDefault-listLTR::before { - width: auto; - position: relative; - left: 0; - } - - .public-DraftStyleDefault-depth0.public-DraftStyleDefault-listLTR { - margin-left: 0; - } -} - -.container { - @include tc-body-md; - - border: 1px solid $tc-gray-10; - margin: 10px 0; - padding: 10px; - - &:hover { - border: 1px solid $tc-gray-40; - } - - // Draft.js will render code-blocks like
- // This will prevent the code/pre styles from being applied twice - pre { - code { - background: none; - border: 0; - border-radius: none; - margin: 0; - padding: 0; - } - } -} - -.focused { - border: 1px solid $tc-dark-blue; - - &:hover { - border: 1px solid $tc-dark-blue-110; - } -} - -.note { - @include tc-body-sm; - - background: $tc-yellow-30; - border: 1px solid $tc-yellow-70; - border-radius: 6px; - font-style: italic; - color: $tc-black; - line-height: 20px; - padding: 15px 20px; - margin: 25px 0; - - a, - p, - ul { - font-size: 13px; - } -} diff --git a/src/apps/earn/src/components/ErrorMessage/index.jsx b/src/apps/earn/src/components/ErrorMessage/index.jsx deleted file mode 100644 index 6c1eddaba..000000000 --- a/src/apps/earn/src/components/ErrorMessage/index.jsx +++ /dev/null @@ -1,58 +0,0 @@ -import { useEffect } from "react"; -import PT from "prop-types"; -import { BaseModal, Button } from "~/libs/ui"; - -const ErrorMessage = ({ title, details, onOk }) => { - useEffect(() => { - document.body.classList.add("scrolling-disabled-by-modal"); - - return () => { - document.body.classList.remove("scrolling-disabled-by-modal"); - }; - }, []); - - return ( - { - e.preventDefault(); - onOk(); - }} - > - OK - - )} - > -
- - ); -}; - -ErrorMessage.defaultProps = { - details: "", -}; - -ErrorMessage.propTypes = { - title: PT.string.isRequired, - details: PT.string, - onOk: PT.func.isRequired, -}; - -export default ErrorMessage; diff --git a/src/apps/earn/src/components/ErrorMessage/styles.scss b/src/apps/earn/src/components/ErrorMessage/styles.scss deleted file mode 100644 index 5c7328798..000000000 --- a/src/apps/earn/src/components/ErrorMessage/styles.scss +++ /dev/null @@ -1,49 +0,0 @@ -@import '@earn/styles/variables'; -@import '@earn/styles/mixins'; - -$sm-space-10: $base-unit * 2; -$sm-space-15: $base-unit * 3; -$sm-space-25: $base-unit * 5; -$sm-space-40: $base-unit * 8; -$button-space-32: 6 * $base-unit; - -.container { - @include roboto-regular; - - overflow: hidden; - padding: 8 * $base-unit; - text-align: center; - - @include xs-to-sm { - padding: 40px 10px; - } -} - -.details { - font-weight: 400; - font-size: 13px; - color: $tc-gray-60; - line-height: $sm-space-25; - padding: 0 $sm-space-15; - margin-bottom: $sm-space-10; - text-align: justify; - - a { - color: $tc-dark-blue; - text-decoration: underline; - } -} - -.title { - color: $tc-red; - font-size: 15px; - font-weight: bold; - line-height: $sm-space-25; - margin-bottom: $sm-space-10; - padding: 0 $sm-space-15; - - .id { - color: #000; - font-weight: 500; - } -} diff --git a/src/apps/earn/src/components/GigsRadioButton/index.jsx b/src/apps/earn/src/components/GigsRadioButton/index.jsx deleted file mode 100644 index e7997d2fe..000000000 --- a/src/apps/earn/src/components/GigsRadioButton/index.jsx +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Radio button component. - */ -import { Fragment, useEffect, useRef, useState } from "react"; -import PT from "prop-types"; -import cn from "classnames"; -import _ from "lodash"; -import styles from "./styles.scss"; -import config from "../../config"; - -function RadioButton({ - className, - layout = "", - options, - onChange, - size, - errorMsg, -}) { - const [internalOptions, setInternalOptions] = useState(options); - const optionsWithKey = internalOptions.map((o, oIndex) => ({ - ...o, - key: oIndex, - })); - let sizeStyle = size === "lg" ? "lgSize" : null; - if (!sizeStyle) { - sizeStyle = size === "xs" ? "xsSize" : "smSize"; - } - const delayedOnChange = useRef( - _.debounce((q, cb) => cb(q), config.GUIKIT.DEBOUNCE_ON_CHANGE_TIME) // eslint-disable-line no-undef - ).current; - - useEffect(() => { - setInternalOptions(options); - }, [options]); - - return ( - -
- {optionsWithKey.map((o) => ( -
- -
- ))} -
- {errorMsg ? {errorMsg} : null} -
- ); -} - -RadioButton.defaultProps = { - onChange: () => {}, - size: "sm", - errorMsg: "", -}; - -RadioButton.propTypes = { - options: PT.arrayOf( - PT.shape({ - label: PT.string, - value: PT.bool.isRequired, - }) - ).isRequired, - onChange: PT.func, - size: PT.oneOf(["xs", "sm", "lg"]), - errorMsg: PT.string, -}; - -export default RadioButton; diff --git a/src/apps/earn/src/components/GigsRadioButton/styles.scss b/src/apps/earn/src/components/GigsRadioButton/styles.scss deleted file mode 100644 index 9d1daa675..000000000 --- a/src/apps/earn/src/components/GigsRadioButton/styles.scss +++ /dev/null @@ -1,178 +0,0 @@ -@import "../../styles/variables"; -@import '../../styles/GUIKit/default'; - -/* Create a custom radio button */ -.checkmark { - position: absolute; - top: 0; - left: 0; - background-color: $tc-white; - border-radius: 50%; - border: 1px solid $gui-kit-gray-30; - - /* Create the indicator (the dot/circle - hidden when not checked) */ - &::after { - content: ''; - position: absolute; - display: none; - top: 50%; - left: 50%; - margin-top: -6px; - margin-left: -6px; - width: 12px; - height: 12px; - border-radius: 50%; - background-color: $tc-white; - box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.35); - } - - &.hasError { - border: 2px solid $gui-kit-level-5; - } -} - -.radioButton { - display: flex; - align-items: center; -} - -.label { - font-size: 14px; - cursor: pointer; -} - -/* The container */ -.container { - display: block; - position: relative; - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - color: $gui-kit-gray-90; - - /* Hide the browser's default radio button */ - input { - position: absolute; - opacity: 0; - cursor: pointer; - - /* When the radio button is checked, add a blue background */ - &:checked ~ .checkmark { - background-color: $gui-kit-level-2; - box-shadow: inset 0 1px 2px 0 rgba(0, 0, 0, 0.29); - border: none; - - /* Show the indicator (dot/circle) when checked */ - &::after { - display: block; - } - } - } -} - -.radioButtonContainer { - display: flex; - flex-direction: column; - - &.horizontal { - flex-direction: row; - - .radioButton { - margin-right: 28px; - - &:last-child { - margin-right: 0; - } - } - } - - .label { - color: $gui-kit-gray-90; - } - - // lg size - &.lgSize { - .container { - padding-left: 24px; - line-height: 24px; - height: 24px; - - .checkmark { - height: 24px; - width: 24px; - - &::after { - margin-top: -6px; - margin-left: -6px; - width: 12px; - height: 12px; - } - } - } - - .label { - margin-left: 8px; - } - } - - // sm size - &.smSize { - .container { - padding-left: 20px; - line-height: 20px; - height: 20px; - - .checkmark { - height: 20px; - width: 20px; - - &::after { - margin-top: -5px; - margin-left: -5px; - width: 10px; - height: 10px; - } - } - } - - .label { - margin-left: 8px; - } - } - - // xs size - &.xsSize { - .container { - padding-left: 15px; - line-height: 15px; - height: 15px; - - .checkmark { - height: 16px; - width: 16px; - - &::after { - margin-top: -4px; - margin-left: -4px; - width: 8px; - height: 8px; - } - } - } - - .label { - margin-left: 8px; - } - } -} - -.errorMessage { - display: block; - - @include errorMessage; - - color: #ef476f; - margin-left: 0; -} diff --git a/src/apps/earn/src/components/LoginModal/index.jsx b/src/apps/earn/src/components/LoginModal/index.jsx deleted file mode 100644 index 6c6557bd0..000000000 --- a/src/apps/earn/src/components/LoginModal/index.jsx +++ /dev/null @@ -1,82 +0,0 @@ -import { useCallback } from "react"; -import MediaQuery from "react-responsive"; -import PT from "prop-types"; -import cn from "classnames"; - -import { Button } from "~/libs/ui"; - -import blobYellow from "../../assets/images/blob-yellow.svg"; -import blobPurple from "../../assets/images/blob-purple.svg"; -import progressBar from "../../assets/images/progress-bar.svg"; -import progressBarMid from "../../assets/images/progress-bar-mid.svg"; -import ProgressBarXS from "../../assets/images/progress-bar-mobile.svg"; -import thinkingFaceMobile from "../../assets/images/thinking-face-mobile.svg"; -import thinkingFace from "../../assets/images/thinking-face-laptop-tablet.svg"; -import { makeLoginUrl, makeRegisterUrl } from "../../utils/url"; -import modalStyles from "../../styles/_modal.scss"; - -import styles from "./styles.scss"; -import BaseModal from "../../../../../libs/ui/lib/components/modals/base-modal/BaseModal"; - -function LoginModal({ onClose, open }) { - const onClickBtnRegister = useCallback(() => { - window.open(makeRegisterUrl(window.location.href)); - }, []); - - return ( - -
- - -

YAY! You are almost done!

-

- Looks like you're not a Topcoder member yet. Or maybe you're - not logged in? - - - - - - - It's quick to register and it's free! -

- - - - - - - - - -
- -
-

- Already a member? Login here -

-
-
- ); -} - -LoginModal.defaultProps = { - utmSource: "gig_listing", -}; - -LoginModal.propTypes = { - utmSource: PT.string, -}; - -export default LoginModal; diff --git a/src/apps/earn/src/components/LoginModal/styles.scss b/src/apps/earn/src/components/LoginModal/styles.scss deleted file mode 100644 index 9e0533fad..000000000 --- a/src/apps/earn/src/components/LoginModal/styles.scss +++ /dev/null @@ -1,156 +0,0 @@ -@import "../../styles/mixins"; - -.title { - position: relative; - color: #9d41c9; - @include barlow-condensed-medium; - font-size: 60px; - line-height: 58px; - text-transform: uppercase; - margin: 0; - margin-bottom: 30px; - - @include xs-to-sm { - max-width: 400px; - margin: auto; - font-size: 60px !important; - line-height: 58px !important; - margin-bottom: 30px; - } - - @media (max-width: 425px) { - max-width: 230px; - margin: auto; - font-size: 36px !important; - line-height: 34px !important; - margin-bottom: 30px; - } -} - -.loginMsg { - color: #2a2a2a; - font-size: 24px; - line-height: 36px; - margin-bottom: 40px; - - img { - display: inline; - } - - @media (max-width: 425px) { - text-align: left !important; - font-size: 20px; - line-height: 30px; - margin-bottom: 30px; - } -} - -.controls { - display: flex; - align-content: center; - justify-content: center; - margin: 0; - - & > button:first-child { - margin-right: 10px !important; - } - - & > a:first-child { - margin-right: 10px !important; - } -} - -.referrals { - display: flex; - overflow: auto; - - .sucessMsg { - font-size: 24px; - line-height: 36px; - margin-bottom: 40px; - } - - .rightAlign { - justify-content: flex-end; - } -} - -.loginRequired { - display: flex; - flex-direction: column; - padding: 80px 55px 40px 60px; - text-align: center; - position: relative; - overflow: hidden; - - @include xs-to-sm { - padding: 50px 35px 40px; - } - - @media (max-width: 425px) { - padding: 50px 35px 70px; - } - - .progressBar { - display: block; - width: 100%; - max-width: 100%; - - @include phone-only { - margin-bottom: 40px; - } - } - - .blobYellow, - .blobPurple { - display: block; - position: absolute; - max-width: 100%; - } - - .blobYellow { - top: 0; - right: 0; - - @media (max-width: 425px) { - max-width: 62px; - } - } - - .blobPurple { - bottom: -5px; - left: 0; - - @media (max-width: 425px) { - max-width: 84px; - bottom: -40px; - } - } - - .thinkingFace { - width: 23px; - margin: 0 5px; - transform: translateY(4px); - - @media (max-width: 425px) { - width: 21px; - transform: translateY(2px); - } - } - - .regTxt { - position: relative; - font-size: 16px; - margin: 30px 0 0; - - a { - color: #0D61BF; - text-decoration: underline; - } - } -} - -.overlay { - background-color: #2a2a2a; - opacity: 0.95; -} diff --git a/src/apps/earn/src/components/Menu/index.jsx b/src/apps/earn/src/components/Menu/index.jsx deleted file mode 100644 index 60fca660b..000000000 --- a/src/apps/earn/src/components/Menu/index.jsx +++ /dev/null @@ -1,182 +0,0 @@ -import React, { useRef, useEffect } from "react"; -import { navigate } from "react-router-dom"; -import PT from "prop-types"; -import _ from "lodash"; - -import IconChevronUp from "../../assets/icons/menu-chevron-up.svg"; -import { MenuSelection, getMenuIcon } from "../../utils"; - -import styles from "./styles.scss"; -import { styled as styledCss } from "@earn/utils"; -const styled = styledCss(styles) - -const Menu = ({ menu, selected, onSelect, isLoggedIn, onUpdateMenu }) => { - const selectionRef = useRef(); - if (!selectionRef.current) { - selectionRef.current = new MenuSelection( - _.cloneDeep(menu), - selected, - onSelect, - onUpdateMenu - ); - } - - useEffect(() => { - selectionRef.current.setMenu(menu); - }, [menu]); - - useEffect(() => { - selectionRef.current.select(selected); - }, [selected]); - - // useEffect(() => { - // if (selectionRef.current.isAuth(selected) && isLoggedIn === false) { - // utils.auth.logIn(); - // } - // }, [selected, isLoggedIn]); - - const onSelectMenuItem = (name, path) => { - selectionRef.current.select(name); - if (path) { - navigate(path); - } - }; - - const getIcon = (menuItem, active) => { - const name = active ? menuItem.iconActive : menuItem.icon; - return getMenuIcon(name); - }; - - const isExpandable = (menuItem) => - selectionRef.current.isExpandable(menuItem); - const isSelected = (menuItem) => selectionRef.current.isSelected(menuItem); - const isExpanded = (menuItem) => selectionRef.current.isExpanded(menuItem); - const isActive = (menuItem) => selectionRef.current.isActive(menuItem); - - const renderSubSubmenu = (subMenuItem) => { - return ( -
    - {subMenuItem.children.map((subSubmenuItem) => ( -
  • - { - onSelectMenuItem(subSubmenuItem.name, subSubmenuItem.path); - }} - > - {subSubmenuItem.name} - -
  • - ))} -
- ); - }; - - const renderSubmenu = (menuItem) => { - if (!menuItem.children) { - return null; - } - - return ( -
    - {menuItem.children.map((subMenuItem) => ( -
  • - { - onSelectMenuItem( - subMenuItem.name, - isExpandable(subMenuItem) ? null : subMenuItem.path - ); - }} - > - {subMenuItem.name} - - {isExpandable(subMenuItem) && renderSubSubmenu(subMenuItem)} -
  • - ))} -
- ); - }; - - return ( - - ); -}; - -Menu.propTypes = { - menu: PT.shape(), - selected: PT.string, - onSelect: PT.func, - isLoggedIn: PT.oneOf([null, true, false]), -}; - -export default Menu; diff --git a/src/apps/earn/src/components/Menu/styles.scss b/src/apps/earn/src/components/Menu/styles.scss deleted file mode 100644 index 3782a14a4..000000000 --- a/src/apps/earn/src/components/Menu/styles.scss +++ /dev/null @@ -1,102 +0,0 @@ -@import "@earn/styles/variables"; - -$menu-padding-x: 4 * $base-unit; -$menu-padding-y: 20px; - -.menu { - padding: $menu-padding-y $menu-padding-x (3 * $base-unit); - - &.logged-in {} - - &.logged-out { - .menu-item-auth { - display: none; - } - } -} - -.menu-item { - padding: 4px 0; - cursor: pointer; - - .link { - display: flex; - align-items: center; - line-height: 26px; - outline: none; - } - - .icon { - width: 24px; - height: 24px; - margin-right: 16px; - text-align: left; - } - - .text {} - - .arrow { - width: 21px; - height: 21px; - margin-left: 8px; - line-height: 1; - text-align: center; - vertical-align: middle; - - &.up {} - &.down { - transform: rotate(180deg); - } - } - - &.active > .link { - font-weight: 500; - } - - &.selected > .link { - color: $tc-turquoise-dark1; - } -} - -.menu-item-main > .link { - margin-left: -20px; - margin-right: -20px; - padding-left: 20px; - padding-right: 20px; -} - -.menu-item-main.active > .link { - box-shadow: inset 4px 0 $tc-turquoise; -} - -.menu-item-main > .link + ul, -.menu-item-secondary > .link + ul { - display: none; -} - -.menu-item-main.expanded > .link + ul, -.menu-item-secondary.expanded > .link + ul { - display: block; - cursor: default; -} - -.menu-item-secondary.active.collapsed { - color: $tc-turquoise-dark1; -} - -.sub-menu { - padding-left: 24px + 16px; - - .menu-item { - cursor: default; - .link { - cursor: pointer; - display: inline-block; - } - } - -} - -.sub-submenu { - padding-left: 20px; -} diff --git a/src/apps/earn/src/components/MetaTags/index.jsx b/src/apps/earn/src/components/MetaTags/index.jsx deleted file mode 100644 index fd60501f3..000000000 --- a/src/apps/earn/src/components/MetaTags/index.jsx +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Auxiliary wrapper around React Helmet that helps to generate meta tags for - * generic use cases. - * - * NOTE: This component relies on `domain` path of Redux store to hold - * the current app domain, which will serve as the base path for the bundled - * images. - */ - -import React from "react"; -import PT from "prop-types"; -import { connect } from "react-redux"; -import { Helmet } from "react-helmet"; - -function MetaTags({ - description, - domain, - image, - siteName, - socialDescription, - socialTitle, - title, - url, -}) { - const img = `${domain}${image}`; - const socTitle = socialTitle || title; - const socDesc = socialDescription || description; - return ( - - {/* General tags. */} - {title} - - - {/* Twitter cards. */} - - - - {image ? : null} - {siteName ? : null} - - {/* Open Graph data. */} - - {image ? : null} - {image ? : null} - - {siteName ? : null} - {url ? : null} - - ); -} - -MetaTags.defaultProps = { - image: null, - siteName: null, - socialDescription: null, - socialTitle: null, - url: null, -}; - -MetaTags.propTypes = { - description: PT.string.isRequired, - domain: PT.string, - image: PT.string, - siteName: PT.string, - socialDescription: PT.string, - socialTitle: PT.string, - title: PT.string.isRequired, - url: PT.string, -}; - -/* TODO: It is not good to depend on the domain written into redux state here, - * better pass it via the renderer context at the server side, and get it from - * the location at the frontend side, or something similar? */ -export default connect((state) => ({ domain: state.domain }))(MetaTags); diff --git a/src/apps/earn/src/components/MultiSelect/index.jsx b/src/apps/earn/src/components/MultiSelect/index.jsx deleted file mode 100644 index 161147612..000000000 --- a/src/apps/earn/src/components/MultiSelect/index.jsx +++ /dev/null @@ -1,237 +0,0 @@ -//import "react-select/dist/react-select.css"; -import styles from './styles.scss'; -import React, { useState } from 'react'; -import PT from 'prop-types'; -import Select, { components } from 'react-select'; -import cn from 'classnames'; -import iconDown from '../../assets/icons/dropdown-arrow.png'; - -const Arrow = () => ( - icon down -); - -const Menu = (props) => { - return ( - - {props.children} - - ); -}; -const MenuList = (props) => { - return ( - - {props.children} - - ); -}; - -const CustomOption = (props) => { - return ( - - {props.children} - - ); -}; - -const ValueContainer = ({ children, ...props }) => ( - - {children} - -); - -const ControlComponent = (props) => { - return ( - - ); -}; -const IndicatorsContainer = (props) => { - return ( - - ); -}; -const SelectContainer = (props) => { - return ( - - ); -}; - -const MultiValueContainer = (props) => { - return ( - - ); -}; - -const MultiValueLabel = (props) => { - return ( - - ); -}; - -const MultiValueRemove = ({ children, ...props }) => ( - - {children} - -); - -const Input = (props) => { - return ( - - ); -}; - -const Placeholder = (props) => { - return ( - - ); -}; - -const IndicatorSeparator = () => { - return null; -}; -const DropdownIndicator = () => { - return null; -}; - -/** - * Displays a multi-select field. - * - * @param {Object} props component properties - * @returns {JSX.Element} - */ -const MultiSelect = ({ - className, - clearable, - label, - isRequired = false, - onChange, - onFocus, - options, - optLabelKey, - optValueKey, - placeholder, - showArrow = false, - size, - value, - error, -}) => { - const [focused, setFocused] = useState(false); - return ( - <> -
setFocused(true)} - onBlurCapture={() => setFocused(false)} - className={[ - styles.container, - styles[size], - className, - !!error ? styles['hasError'] : '', - ].join(' ')} - > - {label && ( - - {label + (isRequired ? ' *' : '')} - - )} - { - const newOptions = optionsWithKey.map((oWithKeyTmp) => ({ - label: oWithKeyTmp.label, - value: o.key === oWithKeyTmp.key, - })); - setInternalOptions(newOptions); - delayedOnChange(_.cloneDeep(newOptions), onChange); - }} - /> - - {o.label ? {o.label} : null} - -
- ))} -
- {errorMsg ? {errorMsg} : null} - - ); -} - -RadioButton.defaultProps = { - onChange: () => {}, - size: "sm", - errorMsg: "", -}; - -RadioButton.propTypes = { - options: PT.arrayOf( - PT.shape({ - label: PT.string, - value: PT.bool.isRequired, - }) - ).isRequired, - onChange: PT.func, - size: PT.oneOf(["xs", "sm", "lg"]), - errorMsg: PT.string, -}; - -export default RadioButton; diff --git a/src/apps/earn/src/components/RadioButton/styles.module.scss b/src/apps/earn/src/components/RadioButton/styles.module.scss deleted file mode 100644 index 0f8fa0bd5..000000000 --- a/src/apps/earn/src/components/RadioButton/styles.module.scss +++ /dev/null @@ -1,166 +0,0 @@ -@import "@earn/styles/variables"; -@import '@earn/styles/GUIKit/default'; - -/* Create a custom radio button */ -.checkmark { - position: absolute; - top: 0; - left: 0; - background-color: $tc-white; - border-radius: 50%; - border: 1px solid $gui-kit-gray-30; - - /* Create the indicator (the dot/circle - hidden when not checked) */ - &::after { - content: ''; - position: absolute; - display: none; - top: 50%; - left: 50%; - margin-top: -6px; - margin-left: -6px; - width: 12px; - height: 12px; - border-radius: 50%; - background-color: $tc-white; - box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.35); - } - - &.hasError { - border: 2px solid $gui-kit-level-5; - } -} - -.radioButton { - display: flex; - align-items: center; -} - -.label { - font-size: 14px; - cursor: pointer; -} - -/* The container */ -.container { - display: block; - position: relative; - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - color: $gui-kit-gray-90; - - /* Hide the browser's default radio button */ - input { - position: absolute; - opacity: 0; - cursor: pointer; - - /* When the radio button is checked, add a blue background */ - &:checked ~ .checkmark { - background-color: $gui-kit-level-2; - box-shadow: inset 0 1px 2px 0 rgba(0, 0, 0, 0.29); - border: none; - - /* Show the indicator (dot/circle) when checked */ - &::after { - display: block; - } - } - } -} - -.radioButtonContainer { - display: flex; - flex-direction: column; - - .label { - color: $gui-kit-gray-90; - } - - // lg size - &.lgSize { - .container { - padding-left: 24px; - line-height: 24px; - height: 24px; - - .checkmark { - height: 24px; - width: 24px; - - &::after { - margin-top: -6px; - margin-left: -6px; - width: 12px; - height: 12px; - } - } - } - - .label { - margin-left: 8px; - } - } - - // sm size - &.smSize { - .container { - padding-left: 20px; - line-height: 20px; - height: 20px; - - .checkmark { - height: 20px; - width: 20px; - - &::after { - margin-top: -5px; - margin-left: -5px; - width: 10px; - height: 10px; - } - } - } - - .label { - margin-left: 8px; - } - } - - // xs size - &.xsSize { - .container { - padding-left: 15px; - line-height: 15px; - height: 15px; - - .checkmark { - height: 16px; - width: 16px; - - &::after { - margin-top: -4px; - margin-left: -4px; - width: 8px; - height: 8px; - } - } - } - - .label { - margin-left: 8px; - } - } -} - -.errorMessage { - display: block; - - @include errorMessage; - - color: #ef476f; - margin-left: 0; -} diff --git a/src/apps/earn/src/components/ReferralAuthModal/index.jsx b/src/apps/earn/src/components/ReferralAuthModal/index.jsx deleted file mode 100644 index 063726d7e..000000000 --- a/src/apps/earn/src/components/ReferralAuthModal/index.jsx +++ /dev/null @@ -1,66 +0,0 @@ -import { useCallback } from "react"; -import PT from "prop-types"; - -import { BaseModal, Button } from "~/libs/ui"; - -import { REFERRAL_PROGRAM_URL } from "../../constants"; -import { makeLoginUrl, makeRegisterUrl } from "../../utils/url"; - -import styles from "./styles.scss"; - -/** - * Displays a modal with "Login" and "Register" buttons which is displayed after - * the user clicks the button in the ReferralBanner. - * - * @param {Object} props component properties - * @returns {JSX.Element} - */ -const ReferralAuthModal = ({ onClose, open }) => { - const onClickBtnLogin = useCallback(() => { - window.location = makeLoginUrl(window.location.href); - }, []); - - const onClickBtnRegister = useCallback(() => { - window.open(makeRegisterUrl(window.location.href)); - }, []); - - return ( - -
Referral Program
-
Please login to receive your referral code.
-
- - -
-
- Find out how the referral program works{" "} - - here - - . -
-
- ); -}; - -ReferralAuthModal.propTypes = { - onClose: PT.func.isRequired, - open: PT.bool.isRequired, -}; - -export default ReferralAuthModal; diff --git a/src/apps/earn/src/components/ReferralAuthModal/styles.scss b/src/apps/earn/src/components/ReferralAuthModal/styles.scss deleted file mode 100644 index a9c23b93b..000000000 --- a/src/apps/earn/src/components/ReferralAuthModal/styles.scss +++ /dev/null @@ -1,48 +0,0 @@ -@import '@libs/ui/styles/includes'; -@import "../../styles/mixins"; -@import "../../styles/variables"; - -.content { - display: block; -} - -.title { - @include barlow-condensed-medium; - font-size: 34px; - line-height: 38px; - text-transform: uppercase; - color: #1e94a3; -} - -.message { - margin: 20px 0 0; - font-size: 20px; - line-height: 30px; - text-align: center; - - @include tablet { - font-size: 24px; - line-height: 36px; - } -} - -.controls { - display: flex; - justify-content: center; - margin: 40px 0 0; - gap: $sp-3; -} - -.hint { - margin: 10px 0 0; - @include roboto-regular; - font-size: 14px; - line-height: 26px; - - a { - font-size: 16px; - line-height: 24px; - color: #0d61bf; - text-decoration: underline; - } -} diff --git a/src/apps/earn/src/components/ReferralEmailModal/index.jsx b/src/apps/earn/src/components/ReferralEmailModal/index.jsx deleted file mode 100644 index 065c75aa4..000000000 --- a/src/apps/earn/src/components/ReferralEmailModal/index.jsx +++ /dev/null @@ -1,68 +0,0 @@ -import modalStyles from "../../styles/_modal.scss"; -import cn from "classnames"; - -import { BaseModal, Button, LinkButton } from "~/libs/ui"; - -import { LoadingCircles } from "~/libs/ui"; -import { GIG_LIST_ROUTE } from "../../constants"; - -import styles from "./styles.scss"; - -const ReferralEmailModal = ({ error, isBusy, isUserError, onClose, open }) => ( - - {isBusy ? ( - <> -
Sending your referral...
- - - ) : ( - <> -
- {error ? "Oops!" : "Congratulations!"} -
-
- {error ? error : "Your referral has been sent."} -
- {!!error && - (isUserError ? ( -
- If you think this is an error please contact -
- support@topcoder.com. -
- ) : ( -
- Looks like there is a problem on our end. Please try again. -
- If this persists please contact{" "} - support@topcoder.com. -
- ))} -
- - - FIND ANOTHER GIG - -
- - )} -
-); - -export default ReferralEmailModal; diff --git a/src/apps/earn/src/components/ReferralEmailModal/styles.scss b/src/apps/earn/src/components/ReferralEmailModal/styles.scss deleted file mode 100644 index c3df4a43b..000000000 --- a/src/apps/earn/src/components/ReferralEmailModal/styles.scss +++ /dev/null @@ -1,24 +0,0 @@ -.sendLoadingIndicator { - display: block; - margin: 20px auto 0; -} - -.hint { - margin: 40px 0 20px; - font-size: 14px; - line-height: 26px; - text-align: center; - - a { - color: #0d61bf; - text-decoration: underline; - } - - + .controls { - margin: 0; - } -} - -.error { - color: #f00; -} diff --git a/src/apps/earn/src/components/SearchField/index.jsx b/src/apps/earn/src/components/SearchField/index.jsx deleted file mode 100644 index 7f58ca0bc..000000000 --- a/src/apps/earn/src/components/SearchField/index.jsx +++ /dev/null @@ -1,63 +0,0 @@ -import styles from "./styles.scss"; -import React, { useCallback } from "react"; -import PT from "prop-types"; -import cn from "classnames"; - -import { ReactComponent as IconMagnifier } from "../../assets/icons/icon-magnifier.svg"; - -/** - * Displays search input field. - * - * @param {Object} props component properties - * @param {string} [props.className] class name added to root element - * @param {string} props.id id for input element - * @param {string} props.placeholder placeholder text - * @param {string} props.name name for input element - * @param {'medium'|'small'} [props.size] field size - * @param {function} props.onChange function called when input value changes - * @param {string} props.value input value - * @returns {JSX.Element} - */ -const SearchField = ({ - className, - id, - name, - size = "medium", - onChange, - placeholder, - value, -}) => { - const onInputChange = useCallback( - (event) => { - onChange(event.target.value); - }, - [onChange] - ); - - return ( -
- - -
- ); -}; - -SearchField.propTypes = { - className: PT.string, - id: PT.string.isRequired, - size: PT.oneOf(["medium", "small"]), - name: PT.string.isRequired, - onChange: PT.func.isRequired, - placeholder: PT.string, - value: PT.oneOfType([PT.number, PT.string]), -}; - -export default SearchField; diff --git a/src/apps/earn/src/components/SearchField/styles.scss b/src/apps/earn/src/components/SearchField/styles.scss deleted file mode 100644 index 8460e83c1..000000000 --- a/src/apps/earn/src/components/SearchField/styles.scss +++ /dev/null @@ -1,38 +0,0 @@ -.container { - display: flex; - align-items: center; - border: 1px solid #aaa; - border-radius: 6px; - background-color: #fff; - - &.medium { - height: 40px; - } - - &.small { - height: 30px; - } -} - -.icon { - margin: auto 10px; - width: 16px; - height: 16px; -} - -input.input { - flex: 1 1 0; - margin: 0; - border: none !important; - padding: 8px 16px 8px 0; - height: 22px; - line-height: 22px; - background: none; - outline: none !important; - box-shadow: none !important; - - &::placeholder { - text-transform: none; - color: #aaa; - } -} diff --git a/src/apps/earn/src/components/Select/index.jsx b/src/apps/earn/src/components/Select/index.jsx deleted file mode 100644 index c0e8a5e25..000000000 --- a/src/apps/earn/src/components/Select/index.jsx +++ /dev/null @@ -1,21 +0,0 @@ -import _ from "lodash"; -import ReactSelect from "react-select"; -import PT from "prop-types"; -import styles from "./style.scss"; - -export default function Select(props) { - const { selectRef } = props; - return ( -
- -
- ); -} - -Select.defaultProps = { - selectRef: _.noop, -}; - -Select.propTypes = { - selectRef: PT.func, -}; diff --git a/src/apps/earn/src/components/Select/style.scss b/src/apps/earn/src/components/Select/style.scss deleted file mode 100644 index f823c926c..000000000 --- a/src/apps/earn/src/components/Select/style.scss +++ /dev/null @@ -1,18 +0,0 @@ -.select { - :global { - // @import "react-select/dist/react-select"; - - width: 100%; - - input.Select-input, - input.Select-input:focus { - background-color: transparent !important; - margin-left: 0 !important; - padding-right: 6px !important; - } - - .Select-multi-value-wrapper { - width: 100% !important; - } - } -} diff --git a/src/apps/earn/src/components/SubmissionManagement/Icons/IconCloudDownload.svg b/src/apps/earn/src/components/SubmissionManagement/Icons/IconCloudDownload.svg deleted file mode 100644 index b17e3bc52..000000000 --- a/src/apps/earn/src/components/SubmissionManagement/Icons/IconCloudDownload.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/src/apps/earn/src/components/SubmissionManagement/Icons/IconMinimalDown.svg b/src/apps/earn/src/components/SubmissionManagement/Icons/IconMinimalDown.svg deleted file mode 100644 index 96fdc951e..000000000 --- a/src/apps/earn/src/components/SubmissionManagement/Icons/IconMinimalDown.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/apps/earn/src/components/SubmissionManagement/Icons/IconMinimalLeft.svg b/src/apps/earn/src/components/SubmissionManagement/Icons/IconMinimalLeft.svg deleted file mode 100644 index 287bf690e..000000000 --- a/src/apps/earn/src/components/SubmissionManagement/Icons/IconMinimalLeft.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - diff --git a/src/apps/earn/src/components/SubmissionManagement/Icons/IconMinimalRight.svg b/src/apps/earn/src/components/SubmissionManagement/Icons/IconMinimalRight.svg deleted file mode 100644 index d2905c94b..000000000 --- a/src/apps/earn/src/components/SubmissionManagement/Icons/IconMinimalRight.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - diff --git a/src/apps/earn/src/components/SubmissionManagement/Icons/IconMinimalUp.svg b/src/apps/earn/src/components/SubmissionManagement/Icons/IconMinimalUp.svg deleted file mode 100644 index 9bf3e2d5a..000000000 --- a/src/apps/earn/src/components/SubmissionManagement/Icons/IconMinimalUp.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - diff --git a/src/apps/earn/src/components/SubmissionManagement/Icons/IconSearch.svg b/src/apps/earn/src/components/SubmissionManagement/Icons/IconSearch.svg deleted file mode 100644 index 30f678532..000000000 --- a/src/apps/earn/src/components/SubmissionManagement/Icons/IconSearch.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/apps/earn/src/components/SubmissionManagement/Icons/IconShare.svg b/src/apps/earn/src/components/SubmissionManagement/Icons/IconShare.svg deleted file mode 100644 index 2c731ab66..000000000 --- a/src/apps/earn/src/components/SubmissionManagement/Icons/IconShare.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - diff --git a/src/apps/earn/src/components/SubmissionManagement/Icons/IconSquareDownload.svg b/src/apps/earn/src/components/SubmissionManagement/Icons/IconSquareDownload.svg deleted file mode 100644 index e56fda655..000000000 --- a/src/apps/earn/src/components/SubmissionManagement/Icons/IconSquareDownload.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/apps/earn/src/components/SubmissionManagement/Icons/IconTrashSimple.svg b/src/apps/earn/src/components/SubmissionManagement/Icons/IconTrashSimple.svg deleted file mode 100644 index 5b430a5e3..000000000 --- a/src/apps/earn/src/components/SubmissionManagement/Icons/IconTrashSimple.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - diff --git a/src/apps/earn/src/components/SubmissionManagement/Icons/IconZoom.svg b/src/apps/earn/src/components/SubmissionManagement/Icons/IconZoom.svg deleted file mode 100644 index 30f678532..000000000 --- a/src/apps/earn/src/components/SubmissionManagement/Icons/IconZoom.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/apps/earn/src/components/SubmissionManagement/ScreeningDetails/index.jsx b/src/apps/earn/src/components/SubmissionManagement/ScreeningDetails/index.jsx deleted file mode 100644 index 706ff51f9..000000000 --- a/src/apps/earn/src/components/SubmissionManagement/ScreeningDetails/index.jsx +++ /dev/null @@ -1,133 +0,0 @@ -/** - * This compnent receives via props the screening object and nicely renders them. - * Note that this component both has the text coming directly from the screening object, - * and the text generated based on the screening status - * - */ - -import PT from 'prop-types'; -import shortid from 'shortid'; -import { styled as styledCss } from '@earn/utils'; - -import styles from './styles.scss'; -const styled = styledCss(styles); - -export default function ScreeningDetails(props) { - const { - screeningObject, - helpPageUrl, - } = props; - - const hasWarnings = screeningObject.warnings; - const hasStatus = screeningObject.status; - const hasStatusPassed = hasStatus === 'passed'; - const hasStatusFailed = hasStatus === 'failed'; - const hasPending = screeningObject.status === 'pending'; - const warnLength = screeningObject.warnings && hasWarnings.length; - - const setStatusInfo = () => { - if (hasPending) { - return { - title: 'Pending', - classname: 'pending', - message: 'Your submission has been received, and will be screened after the end of the phase', - }; - } if (hasStatusPassed && !hasWarnings) { - return { - title: 'Passed Screening', - classname: 'passed', - message: 'You have passed screening.', - }; - } if (hasStatusFailed && !hasWarnings) { - return { - title: 'Failed Screening', - classname: 'failed', - message: 'You have failed screening', - }; - } if (hasStatusPassed && hasWarnings) { - return { - title: 'Passed Screening with Warnings', - classname: 'passed', - message: `You have passed screening, but the screener has given you ${warnLength} warnings that you must fix in round 2.`, - }; - } if (hasStatusFailed && hasWarnings) { - return { - title: 'Failed Screening with Warnings', - classname: 'failed', - message: 'You have failed screening and the screener has given you the following warning.', - }; - } - return { - title: '', - classname: '', - message: 'Your submission has been received, and will be evaluated during Review phase.', - }; - }; - - let warnings = []; - if (screeningObject.warnings) { - warnings = screeningObject.warnings.map((warning, i) => ( -
-
- - Warning - - {' '} - {`${1 + i} : ${warning.brief}`} -
-

- {warning.details} -

-
- )); - } - return ( -
-
-

- {setStatusInfo().title} -

- {/* - NOTE: TonyJ asked to remove the OR links from the page to keep - users within the new Topcoder site as much as we can. Not wiping - out the code just in case we decide to bring it back later. - - Online Review - - */} -
-

- {setStatusInfo().message} - - {' '}Need help? - -

-
- {warnings} - {((hasStatusFailed) || (hasStatusPassed && hasWarnings)) - && ( -

- Need more info on how to pass screening? - Go to help to read Rules & Policies. -

- )} -
-
- ); -} - -ScreeningDetails.defaultProps = { - screeningObject: {}, - helpPageUrl: '', -}; - -ScreeningDetails.propTypes = { - screeningObject: PT.shape({ - status: PT.string, - warnings: PT.arrayOf(PT.shape({ - brief: PT.string.isRequired, - details: PT.string.isRequired, - })), - }), - helpPageUrl: PT.string, -}; diff --git a/src/apps/earn/src/components/SubmissionManagement/ScreeningDetails/styles.scss b/src/apps/earn/src/components/SubmissionManagement/ScreeningDetails/styles.scss deleted file mode 100644 index 2310fef9b..000000000 --- a/src/apps/earn/src/components/SubmissionManagement/ScreeningDetails/styles.scss +++ /dev/null @@ -1,88 +0,0 @@ -@import '@earn/styles/mixins'; -$status-space-10: $base-unit * 2; -$status-space-15: $base-unit * 3; -$status-space-20: $base-unit * 4; -$status-space-25: $base-unit * 5; -$gray-color: $tc-gray-80; -$green-color: $tc-green; -$red-color: $tc-red; - -.online-review-link { - background: none; -} - -.screening-details { - font-weight: 500; - font-size: 14px; - color: $tc-black; - letter-spacing: 0; - line-height: 22px; - - .screening-details-head { - display: flex; - margin-bottom: $base-unit; - - .status-title { - font-weight: 700; - color: $tc-orange; - letter-spacing: 0; - line-height: $status-space-20; - - .passed { - color: $green-color; - } - - &.failed { - color: $red-color; - } - - &.pending { - color: $gray-color; - } - } - - .online-review-link { - display: block; - margin-left: auto; - line-height: $status-space-20; - font-weight: 400; - font-size: 13px; - color: $tc-dark-blue-110; - text-decoration: underline; - padding: 0; - border: none; - text-transform: capitalize; - - &:hover { - opacity: 0.7; - } - - &:focus { - outline: none; - } - } - } - - .screening-warning { - margin-top: $status-space-15; - } - - .more-info { - margin-top: $status-space-15; - margin-bottom: $base-unit; - } - - .warning-bold { - font-weight: 700; - line-height: $status-space-20; - } - - .help-btn { - text-align: right; - } - - .help-link { - color: $tc-dark-blue-110; - cursor: pointer; - } -} diff --git a/src/apps/earn/src/components/SubmissionManagement/ScreeningStatus/index.jsx b/src/apps/earn/src/components/SubmissionManagement/ScreeningStatus/index.jsx deleted file mode 100644 index 3110f371e..000000000 --- a/src/apps/earn/src/components/SubmissionManagement/ScreeningStatus/index.jsx +++ /dev/null @@ -1,97 +0,0 @@ -/** - * This component receives the screening object via props. - * Depending on status, presence and count of warnings, - * it renders itself in one of the ways shown in specs. - * When status is pending it renders just 'Not yet performed' text. - * When no screening object received, nothing is rendered. - * - * When hovered this object should update mouse cursor to pointer, - * and on click it should call the onClick() callback passed from parent. - * Parent code will use that signal to show/hide the panel with - * screening details for this submission. - * - */ - -import _ from 'lodash'; -import React from 'react'; -import PT from 'prop-types'; - -import { styled as styledCss } from '@earn/utils'; - -import styles from './styles.scss'; -const styled = styledCss(styles); - -export default function ScreeningStatus(props) { - const { - screeningObject, - onShowDetails, - submissionId, - } = props; - - const hasWarnings = screeningObject.warnings; - const hasStatus = screeningObject.status; - const hasStatusPassed = hasStatus === 'passed'; - const hasStatusFailed = hasStatus === 'failed'; - const hasPending = screeningObject.status === 'pending'; - const warnLength = screeningObject.warnings && hasWarnings.length; - - const setClassName = () => { - if (hasPending) { - return 'pending'; - } if (hasStatusPassed && !hasWarnings) { - return 'pass-with-no-warn'; - } if (hasStatusFailed && !hasWarnings) { - return 'fail-with-no-warn'; - } - return 'has-warn'; - }; - const setStatusClassName = () => { - if (hasStatusPassed && hasWarnings) { - return 'passed'; - } if (hasStatusFailed && hasWarnings) { - return 'failed'; - } - return ''; - }; - return ( - - ); -} - -ScreeningStatus.defaultProps = { - onShowDetails: _.noop, -}; - -ScreeningStatus.propTypes = { - screeningObject: PT.shape({ - status: PT.string, - warnings: PT.array, - }).isRequired, - onShowDetails: PT.func, - submissionId: PT.number.isRequired, -}; diff --git a/src/apps/earn/src/components/SubmissionManagement/ScreeningStatus/styles.scss b/src/apps/earn/src/components/SubmissionManagement/ScreeningStatus/styles.scss deleted file mode 100644 index 80fa75a06..000000000 --- a/src/apps/earn/src/components/SubmissionManagement/ScreeningStatus/styles.scss +++ /dev/null @@ -1,68 +0,0 @@ -@import '@earn/styles/mixins'; -$status-space-10: $base-unit * 2; -$status-space-20: $base-unit * 4; -$status-space-40: $base-unit * 8; -$gray-color: #a3a3ad; -$green-color: #60c700; -$red-color: #f22f24; - -.status { - font-weight: 700; - display: inline-block; - padding: $base-unit $status-space-10; - border-radius: $status-space-40 0 0 $status-space-40; - text-transform: capitalize; - - &.passed { - background: $tc-orange; - } - - &.failed { - background: $red-color; - } -} - -.screening-status { - background: $tc-gray-50; - border-radius: $status-space-40; - font-weight: 400; - font-size: 13px; - line-height: $status-space-20; - color: $tc-white; - padding: $base-unit $status-space-10; - display: inline-block; - text-transform: initial; - cursor: pointer; - - &.has-warn { - padding: 0 $status-space-10 0 0; - } - - &.pass-with-no-warn { - background: $green-color; - - .status { - padding: 0 $base-unit; - } - } - - &.fail-with-no-warn { - background: $red-color; - - .status { - padding: 0 $base-unit; - } - } - - &.pending { - background: transparent; - font-size: 15px; - color: $gray-color; - font-style: italic; - } -} - -.warning { - padding-left: $status-space-10; - display: inline-block; -} diff --git a/src/apps/earn/src/components/SubmissionManagement/Submission/index.jsx b/src/apps/earn/src/components/SubmissionManagement/Submission/index.jsx deleted file mode 100644 index 367db65b9..000000000 --- a/src/apps/earn/src/components/SubmissionManagement/Submission/index.jsx +++ /dev/null @@ -1,145 +0,0 @@ -/** - * This component receives via props a single submission data object, - * and showScreeningDetails boolean property, which should tell whether - * the Screening Details component should be rendered or not - * (and also to choose the proper orientation of arrow icon). - * - * Also, this component will receive the following callbacks to be triggered - * when user clicks on buttons/icons/links: - * onDelete() (to be triggered by delete icon), - * onDownload() (to be triggered by download icon), - * onShowDetails() (to be triggered by details arrow icon, and also by screening status component). - */ - -import _ from 'lodash'; -import moment from 'moment'; -import { COMPETITION_TRACKS, CHALLENGE_STATUS, safeForDownload } from '@earn/utils/tc'; - -import PT from 'prop-types'; - -import { ReactComponent as DeleteIcon } from '../Icons/IconTrashSimple.svg'; -import { ReactComponent as DownloadIcon } from '../Icons/IconSquareDownload.svg'; -import { ReactComponent as ExpandIcon } from '../Icons/IconMinimalDown.svg'; -import ScreeningStatus from '../ScreeningStatus'; -import { styled as styledCss } from '@earn/utils'; - -import styles from './styles.scss'; -const styled = styledCss(styles); - -export default function Submission(props) { - const { - submissionObject, - showScreeningDetails, - track, - onDownload, - onDelete, - onShowDetails, - status, - allowDelete, - } = props; - const formatDate = date => moment(+new Date(date)).format('MMM DD, YYYY hh:mm A'); - const onDownloadSubmission = onDownload.bind(1, submissionObject.id); - const safeForDownloadCheck = safeForDownload(submissionObject.url); - - return ( - - - ID - {submissionObject.legacySubmissionId} -
{submissionObject.id}
- - - TYPE - {submissionObject.type} - - - Submission Date - {formatDate(submissionObject.created)} - - { - track === COMPETITION_TRACKS.DES && ( - - Screening Status - {safeForDownloadCheck !== true ? safeForDownloadCheck : submissionObject.screening - && ( - - )} - - ) - } - -
- - { /* - TODO: At the moment we just fetch downloads from the legacy - Topcoder Studio API, and we don't need any JS code to this. - It may change soon, as we move to the new TC API for - downloads. Then we'll use this commented out code or - remove it for good. - - */ } - {status !== CHALLENGE_STATUS.COMPLETED - && track === COMPETITION_TRACKS.DES - && safeForDownloadCheck === true && ( - - ) - } - -
- - - ); -} - -Submission.defaultProps = { - submissionObject: {}, - showScreeningDetails: false, - onShowDetails: _.noop, -}; - -Submission.propTypes = { - submissionObject: PT.shape({ - id: PT.string, - legacySubmissionId: PT.string, - warpreviewnings: PT.string, - screening: PT.shape({ - status: PT.string, - }), - submitted: PT.string, - type: PT.string, - created: PT.any, - download: PT.any, - url: PT.string, - }), - showScreeningDetails: PT.bool, - track: PT.string.isRequired, - onDownload: PT.func.isRequired, - onDelete: PT.func.isRequired, - onShowDetails: PT.func, - status: PT.string.isRequired, - allowDelete: PT.bool.isRequired, -}; diff --git a/src/apps/earn/src/components/SubmissionManagement/Submission/styles.scss b/src/apps/earn/src/components/SubmissionManagement/Submission/styles.scss deleted file mode 100644 index 3511c9e26..000000000 --- a/src/apps/earn/src/components/SubmissionManagement/Submission/styles.scss +++ /dev/null @@ -1,202 +0,0 @@ -@import '@earn/styles/mixins'; -$submission-space-10: $base-unit * 2; -$submission-space-20: $base-unit * 4; -$submission-space-25: $base-unit * 5; -$submission-space-50: $base-unit * 10; - -.submission-row { - width: 100%; - font-size: 15px; - color: $tc-black; - font-weight: 400; - - @include xs-to-sm { - display: block; - position: relative; - padding: 10px 0; - } - - td { - // vertical-align: middle; - padding: 16px; - background: $tc-white; - border-top: 1px solid $tc-gray-10; - color: $tc-black; - font-weight: 500; - font-size: 14px; - line-height: 22px; - - .mobile-header { - display: none; - - @include xs-to-md { - display: block; - color: #767676; - font-size: 11px; - line-height: 14px; - text-transform: uppercase; - text-align: left; - justify-content: flex-start; - } - } - - @include xs-to-lg { - padding: $submission-space-10; - } - - @include xs-to-sm { - display: block; - border: none; - } - - &.no-submission { - line-height: $submission-space-20; - padding: $submission-space-50 $submission-space-20; - text-align: center; - } - - &.dev-details { - padding: 16px; - - @include xs-to-md { - padding: 0 10px; - } - } - } - - .preview-col { - @include xs-to-sm { - float: left; - } - - .design-img { - width: 90px; - height: 90px; - - @include xs-to-sm { - width: 80px; - height: 80px; - } - } - - .dev-img { - width: 40px; - height: 40px; - } - } - - .id-col { - font-weight: 700; - width: 32%; - - @include xs-to-sm { - width: 80%; - } - } - - .type-col { - width: 21%; - - @include xs-to-sm { - width: 80%; - } - } - - .date-col { - width: 20%; - color: $tc-black; - font-weight: 500; - font-size: 14px; - line-height: 22px; - - @include xs-to-sm { - padding: 0 10px; - width: 80%; - } - } - - .v5-id { - font-weight: 400; - line-height: 22px; - } - - .status-col { - text-align: center; - - button { - background: none; - border: none; - padding: 0; - - .pending { - text-transform: initial; - font-size: 15px; - color: $tc-gray-40; - line-height: $submission-space-20; - } - } - } - - .action-col { - text-align: center; - min-width: 120px; - - @include xs-to-sm { - position: absolute; - right: 0; - top: 10px; - padding: 10px 0; - min-width: 100px; - text-align: right; - - svg { - width: 14px; - height: 14px; - } - } - - .delete-icon { - margin: 0 0 0 24px; - - @include xs-to-sm { - margin-left: 15px; - } - } - - button { - margin-left: 15px; - margin-top: 3px; - background: none; - border: 0; - font-size: 0; - padding: 0; - line-height: 0; - display: inline-block; - - &:focus { - outline: none; - } - - &:disabled { - opacity: 0.5; - } - } - - .expand-icon { - transition: all 1.5s; - margin-left: 24px; - - @include xs-to-sm { - margin-left: 15px; - } - - &.expanded { - transform: rotate(180deg); - } - } - } - - .status-col button:focus { - outline: none; - } -} diff --git a/src/apps/earn/src/components/SubmissionManagement/SubmissionManagement/index.jsx b/src/apps/earn/src/components/SubmissionManagement/SubmissionManagement/index.jsx deleted file mode 100644 index 897e45451..000000000 --- a/src/apps/earn/src/components/SubmissionManagement/SubmissionManagement/index.jsx +++ /dev/null @@ -1,223 +0,0 @@ -/** - * This component should render the entire page assembly, - * but not yet implement the logic behind user actions. - * It still receives submissions and challenge data, all callbacks, etc. from its parent container - * - * Namely, it receives via props: the mock data object (provided along with this specs), - * the showDetails set, and config object holding all necessary callbacks: - * onBack() - to trigger when user clicks Back button under the challenge name; - * onDelete(submissionId); - * onDownload() (to be triggered by download icon) - * onOpenOnlineReview(submissionId); onHelp(submissionId); - * onShowDetails(submissionId); - * onSubmit() - to trigger when user clicks Add Submission button. - */ - -import _ from 'lodash'; -import PT from 'prop-types'; -import moment from 'moment'; - -import { IconOutline, LinkButton, LoadingCircles } from '~/libs/ui'; -import { phaseEndDate } from '@earn/utils/challenge-listing/helper'; - -import SubmissionsTable from '../SubmissionsTable'; - -import styles from './styles.scss'; - -export default function SubmissionManagement(props) { - const { - challenge, - submissions, - loadingSubmissions, - showDetails, - onDelete, - helpPageUrl, - onDownload, - onShowDetails, - challengeUrl, - onlineReviewUrl, - submissionPhaseStartDate, - } = props; - - const { track } = challenge; - - const challengeType = track.toLowerCase(); - - const isDesign = challengeType === 'design'; - const isDevelop = challengeType === 'development'; - const currentPhase = challenge.phases - .filter(p => p.name !== 'Registration' && p.isOpen) - .sort((a, b) => moment(a.scheduledEndDate).diff(b.scheduledEndDate))[0]; - const submissionPhase = challenge.phases.filter(p => p.name === 'Submission')[0]; - const submissionEndDate = submissionPhase && phaseEndDate(submissionPhase); - - const now = moment(); - const end = moment(currentPhase && currentPhase.scheduledEndDate); - const diff = end.isAfter(now) ? end.diff(now) : 0; - const timeLeft = moment.duration(diff); - - const [days, hours, minutes] = [ - timeLeft.get('days'), - timeLeft.get('hours'), - timeLeft.get('minutes'), - ]; - - const componentConfig = { - helpPageUrl, - onDelete, - onDownload: onDownload.bind(0, challengeType), - onlineReviewUrl, - onShowDetails, - }; - return ( -
-
-
- - -

- {challenge.name} -

-
-
-
-
-

- Manage your submissions -

- - {/* { - isDesign && currentPhase && ( -

- - {currentPhase.name} - {' '} - Ends: - - {' '} - {end.format('dddd MM/DD/YY hh:mm A')} -

- ) - } */} -
-
- { - currentPhase && ( -

- Current Deadline:{' '} - {currentPhase.name} -

- ) - } - - { - challenge.status !== 'Completed' ? ( -
-

- Current Deadline Ends: {' '} - - {days > 0 && (`${days}D`)} - {' '} - {hours} - H - {' '} - {minutes} - M - -

-
- ) : ( -

- The challenge has ended -

- ) - } -
- { - isDesign && ( -

- {/* eslint-disable-next-line max-len */} - We always recommend to download your submission to check you uploaded the correct .zip files  - {/* eslint-disable-next-line max-len */} - and also to verify your declarations file is accurate. If you don’t want to see a submission  - {/* eslint-disable-next-line max-len */} - simply delete it. If you have a new submissions, use the “Add Submission” button to add one  - to the top of the list. -

- ) - } - { - isDevelop && ( -

- {/* eslint-disable-next-line max-len */} - We always recommend to download your submission to check you uploaded the correct .zip files  - {/* eslint-disable-next-line max-len */} - and also to verify your declarations file is accurate. If you don’t want to see a submission  - {/* eslint-disable-next-line max-len */} - simply delete it. If you have a new submissions, use the “Add Submission” button to add one  - to the top of the list. -

- ) - } - {loadingSubmissions && } - {!loadingSubmissions - && ( - - ) - } -
- {now.isBefore(submissionEndDate) && ( -
- - { - (!isDevelop || !submissions || submissions.length === 0) - ? 'Add Submission' : 'Update Submission' - } - -
- )} -
- ); -} - -SubmissionManagement.defaultProps = { - onDelete: _.noop, - onShowDetails: _.noop, - onDownload: _.noop, - onlineReviewUrl: '', - helpPageUrl: '', - loadingSubmissions: false, - challengeUrl: '', - submissions: [], -}; - -SubmissionManagement.propTypes = { - showDetails: PT.shape().isRequired, - onDelete: PT.func, - onlineReviewUrl: PT.string, - helpPageUrl: PT.string, - onDownload: PT.func, - onShowDetails: PT.func, - challenge: PT.shape().isRequired, - submissions: PT.arrayOf(PT.shape()), - loadingSubmissions: PT.bool, - challengeUrl: PT.string, - submissionPhaseStartDate: PT.string.isRequired, -}; diff --git a/src/apps/earn/src/components/SubmissionManagement/SubmissionManagement/styles.scss b/src/apps/earn/src/components/SubmissionManagement/SubmissionManagement/styles.scss deleted file mode 100644 index 8c884499f..000000000 --- a/src/apps/earn/src/components/SubmissionManagement/SubmissionManagement/styles.scss +++ /dev/null @@ -1,286 +0,0 @@ -@import '@earn/styles/mixins'; -$submang-space-140: $base-unit * 28; -$submang-space-50: $base-unit * 10; -$submang-space-35: $base-unit * 7; -$submang-space-30: $base-unit * 6; -$submang-space-25: $base-unit * 5; -$submang-space-20: $base-unit * 4; -$gray-color: $tc-gray-40; -$light-gray-color: $tc-gray-neutral-light; - -.btn-wrap { - display: flex; - justify-content: flex-end; - margin-right: 32px; - margin-top: 34px; - color: white; - - @include xs-to-md { - margin-right: 16px; - margin-top: 32px; - } -} - - -.add-sub-btn-warning { - @include roboto-bold; - - font-weight: 700; - font-size: 16px; - line-height: 24px; - border-radius: 50px !important; - background: $tc-red-70 !important; - color: #fff !important; - text-transform: uppercase; - - @include xs-to-md { - font-size: 14px; - line-height: 20px; - } -} - -.submission-management { - padding-bottom: 40px; -} - -.submission-management-content { - padding: 0 32px; - - @include md { - padding: $submang-space-20 $submang-space-35; - } - - @include xs-to-sm { - padding: 0; - } - - .content-head { - display: flex; - justify-content: space-between; - font-weight: 400; - font-size: 15px; - color: $tc-gray-50; - line-height: $submang-space-25; - margin-top: 36px; - margin-bottom: $base-unit; - - @include xs-to-sm { - flex-direction: column; - margin-top: $submang-space-20; - padding: 0 15px; - } - - .title { - @include barlow-bold; - - font-weight: 600; - font-size: 24px; - color: $tc-black; - line-height: 28px; - text-transform: uppercase; - - @include xs-to-md { - font-size: 20px; - line-height: 22px; - } - } - - .round-ends { - font-size: 15px; - color: $tc-black; - line-height: $submang-space-30; - - @include xs-to-sm { - margin-top: 10px; - margin-bottom: 15px; - } - - span.ends-label { - color: $tc-gray-50; - - @include xs-to-sm { - display: block; - margin-bottom: -10px; - } - } - } - } - - .subTitle { - display: flex; - margin-top: 12px; - - @include xs-to-md { - margin: 17px 16px 0 16px; - flex-direction: column; - } - - .round { - @include roboto-regular; - - font-weight: 400; - font-size: 16px; - line-height: 24px; - color: $tc-black; - - span { - @include roboto-bold; - - font-weight: 700; - } - - @include xs-to-md { - font-size: 14px; - line-height: 20px; - } - } - - .seperator { - width: 2px; - height: 24px; - left: 228px; - top: 0; - background: #d4d4d4; - border-radius: 1px; - margin: 0 16px; - - @include xs-to-md { - display: none; - } - } - } - - .recommend-info { - @include roboto-regular; - - font-size: 16px; - font-weight: 400; - color: $tc-black; - line-height: 24px; - margin-bottom: $submang-space-30; - margin-top: 40px; - - @include xs-to-sm { - padding: 0 15px; - margin-top: 32px; - font-size: 14px; - line-height: 20px; - } - } -} - -.submission-management-header { - display: flex; - justify-content: space-between; - width: 100%; - font-weight: 400; - padding: $submang-space-25 32px 0 32px; - background: #fff; - border-bottom: 1px solid $tc-gray-neutral-dark; - - @include md { - padding: $submang-space-25 $submang-space-35; - } - - @include xs-to-sm { - flex-direction: column; - padding: 9px 16px; - } - - .left-col { - display: flex; - padding-right: 200px; - margin-top: 15px; - margin-bottom: 40px; - - @include xs-to-sm { - padding-right: 0; - display: flex; - flex-direction: column; - } - - .name { - @include barlow-bold; - - font-size: 34px; - color: $tc-black; - line-height: 32px; - font-weight: 600; - margin-left: 16px; - margin-top: -6px; - - @include xs-to-sm { - font-size: 28px; - line-height: 32px; - margin-top: 0; - margin-left: 0; - } - } - - .back-btn { - @include roboto-bold; - - font-weight: 700; - font-size: 16px; - line-height: 24px; - border: 0; - color: #137d60 !important; - width: 32px; - height: 24px; - position: relative; - - @include xs-to-md { - margin-left: 0; - margin-bottom: 8px; - } - - svg { - border: 1.5px solid #137d60; - border-radius: 24px; - - @include xs-to-md { - margin-left: 0; - margin-bottom: 15px; - } - } - } - } - - .right-col { - @include roboto-regular; - - min-width: 100px; - - @include xs-to-sm { - margin-top: 10px; - - p, - div { - display: inline-block; - } - } - - .time-left { - font-size: 20px; - color: $tc-gray-80; - line-height: $submang-space-30; - - @include xs-to-sm { - font-size: 15px; - font-weight: bold; - line-height: $submang-space-25; - margin-left: 5px; - } - } - - .left-label { - font-size: 13px; - color: $tc-gray-50; - line-height: $submang-space-20; - - @include xs-to-sm { - margin-left: 5px; - } - } - } -} diff --git a/src/apps/earn/src/components/SubmissionManagement/SubmissionsTable/index.jsx b/src/apps/earn/src/components/SubmissionManagement/SubmissionsTable/index.jsx deleted file mode 100644 index d55aeb8b9..000000000 --- a/src/apps/earn/src/components/SubmissionManagement/SubmissionsTable/index.jsx +++ /dev/null @@ -1,153 +0,0 @@ -/** - * This component will render the entire assemly of submissions table. - * It receives via props the array of submission data objects; - * a showDetails set with submission IDs for which details panel - * should be shown; and type property (design or develop) to know - * whether the screening status column should be rendered. - * - * Also it will receive a bunch of callbacks which should be properly - * wired to the children components: - * onDelete(submissionId) – to delete specified submission; - * onDownload(submissionId) – to download the specified submission; - * onOpenOnlineReview(submissionId); - * onHelp(submissionId); - * onShowDetails(submissionId). - */ - -import _ from 'lodash'; -import React from 'react'; -import PT from 'prop-types'; -import shortid from 'shortid'; -import moment from 'moment'; -import { COMPETITION_TRACKS } from '@earn/utils/tc'; -import Submission from '../Submission'; -import ScreeningDetails from '../ScreeningDetails'; -import styles from './styles.scss'; - -export default function SubmissionsTable(props) { - const { - submissionObjects, - showDetails, - track, - onDelete, - onlineReviewUrl, - helpPageUrl, - onDownload, - onShowDetails, - status, - submissionPhaseStartDate, - } = props; - - const submissionsWithDetails = []; - if (!submissionObjects || submissionObjects.length === 0) { - submissionsWithDetails.push(( - - - You have no submission uploaded so far. - - - )); - } else { - submissionObjects.forEach((subObject) => { - // submissionPhaseStartDate will be the start date of - // the current submission/checkpoint or empty string if any other phase - const allowDelete = submissionPhaseStartDate - && moment(subObject.submissionDate).isAfter(submissionPhaseStartDate); - - const submission = ( - - ); - submissionsWithDetails.push(submission); - - const submissionDetail = ( - - {showDetails[subObject.id] - && ( - - - - )} - - ); - submissionsWithDetails.push(submissionDetail); - }); - } - - return ( -
- - - - - - - {track === COMPETITION_TRACKS.DES && ( - - )} - - - - - {submissionsWithDetails} - -
- ID - - Type - - Submission Date - - Screening Status - - Actions -
-
- ); -} - -const SubShape = PT.shape({ - id: PT.string, - warpreviewnings: PT.string, - screening: PT.shape({ - status: PT.string, - }), - submitted: PT.string, - track: PT.string, -}); - -SubmissionsTable.defaultProps = { - submissionObjects: [], - onDelete: _.noop, - onDownload: _.noop, - onShowDetails: _.noop, - onlineReviewUrl: '', - helpPageUrl: '', -}; - -SubmissionsTable.propTypes = { - submissionObjects: PT.arrayOf(SubShape), - showDetails: PT.shape().isRequired, - track: PT.string.isRequired, - onDelete: PT.func, - onlineReviewUrl: PT.string, - helpPageUrl: PT.string, - onDownload: PT.func, - onShowDetails: PT.func, - status: PT.string.isRequired, - submissionPhaseStartDate: PT.string.isRequired, -}; diff --git a/src/apps/earn/src/components/SubmissionManagement/SubmissionsTable/styles.scss b/src/apps/earn/src/components/SubmissionManagement/SubmissionsTable/styles.scss deleted file mode 100644 index 498a0fac7..000000000 --- a/src/apps/earn/src/components/SubmissionManagement/SubmissionsTable/styles.scss +++ /dev/null @@ -1,164 +0,0 @@ -@import '@earn/styles/mixins'; -$status-space-10: $base-unit * 2; -$status-space-20: $base-unit * 4; -$status-space-25: $base-unit * 5; -$submission-space-10: $base-unit * 2; -$submission-space-20: $base-unit * 4; -$submission-space-25: $base-unit * 5; -$submission-space-50: $base-unit * 10; - -.submissions-table { - // border: 1px solid $tc-gray-10; - overflow: hidden; - // border-radius: 4px 4px 0 0; - - @include xs-to-sm { - border: none; - border-radius: 0; - border-top: 1px solid $tc-gray-10; - border-bottom: 1px solid $tc-gray-10; - margin: 0 16px; - } - - table { - width: 100%; - - th { - @include barlow-bold; - - font-size: 11px; - color: #767676; - font-weight: 600; - line-height: $status-space-20; - text-align: left; - padding: 0 15px 11px 15px; - text-transform: uppercase; - - &.status, - &.actions { - text-align: center; - } - - @include xs-to-sm { - display: none; - } - } - - .no-submission { - line-height: $submission-space-20; - padding: $submission-space-50 $submission-space-20; - text-align: center; - } - } - - .status-col { - text-align: center; - } - - .action-col { - text-align: center; - } -} - -.submission-row { - width: 100%; - font-size: 15px; - color: $tc-black; - font-weight: 400; - - td { - vertical-align: middle; - padding: 16px 16px 2px 16px; - background: $tc-white; - font-size: 14px; - color: $tc-black; - font-weight: 500; - border-top: 1px solid $tc-gray-10; - line-height: 12px; - - @include xs-to-md { - padding: 16px 16px 2px 8px; - } - - &.no-submission { - line-height: $submission-space-20; - padding: $submission-space-50 $submission-space-20; - text-align: center; - } - - &.dev-details { - padding-right: 60px; - border-top: 0; - padding-top: 0; - } - } - - .id-col { - font-weight: 700; - width: 32%; - } - - .type-col { - width: 21%; - } - - .date-col { - width: 20%; - color: $tc-black; - font-weight: 500; - line-height: 22px; - font-size: 14px; - } - - .status-col { - text-align: center; - - button { - background: none; - border: none; - padding: 0; - - .pending { - text-transform: initial; - font-size: 15px; - color: $tc-gray-40; - line-height: $submission-space-20; - } - } - } - - .action-col { - text-align: center; - - .delete-icon { - margin: 0 $submission-space-25; - } - - button { - background: none; - border: 0; - font-size: 0; - padding: 0; - line-height: 0; - display: inline-block; - margin-left: 15px; - margin-top: 3px; - - &:focus { - outline: none; - } - } - - .expand-icon { - transition: all 1.5s; - - &.expanded { - transform: rotate(180deg); - } - } - } - - .status-col button:focus { - outline: none; - } -} diff --git a/src/apps/earn/src/components/Tag/index.jsx b/src/apps/earn/src/components/Tag/index.jsx deleted file mode 100644 index bdbf35d55..000000000 --- a/src/apps/earn/src/components/Tag/index.jsx +++ /dev/null @@ -1,24 +0,0 @@ -import React from "react"; -import PT from "prop-types"; - -import styles from "./styles.scss"; - -const Tag = ({ tag, onClick }) => ( - { - onClick(tag); - }} - > - {tag} - -); - -Tag.propTypes = { - tag: PT.string, - onClick: PT.func, -}; - -export default Tag; diff --git a/src/apps/earn/src/components/Tag/styles.scss b/src/apps/earn/src/components/Tag/styles.scss deleted file mode 100644 index 81e4dc9f0..000000000 --- a/src/apps/earn/src/components/Tag/styles.scss +++ /dev/null @@ -1,13 +0,0 @@ -@import "@earn/styles/variables"; - -.tag { - display: inline-block; - padding: 3px 4px 3px 5px; - font-size: $font-size-xs; - line-height: 15px; - letter-spacing: 0.55px; - white-space: nowrap; - border: 1px solid $body-color; - border-radius: $border-radius-sm; - cursor: pointer; -} diff --git a/src/apps/earn/src/components/TagList/index.jsx b/src/apps/earn/src/components/TagList/index.jsx deleted file mode 100644 index 4db83b3eb..000000000 --- a/src/apps/earn/src/components/TagList/index.jsx +++ /dev/null @@ -1,148 +0,0 @@ -import { - useCallback, - useEffect, - useMemo, - useRef, - useState, -} from "react"; -import PT from "prop-types"; - -import { Tooltip } from "~/libs/ui"; - -import styles from "./styles.scss"; - -/** - * Displays a tooltip with tags that are not initially shown. - * - * @param {Object} props component properties - * @returns {JSX.Element} - */ -const TagsTooltip = ({ children, onClickTag, renderTag, tags }) => { - const overlay = useMemo( - () => ( -
- {tags - .map((tag) => ({ - tag, - onClickTag, - className: styles.tooltipTag, - })) - .map(renderTag)} -
- ), - [onClickTag, renderTag, tags] - ); - return ( - - {children} - - ); -}; - -/** - * Displays a tag list. Initially displays only the first line of tags. - * - * @param {Object} props component properties - * @param {string} [props.className] class name added to root element - * @param {number} [props.maxTagCount] maximum number of tags to show - * @param {(t: any) => void} [props.onClickTag] function called when tag is clicked - * @param {(t: any) => JSX.Element} props.renderTag function that renders a tag - * @param {Array} props.tags array of tags - * @returns {JSX.Element} - */ -const TagList = ({ - className, - maxTagCount = 5, - onClickTag, - renderTag, - tags, -}) => { - const [isVisible, setIsVisible] = useState(false); - const [showAll, setShowAll] = useState(false); - const [tagsVisible, setTagsVisible] = useState(tags); - - const containerRef = useRef(); - - const onClickBtnMore = useCallback( - (event) => { - event.preventDefault(); - setTagsVisible(tags); - setShowAll(true); - }, - [tags] - ); - - useEffect(() => { - let index = getFirstLineLastElemIndex(containerRef.current); - index = index < 0 ? maxTagCount : Math.min(index, maxTagCount); - if (tags.length < index + 1) { - setShowAll(true); - setTagsVisible(tags); - } else { - setTagsVisible(tags.slice(0, index)); - } - setIsVisible(true); - }, [tags, maxTagCount]); - - return ( -
- {tagsVisible - .map((tag) => ({ className: styles.tag, onClickTag, tag })) - .map(renderTag)} - {!showAll && ( - - - {tags.length - tagsVisible.length}+ - - - )} -
- ); -}; - -TagList.propTypes = { - className: PT.string, - maxTagCount: PT.number, - onClickTag: PT.func.isRequired, - renderTag: PT.func.isRequired, - tags: PT.arrayOf(PT.object).isRequired, -}; - -export default TagList; - -/** - * Searches for the index of the last element on the first line. - * - * @param {Element} container container element - * @returns {number} first line's last element's index - */ -function getFirstLineLastElemIndex(container) { - const firstElement = container.firstElementChild; - let offsetTop = firstElement.offsetTop; - for ( - let elem = firstElement.nextElementSibling, index = 1; - elem; - elem = elem.nextElementSibling, index++ - ) { - if (elem.offsetTop > offsetTop) { - return index - 1; - } - } - return -1; -} diff --git a/src/apps/earn/src/components/TagList/styles.scss b/src/apps/earn/src/components/TagList/styles.scss deleted file mode 100644 index 5fd9114c1..000000000 --- a/src/apps/earn/src/components/TagList/styles.scss +++ /dev/null @@ -1,56 +0,0 @@ -@import "../../styles/mixins"; -@import "../../styles/variables"; - -.container { - position: relative; - display: flex; - flex-wrap: wrap; - height: 27px; - opacity: 0; - - &.visible { - height: auto; - opacity: 1; - } -} - -.btn-more, -.tag { - margin-top: 4px; - margin-right: 4px; - border: 1px solid $body-color; - border-radius: 5px; - padding: 4px 6px 2px; - font-size: 12px; - line-height: 15px; - cursor: pointer; - - &:last-child { - margin-right: 0; - } -} - -.tooltip-tag-list { - display: flex; - flex-wrap: wrap; - background-color: transparent; -} - -.tooltipTag { - flex: 0 0 auto; - margin-top: 4px; - margin-right: 4px; - border-radius: 5px; - padding: 4px 5px 4px 6px; - @include roboto-regular; - font-size: 11px; - letter-spacing: 0.5px; - line-height: 15px; - color: #fff; - background-color: #555; - cursor: pointer; - - &:last-child { - margin-right: 0; - } -} diff --git a/src/apps/earn/src/components/Terms/TermDetails.jsx b/src/apps/earn/src/components/Terms/TermDetails.jsx deleted file mode 100644 index 8aaff868c..000000000 --- a/src/apps/earn/src/components/Terms/TermDetails.jsx +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Terms details component which display text of an agreement - */ - -import React from "react"; -import PT from "prop-types"; - -import { styled as styledCss } from "@earn/utils"; -import { LoadingCircles } from "~/libs/ui"; - -import styles from "./TermDetails.module.scss"; - -const styled = styledCss(styles); - -export default class TermDetails extends React.Component { - constructor(props) { - super(props); - this.state = { - loadingFrame: false, - }; - this.frameLoaded = this.frameLoaded.bind(this); - } - - componentWillMount() { - const { details, getDocuSignUrl } = this.props; - if ( - details.agreeabilityType !== "Electronically-agreeable" && - details.docusignTemplateId - ) { - getDocuSignUrl(details.docusignTemplateId); - this.setState({ loadingFrame: true }); - } - } - - frameLoaded() { - this.setState({ - loadingFrame: false, - }); - } - - render() { - const { details, docuSignUrl, loadingDocuSignUrl } = this.props; - const { loadingFrame } = this.state; - - return ( -
- {details.agreeabilityType === "Electronically-agreeable" && ( -
-
-
- )} - {details.agreeabilityType !== "Electronically-agreeable" && - details.docusignTemplateId === loadingDocuSignUrl && ( - - )} - {details.agreeabilityType !== "Electronically-agreeable" && - details.docusignTemplateId && - !loadingDocuSignUrl && - docuSignUrl && ( -
- {loadingFrame && } -