diff --git a/.travis.yml b/.travis.yml index 01f2f07..2c89824 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,16 +26,30 @@ script: after_success: - docker-compose push + - | + if [[ "$TRAVIS_BRANCH" = "release" ]] ; then + docker pull $ZAPPA_IMG || true + docker build -t $ZAPPA_IMG --cache-from $ZAPPA_IMG -f ./server/zappa.dockerfile ./server/ + docker run --rm \ + -e AWS_ACCESS_KEY_ID=$ZAPPA_AWS_ACCESS_KEY_ID \ + -e AWS_SECRET_ACCESS_KEY=$ZAPPA_AWS_SECRET_ACCESS_KEY \ + -e AWS_DEFAULT_REGION=$ZAPPA_AWS_DEFAULT_REGION \ + -v `pwd`/server/=/code/ \ + $ZAPPA_IMG \ + bash -c 'source /venv/bin/activate && cd /code/ && zappa update prod && zappa manage prod migrate' + docker push $ZAPPA_IMG + fi deploy: provider: s3 - access_key_id: $AWS_ACCESS_KEY_ID - secret_access_key: $AWS_SECRET_ACCESS_KEY + access_key_id: $ZAPPA_AWS_ACCESS_KEY_ID + secret_access_key: $ZAPPA_AWS_SECRET_ACCESS_KEY bucket: $TOGGLECORP_S3_BUCKET skip_cleanup: true local_dir: ./client/build acl: public_read - region: $DEPLOYMENT_REGION + region: $ZAPPA_AWS_DEFAULT_REGION + edge: true # opt in to dpl v2 on: branch: release @@ -44,3 +58,4 @@ env: global: - REACT_STORE_DIR=client/src/vendor/react-store - TC_BRANCH_NAME=`echo ${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH} | tr / _` + - ZAPPA_IMG=docker.pkg.github.com/toggle-corp/togglecorp/zappa:latest diff --git a/client/.babelrc b/client/.babelrc index 31e765c..1ad0898 100644 --- a/client/.babelrc +++ b/client/.babelrc @@ -47,6 +47,7 @@ "#redux": "./src/redux", "#resources": "./src/resources", "#rest": "./src/rest", + "#request": "./src/request", "#schema": "./src/schema", "#utils": "./src/utils", "#notify": "./src/notify", diff --git a/client/Dockerfile b/client/Dockerfile index ed2418f..c29138d 100644 --- a/client/Dockerfile +++ b/client/Dockerfile @@ -1,4 +1,4 @@ -FROM node:10-alpine +FROM node:12.20-alpine MAINTAINER togglecorp info@togglecorp.com diff --git a/client/package.json b/client/package.json index 7c2ea12..98f0c20 100644 --- a/client/package.json +++ b/client/package.json @@ -87,9 +87,9 @@ "identity-obj-proxy": "^3.0.0", "jest": "^22.4.3", "mini-css-extract-plugin": "^0.4.0", - "node-sass": "4.12.0", "optimize-css-assets-webpack-plugin": "^4.0.1", "react-test-renderer": "^16.3.2", + "sass": "^1.45.1", "sass-loader": "^7.3.1", "style-loader": "^0.23.1", "stylelint": "^10.1.0", @@ -105,7 +105,9 @@ "webpack-pwa-manifest": "^3.6.2" }, "dependencies": { + "@togglecorp/fujs": "^1.9.0", "@togglecorp/ravl": "^1.0.2", + "@togglecorp/react-rest-request": "^2.4.5", "body-parser": "^1.18.3", "d3-area-label": "^1.4.0", "d3-array": "^1.2.1", @@ -146,15 +148,11 @@ "react-focus-trap": "^2.5.0", "react-ga": "^2.5.0", "react-helmet": "^5.2.0", - "react-redux": "^5.0.7", + "react-markdown": "^4.3.1", "react-resize-detector": "^2.3.0", "react-router-dom": "^4.2.2", "react-sortable-hoc": "^0.6.8", "react-svg": "^3.0.2", - "redux": "^4.0.0", - "redux-mock-store": "^1.5.1", - "redux-persist": "^5.9.1", - "redux-thunk": "^2.2.0", "reselect": "^3.0.1", "svgsaver": "^0.9.0" } diff --git a/client/public/favicon.ico b/client/public/favicon.ico index ab7dd5f..260737d 100644 Binary files a/client/public/favicon.ico and b/client/public/favicon.ico differ diff --git a/client/public/favicon.png b/client/public/favicon.png index ab7dd5f..068aea4 100644 Binary files a/client/public/favicon.png and b/client/public/favicon.png differ diff --git a/client/src/App.js b/client/src/App.js index 84b255b..529b4b0 100644 --- a/client/src/App.js +++ b/client/src/App.js @@ -1,12 +1,10 @@ import React from 'react'; import { BrowserRouter } from 'react-router-dom'; -import getUserConfirmation from '#utils/getUserConfirmation'; import Multiplexer from './Multiplexer'; - export default () => ( - + ); diff --git a/client/src/Multiplexer.js b/client/src/Multiplexer.js index a0a4fbe..2f39163 100644 --- a/client/src/Multiplexer.js +++ b/client/src/Multiplexer.js @@ -5,11 +5,6 @@ import { withRouter, } from 'react-router-dom'; -import ExclusivelyPublicRoute from '#rscg/ExclusivelyPublicRoute'; -import PrivateRoute from '#rscg/PrivateRoute'; - -// import Navbar from '#components/Navbar'; - import { pathNames, routesOrder, @@ -18,9 +13,7 @@ import { } from '#constants'; const ROUTE = { - exclusivelyPublic: 'exclusively-public', public: 'public', - private: 'private', }; // NOTE: withRouter is required here so that link change are updated @@ -35,34 +28,9 @@ export default class Multiplexer extends React.PureComponent { return null; } const path = pathNames[routeId]; - const { redirectTo, type } = routes[routeId]; - - // FIXME: Use actual authenticated status from redux - const authenticated = false; + const { type } = routes[routeId]; switch (type) { - case ROUTE.exclusivelyPublic: - return ( - - ); - case ROUTE.private: - return ( - - ); case ROUTE.public: return ( this.setState({ rehydrated: true }); - // NOTE: We can also use PersistGate instead of callback to wait for rehydration - persistStore(this.store, undefined, afterRehydrateCallback); - } - - render() { - if (!this.state.rehydrated) { - // NOTE: showing empty div, this lasts for a fraction of a second - return
; - } - - return ( - - - - ); - } -} +return App; diff --git a/client/src/components/Bundle.js b/client/src/components/Bundle.js new file mode 100644 index 0000000..714fb38 --- /dev/null +++ b/client/src/components/Bundle.js @@ -0,0 +1,103 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +const propTypes = { + load: PropTypes.func.isRequired, + errorText: PropTypes.string, + loadingText: PropTypes.string, + decorator: PropTypes.func, +}; +const defaultProps = { + errorText: 'Error while loading page.', + loadingText: 'Loading...', + decorator: undefined, +}; + +// NOTE: Intentionally opted out of PureComponent +class Bundle extends React.Component { + static propTypes = propTypes; + static defaultProps = defaultProps; + + static loadingStyle = { + height: '100%', + fontSize: '2em', + color: 'rgba(0, 0, 0, 0.5)', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }; + + constructor(props) { + super(props); + this.state = { + BundledComponent: null, + failed: false, + }; + } + + componentWillMount() { + this.mounted = true; + this.props.load() + .then(this.handleLoad) + .catch(this.handleLoadError); + } + + componentWillUnmount() { + this.mounted = false; + } + + handleLoad = (BundledComponent) => { + if (!this.mounted) { + console.error('Bundle unmounted while loading Component.'); + return; + } + + let Component = BundledComponent.default || BundledComponent; + if (this.props.decorator) { + Component = this.props.decorator(Component); + } + + this.setState({ + BundledComponent: Component, + }); + } + + handleLoadError = (err) => { + if (!this.mounted) { + console.error('Bundle unmounted while loading Component.'); + return; + } + this.setState({ failed: true }); + console.error('Bundle load failed.', err); + } + + renderLoading = ({ text }) => ( +
+ {text} +
+ ) + + render() { + const { + load, // eslint-disable-line no-unused-vars + decorator, // eslint-disable-line no-unused-vars + errorText, + loadingText, + ...otherProps + } = this.props; + const { + BundledComponent, + failed, + } = this.state; + + if (!BundledComponent) { + const message = failed ? errorText : loadingText; + const Loading = this.renderLoading; + return ; + } + + return ; + } +} + +export default Bundle; diff --git a/client/src/components/Navbar/__tests__/index.js b/client/src/components/Navbar/__tests__/index.js deleted file mode 100644 index da313f8..0000000 --- a/client/src/components/Navbar/__tests__/index.js +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; -import configureStore from 'redux-mock-store'; -import { Provider } from 'react-redux'; -import { shallow } from 'enzyme'; -import Navbar from '../'; - -const initialState = { -}; - -describe('', () => { - const mockStore = configureStore(); - const store = mockStore(initialState); - - const wrapper = shallow( - - {}} - setActiveProject={() => {}} - stopTokenRefresh={() => {}} - /> - , - ); - - it('renders properly', () => { - expect(wrapper.length).toEqual(1); - }); -}); diff --git a/client/src/components/RouteSynchronizer.js b/client/src/components/RouteSynchronizer.js index d2ca185..2debff2 100644 --- a/client/src/components/RouteSynchronizer.js +++ b/client/src/components/RouteSynchronizer.js @@ -1,7 +1,7 @@ import Helmet from 'react-helmet'; import React, { Fragment } from 'react'; -import Bundle from '#rscg/Bundle'; +import Bundle from '#components/Bundle'; export default ({ name, ...otherProps }) => ( diff --git a/client/src/config/rest.js b/client/src/config/rest.js deleted file mode 100644 index c15d5cf..0000000 --- a/client/src/config/rest.js +++ /dev/null @@ -1,26 +0,0 @@ -import { RestRequest } from '#rsu/rest'; - -export const GET = 'GET'; - -// ENDPOINTS - -export const wsEndpoint = !process.env.REACT_APP_API_END - ? 'http://localhost:8006/api/v1' - : `${process.env.REACT_APP_API_END}/api/v1`; - - -// COMMON HEADERS - -export const commonHeaderForGet = { - Accept: 'application/json', - 'Content-Type': 'application/json', -}; - -// COMMON PARAMS - -export const createParamsForGet = () => ({ - method: GET, - headers: commonHeaderForGet, -}); - -export const p = RestRequest.prepareUrlParams; diff --git a/client/src/constants/routes.js b/client/src/constants/routes.js index fe1d2be..6ff756b 100644 --- a/client/src/constants/routes.js +++ b/client/src/constants/routes.js @@ -18,6 +18,12 @@ export const routes = { path: '/', loader: () => import('../views/Home'), }, + career: { + order: 2, + type: ROUTE.public, + path: '/career/', + loader: () => import('../views/Career'), + }, }; export const pathNames = mapObjectToObject(routes, route => route.path); diff --git a/client/src/index.js b/client/src/index.js index cce2f32..b597a44 100644 --- a/client/src/index.js +++ b/client/src/index.js @@ -1,5 +1,5 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import Root from './Root'; +import App from './App'; -ReactDOM.render(, document.getElementById('root')); +ReactDOM.render(, document.getElementById('root')); diff --git a/client/src/redux/index.js b/client/src/redux/index.js deleted file mode 100644 index 76b3627..0000000 --- a/client/src/redux/index.js +++ /dev/null @@ -1,4 +0,0 @@ -export * from './selectors/domainData'; - -// NOTE: named exports from reducers are action-creators and action-types -export * from './reducers/domainData'; diff --git a/client/src/redux/initial-state/domainData.js b/client/src/redux/initial-state/domainData.js deleted file mode 100644 index a4061bd..0000000 --- a/client/src/redux/initial-state/domainData.js +++ /dev/null @@ -1,8 +0,0 @@ -const initialDomainDataState = { - members: [], - clients: [], - services: [], - technologySections: [], -}; - -export default initialDomainDataState; diff --git a/client/src/redux/middlewares/logger.js b/client/src/redux/middlewares/logger.js deleted file mode 100644 index 152ad54..0000000 --- a/client/src/redux/middlewares/logger.js +++ /dev/null @@ -1,10 +0,0 @@ -// eslint-disable-next-line no-unused-vars -const logger = store => next => (action) => { - if (action) { - console.info(`DISPATCHING ${action.type}`); - } - const result = next(action); - return result; -}; - -export default logger; diff --git a/client/src/redux/reducers/domainData.js b/client/src/redux/reducers/domainData.js deleted file mode 100644 index 4d56908..0000000 --- a/client/src/redux/reducers/domainData.js +++ /dev/null @@ -1,82 +0,0 @@ -import update from '#rsu/immutable-update'; -import createReducerWithMap from '#utils/createReducerWithMap'; -import initialDomainDataState from '../initial-state/domainData'; - -// TYPE - -export const SET_MEMBERS = 'domainData/SET_MEMBERS'; -export const SET_CLIENTS = 'domainData/SET_CLIENTS'; -export const SET_SERVICES = 'domainData/SET_SERVICES'; -export const SET_TECHNOLOGY_SECTIONS = 'domainData/SET_TECHNOLOGY_SECTIONS'; - - -// ACTION-CREATOR - -export const setMembersAction = members => ({ - type: SET_MEMBERS, - members, -}); - -export const setClientsAction = clients => ({ - type: SET_CLIENTS, - clients, -}); - -export const setServicesAction = services => ({ - type: SET_SERVICES, - services, -}); - -export const setTechnologySectionsAction = technologySections => ({ - type: SET_TECHNOLOGY_SECTIONS, - technologySections, -}); - -// REDUCER - -const setMembers = (state, { members }) => { - const settings = { - members: { $autoArray: { - $set: members, - } }, - }; - return update(state, settings); -}; - -const setClients = (state, { clients }) => { - const settings = { - clients: { $autoArray: { - $set: clients, - } }, - }; - return update(state, settings); -}; - -const setServices = (state, { services }) => { - const settings = { - services: { $autoArray: { - $set: services, - } }, - }; - return update(state, settings); -}; - -const setTechnologySections = (state, { technologySections }) => { - const settings = { - technologySections: { $autoArray: { - $set: technologySections, - } }, - }; - return update(state, settings); -}; - -const reducers = { - [SET_MEMBERS]: setMembers, - [SET_CLIENTS]: setClients, - [SET_SERVICES]: setServices, - [SET_TECHNOLOGY_SECTIONS]: setTechnologySections, -}; - -const reducer = createReducerWithMap(reducers, initialDomainDataState); - -export default reducer; diff --git a/client/src/redux/reducers/index.js b/client/src/redux/reducers/index.js deleted file mode 100644 index ce2b13d..0000000 --- a/client/src/redux/reducers/index.js +++ /dev/null @@ -1,13 +0,0 @@ -import { persistCombineReducers } from 'redux-persist'; -import storeConfig from '#config/store'; - -import domainDataReducer from './domainData'; - -const reducers = { - dummy: (s = {}) => s, - - domainData: domainDataReducer, -}; - -const reducer = persistCombineReducers(storeConfig, reducers); -export default reducer; diff --git a/client/src/redux/selectors/domainData.js b/client/src/redux/selectors/domainData.js deleted file mode 100644 index dc04e2b..0000000 --- a/client/src/redux/selectors/domainData.js +++ /dev/null @@ -1,18 +0,0 @@ -const emptyList = []; - - -export const membersSelector = ({ domainData }) => ( - domainData.members || emptyList -); - -export const clientsSelector = ({ domainData }) => ( - domainData.clients || emptyList -); - -export const servicesSelector = ({ domainData }) => ( - domainData.services || emptyList -); - -export const technologySectionsSelector = ({ domainData }) => ( - domainData.technologySections || emptyList -); diff --git a/client/src/resources/img/arc.png b/client/src/resources/img/arc.png new file mode 100644 index 0000000..7861b59 Binary files /dev/null and b/client/src/resources/img/arc.png differ diff --git a/client/src/resources/img/aws.png b/client/src/resources/img/aws.png new file mode 100644 index 0000000..b61f32f Binary files /dev/null and b/client/src/resources/img/aws.png differ diff --git a/client/src/resources/img/bc.png b/client/src/resources/img/bc.png new file mode 100644 index 0000000..5c7abad Binary files /dev/null and b/client/src/resources/img/bc.png differ diff --git a/client/src/resources/img/bek.jpg b/client/src/resources/img/bek.jpg new file mode 100644 index 0000000..eb98f04 Binary files /dev/null and b/client/src/resources/img/bek.jpg differ diff --git a/client/src/resources/img/cs.png b/client/src/resources/img/cs.png new file mode 100644 index 0000000..a506f9d Binary files /dev/null and b/client/src/resources/img/cs.png differ diff --git a/client/src/resources/img/cyclical.png b/client/src/resources/img/cyclical.png new file mode 100644 index 0000000..cb9d7c1 Binary files /dev/null and b/client/src/resources/img/cyclical.png differ diff --git a/client/src/resources/img/d3.png b/client/src/resources/img/d3.png new file mode 100644 index 0000000..fe4a306 Binary files /dev/null and b/client/src/resources/img/d3.png differ diff --git a/client/src/resources/img/dfs.png b/client/src/resources/img/dfs.png new file mode 100644 index 0000000..8d0810d Binary files /dev/null and b/client/src/resources/img/dfs.png differ diff --git a/client/src/resources/img/django.png b/client/src/resources/img/django.png new file mode 100644 index 0000000..da7cf9a Binary files /dev/null and b/client/src/resources/img/django.png differ diff --git a/client/src/resources/img/docker.png b/client/src/resources/img/docker.png new file mode 100644 index 0000000..385a004 Binary files /dev/null and b/client/src/resources/img/docker.png differ diff --git a/client/src/resources/img/ds.png b/client/src/resources/img/ds.png new file mode 100644 index 0000000..a2dc038 Binary files /dev/null and b/client/src/resources/img/ds.png differ diff --git a/client/src/resources/img/gensim.png b/client/src/resources/img/gensim.png new file mode 100644 index 0000000..9dc770f Binary files /dev/null and b/client/src/resources/img/gensim.png differ diff --git a/client/src/resources/img/idmc.png b/client/src/resources/img/idmc.png new file mode 100644 index 0000000..b4cef3c Binary files /dev/null and b/client/src/resources/img/idmc.png differ diff --git a/client/src/resources/img/ifrc.png b/client/src/resources/img/ifrc.png new file mode 100644 index 0000000..b13169d Binary files /dev/null and b/client/src/resources/img/ifrc.png differ diff --git a/client/src/resources/img/jips.png b/client/src/resources/img/jips.png new file mode 100644 index 0000000..71babec Binary files /dev/null and b/client/src/resources/img/jips.png differ diff --git a/client/src/resources/img/jquery.png b/client/src/resources/img/jquery.png new file mode 100644 index 0000000..3db528d Binary files /dev/null and b/client/src/resources/img/jquery.png differ diff --git a/client/src/resources/img/leaflet.png b/client/src/resources/img/leaflet.png new file mode 100644 index 0000000..7df41ab Binary files /dev/null and b/client/src/resources/img/leaflet.png differ diff --git a/client/src/resources/img/mapbox.png b/client/src/resources/img/mapbox.png new file mode 100644 index 0000000..be147a0 Binary files /dev/null and b/client/src/resources/img/mapbox.png differ diff --git a/client/src/resources/img/okular.png b/client/src/resources/img/okular.png new file mode 100644 index 0000000..9b1e887 Binary files /dev/null and b/client/src/resources/img/okular.png differ diff --git a/client/src/resources/img/pin.png b/client/src/resources/img/pin.png new file mode 100644 index 0000000..055346d Binary files /dev/null and b/client/src/resources/img/pin.png differ diff --git a/client/src/resources/img/postgres.png b/client/src/resources/img/postgres.png new file mode 100644 index 0000000..c2f96e4 Binary files /dev/null and b/client/src/resources/img/postgres.png differ diff --git a/client/src/resources/img/r.png b/client/src/resources/img/r.png new file mode 100644 index 0000000..10ca780 Binary files /dev/null and b/client/src/resources/img/r.png differ diff --git a/client/src/resources/img/re.png b/client/src/resources/img/re.png new file mode 100644 index 0000000..df74b61 Binary files /dev/null and b/client/src/resources/img/re.png differ diff --git a/client/src/resources/img/react-native.png b/client/src/resources/img/react-native.png new file mode 100644 index 0000000..766c027 Binary files /dev/null and b/client/src/resources/img/react-native.png differ diff --git a/client/src/resources/img/react.png b/client/src/resources/img/react.png new file mode 100644 index 0000000..9dcf2e9 Binary files /dev/null and b/client/src/resources/img/react.png differ diff --git a/client/src/resources/img/sass.png b/client/src/resources/img/sass.png new file mode 100644 index 0000000..b120069 Binary files /dev/null and b/client/src/resources/img/sass.png differ diff --git a/client/src/resources/img/scikit.png b/client/src/resources/img/scikit.png new file mode 100644 index 0000000..52726b9 Binary files /dev/null and b/client/src/resources/img/scikit.png differ diff --git a/client/src/resources/img/se.png b/client/src/resources/img/se.png new file mode 100644 index 0000000..e99d013 Binary files /dev/null and b/client/src/resources/img/se.png differ diff --git a/client/src/resources/img/smtm.png b/client/src/resources/img/smtm.png new file mode 100644 index 0000000..92f38a7 Binary files /dev/null and b/client/src/resources/img/smtm.png differ diff --git a/client/src/resources/img/tc.png b/client/src/resources/img/tc.png new file mode 100644 index 0000000..8acd859 Binary files /dev/null and b/client/src/resources/img/tc.png differ diff --git a/client/src/resources/img/tensorflow.png b/client/src/resources/img/tensorflow.png new file mode 100644 index 0000000..896684d Binary files /dev/null and b/client/src/resources/img/tensorflow.png differ diff --git a/client/src/resources/img/unhcr.png b/client/src/resources/img/unhcr.png new file mode 100644 index 0000000..d1a9d2c Binary files /dev/null and b/client/src/resources/img/unhcr.png differ diff --git a/client/src/resources/img/wvi.png b/client/src/resources/img/wvi.png new file mode 100644 index 0000000..f8f9b4f Binary files /dev/null and b/client/src/resources/img/wvi.png differ diff --git a/client/src/rest/index.js b/client/src/rest/index.js deleted file mode 100644 index 7bc3926..0000000 --- a/client/src/rest/index.js +++ /dev/null @@ -1,6 +0,0 @@ -import { wsEndpoint } from '#config/rest'; - -export const urlForMembers = `${wsEndpoint}/members/`; -export const urlForClients = `${wsEndpoint}/clients/`; -export const urlForServices = `${wsEndpoint}/services/`; -export const urlForTechnologySection = `${wsEndpoint}/technology-sections/`; diff --git a/client/src/schema/index.js b/client/src/schema/index.js index 415f591..40b1a7d 100644 --- a/client/src/schema/index.js +++ b/client/src/schema/index.js @@ -4,23 +4,6 @@ const basicTypeSchemas = basicTypes.map(entry => ({ name: entry.doc.name, schema const userDefinedSchemas = []; -// Service Scheme -{ - const name = 'service'; - const schema = { - doc: { - name: 'Service', - }, - fields: { - id: { type: 'uint', required: true }, - title: { type: 'string', required: true }, - description: { type: 'string' }, - image: { type: 'string' }, - }, - }; - userDefinedSchemas.push({ name, schema }); -} - // Member Scheme { const name = 'memberUrlType'; @@ -62,6 +45,7 @@ const userDefinedSchemas = []; name: { type: 'string', required: true }, image: { type: 'string', required: true }, designation: { type: 'string', required: true }, + hidden: { type: 'boolean' }, membersUrls: { type: 'array.memberUrl', required: true }, }, }; diff --git a/client/src/store.js b/client/src/store.js deleted file mode 100644 index a3aa7f0..0000000 --- a/client/src/store.js +++ /dev/null @@ -1,38 +0,0 @@ -import { compose, createStore, applyMiddleware } from 'redux'; -import thunk from 'redux-thunk'; -import logger from '#redux/middlewares/logger'; -import { reducersToSync } from '#config/store'; -import { createActionSyncMiddleware } from '#rsu/redux-sync'; -import reducer from '#redux/reducers'; - -const prepareStore = () => { - // Invoke refresh access token every 10m - const middleware = [ - logger, - createActionSyncMiddleware(reducersToSync), - thunk, - ]; - - // Get compose from Redux Devtools Extension - // eslint-disable-next-line no-underscore-dangle - const reduxExtensionCompose = typeof window === 'object' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__; - - // Override compose if development mode and redux extension is installed - const overrideCompose = process.env.NODE_ENV === 'development' && reduxExtensionCompose; - const applicableComposer = !overrideCompose - ? compose - : reduxExtensionCompose({ /* specify extention's options here */ }); - - const enhancer = applicableComposer( - applyMiddleware(...middleware), - ); - return createStore(reducer, undefined, enhancer); -}; - - -const isTest = process.env.NODE_ENV === 'test'; - -// NOTE: replace 'undefined' with an initialState in future if needed -const store = !isTest ? prepareStore() : undefined; - -export default store; diff --git a/client/src/stylesheets/_base.scss b/client/src/stylesheets/_base.scss index 8fe7471..57d49fe 100644 --- a/client/src/stylesheets/_base.scss +++ b/client/src/stylesheets/_base.scss @@ -42,21 +42,12 @@ body { font-size: $font-size-default; font-weight: $font-weight-default; - a { - outline: none; - text-transform: uppercase; - text-decoration: none; - color: inherit; - } - h1, h2, h3, h4, h5 { margin: 0; - text-transform: uppercase; - font-weight: $font-weight-light; } } diff --git a/client/src/utils/Request.js b/client/src/utils/Request.js index b00e8bc..dec4db4 100644 --- a/client/src/utils/Request.js +++ b/client/src/utils/Request.js @@ -1,97 +1,160 @@ -import { RestRequest } from '#rsu/rest'; -import schema from '#schema'; -import { alterResponseErrorToFaramError } from '#rest'; - -const requestNotCreatedForStartMessage = 'REQUEST: start() called before init()'; -// const requestNotCreatedForStopMessage = 'REQUEST: stop() called before init()'; -const validationNotDefinedMessage = 'REQUEST: Validation is not defined'; +import { + createRequestCoordinator, + createRequestClient, + methods, +} from '@togglecorp/react-rest-request'; -export default class Request { - constructor(parent, { delay = 50, retryTime = 1000, maxRetryAttempts = 5 } = {}) { - this.parent = parent; +import { sanitizeResponse } from '#utils/common'; +import schema from '#schema'; - this.delay = delay; +export const wsEndpoint = !process.env.REACT_APP_API_END + ? 'http://localhost:8006/api/v1' + : `${process.env.REACT_APP_API_END}/api/v1`; - this.retryTime = retryTime; - this.maxRetryAttempts = maxRetryAttempts; - this.schemaName = undefined; - } +export { methods, RequestHandler } from '@togglecorp/react-rest-request'; - handleFatal = () => { - // console.warn(error); +const getFormData = (jsonData) => { + const formData = new FormData(); + Object.keys(jsonData || {}).forEach( + (key) => { + const value = jsonData[key] || {}; + if (value.prop && value.prop.constructor === Array) { + value.forEach(v => formData.append(key, v)); + } else { + formData.append(key, value); + } + }, + ); + return formData; +}; + +export function getVersionedUrl(endpoint, url) { + const oldVersionString = '/v1'; + const versionString = '/v2'; + if (!url.startsWith(versionString)) { + return `${endpoint}${url}`; } + const startIndex = 0; + const endIndex = endpoint.search(oldVersionString); + const newEndpoint = endpoint.slice(startIndex, endIndex); + return `${newEndpoint}${url}`; +} - start = () => { - if (this.request) { - this.request.start(); - } else { - console.error(requestNotCreatedForStartMessage); +const coordinatorOptions = { + transformParams: (data) => { + const { + body, + method, + extras = {}, + } = data; + + const newBody = extras.hasFile + ? getFormData(body) + : JSON.stringify(body); + + const newHeaders = extras.hasFile + ? { + Accept: 'application/json', + } + : { + Accept: 'application/json', + 'Content-Type': 'application/json; charset=utf-8', + }; + + const params = { + method: method || methods.GET, + body: newBody, + headers: newHeaders, + }; + + // NOTE: This is a hack to bypass auth for S3 requests + // Need to fix this through use of new react-rest-request@2 + // FIXME: react-rest-request@2 has been used + // need to fix this using extras + + return params; + }, + + transformProps: (props) => { + const { + myToken, // eslint-disable-line no-unused-vars + ...otherProps + } = props; + return otherProps; + }, + + transformUrl: (url) => { + if (/^https?:\/\//i.test(url)) { + return url; } - } + return getVersionedUrl(wsEndpoint, url); + }, - stop = () => { - if (this.request) { - this.request.stop(); - } - /* - else { - console.error(requestNotCreatedForStopMessage); - } - */ - } + transformResponse: (body, request) => { + const { + url, + method, + extras = {}, + } = request; - successInterceptor = (response) => { - if (this.schemaName !== undefined) { + // TODO: add null sanitization here + + if (extras.schemaName === undefined) { + // NOTE: usually there is no response body for DELETE + if (method !== methods.DELETE) { + console.error(`Schema is not defined for ${url} ${method}`); + } + } else { try { - schema.validate(response, this.schemaName); + schema.validate(body, extras.schemaName); } catch (e) { - console.error('NETWORK ERROR:', e); - this.handleFatal({ errorMessage: e, errroCode: null }); - return; + console.error(url, method, body, e.message); + throw (e); } - } else { - console.warn(validationNotDefinedMessage); } + return sanitizeResponse(body); + }, - this.handleSuccess(response); - } + transformErrors: response => ({ response }), +}; - failureInterceptor = (response) => { - const newResponse = alterResponseErrorToFaramError(response.errors); - this.handleFailure(newResponse); - } +export const RequestCoordinator = createRequestCoordinator(coordinatorOptions); - createDefault = (createOptions) => { - this.stop(); +export const RequestClient = createRequestClient; - this.createOptions = createOptions; +export const getResponse = (requests, key, defaultValue = {}) => { + const { response = defaultValue } = (requests || {})[key]; + return response; +}; - const { - url, - params, - } = createOptions; +export const getResults = (requests, key, defaultValue = []) => { + const { + response: { + results = defaultValue, + } = {}, + } = (requests || {})[key]; - const request = new RestRequest( - url, - params, - this.handleSuccess ? this.successInterceptor : undefined, - this.handleFailure ? this.failureInterceptor : undefined, - this.handleFatal, - this.handleAbort, - this.handlePreLoad, - this.handlePostLoad, - this.handleAfterLoad, - this.retryTime, - this.maxRetryTime, - this.decayVal, - this.maxRetryAttempts, - this.pollTime, - this.maxPollAttempts, - this.shouldPoll, - this.delay, - ); - - this.request = request; + return results; +}; + +export const getPending = (requests, key) => { + const { + pending, + } = (requests || {})[key]; + + return pending; +}; + +export const isAnyRequestPending = (requests) => { + if (!requests) { + return undefined; } -} + + const requestKeys = Object.keys(requests); + const pending = requestKeys.some( + requestKey => requests[requestKey].pending, + ); + + return pending; +}; diff --git a/client/src/utils/common.js b/client/src/utils/common.js index bdb48df..1b413c3 100644 --- a/client/src/utils/common.js +++ b/client/src/utils/common.js @@ -1,3 +1,9 @@ +import { + isDefined, + isObject, + isList, +} from '@togglecorp/fujs'; + export const mapObjectToObject = (obj, fn) => { const newObj = {}; Object.keys(obj).forEach((key) => { @@ -20,3 +26,33 @@ export const pick = (obj, keys) => keys.reduce( (acc, key) => ({ ...acc, [key]: obj[key] }), {}, ); + +export const forEach = (obj, func) => { + Object.keys(obj).forEach((key) => { + const val = obj[key]; + func(key, val); + }); +}; + +export const sanitizeResponse = (data) => { + if (data === null || data === undefined) { + return undefined; + } + if (isList(data)) { + return data.map(sanitizeResponse).filter(isDefined); + } + if (isObject(data)) { + let newData = {}; + forEach(data, (k, val) => { + const newEntry = sanitizeResponse(val); + if (isDefined(newEntry)) { + newData = { + ...newData, + [k]: newEntry, + }; + } + }); + return newData; + } + return data; +}; diff --git a/client/src/utils/getUserConfirmation.js b/client/src/utils/getUserConfirmation.js deleted file mode 100644 index c17f220..0000000 --- a/client/src/utils/getUserConfirmation.js +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import Confirm from '#rscv/Modal/Confirm'; - -const getUserConfirmation = (message, confirm) => { - const container = document.createElement('div'); - document.body.appendChild(container); - - const confirmWithCleanup = (result) => { - ReactDOM.unmountComponentAtNode(container); - document.body.removeChild(container); - confirm(result); - }; - - ReactDOM.render( - { confirmWithCleanup(result); }} - > -

{ message }

-
, - container, - ); -}; - -export default getUserConfirmation; diff --git a/client/src/views/Career/CareerItem/index.js b/client/src/views/Career/CareerItem/index.js new file mode 100644 index 0000000..5943117 --- /dev/null +++ b/client/src/views/Career/CareerItem/index.js @@ -0,0 +1,79 @@ +import React, { useCallback } from 'react'; +import PropTypes from 'prop-types'; +import { + _cs, + isDefined, +} from '@togglecorp/fujs'; +import ReactMarkdown from 'react-markdown'; + +import styles from './styles.scss'; + +const CareerItem = ({ + title, + url, + description, + isActive, + setActive, + dataKey, +}) => { + const handleExpandClick = useCallback(() => { + setActive(!isActive && dataKey); + }, [isActive, setActive, dataKey]); + + const finalUrl = isDefined(url) + ? url + : `mailto:hr@togglecorp.com?subject=Application for ${title}`; + + return ( +
+ + {isActive && ( + + + + + )} +
+ ); +}; + +CareerItem.propTypes = { + title: PropTypes.string.isRequired, + url: PropTypes.string, + description: PropTypes.string.isRequired, + isActive: PropTypes.bool.isRequired, + setActive: PropTypes.func.isRequired, + dataKey: PropTypes.number.isRequired, +}; + +CareerItem.defaultProps = { + url: undefined, +}; + +export default CareerItem; diff --git a/client/src/views/Career/CareerItem/styles.scss b/client/src/views/Career/CareerItem/styles.scss new file mode 100644 index 0000000..c01df40 --- /dev/null +++ b/client/src/views/Career/CareerItem/styles.scss @@ -0,0 +1,58 @@ +@import '~base-scss/base'; + +.career-item { + .career-header { + display: flex; + align-items: center; + outline: none; + border: 0; + background-color: transparent; + padding: $spacing-large; + width: 100%; + text-transform: unset; + font-family: inherit; + font-size: unset; + + &:last-child { + border-bottom: $width-separator-thin solid $color-separator; + } + + .career-heading { + flex-grow: 1; + text-align: left; + text-transform: unset; + color: $color-accent; + font-size: 1.5em; + font-weight: $font-weight-bold; + } + + .button { + flex-shrink: 0; + background-color: transparent; + font-family: inherit; + } + } + + .description { + padding: $spacing-small $spacing-large; + } + + .footer { + display: flex; + justify-content: flex-end; + padding: $spacing-small $spacing-large $spacing-medium; + + .apply-link { + @extend %button-like-link; + background-color: $color-accent; + text-decoration: none; + color: #fff; + } + } + + &:not(:last-child) { + .footer { + border-bottom: $width-separator-thin solid $color-separator; + } + } +} diff --git a/client/src/views/Career/index.js b/client/src/views/Career/index.js new file mode 100644 index 0000000..5d08748 --- /dev/null +++ b/client/src/views/Career/index.js @@ -0,0 +1,112 @@ +import React, { useState } from 'react'; +import PropTypes from 'prop-types'; +import { Link } from 'react-router-dom'; + +import { + RequestCoordinator, + RequestClient, + methods, +} from '#utils/Request'; + +import CareerItem from './CareerItem'; +import styles from './styles.scss'; + +const EmptyComponent = () => ( +
+
+ We do not have any openings right now, but please send your resume and your interest at + + hr@togglecorp.com + + . +
+
+); + +const propTypes = { + requests: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types +}; + +const requestOptions = { + careerGet: { + url: '/careers/', + method: methods.GET, + onMount: true, + }, +}; + +function Career(props) { + const { + requests: { + careerGet: { + pending, + response = [], + }, + }, + } = props; + + const [active, setActive] = useState(); + + return ( +
+
+ +

+ Work with us +

+

+ Togglecorp is changing the way software is built. + We are looking to hire smart people to be a part of our team. + If you are passionate about quality softwares and love the minimalist + approach to problem solving, + please apply to one of the following open positions. +

+
+
+
+ {response.length > 0 && response.map(career => ( + + ))} + {response.length < 1 && !pending && ( + + )} +
+
+
+
+ Cannot find what you are looking for? Email us at + + hr@togglecorp.com + +
+
+
+ ); +} + +Career.propTypes = propTypes; + +export default RequestCoordinator(RequestClient(requestOptions)(Career)); diff --git a/client/src/views/Career/styles.scss b/client/src/views/Career/styles.scss new file mode 100644 index 0000000..143a06d --- /dev/null +++ b/client/src/views/Career/styles.scss @@ -0,0 +1,136 @@ +@import '~base-scss/base'; + +.career-page { + display: flex; + align-items: center; + flex-direction: column; + background-color: #fff; + width: 100vw; + height: 100vh; + overflow-x: hidden; + overflow-y: auto; + + .header { + display: flex; + flex-direction: column; + flex-shrink: 0; + border-bottom: $width-separator-thin solid $color-separator; + /* + background-color: #fc5296; + background-image: linear-gradient(315deg, #fc5296 0%, #f67062 74%); + */ + background-color: #0cbaba; + background-image: linear-gradient(315deg, #0cbaba 0%, #380036 74%); + + padding: 0 $spacing-super-large $spacing-super-large; + width: 100%; + text-align: center; + color: #fff; + + .navigation { + margin: auto; + padding: $spacing-large 0; + width: 80vw; + max-width: 720px; + text-align: left; + + .nav-link { + text-decoration: none; + color: #fff; + } + + h1 { + margin: 0; + padding: 0; + text-transform: lowercase; + line-height: 1; + + span:last-child { + color: $color-accent; + } + } + } + + .heading { + padding: $spacing-large; + text-transform: unset; + font-size: 36px; + font-weight: bold; + } + + .description { + margin: $spacing-medium auto; + max-width: 720px; + text-align: center; + font-size: 20px; + } + } + + .content { + display: flex; + position: relative; + flex-direction: column; + flex-grow: 1; + width: 100vw; + + .career { + display: flex; + flex-direction: column; + flex-grow: 1; + margin: auto; + padding: $spacing-large; + width: 80vw; + max-width: 720px; + + .empty-component { + display: inline-flex; + align-items: center; + flex-direction: column; + flex-grow: 1; + justify-content: center; + opacity: 0.8; + text-align: center; + font-size: $font-size-large; + } + } + } + + .footer { + border-top: $width-separator-thin solid $color-separator; + padding: $spacing-small; + width: 100vw; + + .inner-child { + margin: auto; + width: 80vw; + max-width: 720px; + text-align: center; + } + } + + .link { + margin: 0 3px; + text-transform: unset; + color: #1261a0; + } + + @media screen and (max-width: 720px) { + .header { + padding: 0 $spacing-large $spacing-large; + + .heading { + font-size: 24px; + } + + .description { + font-size: 16px; + } + } + + .content { + .career { + width: 96vw; + } + } + } +} diff --git a/client/src/views/Home/Clients/index.js b/client/src/views/Home/Clients/index.js index 4917dc4..a440316 100644 --- a/client/src/views/Home/Clients/index.js +++ b/client/src/views/Home/Clients/index.js @@ -1,29 +1,111 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import ListView from '#rscv/List/ListView'; - -import { clientsSelector } from '#redux'; +import bek from '#resources/img/bek.jpg'; +import okular from '#resources/img/okular.png'; +import dfs from '#resources/img/dfs.png'; +import arc from '#resources/img/arc.png'; +import ifrc from '#resources/img/ifrc.png'; +import jips from '#resources/img/jips.png'; +import bc from '#resources/img/bc.png'; +import idmc from '#resources/img/idmc.png'; +import pin from '#resources/img/pin.png'; +import unhcr from '#resources/img/unhcr.png'; +import smtm from '#resources/img/smtm.png'; +import wvi from '#resources/img/wvi.png'; +import cyclical from '#resources/img/cyclical.png'; import styles from './styles.scss'; +const clients = [ + { + id: 1, + title: 'bek', + url: 'https://www.gov.uk/world/organisations/british-embassy-kathmandu', + image: bek, + }, + { + id: 2, + title: 'Okular Analytics', + url: 'https://www.okular-analytics.com/', + image: okular, + }, + { + id: 3, + title: 'Cyclical Insights', + url: 'https://cyclical.io/', + image: cyclical, + }, + { + id: 4, + title: 'World Vision Nepal', + url: 'https://www.wvi.org/nepal', + image: wvi, + }, + { + id: 5, + title: 'UNHCR', + url: 'http://www.unhcr.org/', + image: unhcr, + }, + { + id: 6, + title: 'PIN', + url: 'https://www.clovekvtisni.cz/en/what-we-do/humanitarian-aid-and-development/nepal', + image: pin, + }, + { + id: 7, + title: 'SMTM Capital', + url: 'https://www.smtmcapital.com.np/', + image: smtm, + }, + { + id: 8, + title: 'IDMC', + url: 'http://www.internal-displacement.org/', + image: idmc, + }, + { + id: 9, + title: 'IFRC', + url: 'https://media.ifrc.org/ifrc/', + image: ifrc, + }, + { + id: 10, + title: 'DFS', + url: 'https://datafriendlyspace.org/', + image: dfs, + }, + { + id: 11, + title: 'JIPS', + url: 'https://www.jips.org/', + image: jips, + }, + { + id: 12, + title: 'American Red Cross', + url: 'https://www.redcross.org/', + image: arc, + }, + { + id: 13, + title: 'Boldcode', + url: 'https://boldcode.io/en/', + image: bc, + }, +]; + const propTypes = { className: PropTypes.string, - clients: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types }; -const mapStateToProps = (state, props) => ({ - clients: clientsSelector(state, props), -}); - const defaultProps = { className: '', }; -const keyExtractor = member => member.id; -const rendererParams = (key, client) => ({ client }); - const Client = ({ client }) => (
{'Organizations we\'ve worked with'} - {clients.length > 0 && ( - - )} +
+ {clients.length > 0 && clients.map(client => ( + + ))} +
); } diff --git a/client/src/views/Home/Expertise/index.js b/client/src/views/Home/Expertise/index.js index 118a27a..a4d1a7a 100644 --- a/client/src/views/Home/Expertise/index.js +++ b/client/src/views/Home/Expertise/index.js @@ -1,19 +1,140 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import ListView from '#rscv/List/ListView'; -import List from '#rscv/List'; - -import { technologySectionsSelector } from '#redux'; +import aws from '#resources/img/aws.png'; +import d3 from '#resources/img/d3.png'; +import docker from '#resources/img/docker.png'; +import django from '#resources/img/django.png'; +import leaflet from '#resources/img/leaflet.png'; +import gensim from '#resources/img/gensim.png'; +import jquery from '#resources/img/jquery.png'; +import mapbox from '#resources/img/mapbox.png'; +import postgres from '#resources/img/postgres.png'; +import r from '#resources/img/r.png'; +import react from '#resources/img/react.png'; +import reactNative from '#resources/img/react-native.png'; +import sass from '#resources/img/sass.png'; +import scikit from '#resources/img/scikit.png'; +import tensorflow from '#resources/img/tensorflow.png'; import styles from './styles.scss'; -const keyExtractor = row => row.id; -// Technology Renderer Params -const tRendererParams = (key, technology) => ({ technology }); -// Technology Section Renderer Params -const tsRendererParams = (key, technologySection) => ({ technologySection }); +const sections = [ + { + id: 1, + title: 'Web server', + technologies: [ + { + id: 1, + title: 'Django', + url: 'https://www.djangoproject.com/', + image: django, + }, + { + id: 2, + title: 'Postgres', + url: 'https://www.postgresql.org/', + image: postgres, + }, + { + id: 3, + title: 'Docker', + url: 'https://www.docker.com/', + image: docker, + }, + { + id: 4, + title: 'AWS', + url: 'https://aws.amazon.com/', + image: aws, + }, + ], + }, + { + id: 2, + title: 'Frontend', + technologies: [ + { + id: 5, + title: 'React', + url: 'https://reactjs.org/', + image: react, + }, + { + id: 6, + title: 'Jquery', + url: 'https://jquery.com/', + image: jquery, + }, + { + id: 7, + title: 'Sass', + url: 'https://sass-lang.com/', + image: sass, + }, + { + id: 11, + title: 'React Native', + url: 'https://facebook.github.io/react-native/', + image: reactNative, + }, + ], + }, + { + id: 3, + title: 'Visualization', + technologies: [ + { + id: 8, + title: 'D3', + url: 'https://d3js.org/', + image: d3, + }, + { + id: 9, + title: 'Mapbox', + url: 'https://www.mapbox.com/', + image: mapbox, + }, + { + id: 10, + title: 'Leaflet', + url: 'https://leafletjs.com/', + image: leaflet, + }, + ], + }, + { + id: 4, + title: 'Data Analysis and Machine Learning', + technologies: [ + { + id: 12, + title: 'scikit-learn', + url: 'http://scikit-learn.org/', + image: scikit, + }, + { + id: 13, + title: 'TensorFlow', + url: 'https://www.tensorflow.org/', + image: tensorflow, + }, + { + id: 14, + title: 'Gensim', + url: 'https://radimrehurek.com/gensim/', + image: gensim, + }, + { + id: 15, + title: 'R', + url: 'https://www.r-project.org/', + image: r, + }, + ], + }, +]; const Technology = ({ technology }) => (
  • @@ -24,8 +145,8 @@ const Technology = ({ technology }) => ( > {technology.name}
  • @@ -37,12 +158,12 @@ Technology.propTypes = { const TechnologySection = ({ technologySection }) => (
      - + {technologySection.technologies.map(tech => ( + + ))}
    ); @@ -53,28 +174,18 @@ TechnologySection.propTypes = { const propTypes = { className: PropTypes.string, - technologySections: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types }; -const mapStateToProps = (state, props) => ({ - technologySections: technologySectionsSelector(state, props), -}); - const defaultProps = { className: '', }; - -@connect(mapStateToProps) export default class Expertise extends React.PureComponent { static propTypes = propTypes; static defaultProps = defaultProps; render() { - const { - className, - technologySections, - } = this.props; + const { className } = this.props; return (
    Our expertise - {technologySections.length > 0 && ( - - )} +
    + {sections.map(section => ( + + ))} +
    ); } diff --git a/client/src/views/Home/Services/index.js b/client/src/views/Home/Services/index.js index e01ee11..dffd0a7 100644 --- a/client/src/views/Home/Services/index.js +++ b/client/src/views/Home/Services/index.js @@ -1,29 +1,48 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import ListView from '#rscv/List/ListView'; - -import { servicesSelector } from '#redux'; +import cs from '#resources/img/cs.png'; +import ds from '#resources/img/ds.png'; +import se from '#resources/img/se.png'; +import re from '#resources/img/re.png'; import styles from './styles.scss'; +const services = [ + { + id: 1, + title: 'Consultancy', + description: 'We provide consultancy services regarding software architecture design, database design, programming practices and various technologies.', + image: cs, + }, + { + id: 2, + title: 'Data Science', + description: 'We specialize in data analysis and visualization using statistics and machine learning technologies.', + image: ds, + }, + { + id: 3, + title: 'Software Engineering', + description: 'We design and develop systems of various complexities that run on web, mobile and desktop platforms.', + image: se, + }, + { + id: 4, + title: 'Research & Data Analysis', + description: 'We carryout humanitarian driven secondary data review with a focus on qualitative data research, report writing and data analysis.', + image: re, + }, +]; + const propTypes = { className: PropTypes.string, - services: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types }; -const mapStateToProps = (state, props) => ({ - services: servicesSelector(state, props), -}); - const defaultProps = { className: '', }; -const keyExtractor = member => member.id; -const rendererParams = (key, service) => ({ service }); - const Service = ({ service }) => (
    {service.title} @@ -40,16 +59,12 @@ Service.propTypes = { }; -@connect(mapStateToProps) export default class Services extends React.PureComponent { static propTypes = propTypes; static defaultProps = defaultProps; render() { - const { - className, - services, - } = this.props; + const { className } = this.props; return (
    What we do - {services.length > 0 && ( - - )} +
    + {services.map(service => ( + + ))} +
    ); } diff --git a/client/src/views/Home/Team/Member/index.js b/client/src/views/Home/Team/Member/index.js index 2d44041..c88b4a6 100644 --- a/client/src/views/Home/Team/Member/index.js +++ b/client/src/views/Home/Team/Member/index.js @@ -1,17 +1,12 @@ import React from 'react'; import PropTypes from 'prop-types'; -import ListView from '#rscv/List/ListView'; - import styles from './styles.scss'; const propTypes = { member: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types }; -const keyExtractor = member => member.id; -const rendererParams = (key, memberUrl) => ({ memberUrl }); - const MemberUrl = ({ memberUrl }) => (
    {member.name}
    {member.designation}
    - + {member.membersUrls.length > 0 && ( +
    + {member.membersUrls.map(memberUrl => ( + + ))} +
    + )}
    ); diff --git a/client/src/views/Home/Team/Member/styles.scss b/client/src/views/Home/Team/Member/styles.scss index 2fc8a07..d6c57f1 100644 --- a/client/src/views/Home/Team/Member/styles.scss +++ b/client/src/views/Home/Team/Member/styles.scss @@ -41,8 +41,8 @@ overflow: hidden; .image { - border-radius: 0; transition: 10s transform ease-in-out; + border-radius: 0; width: 160px; height: 100%; object-fit: cover; diff --git a/client/src/views/Home/Team/index.js b/client/src/views/Home/Team/index.js index 0861596..a47afe6 100644 --- a/client/src/views/Home/Team/index.js +++ b/client/src/views/Home/Team/index.js @@ -1,11 +1,6 @@ import React from 'react'; -import { connect } from 'react-redux'; import PropTypes from 'prop-types'; -import ListView from '#rscv/List/ListView'; - -import { membersSelector } from '#redux'; - import Member from './Member'; import styles from './styles.scss'; @@ -15,19 +10,10 @@ const propTypes = { members: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types }; -const mapStateToProps = (state, props) => ({ - members: membersSelector(state, props), -}); - const defaultProps = { className: '', }; -const keyExtractor = member => member.id; -const rendererParams = (key, member) => ({ member }); - - -@connect(mapStateToProps) export default class Team extends React.PureComponent { static propTypes = propTypes; static defaultProps = defaultProps; @@ -35,7 +21,7 @@ export default class Team extends React.PureComponent { render() { const { className, - members, + members = [], } = this.props; return ( @@ -46,15 +32,14 @@ export default class Team extends React.PureComponent {

    Our team

    - {members.length > 0 && ( - - )} +
    + {members.map(member => ( + + ))} +
    ); } diff --git a/client/src/views/Home/index.js b/client/src/views/Home/index.js index c304d5a..c1d1cb6 100644 --- a/client/src/views/Home/index.js +++ b/client/src/views/Home/index.js @@ -1,27 +1,21 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; +import { Link } from 'react-router-dom'; import AnchorLink from '#components/AnchorLink'; -import LoadingAnimation from '#rscv/LoadingAnimation'; - import { - setMembersAction, - setClientsAction, - setServicesAction, - setTechnologySectionsAction, -} from '#redux'; + RequestCoordinator, + RequestClient, + methods, +} from '#utils/Request'; + +import tcLogo from '#resources/img/tc.png'; import Services from './Services'; import Expertise from './Expertise'; import Clients from './Clients'; import Team from './Team'; -import MembersGetRequest from './requests/MembersGetRequest'; -import ClientsGetRequest from './requests/ClientsGetRequest'; -import ServicesGetRequest from './requests/ServicesGetRequest'; -import TechnologySectionsGetRequest from './requests/TechnologySectionsGetRequest'; - import styles from './styles.scss'; const linkList = [ @@ -58,67 +52,27 @@ const linkList = [ ]; const propTypes = { - setMembers: PropTypes.func.isRequired, - setClients: PropTypes.func.isRequired, - setServices: PropTypes.func.isRequired, - setTechnologySections: PropTypes.func.isRequired, + // eslint-disable-next-line react/forbid-prop-types + requests: PropTypes.object.isRequired, }; -const mapDispatchToProps = dispatch => ({ - setMembers: params => dispatch(setMembersAction(params)), - setClients: params => dispatch(setClientsAction(params)), - setServices: params => dispatch(setServicesAction(params)), - setTechnologySections: params => dispatch(setTechnologySectionsAction(params)), -}); +const requestOptions = { + membersGet: { + url: '/members/', + method: methods.GET, + onMount: true, + extras: { + schemaName: 'array.member', + }, + }, +}; -@connect(undefined, mapDispatchToProps) +@RequestCoordinator +@RequestClient(requestOptions) export default class Home extends React.PureComponent { static propTypes = propTypes; - constructor(props) { - super(props); - - this.state = { - serviceLoading: true, - clientLoading: true, - technologySectionLoading: true, - memberLoading: true, - }; - - // Requests - this.membersGetRequest = new MembersGetRequest({ - setState: v => this.setState(v), - setMembers: this.props.setMembers, - }); - this.clientsGetRequest = new ClientsGetRequest({ - setState: v => this.setState(v), - setClients: this.props.setClients, - }); - this.servicesGetRequest = new ServicesGetRequest({ - setState: v => this.setState(v), - setServices: this.props.setServices, - }); - this.technologySectionsGetRequest = new TechnologySectionsGetRequest({ - setState: v => this.setState(v), - setTechnologySections: this.props.setTechnologySections, - }); - } - - componentDidMount() { - this.membersGetRequest.init().start(); - this.clientsGetRequest.init().start(); - this.servicesGetRequest.init().start(); - this.technologySectionsGetRequest.init().start(); - } - - componentWillUnmount() { - this.membersGetRequest.stop(); - this.clientsGetRequest.stop(); - this.servicesGetRequest.stop(); - this.technologySectionsGetRequest.stop(); - } - handleDownButtonClick = (section) => { const servicesContainer = document.getElementsByClassName(styles[section])[0]; @@ -141,9 +95,11 @@ export default class Home extends React.PureComponent {

    Hi, we are

    -

    - Togglecorp -

    + togglecorp

    We build tech for your idea.

    @@ -161,6 +117,13 @@ export default class Home extends React.PureComponent { ))} +
  • + + Career + +
  • @@ -216,16 +179,12 @@ export default class Home extends React.PureComponent { render() { const { - serviceLoading, - clientLoading, - technologySectionLoading, - memberLoading, - } = this.state; - - const loading = serviceLoading || - clientLoading || - technologySectionLoading || - memberLoading; + requests: { + membersGet: { + response: members = [], + } = {}, + } = {}, + } = this.props; return (
    @@ -241,6 +200,7 @@ export default class Home extends React.PureComponent { /> {this.renderContact()}
    diff --git a/client/src/views/Home/requests/ClientsGetRequest.js b/client/src/views/Home/requests/ClientsGetRequest.js deleted file mode 100644 index 407e46f..0000000 --- a/client/src/views/Home/requests/ClientsGetRequest.js +++ /dev/null @@ -1,32 +0,0 @@ -import { - urlForClients, - createParamsForGet, -} from '#rest'; - -import Request from '#utils/Request'; - -export default class ClientsGetRequest extends Request { - schemaName = 'array.client' - - handlePreLoad = () => { - this.parent.setState({ clientLoading: true }); - } - - handlePostLoad = () => { - this.parent.setState({ clientLoading: false }); - } - - handleSuccess = (response) => { - this.parent.setClients(response); - } - - // TODO: Handle Fatal and Failure - - init = () => { - this.createDefault({ - url: urlForClients, - params: createParamsForGet, - }); - return this; - } -} diff --git a/client/src/views/Home/requests/MembersGetRequest.js b/client/src/views/Home/requests/MembersGetRequest.js deleted file mode 100644 index a98d8d7..0000000 --- a/client/src/views/Home/requests/MembersGetRequest.js +++ /dev/null @@ -1,32 +0,0 @@ -import { - urlForMembers, - createParamsForGet, -} from '#rest'; - -import Request from '#utils/Request'; - -export default class MembersGetRequest extends Request { - schemaName = 'array.member' - - handlePreLoad = () => { - this.parent.setState({ memberLoading: true }); - } - - handlePostLoad = () => { - this.parent.setState({ memberLoading: false }); - } - - handleSuccess = (response) => { - this.parent.setMembers(response); - } - - // TODO: Handle Fatal and Failure - - init = () => { - this.createDefault({ - url: urlForMembers, - params: createParamsForGet, - }); - return this; - } -} diff --git a/client/src/views/Home/requests/ServicesGetRequest.js b/client/src/views/Home/requests/ServicesGetRequest.js deleted file mode 100644 index c89f942..0000000 --- a/client/src/views/Home/requests/ServicesGetRequest.js +++ /dev/null @@ -1,32 +0,0 @@ -import { - urlForServices, - createParamsForGet, -} from '#rest'; - -import Request from '#utils/Request'; - -export default class ServicesGetRequest extends Request { - schemaName = 'array.service' - - handlePreLoad = () => { - this.parent.setState({ serviceLoading: true }); - } - - handlePostLoad = () => { - this.parent.setState({ serviceLoading: false }); - } - - handleSuccess = (response) => { - this.parent.setServices(response); - } - - // TODO: Handle Fatal and Failure - - init = () => { - this.createDefault({ - url: urlForServices, - params: createParamsForGet, - }); - return this; - } -} diff --git a/client/src/views/Home/requests/TechnologySectionsGetRequest.js b/client/src/views/Home/requests/TechnologySectionsGetRequest.js deleted file mode 100644 index a0140e7..0000000 --- a/client/src/views/Home/requests/TechnologySectionsGetRequest.js +++ /dev/null @@ -1,32 +0,0 @@ -import { - urlForTechnologySection, - createParamsForGet, -} from '#rest'; - -import Request from '#utils/Request'; - -export default class TechnologySectionsGetRequest extends Request { - schemaName = 'array.technologySection' - - handlePreLoad = () => { - this.parent.setState({ technologySectionLoading: true }); - } - - handlePostLoad = () => { - this.parent.setState({ technologySectionLoading: false }); - } - - handleSuccess = (response) => { - this.parent.setTechnologySections(response); - } - - // TODO: Handle Fatal and Failure - - init = () => { - this.createDefault({ - url: urlForTechnologySection, - params: createParamsForGet, - }); - return this; - } -} diff --git a/client/src/views/Home/styles.scss b/client/src/views/Home/styles.scss index dcd3f28..324cc29 100644 --- a/client/src/views/Home/styles.scss +++ b/client/src/views/Home/styles.scss @@ -7,6 +7,22 @@ width: 100%; overflow: hidden; + a { + outline: none; + text-transform: uppercase; + text-decoration: none; + color: inherit; + } + + h1, + h2, + h3, + h4, + h5 { + text-transform: uppercase; + font-weight: $font-weight-light; + } + nav { ul { display: flex; @@ -27,11 +43,11 @@ flex-direction: row; flex-grow: 1; z-index: 10; - background-color: #ffffff; + background-color: #f9ece5; padding: 48px 0; &:nth-child(even) { - background-color: #f5f5f5; + background-color: #fff; } &.header { @@ -53,6 +69,11 @@ justify-content: center; margin-top: -10vh; + .logo { + width: 420px; + height: auto; + } + .pre-message { margin: 0; font-size: 3vh; @@ -65,18 +86,6 @@ font-size: 4vh; font-weight: $font-weight-light; } - - h1 { - margin: 0; - padding: 0; - text-transform: lowercase; - line-height: 1; - font-size: 10vh; - - span:last-child { - color: $color-accent; - } - } } } @@ -188,8 +197,8 @@ font-size: 32px; } - h1 { - font-size: 64px; + .logo { + width: 80vw; } } } diff --git a/client/yarn.lock b/client/yarn.lock index 59b6d1a..b62d1d3 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -838,6 +838,13 @@ dependencies: regenerator-runtime "^0.12.0" +"@babel/runtime@^7.7.7": + version "7.9.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.6.tgz#a9102eb5cadedf3f31d08a9ecf294af7827ea29f" + integrity sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@7.0.0-beta.44": version "7.0.0-beta.44" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0-beta.44.tgz#f8832f4fdcee5d59bf515e595fc5106c529b394f" @@ -1044,10 +1051,25 @@ version "0.7.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" +"@togglecorp/fujs@^1.8.0", "@togglecorp/fujs@^1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@togglecorp/fujs/-/fujs-1.9.0.tgz#4eaa76021a150c651c580562ff8cf09aed89b558" + integrity sha512-4DmIMMK+W/rxzAm+IQvdo2MGKQsj/8ViaT9K6UIpkHdullu5EINZuDkttLMvopeV6YyDj1YseOz8BLw/3Nte2A== + dependencies: + "@babel/runtime" "^7.7.7" + "@togglecorp/ravl@^1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/@togglecorp/ravl/-/ravl-1.0.2.tgz#d1c6536369d0393c4db899bd9ec804c20d3ca7aa" +"@togglecorp/react-rest-request@^2.4.5": + version "2.4.5" + resolved "https://registry.yarnpkg.com/@togglecorp/react-rest-request/-/react-rest-request-2.4.5.tgz#ace182d53a259cf539b9db5b4d3256f252f66bcb" + integrity sha512-/VA+itpWYesjN/0YNJhGV1c2EZc5SyiaWtJOzamTouPat+B7usKU1cjFWS9qHbEP75AEY1tzzfaxXOcam+Hgcg== + dependencies: + "@togglecorp/fujs" "^1.8.0" + hoist-non-react-statics "^3.3.0" + "@types/events@*": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" @@ -1384,6 +1406,14 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" +anymatch@~3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + append-transform@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-1.0.0.tgz#046a52ae582a228bd72f58acfbe2967c678759ab" @@ -1492,6 +1522,7 @@ arrify@^1.0.0, arrify@^1.0.1: asap@~2.0.3: version "2.0.6" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= asn1.js@^4.0.0: version "4.10.1" @@ -1541,10 +1572,6 @@ async-each@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" -async-foreach@^0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542" - async-limiter@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" @@ -2382,16 +2409,15 @@ binary-extensions@^1.0.0: version "1.11.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205" +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + binaryextensions@2: version "2.1.1" resolved "https://registry.yarnpkg.com/binaryextensions/-/binaryextensions-2.1.1.tgz#3209a51ca4a4ad541a3b8d3d6a6d5b83a2485935" -block-stream@*: - version "0.0.9" - resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" - dependencies: - inherits "~2.0.0" - bluebird@^3.5.1: version "3.5.1" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" @@ -2479,7 +2505,7 @@ braces@^2.3.0, braces@^2.3.1: split-string "^3.0.2" to-regex "^3.0.1" -braces@^3.0.1: +braces@^3.0.1, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== @@ -2755,10 +2781,6 @@ camelcase@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" -camelcase@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" - camelcase@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" @@ -2883,6 +2905,21 @@ cheerio@^1.0.0-rc.2: lodash "^4.15.0" parse5 "^3.0.1" +"chokidar@>=3.0.0 <4.0.0": + version "3.5.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" + integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + chokidar@^2.0.0, chokidar@^2.0.2: version "2.0.4" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.4.tgz#356ff4e2b0e8e43e322d18a372460bbcf3accd26" @@ -2999,14 +3036,6 @@ cliui@^2.1.0: right-align "^0.1.1" wordwrap "0.0.2" -cliui@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - wrap-ansi "^2.0.0" - cliui@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" @@ -3266,6 +3295,7 @@ copy-descriptor@^0.1.0: core-js@^1.0.0: version "1.2.7" resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" + integrity sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY= core-js@^2.4.0, core-js@^2.4.1, core-js@^2.5.0: version "2.5.7" @@ -3322,13 +3352,6 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" -cross-spawn@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982" - dependencies: - lru-cache "^4.0.1" - which "^1.2.9" - cross-spawn@^5.0.1, cross-spawn@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" @@ -3917,6 +3940,14 @@ dom-serializer@0, dom-serializer@~0.1.0: domelementtype "~1.1.1" entities "~1.1.1" +dom-serializer@^0.2.1: + version "0.2.2" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" + integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== + dependencies: + domelementtype "^2.0.1" + entities "^2.0.0" + dom-walk@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018" @@ -3934,6 +3965,11 @@ domelementtype@^1.3.1: resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== +domelementtype@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.0.1.tgz#1f8bdfe91f5a78063274e803b4bdcedf6e94f94d" + integrity sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ== + domelementtype@~1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" @@ -3956,6 +3992,13 @@ domhandler@^2.3.0: dependencies: domelementtype "1" +domhandler@^3.0, domhandler@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-3.0.0.tgz#51cd13efca31da95bbb0c5bee3a48300e333b3e9" + integrity sha512-eKLdI5v9m67kbXQbJSNn1zjh0SDzvzWVWtX+qEI3eMjZw8daH9k8rlj1FZY9memPwjiskQFbe7vHVVJIAqoEhw== + dependencies: + domelementtype "^2.0.1" + domutils@1.1: version "1.1.6" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.1.6.tgz#bddc3de099b9a2efacc51c623f28f416ecc57485" @@ -3976,6 +4019,15 @@ domutils@^1.5.1: dom-serializer "0" domelementtype "1" +domutils@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.1.0.tgz#7ade3201af43703fde154952e3a868eb4b635f16" + integrity sha512-CD9M0Dm1iaHfQ1R/TI+z3/JWp/pgub0j4jIQKH89ARR4ATAV2nbaOQS5XxU9maJP5jHaPdDDQSEHuE2UmpUTKg== + dependencies: + dom-serializer "^0.2.1" + domelementtype "^2.0.1" + domhandler "^3.0.0" + dot-prop@^4.1.1: version "4.2.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57" @@ -4074,6 +4126,7 @@ encodeurl@~1.0.2: encoding@^0.1.11: version "0.1.12" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" + integrity sha1-U4tm8+5izRq1HsMjgp0flIDHS+s= dependencies: iconv-lite "~0.4.13" @@ -4095,6 +4148,11 @@ entities@^1.1.1, entities@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" +entities@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.2.tgz#ac74db0bba8d33808bbf36809c3a5c3683531436" + integrity sha512-dmD3AvJQBUjKpcNkoqr+x+IF0SdRtPz9Vk0uTy4yWqga9ibB6s4v++QFWNohjiUGoMlF552ZvNyXDxz5iW0qmw== + envinfo@^5.7.0: version "5.10.0" resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-5.10.0.tgz#503a9774ae15b93ea68bdfae2ccd6306624ea6df" @@ -4721,6 +4779,7 @@ fb-watchman@^2.0.0: fbjs@^0.8.16: version "0.8.17" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd" + integrity sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90= dependencies: core-js "^1.0.0" isomorphic-fetch "^2.1.1" @@ -4996,14 +5055,10 @@ fsevents@^1.2.2, fsevents@^1.2.3: nan "^2.9.2" node-pre-gyp "^0.10.0" -fstream@^1.0.0, fstream@^1.0.2: - version "1.0.11" - resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" - dependencies: - graceful-fs "^4.1.2" - inherits "~2.0.0" - mkdirp ">=0.5 0" - rimraf "2" +fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== function-bind@^1.1.0, function-bind@^1.1.1: version "1.1.1" @@ -5034,12 +5089,6 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" -gaze@^1.0.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.3.tgz#c441733e13b927ac8c0ff0b4c3b033f28812924a" - dependencies: - globule "^1.0.0" - geojson-rewind@^0.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/geojson-rewind/-/geojson-rewind-0.3.1.tgz#22240797c847cc2f0c1d313e4aa0c915afa7f29d" @@ -5124,11 +5173,18 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + glob-to-regexp@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" -glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@~7.1.1: +glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2: version "7.1.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" dependencies: @@ -5262,14 +5318,6 @@ globjoin@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/globjoin/-/globjoin-0.1.4.tgz#2f4494ac8919e3767c5cbb691e9f463324285d43" -globule@^1.0.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/globule/-/globule-1.2.1.tgz#5dffb1b191f22d20797a9369b49eab4e9839696d" - dependencies: - glob "~7.1.1" - lodash "~4.17.10" - minimatch "~3.0.2" - gonzales-pe@^4.0.3: version "4.2.3" resolved "https://registry.yarnpkg.com/gonzales-pe/-/gonzales-pe-4.2.3.tgz#41091703625433285e0aee3aa47829fc1fbeb6f2" @@ -5489,6 +5537,13 @@ hoist-non-react-statics@^2.5.0: version "2.5.5" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47" +hoist-non-react-statics@^3.3.0: + version "3.3.2" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== + dependencies: + react-is "^16.7.0" + home-or-tmp@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" @@ -5550,6 +5605,16 @@ html-tags@^3.0.0: resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.1.0.tgz#7b5e6f7e665e9fb41f30007ed9e0d41e97fb2140" integrity sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg== +html-to-react@^1.3.4: + version "1.4.2" + resolved "https://registry.yarnpkg.com/html-to-react/-/html-to-react-1.4.2.tgz#7b628ab56cd63a52f2d0b79d0fa838a51f088a57" + integrity sha512-TdTfxd95sRCo6QL8admCkE7mvNNrXtGoVr1dyS+7uvc8XCqAymnf/6ckclvnVbQNUo2Nh21VPwtfEHd0khiV7g== + dependencies: + domhandler "^3.0" + htmlparser2 "^4.0" + lodash.camelcase "^4.3.0" + ramda "^0.26" + html-webpack-plugin@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz#b01abbd723acaaa7b37b6af4492ebda03d9dd37b" @@ -5585,6 +5650,16 @@ htmlparser2@^3.9.1, htmlparser2@^3.9.2: inherits "^2.0.1" readable-stream "^2.0.2" +htmlparser2@^4.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-4.1.0.tgz#9a4ef161f2e4625ebf7dfbe6c0a2f52d18a59e78" + integrity sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q== + dependencies: + domelementtype "^2.0.1" + domhandler "^3.0.0" + domutils "^2.0.0" + entities "^2.0.0" + htmlparser2@~3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.3.0.tgz#cc70d05a59f6542e43f0e685c982e14c924a9efe" @@ -5666,6 +5741,7 @@ iconv-lite@0.4.23: iconv-lite@^0.4.17, iconv-lite@^0.4.22, iconv-lite@^0.4.4, iconv-lite@~0.4.13: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" @@ -5723,6 +5799,11 @@ immutability-helper@^2.7.0: dependencies: invariant "^2.2.0" +immutable@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.0.0.tgz#b86f78de6adef3608395efb269a91462797e2c23" + integrity sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw== + import-fresh@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" @@ -5747,10 +5828,6 @@ imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" -in-publish@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/in-publish/-/in-publish-2.0.0.tgz#e20ff5e3a2afc2690320b6dc552682a9c7fadf51" - indent-string@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" @@ -5776,7 +5853,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" @@ -5860,7 +5937,7 @@ into-stream@^3.1.0: from2 "^2.1.1" p-is-promise "^1.1.0" -invariant@^2.0.0, invariant@^2.2.0, invariant@^2.2.1, invariant@^2.2.2, invariant@^2.2.4: +invariant@^2.2.0, invariant@^2.2.1, invariant@^2.2.2, invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" dependencies: @@ -5923,6 +6000,13 @@ is-binary-path@^1.0.0: dependencies: binary-extensions "^1.0.0" +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + is-boolean-object@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.0.tgz#98f8b28030684219a95f375cfbd88ce3405dff93" @@ -6067,6 +6151,13 @@ is-glob@^4.0.0: dependencies: is-extglob "^2.1.1" +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + is-hexadecimal@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.2.tgz#b6e710d7d07bb66b98cb8cece5c9b4921deeb835" @@ -6180,6 +6271,7 @@ is-scoped@^1.0.0: is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= is-string@^1.0.4: version "1.0.4" @@ -6258,6 +6350,7 @@ isobject@^3.0.0, isobject@^3.0.1: isomorphic-fetch@^2.1.1: version "2.2.1" resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" + integrity sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk= dependencies: node-fetch "^1.0.1" whatwg-fetch ">=0.10.0" @@ -6645,7 +6738,7 @@ jpeg-js@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.2.0.tgz#53e448ec9d263e683266467e9442d2c5a2ef5482" -js-base64@^2.1.8, js-base64@^2.1.9: +js-base64@^2.1.9: version "2.4.9" resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.9.tgz#748911fb04f48a60c4771b375cac45a80df11c03" @@ -6660,6 +6753,7 @@ js-tokens@^3.0.0, js-tokens@^3.0.2: "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@^3.10.0, js-yaml@^3.7.0, js-yaml@^3.9.0, js-yaml@^3.9.1: version "3.12.0" @@ -7070,10 +7164,6 @@ locate-path@^3.0.0: p-locate "^3.0.0" path-exists "^3.0.0" -lodash-es@^4.17.5: - version "4.17.10" - resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.10.tgz#62cd7104cdf5dd87f235a837f0ede0e8e5117e05" - lodash.camelcase@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" @@ -7094,10 +7184,6 @@ lodash.isequal@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" -lodash.isplainobject@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" - lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" @@ -7114,7 +7200,7 @@ lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" -lodash@^4.0.0, lodash@^4.0.1, lodash@^4.12.0, lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.3.0, lodash@~4.17.10: +lodash@^4.0.1, lodash@^4.12.0, lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.3.0: version "4.17.10" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" @@ -7169,9 +7255,10 @@ longest@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1: +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== dependencies: js-tokens "^3.0.0 || ^4.0.0" @@ -7308,6 +7395,13 @@ md5.js@^1.3.4: hash-base "^3.0.0" inherits "^2.0.1" +mdast-add-list-metadata@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mdast-add-list-metadata/-/mdast-add-list-metadata-1.0.1.tgz#95e73640ce2fc1fa2dcb7ec443d09e2bfe7db4cf" + integrity sha512-fB/VP4MJ0LaRsog7hGPxgOrSL3gE/2uEdZyDuSEnKCv/8IkYHiDkIQSbChiJoHyxZZXZ9bzckyRk+vNxFzh8rA== + dependencies: + unist-util-visit-parents "1.1.2" + mdast-util-compact@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/mdast-util-compact/-/mdast-util-compact-1.0.2.tgz#c12ebe16fffc84573d3e19767726de226e95f649" @@ -7359,7 +7453,7 @@ memory-fs@^0.4.0, memory-fs@~0.4.1: errno "^0.1.3" readable-stream "^2.0.1" -meow@^3.3.0, meow@^3.7.0: +meow@^3.3.0: version "3.7.0" resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" dependencies: @@ -7538,7 +7632,7 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" -minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4, minimatch@~3.0.2: +minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" dependencies: @@ -7610,7 +7704,7 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@0.5.1, mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: +mkdirp@0.5.1, mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" dependencies: @@ -7664,11 +7758,6 @@ mute-stream@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" -nan@^2.13.2: - version "2.14.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" - integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== - nan@^2.9.2: version "2.11.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.11.0.tgz#574e360e4d954ab16966ec102c0c049fd961a099" @@ -7740,6 +7829,7 @@ node-dir@0.1.8: node-fetch@^1.0.1: version "1.7.3" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" + integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ== dependencies: encoding "^0.1.11" is-stream "^1.0.1" @@ -7748,24 +7838,6 @@ node-forge@0.7.5: version "0.7.5" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.5.tgz#6c152c345ce11c52f465c2abd957e8639cd674df" -node-gyp@^3.8.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.8.0.tgz#540304261c330e80d0d5edce253a68cb3964218c" - integrity sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA== - dependencies: - fstream "^1.0.0" - glob "^7.0.3" - graceful-fs "^4.1.2" - mkdirp "^0.5.0" - nopt "2 || 3" - npmlog "0 || 1 || 2 || 3 || 4" - osenv "0" - request "^2.87.0" - rimraf "2" - semver "~5.3.0" - tar "^2.0.0" - which "1" - node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -7829,29 +7901,6 @@ node-releases@^1.1.38: dependencies: semver "^6.3.0" -node-sass@4.12.0: - version "4.12.0" - resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.12.0.tgz#0914f531932380114a30cc5fa4fa63233a25f017" - integrity sha512-A1Iv4oN+Iel6EPv77/HddXErL2a+gZ4uBeZUy+a8O35CFYTXhgA8MgLCWBtwpGZdCvTvQ9d+bQxX/QC36GDPpQ== - dependencies: - async-foreach "^0.1.3" - chalk "^1.1.1" - cross-spawn "^3.0.0" - gaze "^1.0.0" - get-stdin "^4.0.1" - glob "^7.0.3" - in-publish "^2.0.0" - lodash "^4.17.11" - meow "^3.7.0" - mkdirp "^0.5.1" - nan "^2.13.2" - node-gyp "^3.8.0" - npmlog "^4.0.0" - request "^2.88.0" - sass-graph "^2.2.4" - stdout-stream "^1.4.0" - "true-case-path" "^1.0.2" - nomnom@^1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/nomnom/-/nomnom-1.8.1.tgz#2151f722472ba79e50a76fc125bb8c8f2e4dc2a7" @@ -7866,12 +7915,6 @@ nomnom@~1.6.2: colors "0.5.x" underscore "~1.4.4" -"nopt@2 || 3": - version "3.0.6" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" - dependencies: - abbrev "1" - nopt@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" @@ -7894,6 +7937,11 @@ normalize-path@^2.0.1, normalize-path@^2.1.1: dependencies: remove-trailing-separator "^1.0.1" +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + normalize-range@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" @@ -7936,7 +7984,7 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" -"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.0.2: +npmlog@^4.0.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" dependencies: @@ -7970,6 +8018,7 @@ oauth-sign@~0.9.0: object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= object-copy@^0.1.0: version "0.1.0" @@ -8137,12 +8186,6 @@ os-homedir@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" -os-locale@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" - dependencies: - lcid "^1.0.0" - os-locale@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2" @@ -8155,7 +8198,7 @@ os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" -osenv@0, osenv@^0.1.4: +osenv@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" dependencies: @@ -8436,6 +8479,11 @@ performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.3.0" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" + integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== + picomatch@^2.0.5: version "2.1.0" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.1.0.tgz#0fd042f568d08b1ad9ff2d3ec0f0bfb3cb80e177" @@ -8996,16 +9044,26 @@ promise-inflight@^1.0.1: promise@^7.1.1: version "7.3.1" resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" + integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== dependencies: asap "~2.0.3" -prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.7, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2: +prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.7, prop-types@^15.6.1, prop-types@^15.6.2: version "15.6.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102" dependencies: loose-envify "^1.3.1" object-assign "^4.1.1" +prop-types@^15.6.0, prop-types@^15.7.2: + version "15.7.2" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" + integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.8.1" + protocol-buffers-schema@^3.3.1: version "3.3.2" resolved "https://registry.yarnpkg.com/protocol-buffers-schema/-/protocol-buffers-schema-3.3.2.tgz#00434f608b4e8df54c59e070efeefc37fb4bb859" @@ -9131,6 +9189,11 @@ railroad-diagrams@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz#eb7e6267548ddedfb899c1b90e57374559cddb7e" +ramda@^0.26: + version "0.26.1" + resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06" + integrity sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ== + randexp@0.4.6: version "0.4.6" resolved "https://registry.yarnpkg.com/randexp/-/randexp-0.4.6.tgz#e986ad5e5e31dae13ddd6f7b3019aa7c87f60ca3" @@ -9209,13 +9272,14 @@ react-color@^2.14.1: tinycolor2 "^1.4.1" react-dom@^16.3.2: - version "16.4.2" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.4.2.tgz#4afed569689f2c561d2b8da0b819669c38a0bda4" + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.13.1.tgz#c1bd37331a0486c078ee54c4740720993b2e0e7f" + integrity sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag== dependencies: - fbjs "^0.8.16" loose-envify "^1.1.0" object-assign "^4.1.1" - prop-types "^15.6.0" + prop-types "^15.6.2" + scheduler "^0.19.1" react-focus-trap@^2.5.0: version "2.6.0" @@ -9243,16 +9307,24 @@ react-is@^16.4.2: version "16.4.2" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.4.2.tgz#84891b56c2b6d9efdee577cc83501dfc5ecead88" -react-redux@^5.0.7: - version "5.0.7" - resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.7.tgz#0dc1076d9afb4670f993ffaef44b8f8c1155a4c8" - dependencies: - hoist-non-react-statics "^2.5.0" - invariant "^2.0.0" - lodash "^4.17.5" - lodash-es "^4.17.5" - loose-envify "^1.1.0" - prop-types "^15.6.0" +react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.6: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +react-markdown@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-4.3.1.tgz#39f0633b94a027445b86c9811142d05381300f2f" + integrity sha512-HQlWFTbDxTtNY6bjgp3C3uv1h2xcjCSi1zAEzfBW9OwJJvENSYiLXWNXN5hHLsoqai7RnZiiHzcnWdXk2Splzw== + dependencies: + html-to-react "^1.3.4" + mdast-add-list-metadata "1.0.1" + prop-types "^15.7.2" + react-is "^16.8.6" + remark-parse "^5.0.0" + unified "^6.1.5" + unist-util-visit "^1.3.0" + xtend "^4.0.1" react-resize-detector@^2.3.0: version "2.3.0" @@ -9317,7 +9389,7 @@ react-test-renderer@^16.0.0-0, react-test-renderer@^16.3.2: prop-types "^15.6.0" react-is "^16.4.2" -"react@^15.6.2 || ^16.0", react@^16.3.2: +"react@^15.6.2 || ^16.0": version "16.4.2" resolved "https://registry.yarnpkg.com/react/-/react-16.4.2.tgz#2cd90154e3a9d9dd8da2991149fdca3c260e129f" dependencies: @@ -9326,6 +9398,15 @@ react-test-renderer@^16.0.0-0, react-test-renderer@^16.3.2: object-assign "^4.1.1" prop-types "^15.6.0" +react@^16.3.2: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e" + integrity sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2" + reactcss@^1.2.0: version "1.2.3" resolved "https://registry.yarnpkg.com/reactcss/-/reactcss-1.2.3.tgz#c00013875e557b1cf0dfd9a368a1c3dab3b548dd" @@ -9436,6 +9517,13 @@ readdirp@^2.0.0: readable-stream "^2.0.2" set-immediate-shim "^1.0.1" +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + realpath-native@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.0.1.tgz#07f40a0cce8f8261e2e8b7ebebf5c95965d7b633" @@ -9501,27 +9589,6 @@ reduce-function-call@^1.0.1: dependencies: balanced-match "^0.4.2" -redux-mock-store@^1.5.1: - version "1.5.3" - resolved "https://registry.yarnpkg.com/redux-mock-store/-/redux-mock-store-1.5.3.tgz#1f10528949b7ce8056c2532624f7cafa98576c6d" - dependencies: - lodash.isplainobject "^4.0.6" - -redux-persist@^5.9.1: - version "5.10.0" - resolved "https://registry.yarnpkg.com/redux-persist/-/redux-persist-5.10.0.tgz#5d8d802c5571e55924efc1c3a9b23575283be62b" - -redux-thunk@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622" - -redux@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.0.tgz#aa698a92b729315d22b34a0553d7e6533555cc03" - dependencies: - loose-envify "^1.1.0" - symbol-observable "^1.2.0" - regenerate-unicode-properties@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-7.0.0.tgz#107405afcc4a190ec5ed450ecaa00ed0cafa7a4c" @@ -9540,6 +9607,11 @@ regenerator-runtime@^0.12.0: version "0.12.1" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de" +regenerator-runtime@^0.13.4: + version "0.13.5" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697" + integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA== + regenerator-transform@^0.10.0: version "0.10.1" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd" @@ -9642,6 +9714,27 @@ remark-parse@^4.0.0: vfile-location "^2.0.0" xtend "^4.0.1" +remark-parse@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-5.0.0.tgz#4c077f9e499044d1d5c13f80d7a98cf7b9285d95" + integrity sha512-b3iXszZLH1TLoyUzrATcTQUZrwNl1rE70rVdSruJFlDaJ9z5aMkhrG43Pp68OgfHndL/ADz6V69Zow8cTQu+JA== + dependencies: + collapse-white-space "^1.0.2" + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" + is-whitespace-character "^1.0.0" + is-word-character "^1.0.0" + markdown-escapes "^1.0.0" + parse-entities "^1.1.0" + repeat-string "^1.5.4" + state-toggle "^1.0.0" + trim "0.0.1" + trim-trailing-lines "^1.0.0" + unherit "^1.0.4" + unist-util-remove-position "^1.0.0" + vfile-location "^2.0.0" + xtend "^4.0.1" + remark-parse@^6.0.0: version "6.0.3" resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-6.0.3.tgz#c99131052809da482108413f87b0ee7f52180a3a" @@ -9769,7 +9862,7 @@ request-promise-native@^1.0.5: stealthy-require "^1.1.0" tough-cookie ">=2.3.3" -request@^2.65.0, request@^2.87.0, request@^2.88.0: +request@^2.65.0, request@^2.87.0: version "2.88.0" resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" dependencies: @@ -9909,12 +10002,6 @@ right-align@^0.1.1: dependencies: align-text "^0.1.1" -rimraf@2, rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" - dependencies: - glob "^7.0.5" - rimraf@2.6.3: version "2.6.3" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" @@ -9922,6 +10009,12 @@ rimraf@2.6.3: dependencies: glob "^7.1.3" +rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" + dependencies: + glob "^7.0.5" + rimraf@~2.2.6: version "2.2.8" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" @@ -10004,6 +10097,7 @@ safe-regex@^1.1.0: "safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== sane@^2.0.0: version "2.5.2" @@ -10020,15 +10114,6 @@ sane@^2.0.0: optionalDependencies: fsevents "^1.2.3" -sass-graph@^2.2.4: - version "2.2.4" - resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.4.tgz#13fbd63cd1caf0908b9fd93476ad43a51d1e0b49" - dependencies: - glob "^7.0.0" - lodash "^4.0.0" - scss-tokenizer "^0.2.3" - yargs "^7.0.0" - sass-loader@^7.3.1: version "7.3.1" resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-7.3.1.tgz#a5bf68a04bcea1c13ff842d747150f7ab7d0d23f" @@ -10040,10 +10125,27 @@ sass-loader@^7.3.1: pify "^4.0.1" semver "^6.3.0" +sass@^1.45.1: + version "1.45.1" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.45.1.tgz#fa03951f924d1ba5762949567eaf660e608a1ab0" + integrity sha512-pwPRiq29UR0o4X3fiQyCtrESldXvUQAAE0QmcJTpsI4kuHHcLzZ54M1oNBVIXybQv8QF2zfkpFcTxp8ta97dUA== + dependencies: + chokidar ">=3.0.0 <4.0.0" + immutable "^4.0.0" + source-map-js ">=0.6.2 <2.0.0" + sax@>=0.6.0, sax@^1.2.4, sax@~1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" +scheduler@^0.19.1: + version "0.19.1" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196" + integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + schema-utils@^0.4.4, schema-utils@^0.4.5: version "0.4.7" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.7.tgz#ba74f597d2be2ea880131746ee17d0a093c68187" @@ -10063,13 +10165,6 @@ scoped-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/scoped-regex/-/scoped-regex-1.0.0.tgz#a346bb1acd4207ae70bd7c0c7ca9e566b6baddb8" -scss-tokenizer@^0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1" - dependencies: - js-base64 "^2.1.8" - source-map "^0.4.2" - seedrandom@^2.4.2: version "2.4.4" resolved "https://registry.yarnpkg.com/seedrandom/-/seedrandom-2.4.4.tgz#b25ea98632c73e45f58b77cfaa931678df01f9ba" @@ -10093,10 +10188,6 @@ semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@~5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" - send@0.16.2: version "0.16.2" resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" @@ -10169,6 +10260,7 @@ set-value@^2.0.0: setimmediate@^1.0.4, setimmediate@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= setprototypeof@1.0.3: version "1.0.3" @@ -10356,6 +10448,11 @@ source-list-map@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.0.tgz#aaa47403f7b245a92fbc97ea08f250d6087ed085" +"source-map-js@>=0.6.2 <2.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.1.tgz#a1741c131e3c77d048252adfa24e23b908670caf" + integrity sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA== + source-map-resolve@^0.5.0: version "0.5.2" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" @@ -10383,7 +10480,7 @@ source-map-url@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" -source-map@^0.4.2, source-map@^0.4.4: +source-map@^0.4.4: version "0.4.4" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" dependencies: @@ -10536,12 +10633,6 @@ statuses@~1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" -stdout-stream@^1.4.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/stdout-stream/-/stdout-stream-1.4.1.tgz#5ac174cdd5cd726104aa0c0b2bd83815d8d535de" - dependencies: - readable-stream "^2.0.1" - stealthy-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" @@ -10605,7 +10696,7 @@ string-template@~0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add" -string-width@^1.0.1, string-width@^1.0.2: +string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" dependencies: @@ -10955,7 +11046,7 @@ symbol-observable@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4" -symbol-observable@^1.1.0, symbol-observable@^1.2.0: +symbol-observable@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" @@ -10999,14 +11090,6 @@ tapable@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.0.0.tgz#cbb639d9002eed9c6b5975eb20598d7936f1f9f2" -tar@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" - dependencies: - block-stream "*" - fstream "^1.0.2" - inherits "2" - tar@^4: version "4.4.6" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.6.tgz#63110f09c00b4e60ac8bcfe1bf3c8660235fbc9b" @@ -11172,12 +11255,6 @@ trough@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.3.tgz#e29bd1614c6458d44869fc28b255ab7857ef7c24" -"true-case-path@^1.0.2": - version "1.0.3" - resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-1.0.3.tgz#f813b5a8c86b40da59606722b144e3225799f47d" - dependencies: - glob "^7.1.2" - tslib@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" @@ -11214,8 +11291,9 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" ua-parser-js@^0.7.18: - version "0.7.18" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.18.tgz#a7bfd92f56edfb117083b69e31d2aa8882d4b1ed" + version "0.7.21" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.21.tgz#853cf9ce93f642f67174273cc34565ae6f308777" + integrity sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ== uglify-es@^3.3.4: version "3.3.9" @@ -11291,7 +11369,7 @@ unicode-property-aliases-ecmascript@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.4.tgz#5a533f31b4317ea76f17d807fa0d116546111dd0" -unified@^6.0.0: +unified@^6.0.0, unified@^6.1.5: version "6.2.0" resolved "https://registry.yarnpkg.com/unified/-/unified-6.2.0.tgz#7fbd630f719126d67d40c644b7e3f617035f6dba" dependencies: @@ -11365,6 +11443,11 @@ unist-util-stringify-position@^1.0.0, unist-util-stringify-position@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz#3f37fcf351279dcbca7480ab5889bb8a832ee1c6" +unist-util-visit-parents@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-1.1.2.tgz#f6e3afee8bdbf961c0e6f028ea3c0480028c3d06" + integrity sha512-yvo+MMLjEwdc3RhhPYSximset7rwjMrdt9E41Smmvg25UQIenzrN83cRnF1JMzoMi9zZOQeYXHSDf7p+IQkW3Q== + unist-util-visit-parents@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-2.0.1.tgz#63fffc8929027bee04bfef7d2cce474f71cb6217" @@ -11377,6 +11460,13 @@ unist-util-visit@^1.1.0: dependencies: unist-util-visit-parents "^2.0.0" +unist-util-visit@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-1.4.1.tgz#4724aaa8486e6ee6e26d7ff3c8685960d560b1e3" + integrity sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw== + dependencies: + unist-util-visit-parents "^2.0.0" + unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" @@ -11811,8 +11901,9 @@ whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3: iconv-lite "0.4.23" whatwg-fetch@>=0.10.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" + version "3.0.0" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb" + integrity sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q== whatwg-mimetype@^2.1.0: version "2.1.0" @@ -11838,15 +11929,11 @@ whet.extend@~0.9.9: version "0.9.9" resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1" -which-module@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" - which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" -which@1, which@^1.2.12, which@^1.2.14, which@^1.2.9, which@^1.3.0, which@^1.3.1: +which@^1.2.12, which@^1.2.14, which@^1.2.9, which@^1.3.0, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" dependencies: @@ -11988,12 +12075,6 @@ yargs-parser@^10.0.0, yargs-parser@^10.1.0: dependencies: camelcase "^4.1.0" -yargs-parser@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a" - dependencies: - camelcase "^3.0.0" - yargs-parser@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-8.1.0.tgz#f1376a33b6629a5d063782944da732631e966950" @@ -12057,24 +12138,6 @@ yargs@^11.1.0: y18n "^3.2.1" yargs-parser "^9.0.2" -yargs@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8" - dependencies: - camelcase "^3.0.0" - cliui "^3.2.0" - decamelize "^1.1.1" - get-caller-file "^1.0.1" - os-locale "^1.4.0" - read-pkg-up "^1.0.1" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^1.0.2" - which-module "^1.0.0" - y18n "^3.2.1" - yargs-parser "^5.0.0" - yargs@~1.2.6: version "1.2.6" resolved "https://registry.yarnpkg.com/yargs/-/yargs-1.2.6.tgz#9c7b4a82fd5d595b2bf17ab6dcc43135432fe34b" diff --git a/docker-compose.yml b/docker-compose.yml index f18fd87..5fec1c0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,6 +3,9 @@ version: '3.2' services: db: image: postgres:9.6 + environment: + # IMPORTANT: Remove this on production + POSTGRES_HOST_AUTH_METHOD: trust volumes: - postgres-data:/var/lib/postgresql/data diff --git a/client/src/redux/selectors/index.js b/server/apps/career/__init__.py similarity index 100% rename from client/src/redux/selectors/index.js rename to server/apps/career/__init__.py diff --git a/server/apps/career/admin.py b/server/apps/career/admin.py new file mode 100644 index 0000000..bb5a1a0 --- /dev/null +++ b/server/apps/career/admin.py @@ -0,0 +1,18 @@ +from django.contrib import admin +from django.utils.safestring import mark_safe + +from .models import Career + + +@admin.register(Career) +class CareerAdmin(admin.ModelAdmin): + fields = ('title', 'description', 'url', 'image', 'image_tag', 'hidden') + readonly_fields = ('image_tag',) + + def image_tag(self, instance): + return mark_safe( + '' + .format(instance.image.url) + ) + image_tag.short_description = 'Image Preview' + image_tag.allow_tags = True diff --git a/server/apps/career/apps.py b/server/apps/career/apps.py new file mode 100644 index 0000000..298ef4f --- /dev/null +++ b/server/apps/career/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class CareerConfig(AppConfig): + name = 'career' diff --git a/server/apps/career/migrations/0001_initial.py b/server/apps/career/migrations/0001_initial.py new file mode 100644 index 0000000..423d42d --- /dev/null +++ b/server/apps/career/migrations/0001_initial.py @@ -0,0 +1,27 @@ +# Generated by Django 2.2.6 on 2020-05-13 11:10 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Career', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255)), + ('description', models.TextField(max_length=255)), + ('url', models.URLField(blank=True, max_length=255, null=True)), + ('image', models.FileField(blank=True, null=True, upload_to='client/image/')), + ], + options={ + 'ordering': ('id',), + }, + ), + ] diff --git a/server/apps/career/migrations/0002_auto_20200513_1245.py b/server/apps/career/migrations/0002_auto_20200513_1245.py new file mode 100644 index 0000000..2e69478 --- /dev/null +++ b/server/apps/career/migrations/0002_auto_20200513_1245.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.6 on 2020-05-13 12:45 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('career', '0001_initial'), + ] + + operations = [ + migrations.RenameField( + model_name='career', + old_name='name', + new_name='title', + ), + ] diff --git a/server/apps/career/migrations/0003_auto_20200517_0428.py b/server/apps/career/migrations/0003_auto_20200517_0428.py new file mode 100644 index 0000000..afb04f7 --- /dev/null +++ b/server/apps/career/migrations/0003_auto_20200517_0428.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.6 on 2020-05-17 04:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('career', '0002_auto_20200513_1245'), + ] + + operations = [ + migrations.AlterField( + model_name='career', + name='description', + field=models.TextField(blank=True), + ), + ] diff --git a/server/apps/career/migrations/0004_career_hidden.py b/server/apps/career/migrations/0004_career_hidden.py new file mode 100644 index 0000000..39ff939 --- /dev/null +++ b/server/apps/career/migrations/0004_career_hidden.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.6 on 2020-06-04 07:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('career', '0003_auto_20200517_0428'), + ] + + operations = [ + migrations.AddField( + model_name='career', + name='hidden', + field=models.BooleanField(default=False, help_text="Don't show this user"), + ), + ] diff --git a/server/apps/career/migrations/__init__.py b/server/apps/career/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/server/apps/career/models.py b/server/apps/career/models.py new file mode 100644 index 0000000..a302a06 --- /dev/null +++ b/server/apps/career/models.py @@ -0,0 +1,15 @@ +from django.db import models + + +class Career(models.Model): + title = models.CharField(max_length=255) + description = models.TextField(blank=True) + url = models.URLField(max_length=255, blank=True, null=True) + image = models.FileField(upload_to='client/image/', blank=True, null=True) + hidden = models.BooleanField(default=False, help_text='Don\'t show this career') + + class Meta: + ordering = ('id',) + + def __str__(self): + return self.title diff --git a/server/apps/career/serializers.py b/server/apps/career/serializers.py new file mode 100644 index 0000000..ef7c221 --- /dev/null +++ b/server/apps/career/serializers.py @@ -0,0 +1,9 @@ +from rest_framework import serializers + +from .models import Career + + +class CareerSerializer(serializers.ModelSerializer): + class Meta: + model = Career + fields = '__all__' diff --git a/server/apps/career/tests.py b/server/apps/career/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/server/apps/career/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/server/apps/career/views.py b/server/apps/career/views.py new file mode 100644 index 0000000..a5efa58 --- /dev/null +++ b/server/apps/career/views.py @@ -0,0 +1,12 @@ +from rest_framework import viewsets + +from togglecorp.views import CacheListMixin +from .models import Career +from .serializers import CareerSerializer + + +class CareerViewSet(CacheListMixin, viewsets.ReadOnlyModelViewSet): + serializer_class = CareerSerializer + + def get_queryset(self): + return Career.objects.filter(hidden=False) diff --git a/server/apps/client/views.py b/server/apps/client/views.py index ad05b18..919fb09 100644 --- a/server/apps/client/views.py +++ b/server/apps/client/views.py @@ -5,6 +5,6 @@ from .serializers import ClientSerializer -class ClientViewSet(CacheListMixin, viewsets.ModelViewSet): +class ClientViewSet(CacheListMixin, viewsets.ReadOnlyModelViewSet): queryset = Client.objects.all() serializer_class = ClientSerializer diff --git a/server/apps/member/views.py b/server/apps/member/views.py index cfcd144..cdb0a93 100644 --- a/server/apps/member/views.py +++ b/server/apps/member/views.py @@ -5,7 +5,7 @@ from .serializers import MemberSerializer -class MemberViewSet(CacheListMixin, viewsets.ModelViewSet): +class MemberViewSet(CacheListMixin, viewsets.ReadOnlyModelViewSet): serializer_class = MemberSerializer def get_queryset(self): diff --git a/server/apps/service/views.py b/server/apps/service/views.py index 0c5c149..c4aa4a9 100644 --- a/server/apps/service/views.py +++ b/server/apps/service/views.py @@ -5,6 +5,6 @@ from .serializers import ServiceSerializer -class ServiceViewSet(CacheListMixin, viewsets.ModelViewSet): +class ServiceViewSet(CacheListMixin, viewsets.ReadOnlyModelViewSet): queryset = Service.objects.all() serializer_class = ServiceSerializer diff --git a/server/apps/technology/views.py b/server/apps/technology/views.py index e0edf0d..8006121 100644 --- a/server/apps/technology/views.py +++ b/server/apps/technology/views.py @@ -5,7 +5,7 @@ from .serializers import TechnologySectionSerializer -class TechnologySectionViewSet(CacheListMixin, viewsets.ModelViewSet): +class TechnologySectionViewSet(CacheListMixin, viewsets.ReadOnlyModelViewSet): serializer_class = TechnologySectionSerializer def get_queryset(self): diff --git a/server/requirements.txt b/server/requirements.txt index e159d98..65f4e96 100644 --- a/server/requirements.txt +++ b/server/requirements.txt @@ -1,12 +1,13 @@ -django>=2.2,<2.3 +boto3==1.9.5 django-cors-headers==2.4.0 -djangorestframework==3.9.4 -djangorestframework-camel-case==0.2.0 -drf-dynamic-fields==0.3.0 -pyjwt==1.6.4 django-jet==1.0.8 -psycopg2-binary==2.7.5 -boto3==1.9.5 django-storages==1.7.1 +django>=2.2,<2.3 +djangorestframework-camel-case==0.2.0 +djangorestframework==3.9.4 +drf-dynamic-fields==0.3.0 ipython==6.5.0 +psycopg2-binary==2.8.5 +pyjwt==1.6.4 uwsgi +zappa==0.51.0 diff --git a/server/togglecorp/receivers.py b/server/togglecorp/receivers.py index fc9b30d..5fe79f3 100644 --- a/server/togglecorp/receivers.py +++ b/server/togglecorp/receivers.py @@ -1,9 +1,10 @@ -import datetime from django.core.cache import cache from django.dispatch import receiver from django.db.models.signals import post_save +from django.utils import timezone from client.models import Client +from career.models import Career from member.models import Member, MemberUrlType, MemberUrl from service.models import Service from technology.models import Technology, TechnologySection @@ -11,38 +12,39 @@ from .views import get_last_modified_key -def set_last_updated_at(sender, instance, created, **kwargs): +def set_last_updated_at(sender, *args, **kwargs): """ Signal to set last_updated_at which is used for caching """ model_key = get_last_modified_key(sender) - cache.set(model_key, datetime.datetime.now(), None) - - -receiver(post_save, sender=Client)(set_last_updated_at) -receiver(post_save, sender=Service)(set_last_updated_at) - - -def set_last_updated_at_member(sender, instance, created, **kwargs): - """ - Signal to set last_updated_at which is used for caching - """ - model_key = get_last_modified_key(Member) - cache.set(model_key, datetime.datetime.now(), None) - - -receiver(post_save, sender=Member)(set_last_updated_at_member) -receiver(post_save, sender=MemberUrl)(set_last_updated_at_member) -receiver(post_save, sender=MemberUrlType)(set_last_updated_at_member) - - -def set_last_updated_at_technology(sender, instance, created, **kwargs): - """ - Signal to set last_updated_at which is used for caching - """ - model_key = get_last_modified_key(TechnologySection) - cache.set(model_key, datetime.datetime.now(), None) - - -receiver(post_save, sender=TechnologySection)(set_last_updated_at_technology) -receiver(post_save, sender=Technology)(set_last_updated_at_technology) + cache.set(model_key, timezone.now(), None) + + +def set_last_updated_at_member(sender, *args, **kwargs): + set_last_updated_at(Member) + + +def set_last_updated_at_technology(sender, *args, **kwargs): + set_last_updated_at(TechnologySection) + + +# Register receivers +for func, models in [ + ( + set_last_updated_at, [ + Client, Service, Career, + ] + ), + ( + set_last_updated_at_member, [ + Member, MemberUrl, MemberUrlType, + ] + ), + ( + set_last_updated_at_technology, [ + Technology, TechnologySection, + ] + ), +]: + for model in models: + receiver(post_save, sender=model)(func) diff --git a/server/togglecorp/s3_storages.py b/server/togglecorp/s3_storages.py index 4625d90..be088d4 100644 --- a/server/togglecorp/s3_storages.py +++ b/server/togglecorp/s3_storages.py @@ -7,7 +7,15 @@ class StaticStorage(S3Boto3Storage): bucket_name = settings.AWS_STORAGE_BUCKET_NAME_STATIC querystring_auth = False + # Don't use token provided by AWS environment (use custom defiend tokens) + def _get_security_token(self): + return None + class MediaStorage(S3Boto3Storage): location = settings.MEDIAFILES_LOCATION bucket_name = settings.AWS_STORAGE_BUCKET_NAME_MEDIA + + # Don't use token provided by AWS environment (use custom defiend tokens) + def _get_security_token(self): + return None diff --git a/server/togglecorp/settings.py b/server/togglecorp/settings.py index c690ac1..346c951 100644 --- a/server/togglecorp/settings.py +++ b/server/togglecorp/settings.py @@ -8,7 +8,7 @@ # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) APPS_DIR = os.path.join(BASE_DIR, 'apps') -sys.path.append(APPS_DIR) +sys.path.insert(0, APPS_DIR) # Quick-start development settings - unsuitable for production @@ -18,7 +18,7 @@ SECRET_KEY = '+c=ii9osx=a^h#m$s%@sql2pyj_#xq%3p(0=r&#+3+hjq@tsis' # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True +DEBUG = os.environ.get('DEBUG', 'True').lower() == 'true' ALLOWED_HOSTS = ['*'] @@ -29,6 +29,7 @@ 'member', 'technology', 'service', + 'career', ] INSTALLED_APPS = [ @@ -96,6 +97,9 @@ 'PASSWORD': os.environ.get('DATABASE_PASSWORD', 'postgres'), 'PORT': os.environ.get('DATABASE_PORT', '5432'), 'HOST': os.environ.get('DATABASE_HOST', 'db'), + 'OPTIONS': { + 'connect_timeout': 5, + } } } @@ -165,6 +169,7 @@ AWS_SECRET_ACCESS_KEY = os.environ['S3_AWS_SECRET_ACCESS_KEY'] AWS_S3_REGION_NAME = os.environ['S3_AWS_REGION_NAME'] + AWS_S3_ADDRESSING_STYLE = 'path' AWS_S3_FILE_OVERWRITE = False AWS_DEFAULT_ACL = 'private' AWS_QUERYSTRING_AUTH = False diff --git a/server/togglecorp/urls.py b/server/togglecorp/urls.py index d8da530..f17e0d9 100644 --- a/server/togglecorp/urls.py +++ b/server/togglecorp/urls.py @@ -10,6 +10,7 @@ from togglecorp import admin as tg_admin # noqa: F401 from member.views import MemberViewSet from client.views import ClientViewSet +from career.views import CareerViewSet from service.views import ServiceViewSet from technology.views import TechnologySectionViewSet @@ -18,6 +19,7 @@ router.register(r'members', MemberViewSet, base_name='member') router.register(r'clients', ClientViewSet, base_name='client') +router.register(r'careers', CareerViewSet, base_name='career') router.register(r'services', ServiceViewSet, base_name='service') router.register( r'technology-sections', diff --git a/server/togglecorp/views.py b/server/togglecorp/views.py index df1df9b..a242744 100644 --- a/server/togglecorp/views.py +++ b/server/togglecorp/views.py @@ -1,8 +1,8 @@ import hashlib -import datetime from django.core.cache import cache from django.utils.decorators import method_decorator from django.views.decorators.http import condition +from django.utils import timezone def get_last_modified_key(model): @@ -14,7 +14,7 @@ def last_modified_func(request, *args, **kwargs): model_key = get_last_modified_key(model) last_modified = cache.get(model_key) if not last_modified: - last_modified = datetime.datetime.now() + last_modified = timezone.now() cache.set(model_key, last_modified, None) return last_modified diff --git a/server/zappa.dockerfile b/server/zappa.dockerfile new file mode 100644 index 0000000..af11e92 --- /dev/null +++ b/server/zappa.dockerfile @@ -0,0 +1,28 @@ +FROM python:3.6-slim + +MAINTAINER togglecorp info@togglecorp.com + +WORKDIR /code + +COPY ./requirements.txt /code/requirements.txt + +RUN apt-get update -y \ + && apt-get install -y \ + vim \ + curl \ + cron \ + unzip \ + python3 \ + python3-dev \ + libpq-dev \ + python3-setuptools \ + python3-pip + +RUN pip install --upgrade pip \ + && pip install virtualenv \ + && virtualenv /venv \ + && . /venv/bin/activate \ + && pip install --no-cache-dir -r requirements.txt \ + && apt-get autoremove + +COPY . /code/ diff --git a/server/zappa_settings.json b/server/zappa_settings.json new file mode 100644 index 0000000..d9d815b --- /dev/null +++ b/server/zappa_settings.json @@ -0,0 +1,16 @@ +{ + "prod": { + "aws_region": "us-east-1", + "django_settings": "togglecorp.settings", + "project_name": "togglecorp-server", + "runtime": "python3.8", + "s3_bucket": "zappa-togglecorp", + "certificate_arn": "arn:aws:acm:us-east-1:182509255933:certificate/64de3375-63d6-4118-89e0-f1b08fab91b8", + "domain": "api.togglecorp.com", + "keep_warm": false, + "vpc_config" : { + "SubnetIds": ["subnet-930078d9", "subnet-5f9b3038"], + "SecurityGroupIds": [ "sg-00085d77439843e85" ] + } + } +}