mirror of
https://github.com/rjNemo/ticket_manager
synced 2026-06-12 11:46:40 +00:00
UI project page
This commit is contained in:
parent
9e47a6e1d8
commit
4710f0a551
16 changed files with 811 additions and 243 deletions
124
Controllers/AssignmentsController.cs
Normal file
124
Controllers/AssignmentsController.cs
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using TicketManager.Data;
|
||||||
|
using TicketManager.Models;
|
||||||
|
|
||||||
|
namespace TicketManager.Controllers
|
||||||
|
{
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
public class AssignmentsController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly AppDbContext _context;
|
||||||
|
|
||||||
|
public AssignmentsController(AppDbContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET: api/Assignments
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<ActionResult<IEnumerable<Assignment>>> GetAssignments()
|
||||||
|
{
|
||||||
|
return await _context.Assignments.ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET: api/Assignments/5
|
||||||
|
[HttpGet("{id}")]
|
||||||
|
public async Task<ActionResult<Assignment>> GetAssignment(int id)
|
||||||
|
{
|
||||||
|
var assignment = await _context.Assignments.FindAsync(id);
|
||||||
|
|
||||||
|
if (assignment == null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
return assignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PUT: api/Assignments/5
|
||||||
|
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
|
||||||
|
// more details see https://aka.ms/RazorPagesCRUD.
|
||||||
|
[HttpPut("{id}")]
|
||||||
|
public async Task<IActionResult> PutAssignment(int id, Assignment assignment)
|
||||||
|
{
|
||||||
|
if (id != assignment.ProjectId)
|
||||||
|
{
|
||||||
|
return BadRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
_context.Entry(assignment).State = EntityState.Modified;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
catch (DbUpdateConcurrencyException)
|
||||||
|
{
|
||||||
|
if (!AssignmentExists(id))
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST: api/Assignments
|
||||||
|
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
|
||||||
|
// more details see https://aka.ms/RazorPagesCRUD.
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<ActionResult<Assignment>> PostAssignment(Assignment assignment)
|
||||||
|
{
|
||||||
|
_context.Assignments.Add(assignment);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
catch (DbUpdateException)
|
||||||
|
{
|
||||||
|
if (AssignmentExists(assignment.ProjectId))
|
||||||
|
{
|
||||||
|
return Conflict();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return CreatedAtAction("GetAssignment", new { id = assignment.ProjectId }, assignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DELETE: api/Assignments/5
|
||||||
|
[HttpDelete("{id}")]
|
||||||
|
public async Task<ActionResult<Assignment>> DeleteAssignment(int id)
|
||||||
|
{
|
||||||
|
var assignment = await _context.Assignments.FindAsync(id);
|
||||||
|
if (assignment == null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
_context.Assignments.Remove(assignment);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
return assignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool AssignmentExists(int id)
|
||||||
|
{
|
||||||
|
return _context.Assignments.Any(e => e.ProjectId == id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -94,6 +94,40 @@ namespace TicketManager.Controllers
|
||||||
return CreatedAtAction("GetProject", new { id = project.Id }, project);
|
return CreatedAtAction("GetProject", new { id = project.Id }, project);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost("{id}/addmembers")]
|
||||||
|
public async Task<ActionResult<Project>> PostAssignment(int id, List<User> usersToAdd)
|
||||||
|
{
|
||||||
|
var project = await _context.Projects.FindAsync(id);
|
||||||
|
|
||||||
|
if (project == null)
|
||||||
|
{ return NotFound(); }
|
||||||
|
|
||||||
|
List<Assignment> assignments = project.AddMembers(usersToAdd);
|
||||||
|
|
||||||
|
foreach (var assignment in assignments)
|
||||||
|
{ _context.Assignments.Add(assignment); }
|
||||||
|
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// await _context.SaveChangesAsync();
|
||||||
|
// }
|
||||||
|
// catch (DbUpdateException)
|
||||||
|
// {
|
||||||
|
// if (AssignmentExists(assignment.ProjectId))
|
||||||
|
// {
|
||||||
|
// return Conflict();
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// throw;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return CreatedAtAction("GetAssignment", new { id = assignment.ProjectId }, assignment);
|
||||||
|
return project;
|
||||||
|
}
|
||||||
|
|
||||||
// DELETE: api/Projects/5
|
// DELETE: api/Projects/5
|
||||||
[HttpDelete("{id}")]
|
[HttpDelete("{id}")]
|
||||||
public async Task<ActionResult<Project>> DeleteProject(int id)
|
public async Task<ActionResult<Project>> DeleteProject(int id)
|
||||||
|
|
@ -114,5 +148,9 @@ namespace TicketManager.Controllers
|
||||||
{
|
{
|
||||||
return _context.Projects.Any(e => e.Id == id);
|
return _context.Projects.Any(e => e.Id == id);
|
||||||
}
|
}
|
||||||
|
private bool AssignmentExists(int id)
|
||||||
|
{
|
||||||
|
return _context.Assignments.Any(e => e.ProjectId == id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ namespace TicketManager.Models
|
||||||
{
|
{
|
||||||
return this.Assignments.Select(a => a.User).ToList();
|
return this.Assignments.Select(a => a.User).ToList();
|
||||||
}
|
}
|
||||||
public void AddMembers(List<User> usersToAdd)
|
public List<Assignment> AddMembers(List<User> usersToAdd)
|
||||||
{
|
{
|
||||||
foreach (var user in usersToAdd)
|
foreach (var user in usersToAdd)
|
||||||
{
|
{
|
||||||
|
|
@ -73,6 +73,7 @@ namespace TicketManager.Models
|
||||||
};
|
};
|
||||||
this.Assignments.Add(newAssign);
|
this.Assignments.Add(newAssign);
|
||||||
}
|
}
|
||||||
|
return this.Assignments;
|
||||||
}
|
}
|
||||||
public void RemoveMembers(List<User> membersToRemove)
|
public void RemoveMembers(List<User> membersToRemove)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
729
client/package-lock.json
generated
729
client/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -10,8 +10,11 @@
|
||||||
"@types/node": "^12.12.26",
|
"@types/node": "^12.12.26",
|
||||||
"@types/react": "^16.9.19",
|
"@types/react": "^16.9.19",
|
||||||
"@types/react-dom": "^16.9.5",
|
"@types/react-dom": "^16.9.5",
|
||||||
|
"@types/react-router-dom": "^5.1.3",
|
||||||
|
"history": "^4.10.1",
|
||||||
"react": "^16.12.0",
|
"react": "^16.12.0",
|
||||||
"react-dom": "^16.12.0",
|
"react-dom": "^16.12.0",
|
||||||
|
"react-router-dom": "^5.1.2",
|
||||||
"react-scripts": "3.3.1",
|
"react-scripts": "3.3.1",
|
||||||
"typescript": "^3.7.5"
|
"typescript": "^3.7.5"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
import React, { FC } from "react";
|
import React, { FC } from "react";
|
||||||
|
import { Button } from "./Button";
|
||||||
|
import { FloatingButton } from "./FloatingButton";
|
||||||
|
|
||||||
type AvatarListProps = {
|
type AvatarListProps = {
|
||||||
avatars: string[];
|
avatars: string[];
|
||||||
|
|
@ -10,6 +12,7 @@ export const AvatarList: FC<AvatarListProps> = ({ avatars }) => {
|
||||||
{avatars.map((avatar: string) => (
|
{avatars.map((avatar: string) => (
|
||||||
<img className="circle" src={avatar} width="50vh" height="50vh" />
|
<img className="circle" src={avatar} width="50vh" height="50vh" />
|
||||||
))}
|
))}
|
||||||
|
<FloatingButton icon="add" color="grey" size="small" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
25
client/src/components/Button.tsx
Normal file
25
client/src/components/Button.tsx
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
import React, { FC, Children } from "react";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
icon?: string;
|
||||||
|
size?: string;
|
||||||
|
shape?: string;
|
||||||
|
color?: string;
|
||||||
|
text?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Button: FC<IProps> = ({
|
||||||
|
size = "small",
|
||||||
|
shape = "",
|
||||||
|
color,
|
||||||
|
text,
|
||||||
|
children
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className={`waves-effect waves-light btn-${size} ${shape} ${color}`}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
};
|
||||||
21
client/src/components/FloatingButton.tsx
Normal file
21
client/src/components/FloatingButton.tsx
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import React, { FC } from "react";
|
||||||
|
import { Button } from "./Button";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
icon?: string;
|
||||||
|
size?: string;
|
||||||
|
color?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FloatingButton: FC<IProps> = ({
|
||||||
|
icon = "add",
|
||||||
|
size = "small",
|
||||||
|
color = "red"
|
||||||
|
}) => {
|
||||||
|
const iconComponent = <i className="material-icons left">{icon}</i>;
|
||||||
|
return (
|
||||||
|
<Button color={color} size={size} shape="btn-floating">
|
||||||
|
{iconComponent}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -5,10 +5,7 @@ type HeaderProps = {
|
||||||
description: string;
|
description: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Header: FC<HeaderProps> = ({
|
export const Header: FC<HeaderProps> = ({ title, description }) => {
|
||||||
title,
|
|
||||||
description
|
|
||||||
}) => {
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h2>{title}</h2>
|
<h2>{title}</h2>
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ export const TicketList: FC<TicketListProps> = ({ tickets }) => {
|
||||||
return (
|
return (
|
||||||
<div className="row">
|
<div className="row">
|
||||||
{tickets.map((t: Ticket) => (
|
{tickets.map((t: Ticket) => (
|
||||||
<li key={t.Id}>{t.Title}</li>
|
<li key={t.id}>{t.title}</li>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -4,25 +4,48 @@ import { ProjectPage } from "../pages/ProjectPage";
|
||||||
import ProjectVM from "../viewModels/ProjectVM";
|
import ProjectVM from "../viewModels/ProjectVM";
|
||||||
import { Constants } from "../utils/Constants";
|
import { Constants } from "../utils/Constants";
|
||||||
import { Project } from "../types/Project";
|
import { Project } from "../types/Project";
|
||||||
|
import { Ticket } from "../types/Ticket";
|
||||||
|
import { User } from "../types/User";
|
||||||
|
|
||||||
export const ProjectController: FC = () => {
|
export const ProjectController: FC = () => {
|
||||||
const [project, setProject] = useState({});
|
// const [project, setProject] = useState({} as Project);
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const { id } = useParams();
|
// const { id } = useParams();
|
||||||
|
|
||||||
const getProject: Function = (id: number) => {
|
// const getProject: Function = (id: number) => {
|
||||||
fetch(`${Constants.getProjectURI}/${id}`)
|
// fetch(`${Constants.getProjectURI}/${id}`)
|
||||||
.then(res => res.json())
|
// .then(res => res.json())
|
||||||
.catch(err => console.log(err))
|
// .catch(err => console.log(err))
|
||||||
.then(data => setProject(data))
|
// .then(data => setProject(data))
|
||||||
.finally(() => setIsLoading(false));
|
// .finally(() => setIsLoading(false));
|
||||||
|
// };
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
// getProject(id);
|
||||||
|
// }, []);
|
||||||
|
|
||||||
|
// const viewModel = new ProjectVM(project);
|
||||||
|
// console.log(viewModel.getMembers());
|
||||||
|
|
||||||
|
const tickets: Ticket[] = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
title: "Ticket #1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
title: "Ticket #2"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const project: Project = {
|
||||||
|
id: 1,
|
||||||
|
title: "Project Title",
|
||||||
|
description: "What is it about",
|
||||||
|
progression: 25,
|
||||||
|
tickets: tickets
|
||||||
};
|
};
|
||||||
|
const viewModel = new ProjectVM(project);
|
||||||
useEffect(() => {
|
|
||||||
getProject(id);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const viewModel = new ProjectVM(project as Project);
|
|
||||||
|
|
||||||
return isLoading ? <p>Loading ...</p> : <ProjectPage viewModel={viewModel} />;
|
return isLoading ? <p>Loading ...</p> : <ProjectPage viewModel={viewModel} />;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
export const HomePage: React.FC = () => {
|
export const HomePage: React.FC = () => {
|
||||||
return <div className="App"></div>;
|
return (
|
||||||
|
<div className="App">
|
||||||
|
<p>HomePage</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -9,18 +9,12 @@ interface IProps {
|
||||||
viewModel: ProjectVM;
|
viewModel: ProjectVM;
|
||||||
}
|
}
|
||||||
export const ProjectPage: FC<IProps> = ({ viewModel }) => {
|
export const ProjectPage: FC<IProps> = ({ viewModel }) => {
|
||||||
const {
|
const { title, description, avatars, value, tickets } = viewModel;
|
||||||
title,
|
|
||||||
description,
|
|
||||||
// avatars,
|
|
||||||
value,
|
|
||||||
tickets
|
|
||||||
} = viewModel;
|
|
||||||
return (
|
return (
|
||||||
<div className="section">
|
<div className="section">
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<Header title={title} description={description} />
|
<Header title={title} description={description} />
|
||||||
{/* <AvatarList avatars={avatars} /> */}
|
<AvatarList avatars={avatars} />
|
||||||
<ProgressBar value={value} />
|
<ProgressBar value={value} />
|
||||||
{/* <TabView> */}
|
{/* <TabView> */}
|
||||||
<TicketList tickets={tickets} />
|
<TicketList tickets={tickets} />
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,5 @@ export interface Project {
|
||||||
description: string;
|
description: string;
|
||||||
progression: number;
|
progression: number;
|
||||||
tickets: Ticket[];
|
tickets: Ticket[];
|
||||||
|
// getMembers(): User[];
|
||||||
getUsers(): User[];
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
export interface Ticket {
|
export interface Ticket {
|
||||||
Id: number;
|
id: number;
|
||||||
Title: string;
|
title: string;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue