* build client

* build client

* update .gitignore

* update signin logo

* created profile popover in navbar

* add accountcontroller to retrieve userinfo and accountpage

* set account page layout

* account form updates user profile

* update react scripts
This commit is contained in:
Ruidy 2020-05-06 21:51:06 +02:00 committed by GitHub
parent 913dbbf0a3
commit f4b94a2162
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 11814 additions and 37 deletions

2
.gitignore vendored
View file

@ -11,4 +11,4 @@ Tests/TicketManager.Tests/obj/
client/node_modules/
client/src/authentication/config.json
client/src/pages/TestPage.tsx
client/build

View file

@ -8,9 +8,11 @@ using TicketManager.Data;
using TicketManager.Resources;
using TicketManager.Models;
namespace TicketManager.Controllers
{
[Authorize]
[Produces("application/json")]
[Route("api/v1/[controller]")]
[ApiController]
public class TicketsController : ControllerBase

BIN
app.db

Binary file not shown.

View file

@ -0,0 +1,19 @@
{
"files": {
"main.js": "/static/js/main.2d8aca08.chunk.js",
"main.js.map": "/static/js/main.2d8aca08.chunk.js.map",
"runtime-main.js": "/static/js/runtime-main.cea588d5.js",
"runtime-main.js.map": "/static/js/runtime-main.cea588d5.js.map",
"static/js/2.f3326ec1.chunk.js": "/static/js/2.f3326ec1.chunk.js",
"static/js/2.f3326ec1.chunk.js.map": "/static/js/2.f3326ec1.chunk.js.map",
"index.html": "/index.html",
"precache-manifest.805f1d41ceb289334c4067299de2458e.js": "/precache-manifest.805f1d41ceb289334c4067299de2458e.js",
"service-worker.js": "/service-worker.js",
"static/js/2.f3326ec1.chunk.js.LICENSE.txt": "/static/js/2.f3326ec1.chunk.js.LICENSE.txt"
},
"entrypoints": [
"static/js/runtime-main.cea588d5.js",
"static/js/2.f3326ec1.chunk.js",
"static/js/main.2d8aca08.chunk.js"
]
}

BIN
client/build/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

1
client/build/index.html Normal file
View file

@ -0,0 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="Ticket Manager App" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"/><link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons"/><title>Ticket Manager App</title></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function t(t){for(var n,l,i=t[0],f=t[1],a=t[2],p=0,s=[];p<i.length;p++)l=i[p],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in f)Object.prototype.hasOwnProperty.call(f,n)&&(e[n]=f[n]);for(c&&c(t);s.length;)s.shift()();return u.push.apply(u,a||[]),r()}function r(){for(var e,t=0;t<u.length;t++){for(var r=u[t],n=!0,i=1;i<r.length;i++){var f=r[i];0!==o[f]&&(n=!1)}n&&(u.splice(t--,1),e=l(l.s=r[0]))}return e}var n={},o={1:0},u=[];function l(t){if(n[t])return n[t].exports;var r=n[t]={i:t,l:!1,exports:{}};return e[t].call(r.exports,r,r.exports,l),r.l=!0,r.exports}l.m=e,l.c=n,l.d=function(e,t,r){l.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,t){if(1&t&&(e=l(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(l.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)l.d(r,n,function(t){return e[t]}.bind(null,n));return r},l.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(t,"a",t),t},l.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},l.p="/";var i=this.webpackJsonpclient=this.webpackJsonpclient||[],f=i.push.bind(i);i.push=t,i=i.slice();for(var a=0;a<i.length;a++)t(i[a]);var c=f;r()}([])</script><script src="/static/js/2.f3326ec1.chunk.js"></script><script src="/static/js/main.2d8aca08.chunk.js"></script></body></html>

BIN
client/build/logo192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
client/build/logo512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View file

@ -0,0 +1,25 @@
{
"short_name": "BugBuster",
"name": "BugBuster | Project Management",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

View file

@ -0,0 +1,22 @@
self.__precacheManifest = (self.__precacheManifest || []).concat([
{
"revision": "ed78f568f574416d38907d84d79ef158",
"url": "/index.html"
},
{
"revision": "66f5a9b6e84ae5628df4",
"url": "/static/js/2.f3326ec1.chunk.js"
},
{
"revision": "3adc01bea76e7956dc3633ee898f6936",
"url": "/static/js/2.f3326ec1.chunk.js.LICENSE.txt"
},
{
"revision": "1c7010ac218763e7f78b",
"url": "/static/js/main.2d8aca08.chunk.js"
},
{
"revision": "5d93c6b2d15332364552",
"url": "/static/js/runtime-main.cea588d5.js"
}
]);

2
client/build/robots.txt Normal file
View file

@ -0,0 +1,2 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *

View file

@ -0,0 +1,39 @@
/**
* Welcome to your Workbox-powered service worker!
*
* You'll need to register this file in your web app and you should
* disable HTTP caching for this file too.
* See https://goo.gl/nhQhGp
*
* The rest of the code is auto-generated. Please don't update this file
* directly; instead, make changes to your Workbox build configuration
* and re-run your build process.
* See https://goo.gl/2aRDsh
*/
importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
importScripts(
"/precache-manifest.805f1d41ceb289334c4067299de2458e.js"
);
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
workbox.core.clientsClaim();
/**
* The workboxSW.precacheAndRoute() method efficiently caches and responds to
* requests for URLs in the manifest.
* See https://goo.gl/S9QRab
*/
self.__precacheManifest = [].concat(self.__precacheManifest || []);
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
workbox.routing.registerNavigationRoute(workbox.precaching.getCacheKeyForURL("/index.html"), {
blacklist: [/^\/_/,/\/[^/?]+\.[^/]+$/],
});

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,49 @@
/*
object-assign
(c) Sindre Sorhus
@license MIT
*/
/**
* A better abstraction over CSS.
*
* @copyright Oleg Isonen (Slobodskoi) / Isonen 2014-present
* @website https://github.com/cssinjs/jss
* @license MIT
*/
/** @license React v0.18.0
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v16.12.0
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v16.12.0
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v16.12.0
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,2 @@
!function(e){function t(t){for(var n,l,i=t[0],f=t[1],a=t[2],p=0,s=[];p<i.length;p++)l=i[p],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in f)Object.prototype.hasOwnProperty.call(f,n)&&(e[n]=f[n]);for(c&&c(t);s.length;)s.shift()();return u.push.apply(u,a||[]),r()}function r(){for(var e,t=0;t<u.length;t++){for(var r=u[t],n=!0,i=1;i<r.length;i++){var f=r[i];0!==o[f]&&(n=!1)}n&&(u.splice(t--,1),e=l(l.s=r[0]))}return e}var n={},o={1:0},u=[];function l(t){if(n[t])return n[t].exports;var r=n[t]={i:t,l:!1,exports:{}};return e[t].call(r.exports,r,r.exports,l),r.l=!0,r.exports}l.m=e,l.c=n,l.d=function(e,t,r){l.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},l.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,t){if(1&t&&(e=l(e)),8&t)return e;if(4&t&&"object"===typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(l.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)l.d(r,n,function(t){return e[t]}.bind(null,n));return r},l.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(t,"a",t),t},l.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},l.p="/";var i=this.webpackJsonpclient=this.webpackJsonpclient||[],f=i.push.bind(i);i.push=t,i=i.slice();for(var a=0;a<i.length;a++)t(i[a]);var c=f;r()}([]);
//# sourceMappingURL=runtime-main.cea588d5.js.map

File diff suppressed because one or more lines are too long

View file

@ -6,7 +6,7 @@
"@auth0/auth0-spa-js": "^1.6.4",
"@material-ui/core": "^4.9.13",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "^4.0.0-alpha.47",
"@material-ui/lab": "^4.0.0-alpha.52",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.4.0",
"@testing-library/user-event": "^7.2.1",
@ -22,7 +22,7 @@
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-router-dom": "^5.1.2",
"react-scripts": "3.3.1",
"react-scripts": "^3.4.1",
"react-swipeable-views": "^0.13.9",
"typescript": "^3.8.3",
"underscore": "^1.9.2"
@ -48,4 +48,4 @@
"last 1 safari version"
]
}
}
}

View file

@ -1,15 +1,19 @@
import React from "react";
import React, { useState } from "react";
import { Link } from "react-router-dom";
import {
AppBar,
Button,
IconButton,
// IconButton,
Toolbar,
Typography,
Avatar,
List,
ListItem,
Popover,
} from "@material-ui/core";
import { createStyles, makeStyles, Theme } from "@material-ui/core/styles";
import MenuIcon from "@material-ui/icons/Menu";
// import MenuIcon from "@material-ui/icons/Menu";
import BugReportIcon from "@material-ui/icons/BugReport";
import * as ROUTES from "../constants/routes";
import { useAuth0 } from "../authentication/auth0";
import { getUID } from "../authentication/helpers";
@ -25,6 +29,9 @@ const useStyles = makeStyles((theme: Theme) =>
title: {
flexGrow: 1,
},
typography: {
padding: theme.spacing(2),
},
})
);
@ -32,20 +39,35 @@ export default function ButtonAppBar() {
const classes = useStyles();
const { isAuthenticated, loginWithRedirect, logout, user } = useAuth0();
const [anchor, setAnchor] = useState<HTMLButtonElement | null>(null);
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) =>
setAnchor(e.currentTarget);
const handleClose = () => setAnchor(null);
const open: boolean = !!anchor;
const id = open ? "profile-popover" : undefined;
return (
<div className={classes.root}>
<AppBar position="static">
<Toolbar>
<IconButton
{/* <IconButton
edge="start"
className={classes.menuButton}
color="inherit"
aria-label="menu"
>
<MenuIcon />
</IconButton>
</IconButton> */}
<Typography variant="h6" className={classes.title}>
<Button color="inherit" component={Link} to={ROUTES.HOME}>
<Button
color="inherit"
component={Link}
to={ROUTES.HOME}
startIcon={<BugReportIcon />}
>
BugBuster
</Button>
</Typography>
@ -60,15 +82,54 @@ export default function ButtonAppBar() {
) : (
<>
<Button
color="inherit"
component={Link}
to={`${ROUTES.USERS}/${getUID(user)}`}
aria-describedby={id}
color="primary"
onClick={handleClick}
>
<Avatar src={user.picture} />
</Button>
<Button color="inherit" onClick={() => logout()}>
Log out
</Button>
<Popover
id={id}
open={open}
anchorEl={anchor}
onClose={handleClose}
anchorOrigin={{
vertical: "bottom",
horizontal: "right",
}}
transformOrigin={{
vertical: "top",
horizontal: "right",
}}
>
<List>
<ListItem>
<Button
color="inherit"
component={Link}
to={`${ROUTES.USERS}/${getUID(user)}`}
onClick={handleClose}
>
Profile
</Button>
</ListItem>
<ListItem>
<Button
color="inherit"
component={Link}
to={ROUTES.ACCOUNT}
onClick={handleClose}
>
Edit Profile
</Button>
</ListItem>
<ListItem>
<Button color="inherit" onClick={() => logout()}>
Log out
</Button>
</ListItem>
</List>
</Popover>
</>
)}
</Toolbar>

View file

@ -1,11 +1,43 @@
import React, { FC } from "react";
import { TextField } from "@material-ui/core";
interface IProps {
label: string;
type?: string;
state: string;
className?: string;
multiline?: boolean;
setState: React.Dispatch<React.SetStateAction<string>>;
}
const InputField: FC<IProps> = ({
label,
type = "text",
multiline = false,
state,
setState,
className,
}) => {
// update text after user input
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
e.preventDefault();
setState(e.target.value);
};
const InputField: FC = () => {
return (
<div className="input-field">
<input id="email" type="text" className="validate" />
<label htmlFor="email">Email</label>
</div>
<TextField
label={label}
value={state}
onChange={handleChange}
color="primary"
variant="outlined"
fullWidth
type={type}
multiline={multiline}
size="small"
// autoFocus
className={className}
/>
);
};

View file

@ -1,5 +1,5 @@
import React, { FC } from "react";
import InputField from "./InputField";
import PasswordField from "./PasswordField";
import Button from "./Buttons/Button";
@ -10,7 +10,8 @@ export const LogInForm: FC = () => {
<div className="center ">
<h4>Login</h4>
<form className="col s10 offset-s1">
<InputField />
<></>
{/* <InputField /> */}
<PasswordField />
<Button color="indigo" size="large">
Submit

View file

@ -4,7 +4,7 @@ import Button from "@material-ui/core/Button";
import CssBaseline from "@material-ui/core/CssBaseline";
import Paper from "@material-ui/core/Paper";
import Grid from "@material-ui/core/Grid";
import LockOutlinedIcon from "@material-ui/icons/LockOutlined";
import BugReportOutlinedIcon from "@material-ui/icons/BugReportOutlined";
import Typography from "@material-ui/core/Typography";
import { makeStyles, createStyles, Theme } from "@material-ui/core/styles";
import { useAuth0 } from "../authentication/auth0";
@ -58,7 +58,7 @@ export default function SignInSide() {
<Grid item xs={12} sm={8} md={5} component={Paper} elevation={6} square>
<div className={classes.paper}>
<Avatar className={classes.avatar}>
<LockOutlinedIcon />
<BugReportOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h3">
BugBuster

View file

@ -0,0 +1,5 @@
const API: string = "/api/v1";
export const PROJECTS: string = API + "/projects";
export const TICKETS: string = API + "/tickets";
export const USERS: string = API + "/users";

View file

@ -3,5 +3,6 @@ export const PROJECTS: string = "/projects";
export const TICKETS: string = "/tickets";
export const USERS: string = "/users";
export const SIGN_IN: string = "/signin";
export const ACCOUNT: string = "/account";
export const NOT_FOUND: string = "/404";
export const TEST: string = "/test";

View file

@ -0,0 +1,40 @@
import React, { FC, useState, useEffect, useRef } from "react";
import AccountPage from "../pages/AccountPage";
import Preloader from "../components/Preloader";
import User from "../types/User";
import { UserService } from "../services";
import { useAuth0 } from "../authentication/auth0";
import { getUID } from "../authentication/helpers";
const AccountController: FC = () => {
const { getTokenSilently, user } = useAuth0();
const [account, setAccount] = useState<User>();
const [loading, setLoading] = useState<boolean>(true);
const token = useRef<string>("");
useEffect(() => {
const getUserInfo = async () => {
// fetch current user data
token.current = await getTokenSilently();
const Users = new UserService(token.current);
const uid: string = getUID(user);
Users.get(uid)
.then((authUser) => setAccount(authUser))
.catch((err) => console.error(err));
};
getUserInfo().then(() => setLoading(false));
}, [getTokenSilently, user]);
return loading ? (
// display preloader until data is fetched
<Preloader />
) : // don't render page until data is fetched
!!account ? (
<AccountPage account={account} token={token.current} />
) : null;
};
export default AccountController;

View file

@ -73,7 +73,7 @@ const ProjectController: FC = () => {
setHasError(true);
setError("Bad Request");
}
}, [id]);
}, [id, getTokenSilently]);
if (hasError) {
return <ErrorController error={error} />;

View file

@ -0,0 +1,117 @@
import React, { FC, useState } from "react";
import { Button, Snackbar } from "@material-ui/core";
import { makeStyles, createStyles } from "@material-ui/core/styles";
import Alert from "@material-ui/lab/Alert";
import User from "../types/User";
import InputField from "../components/InputField";
import PageLayout from "../layouts/PageLayout";
import UserHeader from "../components/UserHeader";
import { UserService } from "../services";
const useStyles = makeStyles((theme) =>
createStyles({
input: {
marginBottom: theme.spacing(2),
},
})
);
interface IProps {
account: User;
token: string;
}
const AccountPage: FC<IProps> = ({ account, token }) => {
const classes = useStyles();
const [firstName, setFirstname] = useState(account.firstName);
const [lastName, setLastname] = useState(account.lastName);
const [presentation, setPresentation] = useState(account.presentation);
const [phone, setPhone] = useState(account.phone);
// user should at least have a name
const isDisabled = firstName === "" && lastName === "";
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
// prevent default button behaviour
e.preventDefault();
// send data to API
const Users = new UserService(token);
const newUser: User = {
id: account.id,
firstName: firstName,
lastName: lastName,
fullName: `${firstName} ${lastName}`,
email: account.email,
presentation: presentation,
picture: account.picture,
phone,
creationDate: Date.now().toLocaleString(),
activities: account.activities,
projects: account.projects,
tickets: account.tickets,
};
Users.update(account.id, newUser)
.then(() => console.log("ok"))
.catch((err) => console.error(err));
// reinitialize inputfiled
// setText("");
};
return (
<PageLayout
header={
<UserHeader
picture={account.picture}
fullName={account.fullName}
presentation={account.presentation}
/>
}
content={
<form onSubmit={handleSubmit}>
<InputField
label="First Name"
state={firstName}
setState={setFirstname}
className={classes.input}
/>
<InputField
label="Last Name"
state={lastName}
setState={setLastname}
className={classes.input}
/>
<InputField
label="Presentation"
state={presentation}
setState={setPresentation}
multiline
className={classes.input}
/>
<InputField
label="Phone"
state={phone}
setState={setPhone}
className={classes.input}
/>
<Button
type="submit"
variant="contained"
color="primary"
disabled={isDisabled}
>
Update Info
</Button>
<Snackbar open={isDisabled} autoHideDuration={6000}>
<Alert severity="warning">
User should have at least a first or last name!
</Alert>
</Snackbar>
</form>
}
/>
);
};
export default AccountPage;

View file

@ -5,9 +5,9 @@ import HomeController from "../controllers/HomeController";
import ProjectController from "../controllers/ProjectController";
import UserController from "../controllers/UserController";
import TicketController from "../controllers/TicketController";
import AccountController from "../controllers/AccountController";
import NotFoundPage from "../pages/NotFoundPage";
import TestPage from "../pages/TestPage";
// import SigninPage from "../pages/SigninPage";
import * as ROUTES from "../constants/routes";
const AppRouter = () => {
@ -15,7 +15,7 @@ const AppRouter = () => {
<Switch>
<PrivateRoute path={ROUTES.TEST} component={TestPage} />
<Route exact path={ROUTES.HOME} component={HomeController} />
{/* <Route path={ROUTES.SIGN_IN} component={SigninPage} /> */}
<PrivateRoute path={ROUTES.ACCOUNT} component={AccountController} />
<PrivateRoute
path={`${ROUTES.PROJECTS}/:id`}
component={ProjectController}

View file

@ -1,6 +1,7 @@
import IService from ".";
import Project from "../types/Project";
import HttpHandler from "./http";
import * as API from "../constants/api";
interface NewProject {
title: string;
@ -12,7 +13,7 @@ export default class ProjectService implements IService<Project> {
constructor(private key: string) {}
private http = new HttpHandler<Project>();
private path: string = "/api/v1/projects";
private path: string = API.PROJECTS;
all = async (): Promise<Project[]> => {
const response = await this.http.get(this.path, this.key);

View file

@ -1,6 +1,7 @@
import IService from ".";
import Ticket from "../types/Ticket";
import HttpHandler from "./http";
import * as API from "../constants/api";
interface NewTicket {
title: string;
@ -17,7 +18,7 @@ export default class TicketService implements IService<Ticket> {
constructor(private key: string) {}
private http = new HttpHandler<Ticket>();
private path: string = "/api/v1/tickets";
private path: string = API.TICKETS;
all = async (): Promise<Ticket[]> => {
const response = await this.http.get(this.path, this.key);

View file

@ -1,12 +1,13 @@
import IService from ".";
import User from "../types/User";
import HttpHandler from "./http";
import * as API from "../constants/api";
export default class UserService implements IService<User> {
constructor(private key: string) {}
private http = new HttpHandler<User>();
private path: string = "/api/v1/users";
private path: string = API.USERS;
all = async (): Promise<User[]> => {
const response = await this.http.get(this.path, this.key);
@ -26,7 +27,10 @@ export default class UserService implements IService<User> {
};
update = async (id: string, item: User): Promise<void> => {
throw new Error("Method not implemented.");
// const response =
await this.http.put(`${this.path}/${id}`, item, this.key);
// const body = response.parsedBody;
// return body ?? ({} as User);
};
delete = async (id: string): Promise<void> => {

View file

@ -1,5 +0,0 @@
export default class Constants {
static projectsURI: string = "/api/v1/projects";
static ticketsURI: string = "/api/v1/tickets";
static usersURI: string = "/api/v1/users";
}

11350
client/yarn.lock Normal file

File diff suppressed because it is too large Load diff