mirror of
https://github.com/rjNemo/ticket_manager
synced 2026-06-06 00:36:39 +00:00
pull last react
This commit is contained in:
commit
f12f5f0187
26 changed files with 362 additions and 280 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -1,9 +1,8 @@
|
||||||
.vs/
|
.vs/
|
||||||
.vscode/
|
.vscode/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
bin/
|
**bin/
|
||||||
obj/
|
**obj/
|
||||||
app.db*
|
|
||||||
Data/Interfaces
|
Data/Interfaces
|
||||||
Data/UnitOfWork.cs
|
Data/UnitOfWork.cs
|
||||||
Data/*Repository.cs
|
Data/*Repository.cs
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ namespace TicketManager.Controllers
|
||||||
return await _context.AppUsers
|
return await _context.AppUsers
|
||||||
.Include(u => u.Assignments)
|
.Include(u => u.Assignments)
|
||||||
.ThenInclude(a => a.Project)
|
.ThenInclude(a => a.Project)
|
||||||
|
.ThenInclude(p => p.Tickets)
|
||||||
.Include(u => u.Activities)
|
.Include(u => u.Activities)
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Select(u => new AppUserDTO(u))
|
.Select(u => new AppUserDTO(u))
|
||||||
|
|
@ -68,6 +69,7 @@ namespace TicketManager.Controllers
|
||||||
var user = await _context.AppUsers
|
var user = await _context.AppUsers
|
||||||
.Include(u => u.Assignments)
|
.Include(u => u.Assignments)
|
||||||
.ThenInclude(a => a.Project)
|
.ThenInclude(a => a.Project)
|
||||||
|
.ThenInclude(p => p.Tickets)
|
||||||
.Include(u => u.Activities)
|
.Include(u => u.Activities)
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.FirstOrDefaultAsync(u => u.Id == id);
|
.FirstOrDefaultAsync(u => u.Id == id);
|
||||||
|
|
@ -80,12 +82,12 @@ namespace TicketManager.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the specific project with Id.
|
/// Updates the specific user with Id.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Sample request:
|
/// Sample request:
|
||||||
///
|
///
|
||||||
/// PUT: api/v1/Projects/3
|
/// PUT: api/v1/Users/3
|
||||||
/// {
|
/// {
|
||||||
/// "id": "357727fd-5262-4522-b8a3-38271d43de84",
|
/// "id": "357727fd-5262-4522-b8a3-38271d43de84",
|
||||||
/// "firstName": "Thomas",
|
/// "firstName": "Thomas",
|
||||||
|
|
@ -97,7 +99,7 @@ namespace TicketManager.Controllers
|
||||||
///
|
///
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <response code="204">Request was succesful but no content is changed</response>
|
/// <response code="204">Request was succesful but no content is changed</response>
|
||||||
/// <response code="404">If the required project is null</response>
|
/// <response code="404">If the required User is null</response>
|
||||||
[HttpPut("{id}")]
|
[HttpPut("{id}")]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
|
@ -129,12 +131,12 @@ namespace TicketManager.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a project.
|
/// Creates a User.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Sample request:
|
/// Sample request:
|
||||||
///
|
///
|
||||||
/// POST: api/v1/Projects/
|
/// POST: api/v1/Users/
|
||||||
/// {
|
/// {
|
||||||
/// "firstName": "Thomas",
|
/// "firstName": "Thomas",
|
||||||
/// "lastName": "Price",
|
/// "lastName": "Price",
|
||||||
|
|
@ -144,7 +146,7 @@ namespace TicketManager.Controllers
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <response code="201">Returns the created project</response>
|
/// <response code="201">Returns the created User</response>
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[ProducesResponseType(StatusCodes.Status201Created)]
|
[ProducesResponseType(StatusCodes.Status201Created)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
|
@ -174,15 +176,15 @@ namespace TicketManager.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deletes the project identified by its Id
|
/// Deletes the User identified by its Id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Sample request:
|
/// Sample request:
|
||||||
///
|
///
|
||||||
/// DELETE: api/v1/Projects/5
|
/// DELETE: api/v1/Users/5
|
||||||
///
|
///
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <response code="200">Returns the deleted project</response>
|
/// <response code="200">Returns the deleted User</response>
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
[HttpDelete("{id}")]
|
[HttpDelete("{id}")]
|
||||||
|
|
@ -205,6 +207,7 @@ namespace TicketManager.Controllers
|
||||||
var user = await _context.AppUsers
|
var user = await _context.AppUsers
|
||||||
.Include(u => u.Assignments)
|
.Include(u => u.Assignments)
|
||||||
.ThenInclude(a => a.Project)
|
.ThenInclude(a => a.Project)
|
||||||
|
.ThenInclude(p => p.Tickets)
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.FirstOrDefaultAsync(u => u.Id == id);
|
.FirstOrDefaultAsync(u => u.Id == id);
|
||||||
if (user == null)
|
if (user == null)
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,8 @@ namespace TicketManager.Controllers
|
||||||
{
|
{
|
||||||
return await _context.Tickets
|
return await _context.Tickets
|
||||||
.Include(t => t.Project)
|
.Include(t => t.Project)
|
||||||
|
.ThenInclude(p => p.Assignments)
|
||||||
|
.ThenInclude(a => a.User)
|
||||||
.Include(t => t.Files)
|
.Include(t => t.Files)
|
||||||
.Include(t => t.Activities)
|
.Include(t => t.Activities)
|
||||||
.Include(t => t.Notes)
|
.Include(t => t.Notes)
|
||||||
|
|
@ -42,6 +44,8 @@ namespace TicketManager.Controllers
|
||||||
{
|
{
|
||||||
var ticket = await _context.Tickets
|
var ticket = await _context.Tickets
|
||||||
.Include(t => t.Project)
|
.Include(t => t.Project)
|
||||||
|
.ThenInclude(p => p.Assignments)
|
||||||
|
.ThenInclude(a => a.User)
|
||||||
.Include(t => t.Files)
|
.Include(t => t.Files)
|
||||||
.Include(t => t.Activities)
|
.Include(t => t.Activities)
|
||||||
.Include(t => t.Notes)
|
.Include(t => t.Notes)
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,10 @@ namespace TicketManager.Models
|
||||||
public List<Ticket> GetTickets()
|
public List<Ticket> GetTickets()
|
||||||
{
|
{
|
||||||
var tickets = new List<Ticket>();
|
var tickets = new List<Ticket>();
|
||||||
GetProjects().ForEach(p => tickets.Concat(p.Tickets));
|
foreach (var p in GetProjects())
|
||||||
|
{
|
||||||
|
tickets.AddRange(p.Tickets);
|
||||||
|
}
|
||||||
return tickets;
|
return tickets;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,4 +50,5 @@
|
||||||
- [<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
|
- [x] Form validators
|
||||||
|
- [ ] Azure
|
||||||
|
|
|
||||||
BIN
app.db
Normal file
BIN
app.db
Normal file
Binary file not shown.
|
|
@ -1,5 +1,33 @@
|
||||||
export class TicketVM {
|
import { Ticket } from "../types/Ticket";
|
||||||
public Id?: number;
|
import { Project } from "../types/Project";
|
||||||
|
import { User } from "../types/User";
|
||||||
|
|
||||||
public constructor() {}
|
export class TicketVM {
|
||||||
|
public id: number;
|
||||||
|
public title: string;
|
||||||
|
public description: string;
|
||||||
|
public creationDate: string;
|
||||||
|
public endingDate: string;
|
||||||
|
public status: string;
|
||||||
|
public impact: string;
|
||||||
|
public difficulty: string;
|
||||||
|
public category: string;
|
||||||
|
public creatorId: string;
|
||||||
|
public project: Project;
|
||||||
|
public users: User[];
|
||||||
|
|
||||||
|
public constructor(ticket: Ticket) {
|
||||||
|
this.id = ticket.id;
|
||||||
|
this.title = ticket.title;
|
||||||
|
this.description = ticket.description;
|
||||||
|
this.creationDate = ticket.creationDate;
|
||||||
|
this.endingDate = ticket.endingDate;
|
||||||
|
this.status = ticket.status;
|
||||||
|
this.impact = ticket.impact;
|
||||||
|
this.difficulty = ticket.difficulty;
|
||||||
|
this.category = ticket.category;
|
||||||
|
this.creatorId = ticket.creatorId;
|
||||||
|
this.project = ticket.project;
|
||||||
|
this.users = ticket.users;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,34 @@
|
||||||
import { Project } from "../types/Project";
|
import { Project } from "../types/Project";
|
||||||
import { Ticket } from "../types/Ticket";
|
import { Ticket } from "../types/Ticket";
|
||||||
import { User } from "../types/User";
|
import { User } from "../types/User";
|
||||||
|
import { Activity } from "../types/Activity";
|
||||||
|
|
||||||
export class UserVM {
|
export class UserVM {
|
||||||
|
public id: string;
|
||||||
|
public firstName: string;
|
||||||
|
public lastName: string;
|
||||||
public fullName: string;
|
public fullName: string;
|
||||||
public presentation: string;
|
public presentation: string;
|
||||||
|
public email: string;
|
||||||
|
public phone: string;
|
||||||
|
public creationDate: string;
|
||||||
public picture: string;
|
public picture: string;
|
||||||
public projects: Project[];
|
public projects: Project[];
|
||||||
public tickets: Ticket[];
|
public tickets: Ticket[];
|
||||||
|
public activities: Activity[];
|
||||||
|
|
||||||
public constructor(user: User) {
|
public constructor(user: User) {
|
||||||
|
this.id = user.id;
|
||||||
|
this.firstName = user.firstName;
|
||||||
|
this.lastName = user.lastName;
|
||||||
this.fullName = user.fullName;
|
this.fullName = user.fullName;
|
||||||
this.presentation = user.presentation;
|
this.presentation = user.presentation;
|
||||||
|
this.email = user.email;
|
||||||
|
this.phone = user.phone;
|
||||||
|
this.creationDate = user.creationDate;
|
||||||
this.picture = user.picture;
|
this.picture = user.picture;
|
||||||
this.projects = user.projects;
|
this.projects = user.projects;
|
||||||
this.tickets = user.tickets;
|
this.tickets = user.tickets;
|
||||||
|
this.activities = user.activities;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import React, { FC } from "react";
|
import React, { FC } from "react";
|
||||||
import { User } from "../types/User";
|
import { User } from "../types/User";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
interface AvatarListProps {
|
interface AvatarListProps {
|
||||||
users: User[];
|
users: User[];
|
||||||
|
|
@ -11,14 +12,15 @@ export const AvatarList: FC<AvatarListProps> = ({ users }) => {
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{users.map((user: User, i: number) => (
|
{users.map((user: User, i: number) => (
|
||||||
<img
|
<Link to={`/users/${user.id}`} key={i}>
|
||||||
key={i}
|
<img
|
||||||
className="circle"
|
className="circle"
|
||||||
src={user.picture}
|
src={user.picture}
|
||||||
width="32vh"
|
width="32vh"
|
||||||
height="32vh"
|
height="32vh"
|
||||||
alt={user.fullName}
|
alt={user.fullName}
|
||||||
/>
|
/>
|
||||||
|
</Link>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,13 @@ interface IProps {
|
||||||
title?: string;
|
title?: string;
|
||||||
remainingDays?: string;
|
remainingDays?: string;
|
||||||
validateTicket?: (event: MouseEvent) => void;
|
validateTicket?: (event: MouseEvent) => void;
|
||||||
// archiveTicket: (event: MouseEvent) => void;
|
link?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const HorizontalCard: FC<IProps> = ({
|
export const HorizontalCard: FC<IProps> = ({
|
||||||
title,
|
title,
|
||||||
remainingDays,
|
remainingDays,
|
||||||
// archiveTicket,
|
link = "#",
|
||||||
validateTicket
|
validateTicket
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
|
|
@ -23,7 +23,7 @@ export const HorizontalCard: FC<IProps> = ({
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="card-title">
|
<div className="card-title">
|
||||||
<h6>
|
<h6>
|
||||||
<Link to="#">
|
<Link to={link}>
|
||||||
<b>{title ?? "Nothing to do"}</b>
|
<b>{title ?? "Nothing to do"}</b>
|
||||||
</Link>
|
</Link>
|
||||||
</h6>
|
</h6>
|
||||||
|
|
|
||||||
10
client/src/components/InputField.tsx
Normal file
10
client/src/components/InputField.tsx
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import React, { FC } from "react";
|
||||||
|
|
||||||
|
export const InputField: FC = () => {
|
||||||
|
return (
|
||||||
|
<div className="input-field">
|
||||||
|
<input id="email" type="text" className="validate" />
|
||||||
|
<label htmlFor="email">Email</label>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
23
client/src/components/LogInForm.tsx
Normal file
23
client/src/components/LogInForm.tsx
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
import React, { FC } from "react";
|
||||||
|
import { InputField } from "./InputField";
|
||||||
|
import { PasswordField } from "./PasswordField";
|
||||||
|
import { Button } from "./Button";
|
||||||
|
|
||||||
|
export const LogInForm: FC = () => {
|
||||||
|
return (
|
||||||
|
<div className="section col s10 offset-s1 white z-depth-1">
|
||||||
|
<div className="row ">
|
||||||
|
<div className="center ">
|
||||||
|
<h4>Login</h4>
|
||||||
|
<form className="col s10 offset-s1">
|
||||||
|
<InputField />
|
||||||
|
<PasswordField />
|
||||||
|
<Button color="indigo" size="large">
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
import React, { FC, useState, FormEvent } from "react";
|
import React, { FC, useState, FormEvent } from "react";
|
||||||
import { useParams, useRouteMatch } from "react-router-dom";
|
import { useRouteMatch } from "react-router-dom";
|
||||||
import { Modal } from "./Modal";
|
import { Modal } from "./Modal";
|
||||||
import { NewTicketForm } from "./NewTicketForm";
|
import { NewTicketForm } from "./NewTicketForm";
|
||||||
import { Ticket } from "../types/Ticket";
|
import { Ticket } from "../types/Ticket";
|
||||||
import { Project } from "../types/Project";
|
import { Project } from "../types/Project";
|
||||||
import { post } from "../utils/http";
|
import { post } from "../utils/http";
|
||||||
import { Constants } from "../utils/Constants";
|
import { Constants } from "../utils/Constants";
|
||||||
import { HttpResponse } from "../types/HttpResponse";
|
// import { HttpResponse } from "../types/HttpResponse";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
show: boolean;
|
show: boolean;
|
||||||
|
|
@ -38,12 +38,9 @@ export const NewTicketModal: FC<IProps> = ({
|
||||||
creatorId: "20bf4b2a-7209-4826-96cd-29c2bc937a94",
|
creatorId: "20bf4b2a-7209-4826-96cd-29c2bc937a94",
|
||||||
projectId: parseInt(projectId)
|
projectId: parseInt(projectId)
|
||||||
};
|
};
|
||||||
// console.log(newTicket);
|
|
||||||
const response: HttpResponse<Ticket> = await post<Ticket>(
|
// const response: HttpResponse<Ticket> =
|
||||||
`${Constants.ticketsURI}`,
|
await post<Ticket>(`${Constants.ticketsURI}`, newTicket);
|
||||||
newTicket
|
|
||||||
);
|
|
||||||
// console.log(response.parsedBody);
|
|
||||||
handleClose();
|
handleClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
10
client/src/components/PasswordField.tsx
Normal file
10
client/src/components/PasswordField.tsx
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import React, { FC } from "react";
|
||||||
|
|
||||||
|
export const PasswordField: FC = () => {
|
||||||
|
return (
|
||||||
|
<div className="input-field">
|
||||||
|
<input id="password" type="password" className="validate" />
|
||||||
|
<label htmlFor="password">Password</label>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
20
client/src/components/ProfileSelector.tsx
Normal file
20
client/src/components/ProfileSelector.tsx
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
import React, { FC } from "react";
|
||||||
|
import { HorizontalCard } from "./HorizontalCard";
|
||||||
|
import { Avatar } from "./Avatar";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
|
export const ProfileSelector: FC = () => {
|
||||||
|
return (
|
||||||
|
<div className="section col s10 offset-s1 white z-depth-1">
|
||||||
|
<div className="row ">
|
||||||
|
<div className="center ">
|
||||||
|
<h4>Select a profile</h4>
|
||||||
|
<Link to="/users/cd179eb7-3a54-4060-b22c-3e947bdffcbc">
|
||||||
|
<Avatar picture="https://vignette.wikia.nocookie.net/jamescameronsavatar/images/0/08/Neytiri_Profilbild.jpg/revision/latest/scale-to-width-down/250?cb=20100107164021&path-prefix=de" />
|
||||||
|
</Link>
|
||||||
|
<h5>Demo User</h5>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import React, { FC, useState, ChangeEvent, MouseEvent } from "react";
|
import React, { FC, useState, ChangeEvent, MouseEvent } from "react";
|
||||||
import { Ticket } from "../types/Ticket";
|
import { Ticket } from "../types/Ticket";
|
||||||
import { FloatingButton } from "./FloatingButton";
|
|
||||||
import { HorizontalCard } from "./HorizontalCard";
|
import { HorizontalCard } from "./HorizontalCard";
|
||||||
import { FilterBar } from "./FilterBar";
|
import { FilterBar } from "./FilterBar";
|
||||||
import { put } from "../utils/http";
|
import { put } from "../utils/http";
|
||||||
|
|
@ -17,19 +16,13 @@ export const ProjectList: FC<IProps> = ({ projects }) => {
|
||||||
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) => {
|
|
||||||
e.preventDefault();
|
|
||||||
setShowNew(true);
|
|
||||||
};
|
|
||||||
const handleChange: (e: ChangeEvent<HTMLInputElement>) => void = (
|
const handleChange: (e: ChangeEvent<HTMLInputElement>) => void = (
|
||||||
e: ChangeEvent<HTMLInputElement>
|
e: ChangeEvent<HTMLInputElement>
|
||||||
) => {
|
) => {
|
||||||
setFilterText(e.target.value);
|
setFilterText(e.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const [showNew, setShowNew] = useState(false);
|
|
||||||
let filteredTickets = projects.filter(
|
let filteredTickets = projects.filter(
|
||||||
t =>
|
t =>
|
||||||
t.status !== "Done" &&
|
t.status !== "Done" &&
|
||||||
|
|
@ -39,18 +32,13 @@ export const ProjectList: FC<IProps> = ({ projects }) => {
|
||||||
<>
|
<>
|
||||||
<div className="row valign-wrapper">
|
<div className="row valign-wrapper">
|
||||||
<h3>Projects</h3>
|
<h3>Projects</h3>
|
||||||
<FloatingButton
|
|
||||||
color="indigo lighten-1"
|
|
||||||
size="small"
|
|
||||||
onClick={onClick}
|
|
||||||
/>
|
|
||||||
<FilterBar
|
<FilterBar
|
||||||
filterText={filterText}
|
filterText={filterText}
|
||||||
handleChange={handleChange}
|
handleChange={handleChange}
|
||||||
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 />
|
||||||
|
|
@ -60,6 +48,7 @@ export const ProjectList: FC<IProps> = ({ projects }) => {
|
||||||
key={t.id}
|
key={t.id}
|
||||||
title={t.title}
|
title={t.title}
|
||||||
remainingDays={t.endingDate}
|
remainingDays={t.endingDate}
|
||||||
|
link={`/projects/${t.id}`}
|
||||||
validateTicket={async (e: MouseEvent) => {
|
validateTicket={async (e: MouseEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
await put<HttpResponse<Ticket>>(
|
await put<HttpResponse<Ticket>>(
|
||||||
|
|
@ -67,7 +56,6 @@ export const ProjectList: FC<IProps> = ({ projects }) => {
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
// archiveTicket={archiveTicket}
|
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,14 @@ import { Project } from "../types/Project";
|
||||||
type TicketListProps = {
|
type TicketListProps = {
|
||||||
tickets: Ticket[];
|
tickets: Ticket[];
|
||||||
allProjects: Project[];
|
allProjects: Project[];
|
||||||
|
addButton?: Boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TicketList: FC<TicketListProps> = ({ tickets, allProjects }) => {
|
export const TicketList: FC<TicketListProps> = ({
|
||||||
|
tickets,
|
||||||
|
allProjects,
|
||||||
|
addButton = true
|
||||||
|
}) => {
|
||||||
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("");
|
||||||
|
|
@ -47,11 +52,13 @@ export const TicketList: FC<TicketListProps> = ({ tickets, allProjects }) => {
|
||||||
allProjects={allProjects}
|
allProjects={allProjects}
|
||||||
/>
|
/>
|
||||||
<h3>Tickets</h3>
|
<h3>Tickets</h3>
|
||||||
<FloatingButton
|
{addButton && (
|
||||||
color="indigo lighten-3"
|
<FloatingButton
|
||||||
size="small"
|
color="indigo lighten-3"
|
||||||
onClick={onClick}
|
size="small"
|
||||||
/>
|
onClick={onClick}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<FilterBar
|
<FilterBar
|
||||||
filterText={filterText}
|
filterText={filterText}
|
||||||
handleChange={handleChange}
|
handleChange={handleChange}
|
||||||
|
|
@ -68,6 +75,7 @@ export const TicketList: FC<TicketListProps> = ({ tickets, allProjects }) => {
|
||||||
key={t.id}
|
key={t.id}
|
||||||
title={t.title}
|
title={t.title}
|
||||||
remainingDays={t.endingDate}
|
remainingDays={t.endingDate}
|
||||||
|
link={`/tickets/${t.id}`}
|
||||||
validateTicket={async (e: MouseEvent) => {
|
validateTicket={async (e: MouseEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
await put<HttpResponse<Ticket>>(
|
await put<HttpResponse<Ticket>>(
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ export const UserTabRouter: FC<IProps> = ({ tickets, tabNames, projects }) => {
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
<Route path={`${url}/tickets`}>
|
<Route path={`${url}/tickets`}>
|
||||||
<TicketList tickets={tickets} allProjects={[]} />
|
<TicketList tickets={tickets} allProjects={[]} addButton={false} />
|
||||||
</Route>
|
</Route>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,7 @@ export const ProjectController: FC = () => {
|
||||||
if (hasError) {
|
if (hasError) {
|
||||||
return <ErrorController error={error} />;
|
return <ErrorController error={error} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const viewModel = new ProjectVM(project, allUsers, allProjects);
|
const viewModel = new ProjectVM(project, allUsers, allProjects);
|
||||||
return isLoading ? <Preloader /> : <ProjectPage viewModel={viewModel} />;
|
return isLoading ? <Preloader /> : <ProjectPage viewModel={viewModel} />;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -1,90 +1,50 @@
|
||||||
import React, { FC, useState, useEffect } from "react";
|
import React, { FC, useState, useEffect } from "react";
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
import { UserPage } from "../pages/UserPage";
|
import { UserPage } from "../pages/UserPage";
|
||||||
import { UserVM } from "../VM/UserVM";
|
import { UserVM } from "../VM/UserVM";
|
||||||
import { User } from "../types/User";
|
import { User } from "../types/User";
|
||||||
import { AppFile } from "../types/AppFile";
|
import { HttpResponse } from "../types/HttpResponse";
|
||||||
import { Activity } from "../types/Activity";
|
|
||||||
import { Ticket } from "../types/Ticket";
|
|
||||||
import { Preloader } from "../components/Preloader";
|
import { Preloader } from "../components/Preloader";
|
||||||
|
import { get } from "../utils/http";
|
||||||
|
import { Constants } from "../utils/Constants";
|
||||||
|
import { ErrorController } from "./ErrorController";
|
||||||
|
|
||||||
export const UserController: FC = () => {
|
export const UserController: FC = () => {
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
const [user, setUser] = useState<User>({} as User);
|
||||||
|
const [hasError, setHasError] = useState(false);
|
||||||
|
const [error, setError] = useState("");
|
||||||
|
const { id } = useParams();
|
||||||
|
|
||||||
const user: User = {
|
async function httpGetUser(id: string): Promise<void> {
|
||||||
id: "resldsm,dgd",
|
try {
|
||||||
firstName: "David",
|
const response: HttpResponse<User> = await get<User>(
|
||||||
lastName: "Whittaker",
|
`${Constants.usersURI}/${id}`
|
||||||
fullName: "David Whittaker",
|
);
|
||||||
presentation: "Interface designer and front-end developer",
|
if (response.parsedBody !== undefined) {
|
||||||
creationDate: new Date().toDateString(),
|
setUser(response.parsedBody);
|
||||||
email: "dw@mail.au",
|
setIsLoading(false);
|
||||||
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[]
|
|
||||||
}
|
}
|
||||||
],
|
} catch (ex) {
|
||||||
tickets: [
|
console.error(ex);
|
||||||
{
|
setHasError(true);
|
||||||
id: 1,
|
setError(ex);
|
||||||
title: "Client objective meeting",
|
}
|
||||||
description: "Client objective meeting",
|
}
|
||||||
endingDate: "2020-02-17 15:51:02.787373",
|
|
||||||
status: "Done",
|
|
||||||
project: {
|
|
||||||
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[]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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",
|
|
||||||
project: {
|
|
||||||
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[]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
activities: []
|
|
||||||
};
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTimeout(() => setIsLoading(false), 1000);
|
if (id !== undefined) {
|
||||||
});
|
httpGetUser(id);
|
||||||
|
} else {
|
||||||
|
setHasError(true);
|
||||||
|
setError("Bad Request");
|
||||||
|
}
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
|
if (hasError) {
|
||||||
|
return <ErrorController error={error} />;
|
||||||
|
}
|
||||||
|
|
||||||
const viewModel = new UserVM(user);
|
const viewModel = new UserVM(user);
|
||||||
return isLoading ? <Preloader /> : <UserPage viewModel={viewModel} />;
|
return isLoading ? <Preloader /> : <UserPage viewModel={viewModel} />;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,21 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { LogInForm } from "../components/LogInForm";
|
||||||
|
import { ProfileSelector } from "../components/ProfileSelector";
|
||||||
|
|
||||||
export const HomePage: React.FC = () => {
|
export const HomePage: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<div className="App">
|
<div className="section">
|
||||||
<p>HomePage</p>
|
<div className="container center">
|
||||||
|
<h1 className="center">Ticket Manager</h1>
|
||||||
|
<div className="row">
|
||||||
|
<div className="col s6">
|
||||||
|
<ProfileSelector />
|
||||||
|
</div>
|
||||||
|
<div className="col s6">
|
||||||
|
<LogInForm />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,79 @@
|
||||||
import React, { FC } from "react";
|
import React, { FC } from "react";
|
||||||
import { Header } from "../components/Header";
|
import { Header } from "../components/Header";
|
||||||
import { AvatarList } from "../components/AvatarList";
|
import { AvatarList } from "../components/AvatarList";
|
||||||
import { ProgressBar } from "../components/ProgressBar";
|
import { TicketVM } from "../VM/TicketVM";
|
||||||
|
import { getRemainingdays } from "../utils/methods";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
viewModel: TicketVM;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TicketPage: FC<IProps> = ({ viewModel }) => {
|
||||||
|
const {
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
users,
|
||||||
|
endingDate,
|
||||||
|
project,
|
||||||
|
status,
|
||||||
|
category,
|
||||||
|
impact,
|
||||||
|
difficulty
|
||||||
|
} = viewModel;
|
||||||
|
const daysToEnd: number = getRemainingdays(endingDate);
|
||||||
|
// let notes: string = "";
|
||||||
|
|
||||||
export const TicketPage: FC = () => {
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="section">
|
||||||
<Header
|
<div className="container">
|
||||||
description="Research, ideate and present brand concepts for client consideration"
|
<Header title={title} description={description} />
|
||||||
title="Brand Concept and Design"
|
<AvatarList users={users} />
|
||||||
/>
|
|
||||||
{/* <AvatarList users={["../images/user_1.jpg", "../images/user_2.jpg"]} /> */}
|
<div className="row section">
|
||||||
<ProgressBar value={60} />
|
<div className="col s9">
|
||||||
{/* // <TabView>
|
<h5>
|
||||||
// <ChildTicket/>
|
<b>In project: </b>{" "}
|
||||||
// <ChildFile/>
|
<Link to={`/projects/${project.id}`}>{project.title}</Link>
|
||||||
// <ChildActivity/>
|
</h5>
|
||||||
// </TabView>
|
</div>
|
||||||
// <Notes/> */}
|
<div className="col s3">
|
||||||
</>
|
<i className="left material-icons">timer</i>
|
||||||
|
<span>Due in {daysToEnd} days</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="section white center">
|
||||||
|
<div className="chip">
|
||||||
|
<span className="indigo-text">Status: </span> {status}
|
||||||
|
{/* <i className="close material-icons">close</i> */}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="chip">
|
||||||
|
<span className="orange-text">Category: </span> {category}
|
||||||
|
{/* <i className="close material-icons">close</i> */}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="chip">
|
||||||
|
<span className="green-text">Impact: </span> {impact}
|
||||||
|
{/* <i className="close material-icons">close</i> */}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="chip">
|
||||||
|
<span className="red-text">Difficulty: </span> {difficulty}
|
||||||
|
{/* <i className="close material-icons">close</i> */}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* <textarea
|
||||||
|
id="notes"
|
||||||
|
className="materialize-textarea validate"
|
||||||
|
value={notes}
|
||||||
|
// onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) =>
|
||||||
|
// setDescription(e.target.value)
|
||||||
|
// }
|
||||||
|
></textarea> */}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,17 @@
|
||||||
import { Project } from "./Project";
|
import { Project } from "./Project";
|
||||||
|
import { User } from "./User";
|
||||||
|
|
||||||
export interface Ticket {
|
export interface Ticket {
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
status: string;
|
creationDate: string;
|
||||||
endingDate: string;
|
endingDate: string;
|
||||||
|
status: string;
|
||||||
|
impact: string;
|
||||||
|
difficulty: string;
|
||||||
|
category: string;
|
||||||
|
creatorId: string;
|
||||||
project: Project;
|
project: Project;
|
||||||
|
users: User[];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.eyJpc3MiOiJodHRwczovL2Rldi1meWpydm9oeC5hdXRoMC5jb20vIiwic3ViIjoiR3dlZTlGUnN3ejNWNE5vZFVRTjJIcjJyQjJTMDI1UmZAY2xpZW50cyIsImF1ZCI6Imh0dHBzOi8vbG9jYWxob3N0OjUwMDEvYXBpL1YxLyIsImlhdCI6MTU4Mzc0MzE1OSwiZXhwIjoxNTgzODI5NTU5LCJhenAiOiJHd2VlOUZSc3d6M1Y0Tm9kVVFOMkhyMnJCMlMwMjVSZiIsImd0eSI6ImNsaWVudC1jcmVkZW50aWFscyJ9.G1PTca14OLP1Ty3YFJaW0n_LRiG8ib1Nw_OcDH4HgWKgK6sZlIQ6RuAxiJjE0X6c7sxiTmtVhsRF-dqnfXuQcMWouj-8nd5C1LdVco3D08t0megehCeSZ4ffjjDQXQjWwvahTfuxJhXFVwK1M7F-dtLnk6lUJC9EjRg5qwtT5pa_js47RawHZWSfm-h6A1tVVs_cjy4I4xQLThKYQ3bTR5fYGNjnfa2AybE9Dv2Q0JehSDqSt1zCyIAnZTxJmgUWPW7Rp2xnmHf6Usmpy_9P5HbnnuRXZ1IT3dllS6OMIki3xyTeJDlCxhI37Ib1Dtx6PaPbFAP88QiRy2hwSi_S6w"
|
"Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik1UWkNSRFEzUkRnd1FUQXlNRFExTmtOQ09UQXlSamhGTURaRU1Ea3pNRGxHUkRrelFqZENSZyJ9.eyJpc3MiOiJodHRwczovL2Rldi1meWpydm9oeC5hdXRoMC5jb20vIiwic3ViIjoiR3dlZTlGUnN3ejNWNE5vZFVRTjJIcjJyQjJTMDI1UmZAY2xpZW50cyIsImF1ZCI6Imh0dHBzOi8vbG9jYWxob3N0OjUwMDEvYXBpL1YxLyIsImlhdCI6MTU4NDE5ODQ4MCwiZXhwIjoxNTg0Mjg0ODgwLCJhenAiOiJHd2VlOUZSc3d6M1Y0Tm9kVVFOMkhyMnJCMlMwMjVSZiIsImd0eSI6ImNsaWVudC1jcmVkZW50aWFscyJ9.I1D49ILGBLhnq9biIA0y6Ra93zTKRDJI_rfGvU05MtT1zkI1ZliX9P-7LyKeWBv8tPonB6gT12lJiai_GHBET8kKbXNqwfVvDJ3eqYK-TtTqfL65RfWL9tQfQybHbfuF9M0oiXMqWMqmsc5Umpp4a3bLTQgwkUEKxcdMm84L7zoaqMycns4mFojWpQJKfPa64oZFDIXYy6hPDXcX50Djuk1m-aqMhtpmqkZvPfwEjvtEtGGCTOJHV7uugn3r8Wk4HX02ShrV676GICE1Yw7eHufAbY7yvHz3ImZ1cfEVrRbbijPA2vogXd5RmqNyindDDlT1Y_C80U0DyvhS7P7apQ"
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,12 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {
|
import { Router, Route, Switch } from "react-router-dom";
|
||||||
Router,
|
|
||||||
Route,
|
|
||||||
Switch
|
|
||||||
// Redirect
|
|
||||||
//Link, NavLink
|
|
||||||
} from "react-router-dom";
|
|
||||||
import * as creacteHistory from "history";
|
import * as creacteHistory from "history";
|
||||||
// import { TicketPage } from "../pages/TicketPage";
|
import { HomeController } from "../controllers/HomeController";
|
||||||
// import { HomeController } from "../controllers/HomeController";
|
|
||||||
import { ProjectController } from "../controllers/ProjectController";
|
import { ProjectController } from "../controllers/ProjectController";
|
||||||
import { NotFoundPage } from "../pages/NotFoundPage";
|
|
||||||
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";
|
||||||
|
import { NotFoundPage } from "../pages/NotFoundPage";
|
||||||
|
// import { TestPage } from "../pages/TestPage";
|
||||||
|
|
||||||
export const history = creacteHistory.createBrowserHistory();
|
export const history = creacteHistory.createBrowserHistory();
|
||||||
|
|
||||||
|
|
@ -22,24 +15,27 @@ export const AppRouter = () => {
|
||||||
<Router history={history}>
|
<Router history={history}>
|
||||||
<div className="grey lighten-3">
|
<div className="grey lighten-3">
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route exact path="/">
|
{/* <Route exact path="/">
|
||||||
<TestPage />
|
<TestPage />
|
||||||
|
</Route> */}
|
||||||
|
|
||||||
|
<Route exact path="/">
|
||||||
|
<HomeController />
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
{/* <Route path="/">
|
|
||||||
<HomeController />
|
|
||||||
</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">
|
|
||||||
<TicketController />
|
|
||||||
</Route> */}
|
|
||||||
|
|
||||||
<Route path="/401">
|
<Route path="/tickets/:id">
|
||||||
|
<TicketController />
|
||||||
|
</Route>
|
||||||
|
|
||||||
|
<Route path="/404">
|
||||||
<NotFoundPage />
|
<NotFoundPage />
|
||||||
</Route>
|
</Route>
|
||||||
</Switch>
|
</Switch>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue