mirror of
https://github.com/rjNemo/ticket_manager
synced 2026-06-06 00:36:39 +00:00
Add input File design, filtering for project page tabs
This commit is contained in:
parent
14e47378dd
commit
2419627400
15 changed files with 156 additions and 59 deletions
|
|
@ -1,18 +1,26 @@
|
|||
import React, { FC } from "react";
|
||||
import { FloatingButton } from "./FloatingButton";
|
||||
import React, { FC, useState, ChangeEvent } from "react";
|
||||
import { ActivityCollection } from "./ActivityCollection";
|
||||
import { Activity } from "../types/Activity";
|
||||
import { FilterBar } from "./FilterBar";
|
||||
|
||||
type IProps = {
|
||||
activities: Activity[];
|
||||
};
|
||||
|
||||
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 (
|
||||
<>
|
||||
<div className="row valign-wrapper">
|
||||
<h3>Activity</h3>
|
||||
<FloatingButton color=" blue-grey lighten-4" size="" />
|
||||
<FilterBar filterText={filterText} handleChange={handleChange} />
|
||||
</div>
|
||||
<ActivityCollection activities={activities} />
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -1,21 +1,28 @@
|
|||
import React, { FC } from "react";
|
||||
import React, { FC, useState, ChangeEvent } from "react";
|
||||
import { AppFile } from "../types/AppFile";
|
||||
import { FloatingButton } from "./FloatingButton";
|
||||
import { FileCollection } from "./FileCollection";
|
||||
import { DropZone } from "./DropZone";
|
||||
import { InputFile } from "./InputFile";
|
||||
import { FilterBar } from "./FilterBar";
|
||||
|
||||
type IProps = {
|
||||
files: AppFile[];
|
||||
};
|
||||
|
||||
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 (
|
||||
<>
|
||||
<div className="row valign-wrapper">
|
||||
<h3>Files</h3>
|
||||
<FloatingButton color=" blue-grey lighten-4" size="" />
|
||||
<FilterBar filterText={filterText} handleChange={handleChange} />
|
||||
</div>
|
||||
<DropZone />
|
||||
<InputFile />
|
||||
<FileCollection files={files} />
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
};
|
||||
39
client/src/components/FilterBar.tsx
Normal file
39
client/src/components/FilterBar.tsx
Normal 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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
import React, { FC, MouseEvent } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { getRemainingdays } from "../utils/methods";
|
||||
|
||||
interface IProps {
|
||||
title: string;
|
||||
remainingDays?: number;
|
||||
remainingDays: string;
|
||||
validateTicket: (event: MouseEvent) => void;
|
||||
archiveTicket: (event: MouseEvent) => void;
|
||||
}
|
||||
|
|
@ -26,7 +27,7 @@ export const HorizontalCard: FC<IProps> = ({
|
|||
</Link>
|
||||
</h6>
|
||||
</div>
|
||||
<span>Due {remainingDays} days</span>
|
||||
<span>Due {getRemainingdays(remainingDays)} days</span>
|
||||
<div className="right">
|
||||
<Link to="#">
|
||||
<i className="material-icons" onClick={validateTicket}>
|
||||
|
|
|
|||
29
client/src/components/InputFile.tsx
Normal file
29
client/src/components/InputFile.tsx
Normal 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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
@ -18,7 +18,6 @@ interface IProps {
|
|||
|
||||
export const TabRouter: FC<IProps> = ({
|
||||
tickets,
|
||||
remainingDays,
|
||||
tabNames,
|
||||
files,
|
||||
activities
|
||||
|
|
@ -32,7 +31,7 @@ export const TabRouter: FC<IProps> = ({
|
|||
<Redirect from={url} to={`${url}/tickets`} />
|
||||
|
||||
<Route path={`${url}/tickets`}>
|
||||
<TicketList tickets={tickets} remainingDays={remainingDays} />
|
||||
<TicketList tickets={tickets} />
|
||||
</Route>
|
||||
|
||||
<Route path={`${url}/files`}>
|
||||
|
|
|
|||
|
|
@ -1,35 +1,65 @@
|
|||
import React, { FC } from "react";
|
||||
import React, { FC, useState, ChangeEvent } from "react";
|
||||
import { Ticket } from "../types/Ticket";
|
||||
import { FloatingButton } from "./FloatingButton";
|
||||
import { HorizontalCard } from "./HorizontalCard";
|
||||
import { FilterBar } from "./FilterBar";
|
||||
|
||||
type TicketListProps = {
|
||||
tickets: Ticket[];
|
||||
remainingDays?: number;
|
||||
};
|
||||
|
||||
export const TicketList: FC<TicketListProps> = ({ tickets, remainingDays }) => {
|
||||
export const TicketList: FC<TicketListProps> = ({ tickets }) => {
|
||||
const archiveTicket = () => {};
|
||||
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 (
|
||||
<>
|
||||
<div className="row valign-wrapper">
|
||||
<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 className="col s12 grey">
|
||||
<ul>
|
||||
{tickets.map((t: Ticket) => (
|
||||
<li key={t.id}>
|
||||
<HorizontalCard
|
||||
title={t.title}
|
||||
remainingDays={remainingDays}
|
||||
validateTicket={validateTicket}
|
||||
archiveTicket={archiveTicket}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
{tickets
|
||||
.filter(t => t.title.includes(filterText))
|
||||
.map((t: Ticket) => (
|
||||
<li key={t.id}>
|
||||
<HorizontalCard
|
||||
title={t.title}
|
||||
remainingDays={t.plannedEnding}
|
||||
validateTicket={validateTicket}
|
||||
archiveTicket={archiveTicket}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -37,12 +37,16 @@ export const ProjectController: FC = () => {
|
|||
const tickets: Ticket[] = [
|
||||
{
|
||||
id: 1,
|
||||
title: "Ticket #1",
|
||||
title: "Client objective meeting",
|
||||
description: "Client objective meeting",
|
||||
plannedEnding: "2020-02-17 15:51:02.787373",
|
||||
status: "Done"
|
||||
},
|
||||
{
|
||||
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"
|
||||
}
|
||||
];
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import "./index.css";
|
||||
import App from "./App";
|
||||
import * as serviceWorker from "./serviceWorker";
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@ export const ProjectPage: FC<IProps> = ({ viewModel }) => {
|
|||
tabNames={tabNames}
|
||||
tickets={tickets}
|
||||
files={files}
|
||||
remainingDays={remainingDays}
|
||||
activities={activities}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
export interface Ticket {
|
||||
id: number;
|
||||
title: string;
|
||||
description: string;
|
||||
status: string;
|
||||
plannedEnding: string;
|
||||
}
|
||||
|
|
|
|||
7
client/src/utils/methods.ts
Normal file
7
client/src/utils/methods.ts
Normal 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());
|
||||
};
|
||||
|
|
@ -5,6 +5,7 @@ import { Project } from "../types/Project";
|
|||
import { AppFile } from "../types/AppFile";
|
||||
import { Activity } from "../types/Activity";
|
||||
import { User } from "../types/User";
|
||||
import { getRemainingdays } from "../utils/methods";
|
||||
|
||||
export default class ProjectVM {
|
||||
public id: number;
|
||||
|
|
@ -12,7 +13,6 @@ export default class ProjectVM {
|
|||
public description: string;
|
||||
public value: number;
|
||||
public tickets: Ticket[];
|
||||
// public avatars: string[];
|
||||
public users: User[];
|
||||
public ticketsTotalCount: number;
|
||||
public ticketsDone: number;
|
||||
|
|
@ -35,7 +35,6 @@ export default class ProjectVM {
|
|||
this.id = project.id;
|
||||
this.title = project.title;
|
||||
this.description = project.description;
|
||||
// this.avatars = project.users.map(u => u.picture);
|
||||
this.users = project.users;
|
||||
this.value = project.progression;
|
||||
this.tickets = project.tickets;
|
||||
|
|
@ -43,12 +42,6 @@ export default class ProjectVM {
|
|||
this.ticketsDone = this.tickets.filter(t => t.status === "Done").length;
|
||||
this.files = project.files;
|
||||
this.activities = project.activities;
|
||||
|
||||
let endingDate: Date = new Date(project.plannedEnding);
|
||||
let today: Date = new Date();
|
||||
let plannedEnding: number = Math.abs(
|
||||
endingDate.getDate() - today.getDate()
|
||||
);
|
||||
this.remainingDays = plannedEnding;
|
||||
this.remainingDays = getRemainingdays(project.plannedEnding);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue