Add input File design, filtering for project page tabs

This commit is contained in:
Ruidy Nemausat 2020-02-20 17:50:14 +01:00
parent 14e47378dd
commit 2419627400
15 changed files with 156 additions and 59 deletions

View file

@ -1,18 +1,26 @@
import React, { FC } from "react"; import React, { FC, useState, ChangeEvent } from "react";
import { FloatingButton } from "./FloatingButton";
import { ActivityCollection } from "./ActivityCollection"; import { ActivityCollection } from "./ActivityCollection";
import { Activity } from "../types/Activity"; import { Activity } from "../types/Activity";
import { FilterBar } from "./FilterBar";
type IProps = { type IProps = {
activities: Activity[]; activities: Activity[];
}; };
export const ActivityList: FC<IProps> = ({ activities }) => { export const ActivityList: FC<IProps> = ({ activities }) => {
const [filterText, setFilterText] = useState<string>("");
const handleChange: (e: ChangeEvent<HTMLInputElement>) => void = (
e: ChangeEvent<HTMLInputElement>
) => {
setFilterText(e.target.value);
};
return ( return (
<> <>
<div className="row valign-wrapper"> <div className="row valign-wrapper">
<h3>Activity</h3> <h3>Activity</h3>
<FloatingButton color=" blue-grey lighten-4" size="" /> <FilterBar filterText={filterText} handleChange={handleChange} />
</div> </div>
<ActivityCollection activities={activities} /> <ActivityCollection activities={activities} />
</> </>

View file

@ -1,21 +1,28 @@
import React, { FC } from "react"; import React, { FC, useState, ChangeEvent } from "react";
import { AppFile } from "../types/AppFile"; import { AppFile } from "../types/AppFile";
import { FloatingButton } from "./FloatingButton";
import { FileCollection } from "./FileCollection"; import { FileCollection } from "./FileCollection";
import { DropZone } from "./DropZone"; import { InputFile } from "./InputFile";
import { FilterBar } from "./FilterBar";
type IProps = { type IProps = {
files: AppFile[]; files: AppFile[];
}; };
export const FileList: FC<IProps> = ({ files }) => { export const FileList: FC<IProps> = ({ files }) => {
const [filterText, setFilterText] = useState<string>("");
const handleChange: (e: ChangeEvent<HTMLInputElement>) => void = (
e: ChangeEvent<HTMLInputElement>
) => {
setFilterText(e.target.value);
};
return ( return (
<> <>
<div className="row valign-wrapper"> <div className="row valign-wrapper">
<h3>Files</h3> <h3>Files</h3>
<FloatingButton color=" blue-grey lighten-4" size="" /> <FilterBar filterText={filterText} handleChange={handleChange} />
</div> </div>
<DropZone /> <InputFile />
<FileCollection files={files} /> <FileCollection files={files} />
</> </>
); );

View file

@ -1,7 +0,0 @@
import React, { FC } from "react";
type IProps = {};
export const DropZone: FC<IProps> = () => {
return <div className="copy">Drag & Drop your files here.</div>;
};

View file

@ -0,0 +1,39 @@
import React, { FC } from "react";
import { useRouteMatch } from "react-router-dom";
type IProps = {
filterText: string;
handleChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
};
export const FilterBar: FC<IProps> = ({ filterText, handleChange }) => {
const { url } = useRouteMatch();
const placeholder: string = url.split("/")[3];
return (
<>
<div className="nav-wrapper">
<div className="input-field">
<input
// className="validate"
id="filter"
type="search"
required
name="filter"
value={filterText}
placeholder={`Filter ${placeholder}`}
onChange={handleChange}
/>
<label className="label-icon" htmlFor="search">
<i className="material-icons">filter_list</i>
</label>
<i
className="material-icons" //onClick={clearSearchBar}
>
close
</i>
</div>
<div className="col s2 valign-wrapper"></div>
</div>
</>
);
};

View file

@ -1,9 +1,10 @@
import React, { FC, MouseEvent } from "react"; import React, { FC, MouseEvent } from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { getRemainingdays } from "../utils/methods";
interface IProps { interface IProps {
title: string; title: string;
remainingDays?: number; remainingDays: string;
validateTicket: (event: MouseEvent) => void; validateTicket: (event: MouseEvent) => void;
archiveTicket: (event: MouseEvent) => void; archiveTicket: (event: MouseEvent) => void;
} }
@ -26,7 +27,7 @@ export const HorizontalCard: FC<IProps> = ({
</Link> </Link>
</h6> </h6>
</div> </div>
<span>Due {remainingDays} days</span> <span>Due {getRemainingdays(remainingDays)} days</span>
<div className="right"> <div className="right">
<Link to="#"> <Link to="#">
<i className="material-icons" onClick={validateTicket}> <i className="material-icons" onClick={validateTicket}>

View file

@ -0,0 +1,29 @@
import React, { FC } from "react";
type IProps = {};
export const InputFile: FC<IProps> = () => {
return (
<>
<form action="/upload">
<div className="file-field input-field">
<div className="btn">
<i className="material-icons ">cloud_upload</i>
<input
type="file"
multiple
accept=".doc,.docx,.pdf,.md,.gdoc,.zip,image/*"
/>
</div>
<div className="file-path-wrapper">
<input
className="file-path validate"
type="text"
placeholder="Upload one or more files"
/>
</div>
</div>
</form>
</>
);
};

View file

@ -18,7 +18,6 @@ interface IProps {
export const TabRouter: FC<IProps> = ({ export const TabRouter: FC<IProps> = ({
tickets, tickets,
remainingDays,
tabNames, tabNames,
files, files,
activities activities
@ -32,7 +31,7 @@ export const TabRouter: FC<IProps> = ({
<Redirect from={url} to={`${url}/tickets`} /> <Redirect from={url} to={`${url}/tickets`} />
<Route path={`${url}/tickets`}> <Route path={`${url}/tickets`}>
<TicketList tickets={tickets} remainingDays={remainingDays} /> <TicketList tickets={tickets} />
</Route> </Route>
<Route path={`${url}/files`}> <Route path={`${url}/files`}>

View file

@ -1,35 +1,65 @@
import React, { FC } from "react"; import React, { FC, useState, ChangeEvent } from "react";
import { Ticket } from "../types/Ticket"; import { Ticket } from "../types/Ticket";
import { FloatingButton } from "./FloatingButton"; import { FloatingButton } from "./FloatingButton";
import { HorizontalCard } from "./HorizontalCard"; import { HorizontalCard } from "./HorizontalCard";
import { FilterBar } from "./FilterBar";
type TicketListProps = { type TicketListProps = {
tickets: Ticket[]; tickets: Ticket[];
remainingDays?: number;
}; };
export const TicketList: FC<TicketListProps> = ({ tickets, remainingDays }) => { export const TicketList: FC<TicketListProps> = ({ tickets }) => {
const archiveTicket = () => {}; const archiveTicket = () => {};
const validateTicket = () => {}; const validateTicket = () => {};
const [filterText, setFilterText] = useState<string>("");
const handleChange: (e: ChangeEvent<HTMLInputElement>) => void = (
e: ChangeEvent<HTMLInputElement>
) => {
setFilterText(e.target.value);
};
// const useFilter = (init: string) => {
// const [filterText, setFilterText] = useState<string>(init);
// // const handleChange: (e: ChangeEvent<HTMLInputElement>) => void = (
// // e: ChangeEvent<HTMLInputElement>
// // ) => {
// // setFilterText(e.target.value);
// // };
// return {
// filterText,
// setFilterText,
// bind: {
// filterText,
// handleChange: (e: ChangeEvent<HTMLInputElement>) => {
// setFilterText(e.target.value);
// }
// }
// };
// };
// const [filterText, handleChange] = useFilter("");
// const [filterText, handleChange] = useFilter("");
return ( return (
<> <>
<div className="row valign-wrapper"> <div className="row valign-wrapper">
<h3>Tickets</h3> <h3>Tickets</h3>
<FloatingButton color=" blue-grey lighten-4" size="big" /> <FloatingButton color=" blue-grey lighten-4" size="small" />
<FilterBar filterText={filterText} handleChange={handleChange} />
</div> </div>
<div className="col s12 grey"> <div className="col s12 grey">
<ul> <ul>
{tickets.map((t: Ticket) => ( {tickets
<li key={t.id}> .filter(t => t.title.includes(filterText))
<HorizontalCard .map((t: Ticket) => (
title={t.title} <li key={t.id}>
remainingDays={remainingDays} <HorizontalCard
validateTicket={validateTicket} title={t.title}
archiveTicket={archiveTicket} remainingDays={t.plannedEnding}
/> validateTicket={validateTicket}
</li> archiveTicket={archiveTicket}
))} />
</li>
))}
</ul> </ul>
</div> </div>
</> </>

View file

@ -37,12 +37,16 @@ export const ProjectController: FC = () => {
const tickets: Ticket[] = [ const tickets: Ticket[] = [
{ {
id: 1, id: 1,
title: "Ticket #1", title: "Client objective meeting",
description: "Client objective meeting",
plannedEnding: "2020-02-17 15:51:02.787373",
status: "Done" status: "Done"
}, },
{ {
id: 2, id: 2,
title: "Ticket #2", title: "Assemble Outcomes Report for client",
description: "Assemble Outcomes Report for client",
plannedEnding: "2020-02-27 15:51:02.787373",
status: "To Do" status: "To Do"
} }
]; ];

View file

@ -1,13 +0,0 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}

View file

@ -1,6 +1,5 @@
import React from "react"; import React from "react";
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
import "./index.css";
import App from "./App"; import App from "./App";
import * as serviceWorker from "./serviceWorker"; import * as serviceWorker from "./serviceWorker";

View file

@ -41,7 +41,6 @@ export const ProjectPage: FC<IProps> = ({ viewModel }) => {
tabNames={tabNames} tabNames={tabNames}
tickets={tickets} tickets={tickets}
files={files} files={files}
remainingDays={remainingDays}
activities={activities} activities={activities}
/> />
</div> </div>

View file

@ -1,5 +1,7 @@
export interface Ticket { export interface Ticket {
id: number; id: number;
title: string; title: string;
description: string;
status: string; status: string;
plannedEnding: string;
} }

View file

@ -0,0 +1,7 @@
export const getRemainingdays: (endDate: string) => number = (
endDate: string
) => {
let endingDate: Date = new Date(endDate);
let today: Date = new Date();
return Math.abs(endingDate.getDate() - today.getDate());
};

View file

@ -5,6 +5,7 @@ import { Project } from "../types/Project";
import { AppFile } from "../types/AppFile"; import { AppFile } from "../types/AppFile";
import { Activity } from "../types/Activity"; import { Activity } from "../types/Activity";
import { User } from "../types/User"; import { User } from "../types/User";
import { getRemainingdays } from "../utils/methods";
export default class ProjectVM { export default class ProjectVM {
public id: number; public id: number;
@ -12,7 +13,6 @@ export default class ProjectVM {
public description: string; public description: string;
public value: number; public value: number;
public tickets: Ticket[]; public tickets: Ticket[];
// public avatars: string[];
public users: User[]; public users: User[];
public ticketsTotalCount: number; public ticketsTotalCount: number;
public ticketsDone: number; public ticketsDone: number;
@ -35,7 +35,6 @@ export default class ProjectVM {
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.avatars = project.users.map(u => u.picture);
this.users = project.users; this.users = project.users;
this.value = project.progression; this.value = project.progression;
this.tickets = project.tickets; this.tickets = project.tickets;
@ -43,12 +42,6 @@ export default class ProjectVM {
this.ticketsDone = this.tickets.filter(t => t.status === "Done").length; this.ticketsDone = this.tickets.filter(t => t.status === "Done").length;
this.files = project.files; this.files = project.files;
this.activities = project.activities; this.activities = project.activities;
this.remainingDays = getRemainingdays(project.plannedEnding);
let endingDate: Date = new Date(project.plannedEnding);
let today: Date = new Date();
let plannedEnding: number = Math.abs(
endingDate.getDate() - today.getDate()
);
this.remainingDays = plannedEnding;
} }
} }