├── .babelrc
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .nowignore
├── .prettierignore
├── .prettierrc
├── .vscode
└── launch.json
├── History.md
├── LICENSE
├── README.md
├── amplify
├── .config
│ └── project-config.json
├── backend
│ ├── awscloudformation
│ │ └── nested-cloudformation-stack.yml
│ ├── backend-config.json
│ └── hosting
│ │ └── S3AndCloudFront
│ │ ├── parameters.json
│ │ └── template.json
└── team-provider-info.json
├── amplifyPublishIgnore.json
├── apollo.config.js
├── config
├── buildAuth.js
├── site.js
└── theme.js
├── gatsby-browser.js
├── gatsby-config.js
├── gatsby-node.js
├── gatsby-ssr.js
├── launch.json
├── now.json
├── package.json
├── src
├── apps
│ └── admin
│ │ ├── components
│ │ ├── affiliation.edit.js
│ │ ├── codex.textinput.js
│ │ ├── company.edit.js
│ │ ├── companylinks.js
│ │ ├── companylinks.select.js
│ │ ├── profile.edit.js
│ │ └── select
│ │ │ ├── helpers
│ │ │ ├── getSuggestions.js
│ │ │ ├── index.js
│ │ │ ├── renderInput.js
│ │ │ └── renderSuggestions.js
│ │ │ └── index.js
│ │ ├── config
│ │ ├── index.js
│ │ └── styles.js
│ │ ├── features
│ │ ├── afffiliation.create
│ │ │ ├── components
│ │ │ │ ├── companySearch
│ │ │ │ │ ├── helpers
│ │ │ │ │ │ ├── getSuggestion.js
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ ├── renderInput.js
│ │ │ │ │ │ └── renderSuggestion.js
│ │ │ │ │ └── index.js
│ │ │ │ ├── index.js
│ │ │ │ └── select.js
│ │ │ ├── graphql
│ │ │ │ ├── index.js
│ │ │ │ ├── mutations.js
│ │ │ │ └── queries.js
│ │ │ ├── helpers
│ │ │ │ ├── checkCanFormSubmit.js
│ │ │ │ ├── checkForSectionErrors.js
│ │ │ │ ├── getCurrentErrors.js
│ │ │ │ ├── getInitialValues.js
│ │ │ │ ├── handleCreateAffiliation.js
│ │ │ │ ├── index.js
│ │ │ │ ├── renderFormHeader.js
│ │ │ │ └── validationSchema.js
│ │ │ └── index.js
│ │ ├── company.create
│ │ │ ├── components
│ │ │ │ ├── basics.js
│ │ │ │ ├── categories
│ │ │ │ │ ├── helpers
│ │ │ │ │ │ ├── formatCategory.js
│ │ │ │ │ │ ├── getSuggestions.js
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ ├── renderInput.js
│ │ │ │ │ │ └── renderSuggestions.js
│ │ │ │ │ └── index.js
│ │ │ │ ├── formErrorMessage.js
│ │ │ │ ├── index.js
│ │ │ │ ├── links
│ │ │ │ │ ├── index.js
│ │ │ │ │ └── links.input.js
│ │ │ │ ├── location
│ │ │ │ │ ├── address.js
│ │ │ │ │ ├── index.js
│ │ │ │ │ └── locationPaper.js
│ │ │ │ ├── logo.js
│ │ │ │ ├── preview.js
│ │ │ │ └── select.js
│ │ │ ├── graphql
│ │ │ │ ├── index.js
│ │ │ │ ├── mutations.js
│ │ │ │ └── queries.js
│ │ │ ├── helpers
│ │ │ │ ├── checkCanFormSubmit.js
│ │ │ │ ├── checkForSectionErrors.js
│ │ │ │ ├── getCurrentErrors.js
│ │ │ │ ├── getDisplayedErrorMessage.js
│ │ │ │ ├── getInitialValues.js
│ │ │ │ ├── handleCreateCompany.js
│ │ │ │ ├── index.js
│ │ │ │ ├── renderExpansionPanel.js
│ │ │ │ ├── renderFormHeader.js
│ │ │ │ └── validationSchema.js
│ │ │ └── index.js
│ │ ├── profile.affiliations
│ │ │ ├── components
│ │ │ │ ├── avatar.js
│ │ │ │ ├── content
│ │ │ │ │ ├── index.js
│ │ │ │ │ ├── primary.js
│ │ │ │ │ └── secondary.js
│ │ │ │ ├── controls
│ │ │ │ │ ├── delete.js
│ │ │ │ │ ├── edit.js
│ │ │ │ │ └── index.js
│ │ │ │ ├── edit.js
│ │ │ │ └── index.js
│ │ │ ├── controller.js
│ │ │ ├── graphql
│ │ │ │ ├── index.js
│ │ │ │ ├── mutations.js
│ │ │ │ └── queries.js
│ │ │ ├── helpers
│ │ │ │ ├── formatDates.js
│ │ │ │ ├── index.js
│ │ │ │ └── renderAffiliation.js
│ │ │ └── index.js
│ │ ├── profile.companies
│ │ │ ├── components
│ │ │ │ ├── avatar.js
│ │ │ │ ├── content
│ │ │ │ │ ├── index.js
│ │ │ │ │ ├── primary.js
│ │ │ │ │ └── secondary.js
│ │ │ │ ├── controls
│ │ │ │ │ ├── delete.js
│ │ │ │ │ ├── edit.js
│ │ │ │ │ └── index.js
│ │ │ │ └── index.js
│ │ │ ├── controller.js
│ │ │ ├── graphql
│ │ │ │ ├── index.js
│ │ │ │ ├── mutations.js
│ │ │ │ └── queries.js
│ │ │ ├── helpers
│ │ │ │ ├── formatDates.js
│ │ │ │ ├── index.js
│ │ │ │ └── renderCompany.js
│ │ │ └── index.js
│ │ └── profile.navigation
│ │ │ ├── components
│ │ │ ├── desktop.js
│ │ │ └── mobile.js
│ │ │ └── index.js
│ │ ├── helpers.js
│ │ ├── index.js
│ │ └── routes
│ │ ├── affiliation
│ │ └── index.js
│ │ ├── auth
│ │ ├── company.js
│ │ ├── confirm.js
│ │ ├── create.js
│ │ ├── createwiz.js
│ │ ├── index.js
│ │ ├── login.js
│ │ ├── mocks.js
│ │ └── profile.js
│ │ ├── company
│ │ ├── index.js
│ │ └── location.js
│ │ └── profile
│ │ ├── graphql
│ │ ├── index.js
│ │ └── queries.js
│ │ ├── index.js
│ │ ├── listitems.js
│ │ ├── profile.js
│ │ └── styles.js
├── atoms
│ ├── confirm.js
│ ├── containers.js
│ ├── index.js
│ ├── inputs.js
│ └── spinner.js
├── bootstrap.js
├── components
│ ├── PrivateRoute.js
│ ├── header
│ │ ├── __mocks__
│ │ │ └── index.js
│ │ ├── header.mobile.js
│ │ ├── header.one.js
│ │ ├── header.secondary.js
│ │ ├── header.styles.js
│ │ ├── header.three.js
│ │ ├── header.two.js
│ │ ├── helpers
│ │ │ └── renderAvatar.js
│ │ └── index.js
│ ├── image.js
│ ├── input
│ │ ├── field.js
│ │ ├── index.js
│ │ ├── input.js
│ │ └── label.js
│ ├── layout.js
│ ├── navigation
│ │ └── navbar.js
│ ├── search
│ │ ├── helpers
│ │ │ ├── getSuggestion.js
│ │ │ ├── index.js
│ │ │ ├── renderInput.js
│ │ │ └── renderSuggestion.js
│ │ └── index.js
│ ├── seo.js
│ └── transition.js
├── constants
│ └── transition.js
├── features
│ └── landinghero
│ │ ├── hero.js
│ │ ├── index.js
│ │ ├── sidekick.js
│ │ ├── sidekickItem.js
│ │ └── styles.js
├── graphql
│ ├── index.js
│ ├── mutations
│ │ └── index.js
│ └── queries
│ │ └── index.js
├── helpers
│ ├── callAll.js
│ ├── enums.js
│ ├── formatPhoneNumber.js
│ ├── truncateText.js
│ └── wrapPageElement.js
├── html.js
├── images
│ └── sls-logo.png
├── molecules
│ ├── avatars.js
│ └── index.js
├── pages
│ ├── 404.js
│ ├── about.js
│ ├── app.js
│ ├── companies
│ │ └── index.js
│ ├── index.js
│ └── tags.js
├── services
│ └── hacks.js
├── store
│ ├── apollo.js
│ ├── auth-context.js
│ ├── createContext.js
│ ├── modal-context.js
│ ├── provider.js
│ ├── useModal.js
│ ├── user-context.js
│ └── utils
│ │ ├── auth-client.js
│ │ ├── bootstrap.js
│ │ ├── const.js
│ │ └── useCallbackStatus.js
└── templates
│ ├── __mocks__
│ ├── listitems.js
│ └── styles.js
│ ├── company.js
│ ├── company
│ ├── companyCategories.js
│ ├── companyContactLinks.js
│ ├── contact.js
│ ├── helpers.js
│ ├── index.js
│ ├── intelligence.js
│ ├── location.js
│ ├── locationmap.js
│ └── news.js
│ ├── profile.js
│ └── tags.js
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "babel-preset-gatsby",
5 | {
6 | "targets": {
7 | "browsers": [">0.25%", "not dead"]
8 | }
9 | }
10 | ]
11 | ],
12 | "plugins": ["babel-plugin-styled-components"]
13 | }
14 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: true,
4 | es6: true,
5 | },
6 | plugins: ['react'],
7 | globals: {
8 | graphql: false,
9 | },
10 | parser: 'babel-eslint',
11 |
12 | parserOptions: {
13 | sourceType: 'module',
14 | ecmaVersion: 8,
15 | ecmaFeatures: {
16 | experimentalObjectRestSpread: true,
17 | jsx: true,
18 | },
19 | },
20 | }
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (http://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # Typescript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # dotenv environment variables file
55 | .env
56 |
57 | # gatsby files
58 | .cache/
59 | public
60 |
61 | # Mac files
62 | .DS_Store
63 |
64 | # Yarn
65 | yarn-error.log
66 | .pnp/
67 | .pnp.js
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | #amplify
72 | amplify/\#current-cloud-backend
73 | amplify/.config/local-*
74 | amplify/backend/amplify-meta.json
75 | aws-exports.js
76 | awsconfiguration.json
77 |
78 | .env
79 | .env.*
80 | codex-tech-index.code-workspace
81 |
--------------------------------------------------------------------------------
/.nowignore:
--------------------------------------------------------------------------------
1 | .cache
2 | .vscode
3 | node_modules
4 | public
5 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | *.md
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": true,
3 | "singleQuote": true,
4 | "trailingComma": "es5"
5 | }
6 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "Gatsby develop",
9 | "type": "node",
10 | "request": "launch",
11 | "protocol": "inspector",
12 | "program": "${workspaceRoot}/node_modules/gatsby/dist/bin/gatsby",
13 | "args": ["develop"],
14 | "stopOnEntry": false,
15 | "runtimeArgs": ["--nolazy"],
16 | "sourceMaps": false
17 | },
18 | {
19 | "name": "Gatsby build",
20 | "type": "node",
21 | "request": "launch",
22 | "protocol": "inspector",
23 | "program": "${workspaceRoot}/node_modules/gatsby/dist/bin/gatsby",
24 | "args": ["build"],
25 | "stopOnEntry": false,
26 | "runtimeArgs": ["--nolazy"],
27 | "sourceMaps": false
28 | },
29 |
30 | {
31 | "type": "node",
32 | "request": "launch",
33 | "name": "Launch Program",
34 | "program": "${workspaceFolder}/develop"
35 | }
36 | ]
37 | }
38 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 gatsbyjs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CodeX LegalTech Index
2 |
3 | Explore a curated list of ~1200 companies changing the way legal is done.
4 |
5 | [Current Website](https://law.haus) vs. [Legacy Website](https://techindex.law.stanford.edu)
6 |
7 |
--------------------------------------------------------------------------------
/amplify/.config/project-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "projectName": "frontendtechlist",
3 | "version": "1.0",
4 | "frontend": "javascript",
5 | "javascript": {
6 | "framework": "react",
7 | "config": {
8 | "SourceDir": "src",
9 | "DistributionDir": "public",
10 | "BuildCommand": "yarn build",
11 | "StartCommand": "npm run-script start"
12 | }
13 | },
14 | "providers": ["awscloudformation"]
15 | }
16 |
--------------------------------------------------------------------------------
/amplify/backend/backend-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "hosting": {
3 | "S3AndCloudFront": {
4 | "service": "S3AndCloudFront",
5 | "providerPlugin": "awscloudformation"
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/amplify/backend/hosting/S3AndCloudFront/parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "bucketName": "frontendtechlist-20190209123941-hostingbucket"
3 | }
--------------------------------------------------------------------------------
/amplify/team-provider-info.json:
--------------------------------------------------------------------------------
1 | {
2 | "master": {
3 | "awscloudformation": {
4 | "AuthRoleName": "frontendtechlist-20190209123828-authRole",
5 | "UnauthRoleArn": "arn:aws:iam::196309159802:role/frontendtechlist-20190209123828-unauthRole",
6 | "AuthRoleArn": "arn:aws:iam::196309159802:role/frontendtechlist-20190209123828-authRole",
7 | "Region": "us-east-1",
8 | "DeploymentBucketName": "frontendtechlist-20190209123828-deployment",
9 | "UnauthRoleName": "frontendtechlist-20190209123828-unauthRole",
10 | "StackName": "frontendtechlist-20190209123828",
11 | "StackId": "arn:aws:cloudformation:us-east-1:196309159802:stack/frontendtechlist-20190209123828/82e72fe0-2c91-11e9-8109-12b31d3117be"
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/amplifyPublishIgnore.json:
--------------------------------------------------------------------------------
1 | []
--------------------------------------------------------------------------------
/apollo.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | client: {
3 | name: 'CodeX Tech Index [web]',
4 | includes: ['./src/**/*.js'],
5 | service: {
6 | name: 'stanford-tech-index',
7 | url: 'https://scti.ok8s.net/apollo',
8 | },
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/config/buildAuth.js:
--------------------------------------------------------------------------------
1 | require('dotenv').config();
2 | global.fetch = require('node-fetch');
3 | const fs = require('fs');
4 | const path = require('path');
5 | const AWS = require('aws-sdk');
6 |
7 | const ACCESS_KEY = process.env.ACCESS_KEY;
8 | const SECRET_KEY = process.env.SECRET_KEY;
9 | const REGION = process.env.REGION;
10 | const USER_POOL = process.env.USERPOOL_ID;
11 | const CLIENT_ID = process.env.CLIENT_ID;
12 | const USERNAME = process.env.USERNAME;
13 | const PASSWORD = process.env.PASSWORD;
14 |
15 | AWS.config.update({
16 | region: 'us-west-2',
17 | accessKeyId: ACCESS_KEY,
18 | secretAccessKey: SECRET_KEY,
19 | credentials: {
20 | accessKeyId: ACCESS_KEY,
21 | secretAccessKey: SECRET_KEY,
22 | },
23 | });
24 |
25 | if (ACCESS_KEY === undefined || SECRET_KEY === undefined) {
26 | throw new Error(
27 | 'Pre-build script could not authenticate because `AWS-related` environmental variables were not provided.'
28 | );
29 | }
30 |
31 | if (USERNAME === undefined || PASSWORD === undefined) {
32 | throw new Error(
33 | '[PRISMA]: Requires authentication. Please supply USERNAME and PASSWORD as ENV'
34 | );
35 | }
36 |
37 | const Cognito = new AWS.CognitoIdentityServiceProvider({
38 | apiVersion: '2016-04-18',
39 | });
40 |
41 | const params = {
42 | AuthFlow: 'ADMIN_NO_SRP_AUTH',
43 | ClientId: CLIENT_ID,
44 | UserPoolId: USER_POOL,
45 | AuthParameters: {
46 | USERNAME: USERNAME,
47 | PASSWORD: PASSWORD,
48 | },
49 | };
50 |
51 | async function getTempJWT() {
52 | return new Promise((res, rej) => {
53 | Cognito.adminInitiateAuth(params, (err, data) => {
54 | if (err) {
55 | console.log(err);
56 | rej(err);
57 | }
58 | const { AuthenticationResult } = data;
59 | const { IdToken } = AuthenticationResult;
60 | res({ jwt: IdToken });
61 | });
62 | });
63 | }
64 |
65 | async function exportTempJWT(jwt) {
66 | const destination = path.resolve(__dirname, './tempjwt.json');
67 | fs.unlink(destination, err => {
68 | fs.appendFile(destination, JSON.stringify(jwt), err => {
69 | if (err) throw err;
70 | console.log(`The data has been written to disk`);
71 | });
72 | });
73 | }
74 |
75 | async function getTemporaryAuthCreds() {
76 | const jwt = await getTempJWT();
77 | return jwt;
78 | }
79 |
80 | module.exports = getTemporaryAuthCreds;
81 |
--------------------------------------------------------------------------------
/config/site.js:
--------------------------------------------------------------------------------
1 | require('dotenv').config({
2 | path: `.env${
3 | process.env.NODE_ENV === 'production' || process.env.LOCAL !== 'true'
4 | ? '.production'
5 | : '.local'
6 | }`,
7 | });
8 | const theme = require('./theme');
9 |
10 | module.exports = () => ({
11 | social: {
12 | twitter: 'https://twitter.com/codexstanford',
13 | },
14 | siteMetadata: {
15 | title: 'CodeX LegalTech Index',
16 | shortTitle: 'LegalTech Index',
17 | description:
18 | 'Explore a curated list of 1129 companies changing the way legal is done',
19 | hostname: 'law.haus',
20 | protocol: 'https',
21 | url: 'https://law.haus',
22 | },
23 | api: {
24 | graphql: {
25 | endpoint: process.env.GATSBY_GRAPHQL_ENDPOINT,
26 | typeName: 'TechList',
27 | fieldName: 'allTechList',
28 | },
29 | },
30 | theme,
31 | });
32 |
--------------------------------------------------------------------------------
/config/theme.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | theme: {
3 | colors: {
4 | primary: '#b1040e',
5 | link: '#006CB8',
6 | hover: '#00548f',
7 | },
8 | typography: {
9 | useNextVariants: true,
10 | fontFamily: [
11 | 'Source Sans Pro',
12 | '-apple-system',
13 | 'BlinkMacSystemFont',
14 | '"Segoe UI"',
15 | 'Roboto',
16 | '"Helvetica Neue"',
17 | 'Arial',
18 | 'sans-serif',
19 | '"Apple Color Emoji"',
20 | '"Segoe UI Emoji"',
21 | '"Segoe UI Symbol"',
22 | ].join(','),
23 | },
24 | palette: {
25 | white: {
26 | main: '#ffffff',
27 | },
28 | primary: {
29 | main: '#b1040e',
30 | },
31 | secondary: {
32 | main: '#04b1a8',
33 | contrastText: '#fff',
34 | },
35 | },
36 | },
37 | }
38 |
--------------------------------------------------------------------------------
/gatsby-browser.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import AppProvider from 'store/provider';
3 |
4 | export const registerServiceWorker = () => true;
5 |
6 | export const onRouteUpdate = ({ location, prevLocation }) => {
7 | // console.log('new pathname', location.pathname);
8 | // console.log('old pathname', prevLocation ? prevLocation.pathname : null);
9 | // Track pageview with google analytics
10 | };
11 |
12 | export const wrapRootElement = ({ element }) => {
13 | return {element};
14 | };
15 |
16 | // Page Transitions
17 | export const onPreRouteUpdate = ({ location, prevLocation }) => {
18 | // console.log('Gatsby started to change location to', location.pathname);
19 | // console.log(
20 | // 'Gatsby started to change location from',
21 | // prevLocation ? prevLocation.pathname : null
22 | // );
23 | };
24 |
--------------------------------------------------------------------------------
/gatsby-ssr.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { renderToString } from 'react-dom/server';
3 | import { ServerStyleSheet } from 'styled-components';
4 | import AppProvider from 'store/provider';
5 |
6 | export const replaceRenderer = ({
7 | bodyComponent,
8 | replaceBodyHTMLString,
9 | setHeadComponents,
10 | }) => {
11 | // React Context in SSR/build
12 | const ConnectedBody = () => {bodyComponent};
13 | replaceBodyHTMLString(renderToString(bodyComponent));
14 |
15 | // Add styled-components in SSR/build
16 | const sheet = new ServerStyleSheet();
17 | const bodyHTML = renderToString(sheet.collectStyles());
18 | const styleElement = sheet.getStyleElement();
19 | setHeadComponents(styleElement);
20 | };
21 |
22 | export const wrapRootElement = ({ element }) => {
23 | return {element};
24 | };
25 |
--------------------------------------------------------------------------------
/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": "Gatsby develop",
6 | "type": "node",
7 | "request": "launch",
8 | "protocol": "inspector",
9 | "program": "${workspaceRoot}/node_modules/gatsby/dist/bin/gatsby",
10 | "args": ["develop"],
11 | "stopOnEntry": false,
12 | "runtimeArgs": ["--nolazy"],
13 | "sourceMaps": false
14 | },
15 | {
16 | "name": "Gatsby build",
17 | "type": "node",
18 | "request": "launch",
19 | "protocol": "inspector",
20 | "program": "${workspaceRoot}/node_modules/gatsby/dist/bin/gatsby",
21 | "args": ["build"],
22 | "stopOnEntry": false,
23 | "runtimeArgs": ["--nolazy"],
24 | "sourceMaps": false
25 | }
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/now.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 2,
3 | "name": "codex",
4 | "build": {
5 | "env": {
6 | "ACCESS_KEY": "@access_key",
7 | "PORT": "8000",
8 | "SECRET_KEY": "@secret_key",
9 | "REGION": "us-west-2",
10 | "CLIENT_ID": "181177ggq1ot45s6t791vposkr",
11 | "USERPOOL_ID": "us-west-2_uzyDC8Snl",
12 | "USERNAME": "@username",
13 | "PASSWORD": "@password",
14 | "GATSBY_GOOGLE_MAPS_API_KEY": "@gatsby_google_maps_api_key",
15 | "GATSBY_BING_SEARCH_NEWS_API": "https://api.cognitive.microsoft.com/bing/v7.0/news/search",
16 | "GATSBY_BING_API_KEY": "@gatsby_bing_api_key",
17 | "GATSBY_ENGINE_API_KEY": "@apollo_engine_api_key",
18 | "GATSBY_APPLICATION_NAME": "Gatsby-Production-Now",
19 | "GATSBY_APPLICATION_VERSION": "0.0.1",
20 | "GATSBY_GRAPHQL_ENDPOINT": "@gatsby_graphql_endpoint",
21 | "GATSBY_GRAPHQL_IDE": "playground"
22 | }
23 | },
24 | "env": {
25 | "ACCESS_KEY": "@access_key",
26 | "PORT": "8000",
27 | "SECRET_KEY": "@secret_key",
28 | "REGION": "us-west-2",
29 | "CLIENT_ID": "181177ggq1ot45s6t791vposkr",
30 | "USERPOOL_ID": "us-west-2_uzyDC8Snl",
31 | "USERNAME": "@username",
32 | "PASSWORD": "@password",
33 | "GATSBY_GOOGLE_MAPS_API_KEY": "@gatsby_google_maps_api_key",
34 | "GATSBY_BING_SEARCH_NEWS_API": "https://api.cognitive.microsoft.com/bing/v7.0/news/search",
35 | "GATSBY_BING_API_KEY": "@gatsby_bing_api_key",
36 | "GATSBY_ENGINE_API_KEY": "@apollo_engine_api_key",
37 | "GATSBY_APPLICATION_NAME": "Gatsby-Production-Now",
38 | "GATSBY_APPLICATION_VERSION": "0.0.1",
39 | "GATSBY_GRAPHQL_ENDPOINT": "@gatsby_graphql_endpoint",
40 | "GATSBY_GRAPHQL_IDE": "playground",
41 | "NODE_ENV": "production"
42 | },
43 | "builds": [
44 | {
45 | "src": "package.json",
46 | "use": "@now/static-build",
47 | "config": { "distDir": "public" }
48 | }
49 | ],
50 | "routes": [
51 | {
52 | "src": "^/(.*).html",
53 | "headers": { "cache-control": "public,max-age=0,must-revalidate" },
54 | "dest": "$1.html"
55 | }
56 | ]
57 | }
58 |
--------------------------------------------------------------------------------
/src/apps/admin/components/codex.textinput.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Field } from 'formik';
3 | import { TextField } from 'formik-material-ui';
4 |
5 | export default function CodeXTextField({
6 | name,
7 | type = 'text',
8 | label,
9 | component = TextField,
10 | errors,
11 | touched,
12 | InputLabelProps,
13 | ...rest
14 | }) {
15 | return (
16 | <>
17 |
26 | {/* {fieldErrors && isTouched ?
{fieldErrors}
: null} */}
27 | >
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/src/apps/admin/components/companylinks.select.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { makeStyles } from '@material-ui/core/styles';
3 | import Input from '@material-ui/core/Input';
4 | import OutlinedInput from '@material-ui/core/OutlinedInput';
5 | import FilledInput from '@material-ui/core/FilledInput';
6 | import InputLabel from '@material-ui/core/InputLabel';
7 | import MenuItem from '@material-ui/core/MenuItem';
8 | import FormHelperText from '@material-ui/core/FormHelperText';
9 | import FormControl from '@material-ui/core/FormControl';
10 | import Select from '@material-ui/core/Select';
11 | import { TextField } from 'formik-material-ui';
12 |
13 | const organizationLinkType = [
14 | { type: 'UrlPrivacyPolicy', niceName: 'Privacy Policy' },
15 | { type: 'UrlSupport', niceName: 'Support' },
16 | { type: 'UrlSales', niceName: 'Sales' },
17 | { type: 'UrlTermsOfService', niceName: 'TOS' },
18 | { type: 'UrlTwitter', niceName: 'Twitter' },
19 | { type: 'UrlLinkedIn', niceName: 'LinkedIn' },
20 | { type: 'UrlFacebook', niceName: 'Facebook' },
21 | { type: 'UrlCrunchbase', niceName: 'Crunchbase' },
22 | { type: 'UrlAngellist', niceName: 'Angellist' },
23 | { type: 'UrlWebsite', niceName: 'Website' },
24 | { type: 'EmailSales', niceName: 'Sales Email' },
25 | { type: 'EmailSupport', niceName: 'Support Email' },
26 | { type: 'Other', niceName: 'Other' },
27 | ];
28 |
29 | export default function SimpleSelect({
30 | classes,
31 | field,
32 | form,
33 | values,
34 | styles,
35 | label = 'Link Type',
36 | options = organizationLinkType,
37 | }) {
38 | const inputLabel = React.useRef(null);
39 | const [labelWidth, setLabelWidth] = React.useState(0);
40 | React.useEffect(() => {
41 | setLabelWidth(inputLabel.current.offsetWidth);
42 | }, []);
43 |
44 | return (
45 |
46 |
47 | {label}
48 |
49 |
53 | }
54 | >
55 | {options.map(item => {
56 | return (
57 |
60 | );
61 | })}
62 |
63 |
64 | );
65 | }
66 |
--------------------------------------------------------------------------------
/src/apps/admin/components/select/helpers/getSuggestions.js:
--------------------------------------------------------------------------------
1 | import deburr from 'lodash/deburr';
2 |
3 | export function getSuggestions(value, suggestions, { showEmpty = false } = {}) {
4 | const inputValue = deburr(value.trim()).toLowerCase();
5 | const inputLength = inputValue.length;
6 | let count = 0;
7 |
8 | return inputLength === 0 && !showEmpty
9 | ? []
10 | : suggestions.filter(suggestion => {
11 | const keep =
12 | count < 5 &&
13 | suggestion.label.slice(0, inputLength).toLowerCase() === inputValue;
14 |
15 | if (keep) {
16 | count += 1;
17 | }
18 |
19 | return keep;
20 | });
21 | }
22 |
--------------------------------------------------------------------------------
/src/apps/admin/components/select/helpers/index.js:
--------------------------------------------------------------------------------
1 | export { renderInput } from './renderInput';
2 | export { renderSuggestion } from './renderSuggestions';
3 | export { getSuggestions } from './getSuggestions';
4 |
--------------------------------------------------------------------------------
/src/apps/admin/components/select/helpers/renderInput.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import TextField from '@material-ui/core/TextField';
3 | import PropTypes from 'prop-types';
4 |
5 | export function renderInput(inputProps) {
6 | const { InputProps, classes, ref, ...other } = inputProps;
7 |
8 | return (
9 |
20 | );
21 | }
22 |
23 | export default renderInput;
24 |
--------------------------------------------------------------------------------
/src/apps/admin/components/select/helpers/renderSuggestions.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import MenuItem from '@material-ui/core/MenuItem';
4 |
5 | export function renderSuggestion(suggestionProps) {
6 | const {
7 | suggestion,
8 | index,
9 | itemProps,
10 | highlightedIndex,
11 | selectedItem,
12 | } = suggestionProps;
13 | const isHighlighted = highlightedIndex === index;
14 | const isSelected = (selectedItem || '').indexOf(suggestion.label) > -1;
15 |
16 | return (
17 |
29 | );
30 | }
31 | renderSuggestion.propTypes = {
32 | highlightedIndex: PropTypes.number,
33 | index: PropTypes.number,
34 | itemProps: PropTypes.object,
35 | selectedItem: PropTypes.string,
36 | suggestion: PropTypes.shape({ label: PropTypes.string }).isRequired,
37 | };
38 |
39 | export default renderSuggestion;
40 |
--------------------------------------------------------------------------------
/src/apps/admin/components/select/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import deburr from 'lodash/deburr';
4 | import Downshift from 'downshift';
5 | import { makeStyles } from '@material-ui/styles';
6 | import Paper from '@material-ui/core/Paper';
7 | import { renderInput, renderSuggestion, getSuggestions } from './helpers';
8 |
9 | export function Select({
10 | setFieldValue,
11 | classes,
12 | options,
13 | name = '',
14 | InputLabelProps,
15 | InputProps,
16 | label = '',
17 | placeholder = '',
18 | ...props
19 | }) {
20 | const [inputValue, setInputValue] = React.useState('');
21 | const [selectedItem, setSelectedItem] = React.useState([]);
22 |
23 | React.useEffect(() => {
24 | setFieldValue(name, selectedItem, false);
25 | }, [selectedItem]);
26 |
27 | function handleKeyDown(event) {
28 | if (
29 | selectedItem.length &&
30 | !inputValue.length &&
31 | event.key === 'Backspace'
32 | ) {
33 | setSelectedItem(selectedItem.slice(0, selectedItem.length - 1));
34 | }
35 | }
36 |
37 | function handleInputChange(event) {
38 | setInputValue(event.target.value);
39 | }
40 |
41 | function handleChange(item) {
42 | let newSelectedItem = [...selectedItem];
43 | if (newSelectedItem.indexOf(item) === -1) {
44 | newSelectedItem = [...newSelectedItem, item];
45 | }
46 | setInputValue(item.label);
47 | setSelectedItem(newSelectedItem);
48 | }
49 |
50 | const handleDelete = item => () => {
51 | const newSelectedItem = [...selectedItem];
52 | newSelectedItem.splice(newSelectedItem.indexOf(item), 1);
53 | setSelectedItem(newSelectedItem);
54 | };
55 |
56 | return (
57 |
62 | {({
63 | getInputProps,
64 | getItemProps,
65 | getLabelProps,
66 | isOpen,
67 | inputValue,
68 | selectedItem,
69 | highlightedIndex,
70 | }) => {
71 | const { onBlur, onChange, onFocus, ...inputProps } = getInputProps({
72 | onKeyDown: handleKeyDown,
73 | placeholder: placeholder,
74 | });
75 | return (
76 |
77 | {renderInput({
78 | fullwidth: props.fullwidth || true,
79 | classes,
80 | name,
81 | label,
82 | InputLabelProps: getLabelProps(),
83 | InputProps: {
84 | onBlur,
85 | onChange: event => {
86 | handleInputChange(event);
87 | onChange(event);
88 | },
89 | onFocus,
90 | },
91 | inputProps,
92 | })}
93 | {isOpen ? (
94 |
95 | {getSuggestions(inputValue, options).map((suggestion, index) =>
96 | renderSuggestion({
97 | suggestion,
98 | index,
99 | itemProps: getItemProps({ item: suggestion }),
100 | highlightedIndex,
101 | selectedItem: selectedItem,
102 | })
103 | )}
104 |
105 | ) : null}
106 |
107 | );
108 | }}
109 |
110 | );
111 | }
112 |
113 | export default Select;
114 |
--------------------------------------------------------------------------------
/src/apps/admin/config/index.js:
--------------------------------------------------------------------------------
1 | export * from './styles';
2 |
--------------------------------------------------------------------------------
/src/apps/admin/features/afffiliation.create/components/companySearch/helpers/getSuggestion.js:
--------------------------------------------------------------------------------
1 | import deburr from 'lodash/deburr';
2 |
3 | export function getSuggestions({ value, data }) {
4 | const inputValue = deburr(value.trim()).toLowerCase();
5 | const inputLength = inputValue.length;
6 | let count = 0;
7 |
8 | return inputLength === 0
9 | ? []
10 | : data.filter(suggestion => {
11 | const keep =
12 | count < 5 &&
13 | suggestion.name &&
14 | suggestion.name[0].payload.slice(0, inputLength).toLowerCase() ===
15 | inputValue;
16 |
17 | if (keep) {
18 | count += 1;
19 | }
20 |
21 | return keep;
22 | });
23 | }
24 |
--------------------------------------------------------------------------------
/src/apps/admin/features/afffiliation.create/components/companySearch/helpers/index.js:
--------------------------------------------------------------------------------
1 | export { renderSuggestion } from './renderSuggestion';
2 | export { renderInput } from './renderInput';
3 | export { getSuggestions } from './getSuggestion';
4 |
--------------------------------------------------------------------------------
/src/apps/admin/features/afffiliation.create/components/companySearch/helpers/renderInput.js:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import TextField from '@material-ui/core/TextField';
3 |
4 | export function renderInput(inputProps) {
5 | const { InputProps, classes, ref, error, ...other } = inputProps;
6 |
7 | return (
8 |
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/src/apps/admin/features/afffiliation.create/components/companySearch/helpers/renderSuggestion.js:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import MenuItem from '@material-ui/core/MenuItem';
3 |
4 | export function renderSuggestion({
5 | suggestion,
6 | index,
7 | itemProps,
8 | highlightedIndex,
9 | selectedItem,
10 | }) {
11 | const isHighlighted = highlightedIndex === index;
12 | const isSelected = (selectedItem || '').indexOf(suggestion.label) > -1;
13 |
14 | return (
15 |
26 | );
27 | }
28 |
--------------------------------------------------------------------------------
/src/apps/admin/features/afffiliation.create/components/index.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codexstanford/techlist-frontend-web/06be56c7f1bbaa34e65ba942a24fe29257ae4b7f/src/apps/admin/features/afffiliation.create/components/index.js
--------------------------------------------------------------------------------
/src/apps/admin/features/afffiliation.create/components/select.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { makeStyles } from '@material-ui/core/styles';
3 | import Input from '@material-ui/core/Input';
4 | import OutlinedInput from '@material-ui/core/OutlinedInput';
5 | import FilledInput from '@material-ui/core/FilledInput';
6 | import InputLabel from '@material-ui/core/InputLabel';
7 | import MenuItem from '@material-ui/core/MenuItem';
8 | import FormHelperText from '@material-ui/core/FormHelperText';
9 | import FormControl from '@material-ui/core/FormControl';
10 | import Select from '@material-ui/core/Select';
11 | import { TextField } from 'formik-material-ui';
12 | import styled from 'styled-components';
13 |
14 | const organizationLinkType = [
15 | { type: 'UrlPrivacyPolicy', niceName: 'Privacy Policy' },
16 | { type: 'UrlSupport', niceName: 'Support' },
17 | { type: 'UrlSales', niceName: 'Sales' },
18 | { type: 'UrlTermsOfService', niceName: 'TOS' },
19 | { type: 'UrlTwitter', niceName: 'Twitter' },
20 | { type: 'UrlLinkedIn', niceName: 'LinkedIn' },
21 | { type: 'UrlFacebook', niceName: 'Facebook' },
22 | { type: 'UrlCrunchbase', niceName: 'Crunchbase' },
23 | { type: 'UrlAngellist', niceName: 'Angellist' },
24 | { type: 'UrlWebsite', niceName: 'Website' },
25 | { type: 'EmailSales', niceName: 'Sales Email' },
26 | { type: 'EmailSupport', niceName: 'Support Email' },
27 | { type: 'Other', niceName: 'Other' },
28 | ];
29 |
30 | export default function SimpleSelect({
31 | classes,
32 | field,
33 | form,
34 | values,
35 | styles,
36 | label = 'Link Type',
37 | options = organizationLinkType,
38 | }) {
39 | const inputLabel = React.useRef(null);
40 | const [labelWidth, setLabelWidth] = React.useState(0);
41 | React.useEffect(() => {
42 | setLabelWidth(inputLabel.current.offsetWidth);
43 | }, []);
44 |
45 | return (
46 |
47 |
48 | {label}
49 |
50 |
54 | }
55 | >
56 | {options.map(item => {
57 | return (
58 |
61 | );
62 | })}
63 |
64 |
65 | );
66 | }
67 |
68 | const StyledFormControl = styled(FormControl)`
69 | padding-right: 1rem;
70 | min-width: 150px;
71 | margin-top: 15px;
72 | `;
73 |
--------------------------------------------------------------------------------
/src/apps/admin/features/afffiliation.create/graphql/index.js:
--------------------------------------------------------------------------------
1 | export * from './mutations';
2 | export * from './queries';
3 |
--------------------------------------------------------------------------------
/src/apps/admin/features/afffiliation.create/graphql/mutations.js:
--------------------------------------------------------------------------------
1 | import gql from 'graphql-tag';
2 |
3 | export const CREATE_AFFILIATION_MUTATION = gql`
4 | mutation CreateAffiliation($data: PersonOrganizationAffiliationCreateInput!) {
5 | createAffiliation(data: $data) {
6 | __typename
7 | id
8 | description
9 | role
10 | fromDate
11 | throughDate
12 | title
13 | updatedAt
14 | organization {
15 | id
16 | name {
17 | payload
18 | fromDate
19 | throughDate
20 | }
21 | }
22 | person {
23 | __typename
24 | id
25 | affiliation {
26 | __typename
27 | id
28 | fromDate
29 | throughDate
30 | title
31 | role
32 | description
33 | organization {
34 | id
35 | name {
36 | payload
37 | }
38 | logo {
39 | payload
40 | }
41 | }
42 | }
43 | }
44 |
45 | metadata {
46 | isDraft
47 | isPublic
48 | isRejected
49 | isUnverified
50 | isVerified
51 | isApproved
52 | isPendingReview
53 | }
54 | }
55 | }
56 | `;
57 |
--------------------------------------------------------------------------------
/src/apps/admin/features/afffiliation.create/graphql/queries.js:
--------------------------------------------------------------------------------
1 | import gql from 'graphql-tag';
2 |
3 | export const GET_COMPANY_TARGET_MARKETS = gql`
4 | query GetTargetMarketsQuery {
5 | organizationTargetMarkets {
6 | id
7 | payload
8 | }
9 | }
10 | `;
11 |
--------------------------------------------------------------------------------
/src/apps/admin/features/afffiliation.create/helpers/checkCanFormSubmit.js:
--------------------------------------------------------------------------------
1 | import { getCurrentErrors } from './getCurrentErrors';
2 |
3 | export const checkCanFormSubmit = (touched, errors) => {
4 | if (Object.keys(touched).length < 1) {
5 | return true;
6 | }
7 |
8 | if (getCurrentErrors(touched, errors).length > 0) {
9 | return true;
10 | }
11 |
12 | return false;
13 | };
14 |
--------------------------------------------------------------------------------
/src/apps/admin/features/afffiliation.create/helpers/checkForSectionErrors.js:
--------------------------------------------------------------------------------
1 | import { getCurrentErrors } from './getCurrentErrors';
2 |
3 | export const checkForSectionErrors = (touched, errors, checkArray) => {
4 | if (getCurrentErrors(touched, errors).length > 0) {
5 | return getCurrentErrors(touched, errors).every(i => {
6 | if (checkArray.includes(i)) {
7 | return true;
8 | } else {
9 | return false;
10 | }
11 | });
12 | } else {
13 | return false;
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/src/apps/admin/features/afffiliation.create/helpers/getCurrentErrors.js:
--------------------------------------------------------------------------------
1 | export const getCurrentErrors = (touched, errors) =>
2 | Object.keys(touched).filter(element => Object.keys(errors).includes(element));
3 |
--------------------------------------------------------------------------------
/src/apps/admin/features/afffiliation.create/helpers/getInitialValues.js:
--------------------------------------------------------------------------------
1 | export function getInitialValues(initialCompany) {
2 | return {
3 | organizationID: initialCompany.id,
4 | };
5 | }
6 |
--------------------------------------------------------------------------------
/src/apps/admin/features/afffiliation.create/helpers/handleCreateAffiliation.js:
--------------------------------------------------------------------------------
1 | import { navigate } from '@reach/router';
2 |
3 | const defaultCreateAffiliationMetadata = {
4 | isDraft: true,
5 | isPublic: false,
6 | isUnverified: false,
7 | isApproved: false,
8 | isPendingReview: false,
9 | };
10 |
11 | export function handleCreateAffiliation(props) {
12 | const { mutation, user, handleClose, ...rest } = props;
13 | return async (values, { setSubmitting }) => {
14 | try {
15 | const result = await mutation({
16 | // Reinstate when mutation is in place on backend
17 | variables: {
18 | data: {
19 | fromDate: new Date(),
20 | person: {
21 | connect: {
22 | id: user.person.id,
23 | },
24 | },
25 | organization: {
26 | connect: {
27 | id: values.organizationID,
28 | },
29 | },
30 |
31 | metadata: {
32 | create: defaultCreateAffiliationMetadata,
33 | },
34 | },
35 | },
36 | });
37 | setSubmitting(false);
38 | handleClose();
39 | navigate('/app/profile/');
40 | } catch (error) {
41 | console.log(error);
42 | }
43 | };
44 | }
45 |
--------------------------------------------------------------------------------
/src/apps/admin/features/afffiliation.create/helpers/index.js:
--------------------------------------------------------------------------------
1 | export * from './handleCreateAffiliation';
2 | export * from './getInitialValues';
3 | export * from './renderFormHeader';
4 | export * from './checkCanFormSubmit';
5 | export * from './getCurrentErrors';
6 | export * from './checkForSectionErrors';
7 | export * from './validationSchema';
8 |
--------------------------------------------------------------------------------
/src/apps/admin/features/afffiliation.create/helpers/renderFormHeader.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import Typography from '@material-ui/core/Typography';
4 |
5 | export function CodeXFormHeader({
6 | text,
7 | variant = 'h5',
8 | color = 'primary',
9 | weight = '700',
10 | }) {
11 | return (
12 |
21 | {text}
22 |
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/src/apps/admin/features/afffiliation.create/helpers/validationSchema.js:
--------------------------------------------------------------------------------
1 | import * as Yup from 'yup';
2 |
3 | export const validationSchema = Yup.object().shape({
4 | fromDate: Yup.date().required('From Date is required.'),
5 | throughDate: Yup.date().required('Through Date is required.'),
6 | role: Yup.string().required('Role is required.'),
7 | title: Yup.string().required('Title is required.'),
8 | });
9 |
--------------------------------------------------------------------------------
/src/apps/admin/features/company.create/components/basics.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import CodeXTextField from '../../../components/codex.textinput';
3 | import CompanyTargetMarketSelect from './select';
4 | import { Field } from 'formik';
5 | import styled from 'styled-components';
6 | import formatCategory from './categories/helpers/formatCategory';
7 |
8 | export function Basics({
9 | errors,
10 | touched,
11 | classes,
12 | targetMarkets,
13 | handleBlur,
14 | ...rest
15 | }) {
16 | return (
17 |
24 |
32 |
41 |
42 |
51 |
52 | 0
60 | ? targetMarkets.organizationTargetMarkets.map(t => ({
61 | type: t.id,
62 | niceName: formatCategory(t.payload),
63 | }))
64 | : []
65 | }
66 | label="Target Markets"
67 | />
68 |
69 |
70 |
71 | );
72 | }
73 |
74 | const FlexLayoutMobile = styled.div`
75 | display: flex;
76 | @media (max-width: 480px) {
77 | flex-direction: column;
78 | }
79 | `;
80 |
81 | const TargetMarketsWrapper = styled.div`
82 | margin-left: 2rem;
83 | @media (max-width: 480px) {
84 | margin-left: 0;
85 | }
86 | `;
87 |
--------------------------------------------------------------------------------
/src/apps/admin/features/company.create/components/categories/helpers/formatCategory.js:
--------------------------------------------------------------------------------
1 | const formatCategory = str => {
2 | if (str.toUpperCase() !== str) {
3 | return str.toUpperCase().replace(/ /g, '_');
4 | } else {
5 | return str
6 | .toLowerCase()
7 | .replace(/[^0-9a-z]/gi, ' ')
8 | .replace(/(?:^|\s)\S/g, function(a) {
9 | return a.toUpperCase();
10 | });
11 | }
12 | };
13 |
14 | export default formatCategory;
15 |
--------------------------------------------------------------------------------
/src/apps/admin/features/company.create/components/categories/helpers/getSuggestions.js:
--------------------------------------------------------------------------------
1 | import deburr from 'lodash/deburr';
2 |
3 | export function getSuggestions(value, suggestions, { showEmpty = false } = {}) {
4 | const inputValue = deburr(value.trim()).toLowerCase();
5 | const inputLength = inputValue.length;
6 | let count = 0;
7 |
8 | return inputLength === 0 && !showEmpty
9 | ? []
10 | : suggestions.filter(suggestion => {
11 | const keep =
12 | count < 5 &&
13 | suggestion.label.slice(0, inputLength).toLowerCase() === inputValue;
14 |
15 | if (keep) {
16 | count += 1;
17 | }
18 |
19 | return keep;
20 | });
21 | }
22 |
--------------------------------------------------------------------------------
/src/apps/admin/features/company.create/components/categories/helpers/index.js:
--------------------------------------------------------------------------------
1 | export { renderInput } from './renderInput';
2 | export { renderSuggestion } from './renderSuggestions';
3 | export { getSuggestions } from './getSuggestions';
4 |
--------------------------------------------------------------------------------
/src/apps/admin/features/company.create/components/categories/helpers/renderInput.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import TextField from '@material-ui/core/TextField';
3 | import PropTypes from 'prop-types';
4 |
5 | export function renderInput(inputProps) {
6 | const { InputProps, classes, ref, ...other } = inputProps;
7 |
8 | const { onBlur } = InputProps;
9 |
10 | return (
11 |
23 | );
24 | }
25 |
26 | export default renderInput;
27 |
--------------------------------------------------------------------------------
/src/apps/admin/features/company.create/components/categories/helpers/renderSuggestions.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import MenuItem from '@material-ui/core/MenuItem';
3 | import PropTypes from 'prop-types';
4 | import formatCategory from './formatCategory';
5 |
6 | export function renderSuggestion(suggestionProps) {
7 | const {
8 | suggestion,
9 | index,
10 | itemProps,
11 | highlightedIndex,
12 | selectedItem,
13 | } = suggestionProps;
14 | const isHighlighted = highlightedIndex === index;
15 | const isSelected = (selectedItem || '').indexOf(suggestion.label) > -1;
16 |
17 | return (
18 |
30 | );
31 | }
32 | renderSuggestion.propTypes = {
33 | highlightedIndex: PropTypes.number,
34 | index: PropTypes.number,
35 | itemProps: PropTypes.object,
36 | selectedItem: PropTypes.string,
37 | suggestion: PropTypes.shape({ label: PropTypes.string }).isRequired,
38 | };
39 |
40 | export default renderSuggestion;
41 |
--------------------------------------------------------------------------------
/src/apps/admin/features/company.create/components/formErrorMessage.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import FormHelperText from '@material-ui/core/FormHelperText';
3 | import { getDisplayedErrorMessage } from '../../../features/company.create/helpers';
4 | import styled from 'styled-components';
5 |
6 | const FormErrorMessage = ({ touched, errors }) => {
7 | if (getDisplayedErrorMessage(touched, errors) !== null) {
8 | return (
9 |
10 |
11 | Error in {getDisplayedErrorMessage(touched, errors).section} section.
12 |
13 |
14 | {getDisplayedErrorMessage(touched, errors).message}
15 |
16 |
17 | );
18 | } else {
19 | return null;
20 | }
21 | };
22 |
23 | const ErrorMessageContainer = styled.div`
24 | display: flex;
25 | flex-direction: column;
26 | justify-content: center;
27 | align-items: center;
28 | margin-top: 0;
29 | margin-bottom: 1rem;
30 | `;
31 |
32 | export default FormErrorMessage;
33 |
--------------------------------------------------------------------------------
/src/apps/admin/features/company.create/components/index.js:
--------------------------------------------------------------------------------
1 | export * from './basics';
2 | export * from './logo';
3 | export * from './location/';
4 | export * from './links/';
5 | export * from './categories/';
6 |
--------------------------------------------------------------------------------
/src/apps/admin/features/company.create/components/links/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Field } from 'formik';
3 | import CompanyLinksInput from './links.input';
4 |
5 | export function Links({ setFieldValue, setValues, values, ...rest }) {
6 | return (
7 |
14 |
20 |
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/src/apps/admin/features/company.create/components/location/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Field } from 'formik';
3 | import CodeXTextField from '../../../../components/codex.textinput';
4 | import AddressField from './address';
5 | import { CompanyLocationMap } from '../../../../../../templates/company/locationmap';
6 |
7 | export function Location({
8 | setFieldValue,
9 | setValues,
10 | values,
11 | handleBlur,
12 | ...rest
13 | }) {
14 | return (
15 |
22 |
28 | {values.locationjson && values.locationjson.geometry ? (
29 |
33 | ) : null}
34 |
35 | );
36 | }
37 |
--------------------------------------------------------------------------------
/src/apps/admin/features/company.create/components/location/locationPaper.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Paper from '@material-ui/core/Paper';
3 | import withStyles from '@material-ui/core/styles/withStyles';
4 |
5 | const styles = theme => ({
6 | paper: {
7 | marginTop: 10,
8 | display: 'flex',
9 | flexDirection: 'column',
10 | alignItems: 'center',
11 | padding: `${theme.spacing(2)}px ${theme.spacing(3)}px ${theme.spacing(
12 | 3
13 | )}px`,
14 | // [theme.breakpoints.up(450 + theme.spacing.unit * 3 * 2)]: {
15 | // marginTop: theme.spacing.unit * 8,
16 | // },
17 | },
18 | });
19 |
20 | const LocationPaper = ({
21 | classes,
22 | suggestions,
23 | highlightedIndex,
24 | selectedItem,
25 | getItemProps,
26 | renderSuggestion,
27 | ...props
28 | }) => {
29 | return (
30 |
35 | {suggestions.map((suggestion, index) =>
36 | renderSuggestion({
37 | suggestion,
38 | index,
39 | itemProps: getItemProps({
40 | item: suggestion,
41 | }),
42 | highlightedIndex,
43 | selectedItem,
44 | })
45 | )}
46 |
47 | );
48 | };
49 |
50 | export default withStyles(styles)(LocationPaper);
51 |
--------------------------------------------------------------------------------
/src/apps/admin/features/company.create/components/logo.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import CodeXTextField from '../../../components/codex.textinput';
3 | import Fab from '@material-ui/core/Fab';
4 | import Avatar from '@material-ui/core/Avatar';
5 | import styled from 'styled-components';
6 |
7 | export function Logo({
8 | errors,
9 | touched,
10 | classes,
11 | setImage,
12 | setFieldValue,
13 | handleBlur,
14 | values,
15 | ...rest
16 | }) {
17 | return (
18 |
19 | {
23 | e.stopPropagation();
24 | e.preventDefault();
25 |
26 | const fileReader = new FileReader();
27 | fileReader.onloadend = e => {
28 | const content = fileReader.result;
29 | setFieldValue('logo', content);
30 | setImage(content);
31 | };
32 | if (e.target.files.length > 0) {
33 | fileReader.readAsDataURL(e.target.files[0]);
34 | }
35 | }}
36 | />
37 |
38 |
39 |
56 |
57 |
58 |
59 | );
60 | }
61 |
62 | const InputContainer = styled.div`
63 | width: 100%;
64 | `;
65 |
66 | const InputLabel = styled.label`
67 | display: flex;
68 | justify-content: center;
69 | min-width: 250px;
70 | min-height: 200px;
71 | `;
72 |
73 | const StyledFab = styled(Fab)`
74 | min-width: 250px;
75 | min-height: 200px;
76 | border-radius: 5px;
77 | `;
78 |
79 | const StyledInput = styled.input.attrs({
80 | type: 'file',
81 | id: 'logo',
82 | accept: 'image/*',
83 | })`
84 | width: 0.1px;
85 | height: 0.1px;
86 | opacity: 0;
87 | overflow: hidden;
88 | position: absolute;
89 | z-index: -1;
90 | `;
91 |
--------------------------------------------------------------------------------
/src/apps/admin/features/company.create/components/select.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { makeStyles } from '@material-ui/core/styles';
3 | import Input from '@material-ui/core/Input';
4 | import OutlinedInput from '@material-ui/core/OutlinedInput';
5 | import FilledInput from '@material-ui/core/FilledInput';
6 | import InputLabel from '@material-ui/core/InputLabel';
7 | import MenuItem from '@material-ui/core/MenuItem';
8 | import FormHelperText from '@material-ui/core/FormHelperText';
9 | import FormControl from '@material-ui/core/FormControl';
10 | import Select from '@material-ui/core/Select';
11 | import { TextField } from 'formik-material-ui';
12 | import styled from 'styled-components';
13 |
14 | const organizationLinkType = [
15 | { type: 'UrlPrivacyPolicy', niceName: 'Privacy Policy' },
16 | { type: 'UrlSupport', niceName: 'Support' },
17 | { type: 'UrlSales', niceName: 'Sales' },
18 | { type: 'UrlTermsOfService', niceName: 'TOS' },
19 | { type: 'UrlTwitter', niceName: 'Twitter' },
20 | { type: 'UrlLinkedIn', niceName: 'LinkedIn' },
21 | { type: 'UrlFacebook', niceName: 'Facebook' },
22 | { type: 'UrlCrunchbase', niceName: 'Crunchbase' },
23 | { type: 'UrlAngellist', niceName: 'Angellist' },
24 | { type: 'UrlWebsite', niceName: 'Website' },
25 | { type: 'EmailSales', niceName: 'Sales Email' },
26 | { type: 'EmailSupport', niceName: 'Support Email' },
27 | { type: 'Other', niceName: 'Other' },
28 | ];
29 |
30 | export default function SimpleSelect({
31 | classes,
32 | field,
33 | form,
34 | values,
35 | styles,
36 | label = 'Link Type',
37 | options = organizationLinkType,
38 | }) {
39 | const inputLabel = React.useRef(null);
40 | const [labelWidth, setLabelWidth] = React.useState(0);
41 | React.useEffect(() => {
42 | setLabelWidth(inputLabel.current.offsetWidth);
43 | }, []);
44 |
45 | return (
46 |
47 |
48 | {label}
49 |
50 |
59 | }
60 | >
61 | {options.map(item => {
62 | return (
63 |
66 | );
67 | })}
68 |
69 |
70 | );
71 | }
72 |
73 | const StyledFormControl = styled(FormControl)`
74 | padding-right: 1rem;
75 | min-width: 175px;
76 | margin-top: 15px;
77 | `;
78 |
--------------------------------------------------------------------------------
/src/apps/admin/features/company.create/graphql/index.js:
--------------------------------------------------------------------------------
1 | export * from './mutations';
2 | export * from './queries';
3 |
--------------------------------------------------------------------------------
/src/apps/admin/features/company.create/graphql/mutations.js:
--------------------------------------------------------------------------------
1 | import gql from 'graphql-tag';
2 |
3 | export const CREATE_COMPANY_MUTATION = gql`
4 | mutation CreateCompany($data: OrganizationCreateInput!) {
5 | createOrganization(data: $data) {
6 | __typename
7 | id
8 | name {
9 | payload
10 | fromDate
11 | throughDate
12 | }
13 | description
14 | yearFounded
15 | location {
16 | __typename
17 | id
18 | formatted_address
19 | geometry
20 | }
21 | affiliation {
22 | __typename
23 | id
24 | fromDate
25 | person {
26 | __typename
27 | id
28 | }
29 | }
30 | admins {
31 | id
32 | person {
33 | id
34 | }
35 | }
36 | metadata {
37 | isDraft
38 | isPublic
39 | isRejected
40 | isUnverified
41 | isVerified
42 | isApproved
43 | isPendingReview
44 | }
45 | }
46 | }
47 | `;
48 |
--------------------------------------------------------------------------------
/src/apps/admin/features/company.create/graphql/queries.js:
--------------------------------------------------------------------------------
1 | import gql from 'graphql-tag';
2 |
3 | export const GET_COMPANY_TARGET_MARKETS = gql`
4 | query GetTargetMarketsQuery {
5 | organizationTargetMarkets {
6 | id
7 | payload
8 | }
9 | }
10 | `;
11 |
--------------------------------------------------------------------------------
/src/apps/admin/features/company.create/helpers/checkCanFormSubmit.js:
--------------------------------------------------------------------------------
1 | import { getCurrentErrors } from './getCurrentErrors';
2 |
3 | export const checkCanFormSubmit = (touched, errors) => {
4 | if (Object.keys(touched).length < 1) {
5 | return true;
6 | }
7 |
8 | if (getCurrentErrors(touched, errors).length > 0) {
9 | return true;
10 | }
11 |
12 | return false;
13 | };
14 |
--------------------------------------------------------------------------------
/src/apps/admin/features/company.create/helpers/checkForSectionErrors.js:
--------------------------------------------------------------------------------
1 | import { getCurrentErrors } from './getCurrentErrors';
2 |
3 | export const checkForSectionErrors = (touched, errors, checkArray) => {
4 | if (getCurrentErrors(touched, errors).length > 0) {
5 | return getCurrentErrors(touched, errors).every(i => {
6 | if (checkArray.includes(i)) {
7 | return true;
8 | } else {
9 | return false;
10 | }
11 | });
12 | } else {
13 | return false;
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/src/apps/admin/features/company.create/helpers/getCurrentErrors.js:
--------------------------------------------------------------------------------
1 | export const getCurrentErrors = (touched, errors) =>
2 | Object.keys(touched).filter(element => Object.keys(errors).includes(element));
3 |
--------------------------------------------------------------------------------
/src/apps/admin/features/company.create/helpers/getDisplayedErrorMessage.js:
--------------------------------------------------------------------------------
1 | import { getCurrentErrors } from './getCurrentErrors';
2 |
3 | export const getDisplayedErrorMessage = (touched, errors) => {
4 | let error = { section: null, message: null };
5 |
6 | switch (getCurrentErrors(touched, errors)[0]) {
7 | case 'name':
8 | error = { section: 'Basics', message: errors.name };
9 | break;
10 | case 'description':
11 | error = { section: 'Basics', message: errors.description };
12 | break;
13 | case 'yearFounded':
14 | error = { section: 'Basics', message: errors.yearFounded };
15 | break;
16 | case 'targetMarkets':
17 | error = { section: 'Basics', message: errors.targetMarkets };
18 | break;
19 | default:
20 | error = null;
21 | }
22 | return error;
23 | };
24 |
--------------------------------------------------------------------------------
/src/apps/admin/features/company.create/helpers/getInitialValues.js:
--------------------------------------------------------------------------------
1 | export function getInitialValues(props) {
2 | return {
3 | name: '',
4 | yearFounded: new Date().toISOString().split('T')[0],
5 | description: '',
6 | locationjson: {},
7 | targetMarkets: '',
8 | logo: '',
9 | links: [
10 | { type: 'UrlWebsite', payload: '' },
11 | { type: 'UrlTwitter', payload: '' },
12 | { type: 'UrlCrunchbase', payload: '' },
13 | ],
14 | };
15 | }
16 |
--------------------------------------------------------------------------------
/src/apps/admin/features/company.create/helpers/handleCreateCompany.js:
--------------------------------------------------------------------------------
1 | import { navigate } from '@reach/router';
2 |
3 | const defaultCreateCompanyMetadata = {
4 | isDraft: true,
5 | isPublic: false,
6 | isUnverified: false,
7 | isApproved: false,
8 | isPendingReview: false,
9 | };
10 |
11 | export function handleCreateCompany(props) {
12 | const { mutation, user, handleClose, ...rest } = props;
13 | return async (
14 | {
15 | name,
16 | description,
17 | yearFounded,
18 | location,
19 | locationjson,
20 | links,
21 | logo,
22 | targetMarkets,
23 | categories,
24 | },
25 | { setSubmitting }
26 | ) => {
27 | const { formatted_address, geometry, place_id } = locationjson;
28 |
29 | try {
30 | const result = await mutation({
31 | variables: {
32 | data: {
33 | categories: {
34 | connect: categories && categories.map(cat => ({ id: cat.value })),
35 | },
36 | logo: {
37 | create: {
38 | payload: logo,
39 | fromDate: new Date(),
40 | isPrimary: true,
41 | isPublic: true,
42 | isDefault: true,
43 | },
44 | },
45 | name: {
46 | create: {
47 | payload: name,
48 | fromDate: new Date(),
49 | },
50 | },
51 | targetMarkets: {
52 | connect: {
53 | id: targetMarkets,
54 | },
55 | },
56 | description,
57 | yearFounded,
58 | location: {
59 | create: {
60 | formatted_address,
61 | geometry,
62 | placeId: place_id,
63 | },
64 | },
65 | links: {
66 | create: links.map(link => {
67 | return {
68 | fromDate: new Date(),
69 | payload: link.payload,
70 | type: link.type,
71 | };
72 | }),
73 | },
74 | affiliation: {
75 | create: {
76 | fromDate: new Date(),
77 | person: {
78 | connect: {
79 | id: user.person.id,
80 | },
81 | },
82 | },
83 | },
84 | admins: {
85 | connect: {
86 | id: user.id,
87 | },
88 | },
89 | metadata: {
90 | create: defaultCreateCompanyMetadata,
91 | },
92 | },
93 | },
94 | });
95 | setSubmitting(false);
96 | handleClose();
97 | navigate('/app/profile/');
98 | } catch (error) {
99 | console.log(error);
100 | }
101 | };
102 | }
103 |
--------------------------------------------------------------------------------
/src/apps/admin/features/company.create/helpers/index.js:
--------------------------------------------------------------------------------
1 | export * from './handleCreateCompany';
2 | export * from './renderExpansionPanel';
3 | export * from './getInitialValues';
4 | export * from './renderFormHeader';
5 | export * from './checkCanFormSubmit';
6 | export * from './getDisplayedErrorMessage';
7 | export * from './getCurrentErrors';
8 | export * from './checkForSectionErrors';
9 | export * from './validationSchema';
10 |
--------------------------------------------------------------------------------
/src/apps/admin/features/company.create/helpers/renderExpansionPanel.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import ExpansionPanel from '@material-ui/core/ExpansionPanel';
4 | import ExpansionPanelSummary from '@material-ui/core/ExpansionPanelSummary';
5 | import ExpansionPanelDetails from '@material-ui/core/ExpansionPanelDetails';
6 | import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
7 | import Typography from '@material-ui/core/Typography';
8 |
9 | export function CodeXExpansionPanel({
10 | children,
11 | defaultExpanded,
12 | expandIcon,
13 | title,
14 | titleColor,
15 | titleComponent,
16 | titleStyle,
17 | titleVariant,
18 | error,
19 | style,
20 | ...props
21 | }) {
22 | return (
23 |
27 |
28 |
34 | {title}
35 |
36 |
37 | {children}
38 |
39 | );
40 | }
41 |
42 | CodeXExpansionPanel.propTypes = {
43 | title: PropTypes.string.isRequired,
44 | titleStyle: PropTypes.object,
45 | titleColor: PropTypes.string,
46 | titleVariant: PropTypes.string,
47 | expandIcon: PropTypes.element,
48 | };
49 |
50 | CodeXExpansionPanel.defaultProps = {
51 | title: '',
52 | titleStyle: {
53 | fontWeight: '800',
54 | letterSpacing: '-.5px',
55 | textDecoration: 'none',
56 | },
57 | titleColor: 'primary',
58 | titleComponent: 'h6',
59 | titleVariant: 'h6',
60 | expandIcon: ,
61 | };
62 |
63 | export default CodeXExpansionPanel;
64 |
--------------------------------------------------------------------------------
/src/apps/admin/features/company.create/helpers/renderFormHeader.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import Typography from '@material-ui/core/Typography';
4 |
5 | export function CodeXFormHeader({
6 | text,
7 | variant = 'h5',
8 | color = 'primary',
9 | weight = '700',
10 | }) {
11 | return (
12 |
22 | {text}
23 |
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/src/apps/admin/features/company.create/helpers/validationSchema.js:
--------------------------------------------------------------------------------
1 | import * as Yup from 'yup';
2 |
3 | const currentDate = new Date();
4 | const yesterday = new Date(
5 | currentDate.setDate(currentDate.getDate() - 1)
6 | ).toISOString();
7 |
8 | export const ValidationSchema = Yup.object().shape({
9 | name: Yup.string().required('Name is required.'),
10 | description: Yup.string()
11 | .required('Description is required.')
12 | .min(150, 'Description must be at least 150 characters.'),
13 | yearFounded: Yup.date()
14 | .required('Date Founded is required.')
15 | .max(yesterday, 'Date Founded must be before today.'),
16 | targetMarkets: Yup.string().required('Target Markets is required'),
17 | });
18 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.affiliations/components/avatar.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { makeStyles } from '@material-ui/styles';
3 | import ListItemAvatar from '@material-ui/core/ListItemAvatar';
4 | import Avatar from '@material-ui/core/Avatar';
5 |
6 | const useStyles = makeStyles(theme => ({
7 | iconAvatar: {
8 | margin: theme.spacing(1),
9 | width: 60,
10 | height: 60,
11 | },
12 | letterAvatar: {
13 | margin: 10,
14 | width: 60,
15 | height: 60,
16 | backgroundColor: '#b1040e',
17 | },
18 | listItem: {
19 | alignSelf: 'flex-start',
20 | marginRight: '.5rem',
21 | },
22 | }));
23 |
24 | export function AffiliationAvatar({ affiliation }) {
25 | const { organization } = affiliation;
26 | const { logo, name } = organization;
27 | const firstInitial = name && name[0].payload.slice(0, 1);
28 | const classes = useStyles();
29 | return (
30 | <>
31 | {logo && logo.length > 0 ? (
32 |
33 | ) : (
34 | {firstInitial}
35 | )}
36 | >
37 | );
38 | }
39 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.affiliations/components/content/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { makeStyles } from '@material-ui/styles';
3 | import renderPrimaryContent from './primary';
4 | import renderSecondaryContent from './secondary';
5 | import styled from 'styled-components';
6 |
7 | const useStyles = makeStyles(() => ({
8 | listItem: {
9 | minWidth: '300px',
10 | },
11 | }));
12 |
13 | export function AffiliationContent({ affiliation, ...props }) {
14 | const classes = useStyles();
15 | return (
16 |
17 |
{renderPrimaryContent({ affiliation })}
18 |
{renderSecondaryContent({ affiliation })}
19 |
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.affiliations/components/content/primary.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Typography from '@material-ui/core/Typography';
3 | import { makeStyles } from '@material-ui/styles';
4 | import Link from '@material-ui/core/Link';
5 | import { navigate } from 'gatsby';
6 | import slugify from 'slugify';
7 |
8 | const useStyles = makeStyles(theme => ({
9 | companyName: {
10 | fontWeight: 500,
11 | },
12 | }));
13 |
14 | function renderAffiliationPrimaryContent({ affiliation }) {
15 | const classes = useStyles();
16 | return (
17 |
19 | navigate(
20 | `/companies/${slugify(affiliation.organization.name[0].payload)}`
21 | )
22 | }
23 | target="_blank"
24 | >
25 |
26 | {affiliation.organization.name[0].payload}
27 |
28 |
29 | );
30 | }
31 |
32 | export default renderAffiliationPrimaryContent;
33 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.affiliations/components/content/secondary.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Typography from '@material-ui/core/Typography';
3 | import { makeStyles } from '@material-ui/styles';
4 | import { formatDateString } from '../../helpers';
5 | const useStyles = makeStyles(theme => ({
6 | subtitle1: {
7 | fontSize: '12px',
8 | },
9 | }));
10 |
11 | function renderAffiliationSecondaryContent({ affiliation }) {
12 | const classes = useStyles();
13 | return (
14 | <>
15 | {`${affiliation.title}`}
19 | {`${formatDateString(affiliation.fromDate)} to ${formatDateString(
23 | affiliation.throughDate
24 | )} `}
25 | >
26 | );
27 | }
28 |
29 | export default renderAffiliationSecondaryContent;
30 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.affiliations/components/controls/delete.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import IconButton from '@material-ui/core/IconButton';
3 | import DeleteIcon from '@material-ui/icons/Delete';
4 | import { useMutation } from 'react-apollo-hooks';
5 | import { makeStyles } from '@material-ui/styles';
6 |
7 | import Confirm from '../../../../../../atoms/confirm';
8 |
9 | import { DELETE_AFFILIATION_MUTATION } from '../../graphql';
10 |
11 | const useStyles = makeStyles(theme => ({
12 | icon: {
13 | margin: theme.spacing(0.5),
14 | },
15 | }));
16 | export function DeleteAffiliationControl({ affiliation, refetch, ...props }) {
17 | const [isDeleting, toggleDelete] = React.useState(false);
18 | const classes = useStyles();
19 |
20 | return (
21 | <>
22 | toggleDelete(!isDeleting)}
25 | className={classes.icon}
26 | >
27 |
28 |
29 |
35 | >
36 | );
37 | }
38 |
39 | function DeleteAffiliation({
40 | isDeleting,
41 | toggleDelete,
42 | affiliation,
43 | refetch,
44 | ...props
45 | }) {
46 | const deleteAffiliation = useMutation(DELETE_AFFILIATION_MUTATION, {
47 | variables: {
48 | where: { id: affiliation.id },
49 | },
50 | });
51 |
52 | if (isDeleting === false) {
53 | return null;
54 | }
55 | return (
56 | {
59 | deleteAffiliation();
60 | refetch();
61 | }}
62 | onCancel={() => null}
63 | >
64 | Test
65 |
66 | );
67 | }
68 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.affiliations/components/controls/edit.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { makeStyles } from '@material-ui/styles';
3 | import IconButton from '@material-ui/core/IconButton';
4 | import EditIcon from '@material-ui/icons/Edit';
5 | import EditAffiliation from '../../../../components/affiliation.edit';
6 |
7 | const useStyles = makeStyles(theme => ({
8 | icon: {
9 | margin: theme.spacing(0.5),
10 | },
11 | }));
12 |
13 | export function EditAffiliationControl({ affiliation, ...props }) {
14 | const [isEditing, toggleEditing] = React.useState(false);
15 | const classes = useStyles();
16 | return (
17 | <>
18 | toggleEditing(!isEditing)}
22 | className={classes.icon}
23 | >
24 |
25 |
26 |
31 | >
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.affiliations/components/controls/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { makeStyles } from '@material-ui/styles';
3 |
4 | import { EditAffiliationControl } from './edit';
5 | import { DeleteAffiliationControl } from './delete';
6 |
7 | const useStyles = makeStyles(theme => ({
8 | listItem: {
9 | display: 'flex',
10 | justifyContent: 'flex-end',
11 | alignSelf: 'flex-start',
12 | flexGrow: 1,
13 | },
14 | }));
15 |
16 | export function AfilliationControls({ affiliation, refetch, ...props }) {
17 | const classes = useStyles();
18 | return (
19 |
20 |
21 |
22 |
23 | );
24 | }
25 |
26 | export default AfilliationControls;
27 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.affiliations/components/edit.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codexstanford/techlist-frontend-web/06be56c7f1bbaa34e65ba942a24fe29257ae4b7f/src/apps/admin/features/profile.affiliations/components/edit.js
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.affiliations/components/index.js:
--------------------------------------------------------------------------------
1 | export * from './avatar';
2 | export * from './content/';
3 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.affiliations/controller.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { makeStyles } from '@material-ui/styles';
3 | import List from '@material-ui/core/List';
4 | import ExpansionPanel from '@material-ui/core/ExpansionPanel';
5 | import ExpansionPanelSummary from '@material-ui/core/ExpansionPanelSummary';
6 | import ExpansionPanelDetails from '@material-ui/core/ExpansionPanelDetails';
7 | import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
8 |
9 | import { renderAffiliation } from './helpers';
10 |
11 | const useStyles = makeStyles(theme => ({
12 | root: {
13 | width: '100%',
14 | backgroundColor: 'white',
15 | },
16 | expansionPanel: {
17 | boxShadow: 'none',
18 | width: '100%',
19 | },
20 | }));
21 |
22 | export default function AffiliationsListController({
23 | affiliations,
24 | first,
25 | second,
26 | rest,
27 | refetch,
28 | ...props
29 | }) {
30 | const classes = useStyles();
31 |
32 | if (affiliations) {
33 | return (
34 | <>
35 | {first &&
36 | renderAffiliation({
37 | affiliation: first,
38 | refetch,
39 | hasDivider: false,
40 | })}
41 | {second &&
42 | renderAffiliation({
43 | affiliation: second,
44 | refetch,
45 | hasDivider: false,
46 | })}
47 | {rest && rest.length > 0 ? (
48 |
49 | } />
50 |
51 |
52 | {rest.map(affiliation =>
53 | renderAffiliation({ affiliation, refetch })
54 | )}
55 |
56 |
57 |
58 | ) : null}
59 | >
60 | );
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.affiliations/graphql/index.js:
--------------------------------------------------------------------------------
1 | export * from './mutations';
2 | export * from './queries';
3 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.affiliations/graphql/mutations.js:
--------------------------------------------------------------------------------
1 | import gql from 'graphql-tag';
2 |
3 | export const DELETE_AFFILIATION_MUTATION = gql`
4 | mutation DeleteAffiliationMutation(
5 | $where: PersonOrganizationAffiliationWhereUniqueInput!
6 | ) {
7 | deleteAffiliation(where: $where) {
8 | __typename
9 | id
10 | createdAt
11 | fromDate
12 | throughDate
13 | title
14 | role
15 | description
16 | organization {
17 | __typename
18 | id
19 | name {
20 | __typename
21 | id
22 | payload
23 | }
24 | description
25 | logo {
26 | __typename
27 | id
28 | payload
29 | }
30 | }
31 | metadata {
32 | __typename
33 | isDraft
34 | isPublic
35 | isRejected
36 | isUnverified
37 | isApproved
38 | isPendingReview
39 | }
40 | }
41 | }
42 | `;
43 |
44 | // PersonOrganizationAffiliationUpdateManyWithoutOrganizationInput
45 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.affiliations/graphql/queries.js:
--------------------------------------------------------------------------------
1 | import gql from 'graphql-tag';
2 |
3 | export const GET_PERSON_AFFILIATIONS_QUERY = gql`
4 | query GetPersonAffiliationsQuery(
5 | $where: PersonOrganizationAffiliationWhereInput
6 | $orderBy: PersonOrganizationAffiliationOrderByInput
7 | ) {
8 | personOrganizationAffiliations(where: $where, orderBy: $orderBy) {
9 | __typename
10 | id
11 | createdAt
12 | fromDate
13 | throughDate
14 | title
15 | role
16 | description
17 | organization {
18 | __typename
19 | id
20 | name {
21 | __typename
22 | id
23 | payload
24 | }
25 | description
26 | logo {
27 | __typename
28 | id
29 | payload
30 | }
31 | }
32 | metadata {
33 | __typename
34 | isDraft
35 | isPublic
36 | isRejected
37 | isUnverified
38 | isApproved
39 | isPendingReview
40 | }
41 | }
42 | }
43 | `;
44 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.affiliations/helpers/formatDates.js:
--------------------------------------------------------------------------------
1 | import { DateTime } from 'luxon';
2 |
3 | export function formatDateString(date) {
4 | if (date === null) {
5 | return 'Present';
6 | }
7 | return DateTime.fromISO(date).toLocaleString();
8 | }
9 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.affiliations/helpers/index.js:
--------------------------------------------------------------------------------
1 | export * from './formatDates';
2 | export * from './renderAffiliation';
3 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.affiliations/helpers/renderAffiliation.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { makeStyles } from '@material-ui/styles';
3 | import Divider from '@material-ui/core/Divider';
4 | import styled from 'styled-components';
5 |
6 | import AffiliationControls from '../components/controls';
7 | import { AffiliationAvatar, AffiliationContent } from '../components';
8 |
9 | const useStyles = makeStyles({
10 | divider: {
11 | color: 'black',
12 | },
13 | wrapper: {
14 | display: 'flex',
15 | alignItems: 'center',
16 | },
17 | });
18 |
19 | export function renderAffiliation({ affiliation, refetch, hasDivider = true }) {
20 | const classes = useStyles();
21 | if (affiliation.organization !== null) {
22 | return (
23 |
24 |
25 |
26 |
27 |
28 | {hasDivider && (
29 |
30 | )}
31 |
32 | );
33 | }
34 | return null;
35 | }
36 |
37 | const AvatarAndContentContainer = styled.div`
38 | display: flex;
39 | align-items: center;
40 | `;
41 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.affiliations/index.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useEffect } from 'react';
2 | import { makeStyles } from '@material-ui/styles';
3 | import Controller from './controller';
4 | import Typography from '@material-ui/core/Typography';
5 | import Media from 'react-media';
6 | import Card from '@material-ui/core/Card';
7 | import CardContent from '@material-ui/core/CardContent';
8 |
9 | import { GET_PERSON_AFFILIATIONS_QUERY } from './graphql';
10 | import { useQuery } from 'react-apollo-hooks';
11 | import { CreateCompanyModalContext } from '../../../../store/modal-context';
12 |
13 | const useStyles = makeStyles(theme => ({
14 | wrapper: {
15 | width: '100%',
16 | maxWidth: 600,
17 | justifyContent: 'space-between',
18 | [theme.breakpoints.down('sm')]: {
19 | minWidth: '100%',
20 | maxWidth: '100vw',
21 | },
22 | },
23 | card: {
24 | width: '100%',
25 | },
26 | }));
27 |
28 | export default function ProfileAffiliations({ person, style, ...props }) {
29 | const classes = useStyles();
30 | const { open } = useContext(CreateCompanyModalContext);
31 |
32 | const { loading, error, data, refetch } = useQuery(
33 | GET_PERSON_AFFILIATIONS_QUERY,
34 | {
35 | variables: {
36 | where: {
37 | person: {
38 | id: person.id,
39 | },
40 | },
41 | orderBy: 'fromDate_DESC',
42 | },
43 | }
44 | );
45 |
46 | useEffect(() => {
47 | refetch();
48 | }, [open]);
49 |
50 | if (loading) {
51 | return null;
52 | }
53 |
54 | const { personOrganizationAffiliations: affiliations } = data;
55 |
56 | const newAffiliations = [];
57 |
58 | affiliations.map(affiliation => {
59 | if (affiliation.organization !== null) {
60 | newAffiliations.push(affiliation);
61 | }
62 | });
63 | console.log('DATA', data);
64 |
65 | console.log('newAffiliations', newAffiliations);
66 |
67 | const [first, second, ...rest] = newAffiliations;
68 |
69 | return (
70 |
71 |
72 |
73 |
74 |
75 | {matches =>
76 | matches ? (
77 |
78 | Affiliations{' '}
79 |
80 | ) : (
81 |
82 | Affiliations{' '}
83 |
84 | )
85 | }
86 |
87 | {affiliations && (
88 |
95 | )}
96 |
97 |
98 |
99 |
100 | );
101 | }
102 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.companies/components/avatar.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { makeStyles } from '@material-ui/styles';
3 | import ListItemAvatar from '@material-ui/core/ListItemAvatar';
4 | import Avatar from '@material-ui/core/Avatar';
5 |
6 | const useStyles = makeStyles({
7 | iconAvatar: {
8 | margin: 10,
9 | width: 60,
10 | height: 60,
11 | },
12 | letterAvatar: {
13 | margin: 10,
14 | width: 60,
15 | height: 60,
16 | backgroundColor: '#b1040e',
17 | },
18 | listItem: {
19 | alignSelf: 'flex-start',
20 | marginRight: '.5rem',
21 | },
22 | });
23 |
24 | export function CompanyAvatar({ company }) {
25 | const { logo, name } = company;
26 | const firstInitial = name[0].payload.slice(0, 1);
27 | const classes = useStyles();
28 | return (
29 |
30 | {logo && logo.length > 0 && logo[0].payload ? (
31 |
32 | ) : (
33 | {firstInitial}
34 | )}
35 |
36 | );
37 | }
38 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.companies/components/content/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import renderPrimaryContent from './primary';
3 | import renderSecondaryContent from './secondary';
4 | import styled from 'styled-components';
5 | import truncateText from '../../../../../../helpers/truncateText';
6 |
7 | export function CompanyContent({ company, ...props }) {
8 | return (
9 |
10 |
11 |
12 | {renderPrimaryContent({ company })}
13 | {renderSecondaryContent({ company })}
14 |
15 |
16 | {company.description && truncateText(company.description, 125)}
17 |
18 |
19 |
20 | );
21 | }
22 |
23 | const Container = styled.div`
24 | min-width: 300px;
25 | `;
26 |
27 | const Wrapper = styled.div`
28 | display: flex;
29 | align-items: flex-start;
30 | justify-content: flex-start;
31 | `;
32 |
33 | const ContentWrapper = styled.div`
34 | flex: 1 0 1rem;
35 | `;
36 |
37 | const CompanyDescription = styled.p`
38 | display: none;
39 | margin: 0;
40 | flex: 2 0 24rem;
41 | @media (max-width: 960px) {
42 | display: block;
43 | }
44 | @media (max-width: 800px) {
45 | display: none;
46 | }
47 | `;
48 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.companies/components/content/primary.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Link from '@material-ui/core/Link';
3 | import { navigate } from 'gatsby';
4 | import slugify from 'slugify';
5 |
6 | function renderCompanyPrimaryContent({ classes, company }) {
7 | return (
8 | navigate(`/companies/${slugify(company.name[0].payload)}`)}
10 | target="_blank"
11 | >
12 | {company.name[0].payload}
13 |
14 | );
15 | }
16 |
17 | export default renderCompanyPrimaryContent;
18 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.companies/components/content/secondary.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { formatDateString } from '../../helpers';
3 | import truncateText from '../../../../../../helpers/truncateText';
4 |
5 | function renderCompanySecondaryContent({ company }) {
6 | return (
7 |
8 | {company.title && (
9 | <>
10 | {`${company.title}`}
11 |
12 | >
13 | )}
14 | {
15 | {`${
16 | company.yearFounded !== null
17 | ? formatDateString(company.yearFounded)
18 | : 'N/A'
19 | }`}
20 | }
21 |
22 | );
23 | }
24 |
25 | export default renderCompanySecondaryContent;
26 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.companies/components/controls/delete.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import IconButton from '@material-ui/core/IconButton';
3 | import DeleteIcon from '@material-ui/icons/Delete';
4 | import Confirm from '../../../../../../atoms/confirm';
5 | import { useMutation } from 'react-apollo-hooks';
6 | import { DELETE_COMPANY_MUTATION } from '../../graphql';
7 |
8 | export function DeleteCompanyControl({ company, ...props }) {
9 | const [isDeleting, toggleDelete] = React.useState(false);
10 | return (
11 | <>
12 | toggleDelete(!isDeleting)}>
13 |
14 |
15 |
20 | >
21 | );
22 | }
23 |
24 | function DeleteCompany({ isDeleting, toggleDelete, company, ...props }) {
25 | const deleteCompany = useMutation(DELETE_COMPANY_MUTATION);
26 |
27 | // console.log('deleteCompany', deleteCompany);
28 |
29 | return (
30 |
33 | deleteCompany({ variables: { where: { id: company.id } } })
34 | }
35 | onClose={() => toggleDelete(false)}
36 | onCancel={() => {}}
37 | >
38 | Test
39 |
40 | );
41 | }
42 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.companies/components/controls/edit.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import IconButton from '@material-ui/core/IconButton';
3 | import EditIcon from '@material-ui/icons/Edit';
4 | import EditCompany from '../../../../components/company.edit';
5 |
6 | export function EditCompanyControl({ company, ...props }) {
7 | const [isEditing, toggleEditing] = React.useState(false);
8 | return (
9 | <>
10 | toggleEditing(!isEditing)}>
11 |
12 |
13 |
18 | >
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.companies/components/controls/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
3 | import ListItem from '@material-ui/core/ListItem';
4 | import ListItemIcon from '@material-ui/core/ListItemIcon';
5 | import IconButton from '@material-ui/core/IconButton';
6 | import EditIcon from '@material-ui/icons/Edit';
7 | import EditCompany from '../../../../components/company.edit';
8 | import { makeStyles } from '@material-ui/styles';
9 |
10 | import { EditCompanyControl } from './edit';
11 | import { DeleteCompanyControl } from './delete';
12 |
13 | const useStyles = makeStyles(theme => ({
14 | listItem: {
15 | alignSelf: 'flex-start',
16 | minWidth: 103,
17 | },
18 | }));
19 |
20 | export function AfilliationControls({ company, ...props }) {
21 | const classes = useStyles();
22 | return (
23 |
24 |
25 |
26 |
27 | );
28 | }
29 |
30 | export default AfilliationControls;
31 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.companies/components/index.js:
--------------------------------------------------------------------------------
1 | export * from './avatar';
2 | export * from './content/';
3 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.companies/controller.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { makeStyles } from '@material-ui/styles';
3 | import List from '@material-ui/core/List';
4 | import ExpansionPanel from '@material-ui/core/ExpansionPanel';
5 | import ExpansionPanelSummary from '@material-ui/core/ExpansionPanelSummary';
6 | import ExpansionPanelDetails from '@material-ui/core/ExpansionPanelDetails';
7 | import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
8 |
9 | import { renderCompany } from './helpers';
10 |
11 | const useStyles = makeStyles(theme => ({
12 | root: {
13 | width: '100%',
14 | backgroundColor: 'white',
15 | },
16 | expansionPanel: {
17 | boxShadow: 'none',
18 | width: '100%',
19 | },
20 | }));
21 |
22 | export default function CompaniesListController({ companies, ...props }) {
23 | const classes = useStyles();
24 | const [first, second, ...rest] = companies;
25 |
26 | return (
27 | <>
28 | {first && renderCompany({ company: first, hasDivider: false })}
29 | {second && renderCompany({ company: second, hasDivider: false })}
30 | {rest && rest.length > 0 ? (
31 |
32 | } />
33 |
34 |
35 | {rest.map(company => {
36 | renderCompany({ company });
37 | })}
38 |
39 |
40 |
41 | ) : null}
42 | >
43 | );
44 | }
45 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.companies/graphql/index.js:
--------------------------------------------------------------------------------
1 | export * from './mutations';
2 | export * from './queries';
3 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.companies/graphql/mutations.js:
--------------------------------------------------------------------------------
1 | import gql from 'graphql-tag';
2 |
3 | export const DELETE_COMPANY_MUTATION = gql`
4 | mutation DeleteCompany($where: OrganizationWhereUniqueInput!) {
5 | deleteOrganization(where: $where) {
6 | __typename
7 | id
8 | }
9 | }
10 | `;
11 |
12 | export const UPDATE_COMPANY_MUATATION = gql`
13 | mutation UpdateCompany(
14 | $where: OrganizationWhereUniqueInput!
15 | $data: OrganizationUpdateInput!
16 | ) {
17 | updateOrganization(where: $where, data: $data) {
18 | __typename
19 | id
20 | name {
21 | payload
22 | }
23 | }
24 | }
25 | `;
26 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.companies/graphql/queries.js:
--------------------------------------------------------------------------------
1 | import gql from 'graphql-tag';
2 |
3 | export const GET_USER_ADMIN_COMPANIES = gql`
4 | query GetUserAdminCompanies($where: PartyAccountWhereUniqueInput!) {
5 | partyAccount(where: $where) {
6 | id
7 | admin {
8 | id
9 | yearFounded
10 | description
11 | name {
12 | id
13 | payload
14 | }
15 | logo {
16 | id
17 | payload
18 | }
19 | description
20 | metadata {
21 | id
22 | isDraft
23 | isPublic
24 | isRejected
25 | isUnverified
26 | isApproved
27 | isPendingReview
28 | }
29 | }
30 | }
31 | }
32 | `;
33 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.companies/helpers/formatDates.js:
--------------------------------------------------------------------------------
1 | import { DateTime } from 'luxon';
2 |
3 | export function formatDateString(date) {
4 | if (date === null) {
5 | return 'Present';
6 | }
7 | return DateTime.fromISO(date).toLocaleString();
8 | }
9 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.companies/helpers/index.js:
--------------------------------------------------------------------------------
1 | export * from './formatDates';
2 | export * from './renderCompany';
3 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.companies/helpers/renderCompany.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { makeStyles } from '@material-ui/styles';
3 | import ListItem from '@material-ui/core/ListItem';
4 | import Divider from '@material-ui/core/Divider';
5 | import styled from 'styled-components';
6 |
7 | import CompanyControls from '../components/controls';
8 | import { CompanyAvatar, CompanyContent } from '../components';
9 | import truncateText from '../../../../../helpers/truncateText';
10 |
11 | const useStyles = makeStyles({
12 | divider: {
13 | color: 'black',
14 | },
15 | listItem: {
16 | diplay: 'flex',
17 | justifyContent: 'space-between',
18 | padding: 0,
19 | },
20 | });
21 |
22 | export function renderCompany({ company, hasDivider = true }) {
23 | const classes = useStyles();
24 | return (
25 | <>
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | {company.description && truncateText(company.description, 125)}
35 |
36 |
37 | {hasDivider && (
38 |
39 | )}
40 | >
41 | );
42 | }
43 |
44 | const AvatarAndContentContainer = styled.div`
45 | display: flex;
46 | align-items: center;
47 | `;
48 |
49 | const CompanyDescription = styled.p`
50 | padding: 0 14px;
51 | @media (max-width: 960px) {
52 | display: none;
53 | }
54 | @media (max-width: 800px) {
55 | display: block;
56 | }
57 | `;
58 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.companies/index.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useEffect } from 'react';
2 | import { makeStyles } from '@material-ui/styles';
3 | import Controller from './controller';
4 | import Typography from '@material-ui/core/Typography';
5 | import Media from 'react-media';
6 | import { GET_USER_ADMIN_COMPANIES } from './graphql';
7 | import { useQuery } from 'react-apollo-hooks';
8 | import Card from '@material-ui/core/Card';
9 | import CardContent from '@material-ui/core/CardContent';
10 | import { CreateCompanyModalContext } from '../../../../store/modal-context';
11 |
12 | const useStyles = makeStyles(theme => ({
13 | root: {
14 | width: '100%',
15 | backgroundColor: 'white',
16 | },
17 | card: {
18 | width: '100%',
19 | minWidth: `300px`,
20 | },
21 | wrapper: {
22 | width: '100%',
23 | maxWidth: 600,
24 | justifyContent: 'space-between',
25 | marginBottom: theme.spacing(2),
26 | [theme.breakpoints.down('sm')]: {
27 | minWidth: '100%',
28 | maxWidth: '100vw',
29 | },
30 | },
31 | }));
32 |
33 | export default function ProfileCompanies({ user, ...props }) {
34 | const classes = useStyles();
35 | const { open } = useContext(CreateCompanyModalContext);
36 |
37 | const { loading, error, data, refetch } = useQuery(GET_USER_ADMIN_COMPANIES, {
38 | variables: {
39 | where: { id: user.id },
40 | orderBy: 'fromDate_DESC',
41 | },
42 | });
43 |
44 | useEffect(() => {
45 | console.log('gotta refetch', open);
46 | refetch();
47 | }, [open]);
48 |
49 | if (loading) {
50 | return null;
51 | }
52 |
53 | const { partyAccount } = data;
54 | return (
55 |
56 |
57 |
58 |
59 |
60 |
61 | {matches =>
62 | matches ? (
63 |
64 | Companies{' '}
65 |
66 | ) : (
67 |
68 | Companies{' '}
69 |
70 | )
71 | }
72 |
73 |
74 | {partyAccount &&
75 | partyAccount.admin &&
76 | partyAccount.admin.length > 0 && (
77 |
82 | )}
83 |
84 |
85 |
86 |
87 | );
88 | }
89 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.navigation/components/desktop.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import Drawer from '@material-ui/core/Drawer';
3 | import Divider from '@material-ui/core/Divider';
4 | import List from '@material-ui/core/List';
5 | import ChevronLeftIcon from '@material-ui/icons/ChevronLeft';
6 | import classNames from 'classnames';
7 | import IconButton from '@material-ui/core/IconButton';
8 | import ListItem from '@material-ui/core/ListItem';
9 | import DashboardIcon from '@material-ui/icons/Dashboard';
10 | import BusinessIcon from '@material-ui/icons/Business';
11 | import ListItemIcon from '@material-ui/core/ListItemIcon';
12 | import ListItemText from '@material-ui/core/ListItemText';
13 | import EditIcon from '@material-ui/icons/Edit';
14 | import { CreateCompanyModalContext } from '../../../../../store/modal-context';
15 |
16 | function DesktopProfileNavigation({
17 | classes,
18 | secondaryListItems,
19 | isOpen,
20 | toggleDrawerVisibility,
21 | toggleEditProfile,
22 | showEditProfile,
23 | logout,
24 | user,
25 | ...props
26 | }) {
27 | const { showModal } = useContext(CreateCompanyModalContext);
28 | const handleLogout = () => {
29 | logout();
30 | };
31 |
32 | return (
33 | <>
34 |
44 |
45 | toggleDrawerVisibility(!isOpen)}>
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | toggleEditProfile(!showEditProfile)}>
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | {secondaryListItems}
73 |
74 | >
75 | );
76 | }
77 |
78 | export default DesktopProfileNavigation;
79 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.navigation/components/mobile.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import Drawer from '@material-ui/core/Drawer';
3 | import Divider from '@material-ui/core/Divider';
4 | import List from '@material-ui/core/List';
5 | import ChevronLeftIcon from '@material-ui/icons/ChevronLeft';
6 | import classNames from 'classnames';
7 | import IconButton from '@material-ui/core/IconButton';
8 | import ListItem from '@material-ui/core/ListItem';
9 | import DashboardIcon from '@material-ui/icons/Dashboard';
10 | import BusinessIcon from '@material-ui/icons/Business';
11 | import ListItemIcon from '@material-ui/core/ListItemIcon';
12 | import ListItemText from '@material-ui/core/ListItemText';
13 | import { CreateCompanyModalContext } from '../../../../../store/modal-context';
14 |
15 | function DesktopProfileNavigation({
16 | classes,
17 | MainListItems,
18 | secondaryListItems,
19 | isOpen,
20 | isClosed,
21 | toggleDrawerVisibility,
22 | logout,
23 | user,
24 | }) {
25 | const { showModal } = useContext(CreateCompanyModalContext);
26 |
27 | return (
28 | <>
29 |
39 |
40 | toggleDrawerVisibility(!isOpen)}>
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | logout()} />
57 |
58 |
59 |
60 | {secondaryListItems}
61 |
62 | >
63 | );
64 | }
65 |
66 | export default DesktopProfileNavigation;
67 |
--------------------------------------------------------------------------------
/src/apps/admin/features/profile.navigation/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import MobileNav from './components/mobile';
3 | import DesktopNav from './components/desktop';
4 | import Media from 'react-media';
5 |
6 | // TODO: extend or trash mobile nav.
7 |
8 | function ProfileNav(props) {
9 | return (
10 |
11 | {matches =>
12 | matches ? :
13 | }
14 |
15 | );
16 | }
17 |
18 | export default ProfileNav;
19 |
--------------------------------------------------------------------------------
/src/apps/admin/helpers.js:
--------------------------------------------------------------------------------
1 | export const validateCreateAccountForm = values => {
2 | const errors = {};
3 | if (!values.email) {
4 | errors.email = 'Required.';
5 | } else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)) {
6 | errors.email = 'Invalid email address';
7 | }
8 |
9 | if (!values.password) {
10 | errors.password = 'Required.';
11 | } else if (values.password.length < 8) {
12 | errors.password = 'Must be at least 8 characters long.';
13 | }
14 |
15 | if (!values.phone) {
16 | errors.phone = 'Required.';
17 | }
18 |
19 | if (!values.confirm) {
20 | errors.confirm = 'Required';
21 | } else if (values.confirm !== values.password) {
22 | errors.confirm = 'Passwords do not match.';
23 | }
24 | return errors;
25 | };
26 |
27 | export const validateSignInForm = values => {
28 | const errors = {};
29 | if (!values.email) {
30 | errors.email = 'Required.';
31 | } else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)) {
32 | errors.email = 'Invalid email address';
33 | }
34 |
35 | if (!values.password) {
36 | errors.password = 'Required.';
37 | } else if (values.password.length < 8) {
38 | errors.password = 'Must be at least 8 characters long.';
39 | }
40 |
41 | return errors;
42 | };
43 |
--------------------------------------------------------------------------------
/src/apps/admin/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PrivateRoute from '../../components/PrivateRoute';
3 | import { Router, navigate } from '@reach/router';
4 | import Login from './routes/auth/login';
5 | import Create from './routes/auth/createwiz';
6 | import Profile from './routes/profile/';
7 | import Layout from '../../components/layout';
8 | import withStyles from '@material-ui/core/styles/withStyles';
9 |
10 | import { styles } from './config/styles';
11 |
12 | import { useUser } from '../../store/user-context';
13 | import { UseModal } from '../../store/useModal';
14 |
15 | function App(props) {
16 | const { login, data, logout, register, getUser } = useUser();
17 |
18 | return (
19 |
26 |
27 |
28 |
35 |
41 |
42 |
43 | );
44 | }
45 |
46 | function AppWithStuff({ children, ...props }) {
47 | return ;
48 | }
49 |
50 | export default withStyles(styles)(AppWithStuff);
51 |
--------------------------------------------------------------------------------
/src/apps/admin/routes/affiliation/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import CreateAffiliation from '../../features/afffiliation.create';
3 | import { useUser } from '../../../../store/user-context';
4 |
5 | function CreateAffiliationScreen({
6 | classes,
7 | navigate,
8 | open,
9 | onCancel,
10 | initialCompany,
11 | ...rest
12 | }) {
13 | const { data } = useUser();
14 | const { user } = data;
15 |
16 | return (
17 | onCancel(!open)}
21 | open={open}
22 | initialCompany={initialCompany}
23 | />
24 | );
25 | }
26 |
27 | export default CreateAffiliationScreen;
28 |
--------------------------------------------------------------------------------
/src/apps/admin/routes/auth/createwiz.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { Mutation, Query } from 'react-apollo';
3 | import styled from 'styled-components';
4 | import { navigate } from '@reach/router';
5 |
6 | import Paper from '@material-ui/core/Paper';
7 | import Typography from '@material-ui/core/Typography';
8 | import Link from '@material-ui/core/Link';
9 | import Divider from '@material-ui/core/Divider';
10 |
11 | import CreateAccount from './create';
12 | import CreateProfile from './profile';
13 |
14 | import {
15 | GET_CURRENT_USER_QUERY,
16 | UPDATE_CURRENT_USER_MUTATION,
17 | } from '../../../../graphql';
18 |
19 | function SignUpWizard({ initialStep = 0, classes, ...rest }) {
20 | const [activeStep, setStep] = useState(initialStep);
21 |
22 | return (
23 |
24 |
25 |
26 | {({ data, loading, error }) => {
27 | if (loading) {
28 | return null;
29 | }
30 | if (error) {
31 | console.log(error);
32 | }
33 |
34 | return (
35 |
36 | {getStepContent({
37 | step: activeStep,
38 | props: { activeStep, setStep, classes, hoist: data },
39 | })}
40 |
41 | );
42 | }}
43 |
44 |
45 |
46 |
47 | Already have an account?{' '}
48 | navigate('/app/login/')}>
49 | Sign in!
50 |
51 |
52 |
53 |
54 | );
55 | }
56 |
57 | export default SignUpWizard;
58 |
59 | const Container = styled.div`
60 | display: flex;
61 | justify-content: center;
62 | `;
63 |
64 | function getStepContent({ step, props, hoist }) {
65 | console.log('STEP', step);
66 | console.log('PROPS', props);
67 | switch (step) {
68 | case 0:
69 | return ;
70 | case 1:
71 | return (
72 |
73 | {({ data, loading, error }) => {
74 | if (loading) {
75 | return null;
76 | }
77 | if (error) {
78 | console.log('Error Case 1', error);
79 | return null;
80 | }
81 | console.log('PROPS Case 1', props);
82 | console.log('DATA Case 1', data);
83 | return (
84 |
85 | {mutation => {
86 | return (
87 |
93 | );
94 | }}
95 |
96 | );
97 | }}
98 |
99 | );
100 |
101 | default:
102 | return 'Unknown step';
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/apps/admin/routes/auth/index.js:
--------------------------------------------------------------------------------
1 | import * as yup from 'yup';
2 |
3 | export const schema = yup.object().shape({
4 | firstName: yup
5 | .string()
6 | .min(2, 'Too Short!')
7 | .max(50, 'Too Long!')
8 | .required(),
9 | lastName: yup
10 | .string()
11 | .min(2, 'Too Short!')
12 | .max(50, 'Too Long!')
13 | .required(),
14 | handle: yup
15 | .string()
16 | .min(2, 'Too Short!')
17 | .max(50, 'Too Long!')
18 | .required(),
19 | });
20 |
21 | export const companySchema = yup.object().shape({});
22 |
--------------------------------------------------------------------------------
/src/apps/admin/routes/auth/mocks.js:
--------------------------------------------------------------------------------
1 | export const opts = [
2 | { value: 'Attorney', label: 'Attorney' },
3 | { value: 'Developer', label: 'Developer' },
4 | { value: 'Academic', label: 'Academic' },
5 | { value: 'Product Desginer', label: 'Product Desginer' },
6 | ];
7 |
8 | export const linkOptions = [
9 | { value: 'LinkedIn', label: 'LinkedIn' },
10 | { value: 'Twitter', label: 'Twitter' },
11 | { value: 'Facebook', label: 'Facebook' },
12 | { value: 'Other', label: 'Other' },
13 | ];
14 |
--------------------------------------------------------------------------------
/src/apps/admin/routes/company/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Dialog from '@material-ui/core/Dialog';
3 | import DialogContent from '@material-ui/core/DialogContent';
4 | import Paper from '@material-ui/core/Paper';
5 | import Slide from '@material-ui/core/Slide';
6 | import styled from 'styled-components';
7 | import CreateCompanyNew from '../../features/company.create';
8 | import Clear from '@material-ui/icons/Clear';
9 |
10 | const Transition = React.forwardRef(function Transition(props, ref) {
11 | return (
12 |
18 | );
19 | });
20 |
21 | function CreateCompanyScreen({
22 | classes,
23 | user,
24 | navigate,
25 | open,
26 | onCancel,
27 | ...rest
28 | }) {
29 | return (
30 |
37 |
38 |
43 |
44 |
45 |
46 | );
47 | }
48 |
49 | const StyledDialog = styled(Dialog)`
50 | padding: 0;
51 | margin: 0 16px;
52 | @media (max-width: 750px) {
53 | margin: 0;
54 | }
55 | `;
56 |
57 | const StyledDialogContent = styled(DialogContent)`
58 | position: relative;
59 | width: 100%;
60 | display: flex;
61 | justify-content: center;
62 | padding: 0 16px;
63 |
64 | @media (min-width: 750px) {
65 | max-width: 900px;
66 | padding: 3rem 3rem;
67 | }
68 | `;
69 |
70 | const MobileExit = styled(Clear)`
71 | display: none;
72 | @media (max-width: 750px) {
73 | display: block;
74 | position: absolute;
75 | top: 10px;
76 | right: 8px;
77 | font-weight: 500;
78 | font-size: 27px;
79 | color: #b1040e; // no theme in place
80 | }
81 | `;
82 |
83 | const PaperComponent = ({ children, ...props }) => {
84 | return {children};
85 | };
86 |
87 | const StyledPaper = styled(Paper)`
88 | width: 100vw;
89 | height: 100vh;
90 | border-radius: 0;
91 | @media (min-width: 750px) {
92 | max-width: 800px;
93 | max-height: 90vh;
94 | overflow: auto;
95 | }
96 | ::-webkit-scrollbar {
97 | width: 0;
98 | }
99 | `;
100 |
101 | export default CreateCompanyScreen;
102 |
--------------------------------------------------------------------------------
/src/apps/admin/routes/profile/graphql/index.js:
--------------------------------------------------------------------------------
1 | export * from './queries';
2 |
--------------------------------------------------------------------------------
/src/apps/admin/routes/profile/graphql/queries.js:
--------------------------------------------------------------------------------
1 | import gql from 'graphql-tag';
2 |
3 | export const GET_USER_ADMIN_COMPANIES = gql`
4 | query GetUserAdminCompanies($where: PartyAccountWhereUniqueInput!) {
5 | partyAccount(where: $where) {
6 | id
7 | admin {
8 | id
9 | yearFounded
10 | description
11 | name {
12 | id
13 | payload
14 | }
15 | logo {
16 | id
17 | payload
18 | }
19 | description
20 | metadata {
21 | id
22 | isDraft
23 | isPublic
24 | isRejected
25 | isUnverified
26 | isApproved
27 | isPendingReview
28 | }
29 | }
30 | }
31 | }
32 | `;
33 |
--------------------------------------------------------------------------------
/src/apps/admin/routes/profile/index.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import UserProfile from './profile';
3 | import CreateProfile from '../../../admin/routes/auth/profile';
4 | import { Mutation } from 'react-apollo';
5 | import { useQuery } from 'react-apollo-hooks';
6 |
7 | import {
8 | UPDATE_CURRENT_USER_MUTATION,
9 | GET_CURRENT_USER_QUERY,
10 | } from '../../../../graphql';
11 |
12 | export const UserProfileWithGraphQL = props => {
13 | const { data, loading, error, refetch } = useQuery(GET_CURRENT_USER_QUERY);
14 | if (loading) {
15 | return null;
16 | }
17 | if (error) {
18 | console.log(error);
19 | return null;
20 | }
21 |
22 | const { me } = data;
23 |
24 | while (me === 'undefined' || me === undefined) {
25 | refetch();
26 | return null;
27 | }
28 |
29 | if (typeof me !== 'undefined') {
30 | const { person } = me;
31 |
32 | if (person.metadata.isDraft === true) {
33 | return (
34 |
35 | {mutation => {
36 | return (
37 |
42 | );
43 | }}
44 |
45 | );
46 | } else if (person.metadata.isDraft === false) {
47 | return ;
48 | }
49 | }
50 | };
51 |
52 | export default UserProfileWithGraphQL;
53 |
--------------------------------------------------------------------------------
/src/apps/admin/routes/profile/listitems.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ListItem from '@material-ui/core/ListItem';
3 | import ListItemIcon from '@material-ui/core/ListItemIcon';
4 | import ListItemText from '@material-ui/core/ListItemText';
5 | import ListSubheader from '@material-ui/core/ListSubheader';
6 | import DashboardIcon from '@material-ui/icons/Dashboard';
7 | import ShoppingCartIcon from '@material-ui/icons/ShoppingCart';
8 | import PeopleIcon from '@material-ui/icons/People';
9 | import BarChartIcon from '@material-ui/icons/BarChart';
10 | import LayersIcon from '@material-ui/icons/Layers';
11 | import AssignmentIcon from '@material-ui/icons/Assignment';
12 | import { navigate } from '@reach/router';
13 | import { useUser } from '../../../../store/user-context';
14 |
15 | export function MainListItems(props) {
16 | const { logout } = useUser();
17 | return (
18 |
19 |
20 |
21 |
22 |
23 | navigate('/')} />
24 |
25 |
26 |
27 |
28 |
29 | logout()} />
30 |
31 |
32 | );
33 | }
34 |
35 | export default MainListItems;
36 |
37 | export const secondaryListItems = (
38 |
39 | {/*
Saved reports
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | */}
58 |
59 | );
60 |
--------------------------------------------------------------------------------
/src/apps/admin/routes/profile/styles.js:
--------------------------------------------------------------------------------
1 | const drawerWidth = 240;
2 |
3 | // TODO: Delete this file it is dead code;
4 |
5 | export const styles = theme => ({
6 | menuButton: {
7 | marginLeft: 12,
8 | marginRight: 36,
9 | },
10 | menuButtonHidden: {
11 | display: 'none',
12 | },
13 | title: {
14 | flexGrow: 1,
15 | },
16 |
17 | content: {
18 | flexGrow: 1,
19 | padding: theme.spacing(3),
20 | height: '100vh',
21 | overflow: 'auto',
22 | },
23 | chartContainer: {
24 | marginLeft: -22,
25 | },
26 | tableContainer: {
27 | height: 320,
28 | },
29 | h5: {
30 | marginBottom: theme.spacing(2),
31 | },
32 | paperRoot: {
33 | ...theme.mixins.gutters(),
34 | paddingTop: theme.spacing(2),
35 | paddingBottom: theme.spacing(2),
36 | marginBottom: theme.spacing(2),
37 | },
38 | card: {
39 | minWidth: 275,
40 | marginBottom: theme.spacing(2),
41 | color: 'black',
42 | },
43 | bullet: {
44 | display: 'inline-block',
45 | margin: '0 2px',
46 | transform: 'scale(0.8)',
47 | },
48 | });
49 |
--------------------------------------------------------------------------------
/src/atoms/containers.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from 'styled-components';
3 |
4 | export const Container = styled.div`
5 | display: flex;
6 | flex-direction: column;
7 | `;
8 |
9 | export const SectionWrapper = styled.div`
10 | display: flex;
11 | flex-direction: column;
12 | margin-top: 1rem;
13 | margin-bottom: 1rem;
14 | `;
15 |
--------------------------------------------------------------------------------
/src/atoms/index.js:
--------------------------------------------------------------------------------
1 | export * from './containers';
2 | export * from './inputs';
3 |
--------------------------------------------------------------------------------
/src/atoms/inputs.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const HiddenFileInput = styled.input.attrs({
4 | type: 'file',
5 | id: 'avatar',
6 | accept: 'image/*',
7 | })`
8 | width: 0.1px;
9 | height: 0.1px;
10 | opacity: 0;
11 | overflow: hidden;
12 | position: absolute;
13 | z-index: -1;
14 | `;
15 |
--------------------------------------------------------------------------------
/src/atoms/spinner.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { css } from 'styled-components';
3 | import { BounceLoader as ClipLoader } from 'react-spinners';
4 |
5 | import styled from 'styled-components';
6 |
7 | const override = css`
8 | display: block;
9 | margin: 0 auto;
10 | border-color: red;
11 | `;
12 |
13 | class AwesomeComponent extends React.Component {
14 | constructor(props) {
15 | super(props);
16 | this.state = {
17 | loading: true,
18 | };
19 | }
20 | render() {
21 | return (
22 |
23 |
29 |
30 | );
31 | }
32 | }
33 |
34 | export default AwesomeComponent;
35 |
36 | const Container = styled.div`
37 | height: 100vh;
38 | width: 100vw;
39 | display: flex;
40 | justify-content: center;
41 | align-items: center;
42 | background-color: #b1040e;
43 | `;
44 |
45 | const Wrapper = styled.div`
46 | height: 100vh;
47 | width: 100vw;
48 | `;
49 |
--------------------------------------------------------------------------------
/src/bootstrap.js:
--------------------------------------------------------------------------------
1 | import { install } from '@material-ui/styles';
2 |
3 | install();
4 |
--------------------------------------------------------------------------------
/src/components/PrivateRoute.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { navigate } from '@reach/router';
4 |
5 | const PrivateRoute = ({ component: Component, location, user, ...rest }) => {
6 | if (!user) {
7 | navigate('/app/login/', {
8 | state: {
9 | from: location,
10 | ...rest,
11 | },
12 | });
13 | }
14 |
15 | return ;
16 | };
17 |
18 | PrivateRoute.propTypes = {
19 | component: PropTypes.any.isRequired,
20 | };
21 |
22 | export default PrivateRoute;
23 |
--------------------------------------------------------------------------------
/src/components/header/__mocks__/index.js:
--------------------------------------------------------------------------------
1 | export default {
2 | headerLeftSectionMocks: [
3 | { title: 'Index', to: '/companies' },
4 | { title: 'About', to: '/about' },
5 | ],
6 | headerSecondaryMocks: [
7 | { title: 'Marketplace', to: '/tags/marketplace/' },
8 | { title: 'Document Automation', to: '/tags/document-automation' },
9 | { title: 'Practice Management', to: '/tags/practice-management' },
10 | { title: 'Research', to: '/tags/legal-research' },
11 | { title: 'Education', to: '/tags/legal-education' },
12 |
13 | { title: 'Discovery', to: '/tags/discovery' },
14 | { title: 'Analytics', to: '/tags/analytics' },
15 | { title: 'Compliance', to: '/tags/compliance' },
16 | ],
17 | featurePostMocks: [
18 | {
19 | title: 'Tattooed Unicorn Venmo Dreamcatcher ',
20 | date: 'Feb 16',
21 | description:
22 | 'Prism bespoke authentic, normcore tousled venmo deep v 3 wolf moon snackwave sriracha.',
23 | },
24 | {
25 | title: 'Hexagon Pok Pok Master Cleanse.',
26 | date: 'Feb 16',
27 | description:
28 | 'Chic brunch, waistcoat freegan craft beer echo park cronut.',
29 | },
30 | ],
31 | };
32 |
--------------------------------------------------------------------------------
/src/components/header/header.one.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import Typography from '@material-ui/core/Typography';
4 | import { Link as GatsbyLink } from 'gatsby';
5 | import Link from '@material-ui/core/Link';
6 |
7 | export function HeaderCenter({ siteTitle, classes, ...props }) {
8 | return (
9 |
10 | {siteTitle && (
11 | (
13 |
14 | ))}
15 | variant="h5"
16 | color="inherit"
17 | style={{
18 | fontWeight: '700',
19 | letterSpacing: '-.5px',
20 | textDecoration: 'none',
21 | }}
22 | noWrap
23 | >
24 | {siteTitle}
25 |
26 | )}
27 |
28 | );
29 | }
30 |
31 | HeaderCenter.propTypes = {
32 | siteTitle: PropTypes.string.isRequired,
33 | };
34 |
35 | export default HeaderCenter;
36 |
--------------------------------------------------------------------------------
/src/components/header/header.secondary.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { makeStyles } from '@material-ui/styles';
3 | import { Link as GatsbyLink } from 'gatsby';
4 | import Toolbar from '@material-ui/core/Toolbar';
5 | import Button from '@material-ui/core/Button';
6 |
7 | const AdapterLink = React.forwardRef((props, ref) => (
8 |
9 | ));
10 |
11 | const useStyles = makeStyles(theme => ({
12 | toolbarSecondary: {
13 | justifyContent: 'space-between',
14 | },
15 |
16 | sectionDesktop: {
17 | display: 'none',
18 | [theme.breakpoints.up('lg')]: {
19 | display: 'flex',
20 | justifyContent: 'center',
21 | },
22 | },
23 | }));
24 |
25 | export function SecondaryHeader(props) {
26 | const classes = useStyles();
27 | const { sections } = props;
28 | return (
29 |
30 |
31 | {sections &&
32 | sections.map(section => (
33 |
44 | ))}
45 |
46 |
47 | );
48 | }
49 |
50 | export default SecondaryHeader;
51 |
--------------------------------------------------------------------------------
/src/components/header/header.styles.js:
--------------------------------------------------------------------------------
1 | import { fade } from '@material-ui/core/styles/colorManipulator';
2 |
3 | export const styles = theme => ({
4 | layout: {
5 | display: 'flex',
6 | justifyContent: 'space-between',
7 | marginLeft: theme.spacing(3),
8 | marginRight: theme.spacing(3),
9 | },
10 | headerAppBar: {
11 | display: 'flex',
12 | alignItems: 'space-between',
13 | flexDirection: 'column',
14 | zIndex: 1300,
15 | boxShadow: 'none',
16 | },
17 | toolbarMain: {
18 | borderBottom: `1px solid ${theme.palette.grey[300]}`,
19 | flexGrow: 1,
20 | display: 'flex',
21 | justifyContent: 'space-between',
22 | },
23 | inputRoot: {
24 | color: 'primary',
25 | width: '100%',
26 | },
27 | container: {
28 | flexGrow: 1,
29 | position: 'relative',
30 | },
31 | paper: {
32 | position: 'absolute',
33 | zIndex: 1,
34 | marginTop: theme.spacing(1),
35 | left: 0,
36 | right: 0,
37 | },
38 | chip: {
39 | margin: `${theme.spacing(0.5)}px ${theme.spacing(0.25)}px`,
40 | },
41 | wrapper: {
42 | display: 'flex',
43 | justifyContent: 'center',
44 | alignItems: 'center',
45 | },
46 | inputInput: {
47 | paddingTop: theme.spacing(1),
48 | paddingRight: theme.spacing(1),
49 | paddingBottom: theme.spacing(1),
50 | paddingLeft: theme.spacing(10),
51 | transition: theme.transitions.create('width'),
52 | width: '100%',
53 | [theme.breakpoints.up('sm')]: {
54 | width: 400,
55 | '&:focus': {
56 | width: 500,
57 | },
58 | },
59 | },
60 | sectionDesktop: {
61 | display: 'none',
62 | [theme.breakpoints.up('md')]: {
63 | display: 'flex',
64 | },
65 | },
66 | sectionMobile: {
67 | display: 'flex',
68 | [theme.breakpoints.up('md')]: {
69 | display: 'none',
70 | },
71 | },
72 | search: {
73 | position: 'relative',
74 | borderRadius: theme.shape.borderRadius,
75 | backgroundColor: fade(theme.palette.primary.main, 0.15),
76 | '&:hover': {
77 | backgroundColor: fade(theme.palette.primary.main, 0.25),
78 | },
79 | marginLeft: 0,
80 | width: '100%',
81 | [theme.breakpoints.up('sm')]: {
82 | marginLeft: theme.spacing(1),
83 | width: 'auto',
84 | },
85 | },
86 | searchIcon: {
87 | width: theme.spacing(9),
88 | height: '100%',
89 | position: 'absolute',
90 | pointerEvents: 'none',
91 | display: 'flex',
92 | alignItems: 'center',
93 | justifyContent: 'center',
94 | },
95 | });
96 |
--------------------------------------------------------------------------------
/src/components/header/header.three.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import PropTypes from 'prop-types';
3 | import { Link as GatsbyLink } from 'gatsby';
4 | import Link from '@material-ui/core/Link';
5 | import Button from '@material-ui/core/Button';
6 | import { useUser } from '../../store/user-context';
7 | import { navigate } from '@reach/router';
8 | import { CreateCompanyModalContext } from '../../store/modal-context';
9 | import renderAvatar from './helpers/renderAvatar';
10 |
11 | export function HeaderLeft({ sections, classes, ...props }) {
12 | const { showModal } = useContext(CreateCompanyModalContext);
13 | const { data, logout } = useUser();
14 | const { user } = data;
15 |
16 | return (
17 |
18 |
19 | {sections &&
20 | sections.map(section => {
21 | return (
22 |
37 | );
38 | })}
39 |
40 |
52 |
53 |
54 | {user ? (
55 | renderAvatar(user)
56 | ) : (
57 |
58 |
68 |
69 | )}
70 |
71 |
72 | );
73 | }
74 |
75 | HeaderLeft.propTypes = {
76 | sections: PropTypes.arrayOf(
77 | PropTypes.shape({
78 | title: PropTypes.string.isRequired,
79 | })
80 | ),
81 | };
82 |
83 | HeaderLeft.defaultProps = {
84 | sections: [],
85 | };
86 |
87 | export default HeaderLeft;
88 |
--------------------------------------------------------------------------------
/src/components/header/header.two.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { styles } from './header.styles';
4 | import { withStyles } from '@material-ui/core/styles';
5 | import MainSearch from '../search';
6 |
7 | export function HeaderRight(props) {
8 | const { classes, allSitePages, shouldShowSearch = true } = props;
9 |
10 | if (shouldShowSearch === false) {
11 | return null;
12 | }
13 |
14 | return (
15 |
16 |
21 |
22 | );
23 | }
24 |
25 | HeaderRight.propTypes = {
26 | classes: PropTypes.object,
27 | allSitePages: PropTypes.any,
28 | };
29 |
30 | export default withStyles(styles)(HeaderRight);
31 |
--------------------------------------------------------------------------------
/src/components/header/helpers/renderAvatar.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Avatar from '@material-ui/core/Avatar';
3 | import PersonIcon from '@material-ui/icons/Person';
4 | import { navigate } from '@reach/router';
5 | import PersonIcon from '@material-ui/icons/Person';
6 |
7 | function renderAvatar(user) {
8 | const { person } = user;
9 | const { avatar, name } = person;
10 |
11 | if (avatar && avatar.length > 0) {
12 | const [userAvatar, ...rest] = avatar;
13 | return (
14 | navigate('/app/profile/')}
17 | style={{ marginLeft: 10 }}
18 | imgProps={{
19 | style: { maxWidth: '100%', maxHeight: '100%' },
20 | }}
21 | />
22 | );
23 | } else if (name && name.length > 0) {
24 | const [userName, ...rest] = name;
25 | const { firstName, lastName } = userName;
26 | return (
27 | navigate('/app/profile/')}
29 | style={{ marginLeft: 10 }}
30 | imgProps={{
31 | style: { maxWidth: '100%', maxHeight: '100%' },
32 | }}
33 | >
34 | {`${firstName[0] + lastName[0]}`}
35 |
36 | );
37 | }
38 |
39 | return (
40 | navigate('/app/profile/')}
42 | style={{ marginLeft: 10 }}
43 | imgProps={{
44 | style: { maxWidth: '100%', maxHeight: '100%' },
45 | }}
46 | >
47 |
48 |
49 | );
50 | }
51 |
52 | export default renderAvatar;
53 |
--------------------------------------------------------------------------------
/src/components/header/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import AppBar from '@material-ui/core/AppBar';
3 | import Toolbar from '@material-ui/core/Toolbar';
4 | import HeaderThree from './header.three';
5 | import HeaderOne from './header.one';
6 | import HeaderTwo from './header.two';
7 | import SecondaryHeader from './header.secondary';
8 | import Hidden from '@material-ui/core/Hidden';
9 | import MobileNav from './header.mobile';
10 | import { styles } from './header.styles';
11 | import { withStyles } from '@material-ui/core/styles';
12 | import mocks from './__mocks__';
13 |
14 | export function Header({
15 | siteTitle,
16 | classes,
17 | shouldShowSecondaryHeader = true,
18 | allSitePage,
19 | shouldShowSearch,
20 | }) {
21 | return (
22 |
23 |
28 |
29 |
30 |
31 |
36 |
40 |
41 |
42 |
48 |
49 |
50 | {shouldShowSecondaryHeader && (
51 |
52 | )}
53 |
54 |
55 | );
56 | }
57 |
58 | export default withStyles(styles)(Header);
59 |
--------------------------------------------------------------------------------
/src/components/image.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StaticQuery, graphql } from 'gatsby';
3 | import Img from 'gatsby-image';
4 |
5 | /*
6 | * This component is built using `gatsby-image` to automatically serve optimized
7 | * images with lazy loading and reduced file sizes. The image is loaded using a
8 | * `StaticQuery`, which allows us to load the image from directly within this
9 | * component, rather than having to pass the image data down from pages.
10 | *
11 | * For more information, see the docs:
12 | * - `gatsby-image`: https://gatsby.app/gatsby-image
13 | * - `StaticQuery`: https://gatsby.app/staticquery
14 | */
15 |
16 | const Image = () => (
17 |
}
30 | />
31 | );
32 | export default Image;
33 |
--------------------------------------------------------------------------------
/src/components/input/field.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import styled from 'styled-components';
4 |
5 | const elevation = [
6 | 'box-shadow: inset 0 7px 9px -7px rgba(0,0,0, 0.7)',
7 | 'box-shadow: 0 1px 3px rgba(0,0,0, 0.12), 0 1px 2px rgba(0,0,0, 0.24)',
8 | 'box-shadow: 0 3px 6px rgba(0,0,0, 0.16), 0 3px 6px rgba(0,0,0, 0.23)',
9 | ];
10 |
11 | InputField.propTypes = {
12 | id: PropTypes.string.isRequired,
13 | leftButton: PropTypes.node,
14 | onBlur: PropTypes.func,
15 | onChange: PropTypes.func,
16 | onFocus: PropTypes.func,
17 | password: PropTypes.bool,
18 | rightButton: PropTypes.node,
19 | type: PropTypes.string,
20 | value: PropTypes.string,
21 | };
22 |
23 | InputField.defaultProps = {
24 | leftButton: undefined,
25 | password: false,
26 | rightButton: undefined,
27 | type: 'text',
28 | };
29 |
30 | export default InputField;
31 |
32 | /* eslint no-console: off */
33 |
34 | function InputField({ leftButton, rightButton, onChange, ...props }) {
35 | return (
36 |
37 | {leftButton && {leftButton}}
38 |
39 | {rightButton && (
40 | {rightButton}
41 | )}
42 |
43 | );
44 | }
45 |
46 | /* eslint react/prop-types: off */
47 | /* eslint no-magic-numbers: off */
48 |
49 | const InputWrapper = styled.div.attrs(props => ({
50 | height: props.height || 60,
51 | }))`
52 | max-width: 50vw;
53 | display: flex;
54 | height: 50px;
55 | width: 100%;
56 | position: relative;
57 | `;
58 |
59 | const InputButtonWrapper = styled.div.attrs(props => ({
60 | height: props.height || 60,
61 | inputWidth: props.width - 0.2 * props.height || 600 - 12,
62 | }))`
63 | width: ${props => props.height * 0.5}px;
64 | height: ${props => props.height * 0.5}px;
65 | position: absolute;
66 | top: ${props => props.height * 0.15}px;
67 | left: ${({ left, inputWidth }) => (left ? '5px' : `${inputWidth * 0.925}px`)};
68 | z-index: 10;
69 | `;
70 |
71 | const Input = styled.input.attrs(props => ({
72 | height: props.height || 60,
73 | readOnly: props.readOnly,
74 | width: props.width || 600,
75 | }))`
76 | max-width: ${props => props.width}px;
77 | min-height: ${props => props.height * 0.75}px;
78 | width: 100%;
79 | cursor: text;
80 | outline: none;
81 | overflow: hidden;
82 | font-size: 1rem;
83 | border: none;
84 | border: solid 1px #ccc;
85 | border-radius: 10px;
86 |
87 | padding-left: 19px;
88 |
89 | &:focus {
90 | outline: none !important;
91 | border: 1px solid #f4f4f4;
92 | ${elevation[1]};
93 | }
94 | `;
95 |
--------------------------------------------------------------------------------
/src/components/input/index.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codexstanford/techlist-frontend-web/06be56c7f1bbaa34e65ba942a24fe29257ae4b7f/src/components/input/index.js
--------------------------------------------------------------------------------
/src/components/input/input.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import Field from './field';
4 | import Label from './label';
5 | import styled from 'styled-components';
6 |
7 | TextInput.propTypes = {
8 | children: PropTypes.node,
9 | id: PropTypes.string,
10 | label: PropTypes.string,
11 | labelModifiers: PropTypes.array,
12 | onChange: PropTypes.func,
13 | width: PropTypes.number,
14 | };
15 |
16 | function TextInput({ label, id, onChange, width, labelModifiers, ...props }) {
17 | return (
18 |
19 | {label && (
20 |
28 | )}
29 |
30 |
31 | );
32 | }
33 |
34 | export default TextInput;
35 |
36 | const StyledInputWrapper = styled.div`
37 | margin: 1rem 0;
38 | display: flex;
39 | justify-content: center;
40 | position: relative;
41 | font-weight: 700;
42 | width: 100%;
43 | &:focus + label {
44 | font-weight: 900;
45 | }
46 | `;
47 |
--------------------------------------------------------------------------------
/src/components/input/label.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import styled from 'styled-components';
4 | import { applyStyleModifiers } from 'styled-components-modifiers';
5 |
6 | /* eslint no-magic-numbers: off */
7 |
8 | Label.propTypes = {
9 | children: PropTypes.node.isRequired,
10 | htmlFor: PropTypes.string,
11 | id: PropTypes.string,
12 | isFocused: PropTypes.bool,
13 | labelSpacing: PropTypes.number,
14 | modifiers: PropTypes.array,
15 | move: PropTypes.bool,
16 | style: PropTypes.object,
17 | };
18 |
19 | export default Label;
20 |
21 | function Label({ children, id, style, isFocused, move, ...props }) {
22 | return (
23 |
24 |
32 | {children}
33 |
34 |
35 | );
36 | }
37 |
38 | const StyledLabel = styled.label.attrs(props => ({}))`
39 | font-size: 13px;
40 | `;
41 |
42 | const LABEL_MODIFIERS = {
43 | odd: () => `
44 | background: rgb(243,244,245);
45 | `,
46 | };
47 |
48 | const StyledLabelWrap = styled.div`
49 | position: absolute;
50 | top: -10px;
51 | left: 15px;
52 | z-index: 10;
53 | background: white;
54 | padding: 0 5px;
55 |
56 | ${applyStyleModifiers(LABEL_MODIFIERS)};
57 | `;
58 |
--------------------------------------------------------------------------------
/src/components/layout.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { StaticQuery, graphql } from 'gatsby';
4 |
5 | import styled from 'styled-components';
6 | import Header from './header/index';
7 |
8 | const Layout = ({
9 | children,
10 | allSitePage,
11 | user,
12 | fullScreen = false,
13 | ...rest
14 | }) => (
15 |
16 | (
27 |
28 |
35 |
36 | {fullScreen ? (
37 | {children}
38 | ) : (
39 |
40 | {children}
41 |
42 | )}
43 |
44 |
45 |
46 | )}
47 | />
48 |
49 | );
50 |
51 | Layout.propTypes = {
52 | children: PropTypes.node.isRequired,
53 | };
54 |
55 | export default Layout;
56 |
57 | const AppWrapper = styled.div`
58 | color: white;
59 |
60 | min-height: 100vh;
61 | `;
62 | const MainWrapper = styled.main`
63 | max-width: 1250px;
64 | margin: 0 auto;
65 | `;
66 | const FSMainWrapper = styled.main``;
67 |
--------------------------------------------------------------------------------
/src/components/navigation/navbar.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'gatsby';
3 | import styled from 'styled-components';
4 |
5 | export default () => (
6 |
7 | You are not logged in
8 |
9 |
16 |
17 | );
18 |
19 | const StyledNavBarWrapper = styled.div`
20 | background-color: #b1040e;
21 | display: flex;
22 | flex: 1;
23 | justify-content: space-between;
24 | `;
25 |
--------------------------------------------------------------------------------
/src/components/search/helpers/getSuggestion.js:
--------------------------------------------------------------------------------
1 | import deburr from 'lodash/deburr';
2 |
3 | export function getSuggestions({ value, data }) {
4 | const inputValue = deburr(value.trim()).toLowerCase();
5 | const inputLength = inputValue.length;
6 | let count = 0;
7 |
8 | return inputLength === 0
9 | ? []
10 | : data.filter(suggestion => {
11 | let keep;
12 | if (
13 | count < 5 &&
14 | !suggestion.person &&
15 | suggestion.name &&
16 | suggestion.name[0].payload.slice(0, inputLength).toLowerCase() ===
17 | inputValue
18 | ) {
19 | keep = true;
20 | } else if (
21 | count < 5 &&
22 | suggestion.person &&
23 | suggestion.person.name &&
24 | `${suggestion.person.name[0].firstName} ${suggestion.person.name[0].lastName}`
25 | .slice(0, inputLength)
26 | .toLowerCase() === inputValue
27 | ) {
28 | keep = true;
29 | }
30 |
31 | if (keep) {
32 | count += 1;
33 | }
34 |
35 | return keep;
36 | });
37 | }
38 |
--------------------------------------------------------------------------------
/src/components/search/helpers/index.js:
--------------------------------------------------------------------------------
1 | export { renderSuggestion } from './renderSuggestion';
2 | export { renderInput } from './renderInput';
3 | export { getSuggestions } from './getSuggestion';
4 |
--------------------------------------------------------------------------------
/src/components/search/helpers/renderInput.js:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import TextField from '@material-ui/core/TextField';
3 |
4 | export function renderInput(inputProps) {
5 | const { InputProps, classes, ref, ...other } = inputProps;
6 |
7 | return (
8 |
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/src/components/search/helpers/renderSuggestion.js:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import MenuItem from '@material-ui/core/MenuItem';
3 |
4 | export function renderSuggestion({
5 | suggestion,
6 | index,
7 | itemProps,
8 | highlightedIndex,
9 | selectedItem,
10 | }) {
11 | const isHighlighted = highlightedIndex === index;
12 | const isSelected = (selectedItem || '').indexOf(suggestion.label) > -1;
13 |
14 | return (
15 |
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/src/components/seo.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import Helmet from 'react-helmet';
4 | import { StaticQuery, graphql } from 'gatsby';
5 |
6 | function SEO({ description, lang, meta, keywords, title }) {
7 | return (
8 | {
11 | const metaDescription =
12 | description || data.site.siteMetadata.description;
13 | return (
14 | 0
56 | ? {
57 | name: `keywords`,
58 | content: keywords.join(`, `),
59 | }
60 | : []
61 | )
62 | .concat(meta)}
63 | />
64 | );
65 | }}
66 | />
67 | );
68 | }
69 |
70 | SEO.defaultProps = {
71 | lang: `en`,
72 | meta: [],
73 | keywords: [],
74 | };
75 |
76 | SEO.propTypes = {
77 | description: PropTypes.string,
78 | lang: PropTypes.string,
79 | meta: PropTypes.array,
80 | keywords: PropTypes.arrayOf(PropTypes.string),
81 | title: PropTypes.string.isRequired,
82 | };
83 |
84 | export default SEO;
85 |
86 | const detailsQuery = graphql`
87 | query DefaultSEOQuery {
88 | site {
89 | siteMetadata {
90 | title
91 | description
92 | author
93 | }
94 | }
95 | }
96 | `;
97 |
--------------------------------------------------------------------------------
/src/components/transition.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import PropTypes from 'prop-types';
3 | import posed, { PoseGroup } from 'react-pose';
4 | import { timeout } from 'constants/transition';
5 |
6 | class Transition extends PureComponent {
7 | render() {
8 | const { children, location } = this.props;
9 |
10 | const RoutesContainer = posed.div({
11 | enter: { opacity: 1, delay: timeout, delayChildren: timeout },
12 | exit: { opacity: 0 },
13 | });
14 |
15 | // To enable page transitions on mount / initial load,
16 | // use the prop `animateOnMount={true}` on `PoseGroup`.
17 | return (
18 |
19 | {children}
20 |
21 | );
22 | }
23 | }
24 |
25 | Transition.propTypes = {
26 | children: PropTypes.node.isRequired,
27 | location: PropTypes.object.isRequired,
28 | };
29 |
30 | export default Transition;
31 |
--------------------------------------------------------------------------------
/src/constants/transition.js:
--------------------------------------------------------------------------------
1 | export const timeout = 0;
2 |
--------------------------------------------------------------------------------
/src/features/landinghero/hero.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Paper from '@material-ui/core/Paper';
3 | import Typography from '@material-ui/core/Typography';
4 | import Grid from '@material-ui/core/Grid';
5 | import Link from '@material-ui/core/Link';
6 |
7 | export function Hero(props) {
8 | const { classes, data } = props;
9 | const { title, description, content } = data;
10 | return (
11 |
12 |
13 |
14 |
15 |
16 |
24 | {title}
25 |
26 |
27 |
33 | {description}
34 |
35 |
36 |
37 |
38 |
39 |
40 | );
41 | }
42 |
43 | export default Hero;
44 |
--------------------------------------------------------------------------------
/src/features/landinghero/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { withStyles } from '@material-ui/core/styles';
4 | import styles from './styles';
5 | import Hero from './hero';
6 | import Sidekick from './sidekick';
7 |
8 | export function LandingHero(props) {
9 | const {
10 | data: {
11 | allNews: { edges: data },
12 | },
13 | } = props;
14 |
15 | const [heroData, ...sidekickData] = data.map(item => item.node);
16 |
17 | return (
18 |
19 |
20 |
21 |
22 | );
23 | }
24 |
25 | export default withStyles(styles)(LandingHero);
26 |
--------------------------------------------------------------------------------
/src/features/landinghero/sidekick.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Grid from '@material-ui/core/Grid';
3 | import SidekickItem from './sidekickItem';
4 |
5 | export function Sidekick(props) {
6 | const { classes, data } = props;
7 | return (
8 |
9 |
10 | {data
11 | .filter(item => {
12 | return item.content && item.imageUrl;
13 | })
14 | .slice(0, 3)
15 | .map(item => {
16 | return (
17 |
18 |
19 |
20 | );
21 | })}
22 |
23 |
24 | );
25 | }
26 |
27 | export default Sidekick;
28 |
--------------------------------------------------------------------------------
/src/features/landinghero/sidekickItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Typography from '@material-ui/core/Typography';
3 | import Card from '@material-ui/core/Card';
4 | import CardActionArea from '@material-ui/core/CardActionArea';
5 | import CardActions from '@material-ui/core/CardActions';
6 | import CardContent from '@material-ui/core/CardContent';
7 | import CardMedia from '@material-ui/core/CardMedia';
8 | import Button from '@material-ui/core/Button';
9 | import Link from '@material-ui/core/Link';
10 |
11 | export function SidekickItem(props) {
12 | const { classes, title, date, content, imageUrl, id } = props;
13 | return (
14 |
15 |
25 |
26 |
27 |
28 | {title
29 | .split(' ')
30 | .slice(0, 6)
31 | .join(' ')}
32 |
33 |
34 |
35 | {content &&
36 | content
37 | .replace(/<[^>]*>/, ' ')
38 | .split(' ')
39 | .slice(0, 25)
40 | .join(' ') + '...'}
41 |
42 |
43 |
44 |
45 |
54 |
55 |
56 |
57 | );
58 | }
59 |
60 | export default SidekickItem;
61 |
--------------------------------------------------------------------------------
/src/features/landinghero/styles.js:
--------------------------------------------------------------------------------
1 | export default theme => ({
2 | mainFeaturedPost: {
3 | backgroundColor: '#544948',
4 | color: theme.palette.common.white,
5 | marginTop: theme.spacing(4),
6 | marginBottom: theme.spacing(4),
7 | },
8 | mainFeaturedPostContent: {
9 | padding: theme.spacing(2),
10 |
11 | [theme.breakpoints.up('md')]: {
12 | padding: theme.spacing(6),
13 | },
14 | },
15 | mainFeaturedPostContentSmall: {
16 | paddingTop: '12px',
17 | [theme.breakpoints.down('sm')]: {
18 | fontSize: '1.2rem',
19 | fontWeight: 500,
20 | },
21 | },
22 | mainFeaturedPostContentSmallSub: {
23 | [theme.breakpoints.down('sm')]: {
24 | paddingTop: '12px',
25 | fontSize: '.9rem',
26 | },
27 | },
28 | mainGrid: {
29 | marginTop: theme.spacing(3),
30 | },
31 | card: {
32 | alignSelf: 'stretch',
33 | flex: 1,
34 | },
35 | cardDetails: {
36 | flex: 1,
37 | },
38 | cardMedia: {
39 | objectFit: 'cover',
40 | alignItems: 'stetch',
41 | },
42 | });
43 |
--------------------------------------------------------------------------------
/src/graphql/index.js:
--------------------------------------------------------------------------------
1 | export * from './queries';
2 | export * from './mutations';
3 |
--------------------------------------------------------------------------------
/src/graphql/mutations/index.js:
--------------------------------------------------------------------------------
1 | import gql from 'graphql-tag';
2 |
3 | export const UPDATE_PERSON_AFFILIATION = gql`
4 | mutation UpdatePersonAffiliation(
5 | $data: PersonOrganizationAffiliationUpdateInput!
6 | $where: PersonOrganizationAffiliationWhereUniqueInput!
7 | ) {
8 | updateAffiliation(where: $where, data: $data) {
9 | __typename
10 | id
11 | fromDate
12 | throughDate
13 | role
14 | title
15 | description
16 | organization {
17 | __typename
18 | id
19 | name {
20 | __typename
21 | id
22 | payload
23 | }
24 | }
25 | }
26 | }
27 | `;
28 |
29 | export const UPDATE_PERSON = gql`
30 | mutation UpdatePerson(
31 | $data: PersonUpdateInput!
32 | $where: PersonWhereUniqueInput!
33 | ) {
34 | updatePerson(where: $where, data: $data) {
35 | id
36 | __typename
37 |
38 | avatar {
39 | id
40 | __typename
41 | payload
42 | }
43 | name {
44 | id
45 | __typename
46 | firstName
47 | lastName
48 | }
49 | }
50 | }
51 | `;
52 |
53 | export const CREATE_USER_MUTATION = gql`
54 | mutation CreateUser($data: PartyAccountCreateInput!) {
55 | createPartyAccount(data: $data) {
56 | __typename
57 | id
58 | }
59 | }
60 | `;
61 |
62 | export const CREATE_PERSON_MUTATION = gql`
63 | mutation CreatePerson($data: PersonCreateInput!) {
64 | createPerson(data: $data) {
65 | __typename
66 | id
67 | }
68 | }
69 | `;
70 |
71 | export const UPDATE_CURRENT_USER_MUTATION = gql`
72 | mutation UpdateCurrentUser(
73 | $data: PartyAccountUpdateInput!
74 | $where: PartyAccountWhereUniqueInput!
75 | ) {
76 | updatePartyAccount(where: $where, data: $data) {
77 | __typename
78 | id
79 | createdAt
80 | name
81 | email
82 | phone
83 | phone_number_verified
84 | email_verified
85 | person {
86 | id
87 | __typename
88 | name {
89 | id
90 | __typename
91 | firstName
92 | lastName
93 | middleInitial
94 | suffix
95 | fromDate
96 | throughDate
97 | }
98 | email {
99 | __typename
100 | id
101 | payload
102 | fromDate
103 | throughDate
104 | }
105 | avatar {
106 | __typename
107 | id
108 | payload
109 | fromDate
110 | throughDate
111 | }
112 | metadata {
113 | id
114 | __typename
115 | isDraft
116 | isPublic
117 | isRejected
118 | isApproved
119 | isPendingReview
120 | }
121 | affiliation {
122 | id
123 | __typename
124 | fromDate
125 | throughDate
126 | title
127 | role
128 | description
129 | organization {
130 | id
131 | __typename
132 | name {
133 | id
134 | __typename
135 | payload
136 | }
137 | logo {
138 | id
139 | __typename
140 | payload
141 | }
142 | }
143 | }
144 | }
145 | }
146 | }
147 | `;
148 |
--------------------------------------------------------------------------------
/src/graphql/queries/index.js:
--------------------------------------------------------------------------------
1 | import gql from 'graphql-tag';
2 |
3 | export const GET_CURRENT_USER_QUERY = gql`
4 | query GetMe {
5 | me {
6 | __typename
7 | id
8 | createdAt
9 | name
10 | email
11 | phone
12 | phone_number_verified
13 | email_verified
14 | admin {
15 | __typename
16 | id
17 | }
18 | person {
19 | __typename
20 | id
21 | name {
22 | __typename
23 | id
24 | firstName
25 | lastName
26 | middleInitial
27 | suffix
28 | fromDate
29 | throughDate
30 | }
31 | email {
32 | __typename
33 | id
34 | payload
35 | fromDate
36 | throughDate
37 | }
38 | avatar {
39 | __typename
40 | id
41 | payload
42 | fromDate
43 | throughDate
44 | }
45 | phone {
46 | __typename
47 | id
48 | payload
49 | fromDate
50 | throughDate
51 | isPublic
52 | isPrimary
53 | }
54 | metadata {
55 | __typename
56 | id
57 | isDraft
58 | isPublic
59 | isRejected
60 | isApproved
61 | isPendingReview
62 | }
63 | affiliation {
64 | __typename
65 | id
66 | fromDate
67 | throughDate
68 | title
69 | role
70 | description
71 | organization {
72 | __typename
73 | id
74 | name {
75 | id
76 | __typename
77 | payload
78 | }
79 | logo {
80 | id
81 | __typename
82 | payload
83 | }
84 | }
85 | }
86 | }
87 | }
88 | }
89 | `;
90 |
91 | export const GET_USER_QUERY = gql`
92 | query GetUserQuery($where: PartyAccountWhereUniqueInput!) {
93 | partyAccount(where: $where) {
94 | id
95 | cognitoId
96 | }
97 | }
98 | `;
99 |
100 | // TODO: delete this code once refactored company module is in place
101 |
102 | export const GET_COMPANY_TARGET_MARKETS = gql`
103 | query GetTargetMarketsQuery {
104 | organizationTargetMarkets {
105 | id
106 | payload
107 | }
108 | }
109 | `;
110 |
--------------------------------------------------------------------------------
/src/helpers/callAll.js:
--------------------------------------------------------------------------------
1 | const callAll = (...fns) => (...args) => fns.forEach(fn => fn && fn(...args));
2 |
3 | export default callAll;
4 |
--------------------------------------------------------------------------------
/src/helpers/enums.js:
--------------------------------------------------------------------------------
1 | export const steps = {
2 | ACCOUNT: 0,
3 | PROFILE: 1,
4 | COMPANY: 2,
5 | TERMS: 3,
6 | };
7 |
--------------------------------------------------------------------------------
/src/helpers/formatPhoneNumber.js:
--------------------------------------------------------------------------------
1 | export const formatPhoneNumber = (value, setFieldValue) => {
2 | var cleaned = ('' + value).replace(/\D/g, '').substring(0, 10);
3 |
4 | if (value.substring(0, 2) === '+1') {
5 | cleaned = cleaned.substring(1, 10);
6 | }
7 |
8 | var match = cleaned.match(/^(1|)?(\d{3})(\d{3})(\d{4})$/);
9 | if (match) {
10 | var intlCode =
11 | match[1] || value.length === 10 || value.length === 11 ? '+1 ' : '';
12 | // if (setFieldValue) {
13 | // // setTimeout(() => {
14 | // return [intlCode, '(', match[2], ') ', match[3], '-', match[4]].join();
15 | // // }, 1000);
16 | // // setTimeout(() => {
17 | // // setFieldValue(
18 | // // 'phone',
19 | // // [intlCode, '(', match[2], ') ', match[3], '-', match[4]].join('')
20 | // // );
21 | // // }, 500);
22 | // }
23 | return [intlCode, '(', match[2], ') ', match[3], '-', match[4]].join('');
24 | }
25 | return cleaned;
26 | };
27 |
28 | export const unformatPhoneNumber = value => {
29 | const cleanedValue = value.replace(/[\D]/g, '');
30 | return `+${cleanedValue}`;
31 | };
32 |
--------------------------------------------------------------------------------
/src/helpers/wrapPageElement.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Transition from 'components/transition';
3 |
4 | /* eslint react/prop-types: 0 */
5 |
6 | const wrapPageElement = ({ element, props }) => {
7 | return {element};
8 | };
9 |
10 | export default wrapPageElement;
11 |
--------------------------------------------------------------------------------
/src/html.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | export default function HTML(props) {
5 | return (
6 |
7 |
8 |
9 |
10 |
14 | {props.headComponents}
15 |
16 |
17 | {props.preBodyComponents}
18 |
21 |
26 | {props.postBodyComponents}
27 |
28 |
33 |
34 |
35 | );
36 | }
37 |
38 | HTML.propTypes = {
39 | htmlAttributes: PropTypes.object,
40 | headComponents: PropTypes.array,
41 | bodyAttributes: PropTypes.object,
42 | preBodyComponents: PropTypes.array,
43 | body: PropTypes.string,
44 | postBodyComponents: PropTypes.array,
45 | };
46 |
--------------------------------------------------------------------------------
/src/images/sls-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codexstanford/techlist-frontend-web/06be56c7f1bbaa34e65ba942a24fe29257ae4b7f/src/images/sls-logo.png
--------------------------------------------------------------------------------
/src/molecules/avatars.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Avatar from '@material-ui/core/Avatar';
3 | import Fab from '@material-ui/core/Fab';
4 |
5 | import { HiddenFileInput } from '../atoms';
6 |
7 | export const AvatarWithPicker = props => {
8 | const { setFieldValue, setImage, image } = props;
9 | return (
10 |
11 | {
13 | e.stopPropagation();
14 | e.preventDefault();
15 | console.log(e.target.files);
16 |
17 | const fileReader = new FileReader();
18 | fileReader.onloadend = e => {
19 | const content = fileReader.result;
20 | setFieldValue('logo', content);
21 | setImage(content);
22 | };
23 | if (e.target.files.length > 0) {
24 | fileReader.readAsDataURL(e.target.files[0]);
25 | }
26 | }}
27 | />
28 |
47 |
48 | );
49 | };
50 |
--------------------------------------------------------------------------------
/src/molecules/index.js:
--------------------------------------------------------------------------------
1 | export * from './avatars';
2 |
--------------------------------------------------------------------------------
/src/pages/404.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import Layout from '../components/layout';
4 | import SEO from '../components/seo';
5 |
6 | const NotFoundPage = () => (
7 |
8 |
9 | NOT FOUND
10 | You just hit a route that doesn't exist... the sadness.
11 |
12 | );
13 |
14 | export default NotFoundPage;
15 |
--------------------------------------------------------------------------------
/src/pages/app.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { Router } from '@reach/router';
4 | import Layout from '../components/layout';
5 |
6 | import Admin from '../apps/admin';
7 |
8 | export function App(props) {
9 | return ;
10 | return (
11 |
12 |
13 |
14 |
15 |
16 |
17 | );
18 | }
19 |
20 | export default App;
21 |
22 | const Profile = () => {
23 | return Profile
;
24 | };
25 |
26 | const Login = () => {
27 | return Login
;
28 | };
29 |
--------------------------------------------------------------------------------
/src/pages/tags.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { withStyles } from '@material-ui/core/styles';
3 | import Layout from '../components/layout';
4 | import SEO from '../components/seo';
5 | import ReactTable from 'react-table';
6 | import ListItemText from '@material-ui/core/ListItemText';
7 | import ListItem from '@material-ui/core/ListItem';
8 | import { Link as GatsbyLink, graphql } from 'gatsby';
9 | import _ from 'lodash';
10 | import Typography from '@material-ui/core/Typography';
11 | import 'react-table/react-table.css';
12 |
13 | const slugify = require('slugify');
14 |
15 | function toTitleCase(str) {
16 | return str.replace(/\w\S*/g, function(txt) {
17 | return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
18 | });
19 | }
20 |
21 | const Tags = ({ pageContext, data, classes, ...rest }) => {
22 | const { companyCategories } = data.allTechList;
23 | return (
24 |
25 |
29 |
30 | {
33 | return data
34 | .filter(item => item.name !== '----' && item.name !== '_-')
35 | .map(item => {
36 | const { name, id } = item;
37 | const formattedName = toTitleCase(name.split('_').join(' '));
38 | return {
39 | id,
40 | name: formattedName,
41 | };
42 | });
43 | }}
44 | defaultSorted={[
45 | {
46 | id: 'Categories',
47 | desc: false,
48 | },
49 | ]}
50 | columns={[
51 | {
52 | Header: () => Categories,
53 | accessor: edge => edge.name,
54 | id: edge => edge.id,
55 | Cell: props => {
56 | return (
57 |
62 |
65 | {props.value ? props.value : ''}
66 |
67 | }
68 | />
69 |
70 | );
71 | },
72 | },
73 | ]}
74 | data={companyCategories}
75 | />
76 |
77 |
78 | );
79 | };
80 |
81 | export default withStyles(theme => ({
82 | root: {
83 | width: '100%',
84 | backgroundColor: theme.palette.background.paper,
85 | marginTop: '1rem',
86 | color: '#333',
87 | },
88 | row: {
89 | width: '100%',
90 | },
91 | }))(Tags);
92 |
93 | export const pageQuery = graphql`
94 | query {
95 | site {
96 | siteMetadata {
97 | title
98 | }
99 | }
100 | allTechList {
101 | organizationCategories {
102 | id
103 | payload
104 | }
105 | }
106 | }
107 | `;
108 |
--------------------------------------------------------------------------------
/src/services/hacks.js:
--------------------------------------------------------------------------------
1 | import { hijackEffects } from 'stop-runaway-react-effects';
2 |
3 | if (process.env.NODE_ENV !== 'production') {
4 | hijackEffects();
5 | }
6 |
--------------------------------------------------------------------------------
/src/store/apollo.js:
--------------------------------------------------------------------------------
1 | import fetch from 'node-fetch';
2 | import { ApolloClient } from 'apollo-client';
3 | import { ApolloLink } from 'apollo-link';
4 | import { onError } from 'apollo-link-error';
5 | import apolloLogger from 'apollo-link-logger';
6 | import { InMemoryCache } from 'apollo-cache-inmemory';
7 | import { createPersistedQueryLink } from 'apollo-link-persisted-queries';
8 | import { setContext } from 'apollo-link-context';
9 | import { createHttpLink } from 'apollo-link-http';
10 |
11 | import { getUser, getToken } from './utils/auth-client';
12 |
13 | const clientCache = new InMemoryCache({
14 | dataIdFromObject: object => object.id || null,
15 | });
16 |
17 | /* tslint:no-shadow off */
18 |
19 | const httpLink = process.browser
20 | ? createPersistedQueryLink({ useGETForHashedQueries: true }).concat(
21 | createHttpLink({
22 | uri: process.env.GATSBY_GRAPHQL_ENDPOINT,
23 | credentials: 'include',
24 |
25 | useGETForQueries: true,
26 | includeExtensions: true,
27 | fetch: fetch,
28 | })
29 | )
30 | : createPersistedQueryLink({ useGETForHashedQueries: true }).concat(
31 | createHttpLink({
32 | uri: process.env.GATSBY_GRAPHQL_ENDPOINT,
33 | useGETForQueries: true,
34 | credentials: 'include',
35 | // uri: 'http://localhost:4000',
36 | includeExtensions: true,
37 |
38 | fetch: fetch,
39 | })
40 | );
41 |
42 | const asyncAuthLink = setContext(
43 | (_, { headers }) =>
44 | new Promise(async (success, fail) => {
45 | const token = await getToken().catch(err => fail(err));
46 | success({
47 | headers: {
48 | ...headers,
49 | authorization: `Bearer ${token}`,
50 | },
51 | });
52 | })
53 | );
54 |
55 | const errorLink = onError(({ graphQLErrors, networkError }) => {
56 | if (graphQLErrors) {
57 | graphQLErrors.map(({ message, locations, path }) =>
58 | console.log(
59 | `[GraphQL error]: Message: ${message}, Location: ${JSON.stringify(
60 | locations
61 | )}, Path: ${path}`
62 | )
63 | );
64 | }
65 | if (networkError) {
66 | console.log(`[Network error]: ${networkError}`);
67 | }
68 | });
69 |
70 | export function configureApolloClient() {
71 | return new ApolloClient({
72 | link: ApolloLink.from([apolloLogger, asyncAuthLink, errorLink, httpLink]),
73 | cache: clientCache,
74 | connectToDevTools: true,
75 | ssrMode: true,
76 |
77 | name: process.env.GATSBY_APPLICATION_NAME,
78 | version: process.env.GATSBY_APPLICATION_VERSION,
79 | });
80 | }
81 |
--------------------------------------------------------------------------------
/src/store/auth-context.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useAsync } from 'react-async';
3 | import * as authClient from './utils/auth-client';
4 | import { bootstrapAppData } from './utils/bootstrap';
5 | import FullPageSpinner from '../atoms/spinner';
6 |
7 | const AuthContext = React.createContext();
8 |
9 | function AuthProvider(props) {
10 | const { client } = props;
11 |
12 | const [firstAttemptFinished, setFirstAttemptFinished] = React.useState(false);
13 | const {
14 | data = { user: null, listItems: [] },
15 | error,
16 | isRejected,
17 | isPending,
18 | isSettled,
19 | reload,
20 | } = useAsync({
21 | promiseFn: bootstrapAppData,
22 | client,
23 | });
24 |
25 | React.useLayoutEffect(() => {
26 | if (isSettled) {
27 | setFirstAttemptFinished(true);
28 | }
29 | }, [isSettled]);
30 |
31 | if (!firstAttemptFinished) {
32 | if (isPending) {
33 | return ;
34 | }
35 | if (isRejected) {
36 | return (
37 |
38 |
Uh oh... There's a problem. Try refreshing the app.
39 |
{error.message}
40 |
41 | );
42 | }
43 | }
44 | const login = form => authClient.login(form).then(reload);
45 | const register = form => authClient.register(form).then(reload);
46 | const logout = () => authClient.logout().then(reload);
47 | const confirm = form => authClient.confirm(form).then(reload);
48 |
49 | return (
50 |
54 | );
55 | }
56 |
57 | function useAuth() {
58 | const context = React.useContext(AuthContext);
59 | if (context === undefined) {
60 | throw new Error(`useAuth must be used within a AuthProvider`);
61 | }
62 | return context;
63 | }
64 | export { AuthProvider, useAuth };
65 |
--------------------------------------------------------------------------------
/src/store/createContext.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | // const TestContext = React.createContext();
4 |
5 | // function TestContextProvier(props) {
6 | // const [count, setCount] = React.useState(0);
7 | // const value = React.useMemo(() => {
8 | // return {
9 | // count,
10 | // setCount,
11 | // };
12 | // }, [count]);
13 | // return ;
14 | // }
15 |
16 | // function useCount() {
17 | // const context = React.useContext(TestContext);
18 | // if (!context) {
19 | // throw new Error('useCount must be used within a CountProvider');
20 | // }
21 | // const { count, setCount } = context;
22 | // const increment = React.useCallback(() => setCount(c => c + 1), [setCount]);
23 |
24 | // return {
25 | // count,
26 | // increment,
27 | // };
28 | // }
29 |
30 | const { Provider, Consumer } = React.createContext();
31 |
32 | export { Provider, Consumer };
33 |
--------------------------------------------------------------------------------
/src/store/modal-context.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export const CreateCompanyModalContext = React.createContext({
4 | open: false,
5 | showModal: () => {},
6 | hideModal: () => {},
7 | });
8 |
--------------------------------------------------------------------------------
/src/store/provider.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import { ApolloProvider } from 'react-apollo';
4 | import { Provider } from './createContext';
5 | import { configureApolloClient } from './apollo';
6 | import { AuthProvider } from './auth-context';
7 | import { UserProvider } from './user-context';
8 |
9 | import { CreateCompanyModalContext } from './modal-context';
10 |
11 | import { ApolloProvider as ApolloHooksProvider } from 'react-apollo-hooks';
12 | import CssBaseline from '@material-ui/core/CssBaseline';
13 |
14 | import { createMuiTheme, responsiveFontSizes } from '@material-ui/core/styles';
15 |
16 | import { ThemeProvider } from '@material-ui/styles';
17 | import { UseModal } from './useModal';
18 |
19 | let theme = createMuiTheme({
20 | typography: {
21 | htmlFontSize: 14,
22 |
23 | fontFamily: [
24 | 'Source Sans Pro',
25 | '-apple-system',
26 | 'BlinkMacSystemFont',
27 | '"Segoe UI"',
28 | 'Roboto',
29 | '"Helvetica Neue"',
30 | 'Arial',
31 | 'sans-serif',
32 | '"Apple Color Emoji"',
33 | '"Segoe UI Emoji"',
34 | '"Segoe UI Symbol"',
35 | ].join(','),
36 | },
37 | palette: {
38 | primary: { main: '#b1040e' },
39 | secondary: { main: '#04b1a8' },
40 | link: {
41 | main: '#006CB8',
42 | },
43 | },
44 | colors: {
45 | primary: '#b1040e',
46 | link: '#006CB8',
47 | hover: '#00548f',
48 | },
49 | overrides: {
50 | MuiCssBaseline: {
51 | '@global': {
52 | '@font-face': ['Source Sans Pro'],
53 | },
54 | },
55 | },
56 | });
57 |
58 | theme = responsiveFontSizes(theme);
59 |
60 | export const client = configureApolloClient();
61 |
62 | class AppProvider extends Component {
63 | state = {
64 | open: false,
65 | showModal: () => this.setState({ open: true }),
66 | hideModal: () => this.setState({ open: false }),
67 | };
68 |
69 | componentDidCatch(error, errorInfo) {
70 | this.setState({ error });
71 | Sentry.configureScope(scope => {
72 | Object.keys(errorInfo).forEach(key => {
73 | scope.setExtra(key, errorInfo[key]);
74 | });
75 | });
76 | Sentry.captureException(error);
77 | }
78 |
79 | render() {
80 | return (
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 | {this.props.children}
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 | );
99 | }
100 | }
101 |
102 | AppProvider.propTypes = {
103 | children: PropTypes.node.isRequired,
104 | };
105 |
106 | export default AppProvider;
107 |
--------------------------------------------------------------------------------
/src/store/useModal.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import CreateCompanyScreen from '../apps/admin/routes/company';
4 | import { useUser } from './user-context';
5 | import withStyles from '@material-ui/core/styles/withStyles';
6 |
7 | import { styles } from '../apps/admin/config/styles';
8 | import { CreateCompanyModalContext } from './modal-context';
9 |
10 | export const UseModal = withStyles(styles)(({ modal, ...props }) => {
11 | const { open, hideModal } = useContext(CreateCompanyModalContext);
12 | const { data } = useUser();
13 | const { user } = data;
14 |
15 | switch (modal) {
16 | case 'createCompany':
17 | return ReactDOM.createPortal(
18 | ,
24 | document.body
25 | );
26 |
27 | default:
28 | return null;
29 | }
30 | });
31 |
--------------------------------------------------------------------------------
/src/store/user-context.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useAuth } from './auth-context';
3 |
4 | const UserContext = React.createContext(null);
5 |
6 | function UserProvider(props) {
7 | const { client } = props;
8 |
9 | const user = useAuth(client);
10 |
11 | return ;
12 | }
13 |
14 | function useUser() {
15 | const context = React.useContext(UserContext);
16 |
17 | if (context === undefined) {
18 | throw new Error(`useUser must be used within a UserProvider`);
19 | }
20 | return context;
21 | }
22 |
23 | export { UserProvider, useUser };
24 |
--------------------------------------------------------------------------------
/src/store/utils/auth-client.js:
--------------------------------------------------------------------------------
1 | import { LOCAL_STORAGE_KEY, GET_USER_QUERY } from './const';
2 |
3 | import fetch from 'node-fetch';
4 | global.fetch = global.fetch || fetch;
5 |
6 | import Auth from '@aws-amplify/auth';
7 | import Amplify from '@aws-amplify/core';
8 |
9 | const isBrowser = typeof window !== `undefined`;
10 |
11 | isBrowser &&
12 | Amplify.configure({
13 | Auth: {
14 | region: 'us-west-2',
15 | userPoolId: 'us-west-2_uzyDC8Snl',
16 | userPoolWebClientId: '181177ggq1ot45s6t791vposkr',
17 | },
18 | });
19 |
20 | function handleUserResponse({ signInUserSession, ...user }) {
21 | const { accessToken, idToken, refreshToken } = signInUserSession;
22 | const { jwtToken: token } = idToken;
23 |
24 | isBrowser && window.localStorage.setItem(LOCAL_STORAGE_KEY, token);
25 | return user;
26 | }
27 |
28 | function login({ username, password }) {
29 | if (isBrowser) {
30 | return Auth.signIn(username, password).then(data => {
31 | return handleUserResponse(data);
32 | });
33 | }
34 | }
35 |
36 | function logout() {
37 | if (isBrowser) {
38 | window.localStorage.removeItem(LOCAL_STORAGE_KEY);
39 | return Auth.signOut();
40 | }
41 | return Promise.resolve();
42 | }
43 |
44 | async function getToken() {
45 | if (window) {
46 | try {
47 | const session = await Auth.currentSession();
48 | const token = session.getIdToken().jwtToken || null;
49 | return Promise.resolve()
50 | .then(() => {
51 | window.localStorage.setItem(LOCAL_STORAGE_KEY, token);
52 | })
53 | .then(() => {
54 | return window.localStorage.getItem(LOCAL_STORAGE_KEY);
55 | });
56 | } catch (err) {
57 | console.log(err);
58 | return null;
59 | }
60 | }
61 | return null;
62 | }
63 |
64 | async function getUser(client) {
65 | const token = await getToken();
66 |
67 | if (!token) {
68 | client.resetStore();
69 | return Promise.resolve(null);
70 | }
71 | if (client !== undefined) {
72 | const { data } = await client
73 | .query({ query: GET_USER_QUERY })
74 | .catch(err => {
75 | logout();
76 | console.log(err);
77 | return Promise.reject(err);
78 | });
79 |
80 | return data;
81 | }
82 | }
83 |
84 | function register({ email, password, phone: phone_number, username }) {
85 | if (isBrowser) {
86 | return Auth.signUp({
87 | username,
88 | password,
89 | attributes: {
90 | email,
91 | phone_number,
92 | },
93 | });
94 | }
95 | }
96 |
97 | function confirm({ username, code }) {
98 | if (isBrowser) {
99 | return Auth.confirmSignUp(username, code, {});
100 | }
101 | }
102 |
103 | export { login, logout, getToken, register, getUser, confirm };
104 |
--------------------------------------------------------------------------------
/src/store/utils/bootstrap.js:
--------------------------------------------------------------------------------
1 | import { getUser } from './auth-client';
2 |
3 | async function bootstrapAppData({ client }) {
4 | const data = await getUser(client);
5 | if (!data) {
6 | return { user: null };
7 | }
8 | const { me } = data;
9 | return {
10 | user: me,
11 | };
12 | }
13 |
14 | export { bootstrapAppData };
15 |
--------------------------------------------------------------------------------
/src/store/utils/const.js:
--------------------------------------------------------------------------------
1 | import gql from 'graphql-tag';
2 |
3 | export const LOCAL_STORAGE_KEY = '__LEGAL_TECH__';
4 |
5 | export const GET_USER_QUERY = gql`
6 | query GetMeQuery {
7 | me {
8 | __typename
9 | id
10 | createdAt
11 | name
12 | email
13 | phone
14 | phone_number_verified
15 | email_verified
16 | admin {
17 | __typename
18 | id
19 | }
20 | person {
21 | __typename
22 | id
23 | name {
24 | __typename
25 | id
26 | firstName
27 | lastName
28 | middleInitial
29 | suffix
30 | fromDate
31 | throughDate
32 | }
33 | email {
34 | __typename
35 | id
36 | payload
37 | fromDate
38 | throughDate
39 | }
40 | avatar {
41 | __typename
42 | id
43 | payload
44 | fromDate
45 | throughDate
46 | }
47 | metadata {
48 | __typename
49 | id
50 | isDraft
51 | isPublic
52 | isRejected
53 | isApproved
54 | isPendingReview
55 | }
56 | affiliation {
57 | __typename
58 | id
59 | fromDate
60 | throughDate
61 | title
62 | role
63 | description
64 | organization {
65 | __typename
66 | id
67 | name {
68 | id
69 | __typename
70 | payload
71 | }
72 | logo {
73 | id
74 | __typename
75 | payload
76 | }
77 | }
78 | }
79 | }
80 | }
81 | }
82 | `;
83 |
--------------------------------------------------------------------------------
/src/store/utils/useCallbackStatus.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | function useIsMounted() {
4 | const mounted = React.useRef(false);
5 | React.useLayoutEffect(() => {
6 | mounted.current = true;
7 | return () => (mounted.current = false);
8 | }, []);
9 | return mounted;
10 | }
11 |
12 | function useCallbackStatus() {
13 | const isMounted = useIsMounted();
14 | const [{ status, error }, setState] = React.useReducer(
15 | (s, a) => ({ ...s, ...a }),
16 | { status: 'rest', error: null }
17 | );
18 |
19 | const safeSetState = (...args) =>
20 | isMounted.current ? setState(...args) : null;
21 |
22 | const isPending = status === 'pending';
23 | const isRejected = status === 'rejected';
24 | function run(promise) {
25 | if (!promise || !promise.then) {
26 | throw new Error(
27 | `The argument passed to useCallbackStatus().run must be a promise. Maybe a function that's passed isn't returning anything?`
28 | );
29 | }
30 | safeSetState({ status: 'pending' });
31 | return promise.then(
32 | value => {
33 | safeSetState({ status: 'rest' });
34 | return value;
35 | },
36 | error => {
37 | safeSetState({ status: 'rejected', error });
38 | throw error;
39 | }
40 | );
41 | }
42 |
43 | return {
44 | isPending,
45 | isRejected,
46 | error,
47 | status,
48 | run,
49 | };
50 | }
51 |
52 | export default useCallbackStatus;
53 |
--------------------------------------------------------------------------------
/src/templates/__mocks__/listitems.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ListItem from '@material-ui/core/ListItem';
3 | import ListItemIcon from '@material-ui/core/ListItemIcon';
4 | import ListItemText from '@material-ui/core/ListItemText';
5 | import ListSubheader from '@material-ui/core/ListSubheader';
6 | import DashboardIcon from '@material-ui/icons/Dashboard';
7 | import ShoppingCartIcon from '@material-ui/icons/ShoppingCart';
8 | import PeopleIcon from '@material-ui/icons/People';
9 | import BarChartIcon from '@material-ui/icons/BarChart';
10 | import LayersIcon from '@material-ui/icons/Layers';
11 | import AssignmentIcon from '@material-ui/icons/Assignment';
12 | import { navigate } from 'gatsby';
13 |
14 | export const mainListItems = (
15 |
16 |
17 |
18 |
19 |
20 | navigate('/')} />
21 |
22 | {/*
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | */}
46 |
47 | );
48 |
49 | export const secondaryListItems = (
50 |
51 | {/*
Saved reports
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | */}
70 |
71 | );
72 |
--------------------------------------------------------------------------------
/src/templates/__mocks__/styles.js:
--------------------------------------------------------------------------------
1 | const drawerWidth = 240;
2 |
3 | export const styles = theme => ({
4 | root: {
5 | display: 'flex',
6 |
7 | position: 'relative',
8 | },
9 | toolbar: {
10 | paddingRight: 24, // keep right padding when drawer closed
11 | },
12 | toolbarIcon: {
13 | display: 'flex',
14 | alignItems: 'center',
15 | justifyContent: 'flex-end',
16 | padding: '0 8px',
17 | top: 50,
18 | ...theme.mixins.toolbar,
19 | },
20 | appBar: {
21 | zIndex: theme.zIndex.drawer + 1,
22 | transition: theme.transitions.create(['width', 'margin'], {
23 | easing: theme.transitions.easing.sharp,
24 | duration: theme.transitions.duration.leavingScreen,
25 | }),
26 | },
27 | appBarShift: {
28 | marginLeft: drawerWidth,
29 | width: `calc(100% - ${drawerWidth}px)`,
30 | transition: theme.transitions.create(['width', 'margin'], {
31 | easing: theme.transitions.easing.sharp,
32 | duration: theme.transitions.duration.enteringScreen,
33 | }),
34 | },
35 | menuButton: {
36 | marginLeft: 12,
37 | marginRight: 36,
38 | },
39 | media: {
40 | height: 0,
41 | paddingTop: '56.25%', // 16:9
42 | },
43 | menuButtonHidden: {
44 | display: 'none',
45 | },
46 | title: {
47 | flexGrow: 1,
48 | },
49 | drawerPaper: {
50 | position: 'relative',
51 | whiteSpace: 'nowrap',
52 | width: drawerWidth,
53 | transition: theme.transitions.create('width', {
54 | easing: theme.transitions.easing.sharp,
55 | duration: theme.transitions.duration.enteringScreen,
56 | }),
57 | },
58 | drawerPaperClose: {
59 | overflowX: 'hidden',
60 | transition: theme.transitions.create('width', {
61 | easing: theme.transitions.easing.sharp,
62 | duration: theme.transitions.duration.leavingScreen,
63 | }),
64 | width: 0 * 7,
65 | [theme.breakpoints.up('sm')]: {
66 | width: 0 * 9,
67 | },
68 | },
69 | appBarSpacer: theme.mixins.toolbar,
70 | content: {
71 | flexGrow: 1,
72 |
73 | padding: theme.spacing(3),
74 | height: '100vh',
75 |
76 | overflow: 'auto',
77 | },
78 | chartContainer: {
79 | marginLeft: -22,
80 | },
81 | tableContainer: {
82 | height: 320,
83 | },
84 | h5: {
85 | marginBottom: theme.spacing(2),
86 | },
87 | paperRoot: {
88 | ...theme.mixins.gutters(),
89 | paddingTop: theme.spacing(2),
90 | paddingBottom: theme.spacing(2),
91 | marginBottom: theme.spacing(2),
92 | },
93 | card: {
94 | marginBottom: theme.spacing(2),
95 | color: 'black',
96 | minWidth: 300,
97 | },
98 | bullet: {
99 | display: 'inline-block',
100 | margin: '0 2px',
101 | transform: 'scale(0.8)',
102 | },
103 | link: {
104 | color: theme.palette.common.white,
105 | },
106 | });
107 |
--------------------------------------------------------------------------------
/src/templates/company/companyCategories.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Chip from '@material-ui/core/Chip';
3 | import { formatCategoryName } from './helpers';
4 |
5 | export const CompanyCategories = ({ company, wrapper, chip, ...props }) => {
6 | const Wrapper = wrapper;
7 |
8 | if (company.categories !== undefined) {
9 | if (company.categories.length > 0) {
10 | return company.categories.map(item => {
11 | return chip ? (
12 |
19 | ) : (
20 |
21 |
22 | {formatCategoryName(item.label ? item.label : item.payload)}
23 |
24 |
25 | );
26 | });
27 | } else {
28 | return null;
29 | }
30 | } else {
31 | return null;
32 | }
33 | };
34 |
35 | CompanyCategories.defaultProps = {
36 | wrapper: 'div',
37 | };
38 |
--------------------------------------------------------------------------------
/src/templates/company/companyContactLinks.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ListItemText from '@material-ui/core/ListItemText';
3 | import Link from '@material-ui/core/Link';
4 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
5 | import { faGlobe } from '@fortawesome/free-solid-svg-icons';
6 | import _ from 'lodash';
7 | import { formatCompanyLink, chooseLinkIcon } from './helpers';
8 | import styled from 'styled-components';
9 | import PropTypes from 'prop-types';
10 |
11 | export function validateCompanyWebsiteUrl(url) {
12 | if (url && !url.startsWith('http')) {
13 | return `http://${url}`;
14 | }
15 | return url;
16 | }
17 |
18 | export function CompanyContactLinks({ links, name, expanded, ...rest }) {
19 | return links.map(link => {
20 | if (link.type === 'UrlWebsite' && link.payload !== '') {
21 | return (
22 |
36 |
44 | {expanded && `${name} Website`}
45 |
46 | }
47 | />
48 | );
49 | } else if (link.type && link.payload !== '') {
50 | return (
51 |
62 |
70 | {expanded &&
71 | formatCompanyLink(
72 | link.type.slice(0, 3) === 'Url'
73 | ? link.type.slice(3)
74 | : link.type
75 | )}
76 |
77 | }
78 | />
79 | );
80 | } else {
81 | return null;
82 | }
83 | });
84 | }
85 |
86 | CompanyContactLinks.defaultProps = {
87 | expanded: true,
88 | };
89 |
90 | CompanyContactLinks.propTypes = {
91 | expanded: PropTypes.bool,
92 | };
93 |
--------------------------------------------------------------------------------
/src/templates/company/contact.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Typography from '@material-ui/core/Typography';
3 | import Card from '@material-ui/core/Card';
4 | import CardContent from '@material-ui/core/CardContent';
5 |
6 | import { CompanyContactLinks } from './companyContactLinks';
7 |
8 | export function CompanyContact({ classes, links, name, ...rest }) {
9 | return (
10 |
11 |
12 |
13 | Company Contacts
14 |
15 |
16 |
17 |
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/src/templates/company/helpers.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Chip from '@material-ui/core/Chip';
3 | import { DateTime } from 'luxon';
4 | import { navigate } from '@reach/router';
5 | import { useQuery } from 'react-apollo-hooks';
6 | import { faBuilding, faLink } from '@fortawesome/free-solid-svg-icons';
7 | import {
8 | faTwitter,
9 | faAngellist,
10 | faFacebookF,
11 | } from '@fortawesome/free-brands-svg-icons';
12 |
13 | import { GET_COMPANY_TARGET_MARKETS } from '../../graphql/queries';
14 |
15 | export function formatCompanyFoundedDate(date) {
16 | if (date === undefined) {
17 | return;
18 | }
19 | const result = DateTime.fromISO(date).year;
20 | return result;
21 | }
22 |
23 | export function formatBingNewsPublishedDate(date) {
24 | if (date === undefined) {
25 | return;
26 | }
27 | const result = DateTime.fromISO(date);
28 | return result.toRelative();
29 | }
30 |
31 | export function formatCompanyCategories(categories) {
32 | if (categories === undefined || '') {
33 | return;
34 | }
35 | return categories.map(item => {
36 | return (
37 |
42 | navigate(
43 | `/tags/${item.payload
44 | .split('_')
45 | .join('-')
46 | .toLowerCase()}`
47 | )
48 | }
49 | />
50 | );
51 | });
52 | }
53 |
54 | export function toTitleCase(str) {
55 | return str.replace(/\w\S*/g, function(txt) {
56 | return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
57 | });
58 | }
59 |
60 | export function formatCompanyTargetMarkets(company) {
61 | if (company.targetMarkets === undefined || company.targetMarkets === '') {
62 | return null;
63 | }
64 |
65 | let targetMarkets = company.targetMarkets;
66 |
67 | if (typeof targetMarkets === 'string') {
68 | const { data, loading, error } = useQuery(GET_COMPANY_TARGET_MARKETS);
69 | const { organizationTargetMarkets } = data;
70 |
71 | organizationTargetMarkets.forEach(item => {
72 | if (targetMarkets === item.id) {
73 | targetMarkets = [item];
74 | }
75 | });
76 | }
77 |
78 | return targetMarkets.map(item => {
79 | return (
80 |
85 | );
86 | });
87 | }
88 |
89 | export function formatCompanyLink(link) {
90 | return link
91 | .replace(/([A-Z])/g, ' $1')
92 | .replace(/^./, str => str.toUpperCase())
93 | .trim();
94 | }
95 |
96 | export function formatCategoryName(str) {
97 | if (str.toUpperCase() !== str) {
98 | return str.toUpperCase().replace(/ /g, '_');
99 | } else {
100 | return str
101 | .toLowerCase()
102 | .replace(/[^0-9a-z]/gi, ' ')
103 | .replace(/(?:^|\s)\S/g, function(a) {
104 | return a.toUpperCase();
105 | });
106 | }
107 | }
108 |
109 | export const chooseLinkIcon = linkType => {
110 | switch (linkType) {
111 | case 'UrlTwitter':
112 | return faTwitter;
113 | case 'UrlAngellist':
114 | return faAngellist;
115 | case 'UrlCrunchbase':
116 | return faBuilding;
117 | case 'UrlFacebook':
118 | return faFacebookF;
119 | default:
120 | return faLink;
121 | }
122 | };
123 |
--------------------------------------------------------------------------------
/src/templates/company/index.js:
--------------------------------------------------------------------------------
1 | export * from './locationmap';
2 | export * from './intelligence';
3 | export * from './news';
4 | export * from './contact';
5 | export * from './companyContactLinks';
6 |
--------------------------------------------------------------------------------
/src/templates/company/intelligence.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Typography from '@material-ui/core/Typography';
3 | import ListItemText from '@material-ui/core/ListItemText';
4 | import Card from '@material-ui/core/Card';
5 | import CardContent from '@material-ui/core/CardContent';
6 | import ListItem from '@material-ui/core/ListItem';
7 | import styled from 'styled-components';
8 | import Chip from '@material-ui/core/Chip';
9 |
10 | import { CompanyCategories } from './companyCategories';
11 | import {
12 | formatCompanyFoundedDate,
13 | formatCompanyTargetMarkets,
14 | } from './helpers';
15 |
16 | export function CompanyIntelligence({ classes, company, ...props }) {
17 | const TargetMarkets = formatCompanyTargetMarkets(company);
18 |
19 | return (
20 |
21 |
22 |
23 | Business Intelligence
24 |
25 |
26 |
29 |
30 | Founded
31 |
32 | >
33 | }
34 | />
35 |
43 |
44 | {TargetMarkets && TargetMarkets.length > 0 ? (
45 |
46 |
49 |
50 | Target Markets
51 |
52 | >
53 | }
54 | />
55 |
56 | {TargetMarkets[0]}
57 |
58 | ) : null}
59 |
60 |
61 |
64 |
65 | Categories
66 |
67 | >
68 | }
69 | />
70 |
71 |
72 |
73 |
74 |
75 |
76 | );
77 | }
78 |
79 | const OperatingModelsItem = styled(ListItem)`
80 | @media (max-width: 480px) {
81 | display: flex;
82 | flex-direction: column;
83 | justify-content: center;
84 | align-items: center;
85 | }
86 | `;
87 |
88 | const OperatingModelsListItemText = styled(ListItemText)`
89 | min-width: 150px;
90 | @media (max-width: 480px) {
91 | text-align: center;
92 | }
93 | `;
94 |
95 | const OperatingModelsChipsContainer = styled.div`
96 | display: flex;
97 | flex-wrap: wrap;
98 | justify-content: flex-end;
99 |
100 | @media (max-width: 480px) {
101 | justify-content: center;
102 | align-items: center;
103 | margin-top: 5px;
104 | }
105 | `;
106 |
--------------------------------------------------------------------------------
/src/templates/company/location.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codexstanford/techlist-frontend-web/06be56c7f1bbaa34e65ba942a24fe29257ae4b7f/src/templates/company/location.js
--------------------------------------------------------------------------------
/src/templates/company/locationmap.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import GoogleMapReact from 'google-map-react';
3 |
4 | import { Container } from '../../atoms';
5 |
6 | export function CompanyLocationMap(props) {
7 | let location = null;
8 |
9 | if (props.location && props.location.geometry) {
10 | location = props.location.geometry.set.location;
11 | }
12 |
13 | return location === null ? null : (
14 |
15 |
16 |
17 |
18 |
19 | );
20 | }
21 |
22 | import styled from 'styled-components';
23 |
24 | const Wrapper = styled.div`
25 | position: absolute;
26 | top: 50%;
27 | left: 50%;
28 | width: 18px;
29 | height: 18px;
30 | background-color: #000;
31 | border: 2px solid #fff;
32 | border-radius: 100%;
33 | user-select: none;
34 | transform: translate(-50%, -50%);
35 | cursor: ${props => (props.onClick ? 'pointer' : 'default')};
36 | &:hover {
37 | z-index: 1;
38 | }
39 | `;
40 |
41 | const Marker = props => (
42 |
46 | );
47 |
--------------------------------------------------------------------------------
/src/templates/company/news.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react';
2 | import Typography from '@material-ui/core/Typography';
3 | import ListItemText from '@material-ui/core/ListItemText';
4 | import Card from '@material-ui/core/Card';
5 | import CardContent from '@material-ui/core/CardContent';
6 | import ListItem from '@material-ui/core/ListItem';
7 | import Link from '@material-ui/core/Link';
8 | import _ from 'lodash';
9 |
10 | import { formatBingNewsPublishedDate } from './helpers';
11 |
12 | export function CompanyNews({ classes, company, ...props }) {
13 | const [news, setNews] = useState(null);
14 | const qstring = company.name[0].payload;
15 |
16 | useEffect(() => {
17 | fetch(
18 | `${process.env.GATSBY_BING_SEARCH_NEWS_API}?q=${encodeURI(qstring)}`,
19 | {
20 | headers: {
21 | 'Ocp-Apim-Subscription-Key': process.env.GATSBY_BING_API_KEY,
22 | },
23 | }
24 | )
25 | .then(data => data.json())
26 | .then(data => {
27 | if (data && data.value && data.value.length > 0) {
28 | setNews(data.value);
29 | }
30 | })
31 | .catch(err => console.log(err));
32 | }, [company.name[0].payload]);
33 |
34 | if (!news) {
35 | return null;
36 | }
37 |
38 | return (
39 |
40 |
41 |
42 | News
43 |
44 | {renderNewsItems(news)}
45 |
46 |
47 | );
48 | }
49 |
50 | function renderNewsItems(news) {
51 | return _.reverse(_.sortBy(news, 'datePublished'))
52 | .slice(0, 5)
53 | .map((item, index) => {
54 | if (!item.name) {
55 | return null;
56 | }
57 | return (
58 |
59 |
62 |
63 | {item.name}
64 |
65 |
66 |
67 | {item.provider && item.provider.length > 0
68 | ? item.provider[0].name
69 | : null}{' '}
70 | | {formatBingNewsPublishedDate(item.datePublished)}
71 |
72 | >
73 | }
74 | secondary={
75 | <>
76 | {item.description}
77 | >
78 | }
79 | />
80 |
81 | );
82 | });
83 | }
84 |
--------------------------------------------------------------------------------