mirror of
https://github.com/rjNemo/ticket_manager
synced 2026-06-12 11:46:40 +00:00
Merge branch 'react' of https://github.com/rjNemo/ticket_manager
This commit is contained in:
commit
5f33789dc2
31 changed files with 367 additions and 120 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -35,3 +35,5 @@ client/.env.production.local
|
||||||
client/npm-debug.log*
|
client/npm-debug.log*
|
||||||
client/yarn-debug.log*
|
client/yarn-debug.log*
|
||||||
client/yarn-error.log*
|
client/yarn-error.log*
|
||||||
|
|
||||||
|
client/src/authentication/config.json
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,3 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@
|
||||||
- [ ] error page redirect when offline.
|
- [ ] error page redirect when offline.
|
||||||
- [x] ticket/files/activities list placeholders when empty
|
- [x] ticket/files/activities list placeholders when empty
|
||||||
- [ ] think about public/private DTO's constructor, getters and setters
|
- [ ] think about public/private DTO's constructor, getters and setters
|
||||||
- [<span style="color:red">X</span>] write dtos without circular dependencies.
|
- [<span style="color:red">x</span>] write dtos without circular dependencies.
|
||||||
- [ ] use dtoRequest for PutProjects
|
- [ ] use dtoRequest for PutProjects
|
||||||
- [ ] render avatarlist after UserModal Update
|
- [ ] render avatarlist after UserModal Update
|
||||||
|
- [ ] Form validators
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,11 @@ namespace TicketManager.Resources
|
||||||
{
|
{
|
||||||
public class NewAppUserDTO
|
public class NewAppUserDTO
|
||||||
{
|
{
|
||||||
|
[Required]
|
||||||
public string FirstName { get; set; }
|
public string FirstName { get; set; }
|
||||||
public string LastName { get; set; }
|
public string LastName { get; set; }
|
||||||
public string Presentation { get; set; }
|
public string Presentation { get; set; }
|
||||||
|
[Required]
|
||||||
[DataType(DataType.EmailAddress)]
|
[DataType(DataType.EmailAddress)]
|
||||||
public string Email { get; set; }
|
public string Email { get; set; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,9 @@ namespace TicketManager.Resources
|
||||||
[Required]
|
[Required]
|
||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
|
[Required]
|
||||||
public DateTime EndingDate { get; set; }
|
public DateTime EndingDate { get; set; }
|
||||||
|
[Required]
|
||||||
public Guid ManagerId { get; set; }
|
public Guid ManagerId { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8,10 +8,11 @@ namespace TicketManager.Resources
|
||||||
{
|
{
|
||||||
public class NewTicketDTO
|
public class NewTicketDTO
|
||||||
{
|
{
|
||||||
|
[Required]
|
||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
|
[Required]
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
|
[Required]
|
||||||
[DataType(DataType.Date)]
|
[DataType(DataType.Date)]
|
||||||
public DateTime EndingDate { get; set; }
|
public DateTime EndingDate { get; set; }
|
||||||
|
|
||||||
|
|
@ -20,8 +21,9 @@ namespace TicketManager.Resources
|
||||||
public string Difficulty { get; set; }
|
public string Difficulty { get; set; }
|
||||||
|
|
||||||
public string Category { get; set; }
|
public string Category { get; set; }
|
||||||
|
[Required]
|
||||||
public Guid CreatorId { get; set; }
|
public Guid CreatorId { get; set; }
|
||||||
|
[Required]
|
||||||
public int ProjectId { get; set; }
|
public int ProjectId { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
43
client/package-lock.json
generated
43
client/package-lock.json
generated
|
|
@ -4,6 +4,19 @@
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@auth0/auth0-spa-js": {
|
||||||
|
"version": "1.6.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@auth0/auth0-spa-js/-/auth0-spa-js-1.6.4.tgz",
|
||||||
|
"integrity": "sha512-zKUfUxOHL/YwSedP1BkKKRRrB4AJ7whMKBVlf6M7dTvVzeCrRzTWEXUF+HB4/iG4skVRyr+U35qQzMYoFWCxhw==",
|
||||||
|
"requires": {
|
||||||
|
"browser-tabs-lock": "^1.2.1",
|
||||||
|
"core-js": "^3.2.1",
|
||||||
|
"es-cookie": "^1.2.0",
|
||||||
|
"fast-text-encoding": "^1.0.0",
|
||||||
|
"promise-polyfill": "^8.1.3",
|
||||||
|
"unfetch": "^4.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@babel/code-frame": {
|
"@babel/code-frame": {
|
||||||
"version": "7.8.3",
|
"version": "7.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
|
||||||
|
|
@ -1441,6 +1454,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-7.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-7.2.1.tgz",
|
||||||
"integrity": "sha512-oZ0Ib5I4Z2pUEcoo95cT1cr6slco9WY7yiPpG+RGNkj8YcYgJnM7pXmYmorNOReh8MIGcKSqXyeGjxnr8YiZbA=="
|
"integrity": "sha512-oZ0Ib5I4Z2pUEcoo95cT1cr6slco9WY7yiPpG+RGNkj8YcYgJnM7pXmYmorNOReh8MIGcKSqXyeGjxnr8YiZbA=="
|
||||||
},
|
},
|
||||||
|
"@types/auth0": {
|
||||||
|
"version": "2.20.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/auth0/-/auth0-2.20.0.tgz",
|
||||||
|
"integrity": "sha512-dIb3VeVD6fSrGMUBWVHcb4N4trxC8BfSNkzqpafhhwsXDiJgeb4GQTqN4b+7Pafyq7vRVpeOoaS4gq9ovT+YwQ=="
|
||||||
|
},
|
||||||
"@types/babel__core": {
|
"@types/babel__core": {
|
||||||
"version": "7.1.3",
|
"version": "7.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.3.tgz",
|
||||||
|
|
@ -2702,6 +2720,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"browser-tabs-lock": {
|
||||||
|
"version": "1.2.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/browser-tabs-lock/-/browser-tabs-lock-1.2.7.tgz",
|
||||||
|
"integrity": "sha512-2H3EX1YstZEI8mkmoKPo7cIUfc0lJe2nysFQjcY4wRk/Z6Mb6jnDPYEloQNP0LQuBe4J7M1T78+sFE0gZagHDw=="
|
||||||
|
},
|
||||||
"browserify-aes": {
|
"browserify-aes": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
|
||||||
|
|
@ -4315,6 +4338,11 @@
|
||||||
"string.prototype.trimright": "^2.1.1"
|
"string.prototype.trimright": "^2.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"es-cookie": {
|
||||||
|
"version": "1.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/es-cookie/-/es-cookie-1.3.2.tgz",
|
||||||
|
"integrity": "sha512-UTlYYhXGLOy05P/vKVT2Ui7WtC7NiRzGtJyAKKn32g5Gvcjn7KAClLPWlipCtxIus934dFg9o9jXiBL0nP+t9Q=="
|
||||||
|
},
|
||||||
"es-to-primitive": {
|
"es-to-primitive": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
|
||||||
|
|
@ -5171,6 +5199,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
|
||||||
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
|
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
|
||||||
},
|
},
|
||||||
|
"fast-text-encoding": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-R9bHCvweUxxwkDwhjav5vxpFvdPGlVngtqmx4pIZfSUhM/Q4NiIUHB456BAf+Q1Nwu3HEZYONtu+Rya+af4jiQ=="
|
||||||
|
},
|
||||||
"faye-websocket": {
|
"faye-websocket": {
|
||||||
"version": "0.10.0",
|
"version": "0.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz",
|
||||||
|
|
@ -10219,6 +10252,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
|
||||||
"integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM="
|
"integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM="
|
||||||
},
|
},
|
||||||
|
"promise-polyfill": {
|
||||||
|
"version": "8.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.1.3.tgz",
|
||||||
|
"integrity": "sha512-MG5r82wBzh7pSKDRa9y+vllNHz3e3d4CNj1PQE4BQYxLme0gKYYBm9YENq+UkEikyZ0XbiGWxYlVw3Rl9O/U8g=="
|
||||||
|
},
|
||||||
"prompts": {
|
"prompts": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/prompts/-/prompts-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/prompts/-/prompts-2.3.0.tgz",
|
||||||
|
|
@ -12549,6 +12587,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.2.tgz",
|
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.2.tgz",
|
||||||
"integrity": "sha512-D39qtimx0c1fI3ya1Lnhk3E9nONswSKhnffBI0gME9C99fYOkNi04xs8K6pePLhvl1frbDemkaBQ5ikWllR2HQ=="
|
"integrity": "sha512-D39qtimx0c1fI3ya1Lnhk3E9nONswSKhnffBI0gME9C99fYOkNi04xs8K6pePLhvl1frbDemkaBQ5ikWllR2HQ=="
|
||||||
},
|
},
|
||||||
|
"unfetch": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-crP/n3eAPUJxZXM9T80/yv0YhkTEx2K1D3h7D1AJM6fzsWZrxdyRuLN0JH/dkZh1LNH8LxCnBzoPFCPbb2iGpg=="
|
||||||
|
},
|
||||||
"unicode-canonical-property-names-ecmascript": {
|
"unicode-canonical-property-names-ecmascript": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,11 @@
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@auth0/auth0-spa-js": "^1.6.4",
|
||||||
"@testing-library/jest-dom": "^4.2.4",
|
"@testing-library/jest-dom": "^4.2.4",
|
||||||
"@testing-library/react": "^9.4.0",
|
"@testing-library/react": "^9.4.0",
|
||||||
"@testing-library/user-event": "^7.2.1",
|
"@testing-library/user-event": "^7.2.1",
|
||||||
|
"@types/auth0": "^2.20.0",
|
||||||
"@types/jest": "^24.9.1",
|
"@types/jest": "^24.9.1",
|
||||||
"@types/node": "^12.12.26",
|
"@types/node": "^12.12.26",
|
||||||
"@types/react": "^16.9.19",
|
"@types/react": "^16.9.19",
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,19 @@
|
||||||
import React, { FC } from "react";
|
import React, { FC } from "react";
|
||||||
import Layout from "./pages/Layout";
|
import Layout from "./pages/Layout";
|
||||||
|
import { useAuth0 } from "./authentication/auth0";
|
||||||
|
|
||||||
const App: FC = () => {
|
const App: FC = () => {
|
||||||
return <Layout />;
|
const { loading } = useAuth0();
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return <div>Loading...</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="App">
|
||||||
|
<Layout />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import { User } from "../types/User";
|
||||||
import { getRemainingdays } from "../utils/methods";
|
import { getRemainingdays } from "../utils/methods";
|
||||||
|
|
||||||
export default class ProjectVM {
|
export default class ProjectVM {
|
||||||
// public id: number;
|
public id: number;
|
||||||
public title: string;
|
public title: string;
|
||||||
public description: string;
|
public description: string;
|
||||||
public creationDate: string;
|
public creationDate: string;
|
||||||
|
|
@ -22,9 +22,14 @@ export default class ProjectVM {
|
||||||
public ticketsTotalCount: number;
|
public ticketsTotalCount: number;
|
||||||
public ticketsDone: number;
|
public ticketsDone: number;
|
||||||
public remainingDays: number;
|
public remainingDays: number;
|
||||||
|
public allProjects: Project[];
|
||||||
|
|
||||||
public constructor(project: Project, allUsers: User[]) {
|
public constructor(
|
||||||
// this.id = project.id;
|
project: Project,
|
||||||
|
allUsers: User[],
|
||||||
|
allProjects: Project[]
|
||||||
|
) {
|
||||||
|
this.id = project.id;
|
||||||
this.title = project.title;
|
this.title = project.title;
|
||||||
this.description = project.description;
|
this.description = project.description;
|
||||||
this.creationDate = project.creationDate;
|
this.creationDate = project.creationDate;
|
||||||
|
|
@ -44,5 +49,6 @@ export default class ProjectVM {
|
||||||
? 0
|
? 0
|
||||||
: this.tickets.filter(t => t.status === "Done").length;
|
: this.tickets.filter(t => t.status === "Done").length;
|
||||||
this.remainingDays = getRemainingdays(project.endingDate);
|
this.remainingDays = getRemainingdays(project.endingDate);
|
||||||
|
this.allProjects = allProjects;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
112
client/src/authentication/auth0.tsx
Normal file
112
client/src/authentication/auth0.tsx
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
import React, { useState, useEffect, useContext, FC } from "react";
|
||||||
|
import createAuth0Client from "@auth0/auth0-spa-js";
|
||||||
|
import Auth0Client from "@auth0/auth0-spa-js/dist/typings/Auth0Client";
|
||||||
|
|
||||||
|
interface IAuth0Context {
|
||||||
|
isAuthenticated: boolean;
|
||||||
|
user: any;
|
||||||
|
loading: boolean;
|
||||||
|
popupOpen: boolean;
|
||||||
|
loginWithPopup(options: PopupLoginOptions): Promise<void>;
|
||||||
|
handleRedirectCallback(): Promise<RedirectLoginResult>;
|
||||||
|
getIdTokenClaims(o?: getIdTokenClaimsOptions): Promise<IdToken>;
|
||||||
|
loginWithRedirect(o: RedirectLoginOptions): Promise<void>;
|
||||||
|
getTokenSilently(o?: GetTokenSilentlyOptions): Promise<string | undefined>;
|
||||||
|
getTokenWithPopup(o?: GetTokenWithPopupOptions): Promise<string | undefined>;
|
||||||
|
logout(o?: LogoutOptions): void;
|
||||||
|
}
|
||||||
|
export interface IAuth0ProviderOptions {
|
||||||
|
children: React.ReactElement;
|
||||||
|
onRedirectCallback?(result: RedirectLoginResult): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEFAULT_REDIRECT_CALLBACK: () => void = () =>
|
||||||
|
window.history.replaceState({}, document.title, window.location.pathname);
|
||||||
|
|
||||||
|
export const Auth0Context: React.Context<IAuth0Context | null> = React.createContext<IAuth0Context | null>(
|
||||||
|
null
|
||||||
|
);
|
||||||
|
export const useAuth0: () => IAuth0Context = () => useContext(Auth0Context)!;
|
||||||
|
export const Auth0Provider = ({
|
||||||
|
children,
|
||||||
|
onRedirectCallback = DEFAULT_REDIRECT_CALLBACK,
|
||||||
|
...initOptions
|
||||||
|
}: IAuth0ProviderOptions & Auth0ClientOptions) => {
|
||||||
|
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
||||||
|
const [user, setUser] = useState();
|
||||||
|
const [auth0Client, setAuth0] = useState<Auth0Client>();
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [popupOpen, setPopupOpen] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const initAuth0 = async () => {
|
||||||
|
const auth0FromHook = await createAuth0Client(initOptions);
|
||||||
|
setAuth0(auth0FromHook);
|
||||||
|
|
||||||
|
if (window.location.search.includes("code=")) {
|
||||||
|
const { appState } = await auth0FromHook.handleRedirectCallback();
|
||||||
|
onRedirectCallback(appState);
|
||||||
|
}
|
||||||
|
|
||||||
|
const isAuthenticated = await auth0FromHook.isAuthenticated();
|
||||||
|
|
||||||
|
setIsAuthenticated(isAuthenticated);
|
||||||
|
|
||||||
|
if (isAuthenticated) {
|
||||||
|
const user = await auth0FromHook.getUser();
|
||||||
|
setUser(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(false);
|
||||||
|
};
|
||||||
|
initAuth0();
|
||||||
|
// eslint-disable-next-line
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const loginWithPopup = async (o: PopupLoginOptions) => {
|
||||||
|
setPopupOpen(true);
|
||||||
|
try {
|
||||||
|
await auth0Client!.loginWithPopup(o);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
} finally {
|
||||||
|
setPopupOpen(false);
|
||||||
|
}
|
||||||
|
const user = await auth0Client!.getUser();
|
||||||
|
setUser(user);
|
||||||
|
setIsAuthenticated(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRedirectCallback = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
const result = await auth0Client!.handleRedirectCallback();
|
||||||
|
const user = await auth0Client!.getUser();
|
||||||
|
setLoading(false);
|
||||||
|
setIsAuthenticated(true);
|
||||||
|
setUser(user);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Auth0Context.Provider
|
||||||
|
value={{
|
||||||
|
isAuthenticated,
|
||||||
|
user,
|
||||||
|
loading,
|
||||||
|
popupOpen,
|
||||||
|
loginWithPopup,
|
||||||
|
handleRedirectCallback,
|
||||||
|
getIdTokenClaims: (o: getIdTokenClaimsOptions | undefined) =>
|
||||||
|
auth0Client!.getIdTokenClaims(o),
|
||||||
|
loginWithRedirect: (o: RedirectLoginOptions) =>
|
||||||
|
auth0Client!.loginWithRedirect(o),
|
||||||
|
getTokenSilently: (o: GetTokenSilentlyOptions | undefined) =>
|
||||||
|
auth0Client!.getTokenSilently(o),
|
||||||
|
getTokenWithPopup: (o: GetTokenWithPopupOptions | undefined) =>
|
||||||
|
auth0Client!.getTokenWithPopup(o),
|
||||||
|
logout: (o: LogoutOptions | undefined) => auth0Client!.logout(o)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Auth0Context.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -6,7 +6,13 @@ interface IProps {
|
||||||
export const Avatar: FC<IProps> = ({ picture }) => {
|
export const Avatar: FC<IProps> = ({ picture }) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<img className="circle" src={picture} height="100vh" width="100vh" />
|
<img
|
||||||
|
className="circle"
|
||||||
|
src={picture}
|
||||||
|
height="100vh"
|
||||||
|
width="100vh"
|
||||||
|
alt="user avatar"
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
16
client/src/components/Navbar.tsx
Normal file
16
client/src/components/Navbar.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import React from "react";
|
||||||
|
import { useAuth0 } from "../authentication/auth0";
|
||||||
|
|
||||||
|
export const NavBar = () => {
|
||||||
|
const { isAuthenticated, loginWithRedirect, logout } = useAuth0();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{!isAuthenticated && (
|
||||||
|
<button onClick={() => loginWithRedirect({})}>Log in</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isAuthenticated && <button onClick={() => logout()}>Log out</button>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import React, { FC } from "react";
|
import React, { FC } from "react";
|
||||||
|
import { Project } from "../types/Project";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
title: string;
|
title: string;
|
||||||
|
|
@ -7,6 +8,9 @@ interface IProps {
|
||||||
setDescription: React.Dispatch<React.SetStateAction<string>>;
|
setDescription: React.Dispatch<React.SetStateAction<string>>;
|
||||||
endingDate: string;
|
endingDate: string;
|
||||||
setEndingDate: React.Dispatch<React.SetStateAction<string>>;
|
setEndingDate: React.Dispatch<React.SetStateAction<string>>;
|
||||||
|
allProjects: Project[];
|
||||||
|
projectId: string;
|
||||||
|
setProjectId: React.Dispatch<React.SetStateAction<string>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const NewTicketForm: FC<IProps> = ({
|
export const NewTicketForm: FC<IProps> = ({
|
||||||
|
|
@ -15,7 +19,10 @@ export const NewTicketForm: FC<IProps> = ({
|
||||||
description,
|
description,
|
||||||
setDescription,
|
setDescription,
|
||||||
endingDate,
|
endingDate,
|
||||||
setEndingDate
|
setEndingDate,
|
||||||
|
allProjects,
|
||||||
|
projectId,
|
||||||
|
setProjectId
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
@ -62,13 +69,23 @@ export const NewTicketForm: FC<IProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="input-field">
|
<div className="input-field">
|
||||||
<select id="project" className="browser-default">
|
<select
|
||||||
<option value="" disabled selected>
|
id="project"
|
||||||
|
className="browser-default"
|
||||||
|
value={projectId}
|
||||||
|
onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setProjectId(e.target.value);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<option value={0} disabled>
|
||||||
Project
|
Project
|
||||||
</option>
|
</option>
|
||||||
<option value="1">Option 1</option>
|
{allProjects.map(p => (
|
||||||
<option value="2">Option 2</option>
|
<option key={p.id} value={p.id}>
|
||||||
<option value="3">Option 3</option>
|
{p.title}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,31 @@
|
||||||
import React, { FC, useState, ChangeEvent, useEffect, FormEvent } from "react";
|
import React, { FC, useState, FormEvent } from "react";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams, useRouteMatch } from "react-router-dom";
|
||||||
import { Modal } from "./Modal";
|
import { Modal } from "./Modal";
|
||||||
import { NewTicketTabRouter } from "./NewTicketTabRouter";
|
import { NewTicketForm } from "./NewTicketForm";
|
||||||
import { User } from "../types/User";
|
|
||||||
import { Ticket } from "../types/Ticket";
|
import { Ticket } from "../types/Ticket";
|
||||||
import { patch, post } from "../utils/http";
|
|
||||||
import { Constants } from "../utils/Constants";
|
|
||||||
import { Project } from "../types/Project";
|
import { Project } from "../types/Project";
|
||||||
|
import { post } from "../utils/http";
|
||||||
|
import { Constants } from "../utils/Constants";
|
||||||
import { HttpResponse } from "../types/HttpResponse";
|
import { HttpResponse } from "../types/HttpResponse";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
show: boolean;
|
show: boolean;
|
||||||
handleClose(): void;
|
handleClose(): void;
|
||||||
allUsers: User[];
|
allProjects: Project[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const NewTicketModal: FC<IProps> = ({ show, handleClose, allUsers }) => {
|
export const NewTicketModal: FC<IProps> = ({
|
||||||
const [filterText, setFilterText] = useState<string>("");
|
show,
|
||||||
const { id } = useParams();
|
handleClose,
|
||||||
|
allProjects
|
||||||
|
}) => {
|
||||||
const [title, setTitle] = useState("");
|
const [title, setTitle] = useState("");
|
||||||
const [description, setDescription] = useState("");
|
const [description, setDescription] = useState("");
|
||||||
const [endingDate, setEndingDate] = useState("");
|
const [endingDate, setEndingDate] = useState("");
|
||||||
|
|
||||||
const handleChange: (e: ChangeEvent<HTMLInputElement>) => void = (
|
const { url } = useRouteMatch();
|
||||||
e: ChangeEvent<HTMLInputElement>
|
const id = url.split("/")[2];
|
||||||
) => {
|
const [projectId, setProjectId] = useState(id);
|
||||||
setFilterText(e.target.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit: (event: FormEvent<HTMLFormElement>) => void = async (
|
const handleSubmit: (event: FormEvent<HTMLFormElement>) => void = async (
|
||||||
e: FormEvent
|
e: FormEvent
|
||||||
|
|
@ -35,20 +34,19 @@ export const NewTicketModal: FC<IProps> = ({ show, handleClose, allUsers }) => {
|
||||||
let newTicket = {
|
let newTicket = {
|
||||||
title: title,
|
title: title,
|
||||||
description: description,
|
description: description,
|
||||||
endingDate: endingDate,
|
endingDate: new Date(endingDate).toISOString(),
|
||||||
creatorId: "20bf4b2a-7209-4826-96cd-29c2bc937a94",
|
creatorId: "20bf4b2a-7209-4826-96cd-29c2bc937a94",
|
||||||
projectId: 1
|
projectId: parseInt(projectId)
|
||||||
};
|
};
|
||||||
console.log(newTicket);
|
// console.log(newTicket);
|
||||||
const response: HttpResponse<Ticket> = await post<Ticket>(
|
const response: HttpResponse<Ticket> = await post<Ticket>(
|
||||||
`${Constants.ticketsURI}`,
|
`${Constants.ticketsURI}`,
|
||||||
newTicket
|
newTicket
|
||||||
);
|
);
|
||||||
console.log(response.parsedBody);
|
// console.log(response.parsedBody);
|
||||||
handleClose();
|
handleClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {});
|
|
||||||
return (
|
return (
|
||||||
<Modal show={show} handleClose={handleClose}>
|
<Modal show={show} handleClose={handleClose}>
|
||||||
<div className="row valign-wrapper indigo">
|
<div className="row valign-wrapper indigo">
|
||||||
|
|
@ -68,15 +66,16 @@ export const NewTicketModal: FC<IProps> = ({ show, handleClose, allUsers }) => {
|
||||||
|
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<NewTicketTabRouter
|
<NewTicketForm
|
||||||
tabNames={["Details", "Members"]}
|
|
||||||
users={allUsers}
|
|
||||||
title={title}
|
title={title}
|
||||||
setTitle={setTitle}
|
setTitle={setTitle}
|
||||||
description={description}
|
description={description}
|
||||||
setDescription={setDescription}
|
setDescription={setDescription}
|
||||||
endingDate={endingDate}
|
endingDate={endingDate}
|
||||||
setEndingDate={setEndingDate}
|
setEndingDate={setEndingDate}
|
||||||
|
allProjects={allProjects}
|
||||||
|
projectId={projectId}
|
||||||
|
setProjectId={setProjectId}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,9 @@
|
||||||
import React, { FC } from "react";
|
import React, { FC } from "react";
|
||||||
import { Route, useRouteMatch, Redirect } from "react-router-dom";
|
import { useRouteMatch } from "react-router-dom";
|
||||||
import { TabRouterHeader } from "./TabRouterHeader";
|
import { TabRouterHeader } from "./TabRouterHeader";
|
||||||
import { NewTicketForm } from "./NewTicketForm";
|
|
||||||
import { MemberList } from "./MemberList";
|
|
||||||
import { User } from "../types/User";
|
|
||||||
import { Ticket } from "../types/Ticket";
|
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
tabNames: string[];
|
tabNames: string[];
|
||||||
users: User[];
|
|
||||||
description: string;
|
description: string;
|
||||||
setDescription: React.Dispatch<React.SetStateAction<string>>;
|
setDescription: React.Dispatch<React.SetStateAction<string>>;
|
||||||
title: string;
|
title: string;
|
||||||
|
|
@ -19,7 +14,6 @@ interface IProps {
|
||||||
|
|
||||||
export const NewTicketTabRouter: FC<IProps> = ({
|
export const NewTicketTabRouter: FC<IProps> = ({
|
||||||
tabNames,
|
tabNames,
|
||||||
users,
|
|
||||||
description,
|
description,
|
||||||
setDescription,
|
setDescription,
|
||||||
title,
|
title,
|
||||||
|
|
@ -33,22 +27,14 @@ export const NewTicketTabRouter: FC<IProps> = ({
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<TabRouterHeader tabNames={tabNames} />
|
<TabRouterHeader tabNames={tabNames} />
|
||||||
|
|
||||||
<Redirect from={url} to={`${url}/details`} />
|
{/* <NewTicketForm
|
||||||
|
title={title}
|
||||||
<Route path={`${url}/details`}>
|
setTitle={setTitle}
|
||||||
<NewTicketForm
|
description={description}
|
||||||
title={title}
|
setDescription={setDescription}
|
||||||
setTitle={setTitle}
|
endingDate={endingDate}
|
||||||
description={description}
|
setEndingDate={setEndingDate}
|
||||||
setDescription={setDescription}
|
/> */}
|
||||||
endingDate={endingDate}
|
|
||||||
setEndingDate={setEndingDate}
|
|
||||||
/>
|
|
||||||
</Route>
|
|
||||||
|
|
||||||
<Route path={`${url}/members`}>
|
|
||||||
<MemberList users={users} />
|
|
||||||
</Route>
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,18 @@ export const ProgressBar: FC<ProgressBarProps> = ({
|
||||||
remainingDays
|
remainingDays
|
||||||
}) => {
|
}) => {
|
||||||
const styleString: CSSProperties = { width: `${value}%` };
|
const styleString: CSSProperties = { width: `${value}%` };
|
||||||
const barColor: string = value < 75 ? "red" : "";
|
let barColor: string = "green";
|
||||||
|
|
||||||
|
if (value < 100) {
|
||||||
|
barColor = "yellow";
|
||||||
|
}
|
||||||
|
if (value < 200 / 3) {
|
||||||
|
barColor = "orange";
|
||||||
|
}
|
||||||
|
if (value < 100 / 3) {
|
||||||
|
barColor = "red";
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="row">
|
<div className="row">
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,11 @@ import { Route, useRouteMatch, Redirect } from "react-router-dom";
|
||||||
import { TabRouterHeader } from "./TabRouterHeader";
|
import { TabRouterHeader } from "./TabRouterHeader";
|
||||||
import { TicketList } from "./TicketList";
|
import { TicketList } from "./TicketList";
|
||||||
import { FileList } from "./AppFileList";
|
import { FileList } from "./AppFileList";
|
||||||
import { ActivityList } from "./ActivityList";
|
// import { ActivityList } from "./ActivityList";
|
||||||
import { Ticket } from "../types/Ticket";
|
import { Ticket } from "../types/Ticket";
|
||||||
import { AppFile } from "../types/AppFile";
|
import { AppFile } from "../types/AppFile";
|
||||||
import { Activity } from "../types/Activity";
|
import { Activity } from "../types/Activity";
|
||||||
import { User } from "../types/User";
|
import { Project } from "../types/Project";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
tickets: Ticket[];
|
tickets: Ticket[];
|
||||||
|
|
@ -15,7 +15,7 @@ interface IProps {
|
||||||
tabNames: string[];
|
tabNames: string[];
|
||||||
files: AppFile[];
|
files: AppFile[];
|
||||||
activities: Activity[];
|
activities: Activity[];
|
||||||
allUsers: User[];
|
allProjects: Project[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TabRouter: FC<IProps> = ({
|
export const TabRouter: FC<IProps> = ({
|
||||||
|
|
@ -23,7 +23,7 @@ export const TabRouter: FC<IProps> = ({
|
||||||
tabNames,
|
tabNames,
|
||||||
files,
|
files,
|
||||||
activities,
|
activities,
|
||||||
allUsers
|
allProjects
|
||||||
}) => {
|
}) => {
|
||||||
const { url } = useRouteMatch();
|
const { url } = useRouteMatch();
|
||||||
|
|
||||||
|
|
@ -35,16 +35,16 @@ export const TabRouter: FC<IProps> = ({
|
||||||
<Redirect from={url} to={`${url}/tickets`} />
|
<Redirect from={url} to={`${url}/tickets`} />
|
||||||
|
|
||||||
<Route path={`${url}/tickets`}>
|
<Route path={`${url}/tickets`}>
|
||||||
<TicketList tickets={tickets} users={allUsers} />
|
<TicketList tickets={tickets} allProjects={allProjects} />
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
<Route path={`${url}/files`}>
|
<Route path={`${url}/files`}>
|
||||||
<FileList files={files} />
|
<FileList files={files} />
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
<Route path={`${url}/activity`}>
|
{/* <Route path={`${url}/activity`}>
|
||||||
<ActivityList activities={activities} />
|
<ActivityList activities={activities} />
|
||||||
</Route>
|
</Route> */}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -3,23 +3,22 @@ import { Ticket } from "../types/Ticket";
|
||||||
import { FloatingButton } from "./FloatingButton";
|
import { FloatingButton } from "./FloatingButton";
|
||||||
import { HorizontalCard } from "./HorizontalCard";
|
import { HorizontalCard } from "./HorizontalCard";
|
||||||
import { FilterBar } from "./FilterBar";
|
import { FilterBar } from "./FilterBar";
|
||||||
import { User } from "../types/User";
|
|
||||||
import { HttpResponse } from "../types/HttpResponse";
|
import { HttpResponse } from "../types/HttpResponse";
|
||||||
import { put } from "../utils/http";
|
import { put } from "../utils/http";
|
||||||
import { Constants } from "../utils/Constants";
|
import { Constants } from "../utils/Constants";
|
||||||
import { NewTicketModal } from "./NewTicketModal";
|
import { NewTicketModal } from "./NewTicketModal";
|
||||||
|
import { Project } from "../types/Project";
|
||||||
|
|
||||||
type TicketListProps = {
|
type TicketListProps = {
|
||||||
tickets: Ticket[];
|
tickets: Ticket[];
|
||||||
users: User[];
|
allProjects: Project[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TicketList: FC<TicketListProps> = ({ tickets, users }) => {
|
export const TicketList: FC<TicketListProps> = ({ tickets, allProjects }) => {
|
||||||
const [filterText, setFilterText] = useState<string>("");
|
const [filterText, setFilterText] = useState<string>("");
|
||||||
const clearFilterText: (e: MouseEvent) => void = (e: MouseEvent) => {
|
const clearFilterText: (e: MouseEvent) => void = (e: MouseEvent) => {
|
||||||
setFilterText("");
|
setFilterText("");
|
||||||
};
|
};
|
||||||
// const archiveTicket = () => {};
|
|
||||||
|
|
||||||
const onClick: (e: MouseEvent) => void = (e: MouseEvent) => {
|
const onClick: (e: MouseEvent) => void = (e: MouseEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
@ -45,7 +44,7 @@ export const TicketList: FC<TicketListProps> = ({ tickets, users }) => {
|
||||||
setShowNew(false);
|
setShowNew(false);
|
||||||
}}
|
}}
|
||||||
show={showNew}
|
show={showNew}
|
||||||
allUsers={users}
|
allProjects={allProjects}
|
||||||
/>
|
/>
|
||||||
<h3>Tickets</h3>
|
<h3>Tickets</h3>
|
||||||
<FloatingButton
|
<FloatingButton
|
||||||
|
|
@ -59,7 +58,7 @@ export const TicketList: FC<TicketListProps> = ({ tickets, users }) => {
|
||||||
clearFilterText={clearFilterText}
|
clearFilterText={clearFilterText}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="col s12 grey">
|
<div className="col s12 grey lighten-1">
|
||||||
<ul>
|
<ul>
|
||||||
{filteredTickets.length === 0 ? (
|
{filteredTickets.length === 0 ? (
|
||||||
<HorizontalCard />
|
<HorizontalCard />
|
||||||
|
|
@ -76,7 +75,6 @@ export const TicketList: FC<TicketListProps> = ({ tickets, users }) => {
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
// archiveTicket={archiveTicket}
|
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { TabRouterHeader } from "./TabRouterHeader";
|
||||||
import { ProjectList } from "./ProjectList";
|
import { ProjectList } from "./ProjectList";
|
||||||
import { Ticket } from "../types/Ticket";
|
import { Ticket } from "../types/Ticket";
|
||||||
import { Project } from "../types/Project";
|
import { Project } from "../types/Project";
|
||||||
|
import { TicketList } from "./TicketList";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
tabNames: string[];
|
tabNames: string[];
|
||||||
|
|
@ -25,9 +26,9 @@ export const UserTabRouter: FC<IProps> = ({ tickets, tabNames, projects }) => {
|
||||||
<ProjectList projects={projects} />
|
<ProjectList projects={projects} />
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
{/* <Route path={`${url}/tickets`}>
|
<Route path={`${url}/tickets`}>
|
||||||
<TicketList tickets={tickets} />
|
<TicketList tickets={tickets} allProjects={[]} />
|
||||||
</Route> */}
|
</Route>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { FC, useState, ChangeEvent, FormEvent } from "react";
|
import React, { FC, useState, ChangeEvent, FormEvent, useEffect } from "react";
|
||||||
import { Modal } from "./Modal";
|
import { Modal } from "./Modal";
|
||||||
import { AvatarList } from "./AvatarList";
|
import { AvatarList } from "./AvatarList";
|
||||||
import { User } from "../types/User";
|
import { User } from "../types/User";
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ export const UsersModalEntry: FC<IProps> = ({ user, setMembers, members }) => {
|
||||||
return Boolean(members.find(m => m.id === id));
|
return Boolean(members.find(m => m.id === id));
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div className="row">
|
<div className="valign-wrapper">
|
||||||
<label htmlFor={user.id}>
|
<label htmlFor={user.id}>
|
||||||
<input
|
<input
|
||||||
id={user.id}
|
id={user.id}
|
||||||
|
|
@ -27,15 +27,16 @@ export const UsersModalEntry: FC<IProps> = ({ user, setMembers, members }) => {
|
||||||
/>
|
/>
|
||||||
<span>
|
<span>
|
||||||
{user.fullName}
|
{user.fullName}
|
||||||
<img
|
{" "}
|
||||||
className="circle"
|
|
||||||
src={user.picture}
|
|
||||||
width="32vh"
|
|
||||||
height="32vh"
|
|
||||||
alt={user.fullName}
|
|
||||||
/>
|
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
|
<img
|
||||||
|
className="circle"
|
||||||
|
src={user.picture}
|
||||||
|
width="32vh"
|
||||||
|
height="32vh"
|
||||||
|
alt={user.fullName}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import { User } from "../types/User";
|
||||||
export const ProjectController: FC = () => {
|
export const ProjectController: FC = () => {
|
||||||
const [project, setProject] = useState<Project>({} as Project);
|
const [project, setProject] = useState<Project>({} as Project);
|
||||||
const [allUsers, setAllUsers] = useState<User[]>([]);
|
const [allUsers, setAllUsers] = useState<User[]>([]);
|
||||||
|
const [allProjects, setAllProjects] = useState<Project[]>([]);
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [hasError, setHasError] = useState(false);
|
const [hasError, setHasError] = useState(false);
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
|
|
@ -48,10 +49,25 @@ export const ProjectController: FC = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function httpGetAllProjects(): Promise<void> {
|
||||||
|
try {
|
||||||
|
const response: HttpResponse<Project> = await get<Project>(
|
||||||
|
`${Constants.projectsURI}`
|
||||||
|
);
|
||||||
|
if (response.parsedBody !== undefined) {
|
||||||
|
setAllProjects((response.parsedBody as unknown) as Project[]);
|
||||||
|
}
|
||||||
|
} catch (ex) {
|
||||||
|
setHasError(true);
|
||||||
|
setError(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (id !== undefined) {
|
if (id !== undefined) {
|
||||||
httpGetProjects(id);
|
httpGetProjects(id);
|
||||||
httpGetAllUsers();
|
httpGetAllUsers();
|
||||||
|
httpGetAllProjects();
|
||||||
} else {
|
} else {
|
||||||
setHasError(true);
|
setHasError(true);
|
||||||
setError("Bad Request");
|
setError("Bad Request");
|
||||||
|
|
@ -61,6 +77,6 @@ export const ProjectController: FC = () => {
|
||||||
if (hasError) {
|
if (hasError) {
|
||||||
return <ErrorController error={error} />;
|
return <ErrorController error={error} />;
|
||||||
}
|
}
|
||||||
const viewModel = new ProjectVM(project, allUsers);
|
const viewModel = new ProjectVM(project, allUsers, allProjects);
|
||||||
return isLoading ? <Preloader /> : <ProjectPage viewModel={viewModel} />;
|
return isLoading ? <Preloader /> : <ProjectPage viewModel={viewModel} />;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
29
client/src/index.jsx
Normal file
29
client/src/index.jsx
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
import React from "react";
|
||||||
|
import ReactDOM from "react-dom";
|
||||||
|
import App from "./App";
|
||||||
|
import * as serviceWorker from "./serviceWorker";
|
||||||
|
import { Auth0Provider } from "./authentication/auth0";
|
||||||
|
import config from "./authentication/config.json";
|
||||||
|
import history from "./utils/history";
|
||||||
|
|
||||||
|
const onRedirectCallback = appState => {
|
||||||
|
history.push(
|
||||||
|
appState && appState.targetUrl
|
||||||
|
? appState.targetUrl
|
||||||
|
: window.location.pathname
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
ReactDOM.render(
|
||||||
|
<Auth0Provider
|
||||||
|
domain={config.domain}
|
||||||
|
client_id={config.clientId}
|
||||||
|
redirect_uri={window.location.origin}
|
||||||
|
onRedirectCallback={onRedirectCallback}
|
||||||
|
>
|
||||||
|
<App />
|
||||||
|
</Auth0Provider>,
|
||||||
|
document.getElementById("root")
|
||||||
|
);
|
||||||
|
|
||||||
|
serviceWorker.unregister();
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import ReactDOM from "react-dom";
|
|
||||||
import App from "./App";
|
|
||||||
import * as serviceWorker from "./serviceWorker";
|
|
||||||
|
|
||||||
ReactDOM.render(<App />, document.getElementById("root"));
|
|
||||||
|
|
||||||
serviceWorker.unregister();
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
import React, { FC } from "react";
|
import React, { FC } from "react";
|
||||||
import { AppRouter } from "../utils/router";
|
import { AppRouter } from "../utils/router";
|
||||||
|
import { NavBar } from "../components/Navbar";
|
||||||
|
|
||||||
const Layout: FC = () => {
|
const Layout: FC = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* <NavBar />
|
<header>
|
||||||
<BreadCrumb /> */}
|
<NavBar />
|
||||||
|
</header>
|
||||||
|
{/* <BreadCrumb /> */}
|
||||||
<AppRouter />
|
<AppRouter />
|
||||||
{/* <Footer /> */}
|
{/* <Footer /> */}
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ interface IProps {
|
||||||
|
|
||||||
export const ProjectPage: FC<IProps> = ({ viewModel }) => {
|
export const ProjectPage: FC<IProps> = ({ viewModel }) => {
|
||||||
const {
|
const {
|
||||||
|
// id,
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
users,
|
users,
|
||||||
|
|
@ -23,10 +24,11 @@ export const ProjectPage: FC<IProps> = ({ viewModel }) => {
|
||||||
ticketsTotalCount,
|
ticketsTotalCount,
|
||||||
remainingDays,
|
remainingDays,
|
||||||
files,
|
files,
|
||||||
activities
|
activities,
|
||||||
|
allProjects
|
||||||
} = viewModel;
|
} = viewModel;
|
||||||
|
|
||||||
const tabNames: string[] = ["Tickets", "Files", "Activity"];
|
const tabNames: string[] = ["Tickets", "Files"]; //, "Activity"];
|
||||||
const [showModal, setShowModal] = useState<boolean>(false);
|
const [showModal, setShowModal] = useState<boolean>(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -59,7 +61,7 @@ export const ProjectPage: FC<IProps> = ({ viewModel }) => {
|
||||||
tickets={tickets}
|
tickets={tickets}
|
||||||
files={files}
|
files={files}
|
||||||
activities={activities}
|
activities={activities}
|
||||||
allUsers={allUsers}
|
allProjects={allProjects}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,6 @@ export const UserPage: FC<IProps> = ({ viewModel }) => {
|
||||||
tickets={tickets}
|
tickets={tickets}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/* // <TabView>
|
|
||||||
// <CardList>
|
|
||||||
// <CardList>
|
|
||||||
// </TabView> */}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
2
client/src/utils/history.ts
Normal file
2
client/src/utils/history.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
import { createBrowserHistory } from "history";
|
||||||
|
export default createBrowserHistory;
|
||||||
|
|
@ -58,5 +58,5 @@ const headers: Headers = new Headers({
|
||||||
Accept: "application/json",
|
Accept: "application/json",
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
Authorization:
|
Authorization:
|
||||||
"Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik1UWkNSRFEzUkRnd1FUQXlNRFExTmtOQ09UQXlSamhGTURaRU1Ea3pNRGxHUkRrelFqZENSZyJ9.eyJpc3MiOiJodHRwczovL2Rldi1meWpydm9oeC5hdXRoMC5jb20vIiwic3ViIjoiR3dlZTlGUnN3ejNWNE5vZFVRTjJIcjJyQjJTMDI1UmZAY2xpZW50cyIsImF1ZCI6Imh0dHBzOi8vbG9jYWxob3N0OjUwMDEvYXBpL1YxLyIsImlhdCI6MTU4Mjk3MTQyMSwiZXhwIjoxNTgzMDU3ODIxLCJhenAiOiJHd2VlOUZSc3d6M1Y0Tm9kVVFOMkhyMnJCMlMwMjVSZiIsImd0eSI6ImNsaWVudC1jcmVkZW50aWFscyJ9.mH_ejE0gpMXrMIVUV7afuagTopCxm2x7F7Ash9L7zfOCQnw71E7NPsEh8w2_nFFz3lwm988vGbJhB_1G0oetK3VnqgahHn-ZJfk8RhQeKZQtCddbFCXSZzbsvi8XekpN2qLSZswrfxM4hiNfedQW1sM6wSbVbv4q6MrpPrtnepOo5lu67b9eHQZA5MQGqCLqqAZtEAa4Z8bVUCUcf3wU4e9W38LngrMSEMN62_ZZ8AVnjFVQ97zWEadJhYT54S9tVioY8jNR-38qjuYH_ZP3mVQg8INza9YFiYzIsIgdYufhorb_cSXc1qK1ZhHf4kRHaiHCYan-c9nN9SM9MCYA9A"
|
"Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik1UWkNSRFEzUkRnd1FUQXlNRFExTmtOQ09UQXlSamhGTURaRU1Ea3pNRGxHUkRrelFqZENSZyJ9.eyJpc3MiOiJodHRwczovL2Rldi1meWpydm9oeC5hdXRoMC5jb20vIiwic3ViIjoiR3dlZTlGUnN3ejNWNE5vZFVRTjJIcjJyQjJTMDI1UmZAY2xpZW50cyIsImF1ZCI6Imh0dHBzOi8vbG9jYWxob3N0OjUwMDEvYXBpL1YxLyIsImlhdCI6MTU4MzI0ODU1NSwiZXhwIjoxNTgzMzM0OTU1LCJhenAiOiJHd2VlOUZSc3d6M1Y0Tm9kVVFOMkhyMnJCMlMwMjVSZiIsImd0eSI6ImNsaWVudC1jcmVkZW50aWFscyJ9.lfgZAwqq3VHNAM1Y33Vj7Jtal6IdN92qWJHwu5T8l-56kBXNC2g1pm6TshRhYDKmedoJmSddjKXClA9eLu1Ve1X8wLj7CbrhZtGNsOcIEStF9icahCy8y2OdP2U6UXJJt9HehwqvwT3JltH_MqyYeJsyMsah3a3rlu6LoEAHNqF4jk8RUxZKjlVJz_iW-tbJ_GoGYrTp_bgvw6IBpgZvJDPGveaA6ms20CoN4zpXaL2ucgtaRbasXfD-z4NDwYN5_9TRPNcf9cuxViZ28CJ66uWxK8BEKysWnABYkh239Q71K2t41hFvQV8ti5l1UKcuqFf_lUGo0wYo9F-MGK6RKA"
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,7 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es5",
|
"target": "es5",
|
||||||
"lib": [
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
"dom",
|
|
||||||
"dom.iterable",
|
|
||||||
"esnext"
|
|
||||||
],
|
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
|
|
@ -19,7 +15,5 @@
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"jsx": "react"
|
"jsx": "react"
|
||||||
},
|
},
|
||||||
"include": [
|
"include": ["src"]
|
||||||
"src"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue