diff --git a/.gitignore b/.gitignore index f64aa1b..0b93d06 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,5 @@ client/.env.production.local client/npm-debug.log* client/yarn-debug.log* client/yarn-error.log* + +client/src/authentication/config.json diff --git a/Program.cs b/Program.cs index 35c5cb4..475e324 100644 --- a/Program.cs +++ b/Program.cs @@ -1,7 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; diff --git a/README.md b/README.md index a01d101..4980c9f 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ - [ ] error page redirect when offline. - [x] ticket/files/activities list placeholders when empty - [ ] think about public/private DTO's constructor, getters and setters -- [X] write dtos without circular dependencies. +- [x] write dtos without circular dependencies. - [ ] use dtoRequest for PutProjects - [ ] render avatarlist after UserModal Update +- [ ] Form validators diff --git a/Resources/AppUser/NewAppUserDTO.cs b/Resources/AppUser/NewAppUserDTO.cs index 824e8fe..1b146e7 100644 --- a/Resources/AppUser/NewAppUserDTO.cs +++ b/Resources/AppUser/NewAppUserDTO.cs @@ -4,10 +4,11 @@ namespace TicketManager.Resources { public class NewAppUserDTO { + [Required] public string FirstName { get; set; } public string LastName { get; set; } public string Presentation { get; set; } - + [Required] [DataType(DataType.EmailAddress)] public string Email { get; set; } diff --git a/Resources/Project/NewProjectDTO.cs b/Resources/Project/NewProjectDTO.cs index e6d1bf9..8043069 100644 --- a/Resources/Project/NewProjectDTO.cs +++ b/Resources/Project/NewProjectDTO.cs @@ -8,7 +8,9 @@ namespace TicketManager.Resources [Required] public string Title { get; set; } public string Description { get; set; } + [Required] public DateTime EndingDate { get; set; } + [Required] public Guid ManagerId { get; set; } } } \ No newline at end of file diff --git a/Resources/Tickets/NewTicketDTO.cs b/Resources/Tickets/NewTicketDTO.cs index 45864d2..a8ebb28 100644 --- a/Resources/Tickets/NewTicketDTO.cs +++ b/Resources/Tickets/NewTicketDTO.cs @@ -8,10 +8,11 @@ namespace TicketManager.Resources { public class NewTicketDTO { + [Required] public string Title { get; set; } - + [Required] public string Description { get; set; } - + [Required] [DataType(DataType.Date)] public DateTime EndingDate { get; set; } @@ -20,8 +21,9 @@ namespace TicketManager.Resources public string Difficulty { get; set; } public string Category { get; set; } - + [Required] public Guid CreatorId { get; set; } + [Required] public int ProjectId { get; set; } } } \ No newline at end of file diff --git a/client/package-lock.json b/client/package-lock.json index 2aff520..a8c0141 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -4,6 +4,19 @@ "lockfileVersion": 1, "requires": true, "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": { "version": "7.8.3", "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", "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": { "version": "7.1.3", "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": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", @@ -4315,6 +4338,11 @@ "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": { "version": "1.2.1", "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", "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": { "version": "0.10.0", "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", "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": { "version": "2.3.0", "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", "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": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", diff --git a/client/package.json b/client/package.json index b827484..87298e4 100644 --- a/client/package.json +++ b/client/package.json @@ -3,9 +3,11 @@ "version": "0.1.0", "private": true, "dependencies": { + "@auth0/auth0-spa-js": "^1.6.4", "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.4.0", "@testing-library/user-event": "^7.2.1", + "@types/auth0": "^2.20.0", "@types/jest": "^24.9.1", "@types/node": "^12.12.26", "@types/react": "^16.9.19", diff --git a/client/src/App.tsx b/client/src/App.tsx index 88220e3..f71b386 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,8 +1,19 @@ import React, { FC } from "react"; import Layout from "./pages/Layout"; +import { useAuth0 } from "./authentication/auth0"; const App: FC = () => { - return ; + const { loading } = useAuth0(); + + if (loading) { + return
Loading...
; + } + + return ( +
+ +
+ ); }; export default App; diff --git a/client/src/VM/ProjectVM.ts b/client/src/VM/ProjectVM.ts index 87faecc..3b89e72 100644 --- a/client/src/VM/ProjectVM.ts +++ b/client/src/VM/ProjectVM.ts @@ -6,7 +6,7 @@ import { User } from "../types/User"; import { getRemainingdays } from "../utils/methods"; export default class ProjectVM { - // public id: number; + public id: number; public title: string; public description: string; public creationDate: string; @@ -22,9 +22,14 @@ export default class ProjectVM { public ticketsTotalCount: number; public ticketsDone: number; public remainingDays: number; + public allProjects: Project[]; - public constructor(project: Project, allUsers: User[]) { - // this.id = project.id; + public constructor( + project: Project, + allUsers: User[], + allProjects: Project[] + ) { + this.id = project.id; this.title = project.title; this.description = project.description; this.creationDate = project.creationDate; @@ -44,5 +49,6 @@ export default class ProjectVM { ? 0 : this.tickets.filter(t => t.status === "Done").length; this.remainingDays = getRemainingdays(project.endingDate); + this.allProjects = allProjects; } } diff --git a/client/src/authentication/auth0.tsx b/client/src/authentication/auth0.tsx new file mode 100644 index 0000000..bb85f11 --- /dev/null +++ b/client/src/authentication/auth0.tsx @@ -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; + handleRedirectCallback(): Promise; + getIdTokenClaims(o?: getIdTokenClaimsOptions): Promise; + loginWithRedirect(o: RedirectLoginOptions): Promise; + getTokenSilently(o?: GetTokenSilentlyOptions): Promise; + getTokenWithPopup(o?: GetTokenWithPopupOptions): Promise; + 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 = React.createContext( + 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(); + 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 ( + + 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} + + ); +}; diff --git a/client/src/components/Avatar.tsx b/client/src/components/Avatar.tsx index 37c45c4..91dd6e9 100644 --- a/client/src/components/Avatar.tsx +++ b/client/src/components/Avatar.tsx @@ -6,7 +6,13 @@ interface IProps { export const Avatar: FC = ({ picture }) => { return ( <> - + user avatar ); }; diff --git a/client/src/components/Navbar.tsx b/client/src/components/Navbar.tsx new file mode 100644 index 0000000..3432b0a --- /dev/null +++ b/client/src/components/Navbar.tsx @@ -0,0 +1,16 @@ +import React from "react"; +import { useAuth0 } from "../authentication/auth0"; + +export const NavBar = () => { + const { isAuthenticated, loginWithRedirect, logout } = useAuth0(); + + return ( +
+ {!isAuthenticated && ( + + )} + + {isAuthenticated && } +
+ ); +}; diff --git a/client/src/components/NewTicketForm.tsx b/client/src/components/NewTicketForm.tsx index c105250..97e0108 100644 --- a/client/src/components/NewTicketForm.tsx +++ b/client/src/components/NewTicketForm.tsx @@ -1,4 +1,5 @@ import React, { FC } from "react"; +import { Project } from "../types/Project"; interface IProps { title: string; @@ -7,6 +8,9 @@ interface IProps { setDescription: React.Dispatch>; endingDate: string; setEndingDate: React.Dispatch>; + allProjects: Project[]; + projectId: string; + setProjectId: React.Dispatch>; } export const NewTicketForm: FC = ({ @@ -15,7 +19,10 @@ export const NewTicketForm: FC = ({ description, setDescription, endingDate, - setEndingDate + setEndingDate, + allProjects, + projectId, + setProjectId }) => { return ( <> @@ -62,13 +69,23 @@ export const NewTicketForm: FC = ({
- ) => { + e.preventDefault(); + setProjectId(e.target.value); + }} + > + - - - + {allProjects.map(p => ( + + ))}
diff --git a/client/src/components/NewTicketModal.tsx b/client/src/components/NewTicketModal.tsx index b2ecb62..03c7ccd 100644 --- a/client/src/components/NewTicketModal.tsx +++ b/client/src/components/NewTicketModal.tsx @@ -1,32 +1,31 @@ -import React, { FC, useState, ChangeEvent, useEffect, FormEvent } from "react"; -import { useParams } from "react-router-dom"; +import React, { FC, useState, FormEvent } from "react"; +import { useParams, useRouteMatch } from "react-router-dom"; import { Modal } from "./Modal"; -import { NewTicketTabRouter } from "./NewTicketTabRouter"; -import { User } from "../types/User"; +import { NewTicketForm } from "./NewTicketForm"; import { Ticket } from "../types/Ticket"; -import { patch, post } from "../utils/http"; -import { Constants } from "../utils/Constants"; import { Project } from "../types/Project"; +import { post } from "../utils/http"; +import { Constants } from "../utils/Constants"; import { HttpResponse } from "../types/HttpResponse"; interface IProps { show: boolean; handleClose(): void; - allUsers: User[]; + allProjects: Project[]; } -export const NewTicketModal: FC = ({ show, handleClose, allUsers }) => { - const [filterText, setFilterText] = useState(""); - const { id } = useParams(); +export const NewTicketModal: FC = ({ + show, + handleClose, + allProjects +}) => { const [title, setTitle] = useState(""); const [description, setDescription] = useState(""); const [endingDate, setEndingDate] = useState(""); - const handleChange: (e: ChangeEvent) => void = ( - e: ChangeEvent - ) => { - setFilterText(e.target.value); - }; + const { url } = useRouteMatch(); + const id = url.split("/")[2]; + const [projectId, setProjectId] = useState(id); const handleSubmit: (event: FormEvent) => void = async ( e: FormEvent @@ -35,20 +34,19 @@ export const NewTicketModal: FC = ({ show, handleClose, allUsers }) => { let newTicket = { title: title, description: description, - endingDate: endingDate, + endingDate: new Date(endingDate).toISOString(), creatorId: "20bf4b2a-7209-4826-96cd-29c2bc937a94", - projectId: 1 + projectId: parseInt(projectId) }; - console.log(newTicket); + // console.log(newTicket); const response: HttpResponse = await post( `${Constants.ticketsURI}`, newTicket ); - console.log(response.parsedBody); + // console.log(response.parsedBody); handleClose(); }; - useEffect(() => {}); return (
@@ -68,15 +66,16 @@ export const NewTicketModal: FC = ({ show, handleClose, allUsers }) => {
-
diff --git a/client/src/components/NewTicketTabRouter.tsx b/client/src/components/NewTicketTabRouter.tsx index 2985d7a..0b68000 100644 --- a/client/src/components/NewTicketTabRouter.tsx +++ b/client/src/components/NewTicketTabRouter.tsx @@ -1,14 +1,9 @@ import React, { FC } from "react"; -import { Route, useRouteMatch, Redirect } from "react-router-dom"; +import { useRouteMatch } from "react-router-dom"; import { TabRouterHeader } from "./TabRouterHeader"; -import { NewTicketForm } from "./NewTicketForm"; -import { MemberList } from "./MemberList"; -import { User } from "../types/User"; -import { Ticket } from "../types/Ticket"; interface IProps { tabNames: string[]; - users: User[]; description: string; setDescription: React.Dispatch>; title: string; @@ -19,7 +14,6 @@ interface IProps { export const NewTicketTabRouter: FC = ({ tabNames, - users, description, setDescription, title, @@ -33,22 +27,14 @@ export const NewTicketTabRouter: FC = ({
- - - - - - - - - + {/* */}
); diff --git a/client/src/components/ProgressBar.tsx b/client/src/components/ProgressBar.tsx index e6444f5..bf0ff90 100644 --- a/client/src/components/ProgressBar.tsx +++ b/client/src/components/ProgressBar.tsx @@ -16,7 +16,18 @@ export const ProgressBar: FC = ({ remainingDays }) => { 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 ( <>
diff --git a/client/src/components/TabRouter.tsx b/client/src/components/TabRouter.tsx index 0938467..8ecfaba 100644 --- a/client/src/components/TabRouter.tsx +++ b/client/src/components/TabRouter.tsx @@ -3,11 +3,11 @@ import { Route, useRouteMatch, Redirect } from "react-router-dom"; import { TabRouterHeader } from "./TabRouterHeader"; import { TicketList } from "./TicketList"; import { FileList } from "./AppFileList"; -import { ActivityList } from "./ActivityList"; +// import { ActivityList } from "./ActivityList"; import { Ticket } from "../types/Ticket"; import { AppFile } from "../types/AppFile"; import { Activity } from "../types/Activity"; -import { User } from "../types/User"; +import { Project } from "../types/Project"; interface IProps { tickets: Ticket[]; @@ -15,7 +15,7 @@ interface IProps { tabNames: string[]; files: AppFile[]; activities: Activity[]; - allUsers: User[]; + allProjects: Project[]; } export const TabRouter: FC = ({ @@ -23,7 +23,7 @@ export const TabRouter: FC = ({ tabNames, files, activities, - allUsers + allProjects }) => { const { url } = useRouteMatch(); @@ -35,16 +35,16 @@ export const TabRouter: FC = ({ - + - + {/* - + */}
); diff --git a/client/src/components/TicketList.tsx b/client/src/components/TicketList.tsx index e07c674..677af17 100644 --- a/client/src/components/TicketList.tsx +++ b/client/src/components/TicketList.tsx @@ -3,23 +3,22 @@ import { Ticket } from "../types/Ticket"; import { FloatingButton } from "./FloatingButton"; import { HorizontalCard } from "./HorizontalCard"; import { FilterBar } from "./FilterBar"; -import { User } from "../types/User"; import { HttpResponse } from "../types/HttpResponse"; import { put } from "../utils/http"; import { Constants } from "../utils/Constants"; import { NewTicketModal } from "./NewTicketModal"; +import { Project } from "../types/Project"; type TicketListProps = { tickets: Ticket[]; - users: User[]; + allProjects: Project[]; }; -export const TicketList: FC = ({ tickets, users }) => { +export const TicketList: FC = ({ tickets, allProjects }) => { const [filterText, setFilterText] = useState(""); const clearFilterText: (e: MouseEvent) => void = (e: MouseEvent) => { setFilterText(""); }; - // const archiveTicket = () => {}; const onClick: (e: MouseEvent) => void = (e: MouseEvent) => { e.preventDefault(); @@ -45,7 +44,7 @@ export const TicketList: FC = ({ tickets, users }) => { setShowNew(false); }} show={showNew} - allUsers={users} + allProjects={allProjects} />

Tickets

= ({ tickets, users }) => { clearFilterText={clearFilterText} />
-
+
    {filteredTickets.length === 0 ? ( @@ -76,7 +75,6 @@ export const TicketList: FC = ({ tickets, users }) => { {} ); }} - // archiveTicket={archiveTicket} /> )) )} diff --git a/client/src/components/UserTabRouter.tsx b/client/src/components/UserTabRouter.tsx index 7ee89ae..d68c8e2 100644 --- a/client/src/components/UserTabRouter.tsx +++ b/client/src/components/UserTabRouter.tsx @@ -4,6 +4,7 @@ import { TabRouterHeader } from "./TabRouterHeader"; import { ProjectList } from "./ProjectList"; import { Ticket } from "../types/Ticket"; import { Project } from "../types/Project"; +import { TicketList } from "./TicketList"; interface IProps { tabNames: string[]; @@ -25,9 +26,9 @@ export const UserTabRouter: FC = ({ tickets, tabNames, projects }) => { - {/* - - */} + + +
); diff --git a/client/src/components/UsersModal.tsx b/client/src/components/UsersModal.tsx index 9afcca1..4b4a971 100644 --- a/client/src/components/UsersModal.tsx +++ b/client/src/components/UsersModal.tsx @@ -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 { AvatarList } from "./AvatarList"; import { User } from "../types/User"; diff --git a/client/src/components/UsersModalEntry.tsx b/client/src/components/UsersModalEntry.tsx index e35501f..36a67f4 100644 --- a/client/src/components/UsersModalEntry.tsx +++ b/client/src/components/UsersModalEntry.tsx @@ -12,7 +12,7 @@ export const UsersModalEntry: FC = ({ user, setMembers, members }) => { return Boolean(members.find(m => m.id === id)); }; return ( -
+
+ {user.fullName}
); }; diff --git a/client/src/controllers/ProjectController.tsx b/client/src/controllers/ProjectController.tsx index 0252cd5..3d30d04 100644 --- a/client/src/controllers/ProjectController.tsx +++ b/client/src/controllers/ProjectController.tsx @@ -13,6 +13,7 @@ import { User } from "../types/User"; export const ProjectController: FC = () => { const [project, setProject] = useState({} as Project); const [allUsers, setAllUsers] = useState([]); + const [allProjects, setAllProjects] = useState([]); const [isLoading, setIsLoading] = useState(true); const [hasError, setHasError] = useState(false); const [error, setError] = useState(""); @@ -48,10 +49,25 @@ export const ProjectController: FC = () => { } } + async function httpGetAllProjects(): Promise { + try { + const response: HttpResponse = await get( + `${Constants.projectsURI}` + ); + if (response.parsedBody !== undefined) { + setAllProjects((response.parsedBody as unknown) as Project[]); + } + } catch (ex) { + setHasError(true); + setError(ex); + } + } + useEffect(() => { if (id !== undefined) { httpGetProjects(id); httpGetAllUsers(); + httpGetAllProjects(); } else { setHasError(true); setError("Bad Request"); @@ -61,6 +77,6 @@ export const ProjectController: FC = () => { if (hasError) { return ; } - const viewModel = new ProjectVM(project, allUsers); + const viewModel = new ProjectVM(project, allUsers, allProjects); return isLoading ? : ; }; diff --git a/client/src/index.jsx b/client/src/index.jsx new file mode 100644 index 0000000..6668661 --- /dev/null +++ b/client/src/index.jsx @@ -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( + + + , + document.getElementById("root") +); + +serviceWorker.unregister(); diff --git a/client/src/index.tsx b/client/src/index.tsx deleted file mode 100644 index 8780a3a..0000000 --- a/client/src/index.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import React from "react"; -import ReactDOM from "react-dom"; -import App from "./App"; -import * as serviceWorker from "./serviceWorker"; - -ReactDOM.render(, document.getElementById("root")); - -serviceWorker.unregister(); diff --git a/client/src/pages/Layout.tsx b/client/src/pages/Layout.tsx index 7cc1b3f..cf9a792 100644 --- a/client/src/pages/Layout.tsx +++ b/client/src/pages/Layout.tsx @@ -1,11 +1,14 @@ import React, { FC } from "react"; import { AppRouter } from "../utils/router"; +import { NavBar } from "../components/Navbar"; const Layout: FC = () => { return ( <> - {/* - */} +
+ +
+ {/* */} {/*
*/} diff --git a/client/src/pages/ProjectPage.tsx b/client/src/pages/ProjectPage.tsx index 16ac841..464d0e4 100644 --- a/client/src/pages/ProjectPage.tsx +++ b/client/src/pages/ProjectPage.tsx @@ -13,6 +13,7 @@ interface IProps { export const ProjectPage: FC = ({ viewModel }) => { const { + // id, title, description, users, @@ -23,10 +24,11 @@ export const ProjectPage: FC = ({ viewModel }) => { ticketsTotalCount, remainingDays, files, - activities + activities, + allProjects } = viewModel; - const tabNames: string[] = ["Tickets", "Files", "Activity"]; + const tabNames: string[] = ["Tickets", "Files"]; //, "Activity"]; const [showModal, setShowModal] = useState(false); return ( @@ -59,7 +61,7 @@ export const ProjectPage: FC = ({ viewModel }) => { tickets={tickets} files={files} activities={activities} - allUsers={allUsers} + allProjects={allProjects} />
diff --git a/client/src/pages/UserPage.tsx b/client/src/pages/UserPage.tsx index 6641aad..2b908af 100644 --- a/client/src/pages/UserPage.tsx +++ b/client/src/pages/UserPage.tsx @@ -23,10 +23,6 @@ export const UserPage: FC = ({ viewModel }) => { tickets={tickets} /> - {/* // - // - // - // */} ); }; diff --git a/client/src/utils/history.ts b/client/src/utils/history.ts new file mode 100644 index 0000000..6a454a1 --- /dev/null +++ b/client/src/utils/history.ts @@ -0,0 +1,2 @@ +import { createBrowserHistory } from "history"; +export default createBrowserHistory; diff --git a/client/src/utils/http.ts b/client/src/utils/http.ts index e3bc88e..d8f02fc 100644 --- a/client/src/utils/http.ts +++ b/client/src/utils/http.ts @@ -58,5 +58,5 @@ const headers: Headers = new Headers({ Accept: "application/json", "Content-Type": "application/json", 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" }); diff --git a/client/tsconfig.json b/client/tsconfig.json index f2850b7..af10394 100644 --- a/client/tsconfig.json +++ b/client/tsconfig.json @@ -1,11 +1,7 @@ { "compilerOptions": { "target": "es5", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], + "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, @@ -19,7 +15,5 @@ "noEmit": true, "jsx": "react" }, - "include": [ - "src" - ] + "include": ["src"] }