mirror of
https://github.com/rjNemo/ticket_manager
synced 2026-06-12 11:46:40 +00:00
Added UserTabRouter, UserHeader,ProjectList,Avatar components. UserPage Layout done. UserVM updated. Added User/:id route to Router
This commit is contained in:
parent
86fd097481
commit
9e89955b1c
15 changed files with 266 additions and 28 deletions
|
|
@ -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;
|
||||||
|
|
@ -24,7 +24,7 @@ export default class ProjectVM {
|
||||||
public remainingDays: number;
|
public remainingDays: number;
|
||||||
|
|
||||||
public constructor(project: Project, allUsers: User[]) {
|
public constructor(project: Project, allUsers: User[]) {
|
||||||
this.id = project.id;
|
// 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;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,19 @@
|
||||||
export class UserVM {
|
import { Project } from "../types/Project";
|
||||||
public Id?: number;
|
import { Ticket } from "../types/Ticket";
|
||||||
|
import { User } from "../types/User";
|
||||||
|
|
||||||
public constructor() {}
|
export class UserVM {
|
||||||
|
public fullName: string;
|
||||||
|
public presentation: string;
|
||||||
|
public picture: string;
|
||||||
|
public projects: Project[];
|
||||||
|
public tickets: Ticket[];
|
||||||
|
|
||||||
|
public constructor(user: User) {
|
||||||
|
this.fullName = user.fullName;
|
||||||
|
this.presentation = user.presentation;
|
||||||
|
this.picture = user.picture;
|
||||||
|
this.projects = user.projects;
|
||||||
|
this.tickets = user.tickets;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import React, { FC } from "react";
|
import React, { FC } from "react";
|
||||||
import { Activity } from "../types/Activity";
|
import { Activity } from "../types/Activity";
|
||||||
import { act } from "react-dom/test-utils";
|
|
||||||
|
|
||||||
type IProps = {
|
type IProps = {
|
||||||
activities: Activity[];
|
activities: Activity[];
|
||||||
|
|
|
||||||
12
client/src/components/Avatar.tsx
Normal file
12
client/src/components/Avatar.tsx
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import React, { FC } from "react";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
picture: string;
|
||||||
|
}
|
||||||
|
export const Avatar: FC<IProps> = ({ picture }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<img className="circle" src={picture} height="100vh" width="100vh" />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
85
client/src/components/ProjectList.tsx
Normal file
85
client/src/components/ProjectList.tsx
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
import React, { FC, useState, ChangeEvent, MouseEvent } from "react";
|
||||||
|
import { Ticket } from "../types/Ticket";
|
||||||
|
import { FloatingButton } from "./FloatingButton";
|
||||||
|
import { HorizontalCard } from "./HorizontalCard";
|
||||||
|
import { FilterBar } from "./FilterBar";
|
||||||
|
import { put } from "../utils/http";
|
||||||
|
import { HttpResponse } from "../types/HttpResponse";
|
||||||
|
import { Constants } from "../utils/Constants";
|
||||||
|
import { NewTicketModal } from "./NewTicketModal";
|
||||||
|
import { Project } from "../types/Project";
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
projects: Project[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ProjectList: FC<IProps> = ({ projects }) => {
|
||||||
|
const [filterText, setFilterText] = useState<string>("");
|
||||||
|
const clearFilterText: (e: MouseEvent) => void = (e: MouseEvent) => {
|
||||||
|
setFilterText("");
|
||||||
|
};
|
||||||
|
// const archiveTicket = () => {};
|
||||||
|
|
||||||
|
const onClick: (e: MouseEvent) => void = (e: MouseEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setShowNew(true);
|
||||||
|
};
|
||||||
|
const handleChange: (e: ChangeEvent<HTMLInputElement>) => void = (
|
||||||
|
e: ChangeEvent<HTMLInputElement>
|
||||||
|
) => {
|
||||||
|
setFilterText(e.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const [showNew, setShowNew] = useState(false);
|
||||||
|
let filteredTickets = projects.filter(
|
||||||
|
t =>
|
||||||
|
t.status !== "Done" &&
|
||||||
|
t.title.toLowerCase().includes(filterText.toLowerCase())
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="row valign-wrapper">
|
||||||
|
<NewTicketModal
|
||||||
|
handleClose={() => {
|
||||||
|
setShowNew(false);
|
||||||
|
}}
|
||||||
|
show={showNew}
|
||||||
|
/>
|
||||||
|
<h3>Projects</h3>
|
||||||
|
<FloatingButton
|
||||||
|
color="indigo lighten-1"
|
||||||
|
size="small"
|
||||||
|
onClick={onClick}
|
||||||
|
/>
|
||||||
|
<FilterBar
|
||||||
|
filterText={filterText}
|
||||||
|
handleChange={handleChange}
|
||||||
|
clearFilterText={clearFilterText}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="col s12 grey">
|
||||||
|
<ul>
|
||||||
|
{filteredTickets.length === 0 ? (
|
||||||
|
<HorizontalCard />
|
||||||
|
) : (
|
||||||
|
filteredTickets.map((t: Ticket) => (
|
||||||
|
<HorizontalCard
|
||||||
|
key={t.id}
|
||||||
|
title={t.title}
|
||||||
|
remainingDays={t.endingDate}
|
||||||
|
validateTicket={async (e: MouseEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
await put<HttpResponse<Ticket>>(
|
||||||
|
`${Constants.ticketsURI}/${t.id}/closed`,
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
// archiveTicket={archiveTicket}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -65,7 +65,7 @@ export const TicketList: FC<TicketListProps> = ({ tickets }) => {
|
||||||
<HorizontalCard
|
<HorizontalCard
|
||||||
key={t.id}
|
key={t.id}
|
||||||
title={t.title}
|
title={t.title}
|
||||||
remainingDays={t.plannedEnding}
|
remainingDays={t.endingDate}
|
||||||
validateTicket={async (e: MouseEvent) => {
|
validateTicket={async (e: MouseEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
await put<HttpResponse<Ticket>>(
|
await put<HttpResponse<Ticket>>(
|
||||||
|
|
|
||||||
21
client/src/components/UserHeader.tsx
Normal file
21
client/src/components/UserHeader.tsx
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import React, { FC } from "react";
|
||||||
|
import { Header } from "../components/Header";
|
||||||
|
import { Avatar } from "../components/Avatar";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
fullName: string;
|
||||||
|
presentation: string;
|
||||||
|
picture: string;
|
||||||
|
}
|
||||||
|
export const UserHeader: FC<IProps> = ({ fullName, presentation, picture }) => {
|
||||||
|
return (
|
||||||
|
<div className="row valign-wrapper">
|
||||||
|
<div className="col s2">
|
||||||
|
<Avatar picture={picture} />
|
||||||
|
</div>
|
||||||
|
<div className="col s10">
|
||||||
|
<Header title={fullName} description={presentation} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
38
client/src/components/UserTabRouter.tsx
Normal file
38
client/src/components/UserTabRouter.tsx
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
import React, { FC } from "react";
|
||||||
|
import { TabRouterHeader } from "./TabRouterHeader";
|
||||||
|
import { TicketList } from "./TicketList";
|
||||||
|
|
||||||
|
import { Ticket } from "../types/Ticket";
|
||||||
|
|
||||||
|
import { Route, useRouteMatch, Redirect } from "react-router-dom";
|
||||||
|
|
||||||
|
import { Project } from "../types/Project";
|
||||||
|
import { ProjectList } from "./ProjectList";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
tabNames: string[];
|
||||||
|
tickets: Ticket[];
|
||||||
|
projects: Project[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const UserTabRouter: FC<IProps> = ({ tickets, tabNames, projects }) => {
|
||||||
|
const { url } = useRouteMatch();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="row">
|
||||||
|
<TabRouterHeader tabNames={tabNames} />
|
||||||
|
|
||||||
|
<Redirect from={url} to={`${url}/projects`} />
|
||||||
|
|
||||||
|
<Route path={`${url}/projects`}>
|
||||||
|
<ProjectList projects={projects} />
|
||||||
|
</Route>
|
||||||
|
|
||||||
|
<Route path={`${url}/tickets`}>
|
||||||
|
<TicketList tickets={tickets} />
|
||||||
|
</Route>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -3,12 +3,10 @@ import { Modal } from "./Modal";
|
||||||
import { AvatarList } from "./AvatarList";
|
import { AvatarList } from "./AvatarList";
|
||||||
import { User } from "../types/User";
|
import { User } from "../types/User";
|
||||||
import { FilterBar } from "./FilterBar";
|
import { FilterBar } from "./FilterBar";
|
||||||
import { HttpResponse } from "../types/HttpResponse";
|
import { patch } from "../utils/http";
|
||||||
import { get, put, patch } from "../utils/http";
|
|
||||||
import { Constants } from "../utils/Constants";
|
import { Constants } from "../utils/Constants";
|
||||||
import { UsersModalEntry } from "./UsersModalEntry";
|
import { UsersModalEntry } from "./UsersModalEntry";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import _ from "underscore";
|
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
show: boolean;
|
show: boolean;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import React, { FC } from "react";
|
import React, { FC } from "react";
|
||||||
import { User } from "../types/User";
|
import { User } from "../types/User";
|
||||||
import _ from "underscore";
|
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
setMembers: React.Dispatch<React.SetStateAction<User[]>>;
|
setMembers: React.Dispatch<React.SetStateAction<User[]>>;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,62 @@
|
||||||
import React, { FC } from "react";
|
import React, { FC, useState, useEffect } from "react";
|
||||||
import { UserPage } from "../pages/UserPage";
|
import { UserPage } from "../pages/UserPage";
|
||||||
|
import { UserVM } from "../VM/UserVM";
|
||||||
|
import { User } from "../types/User";
|
||||||
|
import { AppFile } from "../types/AppFile";
|
||||||
|
import { Activity } from "../types/Activity";
|
||||||
|
import { Ticket } from "../types/Ticket";
|
||||||
|
import { Preloader } from "../components/Preloader";
|
||||||
|
|
||||||
export const UserController: FC = () => {
|
export const UserController: FC = () => {
|
||||||
return <UserPage />;
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
|
||||||
|
const user: User = {
|
||||||
|
id: "resldsm,dgd",
|
||||||
|
firstName: "David",
|
||||||
|
lastName: "Whittaker",
|
||||||
|
fullName: "David Whittaker",
|
||||||
|
presentation: "Interface designer and front-end developer",
|
||||||
|
creationDate: new Date().toDateString(),
|
||||||
|
email: "dw@mail.au",
|
||||||
|
phone: "0998765432",
|
||||||
|
picture: require("../images/user_1.jpg"),
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
title: "Project Title",
|
||||||
|
description: "What is it about",
|
||||||
|
progression: 25,
|
||||||
|
creationDate: new Date().toDateString(),
|
||||||
|
endingDate: "2020-02-17 15:51:02.787373",
|
||||||
|
status: "Todo",
|
||||||
|
manager: {} as User,
|
||||||
|
users: [] as User[],
|
||||||
|
tickets: [] as Ticket[],
|
||||||
|
files: [] as AppFile[],
|
||||||
|
activities: [] as Activity[]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
tickets: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
title: "Client objective meeting",
|
||||||
|
description: "Client objective meeting",
|
||||||
|
endingDate: "2020-02-17 15:51:02.787373",
|
||||||
|
status: "Done"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
title: "Assemble Outcomes Report for client",
|
||||||
|
description: "Assemble Outcomes Report for client",
|
||||||
|
endingDate: "2020-02-27 15:51:02.787373",
|
||||||
|
status: "To Do"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
activities: []
|
||||||
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
setTimeout(() => setIsLoading(false), 1000);
|
||||||
|
});
|
||||||
|
const viewModel = new UserVM(user);
|
||||||
|
return isLoading ? <Preloader /> : <UserPage viewModel={viewModel} />;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,32 @@
|
||||||
import React, { FC } from "react";
|
import React, { FC } from "react";
|
||||||
import { Header } from "../components/Header";
|
import { UserVM } from "../VM/UserVM";
|
||||||
|
import { UserHeader } from "../components/UserHeader";
|
||||||
|
import { UserTabRouter } from "../components/UserTabRouter";
|
||||||
|
|
||||||
export const UserPage: FC = () => {
|
interface IProps {
|
||||||
|
viewModel: UserVM;
|
||||||
|
}
|
||||||
|
export const UserPage: FC<IProps> = ({ viewModel }) => {
|
||||||
|
const { fullName, presentation, picture, projects, tickets } = viewModel;
|
||||||
|
const tabNames: string[] = ["Projects", "Tickets"];
|
||||||
return (
|
return (
|
||||||
<Header title = "Brand Concept and Design" description = "Research, ideate and present brand concepts for client consideration"/>
|
<div className="section">
|
||||||
// <TabView>
|
<div className="container">
|
||||||
|
<UserHeader
|
||||||
|
picture={picture}
|
||||||
|
fullName={fullName}
|
||||||
|
presentation={presentation}
|
||||||
|
/>
|
||||||
|
<UserTabRouter
|
||||||
|
tabNames={tabNames}
|
||||||
|
projects={projects}
|
||||||
|
tickets={tickets}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/* // <TabView>
|
||||||
// <CardList>
|
// <CardList>
|
||||||
// <CardList>
|
// <CardList>
|
||||||
// </TabView>
|
// </TabView> */}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,5 +3,5 @@ export interface Ticket {
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
status: string;
|
status: string;
|
||||||
plannedEnding: string;
|
endingDate: string;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ export interface User {
|
||||||
presentation: string;
|
presentation: string;
|
||||||
email: string;
|
email: string;
|
||||||
phone: string;
|
phone: string;
|
||||||
createdAt: string;
|
creationDate: string;
|
||||||
picture: string;
|
picture: string;
|
||||||
activities: Activity[];
|
activities: Activity[];
|
||||||
projects: Project[];
|
projects: Project[];
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import * as creacteHistory from "history";
|
||||||
import { ProjectController } from "../controllers/ProjectController";
|
import { ProjectController } from "../controllers/ProjectController";
|
||||||
import { NotFoundPage } from "../pages/NotFoundPage";
|
import { NotFoundPage } from "../pages/NotFoundPage";
|
||||||
import { TestPage } from "../pages/TestPage";
|
import { TestPage } from "../pages/TestPage";
|
||||||
// import { UserController } from "../controllers/UserController";
|
import { UserController } from "../controllers/UserController";
|
||||||
// import { TicketController } from "../controllers/TicketController";
|
// import { TicketController } from "../controllers/TicketController";
|
||||||
|
|
||||||
export const history = creacteHistory.createBrowserHistory();
|
export const history = creacteHistory.createBrowserHistory();
|
||||||
|
|
@ -28,24 +28,20 @@ export const AppRouter = () => {
|
||||||
|
|
||||||
{/* <Route path="/">
|
{/* <Route path="/">
|
||||||
<HomeController />
|
<HomeController />
|
||||||
</Route>
|
</Route> */}
|
||||||
<Route path="/users/:id">
|
<Route path="/users/:id">
|
||||||
<UserController />
|
<UserController />
|
||||||
</Route> */}
|
</Route>
|
||||||
<Route path="/projects/:id">
|
<Route path="/projects/:id">
|
||||||
<ProjectController />
|
<ProjectController />
|
||||||
</Route>
|
</Route>
|
||||||
{/* <Route path="/tickets/:id">
|
{/* <Route path="/tickets/:id">
|
||||||
<TicketController />
|
<TicketController />
|
||||||
</Route> */}
|
</Route> */}
|
||||||
|
|
||||||
<Route path="/404">
|
<Route path="/404">
|
||||||
<NotFoundPage />
|
<NotFoundPage />
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
{/* <Route path="*">
|
|
||||||
<Redirect to="/error" />
|
|
||||||
</Route> */}
|
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
</Router>
|
</Router>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue