Compare commits
15 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| efed383a4d | |||
| 9f6618d787 | |||
| 2f766c723c | |||
| 13c95416fb | |||
| d3986880dd | |||
| 8fd76a74eb | |||
| 1b03844edf | |||
| 64fb6ec9da | |||
| 868c040f1a | |||
| fe881ea105 | |||
| a88b94a9bf | |||
| 968088d242 | |||
| f2a7f16dfb | |||
| a3066aa4e0 | |||
| d875b9a4c3 |
121 changed files with 46166 additions and 14531 deletions
7
.eslintignore
Normal file
7
.eslintignore
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
*.json
|
||||
*.yml
|
||||
*.sqlite3
|
||||
*.md
|
||||
**/public
|
||||
LICENSE
|
||||
Dockerfile
|
||||
15
.eslintrc.yml
Normal file
15
.eslintrc.yml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
env:
|
||||
browser: true
|
||||
es2021: true
|
||||
jest: true
|
||||
extends:
|
||||
- plugin:react/recommended
|
||||
- airbnb
|
||||
overrides: []
|
||||
parserOptions:
|
||||
ecmaVersion: latest
|
||||
sourceType: module
|
||||
plugins:
|
||||
- react
|
||||
- jest
|
||||
rules: { 'indent': ['error', 4, { 'SwitchCase': 1 }], 'comma-dangle': 'off' }
|
||||
16
.github/workflows/test-build.yml
vendored
16
.github/workflows/test-build.yml
vendored
|
|
@ -10,7 +10,7 @@ on:
|
|||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
test-build:
|
||||
environment: test
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
|
|
@ -28,3 +28,17 @@ jobs:
|
|||
run: npm ci
|
||||
- name: Test build
|
||||
run: npm run test
|
||||
run-eslint:
|
||||
environment: test
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 16.x
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 16.x
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Test formmating
|
||||
run: npm run eslint:all
|
||||
|
|
|
|||
22
.gitignore
vendored
22
.gitignore
vendored
|
|
@ -1,17 +1,9 @@
|
|||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/node,react,git,visualstudiocode
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=node,react,git,visualstudiocode
|
||||
|
||||
**/dist.tar.gz
|
||||
|
||||
### Express ###
|
||||
**/public/
|
||||
|
||||
### TODO ###
|
||||
TODO
|
||||
|
||||
### Sqlite ###
|
||||
server/**/public
|
||||
*.sqlite3
|
||||
*.sqlite3-journal
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/node,react,visualstudiocode,git
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=node,react,visualstudiocode,git
|
||||
|
||||
### Git ###
|
||||
# Created by git for backups. To disable backups in Git:
|
||||
|
|
@ -200,6 +192,4 @@ sketch
|
|||
.history
|
||||
.ionide
|
||||
|
||||
# Support for Project snippet scope
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/node,react,git,visualstudiocode
|
||||
# End of https://www.toptal.com/developers/gitignore/api/node,react,visualstudiocode,git
|
||||
|
|
|
|||
1
.npmrc
Normal file
1
.npmrc
Normal file
|
|
@ -0,0 +1 @@
|
|||
legacy-peer-deps=true
|
||||
13
client/.storybook/main.js
Normal file
13
client/.storybook/main.js
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
module.exports = {
|
||||
stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
|
||||
addons: [
|
||||
'@storybook/addon-links',
|
||||
'@storybook/addon-essentials',
|
||||
'@storybook/addon-interactions',
|
||||
'@storybook/preset-create-react-app',
|
||||
],
|
||||
framework: '@storybook/react',
|
||||
core: {
|
||||
builder: '@storybook/builder-webpack5',
|
||||
},
|
||||
};
|
||||
100
client/.storybook/preview.js
Normal file
100
client/.storybook/preview.js
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
import { useMemo } from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
import { CssBaseline } from '@mui/material';
|
||||
import { ThemeProvider } from '@mui/material/styles';
|
||||
import '@storybook/addon-console';
|
||||
import { initialize, mswDecorator } from 'msw-storybook-addon';
|
||||
import { cookieDecorator } from 'storybook-addon-cookie';
|
||||
|
||||
import { darkTheme, lightTheme } from '../src/themes';
|
||||
|
||||
export const parameters = {
|
||||
actions: { argTypesRegex: '^on[A-Z].*' },
|
||||
controls: {
|
||||
matchers: {
|
||||
color: /(background|color)$/i,
|
||||
date: /Date$/,
|
||||
},
|
||||
},
|
||||
options: {
|
||||
storySort: {
|
||||
method: 'alphabetical',
|
||||
},
|
||||
},
|
||||
layout: 'fullscreen',
|
||||
viewport: {
|
||||
viewports: {
|
||||
laptop: {
|
||||
name: 'Laptop',
|
||||
styles: {
|
||||
width: '1024px',
|
||||
height: '576px',
|
||||
},
|
||||
type: 'desktop',
|
||||
},
|
||||
tablet: {
|
||||
name: 'Tablet',
|
||||
styles: {
|
||||
width: '768px',
|
||||
height: '1024px',
|
||||
},
|
||||
type: 'Tablet',
|
||||
},
|
||||
mobile: {
|
||||
name: 'Mobile',
|
||||
styles: {
|
||||
width: '360px',
|
||||
height: '740px',
|
||||
},
|
||||
type: 'mobile',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const globalTypes = {
|
||||
theme: {
|
||||
name: 'Theme',
|
||||
title: 'Theme',
|
||||
defaultValue: 'light',
|
||||
toolbar: {
|
||||
icon: 'paintbrush',
|
||||
dynamicTitle: true,
|
||||
items: [
|
||||
{ value: 'light', title: 'Light Mode', left: '🌞' },
|
||||
{ value: 'dark', title: 'Dark Mode', left: '🌙' },
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const THEMES = {
|
||||
light: lightTheme,
|
||||
dark: darkTheme,
|
||||
};
|
||||
|
||||
export const withMuiTheme = (Story, context) => {
|
||||
const { theme: themeKey } = context.globals;
|
||||
|
||||
const theme = useMemo(() => THEMES[themeKey] || THEMES['light'], [themeKey]);
|
||||
|
||||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
<CssBaseline enableColorScheme />
|
||||
<Story />
|
||||
</ThemeProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export const withBrowserRouter = (Story) => {
|
||||
return (
|
||||
<MemoryRouter>
|
||||
<Story />
|
||||
</MemoryRouter>
|
||||
);
|
||||
};
|
||||
|
||||
initialize();
|
||||
|
||||
export const decorators = [mswDecorator, cookieDecorator, withBrowserRouter, withMuiTheme];
|
||||
|
|
@ -26,12 +26,24 @@
|
|||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
"eject": "react-scripts eject",
|
||||
"storybook": "start-storybook -p 6006 -s public",
|
||||
"build-storybook": "build-storybook -s public"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"**/*.stories.*"
|
||||
],
|
||||
"rules": {
|
||||
"import/no-anonymous-default-export": "off"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
|
|
@ -45,5 +57,27 @@
|
|||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@storybook/addon-actions": "^6.5.15",
|
||||
"@storybook/addon-console": "^1.2.3",
|
||||
"@storybook/addon-essentials": "^6.5.15",
|
||||
"@storybook/addon-interactions": "^6.5.15",
|
||||
"@storybook/addon-links": "^6.5.15",
|
||||
"@storybook/builder-webpack5": "^6.5.15",
|
||||
"@storybook/manager-webpack5": "^6.5.15",
|
||||
"@storybook/node-logger": "^6.5.15",
|
||||
"@storybook/preset-create-react-app": "^4.1.2",
|
||||
"@storybook/react": "^6.5.15",
|
||||
"@storybook/testing-library": "^0.0.13",
|
||||
"babel-plugin-named-exports-order": "^0.0.2",
|
||||
"msw": "^0.49.2",
|
||||
"msw-storybook-addon": "^1.6.3",
|
||||
"prop-types": "^15.8.1",
|
||||
"storybook-addon-cookie": "^1.0.6",
|
||||
"webpack": "^5.75.0"
|
||||
},
|
||||
"msw": {
|
||||
"workerDirectory": "public"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
303
client/public/mockServiceWorker.js
Normal file
303
client/public/mockServiceWorker.js
Normal file
|
|
@ -0,0 +1,303 @@
|
|||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
|
||||
/**
|
||||
* Mock Service Worker (0.49.2).
|
||||
* @see https://github.com/mswjs/msw
|
||||
* - Please do NOT modify this file.
|
||||
* - Please do NOT serve this file on production.
|
||||
*/
|
||||
|
||||
const INTEGRITY_CHECKSUM = '3d6b9f06410d179a7f7404d4bf4c3c70'
|
||||
const activeClientIds = new Set()
|
||||
|
||||
self.addEventListener('install', function () {
|
||||
self.skipWaiting()
|
||||
})
|
||||
|
||||
self.addEventListener('activate', function (event) {
|
||||
event.waitUntil(self.clients.claim())
|
||||
})
|
||||
|
||||
self.addEventListener('message', async function (event) {
|
||||
const clientId = event.source.id
|
||||
|
||||
if (!clientId || !self.clients) {
|
||||
return
|
||||
}
|
||||
|
||||
const client = await self.clients.get(clientId)
|
||||
|
||||
if (!client) {
|
||||
return
|
||||
}
|
||||
|
||||
const allClients = await self.clients.matchAll({
|
||||
type: 'window',
|
||||
})
|
||||
|
||||
switch (event.data) {
|
||||
case 'KEEPALIVE_REQUEST': {
|
||||
sendToClient(client, {
|
||||
type: 'KEEPALIVE_RESPONSE',
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
case 'INTEGRITY_CHECK_REQUEST': {
|
||||
sendToClient(client, {
|
||||
type: 'INTEGRITY_CHECK_RESPONSE',
|
||||
payload: INTEGRITY_CHECKSUM,
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
case 'MOCK_ACTIVATE': {
|
||||
activeClientIds.add(clientId)
|
||||
|
||||
sendToClient(client, {
|
||||
type: 'MOCKING_ENABLED',
|
||||
payload: true,
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
case 'MOCK_DEACTIVATE': {
|
||||
activeClientIds.delete(clientId)
|
||||
break
|
||||
}
|
||||
|
||||
case 'CLIENT_CLOSED': {
|
||||
activeClientIds.delete(clientId)
|
||||
|
||||
const remainingClients = allClients.filter((client) => {
|
||||
return client.id !== clientId
|
||||
})
|
||||
|
||||
// Unregister itself when there are no more clients
|
||||
if (remainingClients.length === 0) {
|
||||
self.registration.unregister()
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.addEventListener('fetch', function (event) {
|
||||
const { request } = event
|
||||
const accept = request.headers.get('accept') || ''
|
||||
|
||||
// Bypass server-sent events.
|
||||
if (accept.includes('text/event-stream')) {
|
||||
return
|
||||
}
|
||||
|
||||
// Bypass navigation requests.
|
||||
if (request.mode === 'navigate') {
|
||||
return
|
||||
}
|
||||
|
||||
// Opening the DevTools triggers the "only-if-cached" request
|
||||
// that cannot be handled by the worker. Bypass such requests.
|
||||
if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') {
|
||||
return
|
||||
}
|
||||
|
||||
// Bypass all requests when there are no active clients.
|
||||
// Prevents the self-unregistered worked from handling requests
|
||||
// after it's been deleted (still remains active until the next reload).
|
||||
if (activeClientIds.size === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
// Generate unique request ID.
|
||||
const requestId = Math.random().toString(16).slice(2)
|
||||
|
||||
event.respondWith(
|
||||
handleRequest(event, requestId).catch((error) => {
|
||||
if (error.name === 'NetworkError') {
|
||||
console.warn(
|
||||
'[MSW] Successfully emulated a network error for the "%s %s" request.',
|
||||
request.method,
|
||||
request.url,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// At this point, any exception indicates an issue with the original request/response.
|
||||
console.error(
|
||||
`\
|
||||
[MSW] Caught an exception from the "%s %s" request (%s). This is probably not a problem with Mock Service Worker. There is likely an additional logging output above.`,
|
||||
request.method,
|
||||
request.url,
|
||||
`${error.name}: ${error.message}`,
|
||||
)
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
async function handleRequest(event, requestId) {
|
||||
const client = await resolveMainClient(event)
|
||||
const response = await getResponse(event, client, requestId)
|
||||
|
||||
// Send back the response clone for the "response:*" life-cycle events.
|
||||
// Ensure MSW is active and ready to handle the message, otherwise
|
||||
// this message will pend indefinitely.
|
||||
if (client && activeClientIds.has(client.id)) {
|
||||
;(async function () {
|
||||
const clonedResponse = response.clone()
|
||||
sendToClient(client, {
|
||||
type: 'RESPONSE',
|
||||
payload: {
|
||||
requestId,
|
||||
type: clonedResponse.type,
|
||||
ok: clonedResponse.ok,
|
||||
status: clonedResponse.status,
|
||||
statusText: clonedResponse.statusText,
|
||||
body:
|
||||
clonedResponse.body === null ? null : await clonedResponse.text(),
|
||||
headers: Object.fromEntries(clonedResponse.headers.entries()),
|
||||
redirected: clonedResponse.redirected,
|
||||
},
|
||||
})
|
||||
})()
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
// Resolve the main client for the given event.
|
||||
// Client that issues a request doesn't necessarily equal the client
|
||||
// that registered the worker. It's with the latter the worker should
|
||||
// communicate with during the response resolving phase.
|
||||
async function resolveMainClient(event) {
|
||||
const client = await self.clients.get(event.clientId)
|
||||
|
||||
if (client?.frameType === 'top-level') {
|
||||
return client
|
||||
}
|
||||
|
||||
const allClients = await self.clients.matchAll({
|
||||
type: 'window',
|
||||
})
|
||||
|
||||
return allClients
|
||||
.filter((client) => {
|
||||
// Get only those clients that are currently visible.
|
||||
return client.visibilityState === 'visible'
|
||||
})
|
||||
.find((client) => {
|
||||
// Find the client ID that's recorded in the
|
||||
// set of clients that have registered the worker.
|
||||
return activeClientIds.has(client.id)
|
||||
})
|
||||
}
|
||||
|
||||
async function getResponse(event, client, requestId) {
|
||||
const { request } = event
|
||||
const clonedRequest = request.clone()
|
||||
|
||||
function passthrough() {
|
||||
// Clone the request because it might've been already used
|
||||
// (i.e. its body has been read and sent to the client).
|
||||
const headers = Object.fromEntries(clonedRequest.headers.entries())
|
||||
|
||||
// Remove MSW-specific request headers so the bypassed requests
|
||||
// comply with the server's CORS preflight check.
|
||||
// Operate with the headers as an object because request "Headers"
|
||||
// are immutable.
|
||||
delete headers['x-msw-bypass']
|
||||
|
||||
return fetch(clonedRequest, { headers })
|
||||
}
|
||||
|
||||
// Bypass mocking when the client is not active.
|
||||
if (!client) {
|
||||
return passthrough()
|
||||
}
|
||||
|
||||
// Bypass initial page load requests (i.e. static assets).
|
||||
// The absence of the immediate/parent client in the map of the active clients
|
||||
// means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet
|
||||
// and is not ready to handle requests.
|
||||
if (!activeClientIds.has(client.id)) {
|
||||
return passthrough()
|
||||
}
|
||||
|
||||
// Bypass requests with the explicit bypass header.
|
||||
// Such requests can be issued by "ctx.fetch()".
|
||||
if (request.headers.get('x-msw-bypass') === 'true') {
|
||||
return passthrough()
|
||||
}
|
||||
|
||||
// Notify the client that a request has been intercepted.
|
||||
const clientMessage = await sendToClient(client, {
|
||||
type: 'REQUEST',
|
||||
payload: {
|
||||
id: requestId,
|
||||
url: request.url,
|
||||
method: request.method,
|
||||
headers: Object.fromEntries(request.headers.entries()),
|
||||
cache: request.cache,
|
||||
mode: request.mode,
|
||||
credentials: request.credentials,
|
||||
destination: request.destination,
|
||||
integrity: request.integrity,
|
||||
redirect: request.redirect,
|
||||
referrer: request.referrer,
|
||||
referrerPolicy: request.referrerPolicy,
|
||||
body: await request.text(),
|
||||
bodyUsed: request.bodyUsed,
|
||||
keepalive: request.keepalive,
|
||||
},
|
||||
})
|
||||
|
||||
switch (clientMessage.type) {
|
||||
case 'MOCK_RESPONSE': {
|
||||
return respondWithMock(clientMessage.data)
|
||||
}
|
||||
|
||||
case 'MOCK_NOT_FOUND': {
|
||||
return passthrough()
|
||||
}
|
||||
|
||||
case 'NETWORK_ERROR': {
|
||||
const { name, message } = clientMessage.data
|
||||
const networkError = new Error(message)
|
||||
networkError.name = name
|
||||
|
||||
// Rejecting a "respondWith" promise emulates a network error.
|
||||
throw networkError
|
||||
}
|
||||
}
|
||||
|
||||
return passthrough()
|
||||
}
|
||||
|
||||
function sendToClient(client, message) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const channel = new MessageChannel()
|
||||
|
||||
channel.port1.onmessage = (event) => {
|
||||
if (event.data && event.data.error) {
|
||||
return reject(event.data.error)
|
||||
}
|
||||
|
||||
resolve(event.data)
|
||||
}
|
||||
|
||||
client.postMessage(message, [channel.port2])
|
||||
})
|
||||
}
|
||||
|
||||
function sleep(timeMs) {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(resolve, timeMs)
|
||||
})
|
||||
}
|
||||
|
||||
async function respondWithMock(response) {
|
||||
await sleep(response.delay)
|
||||
return new Response(response.body, response)
|
||||
}
|
||||
|
|
@ -1,16 +1,14 @@
|
|||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
|
||||
import { Form } from '.';
|
||||
import { RecordMp3 } from '../controllers';
|
||||
import { FormSubmit, FormTextField } from './StyledElements';
|
||||
|
||||
function EditUserForm({ form, error, handleSubmit, handleChange }) {
|
||||
const linkStyle = { textDecoration: 'none', color: 'inherit' };
|
||||
|
||||
return (
|
||||
<Form text='Update Account Details'>
|
||||
<Form text='Update Account Details' error={error}>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<FormTextField
|
||||
required
|
||||
|
|
@ -40,13 +38,6 @@ function EditUserForm({ form, error, handleSubmit, handleChange }) {
|
|||
onChange={handleChange}
|
||||
value={form.pronouns}
|
||||
/>
|
||||
{error && (
|
||||
<Box textAlign='right'>
|
||||
<Typography variant='body2' color='secondary'>
|
||||
{error}
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
<FormSubmit color='primary' type='submit'>
|
||||
Update Details
|
||||
</FormSubmit>
|
||||
|
|
@ -56,7 +47,6 @@ function EditUserForm({ form, error, handleSubmit, handleChange }) {
|
|||
</Link>
|
||||
</FormSubmit>
|
||||
</form>
|
||||
<RecordMp3 />
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@ import DarkModeIcon from '@mui/icons-material/DarkMode';
|
|||
import LightModeIcon from '@mui/icons-material/LightMode';
|
||||
import LoginIcon from '@mui/icons-material/Login';
|
||||
import LogoutIcon from '@mui/icons-material/Logout';
|
||||
import MailIcon from '@mui/icons-material/Mail';
|
||||
import SearchIcon from '@mui/icons-material/Search';
|
||||
import MenuIcon from '@mui/icons-material/Menu';
|
||||
import {
|
||||
AppBar,
|
||||
|
|
@ -25,7 +23,7 @@ import { Link } from 'react-router-dom';
|
|||
|
||||
export default function MobileNavbar({ logout, userData, open, setOpen, mode, toggleMode }) {
|
||||
function ControlButtons() {
|
||||
return userData.username ? (
|
||||
return userData?.email ? (
|
||||
<ListItem>
|
||||
<ListItemButton color='inherit' component={Link} to='/login' onClick={logout}>
|
||||
<ListItemIcon>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { Link } from 'react-router-dom';
|
|||
|
||||
export default function Navbar({ logout, userData, mode, toggleMode }) {
|
||||
function NavbarControls() {
|
||||
return userData.email ? (
|
||||
return userData?.email ? (
|
||||
<>
|
||||
<Button color='inherit' component={Link} to='/update'>
|
||||
Account
|
||||
|
|
|
|||
|
|
@ -21,9 +21,12 @@ function OrganizationList({ orgs }) {
|
|||
<Typography variant='h6'>Organizations</Typography>
|
||||
<hr />
|
||||
<Box sx={{ overflowY: 'auto' }}>
|
||||
{orgs.map((org, index) => (
|
||||
<OrgUnit org={org} key={index} />
|
||||
))}
|
||||
{orgs.length === 0 ? (
|
||||
<Typography variant='body1'>Nothing to see here</Typography>
|
||||
) : (
|
||||
orgs.map((org, index) => <OrgUnit org={org} key={index} />)
|
||||
)}
|
||||
{}
|
||||
</Box>
|
||||
</Paper>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Link } from 'react-router-dom';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
import { Typography } from '@mui/material';
|
||||
|
||||
import Form from './Form';
|
||||
import { FormSubmit, FormTextField } from './StyledElements';
|
||||
|
|
|
|||
|
|
@ -11,8 +11,9 @@ import RecoverForm from './RecoverForm';
|
|||
import ResetForm from './ResetForm';
|
||||
import OrganizationList from './OrganizationList';
|
||||
import MemberUnit from './MemberUnit';
|
||||
import Dashboard from './Dashboard';
|
||||
import OrganizationDashboard from './OrganizationDashboard';
|
||||
import UpdateOrganizationForm from './UpdateOrganizationForm';
|
||||
import RegisterForm from './RegisterForm';
|
||||
|
||||
export {
|
||||
Navbar,
|
||||
|
|
@ -28,6 +29,7 @@ export {
|
|||
ResetForm,
|
||||
OrganizationList,
|
||||
MemberUnit,
|
||||
Dashboard,
|
||||
OrganizationDashboard,
|
||||
UpdateOrganizationForm,
|
||||
RegisterForm,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,14 +1,26 @@
|
|||
import Helmet from 'react-helmet';
|
||||
import { useEffect } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import Cookies from 'js-cookie';
|
||||
|
||||
import { EditUserForm } from '../controllers';
|
||||
import { EditUserForm, RecordMp3 } from '../controllers';
|
||||
|
||||
function Login() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
if (!Cookies.get('token')) {
|
||||
navigate('/login');
|
||||
}
|
||||
}, [navigate]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>Edit User | KnowItAll</title>
|
||||
<title>Update Account Details | KnowItAll</title>
|
||||
</Helmet>
|
||||
<EditUserForm />
|
||||
<RecordMp3 />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +1,22 @@
|
|||
import { Box, CssBaseline } from '@mui/material';
|
||||
import { createTheme, ThemeProvider } from '@mui/material/styles';
|
||||
import { ThemeProvider } from '@mui/material/styles';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { Outlet } from 'react-router-dom';
|
||||
|
||||
import { useMode } from '../contexts';
|
||||
import { Navbar } from '../controllers';
|
||||
import { lightTheme, darkTheme } from '../themes';
|
||||
|
||||
const THEMES = {
|
||||
light: lightTheme,
|
||||
dark: darkTheme,
|
||||
};
|
||||
|
||||
function Layout() {
|
||||
const { mode } = useMode();
|
||||
|
||||
const theme = createTheme({
|
||||
palette: { mode },
|
||||
breakpoints: {
|
||||
values: {
|
||||
xs: 0,
|
||||
sm: 768,
|
||||
md: 1024,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
<ThemeProvider theme={THEMES[mode]}>
|
||||
<CssBaseline enableColorScheme />
|
||||
<Helmet>
|
||||
<title>KnowItAll</title>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ function MainPage() {
|
|||
if (!Cookies.get('token')) {
|
||||
navigate('/login');
|
||||
}
|
||||
}, []);
|
||||
}, [navigate]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,18 @@
|
|||
import Helmet from 'react-helmet';
|
||||
|
||||
import { useEffect } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import Cookies from 'js-cookie';
|
||||
import { MemberOrgsDashboard } from '../controllers';
|
||||
|
||||
function MemberOrgs() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
if (!Cookies.get('token')) {
|
||||
navigate('/login');
|
||||
}
|
||||
}, [navigate]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
|
|
|
|||
|
|
@ -1,14 +1,26 @@
|
|||
import Helmet from 'react-helmet';
|
||||
|
||||
import { Dashboard } from '../controllers';
|
||||
import { useEffect } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import Cookies from 'js-cookie';
|
||||
|
||||
import { OrganizationDashboard } from '../controllers';
|
||||
|
||||
function OrgDashboard() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
if (!Cookies.get('token')) {
|
||||
navigate('/login');
|
||||
}
|
||||
}, [navigate]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>Dashboard | KnowItAll</title>
|
||||
</Helmet>
|
||||
<Dashboard />
|
||||
<OrganizationDashboard />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,20 @@
|
|||
import Helmet from 'react-helmet';
|
||||
|
||||
import { useEffect } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import Cookies from 'js-cookie';
|
||||
|
||||
import { OwnedOrgsDashboard } from '../controllers';
|
||||
|
||||
function OwnedOrgs() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
if (!Cookies.get('token')) {
|
||||
navigate('/login');
|
||||
}
|
||||
}, [navigate]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,20 @@
|
|||
import Helmet from 'react-helmet';
|
||||
|
||||
import { useEffect } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import Cookies from 'js-cookie';
|
||||
|
||||
import { UpdateOrganizationForm } from '../controllers';
|
||||
|
||||
function UpdateOrganization() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
if (!Cookies.get('token')) {
|
||||
navigate('/login');
|
||||
}
|
||||
}, [navigate]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createContext, useContext, useState } from 'react';
|
||||
|
||||
const ErrorContext = createContext();
|
||||
export const ErrorContext = createContext();
|
||||
|
||||
export default function ModeProvider({ children }) {
|
||||
const [error, setError] = useState('');
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { createContext, useContext, useState, useEffect } from 'react';
|
||||
import Cookies from 'js-cookie';
|
||||
|
||||
const ModeContext = createContext();
|
||||
export const ModeContext = createContext();
|
||||
|
||||
export default function ModeProvider({ children }) {
|
||||
const [mode, setMode] = useState(Cookies.get('theme') || 'light');
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { remember } from '../services/userServices';
|
|||
|
||||
const initialUserData = { loading: true };
|
||||
|
||||
const UserContext = createContext();
|
||||
export const UserContext = createContext();
|
||||
|
||||
export default function UserProvider({ children }) {
|
||||
const [userData, setUserData] = useState(initialUserData);
|
||||
|
|
@ -26,7 +26,7 @@ export default function UserProvider({ children }) {
|
|||
};
|
||||
|
||||
if (userData.loading) loadUserData();
|
||||
}, [userData.loading]);
|
||||
}, [userData.loading, navigate]);
|
||||
|
||||
return (
|
||||
<UserContext.Provider
|
||||
|
|
|
|||
|
|
@ -1,6 +1,17 @@
|
|||
import ModeProvider, { useMode } from './ModeContext';
|
||||
import UserProvider, { useUser } from './UserContext';
|
||||
import ErrorProvider, { useError } from './ErrorContext';
|
||||
import ModeProvider, { useMode, ModeContext } from './ModeContext';
|
||||
import UserProvider, { useUser, UserContext } from './UserContext';
|
||||
import ErrorProvider, { useError, ErrorContext } from './ErrorContext';
|
||||
import ContextProvider from './ContextProvider';
|
||||
|
||||
export { ModeProvider, useMode, UserProvider, useUser, ErrorProvider, useError, ContextProvider };
|
||||
export {
|
||||
ModeProvider,
|
||||
useMode,
|
||||
UserProvider,
|
||||
useUser,
|
||||
ErrorProvider,
|
||||
useError,
|
||||
ContextProvider,
|
||||
ErrorContext,
|
||||
ModeContext,
|
||||
UserContext,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -29,12 +29,14 @@ function EditUserFormController() {
|
|||
|
||||
async function handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
const { error } = await updateUserDetails(form);
|
||||
|
||||
if (error) {
|
||||
setError(() => error);
|
||||
} else {
|
||||
try {
|
||||
await updateUserDetails(form);
|
||||
navigate('/');
|
||||
} catch (error) {
|
||||
const message = error.response.data;
|
||||
|
||||
setError(() => message.error);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,12 +36,13 @@ function LoginFormController({ setToken }) {
|
|||
async function handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const { error } = await login(form);
|
||||
|
||||
if (error) {
|
||||
setError(() => error);
|
||||
} else {
|
||||
try {
|
||||
await login(form);
|
||||
navigate('/');
|
||||
} catch (error) {
|
||||
const message = error.response.data;
|
||||
|
||||
setError(() => message.error);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,29 +35,52 @@ function MainPageController() {
|
|||
}
|
||||
|
||||
async function retrieveOwnedOrgs() {
|
||||
const { orgs } = await getOwnedOrgs().catch((e) => console.log(e));
|
||||
try {
|
||||
const { orgs } = await getOwnedOrgs().catch((e) => console.error(e));
|
||||
setOwnedOrgs(() => orgs);
|
||||
} catch (error) {
|
||||
const message = error.response.data;
|
||||
|
||||
setOwnedOrgs(() => orgs);
|
||||
console.error(message.error);
|
||||
}
|
||||
}
|
||||
|
||||
async function retrieveMemberOrgs() {
|
||||
const { orgs } = await getMemberOrgs().catch((e) => console.log(e));
|
||||
try {
|
||||
const { orgs } = await getMemberOrgs().catch((e) => console.error(e));
|
||||
setOrgs(() => orgs);
|
||||
} catch (error) {
|
||||
const message = error.response.data;
|
||||
|
||||
setOrgs(() => orgs);
|
||||
console.error(message.error);
|
||||
}
|
||||
}
|
||||
|
||||
async function makeOrg(e) {
|
||||
e.preventDefault();
|
||||
|
||||
await createOrg({ orgName: form.name })
|
||||
.then(retrieveOwnedOrgs)
|
||||
.catch((e) => console.log(e));
|
||||
try {
|
||||
await createOrg({ orgName: form.name })
|
||||
.then(retrieveOwnedOrgs)
|
||||
.catch((e) => console.log(e));
|
||||
} catch (error) {
|
||||
const message = error.response.data;
|
||||
|
||||
console.error(message.error);
|
||||
}
|
||||
}
|
||||
|
||||
async function joinOrg(e) {
|
||||
e.preventDefault();
|
||||
|
||||
await addMember.then(retrieveMemberOrgs).catch((e) => console.log(e));
|
||||
try {
|
||||
await addMember(form.id);
|
||||
await retrieveMemberOrgs();
|
||||
} catch (error) {
|
||||
const message = error.response.data;
|
||||
|
||||
console.error(message.error);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import Cookies from 'js-cookie';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { OrganizationList } from '../components';
|
||||
import { getMemberOrgs } from '../services/userServices';
|
||||
|
|
@ -6,13 +7,21 @@ import { getMemberOrgs } from '../services/userServices';
|
|||
function MemberOrgsDashboard() {
|
||||
const [orgs, setOrgs] = useState([]);
|
||||
|
||||
async function getOrgs() {
|
||||
getMemberOrgs().then((res) => {
|
||||
setOrgs(() => res.orgs);
|
||||
});
|
||||
function getOrgs() {
|
||||
if (Cookies.get('token')) {
|
||||
getMemberOrgs()
|
||||
.then((res) => {
|
||||
setOrgs(() => res.orgs);
|
||||
})
|
||||
.catch((error) => {
|
||||
const message = error.response.data;
|
||||
|
||||
console.error(message.error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(getOrgs);
|
||||
useEffect(getOrgs, []);
|
||||
|
||||
return <OrganizationList orgs={orgs} />;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ export default function NavbarController() {
|
|||
const toggleOpen = () => setOpen((initial) => !initial);
|
||||
|
||||
const toggleMode = () => {
|
||||
console.log('Changed mode');
|
||||
setMode(() => (mode === 'light' ? 'dark' : 'light'));
|
||||
};
|
||||
|
||||
|
|
@ -25,7 +24,14 @@ export default function NavbarController() {
|
|||
const md = useMediaQuery((theme) => theme.breakpoints.only('md'));
|
||||
|
||||
const logoutUser = async () => {
|
||||
await logout();
|
||||
try {
|
||||
await logout();
|
||||
} catch (error) {
|
||||
const message = error.response.data;
|
||||
|
||||
console.error(message.error);
|
||||
}
|
||||
|
||||
Cookies.remove('token');
|
||||
setUserData({});
|
||||
navigate('/login');
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
|
||||
import { Dashboard } from '../components';
|
||||
import { OrganizationDashboard } from '../components';
|
||||
import { getAudio } from '../services/userServices';
|
||||
import { getOrg, deleteOrg, removeMember } from '../services/orgServices';
|
||||
|
||||
function DashboardController() {
|
||||
function OrganizationDashboardController() {
|
||||
const { orgID } = useParams();
|
||||
const navigate = useNavigate();
|
||||
|
||||
|
|
@ -23,7 +23,7 @@ function DashboardController() {
|
|||
});
|
||||
const [rows, setRows] = useState([]);
|
||||
const [status, setStatus] = useState(true);
|
||||
const [selection, setSelection] = useState(null);
|
||||
const [selection, setSelection] = useState([]);
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -38,7 +38,7 @@ function DashboardController() {
|
|||
setStatus(() => res.owner);
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log(e);
|
||||
console.error(e.response.data.error);
|
||||
navigate('/');
|
||||
});
|
||||
}
|
||||
|
|
@ -51,7 +51,12 @@ function DashboardController() {
|
|||
}
|
||||
|
||||
async function handleDelete() {
|
||||
await deleteOrg(orgID).then(navigate('/'));
|
||||
await deleteOrg(orgID)
|
||||
.then(navigate('/'))
|
||||
.catch((e) => {
|
||||
console.error(e.response.data.error);
|
||||
navigate('/');
|
||||
});
|
||||
}
|
||||
|
||||
function copyID() {
|
||||
|
|
@ -67,26 +72,40 @@ function DashboardController() {
|
|||
async function removeSelected() {
|
||||
const doomedUserIDs = selection.map((row) => row.id);
|
||||
|
||||
await removeMember(orgID, doomedUserIDs);
|
||||
try {
|
||||
await removeMember(orgID, doomedUserIDs);
|
||||
} catch (error) {
|
||||
const message = error.response.data;
|
||||
|
||||
console.error(message.error);
|
||||
}
|
||||
}
|
||||
|
||||
async function leaveOrg() {
|
||||
await removeMember(orgID, {}).then(navigate('/'));
|
||||
await removeMember(orgID, {})
|
||||
.then(navigate('/'))
|
||||
.catch((e) => {
|
||||
console.error(e.response.data.error);
|
||||
});
|
||||
}
|
||||
|
||||
async function play(id) {
|
||||
await getAudio(id).then((res) => {
|
||||
const uploadedFile = new File([res], 'userAudio.mp3', {
|
||||
type: res.type,
|
||||
lastModified: Date.now(),
|
||||
await getAudio(id)
|
||||
.then((res) => {
|
||||
const uploadedFile = new File([res], 'userAudio.mp3', {
|
||||
type: res.type,
|
||||
lastModified: Date.now(),
|
||||
});
|
||||
const audioFile = new Audio(URL.createObjectURL(uploadedFile));
|
||||
audioFile.play();
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e.response.data.error);
|
||||
});
|
||||
const audioFile = new Audio(URL.createObjectURL(uploadedFile));
|
||||
audioFile.play();
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<Dashboard
|
||||
<OrganizationDashboard
|
||||
org={org}
|
||||
rows={rows}
|
||||
open={open}
|
||||
|
|
@ -103,4 +122,4 @@ function DashboardController() {
|
|||
);
|
||||
}
|
||||
|
||||
export default DashboardController;
|
||||
export default OrganizationDashboardController;
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import Cookies from 'js-cookie';
|
||||
|
||||
import { OrganizationList } from '../components';
|
||||
import { getOwnedOrgs } from '../services/userServices';
|
||||
|
|
@ -6,13 +7,19 @@ import { getOwnedOrgs } from '../services/userServices';
|
|||
function OwnedOrgsDashboard() {
|
||||
const [orgs, setOrgs] = useState([]);
|
||||
|
||||
async function getOrgs() {
|
||||
getOwnedOrgs().then((res) => {
|
||||
setOrgs(() => res.orgs);
|
||||
});
|
||||
function getOrgs() {
|
||||
if (Cookies.get('token')) {
|
||||
getOwnedOrgs()
|
||||
.then((res) => {
|
||||
setOrgs(() => res.orgs);
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e.response.data.error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(getOrgs);
|
||||
useEffect(getOrgs, []);
|
||||
|
||||
return <OrganizationList orgs={orgs} />;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import axios from 'axios';
|
||||
import Cookies from 'js-cookie';
|
||||
import MicRecorder from 'mic-recorder-to-mp3';
|
||||
|
||||
|
|
@ -21,19 +20,31 @@ function RecordMp3Controller() {
|
|||
|
||||
useEffect(() => {
|
||||
if (!userData.loading) {
|
||||
getAudio(userData.id).then((data) => {
|
||||
setUploadedFile(() => {
|
||||
// Retrieve audio file of user if exists
|
||||
return new File([data], 'userAudio.mp3', {
|
||||
type: data.type,
|
||||
lastModified: Date.now(),
|
||||
getAudio(userData.id)
|
||||
.then((data) => {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
setUploadedFile(() => {
|
||||
// Retrieve audio file of user if exists
|
||||
return new File([data], 'userAudio.mp3', {
|
||||
type: data.type,
|
||||
lastModified: Date.now(),
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e.response.data.error);
|
||||
});
|
||||
});
|
||||
}
|
||||
}, [userData]);
|
||||
|
||||
function togglePlay() {
|
||||
if (!uploadedFile) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (audioRecording && !audioRecording.ended) {
|
||||
// Reset audio and reset if track is playing
|
||||
audioRecording.pause();
|
||||
|
|
@ -85,7 +96,9 @@ function RecordMp3Controller() {
|
|||
formData.append('audioFile', uploadedFile, 'userAudio.mp3');
|
||||
formData.append('token', Cookies.get('token'));
|
||||
|
||||
await setAudio(formData);
|
||||
setAudio(formData).catch((e) => {
|
||||
console.error(e.response.data.error);
|
||||
});
|
||||
}
|
||||
|
||||
return <RecordMp3 {...{ togglePlay, record, uploadRecord, handleSubmit }} />;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,14 @@ function RecoverFormController() {
|
|||
|
||||
async function handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
await requestReset(form);
|
||||
|
||||
try {
|
||||
await requestReset(form);
|
||||
} catch (error) {
|
||||
const message = error.response.data;
|
||||
|
||||
setError(message.error);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||
|
||||
import RegisterForm from '../components/RegisterForm';
|
||||
import { RegisterForm } from '../components';
|
||||
import { useError } from '../contexts';
|
||||
import { register } from '../services/userServices';
|
||||
|
||||
|
|
@ -32,12 +32,14 @@ function RegisterFormController() {
|
|||
e.preventDefault();
|
||||
|
||||
const { confirmPassword, ...filteredForm } = form;
|
||||
const { error } = await register({ ...filteredForm, orgID: params.get('orgID') });
|
||||
|
||||
if (error) {
|
||||
setError(() => error);
|
||||
} else {
|
||||
try {
|
||||
await register({ ...filteredForm, orgID: params.get('orgID') });
|
||||
navigate('/login');
|
||||
} catch (error) {
|
||||
const message = error.response.data;
|
||||
|
||||
setError(message.error);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,12 +27,13 @@ function ResetFormController() {
|
|||
async function handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const error = await resetPassword(id, { password: form.password });
|
||||
|
||||
if (error) {
|
||||
setError(() => error);
|
||||
} else {
|
||||
try {
|
||||
await resetPassword(id, { password: form.password });
|
||||
navigate('/login');
|
||||
} catch (error) {
|
||||
const message = error.response.data;
|
||||
|
||||
setError(message.error);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,9 +19,13 @@ function UpdateOrganizationFormController() {
|
|||
async function handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
await updateOrgDetails(orgID, { orgName: name }).then(() => {
|
||||
navigate(`/org/${orgID}`);
|
||||
});
|
||||
await updateOrgDetails(orgID, { orgName: name })
|
||||
.then(() => {
|
||||
navigate(`/org/${orgID}`);
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e.response.data.error);
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import RecoverForm from './RecoverFormController';
|
|||
import ResetForm from './ResetFormController';
|
||||
import OwnedOrgsDashboard from './OwnedOrgsDashboard';
|
||||
import MemberOrgsDashboard from './MemberOrgsDashboard';
|
||||
import Dashboard from './DashboardController';
|
||||
import OrganizationDashboard from './OrganizationDashboardController';
|
||||
import UpdateOrganizationForm from './UpdateOrganizationFormController';
|
||||
|
||||
export {
|
||||
|
|
@ -22,6 +22,6 @@ export {
|
|||
ResetForm,
|
||||
OwnedOrgsDashboard,
|
||||
MemberOrgsDashboard,
|
||||
Dashboard,
|
||||
OrganizationDashboard,
|
||||
UpdateOrganizationForm,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,74 +6,43 @@ async function addMember(id) {
|
|||
const instance = createRequest(`/org`);
|
||||
instance.defaults.headers.common['Authorization'] = `bearer ${Cookies.get('token')}`;
|
||||
|
||||
try {
|
||||
await instance.post(`/${id}/add`);
|
||||
} catch (error) {
|
||||
console.log(error.response.data);
|
||||
return error;
|
||||
}
|
||||
await instance.post(`/${id}/add`);
|
||||
}
|
||||
|
||||
async function createOrg(data) {
|
||||
const instance = createRequest('/org');
|
||||
instance.defaults.headers.common['Authorization'] = `bearer ${Cookies.get('token')}`;
|
||||
|
||||
try {
|
||||
await instance.post('/', data);
|
||||
} catch (error) {
|
||||
console.log(error.response.data);
|
||||
return error;
|
||||
}
|
||||
await instance.post('/', data);
|
||||
}
|
||||
|
||||
async function deleteOrg(id) {
|
||||
const instance = createRequest('/org');
|
||||
instance.defaults.headers.common['Authorization'] = `bearer ${Cookies.get('token')}`;
|
||||
|
||||
try {
|
||||
await instance.delete(`/${id}`);
|
||||
} catch (error) {
|
||||
console.log(error.response.data);
|
||||
return error;
|
||||
}
|
||||
await instance.delete(`/${id}`);
|
||||
}
|
||||
|
||||
async function getOrg(id) {
|
||||
const instance = createRequest('/org');
|
||||
instance.defaults.headers.common['Authorization'] = `bearer ${Cookies.get('token')}`;
|
||||
|
||||
try {
|
||||
const response = await instance.get(`/${id}`);
|
||||
console.log(response.data);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.log(error.response.data);
|
||||
return error;
|
||||
}
|
||||
const response = await instance.get(`/${id}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function removeMember(id, doomedUsers) {
|
||||
const instance = createRequest('/org');
|
||||
instance.defaults.headers.common['Authorization'] = `bearer ${Cookies.get('token')}`;
|
||||
|
||||
try {
|
||||
await instance.post(`/${id}/remove`, { doomedUsers });
|
||||
} catch (error) {
|
||||
console.log(error.response.data);
|
||||
return error;
|
||||
}
|
||||
await instance.post(`/${id}/remove`, { doomedUsers });
|
||||
}
|
||||
|
||||
async function updateOrgDetails(id, data) {
|
||||
const instance = createRequest('/org');
|
||||
instance.defaults.headers.common['Authorization'] = `bearer ${Cookies.get('token')}`;
|
||||
|
||||
try {
|
||||
await instance.patch(`/${id}`, { data });
|
||||
} catch (error) {
|
||||
console.log(error.response.data);
|
||||
return error;
|
||||
}
|
||||
await instance.patch(`/${id}`, { data });
|
||||
}
|
||||
|
||||
export { addMember, createOrg, deleteOrg, getOrg, removeMember, updateOrgDetails };
|
||||
|
|
|
|||
|
|
@ -10,43 +10,28 @@ async function login({ email, password, remember }) {
|
|||
const instance = createRequest('/auth');
|
||||
instance.defaults.headers.common['Authorization'] = `basic ${encoded}`;
|
||||
|
||||
try {
|
||||
const { data } = await instance.post('/login', { remember });
|
||||
const { data } = await instance.post('/login', { remember });
|
||||
|
||||
if (remember) {
|
||||
Cookies.set('token', data.token, { expires: 365 });
|
||||
} else {
|
||||
Cookies.set('token', data.token, { expires: 1 });
|
||||
}
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.log(error.response.data);
|
||||
return error;
|
||||
if (remember) {
|
||||
Cookies.set('token', data.token, { expires: 365 });
|
||||
} else {
|
||||
Cookies.set('token', data.token, { expires: 1 });
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
async function register(data) {
|
||||
const instance = createRequest('/auth');
|
||||
|
||||
try {
|
||||
await instance.post('/register', data);
|
||||
} catch (error) {
|
||||
console.log(error.response.data);
|
||||
return error;
|
||||
}
|
||||
await instance.post('/register', data);
|
||||
}
|
||||
|
||||
async function logout() {
|
||||
const instance = createRequest('/auth');
|
||||
instance.defaults.headers.common['Authorization'] = `bearer ${Cookies.get('token')}`;
|
||||
|
||||
try {
|
||||
await instance.post('/logout');
|
||||
} catch (error) {
|
||||
console.log(error.response.data);
|
||||
return error;
|
||||
}
|
||||
await instance.post('/logout');
|
||||
}
|
||||
|
||||
async function remember() {
|
||||
|
|
@ -54,50 +39,29 @@ async function remember() {
|
|||
instance.defaults.headers.common['Authorization'] = `bearer ${Cookies.get('token')}`;
|
||||
|
||||
if (Cookies.get('token')) {
|
||||
try {
|
||||
const { data } = await instance.post('/remember');
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.log(error.response.data);
|
||||
return error.response.data;
|
||||
}
|
||||
const { data } = await instance.post('/remember');
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
async function getUser(userID) {
|
||||
const instance = createRequest('/auth');
|
||||
|
||||
try {
|
||||
const { data } = await instance.get(`/${userID}`);
|
||||
const { data } = await instance.get(`/${userID}`);
|
||||
|
||||
return data.user;
|
||||
} catch (error) {
|
||||
console.log(error.response.data);
|
||||
return error.response.data;
|
||||
}
|
||||
return data.user;
|
||||
}
|
||||
|
||||
async function requestReset(data) {
|
||||
const instance = createRequest('/auth');
|
||||
|
||||
try {
|
||||
await instance.post(`/reset`, data);
|
||||
} catch (error) {
|
||||
console.log(error.response.data);
|
||||
return error.response.data;
|
||||
}
|
||||
await instance.post(`/reset`, data);
|
||||
}
|
||||
|
||||
async function resetPassword(id, data) {
|
||||
const instance = createRequest('/auth');
|
||||
|
||||
try {
|
||||
await instance.patch(`/reset/${id}`, data);
|
||||
} catch (error) {
|
||||
console.log(error.response.data);
|
||||
return error.response.data;
|
||||
}
|
||||
await instance.patch(`/reset/${id}`, data);
|
||||
}
|
||||
|
||||
async function setAudio(data) {
|
||||
|
|
@ -105,12 +69,7 @@ async function setAudio(data) {
|
|||
instance.defaults.headers.common['Content-Type'] = 'multipart/form-data';
|
||||
instance.defaults.headers.common['Authorization'] = `bearer ${Cookies.get('token')}`;
|
||||
|
||||
try {
|
||||
await instance.post('/audio', data);
|
||||
} catch (error) {
|
||||
console.log(error.response.data);
|
||||
return error.response.data;
|
||||
}
|
||||
await instance.post('/audio', data);
|
||||
}
|
||||
|
||||
async function getAudio(userID) {
|
||||
|
|
@ -128,40 +87,25 @@ async function updateUserDetails(data) {
|
|||
const instance = createRequest('/auth');
|
||||
instance.defaults.headers.common['Authorization'] = `bearer ${Cookies.get('token')}`;
|
||||
|
||||
try {
|
||||
await instance.post(`/update`, data);
|
||||
} catch (error) {
|
||||
console.log(error.response.data);
|
||||
return error.response.data;
|
||||
}
|
||||
await instance.post(`/update`, data);
|
||||
}
|
||||
|
||||
async function getMemberOrgs() {
|
||||
const instance = createRequest('/auth');
|
||||
instance.defaults.headers.common['Authorization'] = `bearer ${Cookies.get('token')}`;
|
||||
|
||||
try {
|
||||
const { data } = await instance.get(`/orgs/member`);
|
||||
const { data } = await instance.get(`/orgs/member`);
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.log(error.response.data);
|
||||
return error.response.data;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
async function getOwnedOrgs() {
|
||||
const instance = createRequest('/auth');
|
||||
instance.defaults.headers.common['Authorization'] = `bearer ${Cookies.get('token')}`;
|
||||
|
||||
try {
|
||||
const { data } = await instance.get(`/orgs`);
|
||||
const { data } = await instance.get(`/orgs`);
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.log(error.response.data);
|
||||
return error.response.data;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
export {
|
||||
|
|
|
|||
55
client/src/stories/EditUserForm.stories.jsx
Normal file
55
client/src/stories/EditUserForm.stories.jsx
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import { rest } from 'msw';
|
||||
|
||||
import { ErrorProvider, UserContext } from '../contexts';
|
||||
import { EditUserForm } from '../controllers';
|
||||
|
||||
export default {
|
||||
title: 'Forms/EditUser',
|
||||
component: EditUserForm,
|
||||
};
|
||||
|
||||
const Template = (args) => (
|
||||
<UserContext.Provider
|
||||
value={{
|
||||
userData: args,
|
||||
}}
|
||||
>
|
||||
<ErrorProvider>
|
||||
<EditUserForm />
|
||||
</ErrorProvider>
|
||||
</UserContext.Provider>
|
||||
);
|
||||
|
||||
export const WithIntialData = Template.bind({});
|
||||
WithIntialData.args = {
|
||||
firstName: 'Test',
|
||||
lastName: 'User',
|
||||
email: 'test.user@test.com',
|
||||
pronouns: 'they/them',
|
||||
};
|
||||
WithIntialData.parameters = {
|
||||
msw: {
|
||||
handlers: [
|
||||
rest.post(`${process.env.REACT_APP_API_ROOT}/auth/update`, (req, res, ctx) => {
|
||||
return res(ctx.status(200));
|
||||
}),
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const WithoutInitialData = Template.bind({});
|
||||
WithoutInitialData.args = {
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
email: '',
|
||||
pronouns: '',
|
||||
};
|
||||
WithoutInitialData.parameters = {
|
||||
msw: {
|
||||
handlers: [
|
||||
rest.post(`${process.env.REACT_APP_API_ROOT}/auth/update`, (req, res, ctx) => {
|
||||
return res(ctx.status(200));
|
||||
}),
|
||||
],
|
||||
},
|
||||
};
|
||||
14
client/src/stories/Form.stories.jsx
Normal file
14
client/src/stories/Form.stories.jsx
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import { Form } from '../components';
|
||||
|
||||
export default {
|
||||
title: 'Layout/Form',
|
||||
component: Form,
|
||||
args: {
|
||||
text: 'Test Form',
|
||||
error: '',
|
||||
},
|
||||
};
|
||||
|
||||
const Template = (args) => <Form {...args} />;
|
||||
|
||||
export const Primary = Template.bind({});
|
||||
26
client/src/stories/LoginForm.stories.jsx
Normal file
26
client/src/stories/LoginForm.stories.jsx
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import { rest } from 'msw';
|
||||
|
||||
import { LoginForm } from '../controllers';
|
||||
import { ErrorProvider } from '../contexts';
|
||||
|
||||
export default {
|
||||
title: 'Forms/Login',
|
||||
component: LoginForm,
|
||||
};
|
||||
|
||||
const Template = (args) => (
|
||||
<ErrorProvider>
|
||||
<LoginForm {...args} />
|
||||
</ErrorProvider>
|
||||
);
|
||||
|
||||
export const Primary = Template.bind({});
|
||||
Primary.parameters = {
|
||||
msw: {
|
||||
handlers: [
|
||||
rest.post(`${process.env.REACT_APP_API_ROOT}/auth/login`, (req, res, ctx) => {
|
||||
return res(ctx.status(200));
|
||||
}),
|
||||
],
|
||||
},
|
||||
};
|
||||
64
client/src/stories/MainPage.stories.jsx
Normal file
64
client/src/stories/MainPage.stories.jsx
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
import { rest } from 'msw';
|
||||
|
||||
import { MainPageDisplay } from '../controllers';
|
||||
import { ErrorProvider } from '../contexts';
|
||||
|
||||
export default {
|
||||
title: 'Pages/Main Page',
|
||||
component: MainPageDisplay,
|
||||
parameters: {
|
||||
msw: {
|
||||
handlers: [
|
||||
rest.get(`${process.env.REACT_APP_API_ROOT}/auth/orgs`, (req, res, ctx) => {
|
||||
return res(
|
||||
ctx.json({
|
||||
orgs: [
|
||||
{
|
||||
id: 'fcfce355-7877-4a7f-a762-33ead250daa2',
|
||||
name: 'Test Org Owned',
|
||||
memberCount: 42,
|
||||
createdAt: 1672202132563,
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
}),
|
||||
rest.get(`${process.env.REACT_APP_API_ROOT}/auth/orgs/member`, (req, res, ctx) => {
|
||||
return res(
|
||||
ctx.json({
|
||||
orgs: [
|
||||
{
|
||||
id: 'fcfce355-7877-4a7f-a762-33ead250daa2',
|
||||
name: 'Test Org Member',
|
||||
memberCount: 42,
|
||||
createdAt: 1672202132563,
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
}),
|
||||
rest.post(`${process.env.REACT_APP_API_ROOT}/org/*/add`, (req, res, ctx) => {
|
||||
return res(ctx.status(200));
|
||||
}),
|
||||
rest.post(`${process.env.REACT_APP_API_ROOT}/org`, (req, res, ctx) => {
|
||||
return res(ctx.status(200));
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const Template = (args) => (
|
||||
<ErrorProvider>
|
||||
<MainPageDisplay {...args} />
|
||||
</ErrorProvider>
|
||||
);
|
||||
|
||||
export const WithOrganizations = Template.bind({});
|
||||
WithOrganizations.parameters = {
|
||||
cookie: {
|
||||
token: 'testtoken',
|
||||
},
|
||||
};
|
||||
|
||||
export const WithoutOrganizations = Template.bind({});
|
||||
44
client/src/stories/MemberOrgsDashbaord.stories.jsx
Normal file
44
client/src/stories/MemberOrgsDashbaord.stories.jsx
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import { rest } from 'msw';
|
||||
|
||||
import { MemberOrgsDashboard } from '../controllers';
|
||||
import { ErrorProvider } from '../contexts';
|
||||
|
||||
export default {
|
||||
title: 'Pages/Member Dashboard',
|
||||
component: MemberOrgsDashboard,
|
||||
parameters: {
|
||||
msw: {
|
||||
handlers: [
|
||||
rest.get(`${process.env.REACT_APP_API_ROOT}/auth/orgs/member`, (req, res, ctx) => {
|
||||
return res(
|
||||
ctx.json({
|
||||
orgs: [
|
||||
{
|
||||
id: 'fcfce355-7877-4a7f-a762-33ead250daa2',
|
||||
name: 'Test Org Member',
|
||||
memberCount: 42,
|
||||
createdAt: 1672202132563,
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const Template = (args) => (
|
||||
<ErrorProvider>
|
||||
<MemberOrgsDashboard {...args} />
|
||||
</ErrorProvider>
|
||||
);
|
||||
|
||||
export const WithOrganizations = Template.bind({});
|
||||
WithOrganizations.parameters = {
|
||||
cookie: {
|
||||
token: 'testToken',
|
||||
},
|
||||
};
|
||||
|
||||
export const WithoutOrganizations = Template.bind({});
|
||||
24
client/src/stories/MemberUnit.stories.jsx
Normal file
24
client/src/stories/MemberUnit.stories.jsx
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import { MemberUnit } from '../components';
|
||||
|
||||
export default {
|
||||
title: 'Components/MemberUnit',
|
||||
component: MemberUnit,
|
||||
argTypes: {
|
||||
onClick: { action: 'playAudio' },
|
||||
},
|
||||
};
|
||||
|
||||
const Template = (args) => {
|
||||
const { onClick, ...member } = args;
|
||||
return <MemberUnit member={member} onClick={onClick} />;
|
||||
};
|
||||
|
||||
export const Primary = Template.bind({});
|
||||
Primary.args = {
|
||||
id: 'fcfce355-7877-4a7f-a762-33ead250daa2',
|
||||
firstName: 'Test',
|
||||
lastName: 'User',
|
||||
nickname: 'TU',
|
||||
pronouns: 'they/them',
|
||||
email: 'test.user@know-it-all.com',
|
||||
};
|
||||
38
client/src/stories/Navbar.stories.jsx
Normal file
38
client/src/stories/Navbar.stories.jsx
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import { rest } from 'msw';
|
||||
|
||||
import { ModeProvider, UserContext } from '../contexts';
|
||||
import { Navbar } from '../controllers';
|
||||
|
||||
export default {
|
||||
title: 'Layout/Navbar',
|
||||
component: Navbar,
|
||||
parameters: {
|
||||
msw: {
|
||||
handlers: [
|
||||
rest.post(`${process.env.REACT_APP_API_ROOT}/auth/logout`, (req, res, ctx) => {
|
||||
return res(ctx.json({}));
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const Template = (args) => (
|
||||
<UserContext.Provider
|
||||
value={{
|
||||
setUserData: () => {},
|
||||
userData: args,
|
||||
}}
|
||||
>
|
||||
<ModeProvider>
|
||||
<Navbar />
|
||||
</ModeProvider>
|
||||
</UserContext.Provider>
|
||||
);
|
||||
|
||||
export const LoggedIn = Template.bind({});
|
||||
LoggedIn.args = {
|
||||
email: 'test.user@test.com',
|
||||
};
|
||||
|
||||
export const LoggedOut = Template.bind({});
|
||||
16
client/src/stories/OrgUnit.stories.jsx
Normal file
16
client/src/stories/OrgUnit.stories.jsx
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import { OrgUnit } from '../components';
|
||||
|
||||
export default {
|
||||
title: 'Components/OrgUnit',
|
||||
component: OrgUnit,
|
||||
};
|
||||
|
||||
const Template = (args) => <OrgUnit org={args} />;
|
||||
|
||||
export const Primary = Template.bind({});
|
||||
Primary.args = {
|
||||
id: 'fcfce355-7877-4a7f-a762-33ead250daa2',
|
||||
name: 'Test Org',
|
||||
memberCount: 42,
|
||||
createdAt: 1672202132563,
|
||||
};
|
||||
84
client/src/stories/OrganizationDashboard.stories.jsx
Normal file
84
client/src/stories/OrganizationDashboard.stories.jsx
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
import { rest } from 'msw';
|
||||
|
||||
import { OrganizationDashboard } from '../controllers';
|
||||
import { ErrorProvider } from '../contexts';
|
||||
|
||||
export default {
|
||||
title: 'Pages/Organization Dashboard',
|
||||
component: OrganizationDashboard,
|
||||
};
|
||||
|
||||
const Template = (args) => (
|
||||
<ErrorProvider>
|
||||
<OrganizationDashboard {...args} />
|
||||
</ErrorProvider>
|
||||
);
|
||||
|
||||
export const Owner = Template.bind({});
|
||||
Owner.parameters = {
|
||||
cookie: {
|
||||
token: 'testToken',
|
||||
},
|
||||
msw: {
|
||||
handlers: [
|
||||
rest.get(`${process.env.REACT_APP_API_ROOT}/org/*`, (req, res, ctx) => {
|
||||
return res(
|
||||
ctx.json({
|
||||
org: {
|
||||
id: 'testID',
|
||||
name: 'Test Org',
|
||||
owner: {
|
||||
firstName: 'Test',
|
||||
lastName: 'User',
|
||||
email: 'test.user@test.com',
|
||||
},
|
||||
member: [],
|
||||
createdAt: 1672202132563,
|
||||
},
|
||||
memberCount: 0,
|
||||
owner: true,
|
||||
})
|
||||
);
|
||||
}),
|
||||
rest.delete(`${process.env.REACT_APP_API_ROOT}/org/*`, (req, res, ctx) => {
|
||||
return res(ctx.status(200));
|
||||
}),
|
||||
rest.post(`${process.env.REACT_APP_API_ROOT}/org/*/remove`, (req, res, ctx) => {
|
||||
return res(ctx.status(200));
|
||||
}),
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const NotOwner = Template.bind({});
|
||||
NotOwner.parameters = {
|
||||
cookie: {
|
||||
token: 'testToken',
|
||||
},
|
||||
msw: {
|
||||
handlers: [
|
||||
rest.get(`${process.env.REACT_APP_API_ROOT}/org/*`, (req, res, ctx) => {
|
||||
return res(
|
||||
ctx.json({
|
||||
org: {
|
||||
id: 'testID',
|
||||
name: 'Test Org',
|
||||
owner: {
|
||||
firstName: 'Test',
|
||||
lastName: 'User',
|
||||
email: 'test.user@test.com',
|
||||
},
|
||||
member: [],
|
||||
createdAt: 1672202132563,
|
||||
},
|
||||
memberCount: 0,
|
||||
owner: false,
|
||||
})
|
||||
);
|
||||
}),
|
||||
rest.post(`${process.env.REACT_APP_API_ROOT}/org/*/remove`, (req, res, ctx) => {
|
||||
return res(ctx.status(200));
|
||||
}),
|
||||
],
|
||||
},
|
||||
};
|
||||
44
client/src/stories/OwnedOrgsDashboard.stories.jsx
Normal file
44
client/src/stories/OwnedOrgsDashboard.stories.jsx
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import { rest } from 'msw';
|
||||
|
||||
import { OwnedOrgsDashboard } from '../controllers';
|
||||
import { ErrorProvider } from '../contexts';
|
||||
|
||||
export default {
|
||||
title: 'Pages/Owned Dashboard',
|
||||
component: OwnedOrgsDashboard,
|
||||
parameters: {
|
||||
msw: {
|
||||
handlers: [
|
||||
rest.get(`${process.env.REACT_APP_API_ROOT}/auth/orgs`, (req, res, ctx) => {
|
||||
return res(
|
||||
ctx.json({
|
||||
orgs: [
|
||||
{
|
||||
id: 'fcfce355-7877-4a7f-a762-33ead250daa2',
|
||||
name: 'Test Org Member',
|
||||
memberCount: 42,
|
||||
createdAt: 1672202132563,
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const Template = (args) => (
|
||||
<ErrorProvider>
|
||||
<OwnedOrgsDashboard {...args} />
|
||||
</ErrorProvider>
|
||||
);
|
||||
|
||||
export const WithOrganizations = Template.bind({});
|
||||
WithOrganizations.parameters = {
|
||||
cookie: {
|
||||
token: 'testToken',
|
||||
},
|
||||
};
|
||||
|
||||
export const WithoutOrganizations = Template.bind({});
|
||||
40
client/src/stories/RecordMp3.stories.jsx
Normal file
40
client/src/stories/RecordMp3.stories.jsx
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import { rest } from 'msw';
|
||||
|
||||
import { RecordMp3 } from '../controllers';
|
||||
import { UserContext } from '../contexts';
|
||||
|
||||
export default {
|
||||
title: 'Forms/RecordMp3',
|
||||
component: RecordMp3,
|
||||
};
|
||||
|
||||
const Template = (args) => (
|
||||
<UserContext.Provider
|
||||
value={{
|
||||
setUserData: () => {},
|
||||
userData: args,
|
||||
}}
|
||||
>
|
||||
<RecordMp3 />
|
||||
</UserContext.Provider>
|
||||
);
|
||||
|
||||
export const Primary = Template.bind({});
|
||||
Primary.args = {
|
||||
id: 'test-id',
|
||||
};
|
||||
Primary.parameters = {
|
||||
msw: {
|
||||
handlers: [
|
||||
rest.get(
|
||||
`${process.env.REACT_APP_DOMAIN_ROOT}/public/audio/test-id.mp3`,
|
||||
(req, res, ctx) => {
|
||||
return res(ctx.status(404));
|
||||
}
|
||||
),
|
||||
rest.post(`${process.env.REACT_APP_API_ROOT}/auth/audio`, (req, res, ctx) => {
|
||||
return res(ctx.status(200));
|
||||
}),
|
||||
],
|
||||
},
|
||||
};
|
||||
26
client/src/stories/RecoverForm.stories.jsx
Normal file
26
client/src/stories/RecoverForm.stories.jsx
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import { rest } from 'msw';
|
||||
|
||||
import { RecoverForm } from '../controllers';
|
||||
import { ErrorProvider } from '../contexts';
|
||||
|
||||
export default {
|
||||
title: 'Forms/Recover Password',
|
||||
component: RecoverForm,
|
||||
};
|
||||
|
||||
const Template = (args) => (
|
||||
<ErrorProvider>
|
||||
<RecoverForm {...args} />
|
||||
</ErrorProvider>
|
||||
);
|
||||
|
||||
export const Primary = Template.bind({});
|
||||
Primary.parameters = {
|
||||
msw: {
|
||||
handlers: [
|
||||
rest.post(`${process.env.REACT_APP_API_ROOT}/auth/reset/undefined`, (req, res, ctx) => {
|
||||
return res(ctx.status(200));
|
||||
}),
|
||||
],
|
||||
},
|
||||
};
|
||||
26
client/src/stories/RegisterForm.stories.jsx
Normal file
26
client/src/stories/RegisterForm.stories.jsx
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import { rest } from 'msw';
|
||||
|
||||
import { RegisterForm } from '../controllers';
|
||||
import { ErrorProvider } from '../contexts';
|
||||
|
||||
export default {
|
||||
title: 'Forms/RegisterForm',
|
||||
component: RegisterForm,
|
||||
};
|
||||
|
||||
const Template = (args) => (
|
||||
<ErrorProvider>
|
||||
<RegisterForm {...args} />
|
||||
</ErrorProvider>
|
||||
);
|
||||
|
||||
export const Primary = Template.bind({});
|
||||
Primary.parameters = {
|
||||
msw: {
|
||||
handlers: [
|
||||
rest.post(`${process.env.REACT_APP_API_ROOT}/auth/register`, (req, res, ctx) => {
|
||||
return res(ctx.status(200));
|
||||
}),
|
||||
],
|
||||
},
|
||||
};
|
||||
29
client/src/stories/ResetForm.stories.jsx
Normal file
29
client/src/stories/ResetForm.stories.jsx
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import { rest } from 'msw';
|
||||
|
||||
import { ResetForm } from '../controllers';
|
||||
import { ErrorProvider } from '../contexts';
|
||||
|
||||
export default {
|
||||
title: 'Forms/Reset Password',
|
||||
component: ResetForm,
|
||||
};
|
||||
|
||||
const Template = (args) => (
|
||||
<ErrorProvider>
|
||||
<ResetForm {...args} />
|
||||
</ErrorProvider>
|
||||
);
|
||||
|
||||
export const Primary = Template.bind({});
|
||||
Primary.parameters = {
|
||||
msw: {
|
||||
handlers: [
|
||||
rest.patch(
|
||||
`${process.env.REACT_APP_API_ROOT}/auth/reset/undefined`,
|
||||
(req, res, ctx) => {
|
||||
return res(ctx.status(200));
|
||||
}
|
||||
),
|
||||
],
|
||||
},
|
||||
};
|
||||
31
client/src/stories/StackItem.stories.jsx
Normal file
31
client/src/stories/StackItem.stories.jsx
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import { Stack, Box } from '@mui/material';
|
||||
|
||||
import { StackItem } from '../components';
|
||||
import { ErrorProvider } from '../contexts';
|
||||
|
||||
export default {
|
||||
title: 'Components/Stack Item',
|
||||
component: StackItem,
|
||||
args: {
|
||||
text: 'Default Text',
|
||||
},
|
||||
};
|
||||
|
||||
export const Single = (args) => (
|
||||
<ErrorProvider>
|
||||
<Stack spacing={2} sx={{ padding: 2 }}>
|
||||
<StackItem {...args} />
|
||||
</Stack>
|
||||
</ErrorProvider>
|
||||
);
|
||||
|
||||
export const Multiple = (args) => (
|
||||
<ErrorProvider>
|
||||
<Stack spacing={2} sx={{ padding: 2 }}>
|
||||
<StackItem {...args} />
|
||||
<StackItem {...args} />
|
||||
<StackItem {...args} />
|
||||
<StackItem {...args} />
|
||||
</Stack>
|
||||
</ErrorProvider>
|
||||
);
|
||||
26
client/src/stories/UpdateOrganizationForm.stories.jsx
Normal file
26
client/src/stories/UpdateOrganizationForm.stories.jsx
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import { rest } from 'msw';
|
||||
|
||||
import { UpdateOrganizationForm } from '../controllers';
|
||||
import { ErrorProvider } from '../contexts';
|
||||
|
||||
export default {
|
||||
title: 'Forms/Update Organization',
|
||||
component: UpdateOrganizationForm,
|
||||
};
|
||||
|
||||
const Template = (args) => (
|
||||
<ErrorProvider>
|
||||
<UpdateOrganizationForm {...args} />
|
||||
</ErrorProvider>
|
||||
);
|
||||
|
||||
export const Primary = Template.bind({});
|
||||
Primary.parameters = {
|
||||
msw: {
|
||||
handlers: [
|
||||
rest.patch(`${process.env.REACT_APP_API_ROOT}/org/undefined`, (req, res, ctx) => {
|
||||
return res(ctx.status(200));
|
||||
}),
|
||||
],
|
||||
},
|
||||
};
|
||||
16
client/src/themes/dark.js
Normal file
16
client/src/themes/dark.js
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import { createTheme } from '@mui/material/styles';
|
||||
|
||||
const darkTheme = createTheme({
|
||||
palette: {
|
||||
mode: 'dark',
|
||||
},
|
||||
breakpoints: {
|
||||
values: {
|
||||
xs: 0,
|
||||
sm: 768,
|
||||
md: 1024,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export { darkTheme };
|
||||
4
client/src/themes/index.js
Normal file
4
client/src/themes/index.js
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
import { darkTheme } from './dark';
|
||||
import { lightTheme } from './light';
|
||||
|
||||
export { darkTheme, lightTheme };
|
||||
16
client/src/themes/light.js
Normal file
16
client/src/themes/light.js
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import { createTheme } from '@mui/material/styles';
|
||||
|
||||
const lightTheme = createTheme({
|
||||
palette: {
|
||||
mode: 'light',
|
||||
},
|
||||
breakpoints: {
|
||||
values: {
|
||||
xs: 0,
|
||||
sm: 768,
|
||||
md: 1024,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export { lightTheme };
|
||||
31636
package-lock.json
generated
31636
package-lock.json
generated
File diff suppressed because it is too large
Load diff
20
package.json
20
package.json
|
|
@ -5,15 +5,20 @@
|
|||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "concurrently \"npm run start -w client\" \"npm run start -w server\"",
|
||||
"start:server": "npm start server",
|
||||
"start:client": "npm start -w client",
|
||||
"start:server": "npm start -w server",
|
||||
"build": "npm run build -w client",
|
||||
"build:package": "tar --exclude='.env' -czf dist.tar.gz client node_modules server LICENSE package-lock.json package.json",
|
||||
"build:package": "tar -czf dist.tar.gz client node_modules server LICENSE package-lock.json package.json",
|
||||
"dist": "npm run build:package",
|
||||
"test": "npm run test -w server",
|
||||
"migrate": "npm run migrate -w server",
|
||||
"migrate:undo": "npm run migrate:undo -w server",
|
||||
"predev": "npm run build",
|
||||
"predist": "npm ci && npm run build"
|
||||
"predist": "npm ci && npm run build",
|
||||
"eslint": "npx eslint",
|
||||
"eslint:fix": "npx eslint --fix",
|
||||
"eslint:all": "npx eslint '**'",
|
||||
"eslint:all:fix": "npx eslint '**' --fix",
|
||||
"storybook": "npm run storybook -w client"
|
||||
},
|
||||
"author": "C4 Patino",
|
||||
"license": "ISC",
|
||||
|
|
@ -29,6 +34,13 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"concurrently": "^7.6.0",
|
||||
"eslint": "^8.30.0",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-jest": "^27.1.7",
|
||||
"eslint-plugin-jsx-a11y": "^6.6.1",
|
||||
"eslint-plugin-react": "^7.31.11",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"sequelize-cli": "^6.4.1"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,32 +1,30 @@
|
|||
var createError = require('http-errors');
|
||||
var express = require('express');
|
||||
var path = require('path');
|
||||
var cookieParser = require('cookie-parser');
|
||||
var logger = require('morgan');
|
||||
var bodyParser = require('body-parser');
|
||||
var cors = require('cors');
|
||||
const createError = require('http-errors');
|
||||
const express = require('express');
|
||||
const path = require('path');
|
||||
const cookieParser = require('cookie-parser');
|
||||
const morgan = require('morgan');
|
||||
const bodyParser = require('body-parser');
|
||||
const cors = require('cors');
|
||||
|
||||
var authRouter = require('./routes/authRouter');
|
||||
var orgRouter = require('./routes/orgRouter');
|
||||
const authRouter = require('./routes/authRouter');
|
||||
const orgRouter = require('./routes/orgRouter');
|
||||
|
||||
var app = express();
|
||||
const app = express();
|
||||
|
||||
// view engine setup
|
||||
app.set('views', path.join(__dirname, 'views'));
|
||||
app.set('view engine', 'jade');
|
||||
|
||||
if (process.env.NODE_ENV != 'test') {
|
||||
app.use(logger('dev'));
|
||||
if (process.env.NODE_ENV !== 'test') {
|
||||
app.use(morgan('dev'));
|
||||
}
|
||||
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: false }));
|
||||
app.use(cookieParser());
|
||||
app.use(cors());
|
||||
app.use(
|
||||
bodyParser.urlencoded({
|
||||
extended: true,
|
||||
})
|
||||
);
|
||||
app.use(bodyParser.urlencoded({ extended: true }));
|
||||
|
||||
app.use(bodyParser.json());
|
||||
|
||||
app.use('/public', express.static(path.join(__dirname, 'public')));
|
||||
|
|
@ -40,7 +38,7 @@ app.get('*', (req, res) => {
|
|||
});
|
||||
|
||||
// catch 404 and forward to error handler
|
||||
app.use(function (req, res, next) {
|
||||
app.use((req, res, next) => {
|
||||
next(createError(404));
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,56 +1,17 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
var debug = require('debug')('server:server');
|
||||
var http = require('http');
|
||||
const http = require('http');
|
||||
require('dotenv').config();
|
||||
|
||||
var app = require('../app');
|
||||
var { sequelize } = require('../db/models/index');
|
||||
const app = require('../app');
|
||||
const logger = require('../utils/logger');
|
||||
|
||||
/**
|
||||
* Get port from environment and store in Express.
|
||||
*/
|
||||
|
||||
var port = normalizePort(process.env.PORT || '3000');
|
||||
app.set('port', port);
|
||||
|
||||
/**
|
||||
* Create HTTP server.
|
||||
*/
|
||||
|
||||
var server = http.createServer(app);
|
||||
|
||||
async function connectDatabase() {
|
||||
try {
|
||||
await sequelize.authenticate();
|
||||
console.log(
|
||||
`[INFO]: Database connection to '${process.env.NODE_ENV}' has been established successfully.`
|
||||
);
|
||||
} catch (error) {
|
||||
console.log('[ERROR]: Unable to connect to the database;', error);
|
||||
}
|
||||
}
|
||||
connectDatabase().then(() => {
|
||||
/**
|
||||
* Listen on provided port, on all network interfaces.
|
||||
*/
|
||||
|
||||
server.listen(port);
|
||||
server.on('error', onError);
|
||||
server.on('listening', onListening);
|
||||
});
|
||||
|
||||
/**
|
||||
* Normalize a port into a number, string, or false.
|
||||
*/
|
||||
const { sequelize } = require('../db/models/index');
|
||||
|
||||
function normalizePort(val) {
|
||||
var port = parseInt(val, 10);
|
||||
const port = parseInt(val, 10);
|
||||
|
||||
if (isNaN(port)) {
|
||||
if (Number.isNaN(port)) {
|
||||
// named pipe
|
||||
return val;
|
||||
}
|
||||
|
|
@ -63,6 +24,19 @@ function normalizePort(val) {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get port from environment and store in Express.
|
||||
*/
|
||||
|
||||
const port = normalizePort(process.env.PORT || '3000');
|
||||
app.set('port', port);
|
||||
|
||||
/**
|
||||
* Create HTTP server.
|
||||
*/
|
||||
|
||||
const server = http.createServer(app);
|
||||
|
||||
/**
|
||||
* Event listener for HTTP server "error" event.
|
||||
*/
|
||||
|
|
@ -72,16 +46,16 @@ function onError(error) {
|
|||
throw error;
|
||||
}
|
||||
|
||||
var bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port;
|
||||
const bind = typeof port === 'string' ? `Pipe ${port}` : `Port ${port}`;
|
||||
|
||||
// handle specific listen errors with friendly messages
|
||||
switch (error.code) {
|
||||
case 'EACCES':
|
||||
console.error(bind + ' requires elevated privileges');
|
||||
logger.error(`${bind} requires elevated privileges`);
|
||||
process.exit(1);
|
||||
break;
|
||||
case 'EADDRINUSE':
|
||||
console.error(bind + ' is already in use');
|
||||
logger.error(`${bind} is already in use`);
|
||||
process.exit(1);
|
||||
break;
|
||||
default:
|
||||
|
|
@ -94,7 +68,28 @@ function onError(error) {
|
|||
*/
|
||||
|
||||
async function onListening() {
|
||||
var addr = server.address();
|
||||
var bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port;
|
||||
debug('Listening on ' + bind);
|
||||
const addr = server.address();
|
||||
const bind = typeof addr === 'string' ? `pipe ${addr}` : `port ${addr.port}`;
|
||||
logger.info(`Listening on ${bind}`);
|
||||
}
|
||||
|
||||
async function connectDatabase() {
|
||||
try {
|
||||
await sequelize.authenticate();
|
||||
logger.info(
|
||||
`Database connection to '${process.env.NODE_ENV}' has been established successfully.`
|
||||
);
|
||||
} catch (error) {
|
||||
logger.error('Unable to connect to the database;', error);
|
||||
}
|
||||
}
|
||||
|
||||
connectDatabase().then(() => {
|
||||
/**
|
||||
* Listen on provided port, on all network interfaces.
|
||||
*/
|
||||
|
||||
server.listen(port);
|
||||
server.on('error', onError);
|
||||
server.on('listening', onListening);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
async up(queryInterface, Sequelize) {
|
||||
await queryInterface.createTable('organization', {
|
||||
|
|
@ -20,7 +18,7 @@ module.exports = {
|
|||
});
|
||||
},
|
||||
|
||||
async down(queryInterface, Sequelize) {
|
||||
async down(queryInterface) {
|
||||
await queryInterface.dropTable('organization');
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
async up(queryInterface, Sequelize) {
|
||||
await queryInterface.createTable('user', {
|
||||
|
|
@ -36,7 +34,7 @@ module.exports = {
|
|||
});
|
||||
},
|
||||
|
||||
async down(queryInterface, Sequelize) {
|
||||
async down(queryInterface) {
|
||||
await queryInterface.dropTable('user');
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
async up(queryInterface, Sequelize) {
|
||||
await queryInterface.createTable('reset_request', {
|
||||
|
|
@ -16,7 +14,7 @@ module.exports = {
|
|||
});
|
||||
},
|
||||
|
||||
async down(queryInterface, Sequelize) {
|
||||
async down(queryInterface) {
|
||||
await queryInterface.dropTable('reset_request');
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
async up(queryInterface, Sequelize) {
|
||||
await queryInterface.createTable('token', {
|
||||
|
|
@ -19,7 +17,7 @@ module.exports = {
|
|||
});
|
||||
},
|
||||
|
||||
async down(queryInterface, Sequelize) {
|
||||
async down(queryInterface) {
|
||||
await queryInterface.dropTable('token');
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
async up(queryInterface, Sequelize) {
|
||||
await queryInterface.addColumn('organization', 'ownerID', {
|
||||
|
|
@ -13,7 +11,7 @@ module.exports = {
|
|||
});
|
||||
},
|
||||
|
||||
async down(queryInterface, Sequelize) {
|
||||
async down(queryInterface) {
|
||||
await queryInterface.removeColumn('organization', 'ownerID');
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
async up(queryInterface, Sequelize) {
|
||||
await queryInterface.createTable('org_members', {
|
||||
|
|
@ -22,7 +20,7 @@ module.exports = {
|
|||
});
|
||||
},
|
||||
|
||||
async down(queryInterface, Sequelize) {
|
||||
async down(queryInterface) {
|
||||
await queryInterface.dropTable('org_members');
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
async up(queryInterface, Sequelize) {
|
||||
await queryInterface.addColumn('reset_request', 'ownerID', {
|
||||
|
|
@ -13,7 +11,7 @@ module.exports = {
|
|||
});
|
||||
},
|
||||
|
||||
async down(queryInterface, Sequelize) {
|
||||
async down(queryInterface) {
|
||||
await queryInterface.removeColumn('reset_request', 'ownerID');
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
async up(queryInterface, Sequelize) {
|
||||
await queryInterface.addColumn('token', 'ownerID', {
|
||||
|
|
@ -13,7 +11,7 @@ module.exports = {
|
|||
});
|
||||
},
|
||||
|
||||
async down(queryInterface, Sequelize) {
|
||||
async down(queryInterface) {
|
||||
await queryInterface.removeColumn('token', 'ownerID');
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ module.exports = (sequelize, DataTypes) => {
|
|||
allowNull: false,
|
||||
},
|
||||
},
|
||||
{ sequelize, tableName: 'organization' }
|
||||
{ sequelize, tableName: 'organization' },
|
||||
);
|
||||
|
||||
return Organization;
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ module.exports = (sequelize, DataTypes) => {
|
|||
defaultValue: DataTypes.UUIDV4,
|
||||
},
|
||||
},
|
||||
{ sequelize, tableName: 'reset_request' }
|
||||
{ sequelize, tableName: 'reset_request' },
|
||||
);
|
||||
|
||||
return ResetRequest;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ module.exports = (sequelize, DataTypes) => {
|
|||
defaultValue: true,
|
||||
},
|
||||
},
|
||||
{ sequelize, tableName: 'token' }
|
||||
{ sequelize, tableName: 'token' },
|
||||
);
|
||||
|
||||
return Token;
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ module.exports = (sequelize, DataTypes) => {
|
|||
type: DataTypes.STRING,
|
||||
},
|
||||
},
|
||||
{ sequelize, tableName: 'user' }
|
||||
{ sequelize, tableName: 'user' },
|
||||
);
|
||||
|
||||
return User;
|
||||
|
|
|
|||
|
|
@ -2,9 +2,11 @@ require('dotenv').config({ path: `../.env.${process.env.NODE_ENV}` });
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const Sequelize = require('sequelize');
|
||||
|
||||
const basename = path.basename(__filename);
|
||||
const env = process.env.NODE_ENV;
|
||||
const config = require('../config/config')[env];
|
||||
|
||||
const db = {};
|
||||
|
||||
const sequelize = new Sequelize({
|
||||
|
|
@ -13,18 +15,10 @@ const sequelize = new Sequelize({
|
|||
});
|
||||
|
||||
fs.readdirSync(__dirname)
|
||||
.filter((file) => {
|
||||
return (
|
||||
file.indexOf('.') !== 0 &&
|
||||
file !== basename &&
|
||||
file.slice(-3) === '.js'
|
||||
);
|
||||
})
|
||||
.filter((file) => file.indexOf('.') !== 0 && file !== basename && file.slice(-3) === '.js')
|
||||
.forEach((file) => {
|
||||
const model = require(path.join(__dirname, file))(
|
||||
sequelize,
|
||||
Sequelize.DataTypes
|
||||
);
|
||||
// eslint-disable-next-line import/no-dynamic-require, global-require
|
||||
const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
|
||||
db[model.name] = model;
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ async function basicAuth(req, res, next) {
|
|||
|
||||
// Make sure that a user exists with that email
|
||||
const result = await User.findOne({
|
||||
where: { email: email },
|
||||
where: { email },
|
||||
});
|
||||
if (!result) {
|
||||
return res.status(404).send(errors.NotFound);
|
||||
|
|
@ -24,7 +24,7 @@ async function basicAuth(req, res, next) {
|
|||
// Hash the password and verify that the user hash is identical
|
||||
const hashedPassword = forge.md.sha512.create().update(password).digest().toHex();
|
||||
|
||||
if (hashedPassword != result.password) {
|
||||
if (hashedPassword !== result.password) {
|
||||
return res.status(403).send(errors.Forbidden);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ const { Organization } = require('../db/models/index');
|
|||
const errors = require('../config/error.json');
|
||||
|
||||
async function checkKnownOrg(req, res, next) {
|
||||
const user = req.user;
|
||||
const { user } = req;
|
||||
const orgID = req.params.orgID || req.query.orgID || req.body.orgID;
|
||||
|
||||
// Get all organizations by the provided ID, with a list of members and owner
|
||||
|
|
@ -28,7 +28,7 @@ async function checkKnownOrg(req, res, next) {
|
|||
}
|
||||
|
||||
// Verify that the user who submitted the request exists as an owner or member
|
||||
if (!result.owner && result.member.length == 0) {
|
||||
if (!result.owner && result.member.length === 0) {
|
||||
return res.status(403).send(errors.Forbidden);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,15 +3,15 @@ const { User } = require('../db/models/index');
|
|||
const errors = require('../config/error.json');
|
||||
|
||||
async function checkKnownUser(req, res, next) {
|
||||
const user = req.user;
|
||||
const { user } = req;
|
||||
const userID = req.params.userID || req.query.userID || req.body.userID;
|
||||
|
||||
// Return result immediately if the user is querying themselves
|
||||
if (user.id == userID) {
|
||||
if (user.id === userID) {
|
||||
return next();
|
||||
}
|
||||
|
||||
// Find the queried user and the members of their owned organizations and owner/members of their member organizations
|
||||
// Find the queried user and append all the users in adjacent organizations
|
||||
const result = await User.findByPk(userID, {
|
||||
include: [
|
||||
{
|
||||
|
|
@ -45,10 +45,10 @@ async function checkKnownUser(req, res, next) {
|
|||
}
|
||||
|
||||
// Filter for an organization where the user is a member or an owner
|
||||
const filteredResult = result.memberOrg.filter((org) => org.member.length != 0 || org.owner);
|
||||
const filteredResult = result.memberOrg.filter((org) => org.member.length !== 0 || org.owner);
|
||||
|
||||
// Error if there is not an organization that includes that user and they are not in an owned organization
|
||||
if (filteredResult.length == 0 && result.ownedOrg.length == 0) {
|
||||
// Error if there is no relation between the users
|
||||
if (filteredResult.length === 0 && result.ownedOrg.length === 0) {
|
||||
return res.status(403).send(errors.Forbidden);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ const formidable = require('formidable');
|
|||
|
||||
function parseFormData(req, res, next) {
|
||||
// Parse multipart form data into formidable object and set static file save location
|
||||
var form = formidable();
|
||||
const form = formidable();
|
||||
form.uploadDir = `${__dirname}/../public/`;
|
||||
|
||||
form.parse(req, (err, fields, files) => {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ async function tokenAuth(req, res, next) {
|
|||
const date = new Date();
|
||||
if (!result) {
|
||||
return res.status(401).send(errors.Unauthenticated);
|
||||
} else if (result.expires && date.setDate(date.getDate + 1) > result.createdAt) {
|
||||
} if (result.expires && date.setDate(date.getDate + 1) > result.createdAt) {
|
||||
await Token.findOneAndDelete({ token });
|
||||
return res.status(401).send(errors.Unauthenticated);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@
|
|||
"sequelize": "^6.17.0",
|
||||
"sequelize-cli": "^6.4.1",
|
||||
"sqlite3": "^5.0.2",
|
||||
"uuid": "^8.3.2"
|
||||
"uuid": "^8.3.2",
|
||||
"winston": "^3.8.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"jest": "^27.5.1",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
async function GetMemberOrgs(req, res, next) {
|
||||
async function GetMemberOrgs(req, res) {
|
||||
// Retrieve all of the organizations the user is a part of
|
||||
const user = req.user;
|
||||
const { user } = req;
|
||||
|
||||
const result = await user.getMemberOrg({
|
||||
attributes: { exclude: ['updatedAt'] },
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
async function GetOwnedOrgs(req, res, next) {
|
||||
async function GetOwnedOrgs(req, res) {
|
||||
// Retrieve all organizations the user owns
|
||||
const user = req.user;
|
||||
const { user } = req;
|
||||
|
||||
const result = await user.getOwnedOrg({
|
||||
attributes: { exclude: ['updatedAt'] },
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
const { User } = require('../../db/models/index');
|
||||
|
||||
async function GetUserDetails(req, res, next) {
|
||||
const userID = req.params.userID;
|
||||
async function GetUserDetails(req, res) {
|
||||
const { userID } = req.params;
|
||||
|
||||
// Retrieve user while excluding sensitive information
|
||||
const result = await User.findOne({
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
const { Token } = require('../../db/models/index');
|
||||
|
||||
async function Login(req, res, next) {
|
||||
const user = req.user;
|
||||
const remember = req.body.remember;
|
||||
async function Login(req, res) {
|
||||
const { user } = req;
|
||||
const { remember } = req.body;
|
||||
|
||||
// Create token for user while setting expiry
|
||||
await Token.destroy({ where: { ownerID: user.id } });
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
async function Logout(req, res, next) {
|
||||
const user = req.user;
|
||||
async function Logout(req, res) {
|
||||
const { user } = req;
|
||||
const token = await user.getToken();
|
||||
|
||||
if (token) {
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ const forge = require('node-forge');
|
|||
const { User, Organization } = require('../../db/models/index');
|
||||
const errors = require('../../config/error.json');
|
||||
|
||||
async function Register(req, res, next) {
|
||||
const body = req.body;
|
||||
async function Register(req, res) {
|
||||
const { body } = req;
|
||||
|
||||
// Verify that the object body contains all SQL required fields
|
||||
if (!body.firstName || !body.lastName || !body.email || !body.password) {
|
||||
|
|
@ -32,7 +32,7 @@ async function Register(req, res, next) {
|
|||
|
||||
if (!org) {
|
||||
// Create user regularly if organization is not found
|
||||
const result = await User.create({
|
||||
await User.create({
|
||||
...body,
|
||||
password: passwordHash,
|
||||
});
|
||||
|
|
@ -49,8 +49,6 @@ async function Register(req, res, next) {
|
|||
|
||||
return res.sendStatus(200);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
|
||||
return res.status(500).send(errors.Generic);
|
||||
}
|
||||
} else {
|
||||
|
|
@ -63,8 +61,6 @@ async function Register(req, res, next) {
|
|||
|
||||
return res.sendStatus(200);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
|
||||
return res.status(500).send(errors.Generic);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
async function Remember(req, res) {
|
||||
const user = req.user;
|
||||
const { user } = req;
|
||||
|
||||
return res.send({
|
||||
user,
|
||||
|
|
|
|||
|
|
@ -3,16 +3,16 @@ const { CourierClient } = require('@trycourier/courier');
|
|||
const { User } = require('../../db/models/index');
|
||||
const errors = require('../../config/error.json');
|
||||
|
||||
async function RequestReset(req, res, next) {
|
||||
async function RequestReset(req, res) {
|
||||
try {
|
||||
// Make sure that the request contains an email
|
||||
const email = req.body.email;
|
||||
const { email } = req.body;
|
||||
if (!email) {
|
||||
return res.status(400).send(errors.Incomplete);
|
||||
}
|
||||
|
||||
// Confirm that a user exists with that email
|
||||
const result = await User.findOne({ where: { email: email } });
|
||||
const result = await User.findOne({ where: { email } });
|
||||
if (!result) {
|
||||
return res.status(404).send(errors.NotFound);
|
||||
}
|
||||
|
|
@ -38,7 +38,6 @@ async function RequestReset(req, res, next) {
|
|||
|
||||
return res.sendStatus(200);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return res.status(500).send(errors.Generic);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ const forge = require('node-forge');
|
|||
const { ResetRequest } = require('../../db/models/index');
|
||||
const errors = require('../../config/error.json');
|
||||
|
||||
async function ResetPassword(req, res, next) {
|
||||
async function ResetPassword(req, res) {
|
||||
const reqID = req.params.id;
|
||||
const password = req.body.password;
|
||||
const { password } = req.body;
|
||||
|
||||
// Verify that the form contains a new password
|
||||
if (!password) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
const fs = require('fs');
|
||||
|
||||
function SetAudio(req, res, next) {
|
||||
const user = req.user;
|
||||
function SetAudio(req, res) {
|
||||
const { user } = req;
|
||||
|
||||
const oldLocation = req.files.audioFile.filepath;
|
||||
|
||||
|
|
@ -11,9 +11,7 @@ function SetAudio(req, res, next) {
|
|||
const newLocation = `${uploadDir}/${fileName}`;
|
||||
|
||||
// Move file from public to folder
|
||||
fs.rename(oldLocation, newLocation, (err) => {
|
||||
if (err) console.log(err);
|
||||
});
|
||||
fs.rename(oldLocation, newLocation);
|
||||
|
||||
res.sendStatus(200);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ const { Op } = require('sequelize');
|
|||
const { User } = require('../../db/models/index');
|
||||
const errors = require('../../config/error.json');
|
||||
|
||||
async function UpdateUserDetails(req, res, next) {
|
||||
async function UpdateUserDetails(req, res) {
|
||||
const { token, ...body } = req.body;
|
||||
|
||||
// Verify that the email is not a duplicate, if it exists
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
var express = require('express');
|
||||
var router = express.Router();
|
||||
const express = require('express');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const parseFormData = require('../middleware/parseFormData');
|
||||
const basicAuth = require('../middleware/basicAuth');
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ const { Organization } = require('../../db/models/index');
|
|||
|
||||
const errors = require('../../config/error.json');
|
||||
|
||||
async function AddOrgMember(req, res, next) {
|
||||
const user = req.user;
|
||||
const orgID = req.params.orgID;
|
||||
async function AddOrgMember(req, res) {
|
||||
const { user } = req;
|
||||
const { orgID } = req.params;
|
||||
|
||||
// Verify that an organization exists with the ID provided
|
||||
const result = await Organization.findByPk(orgID);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
const errors = require('../../config/error.json');
|
||||
|
||||
async function CreateOrg(req, res, next) {
|
||||
const user = req.user;
|
||||
const orgName = req.body.orgName;
|
||||
async function CreateOrg(req, res) {
|
||||
const { user } = req;
|
||||
const { orgName } = req.body;
|
||||
|
||||
// Error if the form does not contain an organization name
|
||||
if (!orgName) {
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ const { Organization } = require('../../db/models/index');
|
|||
|
||||
const errors = require('../../config/error.json');
|
||||
|
||||
async function DeleteOrg(req, res, next) {
|
||||
const user = req.user;
|
||||
const orgID = req.params.orgID;
|
||||
async function DeleteOrg(req, res) {
|
||||
const { user } = req;
|
||||
const { orgID } = req.params;
|
||||
|
||||
// Error if an organization id was not provided
|
||||
if (!orgID) {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
const { Organization } = require('../../db/models/index');
|
||||
|
||||
async function GetOrg(req, res, next) {
|
||||
const user = req.user;
|
||||
const orgID = req.params.orgID;
|
||||
async function GetOrg(req, res) {
|
||||
const { user } = req;
|
||||
const { orgID } = req.params;
|
||||
|
||||
// Retrieve organization, owner, and members
|
||||
const org = await Organization.findByPk(orgID, {
|
||||
|
|
@ -27,7 +27,7 @@ async function GetOrg(req, res, next) {
|
|||
return res.send({
|
||||
org,
|
||||
memberCount: org.member.length,
|
||||
owner: org.ownerID == user.id,
|
||||
owner: org.ownerID === user.id,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue