Created NewProjectModal not implemented yet

This commit is contained in:
Ruidy Nemausat 2020-04-18 11:47:25 +02:00
parent 8a3e1d96d5
commit 1368859860
8 changed files with 321 additions and 50 deletions

View file

@ -56,5 +56,5 @@
- [ ] Refactor Lists code
- [ ] Query project members in UserPage
- [ ] Query progression info in UserPage
- [ ] Add info fields in New Ticket Form
- [x] Add info fields in New Ticket Form
- [ ] Filter users in Users Modal

View file

@ -16,8 +16,9 @@ export class UserVM {
public projects: Project[];
public tickets: Ticket[];
public activities: Activity[];
public allUsers: User[];
public constructor(user: User) {
public constructor(user: User, allUsers: User[]) {
this.id = user.id;
this.firstName = user.firstName;
this.lastName = user.lastName;
@ -30,5 +31,6 @@ export class UserVM {
this.projects = user.projects;
this.tickets = user.tickets;
this.activities = user.activities;
this.allUsers = allUsers;
}
}

View file

@ -8,26 +8,38 @@ import {
} from "@material-ui/core";
import { FilterBar } from "../FilterBar";
import ProjectCard from "../Cards/ProjectCard";
import { FloatingButton } from "../Buttons/FloatingButton";
import { NewProjectModal } from "../Modals/NewProjectModal";
import { Project } from "../../types/Project";
import { User } from "../../types/User";
const useStyles = makeStyles((theme: Theme) =>
createStyles({
header: {
paddingBottom: theme.spacing(2),
},
addButton: {
position: "relative",
marginLeft: "20px",
},
})
);
type IProps = {
projects: Project[];
allUsers: User[];
};
export const ProjectList: FC<IProps> = ({ projects }) => {
export const ProjectList: FC<IProps> = ({ projects, allUsers }) => {
const [filterText, setFilterText] = useState<string>("");
const clearFilterText: (e: MouseEvent) => void = (e: MouseEvent) => {
setFilterText("");
};
const [showNew, setShowNew] = useState(false);
const onClick = (e: MouseEvent): void => {
e.preventDefault();
setShowNew(true);
};
const handleChange = (e: ChangeEvent<HTMLInputElement>): void => {
setFilterText(e.target.value);
};
@ -41,47 +53,59 @@ export const ProjectList: FC<IProps> = ({ projects }) => {
const classes = useStyles();
return (
<Grid container>
<Grid
container
direction="row"
justify="space-between"
alignItems="center"
className={classes.header}
>
<Typography variant="h4" component="h4">
Projects
</Typography>
<FilterBar
filterText={filterText}
handleChange={handleChange}
clearFilterText={clearFilterText}
/>
<>
<NewProjectModal
handleClose={() => {
setShowNew(false);
}}
show={showNew}
allUsers={allUsers}
/>
<Grid container>
<Grid
container
direction="row"
justify="space-between"
alignItems="center"
className={classes.header}
>
<Typography variant="h4" component="h4">
Projects
<span className={classes.addButton}>
<FloatingButton color="primary" size="small" onClick={onClick} />
</span>
</Typography>
<FilterBar
filterText={filterText}
handleChange={handleChange}
clearFilterText={clearFilterText}
/>
</Grid>
<Grid item xs={12}>
<div className="col s12 grey lighten-1">
{filteredProjects.length === 0 ? (
<ProjectCard />
) : (
filteredProjects.map((t: Project) => (
<ProjectCard
key={t.id}
title={t.title}
remainingDays={t.endingDate}
link={`/projects/${t.id}`}
members={t.users}
progress={t.progression}
ticketsNumber={t.tickets === undefined ? 0 : t.tickets.length}
ticketsDone={
t.tickets === undefined
? 0
: t.tickets.filter((t) => t.status === "Done").length
}
/>
))
)}
</div>
</Grid>
</Grid>
<Grid item xs={12}>
<div className="col s12 grey lighten-1">
{filteredProjects.length === 0 ? (
<ProjectCard />
) : (
filteredProjects.map((t: Project) => (
<ProjectCard
key={t.id}
title={t.title}
remainingDays={t.endingDate}
link={`/projects/${t.id}`}
members={t.users}
progress={t.progression}
ticketsNumber={t.tickets === undefined ? 0 : t.tickets.length}
ticketsDone={
t.tickets === undefined
? 0
: t.tickets.filter((t) => t.status === "Done").length
}
/>
))
)}
</div>
</Grid>
</Grid>
</>
);
};

View file

@ -0,0 +1,214 @@
import React, { FC, useState, FormEvent } from "react";
import { useRouteMatch } from "react-router-dom";
import {
TextField,
MenuItem,
Grid,
makeStyles,
Theme,
createStyles,
} from "@material-ui/core";
import { Modal } from "./Modal";
import { Ticket } from "../../types/Ticket";
import { User } from "../../types/User";
import { post } from "../../utils/http";
import { Constants } from "../../utils/Constants";
import Category from "../../types/enums/category";
import Impact from "../../types/enums/impact";
import Difficulty from "../../types/enums/difficulty";
interface IProps {
show: boolean;
handleClose: () => void;
allUsers: User[];
}
const useStyles = makeStyles((theme: Theme) => ({
select: {
width: 120,
},
}));
export const NewProjectModal: FC<IProps> = ({
show,
handleClose,
allUsers,
}) => {
const [title, setTitle] = useState("");
const [description, setDescription] = useState("");
const [endingDate, setEndingDate] = useState("");
const { url } = useRouteMatch();
const id = url.split("/")[2];
const [projectId, setProjectId] = useState(id);
const [categoryID, setCategoryID] = useState(0);
const [impactID, setImpactID] = useState(0);
const [difficultyID, setDifficultyID] = useState(0);
const handleSubmit = async (e: FormEvent) => {
e.preventDefault();
let newTicket = {
title: title,
description: description,
endingDate: new Date(endingDate).toISOString(),
creatorId: "20bf4b2a-7209-4826-96cd-29c2bc937a94", // get current User id
projectId: parseInt(projectId),
impact: impactID,
difficulty: difficultyID,
category: categoryID,
};
// const response: HttpResponse<Ticket> =
await post<Ticket>(`${Constants.ticketsURI}`, newTicket);
handleClose();
};
const classes = useStyles();
return (
<Modal
name="New Ticket"
show={show}
handleClose={handleClose}
action="New Ticket"
handleAction={handleSubmit}
>
<TextField
variant="outlined"
margin="normal"
required
fullWidth
id="title"
value={title}
label="Title"
name="text"
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setTitle(e.target.value)
}
autoFocus
/>
<TextField
variant="outlined"
margin="normal"
required
fullWidth
id="description"
value={description}
label="Description"
name="text"
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) =>
setDescription(e.target.value)
}
multiline
/>
<TextField
id="project"
name="project"
select
fullWidth
required
label="Project"
value={projectId}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
e.preventDefault();
setProjectId(e.target.value);
}}
// helperText="Please select your currency"
variant="outlined"
margin="normal"
>
{allUsers.map((p) => (
<MenuItem key={p.id} value={p.id}>
{p}
</MenuItem>
))}
</TextField>
<TextField
id="date"
name="date"
label="Due Date"
type="date"
margin="normal"
fullWidth
// defaultValue={new Date().toISOString()}
// className={classes.textField}
InputLabelProps={{
shrink: true,
}}
variant="outlined"
required
value={endingDate}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setEndingDate(e.target.value)
}
/>
<Grid container justify="space-between">
<TextField
id="category"
name="category"
select
label="Category"
value={categoryID}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
e.preventDefault();
setCategoryID(parseInt(e.target.value));
}}
variant="outlined"
margin="normal"
className={classes.select}
>
{Category.map((c: string, i: number) => (
<MenuItem key={i} value={i}>
{c}
</MenuItem>
))}
</TextField>
<TextField
className={classes.select}
id="impact"
name="impact"
select
label="Impact"
value={impactID}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
e.preventDefault();
setImpactID(parseInt(e.target.value));
}}
variant="outlined"
margin="normal"
>
{Impact.map((c: string, i: number) => (
<MenuItem key={i} value={i}>
{c}
</MenuItem>
))}
</TextField>
<TextField
className={classes.select}
id="difficulty"
name="difficulty"
select
label="Difficulty"
value={difficultyID}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
e.preventDefault();
setDifficultyID(parseInt(e.target.value));
}}
variant="outlined"
margin="normal"
>
{Difficulty.map((c: string, i: number) => (
<MenuItem key={i} value={i}>
{c}
</MenuItem>
))}
</TextField>
</Grid>
</Modal>
);
};

View file

@ -10,6 +10,7 @@ import { Ticket } from "../../types/Ticket";
import { Project } from "../../types/Project";
import { ProjectList } from "../Lists/ProjectList";
import { TicketList } from "../Lists/TicketList";
import { User } from "../../types/User";
interface TabProps {
children?: ReactNode;
@ -53,9 +54,15 @@ interface IProps {
tabNames: string[];
tickets: Ticket[];
projects: Project[];
allUsers: User[];
}
export const UserTabPanel: FC<IProps> = ({ tickets, tabNames, projects }) => {
export const UserTabPanel: FC<IProps> = ({
tickets,
tabNames,
projects,
allUsers,
}) => {
const classes = useStyles();
const theme = useTheme();
const [value, setValue] = useState(0);
@ -94,7 +101,7 @@ export const UserTabPanel: FC<IProps> = ({ tickets, tabNames, projects }) => {
onChangeIndex={handleChangeIndex}
>
<TabPanel value={value} index={0} dir={theme.direction}>
<ProjectList projects={projects} />
<ProjectList projects={projects} allUsers={allUsers} />
</TabPanel>
<TabPanel value={value} index={1} dir={theme.direction}>
<TicketList tickets={tickets} allProjects={[]} addButton={false} />

View file

@ -23,7 +23,7 @@ export const UserTabRouter: FC<IProps> = ({ tickets, tabNames, projects }) => {
<Redirect from={url} to={`${url}/projects`} />
<Route path={`${url}/projects`}>
<ProjectList projects={projects} />
{/* <ProjectList projects={projects} /> */}
</Route>
<Route path={`${url}/tickets`}>

View file

@ -14,6 +14,7 @@ export const UserController: FC = () => {
const [user, setUser] = useState<User>({} as User);
const [hasError, setHasError] = useState(false);
const [error, setError] = useState("");
const [allUsers, setAllUsers] = useState<User[]>([]);
const { id } = useParams();
async function httpGetUser(id: string): Promise<void> {
@ -32,9 +33,24 @@ export const UserController: FC = () => {
}
}
async function httpGetAllUsers(): Promise<void> {
try {
const response: HttpResponse<User> = await get<User>(
`${Constants.usersURI}`
);
if (response.parsedBody !== undefined) {
setAllUsers((response.parsedBody as unknown) as User[]);
}
} catch (ex) {
setHasError(true);
setError(ex);
}
}
useEffect(() => {
if (id !== undefined) {
httpGetUser(id);
httpGetAllUsers();
} else {
setHasError(true);
setError("Bad Request");
@ -45,6 +61,6 @@ export const UserController: FC = () => {
return <ErrorController error={error} />;
}
const viewModel = new UserVM(user);
const viewModel = new UserVM(user, allUsers);
return isLoading ? <Preloader /> : <UserPage viewModel={viewModel} />;
};

View file

@ -9,7 +9,14 @@ interface IProps {
}
export const UserPage: FC<IProps> = ({ viewModel }) => {
const { fullName, presentation, picture, projects, tickets } = viewModel;
const {
fullName,
presentation,
picture,
projects,
tickets,
allUsers,
} = viewModel;
const tabNames: string[] = ["Projects", "Tickets"];
return (
@ -26,6 +33,7 @@ export const UserPage: FC<IProps> = ({ viewModel }) => {
tabNames={tabNames}
projects={projects}
tickets={tickets}
allUsers={allUsers}
/>
}
/>