diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 000000000..cd5b145c6 --- /dev/null +++ b/babel.config.js @@ -0,0 +1,70 @@ +module.exports = function (api) { + const isProd = process.env.APPMODE === "production"; + api.cache(!isProd); + + const generateScopedName = isProd + ? "[hash:base64:6]" + : "self_service_[path][name]___[local]___[hash:base64:6]"; + return { + presets: ["@babel/preset-env", "@babel/preset-react"], + plugins: [ + [ + "@babel/plugin-transform-runtime", + { + useESModules: true, + regenerator: false, + }, + ], + [ + "react-css-modules", + { + filetypes: { + ".scss": { + syntax: "postcss-scss", + }, + }, + generateScopedName, + }, + ], + [ + "inline-react-svg", + { + "svgo": { + "plugins": [ + { + "cleanupIDs": false + } + ] + } + } + ], + ], + env: { + test: { + presets: [ + [ + "@babel/preset-env", + { + targets: "current node", + }, + ], + ], + plugins: [ + [ + "module-resolver", + { + alias: { + styles: "./src/styles", + components: "./src/components", + hooks: "./src/hooks", + utils: "./src/utils", + constants: "./src/constants", + services: "./src/services", + }, + }, + ], + ], + }, + }, + }; +}; diff --git a/config/dev.js b/config/dev.js new file mode 100644 index 000000000..99a848249 --- /dev/null +++ b/config/dev.js @@ -0,0 +1,38 @@ +module.exports = { + /** + * URL of Topcoder Community Website + */ + TOPCODER_COMMUNITY_WEBSITE_URL: "https://topcoder-dev.com", + TERMS_URL: + "https://www.topcoder-dev.com/challenges/terms/detail/317cd8f9-d66c-4f2a-8774-63c612d99cd4", + PRIVACY_POLICY_URL: "https://www.topcoder-dev.com/policy", + SIGN_IN_URL: `https://accounts-auth0.topcoder-dev.com/?retUrl=https%3A%2F%2Fplatform.topcoder-dev.com%2Fself-service%2Fwizard®Source=selfService`, + SIGN_UP_URL: `https://accounts-auth0.topcoder-dev.com/?retUrl=https%3A%2F%2Fplatform.topcoder-dev.com%2Fself-service%2Fwizard®Source=selfService&mode=signUp`, + /** + * URL of Topcoder Connect Website + */ + CONNECT_WEBSITE_URL: "https://connect.topcoder-dev.com", + VANILLA_EMBED_JS: "https://vanilla.topcoder-dev.com/js/embed.js", + VANILLA_EMBED_TYPE: "mfe", + VANILLA_FORUM_API: "https://vanilla.topcoder-dev.com/api/v2", + VANILLA_ACCESS_TOKEN: "va.JApNvUOx3549h20I6tnl1kOQDc75NDIp.0jG3dA.EE3gZgV", + + API: { + V5: "https://api.topcoder-dev.com/v5", + V3: "https://api.topcoder-dev.com/v3", + }, + + STRIPE: { + API_KEY: "pk_test_rfcS49MHRVUKomQ9JgSH7Xqz", + API_VERSION: "2020-08-27", + CUSTOMER_TOKEN: + "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJ0ZXN0MSIsImV4cCI6MjU2MzA3NjY4OSwidXNlcklkIjoiNDAwNTEzMzMiLCJpYXQiOjE0NjMwNzYwODksImVtYWlsIjoidGVzdEB0b3Bjb2Rlci5jb20iLCJqdGkiOiJiMzNiNzdjZC1iNTJlLTQwZmUtODM3ZS1iZWI4ZTBhZTZhNGEifQ.jl6Lp_friVNwEP8nfsfmL-vrQFzOFp2IfM_HC7AwGcg", + ADMIN_TOKEN: + "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIiwiYWRtaW5pc3RyYXRvciJdLCJpc3MiOiJodHRwczovL2FwaS50b3Bjb2Rlci1kZXYuY29tIiwiaGFuZGxlIjoidGVzdDEiLCJleHAiOjI1NjMwNzY2ODksInVzZXJJZCI6IjQwMDUxMzMzIiwiaWF0IjoxNDYzMDc2MDg5LCJlbWFpbCI6InRlc3RAdG9wY29kZXIuY29tIiwianRpIjoiYjMzYjc3Y2QtYjUyZS00MGZlLTgzN2UtYmViOGUwYWU2YTRhIn0.wKWUe0-SaiFVN-VR_-GwgFlvWaDkSbc8H55ktb9LAVw", + }, + /** + * Expire time period of auto saved intake form: 24 hours + */ + AUTO_SAVED_COOKIE_EXPIRED_IN: 24 * 60, + TIME_ZONE: "Europe/London", +}; diff --git a/config/index.js b/config/index.js new file mode 100644 index 000000000..4cbf464b5 --- /dev/null +++ b/config/index.js @@ -0,0 +1,14 @@ +/* global process */ + +module.exports = (() => { + const env = process.env.APPENV || "dev"; + + console.info(`APPENV: "${env}"`); + + // for security reason don't let to require any arbitrary file defined in process.env + if (["prod", "dev"].indexOf(env) < 0) { + return require("./dev"); + } + + return require("./" + env); +})(); diff --git a/config/local.js b/config/local.js new file mode 100644 index 000000000..7af0bba50 --- /dev/null +++ b/config/local.js @@ -0,0 +1,3 @@ +module.exports = { + COMMUNITY_ADMIN_URL: "", +}; diff --git a/config/prod.js b/config/prod.js new file mode 100644 index 000000000..ff5d082bc --- /dev/null +++ b/config/prod.js @@ -0,0 +1,39 @@ +module.exports = { + /** + * URL of Topcoder Community Website + */ + TOPCODER_COMMUNITY_WEBSITE_URL: "https://topcoder.com", + TERMS_URL: + "https://www.topcoder.com/challenges/terms/detail/564a981e-6840-4a5c-894e-d5ad22e9cd6f", + PRIVACY_POLICY_URL: "https://www.topcoder.com/policy", + SIGN_IN_URL: `https://accounts-auth0.topcoder.com/?retUrl=https%3A%2F%2Fplatform.topcoder.com%2Fself-service%2Fwizard®Source=selfService`, + SIGN_UP_URL: `https://accounts-auth0.topcoder.com/?retUrl=https%3A%2F%2Fplatform.topcoder.com%2Fself-service%2Fwizard®Source=selfService&mode=signUp`, + + /** + * URL of Topcoder Connect Website + */ + CONNECT_WEBSITE_URL: "https://connect.topcoder.com", + VANILLA_EMBED_JS: "https://discussions.topcoder.com/js/embed.js", + VANILLA_EMBED_TYPE: "standard", + VANILLA_FORUM_API: "https://vanilla.topcoder.com/api/v2", + VANILLA_ACCESS_TOKEN: "va.JApNvUOx3549h20I6tnl1kOQDc75NDIp.0jG3dA.EE3gZgV", + + API: { + V5: "https://api.topcoder.com/v5", + V3: "https://api.topcoder.com/v3", + }, + + STRIPE: { + API_KEY: "pk_live_m3bCBVSfkfMOEp3unZFRsHXi", + API_VERSION: "2020-08-27", + CUSTOMER_TOKEN: + "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJ0ZXN0MSIsImV4cCI6MjU2MzA3NjY4OSwidXNlcklkIjoiNDAwNTEzMzMiLCJpYXQiOjE0NjMwNzYwODksImVtYWlsIjoidGVzdEB0b3Bjb2Rlci5jb20iLCJqdGkiOiJiMzNiNzdjZC1iNTJlLTQwZmUtODM3ZS1iZWI4ZTBhZTZhNGEifQ.jl6Lp_friVNwEP8nfsfmL-vrQFzOFp2IfM_HC7AwGcg", + ADMIN_TOKEN: + "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIiwiYWRtaW5pc3RyYXRvciJdLCJpc3MiOiJodHRwczovL2FwaS50b3Bjb2Rlci1kZXYuY29tIiwiaGFuZGxlIjoidGVzdDEiLCJleHAiOjI1NjMwNzY2ODksInVzZXJJZCI6IjQwMDUxMzMzIiwiaWF0IjoxNDYzMDc2MDg5LCJlbWFpbCI6InRlc3RAdG9wY29kZXIuY29tIiwianRpIjoiYjMzYjc3Y2QtYjUyZS00MGZlLTgzN2UtYmViOGUwYWU2YTRhIn0.wKWUe0-SaiFVN-VR_-GwgFlvWaDkSbc8H55ktb9LAVw", + }, + /** + * Expire time period of auto saved intake form: 24 hours + */ + AUTO_SAVED_COOKIE_EXPIRED_IN: 24 * 60, + TIME_ZONE: "Europe/London", +}; diff --git a/package.json b/package.json index a4abcac26..c07ef512c 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "scripts": { "start": "sh start-ssl.sh", "start:bsouza": "sh start-ssl-bsouza.sh", + "start:mfe": "cross-env webpack-dev-server --port 8519 --host 0.0.0.0", "build": "react-scripts build", "build:dev": "sh build-dev.sh", "build:prod": "sh build-prod.sh", @@ -17,32 +18,98 @@ "dependencies": { "@datadog/browser-logs": "^4.5.0", "@heroicons/react": "^1.0.6", + "@reach/router": "^1.3.4", + "apexcharts": "^3.35.3", "axios": "^0.26.1", "browser-cookies": "^1.2.0", "classnames": "^2.3.1", + "crypto-js": "^4.1.1", + "lodash": "^4.17.21", + "moment": "^2.29.3", + "moment-timezone": "^0.5.34", + "prop-types": "^15.8.1", + "rc-checkbox": "^2.3.2", "react": "^17.0.2", + "react-apexcharts": "^1.4.0", "react-dom": "^17.0.2", + "react-elastic-carousel": "^0.11.5", "react-gtm-module": "^2.0.11", + "react-redux": "^8.0.2", + "react-redux-toastr": "^7.6.8", "react-responsive-modal": "^6.2.0", "react-router-dom": "^6.2.1", "react-scripts": "5.0.0", + "react-select": "^5.3.2", + "react-spinners": "^0.13.1", "react-toastify": "^8.2.0", + "react-tooltip": "^4.2.21", + "redux": "^4.2.0", + "redux-logger": "^3.0.6", + "redux-promise-middleware": "^6.1.2", + "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" }, "devDependencies": { + "@babel/core": "^7.7.5", + "@babel/plugin-syntax-jsx": "^7.17.12", + "@babel/plugin-transform-runtime": "^7.8.3", + "@babel/preset-env": "^7.7.6", + "@babel/preset-react": "^7.7.4", + "@babel/preset-typescript": "^7.16.7", + "@babel/runtime": "^7.8.7", + "@stripe/react-stripe-js": "1.7.2", + "@stripe/stripe-js": "1.29.0", "@testing-library/jest-dom": "^5.14.1", "@testing-library/react": "^12.0.0", "@testing-library/user-event": "^13.2.1", "@types/axios": "^0.14.0", "@types/jest": "^27.0.1", + "@types/lodash": "^4.14.182", "@types/node": "^16.7.13", + "@types/reach__router": "^1.3.10", "@types/react": "^17.0.20", "@types/react-dom": "^17.0.9", "@types/react-gtm-module": "^2.0.1", - "@types/react-router-dom": "^5.3.3" + "@types/react-redux-toastr": "^7.6.2", + "@types/react-router-dom": "^5.3.3", + "@types/systemjs": "^6.1.0", + "autoprefixer": "^9.8.6", + "babel-eslint": "^11.0.0-beta.2", + "babel-jest": "^24.9.0", + "babel-plugin-inline-react-svg": "^1.1.2", + "babel-plugin-module-resolver": "^4.0.0", + "babel-plugin-react-css-modules": "^5.2.6", + "concurrently": "^5.0.1", + "config": "^3.3.6", + "cross-env": "^7.0.2", + "eslint": "^6.7.2", + "eslint-config-prettier": "^6.7.0", + "eslint-config-react-important-stuff": "^2.0.0", + "eslint-plugin-prettier": "^3.1.1", + "file-loader": "^6.2.0", + "identity-obj-proxy": "^3.0.0", + "jest": "^25.2.7", + "jest-cli": "^25.2.7", + "postcss-loader": "^4.0.4", + "postcss-scss": "^3.0.2", + "prettier": "^2.0.4", + "pretty-quick": "^2.0.1", + "resolve-url-loader": "^3.1.2", + "sass": "^1.48.0", + "sass-loader": "^10.0.5", + "single-spa-react": "^2.14.0", + "style-loader": "^2.0.0", + "systemjs-webpack-interop": "^2.1.2", + "webpack": "^4.41.2", + "webpack-cli": "^3.3.10", + "webpack-config-single-spa-react": "^1.0.3", + "webpack-dev-server": "^3.9.0", + "webpack-merge": "^4.2.2" }, "eslintConfig": { "extends": [ @@ -66,4 +133,4 @@ ] }, "types": "./types/index.d.ts" -} \ No newline at end of file +} diff --git a/src-ts/App.test.tsx b/src-ts/App.test.tsx deleted file mode 100644 index 112d36d48..000000000 --- a/src-ts/App.test.tsx +++ /dev/null @@ -1,4 +0,0 @@ -describe('', () => { - - test('renders the body of the application', () => {}) -}) diff --git a/src-ts/header/Header.test.tsx b/src-ts/header/Header.test.tsx deleted file mode 100644 index d1922dc20..000000000 --- a/src-ts/header/Header.test.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import '@testing-library/jest-dom' - -describe('
', () => { - - test('it should render the header', () => {}) -}) diff --git a/src-ts/header/logo/Logo.test.tsx b/src-ts/header/logo/Logo.test.tsx deleted file mode 100644 index a212db06a..000000000 --- a/src-ts/header/logo/Logo.test.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import '@testing-library/jest-dom' - -describe(' is on the home page', () => { - - test('it should only display the logo and should NOT be a link', () => { }) -}) - -describe(' is NOT on the home page', () => { - - test('it should display the logo and have it be a link', () => { }) -}) diff --git a/src-ts/header/tool-selectors/ToolSelectors.test.tsx b/src-ts/header/tool-selectors/ToolSelectors.test.tsx deleted file mode 100644 index 0a8b36550..000000000 --- a/src-ts/header/tool-selectors/ToolSelectors.test.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import '@testing-library/jest-dom' - -describe('', () => { - - test('it should render the tool sections', () => { }) -}) diff --git a/src-ts/header/tool-selectors/tool-selectors-narrow/ToolSelectorsNarrow.test.tsx b/src-ts/header/tool-selectors/tool-selectors-narrow/ToolSelectorsNarrow.test.tsx deleted file mode 100644 index f29765cf5..000000000 --- a/src-ts/header/tool-selectors/tool-selectors-narrow/ToolSelectorsNarrow.test.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import '@testing-library/jest-dom' - -describe(' is closed', () => { - - test('it should render the tool-selectors-narrow-closed icon', () => { }) -}) - -describe(' is open', () => { - - test('it should render the tool-selectors-narrow-open icon', () => { }) -}) diff --git a/src-ts/header/tool-selectors/tool-selectors-narrow/tool-selector-narrow/ToolSelectorNarrow.test.tsx b/src-ts/header/tool-selectors/tool-selectors-narrow/tool-selector-narrow/ToolSelectorNarrow.test.tsx deleted file mode 100644 index a80e60ec4..000000000 --- a/src-ts/header/tool-selectors/tool-selectors-narrow/tool-selector-narrow/ToolSelectorNarrow.test.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import '@testing-library/jest-dom' - -describe(' is NOT open', () => { - - test('it should render the tool-selector-narrow icon', () => { }) -}) diff --git a/src-ts/header/tool-selectors/tool-selectors-wide/ToolSelectorsWide.test.tsx b/src-ts/header/tool-selectors/tool-selectors-wide/ToolSelectorsWide.test.tsx deleted file mode 100644 index c17488673..000000000 --- a/src-ts/header/tool-selectors/tool-selectors-wide/ToolSelectorsWide.test.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import '@testing-library/jest-dom' - -describe('', () => { - - test('it should render the tool selectors in wide format', () => { }) -}) diff --git a/src-ts/header/tool-selectors/tool-selectors-wide/tool-selector-wide/ToolSelectorWide.test.tsx b/src-ts/header/tool-selectors/tool-selectors-wide/tool-selector-wide/ToolSelectorWide.test.tsx deleted file mode 100644 index a3ec22da7..000000000 --- a/src-ts/header/tool-selectors/tool-selectors-wide/tool-selector-wide/ToolSelectorWide.test.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import '@testing-library/jest-dom' - -describe('', () => { - - test('it should render the tools selector in wide format', () => { }) -}) - -describe(' tool is the currently active tool', () => { - - test('it should render the tools selector active indicator', () => { }) -}) - -describe(' tool is NOT the currently active tool', () => { - - test('it should NOT render the tools selector active indicator', () => { }) -}) diff --git a/src-ts/header/utility-selectors/UtilitySelector/ProfileSelector/ProfileSelector.test.tsx b/src-ts/header/utility-selectors/UtilitySelector/ProfileSelector/ProfileSelector.test.tsx deleted file mode 100644 index ddf29e295..000000000 --- a/src-ts/header/utility-selectors/UtilitySelector/ProfileSelector/ProfileSelector.test.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import '@testing-library/jest-dom' - -describe(' when the props have NOT been initialized', () => { - - test('it should NOT display the ProfileSelector', () => { }) -}) - -describe(' when the props have been initialized', () => { - - test('it should display the ProfileSelector', () => { }) -}) - -describe(' when the props have been initialized and there NOT is a profile', () => { - - test('it should display the login', () => { }) - - test('it should display the signup', () => { }) - - test('it should NOT display the Avatar', () => { }) -}) - -describe(' when the props have been initialized and there is a profile', () => { - - test('it should NOT display the login', () => { }) - - test('it should NOT display the signup', () => { }) - - test('it should display the Avatar', () => { }) -}) diff --git a/src-ts/header/utility-selectors/UtilitySelector/ProfileSelector/profile-logged-in/ProfileLoggedIn.test.tsx b/src-ts/header/utility-selectors/UtilitySelector/ProfileSelector/profile-logged-in/ProfileLoggedIn.test.tsx deleted file mode 100644 index ab432ac63..000000000 --- a/src-ts/header/utility-selectors/UtilitySelector/ProfileSelector/profile-logged-in/ProfileLoggedIn.test.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import '@testing-library/jest-dom' - -describe(' ', () => { - - test('it should display the ProfileLoggedIn', () => { }) -}) diff --git a/src-ts/header/utility-selectors/UtilitySelector/ProfileSelector/profile-logged-in/profile-panel/ProfilePanel.test.tsx b/src-ts/header/utility-selectors/UtilitySelector/ProfileSelector/profile-logged-in/profile-panel/ProfilePanel.test.tsx deleted file mode 100644 index fc87dd23a..000000000 --- a/src-ts/header/utility-selectors/UtilitySelector/ProfileSelector/profile-logged-in/profile-panel/ProfilePanel.test.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import '@testing-library/jest-dom' - -describe('', () => { - - test('it should display the ProfilePanel', () => { - /* const renderResult: RenderResult = render( - - - - ) - const ProfilePanelElement: HTMLElement | null = renderResult.container.querySelector('.profile-selector') - expect(ProfilePanelElement).toBeNull() */ - }) -}) diff --git a/src-ts/header/utility-selectors/UtilitySelector/ProfileSelector/profile-not-logged-in/ProfileNotLoggedIn.test.tsx b/src-ts/header/utility-selectors/UtilitySelector/ProfileSelector/profile-not-logged-in/ProfileNotLoggedIn.test.tsx deleted file mode 100644 index 1363fa83a..000000000 --- a/src-ts/header/utility-selectors/UtilitySelector/ProfileSelector/profile-not-logged-in/ProfileNotLoggedIn.test.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import '@testing-library/jest-dom' - -describe(' ', () => { - - test('it should display the ProfileNotLoggedIn', () => {}) -}) diff --git a/src-ts/header/utility-selectors/UtilitySelectors.test.tsx b/src-ts/header/utility-selectors/UtilitySelectors.test.tsx deleted file mode 100644 index fa74ea43b..000000000 --- a/src-ts/header/utility-selectors/UtilitySelectors.test.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import '@testing-library/jest-dom' - -describe('', () => { - - test('it should display the utility selectors', () => { }) -}) diff --git a/src-ts/lib/avatar/Avatar.test.tsx b/src-ts/lib/avatar/Avatar.test.tsx deleted file mode 100644 index aedefe45f..000000000 --- a/src-ts/lib/avatar/Avatar.test.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import '@testing-library/jest-dom' - -describe(' and there is NOT a profile', () => { - - test('it should NOT display the Avatar', () => {}) -}) - -describe(' and there is a profile', () => { - - test('if there is NO photoURL, firstname, or lastname, it should NOT display the Avatar', () => { }) - - test('if there is a photoURL, it should display the Avatar', () => { }) - - test('if there is a photoURL, it should NOT display the Avatar letters', () => { }) - - test('if there is NOT a photoURL, it should display the Avatar Letters', () => { }) - - test('if there is NOT an avatar URL, it should NOT display the Avatar', () => {}) -}) diff --git a/src-ts/lib/button/Button.test.tsx b/src-ts/lib/button/Button.test.tsx deleted file mode 100644 index 0dae599ff..000000000 --- a/src-ts/lib/button/Button.test.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import '@testing-library/jest-dom' - -describe(' + ); + + return routeTo ? {button} : button; + } +}; + +Button.propTypes = { + children: PT.node, + size: PT.oneOf(Object.values(BUTTON_SIZE)), + type: PT.oneOf(Object.values(BUTTON_TYPE)), + onClick: PT.func, + className: PT.string, + innerRef: PT.func, + disabled: PT.bool, + routeTo: PT.string, + href: PT.string, + isSubmit: PT.bool, +}; + +export default Button; diff --git a/src/components/Button/styles.module.scss b/src/components/Button/styles.module.scss new file mode 100644 index 000000000..3168f947a --- /dev/null +++ b/src/components/Button/styles.module.scss @@ -0,0 +1,118 @@ +@import "styles/include"; + +.button { + @include font-roboto; + background: transparent; + border: 0; + box-sizing: border-box; + cursor: pointer; + align-items: center; + display: flex; + justify-content: center; + margin: 0; + padding: 0; + text-decoration: none; + outline: none; + white-space: nowrap; + + &[disabled] { + cursor: default; + } + + svg { + fill: white; + } +} + +.size-large { + border-radius: 25px; + font-size: 20px; + font-weight: 700; + letter-spacing: 0.8px; + line-height: 46px; + height: 48px; + padding: 18px 30px; + text-transform: uppercase; +} + +.size-medium { + border-radius: 20px; + font-size: 14px; + font-weight: 700; + letter-spacing: 0.8px; + line-height: 38px; + height: 40px; + padding: 0 19px; + text-transform: uppercase; +} + +.size-small { + border-radius: 15px; + font-size: 12px; + font-weight: 700; + line-height: 28px; + letter-spacing: 0.8px; + height: 30px; + padding: 0 14px; + text-transform: uppercase; +} + +.size-tiny { + border-radius: 12px; + padding: 6px 15px; + height: 24px; + font-size: 10px; + font-weight: 700; + line-height: 22px; + letter-spacing: 0.8px; + text-transform: uppercase; +} + +.type-primary { + border: 2px solid $green1; + background-color: $green1; + color: #fff; +} + +.type-warning { + border: 2px solid #ef476f; + background-color: #ef476f; + color: #fff; +} + +.type-secondary { + background-color: #fff; + border: 2px solid $green1; + color: #229174; +} + +.type-secondary[disabled] { + border-color: #b5b5b5; + color: #b5b5b5; +} + +.type-rounded { + position: relative; + width: 22px; + border-radius: 50%; + background-color: #fff; + border: 1px solid $green1; + color: #229174; + + svg { + position: absolute; + left: 37%; + } +} + +.type-rounded[disabled] { + border-radius: 50%; + border-color: #b5b5b5; + color: #b5b5b5; +} + +.type-primary[disabled], +.type-warning[disabled] { + background-color: #b5b5b5; + border-color: #b5b5b5; +} diff --git a/src/components/DateInput/index.jsx b/src/components/DateInput/index.jsx new file mode 100644 index 000000000..e5d93747b --- /dev/null +++ b/src/components/DateInput/index.jsx @@ -0,0 +1,81 @@ +/** + * DateInput + * + * Date Input control. + */ +import React, { createRef, useState } from "react"; +import PT from "prop-types"; +import DatePicker from "react-datepicker"; +import cn from "classnames"; +import "react-datepicker/dist/react-datepicker.css"; +import CalendarIcon from "../../assets/images/icon-calendar.svg"; +import ArrowIcon from "../../assets/images/icon-arrow.svg"; +import styles from "./styles.module.scss"; +import moment from "moment"; + +const DateInput = (props) => { + const [open, setOpen] = useState(false); + const calendarRef = createRef(); + return ( +
+
calendarRef.current.setOpen(true)} + styleName={cn("styles.icon", "styles.icon-calendar")} + role="button" + tabIndex={0} + > + +
+ { + setOpen(false); + }} + onFocus={props.onFocus} + showYearDropdown + onCalendarOpen={() => setOpen(true)} + maxDate={ + props.allowFutureDate ? null : moment().subtract(1, "days").toDate() + } + disabled={props.disabled} + /> +
calendarRef.current.setOpen(true)} + role="button" + tabIndex={0} + > + +
+
+ ); +}; + +DateInput.propTypes = { + value: PT.string, + onChange: PT.func.isRequired, + placeholder: PT.string, + onBlur: PT.func, + onFocus: PT.func, + className: PT.string, + style2: PT.bool, + disabled: PT.bool, + allowFutureDate: PT.bool, +}; + +export default DateInput; diff --git a/src/components/DateInput/styles.module.scss b/src/components/DateInput/styles.module.scss new file mode 100644 index 000000000..6949cde2a --- /dev/null +++ b/src/components/DateInput/styles.module.scss @@ -0,0 +1,64 @@ +@import "styles/include"; + +.datepicker-wrapper { + position: relative; + padding: 0 10px; + .icon { + position: absolute; + display: flex; + padding: 8px 0 8px 4px; + align-items: center; + & > svg { + width: 20px; + height: 20px; + } + } + .icon-calendar { + left: 8px; + cursor: pointer; + } + + .icon-arrow { + right: 8px; + top: 0; + & > svg { + color: hsl(0, 0%, 80%); + } + &:hover { + & > svg { + color: hsl(0, 0%, 60%); + } + } + &.icon-arrow-open { + & > svg { + color: hsl(0, 0%, 40%); + } + } + } + + &.error { + input { + border-color: #fe665d; + } + } + + & > div:nth-child(2) { + margin-left: 24px; + } + + &.style2 input { + border: none !important; + box-shadow: none !important; + margin-bottom: 0 !important; + font-size: 18px; + &::placeholder { + color: #aaa; + font-size: 18px; + text-transform: none !important; + } + } +} + +.datepicker-wrapper > div:nth-child(2) > div:nth-child(2) > div:nth-child(2) { + z-index: 100; +} diff --git a/src/components/FormElements/FormField/index.jsx b/src/components/FormElements/FormField/index.jsx new file mode 100644 index 000000000..1c00cab13 --- /dev/null +++ b/src/components/FormElements/FormField/index.jsx @@ -0,0 +1,72 @@ +/** + * FormField + * + * A Form Field Is a wrapper for input to add the label to it + */ +import cn from "classnames"; +import PT from "prop-types"; +import React from "react"; +import "./styles.module.scss"; + +const FormField = ({ + children, + label = "", + placeholder = "", + onChange = (f) => f, + className, + styleName, + disabled, + helperText, + ...props +}) => { + const handleClick = (e) => { + // focus on input label click + const inputElement = e.target.closest(".form-field").querySelector("input"); + inputElement && inputElement.focus(); + }; + return ( +
+
+
+ {label} +
+ {children} +
+ {helperText &&
{helperText}
} + +
+ {props.error} +
+
+ ); +}; + +FormField.propTypes = { + onChange: PT.func, + label: PT.string, + placeholder: PT.string, + children: PT.node, + error: PT.string, +}; + +export default FormField; diff --git a/src/components/FormElements/FormField/styles.module.scss b/src/components/FormElements/FormField/styles.module.scss new file mode 100644 index 000000000..3547a2ae8 --- /dev/null +++ b/src/components/FormElements/FormField/styles.module.scss @@ -0,0 +1,64 @@ +@import "styles/include"; + +.form-field-wrapper { + position: relative; + display: flex; + flex-direction: column; + justify-content: flex-start; + margin-bottom: 18px; + @include mobile { + margin-bottom: 0px; + } + + &.helper { + margin-bottom: 35px !important; + } + + .form-field { + border: 1px solid #b7b7b7; + border-radius: 4px; + background: white; + padding-top: 24px; + margin-bottom: 10px; + + &.disabled { + background-color: $grey-bg !important; + } + + .label { + position: absolute; + top: 5px; + left: 15px; + color: $green1; + font-size: 13px; + } + + > div { + border-radius: 4px; + } + } + .error { + color: $red; + } + + .helperText { + @include font-roboto; + font-weight: 400; + font-size: 12px; + line-height: 14px; + color: #555; + margin-top: -6px; + } +} + +.labelStyle { + font-size: 11px !important; + margin-top: 10px; + left: 10px !important; + top: 8px !important; +} + +.formTitleStyle { + margin-top: 12px !important; + padding-top: 0 !important; +} diff --git a/src/components/FormElements/FormInputCheckbox/index.jsx b/src/components/FormElements/FormInputCheckbox/index.jsx new file mode 100644 index 000000000..7e5843a0a --- /dev/null +++ b/src/components/FormElements/FormInputCheckbox/index.jsx @@ -0,0 +1,54 @@ +/* eslint-disable jsx-a11y/label-has-associated-control */ +/** + * FormInputCheckbox + * + * Form Input Checkbox + */ +import cn from "classnames"; +import PT from "prop-types"; +import Checkbox from "rc-checkbox"; +import "rc-checkbox/assets/index.css"; +import React from "react"; +import styles from "./styles.module.scss"; + +const FormInputCheckbox = ({ + label, + additionalContent, + onChange = (f) => f, + styleName, + inline, + ...props +}) => { + return inline ? ( +
+ +
+ ) : ( + // eslint-disable-next-line jsx-a11y/label-has-associated-control + + ); +}; + +FormInputCheckbox.propTypes = { + label: PT.string, +}; + +export default FormInputCheckbox; diff --git a/src/components/FormElements/FormInputCheckbox/styles.module.scss b/src/components/FormElements/FormInputCheckbox/styles.module.scss new file mode 100644 index 000000000..da8a7afdb --- /dev/null +++ b/src/components/FormElements/FormInputCheckbox/styles.module.scss @@ -0,0 +1,60 @@ +@import "styles/include"; + +.form-input-checkbox { + display: flex; + align-items: center; + + .label { + @include font-roboto; + font-size: 14px; + line-height: 18px; + margin-left: 8px; + font-weight: normal; + + span { + text-decoration: underline; + color: $link-blue; + cursor: pointer; + } + } +} + +/* rc checkbox custom style */ +:global { + .rc-checkbox.form-input-rc-checkbox { + display: flex; + + .rc-checkbox-inner { + width: 20px !important; + height: 20px !important; + + &:hover { + border-color: $green1; + } + } + + .rc-checkbox-inner:after { + left: 6px !important; + top: 0 !important; + width: 7px !important; + height: 14px !important; + } + + + } + + .rc-checkbox.form-input-rc-checkbox.rc-checkbox-checked { + .rc-checkbox-inner { + background-color: $green1; + border-color: $green1; + } + } +} + +.inline { + display: inline-block; + .span { + color: $link-blue; + text-decoration: underline; + } +} diff --git a/src/components/FormElements/FormInputNumber/index.jsx b/src/components/FormElements/FormInputNumber/index.jsx new file mode 100644 index 000000000..253380384 --- /dev/null +++ b/src/components/FormElements/FormInputNumber/index.jsx @@ -0,0 +1,22 @@ +/** + * FormInputNumber + * + * Form Input Type=text + */ +import cn from "classnames"; +import React from "react"; +import "./styles.module.scss"; + +const FormInputNumber = ({ styleName, ...props }) => { + return ( + + ); +}; + +FormInputNumber.propTypes = {}; + +export default FormInputNumber; diff --git a/src/components/FormElements/FormInputNumber/styles.module.scss b/src/components/FormElements/FormInputNumber/styles.module.scss new file mode 100644 index 000000000..2724e6ac4 --- /dev/null +++ b/src/components/FormElements/FormInputNumber/styles.module.scss @@ -0,0 +1,25 @@ +@import "styles/include"; + +.form-input-number { + background-color: #ffffff !important; + box-sizing: border-box !important; + color: #444 !important; + font-size: 18px !important; + height: 32px !important; + outline: none !important; + border: 0 !important; + padding: 0 15px !important; + width: 100% !important; + &::placeholder { + color: #aaaaaa !important; + font-size: 18px !important; + text-transform: none !important; + } + &:focus { + box-shadow: none !important; + } + + &:disabled { + background-color: $grey-bg !important; + } +} diff --git a/src/components/FormElements/FormInputText/index.jsx b/src/components/FormElements/FormInputText/index.jsx new file mode 100644 index 000000000..47bfa8d02 --- /dev/null +++ b/src/components/FormElements/FormInputText/index.jsx @@ -0,0 +1,22 @@ +/** + * FormInputText + * + * Form Input Type=text + */ +import cn from "classnames"; +import React from "react"; +import "./styles.module.scss"; + +const FormInputText = ({ styleName, ...props }) => { + return ( + + ); +}; + +FormInputText.propTypes = {}; + +export default FormInputText; diff --git a/src/components/FormElements/FormInputText/styles.module.scss b/src/components/FormElements/FormInputText/styles.module.scss new file mode 100644 index 000000000..6a5264f42 --- /dev/null +++ b/src/components/FormElements/FormInputText/styles.module.scss @@ -0,0 +1,5 @@ +@import "styles/include"; + +.form-input-text { + @include formInputText; +} diff --git a/src/components/FormElements/FormInputTextArea/index.jsx b/src/components/FormElements/FormInputTextArea/index.jsx new file mode 100644 index 000000000..8d9f65f9e --- /dev/null +++ b/src/components/FormElements/FormInputTextArea/index.jsx @@ -0,0 +1,22 @@ +/** + * FormInputTextArea + * + * Form Input textarea + */ +import cn from "classnames"; +import React from "react"; +import "./styles.module.scss"; + +const FormInputTextArea = ({ styleName, ...props }) => { + return ( +