diff --git a/.circleci/config.yml b/.circleci/config.yml index 26ede8fd0..d49a32f50 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -37,6 +37,12 @@ save_cache_settings: &save_cache_settings paths: - node_modules +running_yarn_tslint: &running_yarn_tslint + name: Running Yarn tslint + command: | + yarn add tslint -g + yarn lint + running_yarn_build: &running_yarn_build name: Running Yarn Build command: | @@ -58,6 +64,12 @@ build_configuration_fetch: &build_configuration_fetch ./awsconfiguration.sh $DEPLOY_ENV ./buildenv.sh -e $DEPLOY_ENV -b ${LOGICAL_ENV}-${APPNAME}-buildvar +lint_steps: &lint_steps + # Initialization. + - checkout + - setup_remote_docker + - run: *running_yarn_tslint + build_steps: &build_steps # Initialization. - checkout @@ -83,6 +95,22 @@ deploy_steps: &deploy_steps ./master_deploy.sh -d CFRONT -e $DEPLOY_ENV -c $ENABLE_CACHE jobs: + lint-dev: + <<: *defaults + environment: + DEPLOY_ENV: "DEV" + LOGICAL_ENV: "dev" + APPNAME: "platform-ui-mvp" + steps: *lint_steps + + lint-prod: + <<: *defaults + environment: + DEPLOY_ENV: "PROD" + LOGICAL_ENV: "prod" + APPNAME: "platform-ui-mvp" + steps: *lint_steps + build-dev: <<: *defaults environment: @@ -122,6 +150,20 @@ workflows: version: 2 build: jobs: + - lint-dev: + context : org-global + filters: + branches: + ignore: + - master + + - lint-prod: + context : org-global + filters: + branches: + only: + - master + - build-dev: context : org-global filters: diff --git a/README.md b/README.md index 44b167472..4951a9a68 100644 --- a/README.md +++ b/README.md @@ -2,18 +2,30 @@ The Platform UI is the official Topcoder web app to host all modern user interfaces to be used by all users. -Beginning March, 2022 all future user interfaces at Topcoder will be implemented here. Pre-existing user interfaces will be ported to here over time until this is the only user interface any user sees when interacting with Topcoder. +All future user interfaces at Topcoder will be implemented here. +Pre-existing user interfaces will be ported to here over time until this is the only user interface any user sees when interacting with Topcoder. + +>**NOTE:** The information in this file describes our coding standards and best practices. All new code should follow these guidelines both when coding new features as well as porting old features. Please take the time to read through this file in detail. + +# Getting started with local development - [Local Environment Setup](#local-environment-setup) - [Deployments](#deployments) - [Yarn Commands](#yarn-commands) + +# Application structure + - [Folder Structure](#folder-structure) - [Adding a Tool or Util](#adding-a-tool-or-util) + +# Coding Practices - [Git](#git) - [Linting](#linting) - [Styling](#styling) - [Icons](#icons) +--- + ## Local Environment Setup ### Dependencies @@ -393,6 +405,23 @@ example.scss } } ``` + +Mobile UIs use xs, sm, and md breakpoints. Larger breakpoints are desktop UIs. + +For specifying mobile CSS, you can use @include ltemd: +``` +.exampleDesktopContent { + display: flex; + width: 100%; + flex-direction: column; + + @include ltemd { + flex-direction: row; + } +} +``` + + >**WARNING:** Do not add any breakpoints! ## Icons diff --git a/package.json b/package.json index afde5d6ec..1fa4fdc1f 100644 --- a/package.json +++ b/package.json @@ -6,21 +6,25 @@ "start": "sh start-ssl.sh", "start:bsouza": "sh start-ssl-bsouza.sh", "build": "yarn react-app-rewired build", - "lint": "tslint 'src-ts/**/*.{ts,tsx}'", - "lint:fix": "tslint 'src-ts/**/*.{ts,tsx}' --fix", + "lint": "tslint 'src-ts/**/*.{ts,tsx}' && eslint 'src*/**/*.{js,jsx,ts,tsx}'", + "lint:fix": "tslint 'src-ts/**/*.{ts,tsx}' --fix && eslint 'src*/**/*.{js,jsx,ts,tsx}' --fix", + "tslint": "tslint 'src-ts/**/*.{ts,tsx}'", + "tslint:fix": "tslint 'src-ts/**/*.{ts,tsx}' --fix", "eslint": "eslint 'src/**/*.{js,jsx}'", "eslint:fix": "eslint 'src/**/*.{js,jsx}' --fix", "test": "react-scripts test --watchAll", "test:no-watch": "react-scripts test --watchAll=false --passWithNoTests" }, "dependencies": { - "@datadog/browser-logs": "^4.5.0", + "@datadog/browser-logs": "^4.7.1", "@heroicons/react": "^1.0.6", "apexcharts": "^3.35.3", "axios": "^0.26.1", "browser-cookies": "^1.2.0", "classnames": "^2.3.1", "crypto-js": "^4.1.1", + "customize-cra": "^1.0.0", + "html2canvas": "^1.4.1", "lodash": "^4.17.21", "moment": "^2.29.3", "moment-timezone": "^0.5.34", @@ -28,6 +32,7 @@ "rc-checkbox": "^2.3.2", "react": "^17.0.2", "react-apexcharts": "^1.4.0", + "react-app-rewired": "^2.2.1", "react-dom": "^17.0.2", "react-elastic-carousel": "^0.11.5", "react-gtm-module": "^2.0.11", @@ -46,10 +51,9 @@ "redux-thunk": "^2.4.1", "sass": "^1.49.8", "styled-components": "^5.3.5", - "tc-auth-lib": "topcoder-platform/tc-auth-lib#1.0.3", - "typescript": "^4.4.2", - "uuid": "^8.3.2", - "web-vitals": "^2.1.0" + "tc-auth-lib": "topcoder-platform/tc-auth-lib#1.0.4", + "typescript": "^4.6.3", + "uuid": "^8.3.2" }, "devDependencies": { "@babel/core": "^7.7.5", @@ -67,14 +71,16 @@ "@types/axios": "^0.14.0", "@types/jest": "^27.0.1", "@types/lodash": "^4.14.182", - "@types/node": "^16.7.13", + "@types/node": "^17.0.24", "@types/reach__router": "^1.3.10", - "@types/react": "^17.0.20", - "@types/react-dom": "^17.0.9", + "@types/react": "^18.0.5", + "@types/react-dom": "^18.0.1", "@types/react-gtm-module": "^2.0.1", "@types/react-redux-toastr": "^7.6.2", "@types/react-router-dom": "^5.3.3", + "@types/segment-analytics": "^0.0.34", "@types/systemjs": "^6.1.0", + "@types/uuid": "^8.3.4", "autoprefixer": "^9.8.6", "babel-eslint": "^11.0.0-beta.2", "babel-jest": "^24.9.0", @@ -84,23 +90,22 @@ "concurrently": "^5.0.1", "config": "^3.3.6", "cross-env": "^7.0.2", - "customize-cra": "^1.0.0", "eslint": "^8.18.0", "eslint-config-prettier": "^6.7.0", "eslint-config-react-app": "^7.0.1", "eslint-config-react-important-stuff": "^2.0.0", "eslint-plugin-prettier": "^3.1.1", "file-loader": "^6.2.0", + "husky": "^8.0.0", "identity-obj-proxy": "^3.0.0", "jest": "^25.2.7", "jest-cli": "^25.2.7", + "lint-staged": "^13.0.3", "postcss-loader": "^4.0.4", "postcss-scss": "^3.0.2", "prettier": "^2.0.4", "pretty-quick": "^2.0.1", - "react-app-rewired": "^2.2.1", "resolve-url-loader": "^3.1.2", - "sass": "^1.48.0", "sass-loader": "^10.0.5", "style-loader": "^2.0.0", "systemjs-webpack-interop": "^2.1.2", diff --git a/src-ts/App.tsx b/src-ts/App.tsx index 830c8d433..10e9900f5 100644 --- a/src-ts/App.tsx +++ b/src-ts/App.tsx @@ -2,12 +2,8 @@ import { FC, ReactElement, useContext } from 'react' import { Routes } from 'react-router-dom' import { toast, ToastContainer } from 'react-toastify' -import { EnvironmentConfig } from './config' import { Header } from './header' -import { analyticsInitialize, logInitialize, routeContext, RouteContextData } from './lib' - -analyticsInitialize(EnvironmentConfig) -logInitialize(EnvironmentConfig) +import { routeContext, RouteContextData } from './lib' const App: FC<{}> = () => { diff --git a/src-ts/config/constants.ts b/src-ts/config/constants.ts index f52749e6c..3d35f3335 100644 --- a/src-ts/config/constants.ts +++ b/src-ts/config/constants.ts @@ -1,4 +1,5 @@ export enum ToolTitle { + learn = 'Learn', settings = 'Account Settings', work = 'Work', } diff --git a/src-ts/config/environments/environment.default.config.ts b/src-ts/config/environments/environment.default.config.ts index b7bf62fa7..f1339ec3b 100644 --- a/src-ts/config/environments/environment.default.config.ts +++ b/src-ts/config/environments/environment.default.config.ts @@ -2,7 +2,13 @@ import { GlobalConfig } from '../../lib' import { AppHostEnvironment } from './app-host-environment.enum' +const COMMUNITY_WEBSITE: string = 'https://www.topcoder-dev.com' + export const EnvironmentConfigDefault: GlobalConfig = { + ANALYTICS: { + SEGMENT_KEY: undefined, + TAG_MANAGER_ID: undefined, + }, API: { FORUM_ACCESS_TOKEN: 'va.JApNvUOx3549h20I6tnl1kOQDc75NDIp.0jG3dA.EE3gZgV', FORUM_V2: 'https://vanilla.topcoder-dev.com/api/v2', @@ -10,12 +16,26 @@ export const EnvironmentConfigDefault: GlobalConfig = { V5: 'https://api.topcoder-dev.com/v5', }, ENV: AppHostEnvironment.default, + LEARN_SRC: 'https://fcc.topcoder-dev.com:4431', LOGGING: { PUBLIC_TOKEN: 'puba0825671e469d16f940c5a30dc738f11', SERVICE: 'platform-ui', }, REAUTH_OFFSET: 55, - TAG_MANAGER_ID: undefined, + // TODO: Move stripe creds to .env file + STRIPE: { + ADMIN_TOKEN: + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIiwiYWRtaW5pc3RyYXRvciJdLCJpc3MiOiJodHRwczovL2FwaS50b3Bjb2Rlci1kZXYuY29tIiwiaGFuZGxlIjoidGVzdDEiLCJleHAiOjI1NjMwNzY2ODksInVzZXJJZCI6IjQwMDUxMzMzIiwiaWF0IjoxNDYzMDc2MDg5LCJlbWFpbCI6InRlc3RAdG9wY29kZXIuY29tIiwianRpIjoiYjMzYjc3Y2QtYjUyZS00MGZlLTgzN2UtYmViOGUwYWU2YTRhIn0.wKWUe0-SaiFVN-VR_-GwgFlvWaDkSbc8H55ktb9LAVw', + API_KEY: 'pk_test_rfcS49MHRVUKomQ9JgSH7Xqz', + API_VERSION: '2020-08-27', + CUSTOMER_TOKEN: + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJ0ZXN0MSIsImV4cCI6MjU2MzA3NjY4OSwidXNlcklkIjoiNDAwNTEzMzMiLCJpYXQiOjE0NjMwNzYwODksImVtYWlsIjoidGVzdEB0b3Bjb2Rlci5jb20iLCJqdGkiOiJiMzNiNzdjZC1iNTJlLTQwZmUtODM3ZS1iZWI4ZTBhZTZhNGEifQ.jl6Lp_friVNwEP8nfsfmL-vrQFzOFp2IfM_HC7AwGcg', + }, + TOPCODER_URLS: { + CHALLENGES_PAGE: `${COMMUNITY_WEBSITE}/challenges`, + GIGS_PAGE: `${COMMUNITY_WEBSITE}/gigs`, + USER_PROFILE: `${COMMUNITY_WEBSITE}/members`, + }, URL: { ACCOUNTS_APP_CONNECTOR: 'https://accounts-auth0.topcoder-dev.com', }, diff --git a/src-ts/config/environments/environment.dev.config.ts b/src-ts/config/environments/environment.dev.config.ts index 5a3256772..2bebd3fdc 100644 --- a/src-ts/config/environments/environment.dev.config.ts +++ b/src-ts/config/environments/environment.dev.config.ts @@ -1,14 +1,25 @@ import { GlobalConfig } from '../../lib' -import { ToolTitle } from '../constants' import { AppHostEnvironment } from './app-host-environment.enum' import { EnvironmentConfigDefault } from './environment.default.config' export const EnvironmentConfigDev: GlobalConfig = { ...EnvironmentConfigDefault, - DISABLED_TOOLS: [ - ToolTitle.designLib, - ], + ANALYTICS: { + SEGMENT_KEY: EnvironmentConfigDefault.ANALYTICS.SEGMENT_KEY, + TAG_MANAGER_ID: 'GTM-MXXQHG8', + // TAG_MANAGER_ID: 'GTM-W7B537Z', + }, + DISABLED_TOOLS: [], ENV: AppHostEnvironment.dev, - TAG_MANAGER_ID: 'GTM-W7B537Z', + LEARN_SRC: 'https://freecodecamp.topcoder-dev.com', + // TODO: Move stripe creds to .env file + STRIPE: { + ADMIN_TOKEN: + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIiwiYWRtaW5pc3RyYXRvciJdLCJpc3MiOiJodHRwczovL2FwaS50b3Bjb2Rlci1kZXYuY29tIiwiaGFuZGxlIjoidGVzdDEiLCJleHAiOjI1NjMwNzY2ODksInVzZXJJZCI6IjQwMDUxMzMzIiwiaWF0IjoxNDYzMDc2MDg5LCJlbWFpbCI6InRlc3RAdG9wY29kZXIuY29tIiwianRpIjoiYjMzYjc3Y2QtYjUyZS00MGZlLTgzN2UtYmViOGUwYWU2YTRhIn0.wKWUe0-SaiFVN-VR_-GwgFlvWaDkSbc8H55ktb9LAVw', + API_KEY: 'pk_test_rfcS49MHRVUKomQ9JgSH7Xqz', + API_VERSION: '2020-08-27', + CUSTOMER_TOKEN: + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJ0ZXN0MSIsImV4cCI6MjU2MzA3NjY4OSwidXNlcklkIjoiNDAwNTEzMzMiLCJpYXQiOjE0NjMwNzYwODksImVtYWlsIjoidGVzdEB0b3Bjb2Rlci5jb20iLCJqdGkiOiJiMzNiNzdjZC1iNTJlLTQwZmUtODM3ZS1iZWI4ZTBhZTZhNGEifQ.jl6Lp_friVNwEP8nfsfmL-vrQFzOFp2IfM_HC7AwGcg', + }, } diff --git a/src-ts/config/environments/environment.prod.config.ts b/src-ts/config/environments/environment.prod.config.ts index 1bb9d32d8..493bfdad9 100644 --- a/src-ts/config/environments/environment.prod.config.ts +++ b/src-ts/config/environments/environment.prod.config.ts @@ -1,19 +1,39 @@ import { GlobalConfig } from '../../lib' -import { ToolTitle } from '../constants' import { AppHostEnvironment } from './app-host-environment.enum' import { EnvironmentConfigDefault } from './environment.default.config' +const COMMUNITY_WEBSITE: string = 'https://www.topcoder.com' + export const EnvironmentConfigProd: GlobalConfig = { ...EnvironmentConfigDefault, + ANALYTICS: { + SEGMENT_KEY: '8fCbi94o3ruUUGxRRGxWu194t6iVq9LH', + TAG_MANAGER_ID: 'GTM-MXXQHG8', + }, API: { + FORUM_ACCESS_TOKEN: EnvironmentConfigDefault.API.FORUM_ACCESS_TOKEN, FORUM_V2: 'https://vanilla.topcoder.com/api/v2', V3: 'https://api.topcoder.com/v3', V5: 'https://api.topcoder.com/v5', }, DISABLED_TOOLS: [ ], ENV: AppHostEnvironment.prod, - TAG_MANAGER_ID: 'GTM-MXXQHG8', + LEARN_SRC: 'https://fcc.topcoder.com:4431', + // TODO: Move stripe creds to .env file + STRIPE: { + ADMIN_TOKEN: + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIiwiYWRtaW5pc3RyYXRvciJdLCJpc3MiOiJodHRwczovL2FwaS50b3Bjb2Rlci1kZXYuY29tIiwiaGFuZGxlIjoidGVzdDEiLCJleHAiOjI1NjMwNzY2ODksInVzZXJJZCI6IjQwMDUxMzMzIiwiaWF0IjoxNDYzMDc2MDg5LCJlbWFpbCI6InRlc3RAdG9wY29kZXIuY29tIiwianRpIjoiYjMzYjc3Y2QtYjUyZS00MGZlLTgzN2UtYmViOGUwYWU2YTRhIn0.wKWUe0-SaiFVN-VR_-GwgFlvWaDkSbc8H55ktb9LAVw', + API_KEY: 'pk_live_m3bCBVSfkfMOEp3unZFRsHXi', + API_VERSION: '2020-08-27', + CUSTOMER_TOKEN: + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJ0ZXN0MSIsImV4cCI6MjU2MzA3NjY4OSwidXNlcklkIjoiNDAwNTEzMzMiLCJpYXQiOjE0NjMwNzYwODksImVtYWlsIjoidGVzdEB0b3Bjb2Rlci5jb20iLCJqdGkiOiJiMzNiNzdjZC1iNTJlLTQwZmUtODM3ZS1iZWI4ZTBhZTZhNGEifQ.jl6Lp_friVNwEP8nfsfmL-vrQFzOFp2IfM_HC7AwGcg', + }, + TOPCODER_URLS: { + CHALLENGES_PAGE: `${COMMUNITY_WEBSITE}/challenges`, + GIGS_PAGE: `${COMMUNITY_WEBSITE}/gigs`, + USER_PROFILE: `${COMMUNITY_WEBSITE}/members`, + }, URL: { ACCOUNTS_APP_CONNECTOR: 'https://accounts-auth0.topcoder.com', }, diff --git a/src-ts/declarations.d.ts b/src-ts/declarations.d.ts index 81c5ad039..fa09433a4 100644 --- a/src-ts/declarations.d.ts +++ b/src-ts/declarations.d.ts @@ -3,6 +3,8 @@ declare module '*.html' { export = htmlFile } +declare module '*.pdf' + declare module '*.scss' { const scssFile: { [style: string]: any } export = scssFile diff --git a/src-ts/header/logo/Logo.tsx b/src-ts/header/logo/Logo.tsx index db850207a..2d7744c50 100644 --- a/src-ts/header/logo/Logo.tsx +++ b/src-ts/header/logo/Logo.tsx @@ -1,26 +1,28 @@ import { FC, useContext } from 'react' import { Link, useLocation } from 'react-router-dom' -import { LogoIcon, routeContext, RouteContextData } from '../../lib' +import { + LogoIcon, + routeContext, + RouteContextData, +} from '../../lib' import '../../lib/styles/index.scss' import styles from './Logo.module.scss' const Logo: FC<{}> = () => { - const { - isRootRoute, - rootLoggedInRoute, - }: RouteContextData = useContext(routeContext) + const routeContextData: RouteContextData = useContext(routeContext) // the logo should be a link to the home page for all pages except the home page - const isLink: boolean = !isRootRoute(useLocation().pathname) + const isLink: boolean = !routeContextData.isRootRoute(useLocation().pathname) + const rootRoute: string = routeContextData.rootLoggedInRoute || '' return (