Fixed broken API endpoints

This commit is contained in:
Ceferino Patino 2022-03-14 09:18:17 -05:00
commit d000283453
26 changed files with 28831 additions and 24585 deletions

52365
client/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -20,7 +20,7 @@
},
"scripts": {
"start": "react-scripts start",
"build": "cp ${ENV:-'../.env'} .env && react-scripts build",
"build": "cp ${ENV:-'../.env'} .env && rm -rf build && react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},

View file

@ -1,4 +1,4 @@
import React from 'react';
import React, { useState } from 'react';
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
import Navbar from './components/Navbar';
@ -17,14 +17,22 @@ import OrgDashboard from './components/orgs/OrgDashboard';
import UpdateOrganization from './components/forms/UpdateOrganization';
function App() {
const [token, setToken] = useState(false);
return (
<React.Fragment>
<BrowserRouter>
<Routes>
<Route path='' element={<Navbar />}>
<Route
path=''
element={<Navbar tokenState={{ token, setToken }} />}
>
<Route index element={<MainPage />} />
<Route path='login' element={<Login />} />
<Route
path='login'
element={<Login setToken={setToken} />}
/>
<Route path='register' element={<Register />} />
<Route path='update' element={<EditUser />} />
<Route path='recover' element={<Form />}>
@ -32,10 +40,10 @@ function App() {
<Route path=':id' element={<Reset />} />
</Route>
<Route path='org' element={<OwnedOrgs />} />
<Route path='joined'>
<Route index element={<MemberOrgs />} />
<Route path=':id'>
<Route path='org'>
<Route index element={<OwnedOrgs />} />
<Route path='joined' element={<MemberOrgs />} />
<Route path=':orgID'>
<Route index element={<OrgDashboard />} />
<Route
path='update'

View file

@ -35,55 +35,11 @@ function MainPage() {
}
});
useEffect(() => {
async function getOwnedOrgs() {
await axios
.get(
`${
process.env.REACT_APP_DOMAIN_ROOT
}/auth?token=${Cookies.get('token')}`
)
.then((response) => {
setOwnedOrgs(() =>
response.data.map((org) => {
return {
orgID: org.orgID,
orgName: org.orgName,
memberCount: org.memberCount,
createdAt: org.createdAt,
};
})
);
})
.catch((e) => {
console.log(e);
});
}
getOwnedOrgs();
}, []);
useEffect(() => {}, []);
useEffect(() => {
async function getOrgs() {
await axios
.get(
`${
process.env.REACT_APP_DOMAIN_ROOT
}/auth/orgs?token=${Cookies.get('token')}`
)
.then((response) => {
setOrgs(() =>
response.data.map((org) => {
return {
orgID: org.orgID,
orgName: org.orgName,
memberCount: org.memberCount,
createdAt: org.createdAt,
};
})
);
});
}
getOrgs();
getMemberOrgs();
getOwnedOrgs();
}, []);
function handleChange(e) {
@ -95,24 +51,76 @@ function MainPage() {
});
}
async function joinOrg(e) {
e.preventDefault();
async function getOwnedOrgs() {
await axios
.get(
`${process.env.REACT_APP_DOMAIN_ROOT}/auth?token=${Cookies.get(
'token'
)}`
)
.then((response) => {
setOwnedOrgs(() =>
response.data.map((org) => {
return {
orgID: org.orgID,
orgName: org.orgName,
memberCount: org.memberCount,
createdAt: org.createdAt,
};
})
);
})
.catch((e) => {
console.log(e);
});
}
await axios.post(`${process.env.REACT_APP_DOMAIN_ROOT}/org/create`, {
token: Cookies.get('token'),
orgName: form.name,
});
async function getMemberOrgs() {
await axios
.get(
`${
process.env.REACT_APP_DOMAIN_ROOT
}/auth/orgs?token=${Cookies.get('token')}`
)
.then((response) => {
setOrgs(() =>
response.data.map((org) => {
return {
orgID: org.orgID,
orgName: org.orgName,
memberCount: org.memberCount,
createdAt: org.createdAt,
};
})
);
});
}
async function createOrg(e) {
e.preventDefault();
console.log('create');
await axios.post(
`${process.env.REACT_APP_DOMAIN_ROOT}/org/${form.id}`,
{
await axios
.post(`${process.env.REACT_APP_DOMAIN_ROOT}/org/create`, {
token: Cookies.get('token'),
}
);
orgName: form.name,
})
.then(() => {
navigate('');
getOwnedOrgs();
});
}
async function joinOrg(e) {
e.preventDefault();
await axios
.post(`${process.env.REACT_APP_DOMAIN_ROOT}/org/${form.id}/add`, {
token: Cookies.get('token'),
})
.then(() => {
getMemberOrgs();
});
}
return (
@ -131,7 +139,6 @@ function MainPage() {
<form onSubmit={joinOrg}>
<FormGroup row>
<TextField
id='standard-name'
label='Organization ID'
name='id'
value={form.id}
@ -154,10 +161,9 @@ function MainPage() {
<form onSubmit={createOrg}>
<FormGroup row>
<TextField
id='standard-name'
label='Organization Name'
name='orgName'
value={form.orgName}
name='name'
value={form.name}
onChange={handleChange}
sx={{ width: '70%' }}
/>
@ -191,7 +197,7 @@ function MainPage() {
</StackItem>
<StackItem>
<Typography variant='h6'>
<Link to='joined' style={linkStyle}>
<Link to='org/joined' style={linkStyle}>
Joined Organizations
</Link>
</Typography>

View file

@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react';
import { Link, Outlet } from 'react-router-dom';
import React from 'react';
import { Link, Outlet, useNavigate } from 'react-router-dom';
import axios from 'axios';
import Cookies from 'js-cookie';
@ -15,8 +15,10 @@ import {
import MenuIcon from '@mui/icons-material/Menu';
import { ThemeProvider, createTheme } from '@mui/material/styles';
function Navbar() {
const [token, setToken] = useState('');
function Navbar({ tokenState }) {
const navigate = useNavigate();
const { token, setToken } = tokenState;
const linkStyle = { textDecoration: 'none', color: 'inherit' };
const darkTheme = createTheme({
@ -25,17 +27,48 @@ function Navbar() {
},
});
useEffect(() => {
setToken(() => Cookies.get('token'));
}, []);
function ButtonGroup({ token }) {
if (token) {
return (
<Button onClick={handleLogout} color='inherit'>
<Link to='/login' style={linkStyle}>
Logout
</Link>
</Button>
);
} else {
return (
<React.Fragment>
<Button color='inherit'>
<Link to='/login' style={linkStyle}>
Login
</Link>
</Button>
<Button color='inherit'>
<Link to='/register' style={linkStyle}>
Register
</Link>
</Button>
</React.Fragment>
);
}
}
async function handleLogout() {
await axios.post(`${process.env.REACT_APP_DOMAIN_ROOT}/auth/logout`, {
token: Cookies.get('token'),
});
await axios
.post(`${process.env.REACT_APP_DOMAIN_ROOT}/auth/logout`, {
token: Cookies.get('token'),
})
.catch((e) => {
console.log(e);
});
Cookies.remove('token');
Cookies.remove('userID');
setToken(() => '');
setToken(() => false);
navigate('login');
}
return (
@ -63,27 +96,7 @@ function Navbar() {
KnowItAll
</Typography>
{token && (
<Button onClick={handleLogout} color='inherit'>
<Link to='/login' style={linkStyle}>
Logout
</Link>
</Button>
)}
{!token && (
<React.Fragment>
<Button color='inherit'>
<Link to='/login' style={linkStyle}>
Login
</Link>
</Button>
<Button color='inherit'>
<Link to='/register' style={linkStyle}>
Register
</Link>
</Button>
</React.Fragment>
)}
<ButtonGroup token={token} />
</Toolbar>
</AppBar>
<Outlet />

View file

@ -78,7 +78,6 @@ function EditUser() {
<TextField
required
fullWidth
id='outline-required'
label='First Name'
name='firstName'
variant='outlined'
@ -89,7 +88,6 @@ function EditUser() {
<TextField
required
fullWidth
id='outline-required'
label='Last Name'
name='lastName'
variant='outlined'
@ -100,7 +98,6 @@ function EditUser() {
<TextField
required
fullWidth
id='outline-required'
label='Email'
name='email'
variant='outlined'
@ -110,7 +107,6 @@ function EditUser() {
/>
<TextField
fullWidth
id='outline-required'
label='Pronouns'
name='pronouns'
variant='outlined'

View file

@ -15,7 +15,7 @@ import {
import Form from '../utils/Form';
function Login() {
function Login({ setToken }) {
const navigate = useNavigate();
const [form, setForm] = useState({
@ -26,19 +26,22 @@ function Login() {
const [error, setError] = useState('');
useEffect(() => {
if (Cookies.get('token')) {
async function logout() {
await axios
.post(`${process.env.REACT_APP_DOMAIN_ROOT}/auth/logout`, {
token: Cookies.get('token'),
})
.catch((e) => console.log(e));
}
logout();
async function logout() {
await axios
.post(`${process.env.REACT_APP_DOMAIN_ROOT}/auth/logout`, {
token: Cookies.get('token'),
})
.catch((e) => {
console.log(e);
});
Cookies.remove('token');
Cookies.remove('userID');
setToken(() => false);
}
}, []);
logout();
}, [setToken]);
function handleChange(e) {
const name = e.target.name;
@ -69,6 +72,7 @@ function Login() {
Cookies.set('userID', response.data.userID, {
expires: form.remember ? 3650 : 1,
});
setToken(true);
navigate('/');
})
.catch((e) => setError(() => e.response.data));
@ -83,7 +87,6 @@ function Login() {
<TextField
required
fullWidth
id='outline-required'
label='Email'
name='email'
variant='outlined'
@ -94,7 +97,6 @@ function Login() {
<TextField
required
fullWidth
id='outlined-password-input'
label='Password'
name='password'
type='password'

View file

@ -3,8 +3,6 @@ import axios from 'axios';
import { Box, Typography, TextField, Button } from '@mui/material';
import Form from '../utils/Form';
function Recover() {
const [form, setForm] = useState({
email: '',
@ -34,47 +32,44 @@ function Recover() {
}
return (
<Form>
<form onSubmit={handleSubmit}>
<Typography variant='h4' sx={{ marginTop: '1vh 0vh' }}>
Recover my Password
</Typography>
<TextField
required
fullWidth
id='outline-required'
label='Email'
name='email'
variant='outlined'
onChange={handleChange}
value={form.email}
sx={{ margin: '1vh 0vh' }}
/>
{error && (
<Box textAlign='right'>
<Typography variant='body2' color='error'>
{error}
</Typography>
</Box>
)}
{success && (
<Box textAlign='right'>
<Typography variant='body2' color='primary'>
{success}
</Typography>
</Box>
)}
<Button
variant='contained'
color='primary'
sx={{ margin: '1vh 0vh' }}
type='submit'
fullWidth
>
Recover my password
</Button>
</form>
</Form>
<form onSubmit={handleSubmit}>
<Typography variant='h4' sx={{ marginTop: '1vh 0vh' }}>
Recover my Password
</Typography>
<TextField
required
fullWidth
label='Email'
name='email'
variant='outlined'
onChange={handleChange}
value={form.email}
sx={{ margin: '1vh 0vh' }}
/>
{error && (
<Box textAlign='right'>
<Typography variant='body2' color='error'>
{error}
</Typography>
</Box>
)}
{success && (
<Box textAlign='right'>
<Typography variant='body2' color='primary'>
{success}
</Typography>
</Box>
)}
<Button
variant='contained'
color='primary'
sx={{ margin: '1vh 0vh' }}
type='submit'
fullWidth
>
Recover my password
</Button>
</form>
);
}

View file

@ -53,7 +53,6 @@ function Register() {
<TextField
required
fullWidth
id='outline-required'
label='First Name'
name='firstName'
variant='outlined'
@ -64,7 +63,6 @@ function Register() {
<TextField
required
fullWidth
id='outline-required'
label='Last Name'
name='lastName'
variant='outlined'
@ -75,7 +73,6 @@ function Register() {
<TextField
required
fullWidth
id='outline-required'
label='Email'
name='email'
variant='outlined'
@ -85,7 +82,6 @@ function Register() {
/>
<TextField
fullWidth
id='outline-required'
label='Pronouns'
name='pronouns'
variant='outlined'
@ -96,7 +92,6 @@ function Register() {
<TextField
required
fullWidth
id='outlined-password-input'
label='Password'
name='password'
type='password'
@ -108,7 +103,6 @@ function Register() {
<TextField
required
fullWidth
id='outlined-password-input'
label='Confirm Password'
name='confirmPassword'
type='password'

View file

@ -5,8 +5,6 @@ import axios from 'axios';
import { Box, Typography, TextField, Button } from '@mui/material';
import Form from '../utils/Form';
function Reset() {
const navigate = useNavigate();
const { id } = useParams();
@ -42,54 +40,50 @@ function Reset() {
}
return (
<Form>
<form onSubmit={handleSubmit}>
<Typography variant='h4' sx={{ marginTop: '1vh 0vh' }}>
Reset my Password
</Typography>
<TextField
required
fullWidth
id='outline-required'
label='Password'
name='password'
variant='outlined'
type='password'
onChange={handleChange}
value={form.password}
sx={{ margin: '1vh 0vh' }}
/>
<TextField
required
fullWidth
id='outline-required'
label='Confirm Password'
name='confirmPassword'
variant='outlined'
type='password'
onChange={handleChange}
value={form.confirmPassword}
sx={{ margin: '1vh 0vh' }}
/>
{error && (
<Box textAlign='right'>
<Typography variant='body2' color='error'>
{error}
</Typography>
</Box>
)}
<Button
variant='contained'
color='primary'
sx={{ margin: '1vh 0vh' }}
type='submit'
fullWidth
disabled={form.password !== form.confirmPassword}
>
Reset my Password
</Button>
</form>
</Form>
<form onSubmit={handleSubmit}>
<Typography variant='h4' sx={{ marginTop: '1vh 0vh' }}>
Reset my Password
</Typography>
<TextField
required
fullWidth
label='Password'
name='password'
variant='outlined'
type='password'
onChange={handleChange}
value={form.password}
sx={{ margin: '1vh 0vh' }}
/>
<TextField
required
fullWidth
label='Confirm Password'
name='confirmPassword'
variant='outlined'
type='password'
onChange={handleChange}
value={form.confirmPassword}
sx={{ margin: '1vh 0vh' }}
/>
{error && (
<Box textAlign='right'>
<Typography variant='body2' color='error'>
{error}
</Typography>
</Box>
)}
<Button
variant='contained'
color='primary'
sx={{ margin: '1vh 0vh' }}
type='submit'
fullWidth
disabled={form.password !== form.confirmPassword}
>
Reset my Password
</Button>
</form>
);
}

View file

@ -1,7 +1,8 @@
import React, { useState } from 'react';
import { useParams } from 'react-router-dom';
import { useParams, useNavigate } from 'react-router-dom';
import axios from 'axios';
import Cookies from 'js-cookie';
import { Typography, TextField, Button } from '@mui/material';
@ -9,6 +10,7 @@ import Form from '../utils/Form';
function UpdateOrganization() {
const { orgID } = useParams();
const navigate = useNavigate();
const [name, setName] = useState('');
@ -20,10 +22,15 @@ function UpdateOrganization() {
async function handleSubmit(e) {
e.preventDefault();
await axios.patch(`${process.env.REACT_APP_DOMAIN_ROOT}/org/update`, {
orgName: name,
orgID,
});
await axios
.patch(`${process.env.REACT_APP_DOMAIN_ROOT}/org/update`, {
token: Cookies.get('token'),
orgName: name,
orgID,
})
.then(() => {
navigate(`/org/${orgID}`);
});
}
return (
@ -35,8 +42,7 @@ function UpdateOrganization() {
<TextField
required
fullWidth
id='outline-required'
label='name'
label='Name'
name='name'
variant='outlined'
onChange={handleChange}

View file

@ -19,7 +19,18 @@ function OrgDashboard() {
const { orgID } = useParams();
const navigate = useNavigate();
const [org, setOrg] = useState({});
const [org, setOrg] = useState({
orgID: '',
orgName: '',
orgOwner: {
firstName: '',
lastName: '',
email: '',
},
orgMembers: [],
createdAt: '',
memberCount: 0,
});
const [rows, setRows] = useState([]);
const [status, setStatus] = useState('');
const [selection, setSelection] = useState();
@ -36,37 +47,41 @@ function OrgDashboard() {
)
.then((response) => {
const res = response.data;
setOrg(() => {
return {
orgID: res.orgID,
orgName: res.orgName,
orgID: res.org.orgID,
orgName: res.org.orgName,
orgOwner: {
firstName: res.orgOwner.firstName,
lastName: res.orgOwner.lastName,
email: res.orgOwner.email,
firstName: res.org.orgOwner.firstName,
lastName: res.org.orgOwner.lastName,
email: res.org.orgOwner.email,
},
orgMembers: res.orgMembers,
createdAt: res.createdAt,
orgMembers: res.org.orgMembers,
createdAt: res.org.createdAt,
memberCount: res.memberCount,
};
});
const orgMembers = res.orgMembers.map((member, index) => {
const orgMembers = res.org.orgMembers.map((member) => {
return {
id: member.userID,
...member,
};
});
setRows(() => orgMembers);
setStatus(() => res.status);
})
.catch(() => {
.catch((e) => {
console.log(e);
navigate('/');
});
}
getData();
});
}, [navigate, orgID]);
async function handleDelete() {
await axios
@ -76,7 +91,7 @@ function OrgDashboard() {
orgID,
},
})
.then(navigate(''));
.then(navigate('/'));
}
function copyID() {
@ -117,7 +132,7 @@ function OrgDashboard() {
Organization ID: {org.orgID}
</Typography>
<Typography variant='body1'>
Members: {org.orgMembers.length}
Members: {org.memberCount}
</Typography>
<Typography variant='body1'>
Created On: {org.createdAt}

View file

@ -9,7 +9,7 @@ function OrgUnit({ org }) {
return (
<React.Fragment>
<Typography variant='h6'>
<Link to={orgID}>Name: {orgName}</Link>
<Link to={`org/${orgID}`}>Name: {orgName}</Link>
</Typography>
<Box>
<Stack spacing={2} direction={{ xs: 'column', md: 'row' }}>