add UserTabPanel Component and minor refactoring

This commit is contained in:
Ruidy Nemausat 2020-04-03 10:53:10 +02:00
parent 4406063253
commit f9e1fd9392
24 changed files with 269 additions and 138 deletions

115
client/package-lock.json generated
View file

@ -1714,6 +1714,14 @@
"@types/react-router": "*"
}
},
"@types/react-swipeable-views": {
"version": "0.13.0",
"resolved": "https://registry.npmjs.org/@types/react-swipeable-views/-/react-swipeable-views-0.13.0.tgz",
"integrity": "sha512-orrreCcXev6IUXDuHf07RDDCAoIZRMSr95eyWmYNRfjic7w/O+68iPu0NCysVls+UygRNvoqZMuXI72N/58E1w==",
"requires": {
"@types/react": "*"
}
},
"@types/react-transition-group": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.2.4.tgz",
@ -8014,6 +8022,11 @@
"object.assign": "^4.1.0"
}
},
"keycode": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.0.tgz",
"integrity": "sha1-PQr1bce4uOXLqNCpfxByBO7CKwQ="
},
"killable": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
@ -10850,6 +10863,16 @@
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.5.tgz",
"integrity": "sha512-+DMR2k5c6BqMDSMF8hLH0vYKtKTeikiFW+fj0LClN+XZg4N9b8QUAdHC62CGWNLTi/gnuuemNcNcTFrCvK1f+A=="
},
"react-event-listener": {
"version": "0.6.6",
"resolved": "https://registry.npmjs.org/react-event-listener/-/react-event-listener-0.6.6.tgz",
"integrity": "sha512-+hCNqfy7o9wvO6UgjqFmBzARJS7qrNoda0VqzvOuioEpoEXKutiKuv92dSz6kP7rYLmyHPyYNLesi5t/aH1gfw==",
"requires": {
"@babel/runtime": "^7.2.0",
"prop-types": "^15.6.0",
"warning": "^4.0.1"
}
},
"react-is": {
"version": "16.12.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz",
@ -10961,6 +10984,85 @@
"workbox-webpack-plugin": "4.3.1"
}
},
"react-swipeable-views": {
"version": "0.13.9",
"resolved": "https://registry.npmjs.org/react-swipeable-views/-/react-swipeable-views-0.13.9.tgz",
"integrity": "sha512-WXC2FKYvZ9QdJ31v9LjEJEl1bA7E4AcaloTkbW0uU0dYf5uvv4aOpiyxubvOkVl1a5L2UAHmKSif4TmJ9usrSg==",
"requires": {
"@babel/runtime": "7.0.0",
"prop-types": "^15.5.4",
"react-swipeable-views-core": "^0.13.7",
"react-swipeable-views-utils": "^0.13.9",
"warning": "^4.0.1"
},
"dependencies": {
"@babel/runtime": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.0.0.tgz",
"integrity": "sha512-7hGhzlcmg01CvH1EHdSPVXYX1aJ8KCEyz6I9xYIi/asDtzBPMyMhVibhM/K6g/5qnKBwjZtp10bNZIEFTRW1MA==",
"requires": {
"regenerator-runtime": "^0.12.0"
}
},
"regenerator-runtime": {
"version": "0.12.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz",
"integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg=="
}
}
},
"react-swipeable-views-core": {
"version": "0.13.7",
"resolved": "https://registry.npmjs.org/react-swipeable-views-core/-/react-swipeable-views-core-0.13.7.tgz",
"integrity": "sha512-ekn9oDYfBt0oqJSGGwLEhKvn+QaqMGTy//9dURTLf+vp7W5j6GvmKryYdnwJCDITaPFI2hujXV4CH9krhvaE5w==",
"requires": {
"@babel/runtime": "7.0.0",
"warning": "^4.0.1"
},
"dependencies": {
"@babel/runtime": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.0.0.tgz",
"integrity": "sha512-7hGhzlcmg01CvH1EHdSPVXYX1aJ8KCEyz6I9xYIi/asDtzBPMyMhVibhM/K6g/5qnKBwjZtp10bNZIEFTRW1MA==",
"requires": {
"regenerator-runtime": "^0.12.0"
}
},
"regenerator-runtime": {
"version": "0.12.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz",
"integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg=="
}
}
},
"react-swipeable-views-utils": {
"version": "0.13.9",
"resolved": "https://registry.npmjs.org/react-swipeable-views-utils/-/react-swipeable-views-utils-0.13.9.tgz",
"integrity": "sha512-QLGxRKrbJCbWz94vkWLzb1Daaa2Y/TZKmsNKQ6WSNrS+chrlfZ3z9tqZ7YUJlW6pRWp3QZdLSY3UE3cN0TXXmw==",
"requires": {
"@babel/runtime": "7.0.0",
"keycode": "^2.1.7",
"prop-types": "^15.6.0",
"react-event-listener": "^0.6.0",
"react-swipeable-views-core": "^0.13.7",
"shallow-equal": "^1.2.1"
},
"dependencies": {
"@babel/runtime": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.0.0.tgz",
"integrity": "sha512-7hGhzlcmg01CvH1EHdSPVXYX1aJ8KCEyz6I9xYIi/asDtzBPMyMhVibhM/K6g/5qnKBwjZtp10bNZIEFTRW1MA==",
"requires": {
"regenerator-runtime": "^0.12.0"
}
},
"regenerator-runtime": {
"version": "0.12.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz",
"integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg=="
}
}
},
"react-transition-group": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.3.0.tgz",
@ -11747,6 +11849,11 @@
}
}
},
"shallow-equal": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz",
"integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA=="
},
"shebang-command": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
@ -13129,6 +13236,14 @@
"makeerror": "1.0.x"
}
},
"warning": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
"integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
"requires": {
"loose-envify": "^1.0.0"
}
},
"watchpack": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz",

View file

@ -16,12 +16,14 @@
"@types/react": "^16.9.19",
"@types/react-dom": "^16.9.5",
"@types/react-router-dom": "^5.1.3",
"@types/react-swipeable-views": "^0.13.0",
"@types/underscore": "^1.9.4",
"history": "^4.10.1",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-router-dom": "^5.1.2",
"react-scripts": "3.3.1",
"react-swipeable-views": "^0.13.9",
"typescript": "^3.7.5",
"underscore": "^1.9.2"
},

View file

@ -12,9 +12,7 @@ export const ActivityList: FC<IProps> = ({ activities }) => {
const clearFilterText: (e: MouseEvent) => void = (e: MouseEvent) => {
setFilterText("");
};
const handleChange: (e: ChangeEvent<HTMLInputElement>) => void = (
e: ChangeEvent<HTMLInputElement>
) => {
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
setFilterText(e.target.value);
};

View file

@ -10,12 +10,10 @@ type IProps = {
export const FileList: FC<IProps> = ({ files }) => {
const [filterText, setFilterText] = useState<string>("");
const clearFilterText: (e: MouseEvent) => void = (e: MouseEvent) => {
const clearFilterText = (e: MouseEvent): void => {
setFilterText("");
};
const handleChange: (e: ChangeEvent<HTMLInputElement>) => void = (
e: ChangeEvent<HTMLInputElement>
) => {
const handleChange = (e: ChangeEvent<HTMLInputElement>): void => {
setFilterText(e.target.value);
};
return (

View file

@ -3,7 +3,6 @@ import { Link } from "react-router-dom";
import Avatar from "@material-ui/core/Avatar";
import AvatarGroup from "@material-ui/lab/AvatarGroup";
import { User } from "../types/User";
import { UserAvatar } from "./UserAvatar";
interface AvatarListProps {
users: User[];

View file

@ -1,5 +1,4 @@
import React from "react";
import { Link } from "react-router-dom";
import { createStyles, makeStyles, Theme } from "@material-ui/core/styles";
import AppBar from "@material-ui/core/AppBar";
import Toolbar from "@material-ui/core/Toolbar";

View file

@ -7,7 +7,6 @@ type IProps = {
};
export const FileCollection: FC<IProps> = ({ files, filterText }) => {
console.log();
return (
<>
<ul className="collection">

View file

@ -1,10 +1,7 @@
import React, { FC, MouseEvent } from "react";
import { Fab, SvgIconTypeMap } from "@material-ui/core";
import { OverridableComponent } from "@material-ui/core/OverridableComponent";
import { Fab } from "@material-ui/core";
import AddIcon from "@material-ui/icons/Add";
import { Button } from "./Button";
interface IProps {
icon?: string;
color?: "inherit" | "primary" | "secondary" | "default" | undefined;
@ -13,20 +10,6 @@ interface IProps {
text?: string;
}
// export const FloatingButton: FC<IProps> = ({
// icon = "add",
// size = "small",
// color = "red",
// onClick
// }) => {
// const iconComponent = <i className="material-icons left">{icon}</i>;
// return (
// <Button color={color} size={size} shape="btn-floating" onClick={onClick}>
// {iconComponent}
// </Button>
// );
// };
export const FloatingButton: FC<IProps> = ({
color,
icon,

View file

@ -1,23 +1,31 @@
import React from "react";
import { AppBar } from "@material-ui/core";
import CssBaseline from "@material-ui/core/CssBaseline";
import React, { FC } from "react";
import Typography from "@material-ui/core/Typography";
import { makeStyles } from "@material-ui/core/styles";
import Container from "@material-ui/core/Container";
import Link from "@material-ui/core/Link";
function Copyright() {
interface IProps {
brand: string;
text: string;
}
const copyParams: IProps = {
brand: "BugBuster",
text: "Made with 🔥"
};
const Copyright: FC<IProps> = ({ brand, text }) => {
return (
<Typography variant="body2" color="textSecondary">
{"© "}
<Link color="inherit" href="/">
BugBuster
{brand}
</Link>{" "}
{new Date().getFullYear()}
{". All Rights Reserved. Made with 🔥"}
{`. All Rights Reserved. ${text}`}
</Typography>
);
}
};
const useStyles = makeStyles(theme => ({
footer: {
@ -46,7 +54,7 @@ export default function Footer() {
Ruidy Nemausat
</Link>{" "}
</Typography>
<Copyright />
<Copyright brand={copyParams.brand} text={copyParams.text} />
</Container>
</footer>
);

View file

@ -45,11 +45,6 @@ export const HorizontalCard: FC<IProps> = ({
check
</i>
</Link>
{/* <Link to="#">
<i className="material-icons" onClick={archiveTicket}>
archive
</i>
</Link> */}
</div>
</div>
</div>

View file

@ -1,8 +1,6 @@
import React, { FC } from "react";
type IProps = {};
export const InputFile: FC<IProps> = () => {
export const InputFile: FC = () => {
return (
<>
<form action="/upload">

View file

@ -10,12 +10,10 @@ interface IProps {
export const MemberList: FC<IProps> = ({ users }) => {
const [members, setMembers] = useState<User[]>([]);
const [filterText, setFilterText] = useState<string>("");
const clearFilterText: (e: MouseEvent) => void = (e: MouseEvent) => {
const clearFilterText = (e: MouseEvent): void => {
setFilterText("");
};
const handleChange: (e: ChangeEvent<HTMLInputElement>) => void = (
e: ChangeEvent<HTMLInputElement>
) => {
const handleChange = (e: ChangeEvent<HTMLInputElement>): void => {
setFilterText(e.target.value);
};
return (

View file

@ -6,11 +6,10 @@ import { Ticket } from "../types/Ticket";
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;
handleClose: () => void;
allProjects: Project[];
}

View file

@ -1,41 +0,0 @@
import React, { FC } from "react";
import { useRouteMatch } from "react-router-dom";
import { TabRouterHeader } from "./TabRouterHeader";
interface IProps {
tabNames: string[];
description: string;
setDescription: React.Dispatch<React.SetStateAction<string>>;
title: string;
setTitle: React.Dispatch<React.SetStateAction<string>>;
endingDate: string;
setEndingDate: React.Dispatch<React.SetStateAction<string>>;
}
export const NewTicketTabRouter: FC<IProps> = ({
tabNames,
description,
setDescription,
title,
setTitle,
endingDate,
setEndingDate
}) => {
const { url } = useRouteMatch();
return (
<>
<div className="row">
<TabRouterHeader tabNames={tabNames} />
{/* <NewTicketForm
title={title}
setTitle={setTitle}
description={description}
setDescription={setDescription}
endingDate={endingDate}
setEndingDate={setEndingDate}
/> */}
</div>
</>
);
};

View file

@ -1,5 +1,4 @@
import React, { FC } from "react";
import { HorizontalCard } from "./HorizontalCard";
import { UserAvatar } from "./UserAvatar";
import { Link } from "react-router-dom";

View file

@ -1,4 +1,4 @@
import React, { FC, CSSProperties } from "react";
import React, { FC } from "react";
import { makeStyles, Theme, createStyles } from "@material-ui/core/styles";
import LinearProgress from "@material-ui/core/LinearProgress";
import { Box } from "@material-ui/core";
@ -25,23 +25,23 @@ const useStyles = makeStyles((theme: Theme) =>
export const ProgressBar: FC<IProps> = ({
value,
max = 100,
// max = 100,
tasksDone,
tasksTotalCount,
remainingDays
}) => {
const styleString: CSSProperties = { width: `${value}%` };
let barColor: string = "green";
// const styleString: CSSProperties = { width: `${value}%` };
// let barColor: string = "green";
if (value < 100) {
barColor = "yellow";
}
if (value < 200 / 3) {
barColor = "orange";
}
if (value < 100 / 3) {
barColor = "red";
}
// if (value < 100) {
// barColor = "yellow";
// }
// if (value < 200 / 3) {
// barColor = "orange";
// }
// if (value < 100 / 3) {
// barColor = "red";
// }
const classes = useStyles();

View file

@ -17,9 +17,7 @@ export const ProjectList: FC<IProps> = ({ projects }) => {
setFilterText("");
};
const handleChange: (e: ChangeEvent<HTMLInputElement>) => void = (
e: ChangeEvent<HTMLInputElement>
) => {
const handleChange = (e: ChangeEvent<HTMLInputElement>): void => {
setFilterText(e.target.value);
};

View file

@ -3,7 +3,6 @@ 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 { Ticket } from "../types/Ticket";
import { AppFile } from "../types/AppFile";
import { Activity } from "../types/Activity";

View file

@ -21,17 +21,15 @@ export const TicketList: FC<TicketListProps> = ({
addButton = true
}) => {
const [filterText, setFilterText] = useState<string>("");
const clearFilterText: (e: MouseEvent) => void = (e: MouseEvent) => {
const clearFilterText = (e: MouseEvent): void => {
setFilterText("");
};
const onClick: (e: MouseEvent) => void = (e: MouseEvent) => {
const onClick = (e: MouseEvent): void => {
e.preventDefault();
setShowNew(true);
};
const handleChange: (e: ChangeEvent<HTMLInputElement>) => void = (
e: ChangeEvent<HTMLInputElement>
) => {
const handleChange = (e: ChangeEvent<HTMLInputElement>): void => {
setFilterText(e.target.value);
};

View file

@ -7,20 +7,6 @@ interface IProps {
alt: string;
}
// export const Avatar: FC<IProps> = () => {
// return (
// <>
// <img
// className="circle"
// src={picture}
// height="100vh"
// width="100vh"
// alt="user avatar"
// />
// </>
// );
// };
const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {

View file

@ -0,0 +1,102 @@
import React, { FC } from "react";
import SwipeableViews from "react-swipeable-views";
import { makeStyles, Theme, useTheme } from "@material-ui/core/styles";
import AppBar from "@material-ui/core/AppBar";
import Tabs from "@material-ui/core/Tabs";
import Tab from "@material-ui/core/Tab";
import Typography from "@material-ui/core/Typography";
import Box from "@material-ui/core/Box";
import { Header } from "./Header";
import { Ticket } from "../types/Ticket";
import { Project } from "../types/Project";
import { ProjectList } from "./ProjectList";
import { TicketList } from "./TicketList";
interface TabProps {
children?: React.ReactNode;
dir?: string;
index: any;
value: any;
}
const TabPanel: FC<TabProps> = (props: TabProps) => {
const { children, value, index, ...other } = props;
return (
<Typography
component="div"
role="tabpanel"
hidden={value !== index}
id={`full-width-tabpanel-${index}`}
aria-labelledby={`full-width-tab-${index}`}
{...other}
>
{value === index && <Box p={3}>{children}</Box>}
</Typography>
);
};
const a11yProps = (index: any) => {
return {
id: `full-width-tab-${index}`,
"aria-controls": `full-width-tabpanel-${index}`
};
};
const useStyles = makeStyles((theme: Theme) => ({
root: {
backgroundColor: theme.palette.background.paper,
width: 500
}
}));
interface IProps {
tabNames: string[];
tickets: Ticket[];
projects: Project[];
}
export const UserTabPanel: FC<IProps> = ({ tickets, tabNames, projects }) => {
const classes = useStyles();
const theme = useTheme();
const [value, setValue] = React.useState(0);
const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => {
setValue(newValue);
};
const handleChangeIndex = (index: number) => {
setValue(index);
};
return (
<div className={classes.root}>
<AppBar position="static" color="default">
<Tabs
value={value}
onChange={handleChange}
indicatorColor="primary"
textColor="primary"
variant="fullWidth"
aria-label="full width tabs example"
>
{tabNames.map((t: string, i: number) => (
<Tab label={t} {...a11yProps({ i })} />
))}
</Tabs>
</AppBar>
<SwipeableViews
axis={theme.direction === "rtl" ? "x-reverse" : "x"}
index={value}
onChangeIndex={handleChangeIndex}
>
<TabPanel value={value} index={0} dir={theme.direction}>
<ProjectList projects={projects} />
</TabPanel>
<TabPanel value={value} index={1} dir={theme.direction}>
<TicketList tickets={tickets} allProjects={[]} addButton={false} />
</TabPanel>
</SwipeableViews>
</div>
);
};

View file

@ -1,4 +1,4 @@
import React, { FC, useState, ChangeEvent, FormEvent, useEffect } from "react";
import React, { FC, useState, ChangeEvent, FormEvent } from "react";
import { Modal } from "./Modal";
import { AvatarList } from "./AvatarList";
import { User } from "../types/User";
@ -25,9 +25,7 @@ export const UsersModal: FC<IProps> = ({
const [members, setMembers] = useState<User[]>(users);
const { id } = useParams();
const handleChange: (e: ChangeEvent<HTMLInputElement>) => void = (
e: ChangeEvent<HTMLInputElement>
) => {
const handleChange = (e: ChangeEvent<HTMLInputElement>): void => {
setFilterText(e.target.value);
};
@ -42,7 +40,6 @@ export const UsersModal: FC<IProps> = ({
handleClose();
};
useEffect(() => {});
return (
<Modal show={show} handleClose={handleClose}>
<div className="row valign-wrapper indigo">

View file

@ -2,10 +2,12 @@ import React, { FC } from "react";
import { UserVM } from "../VM/UserVM";
import { UserHeader } from "../components/UserHeader";
import { UserTabRouter } from "../components/UserTabRouter";
import { UserTabPanel } from "../components/UserTabPanel";
interface IProps {
viewModel: UserVM;
}
export const UserPage: FC<IProps> = ({ viewModel }) => {
const { fullName, presentation, picture, projects, tickets } = viewModel;
const tabNames: string[] = ["Projects", "Tickets"];
@ -17,7 +19,7 @@ export const UserPage: FC<IProps> = ({ viewModel }) => {
fullName={fullName}
presentation={presentation}
/>
<UserTabRouter
<UserTabPanel
tabNames={tabNames}
projects={projects}
tickets={tickets}

View file

@ -6,14 +6,14 @@ import { ProjectController } from "../controllers/ProjectController";
import { UserController } from "../controllers/UserController";
import { TicketController } from "../controllers/TicketController";
import { NotFoundPage } from "../pages/NotFoundPage";
// import { TestPage } from "../pages/TestPage";
import { TestPage } from "../pages/TestPage";
export const AppRouter = () => {
return (
<Switch>
{/* <Route exact path="/">
<TestPage />
</Route> */}
<Route exact path="/test">
<TestPage />
</Route>
<Route exact path="/">
<HomeController />