diff --git a/.eslintrc b/.eslintrc index 8c54c23226443acba14bea93c6364bf47f76b716..b179fe12ea7611d5893afad416d190f9e0d39b8e 100644 --- a/.eslintrc +++ b/.eslintrc @@ -22,6 +22,7 @@ "react" ], "rules": { + "quotes": [2, "single", { "avoidEscape": true }], "react/jsx-filename-extension": "off", "react/destructuring-assignment": "off", "no-shadow": "off", diff --git a/.gitignore b/.gitignore index a79218d81887937a173003e75622a33a8b45df51..252c298ae4d31634257523d3ca8a241287305436 100644 --- a/.gitignore +++ b/.gitignore @@ -63,4 +63,3 @@ typings/ # custom ignored files build src/auth_config.json -src/api_keys.json diff --git a/prettier.config.js b/prettier.config.js index ca1d62b32b89868e00dce8063a89644248a298a6..9ca0440a30c4c42ff450f63267cf9d67883a140f 100644 --- a/prettier.config.js +++ b/prettier.config.js @@ -1,6 +1,7 @@ module.exports = { printWidth: 150, - arrowParens: "always", + arrowParens: 'always', semi: true, tabWidth: 2, + singleQuote: true, }; diff --git a/src/App.js b/src/App.js index 397d790f01a5a85b79081c0894b08231b48aba8a..c4e1c1e5e203d6d71c0e883c1453ade362465b8a 100644 --- a/src/App.js +++ b/src/App.js @@ -1,63 +1,63 @@ -import React, { useState } from "react"; -import { BrowserRouter, Route, Routes } from "react-router-dom"; +import React, { useState } from 'react'; +import { BrowserRouter, Route, Routes } from 'react-router-dom'; -import PrivateRoute from "./components/PrivateRoute"; -import Loading from "./components/Loading"; -import NavBar from "./components/NavBar"; -import LandingPage from "./views/LandingPage"; -import Chart from "./views/ChartPage"; -import Profile from "./views/Profile"; -import { useAuth0 } from "./react-auth0-spa"; -// import history from "./utils/history"; -import clsx from "clsx"; -import { makeStyles } from "@material-ui/core/styles"; -import CssBaseline from "@material-ui/core/CssBaseline"; -import Drawer from "@material-ui/core/Drawer"; -import Divider from "@material-ui/core/Divider"; -import IconButton from "@material-ui/core/IconButton"; -import Container from "@material-ui/core/Container"; -import ChevronLeftIcon from "@material-ui/icons/ChevronLeft"; -import Navigation from "./components/Navigation"; -import HomePage from "./views/HomePage"; -import DetailPage from "./views/DetailPage"; +import PrivateRoute from './components/PrivateRoute'; +import Loading from './components/Loading'; +import NavBar from './components/NavBar'; +import LandingPage from './views/LandingPage'; +import Chart from './views/ChartPage'; +import Profile from './views/Profile'; +import { useAuth0 } from './react-auth0-spa'; +// import history from './utils/history'; +import clsx from 'clsx'; +import { makeStyles } from '@material-ui/core/styles'; +import CssBaseline from '@material-ui/core/CssBaseline'; +import Drawer from '@material-ui/core/Drawer'; +import Divider from '@material-ui/core/Divider'; +import IconButton from '@material-ui/core/IconButton'; +import Container from '@material-ui/core/Container'; +import ChevronLeftIcon from '@material-ui/icons/ChevronLeft'; +import Navigation from './components/Navigation'; +import HomePage from './views/HomePage'; +import DetailPage from './views/DetailPage'; const drawerWidth = 240; const useStyles = makeStyles((theme) => ({ root: { - display: "flex", + display: 'flex', }, toolbarIcon: { - display: "flex", - alignItems: "center", - justifyContent: "flex-end", - padding: "0 8px", + display: 'flex', + alignItems: 'center', + justifyContent: 'flex-end', + padding: '0 8px', ...theme.mixins.toolbar, }, title: { flexGrow: 1, }, drawerPaper: { - position: "relative", - whiteSpace: "nowrap", + position: 'relative', + whiteSpace: 'nowrap', width: drawerWidth, - transition: theme.transitions.create("width", { + transition: theme.transitions.create('width', { easing: theme.transitions.easing.sharp, duration: theme.transitions.duration.enteringScreen, }), }, drawerPaperClose: { - overflowX: "hidden", + overflowX: 'hidden', width: theme.spacing(7), - [theme.breakpoints.up("sm")]: { + [theme.breakpoints.up('sm')]: { width: theme.spacing(9), }, }, appBarSpacer: theme.mixins.toolbar, content: { flexGrow: 1, - height: "100vh", - overflow: "auto", + height: '100vh', + overflow: 'auto', }, container: { paddingTop: theme.spacing(4), @@ -65,9 +65,9 @@ const useStyles = makeStyles((theme) => ({ }, paper: { padding: theme.spacing(2), - display: "flex", - overflow: "auto", - flexDirection: "column", + display: 'flex', + overflow: 'auto', + flexDirection: 'column', }, fixedHeight: { height: 240, @@ -115,7 +115,7 @@ const App = () => { )} <main className={classes.content}> <div className={classes.appBarSpacer} /> - <Container maxWidth="lg" className={classes.container} style={{ height: "100%" }}> + <Container maxWidth="lg" className={classes.container} style={{ height: '100%' }}> <Routes> <Route path="/home-page" element={<PrivateRoute component={HomePage} />} /> <Route path="/profile" element={<PrivateRoute component={Profile} />} /> diff --git a/src/App.test.js b/src/App.test.js index d2d65b5725f6191b95f6d07c67d9f99548e192a0..5d1515a5d59cb045b01174a496611fa67ecfd669 100644 --- a/src/App.test.js +++ b/src/App.test.js @@ -1,18 +1,18 @@ -import React from "react"; -import ReactDOM from "react-dom"; -import App from "./App"; -import { useAuth0 } from "./react-auth0-spa"; -jest.mock("./react-auth0-spa"); +import React from 'react'; +import ReactDOM from 'react-dom'; +import App from './App'; +import { useAuth0 } from './react-auth0-spa'; +jest.mock('./react-auth0-spa'); -describe("app", () => { +describe('app', () => { beforeEach(() => { // Mock the Auth0 hook and make it return a logged in state useAuth0.mockReturnValue({ loading: true, }); }); - it("renders without crashing", () => { - const div = document.createElement("div"); + it('renders without crashing', () => { + const div = document.createElement('div'); ReactDOM.render(<App />, div); ReactDOM.unmountComponentAtNode(div); }); diff --git a/src/api_keys.json b/src/api_keys.json deleted file mode 100644 index 04493bcc79895dd784508421dc283af9bb68c68a..0000000000000000000000000000000000000000 --- a/src/api_keys.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "air": { - "apiBase": "https://muniair-f106.restdb.io", - "key": "5e0ef5cbb68f0802dd3e5f4d" - }, - "sun": { - "apiBase": "https://munisun-d71a.restdb.io", - "key": "5df0aababf46220df655d9df" - }, - "temp": { - "apiBase": "https://munitest-16ae.restdb.io", - "key": "5dc7d91d64e7774913b6eaf8" - } -} diff --git a/src/components/DataTable.js b/src/components/DataTable.js index 06afd3caad542ee8d116ca9ead7b2107a05fc1a9..7a0a549df4a9faa5ed73dc0b15f7c0c91eb09d61 100644 --- a/src/components/DataTable.js +++ b/src/components/DataTable.js @@ -1,27 +1,27 @@ -import React from "react"; -import { makeStyles } from "@material-ui/core/styles"; -import PropTypes from "prop-types"; -import Table from "@material-ui/core/Table"; -import TableBody from "@material-ui/core/TableBody"; -import TableCell from "@material-ui/core/TableCell"; -import TableContainer from "@material-ui/core/TableContainer"; -import TableHead from "@material-ui/core/TableHead"; -import TableRow from "@material-ui/core/TableRow"; -import Paper from "@material-ui/core/Paper"; -import Skeleton from "@material-ui/lab/Skeleton"; -import { useNavigate } from "react-router-dom"; +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import PropTypes from 'prop-types'; +import Table from '@material-ui/core/Table'; +import TableBody from '@material-ui/core/TableBody'; +import TableCell from '@material-ui/core/TableCell'; +import TableContainer from '@material-ui/core/TableContainer'; +import TableHead from '@material-ui/core/TableHead'; +import TableRow from '@material-ui/core/TableRow'; +import Paper from '@material-ui/core/Paper'; +import Skeleton from '@material-ui/lab/Skeleton'; +import { useNavigate } from 'react-router-dom'; const useStyles = makeStyles((theme) => ({ table: { minWidth: 650, - overflowY: "scroll", + overflowY: 'scroll', }, tableContainer: { margin: 24, }, tableRow: { - "&:hover": { - cursor: "pointer", + '&:hover': { + cursor: 'pointer', backgroundColor: theme.palette.background.default, }, }, @@ -34,8 +34,8 @@ function createData({ month, year, ...row }) { } const headerTemplate = { - year: "Year", - month: "MÄ›sĂc", + year: 'Year', + month: 'MÄ›sĂc', labels: createNumbers(), }; diff --git a/src/components/Loading.js b/src/components/Loading.js index 65e2b0457ec42084abe70e81a937672e06396a7d..6ad4a356c6f5483d325cfc634aba31238611e2b2 100644 --- a/src/components/Loading.js +++ b/src/components/Loading.js @@ -1,4 +1,4 @@ -import React from "react"; +import React from 'react'; const Loading = () => <div>Loading ...</div>; diff --git a/src/components/NavBar.js b/src/components/NavBar.js index 5269711c627fee8d65a85b31abe2138c11e1ba8b..1293fc95f2882553b67f3bf95a7049c56dc85d0c 100644 --- a/src/components/NavBar.js +++ b/src/components/NavBar.js @@ -1,20 +1,20 @@ -import React, { useState } from "react"; -import PropTypes from "prop-types"; -import { useAuth0 } from "../react-auth0-spa"; -import clsx from "clsx"; -import { makeStyles } from "@material-ui/core/styles"; -import AppBar from "@material-ui/core/AppBar"; -import Toolbar from "@material-ui/core/Toolbar"; -import Typography from "@material-ui/core/Typography"; -import IconButton from "@material-ui/core/IconButton"; -import Badge from "@material-ui/core/Badge"; -import NotificationsIcon from "@material-ui/icons/Notifications"; -import MenuIcon from "@material-ui/icons/Menu"; -import Button from "@material-ui/core/Button"; -import Avatar from "@material-ui/core/Avatar"; -import Menu from "@material-ui/core/Menu"; -import MenuItem from "@material-ui/core/MenuItem"; -import { useNavigate } from "react-router-dom"; +import React, { useState } from 'react'; +import PropTypes from 'prop-types'; +import { useAuth0 } from '../react-auth0-spa'; +import clsx from 'clsx'; +import { makeStyles } from '@material-ui/core/styles'; +import AppBar from '@material-ui/core/AppBar'; +import Toolbar from '@material-ui/core/Toolbar'; +import Typography from '@material-ui/core/Typography'; +import IconButton from '@material-ui/core/IconButton'; +import Badge from '@material-ui/core/Badge'; +import NotificationsIcon from '@material-ui/icons/Notifications'; +import MenuIcon from '@material-ui/icons/Menu'; +import Button from '@material-ui/core/Button'; +import Avatar from '@material-ui/core/Avatar'; +import Menu from '@material-ui/core/Menu'; +import MenuItem from '@material-ui/core/MenuItem'; +import { useNavigate } from 'react-router-dom'; const drawerWidth = 240; @@ -22,7 +22,7 @@ const useStyles = makeStyles((theme) => { return { appBar: { zIndex: theme.zIndex.drawer + 1, - transition: theme.transitions.create(["width", "margin"], { + transition: theme.transitions.create(['width', 'margin'], { easing: theme.transitions.easing.sharp, duration: theme.transitions.duration.leavingScreen, }), @@ -30,7 +30,7 @@ const useStyles = makeStyles((theme) => { appBarShift: { marginLeft: drawerWidth, width: `calc(100% - ${drawerWidth}px)`, - transition: theme.transitions.create(["width", "margin"], { + transition: theme.transitions.create(['width', 'margin'], { easing: theme.transitions.easing.sharp, duration: theme.transitions.duration.enteringScreen, }), @@ -42,7 +42,7 @@ const useStyles = makeStyles((theme) => { marginRight: 36, }, menuButtonHidden: { - display: "none", + display: 'none', }, title: { flexGrow: 1, @@ -114,7 +114,7 @@ const NavBar = ({ open, handleOpen }) => { <MenuItem onClick={() => { handleClose(); - navigate("/profile"); + navigate('/profile'); }} > Profile diff --git a/src/components/Navigation.js b/src/components/Navigation.js index 955d1b83a0c67f51b7051ca67569e3a0053d611b..6327a1f84dbfc9782e87bc972d8df0958e2acb4f 100644 --- a/src/components/Navigation.js +++ b/src/components/Navigation.js @@ -1,19 +1,19 @@ -import React from "react"; -import clsx from "clsx"; -import ListItem from "@material-ui/core/ListItem"; -import ListItemIcon from "@material-ui/core/ListItemIcon"; -import ListItemText from "@material-ui/core/ListItemText"; -import ShowChartIcon from "@material-ui/icons/ShowChart"; -import DashboardIcon from "@material-ui/icons/Dashboard"; -import { NavLink as RouterNavLink } from "react-router-dom"; -import { makeStyles } from "@material-ui/core/styles"; +import React from 'react'; +import clsx from 'clsx'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemText from '@material-ui/core/ListItemText'; +import ShowChartIcon from '@material-ui/icons/ShowChart'; +import DashboardIcon from '@material-ui/icons/Dashboard'; +import { NavLink as RouterNavLink } from 'react-router-dom'; +import { makeStyles } from '@material-ui/core/styles'; const useStyles = makeStyles((theme) => ({ link: { - textDecoration: "none", + textDecoration: 'none', color: theme.palette.text.primary, - "&:hover": { - textDecoration: "none", + '&:hover': { + textDecoration: 'none', }, }, })); @@ -22,7 +22,7 @@ const Navigation = () => { const styles = useStyles(); return ( <div> - <RouterNavLink to="/" className={clsx(styles.link)} activeClassName="selected"> + <RouterNavLink to="/home-page" className={({ isActive }) => [clsx(styles.link), isActive ? 'selected' : null].join(' ')}> <ListItem button> <ListItemIcon> <DashboardIcon /> @@ -30,7 +30,7 @@ const Navigation = () => { <ListItemText primary="Dashboard" /> </ListItem> </RouterNavLink> - <RouterNavLink to="/chart" className={clsx(styles.link)} activeClassName="selected"> + <RouterNavLink to="/chart" className={({ isActive }) => [clsx(styles.link), isActive ? 'selected' : null].join(' ')}> <ListItem button> <ListItemIcon> <ShowChartIcon /> diff --git a/src/components/PrivateRoute.js b/src/components/PrivateRoute.js index debc7376e81e5a96af091649632d716b86dd3cac..5ecf903609ace2338347205c4db42b1a8779a3ac 100644 --- a/src/components/PrivateRoute.js +++ b/src/components/PrivateRoute.js @@ -1,14 +1,29 @@ -import React, { useEffect } from "react"; -import PropTypes from "prop-types"; -import { useLocation } from "react-router-dom"; -import { useAuth0 } from "../react-auth0-spa"; +import React, { useEffect } from 'react'; +import PropTypes from 'prop-types'; +import { useLocation } from 'react-router-dom'; +import { useAuth0 } from '../react-auth0-spa'; +// import authConfig from '../auth_config.json'; const PrivateRoute = ({ component: Component, ...rest }) => { - const { isAuthenticated, loginWithRedirect } = useAuth0(); + const { isAuthenticated, loginWithRedirect, user, ...props } = useAuth0(); const location = useLocation(); + console.log(props, 'huuuuhhuuuuhhuuuuh'); + useEffect(() => { const fn = async () => { + console.log(user, 'huuuuh'); + // const { access_token } = await ( + // await fetch(`https://${authConfig.domain}/oauth/token`, { + // method: 'POST', + // headers: { 'content-type': 'application/json' }, + // body: JSON.stringify(body), + // }) + // ).json(); + // const data = await fetch(`https://${authConfig.domain}/userinfo`, { + // headers: { Authorization: `Bearer ${access_token}`, 'Content-Type': 'application/json' }, + // }); + // console.log(await data.json(), 'fuuuuuuuh'); if (!isAuthenticated) { await loginWithRedirect({ appState: { targetUrl: location.pathname }, diff --git a/src/index.css b/src/index.css index cee5f348fb90f0aaf875716f9bde36270331f3ba..e2bd8f36a015170d258a9adb2849f9848499df68 100644 --- a/src/index.css +++ b/src/index.css @@ -1,14 +1,14 @@ body { margin: 0; padding: 0; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", - "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } code { - font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; } diff --git a/src/index.js b/src/index.js index d2841c519e5d372196c0abf86af5732221c5f2e4..3d127de3b7a90b2c304d7653cf4075e6039ea6c9 100644 --- a/src/index.js +++ b/src/index.js @@ -1,10 +1,10 @@ -import React from "react"; -import ReactDOM from "react-dom"; -import "./index.css"; -import App from "./App"; -import { Auth0Provider } from "./react-auth0-spa"; -import config from "./auth_config.json"; -import history from "./utils/history"; +import React from 'react'; +import ReactDOM from 'react-dom'; +import './index.css'; +import App from './App'; +import { Auth0Provider } from './react-auth0-spa'; +import config from './auth_config.json'; +import history from './utils/history'; const onRedirectCallback = (appState) => { history.push(appState && appState.targetUrl ? appState.targetUrl : window.location.pathname); @@ -14,5 +14,5 @@ ReactDOM.render( <Auth0Provider domain={config.domain} client_id={config.clientId} redirect_uri={window.location.origin} onRedirectCallback={onRedirectCallback}> <App /> </Auth0Provider>, - document.getElementById("root") + document.getElementById('root') ); diff --git a/src/react-auth0-spa.js b/src/react-auth0-spa.js index 58425044c93225bf65182db3ea688a876b69837f..1581258166909d03c455f7e02db6fd36a9f283b3 100644 --- a/src/react-auth0-spa.js +++ b/src/react-auth0-spa.js @@ -1,9 +1,9 @@ -import React, { useState, useEffect, useContext } from "react"; -import createAuth0Client from "@auth0/auth0-spa-js"; -import PropTypes from "prop-types"; -import Api from "./utils/api"; +import React, { useState, useEffect, useContext } from 'react'; +import createAuth0Client from '@auth0/auth0-spa-js'; +import PropTypes from 'prop-types'; +import { createInstance } from './utils/api'; -import authConfig from "./auth_config.json"; +import authConfig from './auth_config.json'; const DEFAULT_REDIRECT_CALLBACK = () => window.history.replaceState({}, document.title, window.location.pathname); @@ -15,13 +15,25 @@ export const Auth0Provider = ({ children, onRedirectCallback = DEFAULT_REDIRECT_ const [auth0Client, setAuth0] = useState(); const [loading, setLoading] = useState(true); const [popupOpen, setPopupOpen] = useState(false); + const [apiSun, setApiSun] = useState({}); + const [apiTemp, setApiTemp] = useState({}); + const [apiAir, setApiAir] = useState({}); + + useEffect(() => { + if (user) { + const { sun, air, temp } = user; + setApiSun(createInstance(sun)); + setApiTemp(createInstance(temp)); + setApiAir(createInstance(air)); + } + }, [user]); useEffect(() => { const initAuth0 = async () => { const auth0FromHook = await createAuth0Client(initOptions); setAuth0(auth0FromHook); - if (window.location.search.includes("code=")) { + if (window.location.search.includes('code=')) { const { appState } = await auth0FromHook.handleRedirectCallback(); onRedirectCallback(appState); } @@ -69,23 +81,23 @@ export const Auth0Provider = ({ children, onRedirectCallback = DEFAULT_REDIRECT_ * Update user data in Auth0 application. * @param {*} userData data to be updated. * @example - * updateProfile({ user_metadata: { name: "something" } }) // updates user metadata + * updateProfile({ user_metadata: { name: 'something' } }) // updates user metadata * @see https://auth0.com/docs/api/management/v2#!/Users/patch_users_by_id */ const updateProfile = async (userData) => { if (isAuthenticated) { - const [{ _id, ...body }] = await Api.get("/rest/auth"); + const [{ _id, ...body }] = await apiSun.get('/rest/auth'); const { access_token } = await ( await fetch(`https://${authConfig.domain}/oauth/token`, { - method: "POST", - headers: { "content-type": "application/json" }, + method: 'POST', + headers: { 'content-type': 'application/json' }, body: JSON.stringify(body), }) ).json(); return ( await fetch(`https://${authConfig.domain}/api/v2/users/${user.sub}`, { - method: "PATCH", - headers: { Authorization: `Bearer ${access_token}`, "Content-Type": "application/json" }, + method: 'PATCH', + headers: { Authorization: `Bearer ${access_token}`, 'Content-Type': 'application/json' }, body: JSON.stringify(userData), }) ).json(); @@ -97,6 +109,9 @@ export const Auth0Provider = ({ children, onRedirectCallback = DEFAULT_REDIRECT_ value={{ isAuthenticated, user, + apiSun, + apiAir, + apiTemp, loading, popupOpen, loginWithPopup, diff --git a/src/utils/api.js b/src/utils/api.js index 3c21ef2c7cff7adbbb2ba57b838134d94f13c7c1..4458951aa5ba8b5396a00fa3477a44afdfd31bf9 100644 --- a/src/utils/api.js +++ b/src/utils/api.js @@ -1,8 +1,6 @@ -import axios from "axios"; -import set from "lodash/set"; -import get from "lodash/get"; -import config from "../auth_config.json"; -import keys from "../api_keys.json"; +import axios from 'axios'; +import set from 'lodash/set'; +import get from 'lodash/get'; /** * Update or add data at given path in custom data. @@ -10,24 +8,24 @@ import keys from "../api_keys.json"; * @param {object} payload data payload that'll be saved on path. * @param {add|replace} method either add or replace. * @example: - * API.updateData("professors.example", {some: "value"}) // merging with previous data - * API.updateData("professors.example", {different: "value"}, "replace") // rewriting data + * apiSun.updateData('professors.example', {some: 'value'}) // merging with previous data + * apiSun.updateData('professors.example', {different: 'value'}, 'replace') // rewriting data */ const updateData = (api) => - (path, payload, method = "add") => { + (path, payload, method = 'add') => { if (!path) { - throw new Error("Error, path must be specified when calling updateData endpoint"); + throw new Error('Error, path must be specified when calling updateData endpoint'); } - if (path.split(".").length < 2) { - throw new Error(`Error, path must have proper namespace in format "team.dataset".`); + if (path.split('.').length < 2) { + throw new Error('Error, path must have proper namespace in format "team.dataset".'); } - if (!method || !["replace", "add"].includes(method)) { - throw new Error(`Update method is not specified. It muset be one of ["replace", "add"]`); + if (!method || !['replace', 'add'].includes(method)) { + throw new Error('Update method is not specified. It muset be one of ["replace", "add"]'); } - return apiInstance.get("/rest/customdata").then((data) => { - const [namespace, ...paths] = path.split("."); - const destination = paths.join(""); + return apiInstance.get('/rest/customdata').then((data) => { + const [namespace, ...paths] = path.split('.'); + const destination = paths.join(''); const index = data.findIndex(({ team }) => team === namespace); if (index === -1) { throw new Error(`${target} is not valid namepsace. Available namespaces ${data.map(({ team }) => team)}`); @@ -35,8 +33,8 @@ const updateData = const target = `[${index}].data.${destination}`; return api.post( - "/rest/customdata", - method === "replace" ? set(data, target, payload) : set(data, `${target}[${[...(get(data, target) || [])].length}]`, payload) + '/rest/customdata', + method === 'replace' ? set(data, target, payload) : set(data, `${target}[${[...(get(data, target) || [])].length}]`, payload) ); }); }; @@ -47,54 +45,27 @@ const updateData = */ const getCustomData = (api) => - (teamName = "professors") => { - return api.get(`/rest/customdata?q={"team": "${teamName}"}`); + (teamName = 'professors') => { + return api.get(`/rest/customdata?q={'team': '${teamName}'}`); }; -const instances = Object.entries(keys).reduce( - (acc, [key, api]) => ({ - ...acc, - [key]: { - apiInstance: axios.create({ - baseURL: api.apiBase, - headers: { - "content-type": "application/json", - "x-apikey": api.key, - "cache-control": "no-cache", - }, - }), +export const createInstance = (instance) => { + const apiInstance = axios.create({ + baseURL: `https://${instance.base}.restdb.io`, + headers: { + 'content-type': 'application/json', + 'x-apikey': instance.token, + 'cache-control': 'no-cache', }, - }), - {} -); + }); -Object.keys(instances).forEach((instanceKey) => { - instances[instanceKey].apiInstance.interceptors.response.use((response) => response.data || response); - instances[instanceKey].get = (path) => instances[instanceKey].apiInstance.get(path); - instances[instanceKey].post = (path, payload) => instances[instanceKey].apiInstance.post(path, payload); - instances[instanceKey].updateData = updateData(instances[instanceKey].apiInstance); - instances[instanceKey].getCustomData = getCustomData(instances[instanceKey].apiInstance); -}); + apiInstance.interceptors.response.use((response) => response.data || response); -const apiHeaders = { - "content-type": "application/json", - "x-apikey": config.apiKey, - "cache-control": "no-cache", -}; - -const apiInstance = axios.create({ - baseURL: config.apiBase, - headers: apiHeaders, -}); - -apiInstance.interceptors.response.use((response) => response.data || response); - -const Api = { - ...instances, - updateData: updateData(apiInstance), - getCustomData: getCustomData(apiInstance), - get: (path) => apiInstance.get(path), - post: (path, payload) => apiInstance.post(path, payload), + return { + apiInstance, + get: (path) => apiInstance.get(path), + post: (path, payload) => apiInstance.post(path, payload), + updateData: updateData(apiInstance), + getCustomData: getCustomData(apiInstance), + }; }; - -export default Api; diff --git a/src/utils/history.js b/src/utils/history.js index 2724b5bce162b273c3611fa6c5e6e0932a71a983..13e940c1d9f33847003b1a01019f4bf681ad7b36 100644 --- a/src/utils/history.js +++ b/src/utils/history.js @@ -1,2 +1,2 @@ -import { createBrowserHistory } from "history"; +import { createBrowserHistory } from 'history'; export default createBrowserHistory(); diff --git a/src/views/ChartPage.js b/src/views/ChartPage.js index 6b55a949ce96657edbc2fc38bd71b20bbc246fd8..284d055bbbc3c6b45e1444fafd796a3ee9c31d8f 100644 --- a/src/views/ChartPage.js +++ b/src/views/ChartPage.js @@ -1,14 +1,14 @@ -import React, { useEffect, useState } from "react"; -import Api from "../utils/api"; -// import TextField from "@material-ui/core/TextField"; -// import Grid from "@material-ui/core/Grid"; -// import Toolbar from "@material-ui/core/Toolbar"; -// import Autocomplete from "@material-ui/lab/Autocomplete"; -import { ResponsiveLine } from "@nivo/line"; -import mean from "lodash/mean"; +import React, { useEffect, useState } from 'react'; +// import TextField from '@material-ui/core/TextField'; +// import Grid from '@material-ui/core/Grid'; +// import Toolbar from '@material-ui/core/Toolbar'; +// import Autocomplete from '@material-ui/lab/Autocomplete'; +import { ResponsiveLine } from '@nivo/line'; +import mean from 'lodash/mean'; +import { useAuth0 } from '../react-auth0-spa'; const queryBuilder = ({ filter }) => { - return `q={${filter ? `"year": ${filter}` : ""}}`; + return `q={${filter ? `'year': ${filter}` : ''}}`; }; const Chart = () => { @@ -16,18 +16,19 @@ const Chart = () => { const [data, setData] = useState([]); const [filter] = useState(undefined); const [initialized, setInitialized] = useState(false); + const { apiSun } = useAuth0(); // const onFilter = (_e, filter) => { // setFilter(filter); // }; useEffect(() => { - Api.sun.get(`rest/sunshine?q={}&h={"$fields": {"year": 1}}`).then((data) => setYears([...new Set(data.map(({ year }) => `${year}`))])); + apiSun.get('rest/sunshine?q={}&h={"$fields": {"year": 1}}').then((data) => setYears([...new Set(data.map(({ year }) => `${year}`))])); }, []); useEffect(() => { setInitialized(false); - Api.sun.get(`rest/sunshine?${queryBuilder({ filter })}`).then((data) => { + apiSun.get(`rest/sunshine?${queryBuilder({ filter })}`).then((data) => { setData(data); setInitialized(true); }); @@ -38,7 +39,7 @@ const Chart = () => { ...acc, [year]: { id: year, - color: "hsl(95, 70%, 50%)", + color: 'hsl(95, 70%, 50%)', data: [...((acc[year] && acc[year].data) || []), { x: month, y: mean(Object.values(item)) }], }, }), @@ -46,64 +47,64 @@ const Chart = () => { ); return ( - <div style={{ height: "100%" }}> + <div style={{ height: '100%' }}> <h1>Average sunshine in a month</h1> {initialized && ( <ResponsiveLine data={Object.values(chartData)} - keys={["val"]} + keys={['val']} indexBy="key" margin={{ top: 50, right: 110, bottom: 50, left: 60 }} - xScale={{ type: "point" }} - yScale={{ type: "linear", min: "auto", max: "auto", stacked: true, reverse: false }} + xScale={{ type: 'point' }} + yScale={{ type: 'linear', min: 'auto', max: 'auto', stacked: true, reverse: false }} axisTop={null} axisRight={null} axisBottom={{ - orient: "bottom", + orient: 'bottom', tickSize: 5, tickPadding: 5, tickRotation: 0, - legend: "month", + legend: 'month', legendOffset: 36, - legendPosition: "middle", + legendPosition: 'middle', }} axisLeft={{ - orient: "left", + orient: 'left', tickSize: 5, tickPadding: 5, tickRotation: 0, - legend: "Sunshine", + legend: 'Sunshine', legendOffset: -40, - legendPosition: "middle", + legendPosition: 'middle', }} - colors={{ scheme: "nivo" }} + colors={{ scheme: 'nivo' }} pointSize={10} - pointColor={{ theme: "background" }} + pointColor={{ theme: 'background' }} pointBorderWidth={2} - pointBorderColor={{ from: "serieColor" }} + pointBorderColor={{ from: 'serieColor' }} pointLabel="y" pointLabelYOffset={-12} useMesh={true} legends={[ { - anchor: "bottom-right", - direction: "column", + anchor: 'bottom-right', + direction: 'column', justify: false, translateX: 100, translateY: 0, itemsSpacing: 0, - itemDirection: "left-to-right", + itemDirection: 'left-to-right', itemWidth: 80, itemHeight: 20, itemOpacity: 0.75, symbolSize: 12, - symbolShape: "circle", - symbolBorderColor: "rgba(0, 0, 0, .5)", + symbolShape: 'circle', + symbolBorderColor: 'rgba(0, 0, 0, .5)', effects: [ { - on: "hover", + on: 'hover', style: { - itemBackground: "rgba(0, 0, 0, .03)", + itemBackground: 'rgba(0, 0, 0, .03)', itemOpacity: 1, }, }, diff --git a/src/views/DetailPage.js b/src/views/DetailPage.js index b73becf22c7adc463063cc446640338e7e5468b0..6b7756702fff6bd3e73bf0a14633bb01ee5713ec 100644 --- a/src/views/DetailPage.js +++ b/src/views/DetailPage.js @@ -1,19 +1,21 @@ -import React, { useEffect, useState } from "react"; -import Api from "../utils/api"; -import { useParams } from "react-router"; -import CircularProgress from "@material-ui/core/CircularProgress"; -import List from "@material-ui/core/List"; -import ListItem from "@material-ui/core/ListItem"; -import ListItemText from "@material-ui/core/ListItemText"; -import Card from "@material-ui/core/Card"; -import CardContent from "@material-ui/core/CardContent"; +import React, { useEffect, useState } from 'react'; +import { useParams } from 'react-router'; +import CircularProgress from '@material-ui/core/CircularProgress'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemText from '@material-ui/core/ListItemText'; +import Card from '@material-ui/core/Card'; +import CardContent from '@material-ui/core/CardContent'; +import { useAuth0 } from '../react-auth0-spa'; const DetailPage = () => { const [detail, setDetail] = useState({}); const [isLoading, setIsLoading] = useState(true); + const { apiSun } = useAuth0(); + useEffect(() => { setIsLoading(true); - Api.sun.get(`rest/sunshine?q={ "_id": "${id}" }`).then(([data]) => { + apiSun.get(`rest/sunshine?q={ '_id': '${id}' }`).then(([data]) => { setDetail(data); setIsLoading(false); }); @@ -39,7 +41,7 @@ const DetailPage = () => { ))} </List> ) : ( - "Error while fetching detail" + 'Error while fetching detail' )} </CardContent> </Card> diff --git a/src/views/HomePage.js b/src/views/HomePage.js index 30fb5096fefecdee7d3232dfef70f48309d86cd6..aa5d2e1dbaae918a394f274cabc00f69f5f4cd3d 100644 --- a/src/views/HomePage.js +++ b/src/views/HomePage.js @@ -1,24 +1,25 @@ -import React, { useEffect, useState } from "react"; -import Api from "../utils/api"; -import Pagination from "@material-ui/lab/Pagination"; -import TextField from "@material-ui/core/TextField"; -import DataTable from "../components/DataTable"; -import Grid from "@material-ui/core/Grid"; -import Toolbar from "@material-ui/core/Toolbar"; -import Autocomplete from "@material-ui/lab/Autocomplete"; +import React, { useEffect, useState } from 'react'; +import Pagination from '@material-ui/lab/Pagination'; +import TextField from '@material-ui/core/TextField'; +import DataTable from '../components/DataTable'; +import Grid from '@material-ui/core/Grid'; +import Toolbar from '@material-ui/core/Toolbar'; +import Autocomplete from '@material-ui/lab/Autocomplete'; +import { useAuth0 } from '../react-auth0-spa'; const queryBuilder = ({ max = 15, skip = 0, filter }) => { - return `q={${filter ? `"year": ${filter}` : ""}}&max=${max}&skip=${skip}`; + return `q={${filter ? `'year': ${filter}` : ''}}&max=${max}&skip=${skip}`; }; const HomePage = () => { const [years, setYears] = useState([]); const [data, setData] = useState([]); + const { apiSun } = useAuth0(); const [{ max, skip, count, filterValue }, setPagination] = useState({ max: 15, skip: 0, count: 0, - filterValue: "", + filterValue: '', }); const [initialized, setInitialized] = useState(false); @@ -39,16 +40,16 @@ const HomePage = () => { useEffect(() => { setInitialized(false); Promise.all([ - Api.sun.get(`rest/sunshine?${queryBuilder({ max, skip, filter: filterValue })}&totals=true`).then(({ data, totals }) => { + apiSun.get(`rest/sunshine?${queryBuilder({ max, skip, filter: filterValue })}&totals=true`).then(({ data, totals }) => { setData(data); setPagination((prevPagination) => ({ ...prevPagination, count: totals.total })); }), - Api.sun.get(`rest/sunshine?q={}&h={"$fields": {"year": 1}}`).then((data) => setYears([...new Set(data.map(({ year }) => `${year}`))])), + apiSun.get('rest/sunshine?q={}&h={"$fields": {"year": 1}}').then((data) => setYears([...new Set(data.map(({ year }) => `${year}`))])), ]).then(() => setInitialized(true)); }, [skip, filterValue]); - const addMessage = () => Api.updateData("professors.messages", { message: "Hola" }, "add"); - const replaceMessage = () => Api.updateData("professors.messages", { message: "Hola" }, "replace"); + const addMessage = () => apiSun.updateData('professors.messages', { message: 'Hola' }, 'add'); + const replaceMessage = () => apiSun.updateData('professors.messages', { message: 'Hola' }, 'replace'); return ( <div> <h1>Home page</h1> @@ -58,7 +59,7 @@ const HomePage = () => { <Grid container spacing={3} justify="space-between"> <Grid item> <Autocomplete - style={{ minWidth: "150px" }} + style={{ minWidth: '150px' }} options={years} id="year-picker" value={filterValue} @@ -69,7 +70,7 @@ const HomePage = () => { </Grid> <Grid item> <Pagination - style={{ marginTop: "12px" }} + style={{ marginTop: '12px' }} count={Math.ceil(count / max)} page={skip / max + 1} onChange={handlePagination} diff --git a/src/views/LandingPage.js b/src/views/LandingPage.js index 1c7e9467b6747c5635609ce7ee3b0cb0a2113ee1..c7f78c34dfa46bbc24e750869a542dc953202951 100644 --- a/src/views/LandingPage.js +++ b/src/views/LandingPage.js @@ -1,13 +1,15 @@ -import React from "react"; -import { useAuth0 } from "../react-auth0-spa"; -import { useNavigate } from "react-router-dom"; +import React, { useEffect } from 'react'; +import { useAuth0 } from '../react-auth0-spa'; +import { useNavigate } from 'react-router-dom'; const LandingPage = () => { const navigate = useNavigate(); const { user } = useAuth0(); - if (user) { - navigate("/home-page"); - } + useEffect(() => { + if (user) { + navigate('/home-page'); + } + }, [user]); return <h1>Landing page</h1>; }; diff --git a/src/views/Profile.js b/src/views/Profile.js index 8d1a1a6df20f8c6b05c8bc95a6417974cfcf9f99..02cd1bfb0e567fa9b68d00c2f482934682aac3fb 100644 --- a/src/views/Profile.js +++ b/src/views/Profile.js @@ -1,11 +1,11 @@ -import React from "react"; -import { Form, Field } from "react-final-form"; -import TextField from "@material-ui/core/TextField"; -import Grid from "@material-ui/core/Grid"; -import "./profile.css"; -import Loading from "../components/Loading"; -import { useAuth0 } from "../react-auth0-spa"; -import PropTypes from "prop-types"; +import React from 'react'; +import { Form, Field } from 'react-final-form'; +import TextField from '@material-ui/core/TextField'; +import Grid from '@material-ui/core/Grid'; +import './profile.css'; +import Loading from '../components/Loading'; +import { useAuth0 } from '../react-auth0-spa'; +import PropTypes from 'prop-types'; const FormTextField = ({ input, meta, ...props }) => ( <Grid item xs={12}> @@ -46,7 +46,7 @@ const Profile = () => { <Grid container> <Form initialValues={initialValues} onSubmit={handleSubmit}> {({ handleSubmit }) => ( - <form onSubmit={handleSubmit} style={{ width: "100%" }}> + <form onSubmit={handleSubmit} style={{ width: '100%' }}> <Field name="name" component={FormTextField} label="Name" type="text" /> <Field name="nickname" component={FormTextField} label="Nickname" type="text" /> <Field name="picture" component={FormTextField} label="Picture" type="text" />