mirror of
https://github.com/rjNemo/ticket_manager
synced 2026-06-12 11:46:40 +00:00
Static Project page done. Activity & Files tabs added. Preloader added. Changed History to Activity
This commit is contained in:
parent
b861a65892
commit
0990e59d64
18 changed files with 288 additions and 16 deletions
|
|
@ -44,3 +44,4 @@
|
||||||
- update assignments automatically from context
|
- update assignments automatically from context
|
||||||
- use PATCH instead of PUT
|
- use PATCH instead of PUT
|
||||||
- logging
|
- logging
|
||||||
|
- check useRef, useReducer, dispatch
|
||||||
|
|
|
||||||
35
client/src/components/ActivityCollection.tsx
Normal file
35
client/src/components/ActivityCollection.tsx
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import React, { FC } from "react";
|
||||||
|
import { Activity } from "../types/Activity";
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
activities: Activity[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ActivityCollection: FC<IProps> = ({ activities }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ul className="collection">
|
||||||
|
{activities.map((f: Activity) => (
|
||||||
|
<ActivityEntry activity={f} />
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
type IFProps = {
|
||||||
|
activity: Activity;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ActivityEntry: FC<IFProps> = ({ activity }) => {
|
||||||
|
return (
|
||||||
|
<li key={activity.id} className="collection-item avatar">
|
||||||
|
<img src={activity.user.picture} alt="" className="circle" />
|
||||||
|
{/* <i className="material-icons circle">folder</i> */}
|
||||||
|
<span className="title">
|
||||||
|
{activity.user.firstName} {activity.description} {activity.ticket.title}
|
||||||
|
</span>
|
||||||
|
<p>{activity.date.toDateString()}</p>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
};
|
||||||
20
client/src/components/ActivityList.tsx
Normal file
20
client/src/components/ActivityList.tsx
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
import React, { FC } from "react";
|
||||||
|
import { FloatingButton } from "./FloatingButton";
|
||||||
|
import { ActivityCollection } from "./ActivityCollection";
|
||||||
|
import { Activity } from "../types/Activity";
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
activities: Activity[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ActivityList: FC<IProps> = ({ activities }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="row valign-wrapper">
|
||||||
|
<h3>Activity</h3>
|
||||||
|
<FloatingButton color=" blue-grey lighten-4" size="" />
|
||||||
|
</div>
|
||||||
|
<ActivityCollection activities={activities} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
22
client/src/components/AppFileList.tsx
Normal file
22
client/src/components/AppFileList.tsx
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
import React, { FC } from "react";
|
||||||
|
import { AppFile } from "../types/AppFile";
|
||||||
|
import { FloatingButton } from "./FloatingButton";
|
||||||
|
import { FileCollection } from "./FileCollection";
|
||||||
|
import { DropZone } from "./DropZone";
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
files: AppFile[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const FileList: FC<IProps> = ({ files }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="row valign-wrapper">
|
||||||
|
<h3>Files</h3>
|
||||||
|
<FloatingButton color=" blue-grey lighten-4" size="" />
|
||||||
|
</div>
|
||||||
|
<DropZone />
|
||||||
|
<FileCollection files={files} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
7
client/src/components/DropZone.tsx
Normal file
7
client/src/components/DropZone.tsx
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
import React, { FC } from "react";
|
||||||
|
|
||||||
|
type IProps = {};
|
||||||
|
|
||||||
|
export const DropZone: FC<IProps> = () => {
|
||||||
|
return <div className="copy">Drag & Drop your files here.</div>;
|
||||||
|
};
|
||||||
38
client/src/components/FileCollection.tsx
Normal file
38
client/src/components/FileCollection.tsx
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
import React, { FC } from "react";
|
||||||
|
import { AppFile } from "../types/AppFile";
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
files: AppFile[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const FileCollection: FC<IProps> = ({ files }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ul className="collection">
|
||||||
|
{files.map((f: AppFile) => (
|
||||||
|
<FileEntry file={f} />
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
type IFProps = {
|
||||||
|
file: AppFile;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const FileEntry: FC<IFProps> = ({ file }) => {
|
||||||
|
return (
|
||||||
|
<li key={file.id} className="collection-item avatar">
|
||||||
|
{/* <img src={require("../images/user_1.jpg")} alt="" className="circle" /> */}
|
||||||
|
<i className="material-icons circle">folder</i>
|
||||||
|
<span className="title">{file.name}</span>
|
||||||
|
<p>
|
||||||
|
{file.size}kb {file.format}
|
||||||
|
</p>
|
||||||
|
<a href="#!" className="secondary-content">
|
||||||
|
<i className="material-icons">more_vert</i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
};
|
||||||
55
client/src/components/Preloader.tsx
Normal file
55
client/src/components/Preloader.tsx
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
import React, { FC } from "react";
|
||||||
|
|
||||||
|
export const Preloader: FC = () => {
|
||||||
|
return (
|
||||||
|
<div className="preloader-wrapper big active">
|
||||||
|
<div className="spinner-layer spinner-blue">
|
||||||
|
<div className="circle-clipper left">
|
||||||
|
<div className="circle"></div>
|
||||||
|
</div>
|
||||||
|
<div className="gap-patch">
|
||||||
|
<div className="circle"></div>
|
||||||
|
</div>
|
||||||
|
<div className="circle-clipper right">
|
||||||
|
<div className="circle"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="spinner-layer spinner-red">
|
||||||
|
<div className="circle-clipper left">
|
||||||
|
<div className="circle"></div>
|
||||||
|
</div>
|
||||||
|
<div className="gap-patch">
|
||||||
|
<div className="circle"></div>
|
||||||
|
</div>
|
||||||
|
<div className="circle-clipper right">
|
||||||
|
<div className="circle"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="spinner-layer spinner-yellow">
|
||||||
|
<div className="circle-clipper left">
|
||||||
|
<div className="circle"></div>
|
||||||
|
</div>
|
||||||
|
<div className="gap-patch">
|
||||||
|
<div className="circle"></div>
|
||||||
|
</div>
|
||||||
|
<div className="circle-clipper right">
|
||||||
|
<div className="circle"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="spinner-layer spinner-green">
|
||||||
|
<div className="circle-clipper left">
|
||||||
|
<div className="circle"></div>
|
||||||
|
</div>
|
||||||
|
<div className="gap-patch">
|
||||||
|
<div className="circle"></div>
|
||||||
|
</div>
|
||||||
|
<div className="circle-clipper right">
|
||||||
|
<div className="circle"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -1,16 +1,28 @@
|
||||||
import React, { FC } from "react";
|
import React, { FC } from "react";
|
||||||
import { TabRouterHeader } from "./TabRouterHeader";
|
import { TabRouterHeader } from "./TabRouterHeader";
|
||||||
import { TicketList } from "./TicketList";
|
import { TicketList } from "./TicketList";
|
||||||
|
import { FileList } from "./AppFileList";
|
||||||
import { Ticket } from "../types/Ticket";
|
import { Ticket } from "../types/Ticket";
|
||||||
|
import { AppFile } from "../types/AppFile";
|
||||||
import { Switch, Route, useRouteMatch, Redirect } from "react-router-dom";
|
import { Switch, Route, useRouteMatch, Redirect } from "react-router-dom";
|
||||||
|
import { ActivityList } from "./ActivityList";
|
||||||
|
import { Activity } from "../types/Activity";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
tickets: Ticket[];
|
tickets: Ticket[];
|
||||||
remainingDays?: number;
|
remainingDays?: number;
|
||||||
tabNames: string[];
|
tabNames: string[];
|
||||||
|
files: AppFile[];
|
||||||
|
activities: Activity[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TabRouter: FC<IProps> = ({ tickets, remainingDays, tabNames }) => {
|
export const TabRouter: FC<IProps> = ({
|
||||||
|
tickets,
|
||||||
|
remainingDays,
|
||||||
|
tabNames,
|
||||||
|
files,
|
||||||
|
activities
|
||||||
|
}) => {
|
||||||
const { url } = useRouteMatch();
|
const { url } = useRouteMatch();
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
@ -25,11 +37,11 @@ export const TabRouter: FC<IProps> = ({ tickets, remainingDays, tabNames }) => {
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
<Route path={`${url}/files`}>
|
<Route path={`${url}/files`}>
|
||||||
{/* <TicketList tickets={tickets} /> */}
|
<FileList files={files} />
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
<Route path={`${url}/activity`}>
|
<Route path={`${url}/activity`}>
|
||||||
{/* <TicketList tickets={tickets} /> */}
|
<ActivityList activities={activities} />
|
||||||
</Route>
|
</Route>
|
||||||
</div>
|
</div>
|
||||||
</Switch>
|
</Switch>
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ export const TabRouterHeader: FC<IProps> = ({
|
||||||
nTabs,
|
nTabs,
|
||||||
tabNames
|
tabNames
|
||||||
}) => {
|
}) => {
|
||||||
const [isActive, setIsActive] = useState(0);
|
const [isActive, setIsActive] = useState<number>(0);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -18,7 +18,9 @@ export const ProjectPage: FC<IProps> = ({ viewModel }) => {
|
||||||
tickets,
|
tickets,
|
||||||
ticketsDone,
|
ticketsDone,
|
||||||
ticketsTotalCount,
|
ticketsTotalCount,
|
||||||
remainingDays
|
remainingDays,
|
||||||
|
files,
|
||||||
|
activities
|
||||||
} = viewModel;
|
} = viewModel;
|
||||||
const tabNames: string[] = ["Tickets", "Files", "Activity"];
|
const tabNames: string[] = ["Tickets", "Files", "Activity"];
|
||||||
return (
|
return (
|
||||||
|
|
@ -38,7 +40,9 @@ export const ProjectPage: FC<IProps> = ({ viewModel }) => {
|
||||||
<TabRouter
|
<TabRouter
|
||||||
tabNames={tabNames}
|
tabNames={tabNames}
|
||||||
tickets={tickets}
|
tickets={tickets}
|
||||||
|
files={files}
|
||||||
remainingDays={remainingDays}
|
remainingDays={remainingDays}
|
||||||
|
activities={activities}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
10
client/src/types/Activity.ts
Normal file
10
client/src/types/Activity.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { User } from "./User";
|
||||||
|
import { Ticket } from "./Ticket";
|
||||||
|
|
||||||
|
export interface Activity {
|
||||||
|
id: number;
|
||||||
|
description: string;
|
||||||
|
date: Date;
|
||||||
|
user: User;
|
||||||
|
ticket: Ticket;
|
||||||
|
}
|
||||||
9
client/src/types/AppFile.ts
Normal file
9
client/src/types/AppFile.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { User } from "./User";
|
||||||
|
|
||||||
|
export interface AppFile {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
format: string;
|
||||||
|
size: number;
|
||||||
|
}
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
export interface File {
|
|
||||||
Id: number;
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
export interface History {
|
|
||||||
Id: number;
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import { Ticket } from "./Ticket";
|
import { Ticket } from "./Ticket";
|
||||||
import { User } from "./User";
|
import { User } from "./User";
|
||||||
|
import { AppFile } from "./AppFile";
|
||||||
|
import { Activity } from "./Activity";
|
||||||
|
|
||||||
export interface Project {
|
export interface Project {
|
||||||
id: number;
|
id: number;
|
||||||
|
|
@ -9,4 +11,6 @@ export interface Project {
|
||||||
tickets: Ticket[];
|
tickets: Ticket[];
|
||||||
users: User[];
|
users: User[];
|
||||||
plannedEnding: string;
|
plannedEnding: string;
|
||||||
|
files: AppFile[];
|
||||||
|
activities: Activity[];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
export interface User {
|
export interface User {
|
||||||
id: string;
|
id: string;
|
||||||
picture: string;
|
picture: string;
|
||||||
|
firstName: string;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ import { Ticket } from "../types/Ticket";
|
||||||
import { Project } from "../types/Project";
|
import { Project } from "../types/Project";
|
||||||
import { Constants } from "../utils/Constants";
|
import { Constants } from "../utils/Constants";
|
||||||
import { User } from "../types/User";
|
import { User } from "../types/User";
|
||||||
|
import { AppFile } from "../types/AppFile";
|
||||||
|
import { Activity } from "../types/Activity";
|
||||||
|
|
||||||
export default class ProjectVM {
|
export default class ProjectVM {
|
||||||
public id: number;
|
public id: number;
|
||||||
|
|
@ -13,6 +15,8 @@ export default class ProjectVM {
|
||||||
public ticketsTotalCount: number;
|
public ticketsTotalCount: number;
|
||||||
public ticketsDone: number;
|
public ticketsDone: number;
|
||||||
public remainingDays: number;
|
public remainingDays: number;
|
||||||
|
public files: AppFile[];
|
||||||
|
public activities: Activity[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* getMembers
|
* getMembers
|
||||||
|
|
@ -34,13 +38,14 @@ export default class ProjectVM {
|
||||||
this.tickets = project.tickets;
|
this.tickets = project.tickets;
|
||||||
this.ticketsTotalCount = this.tickets.length;
|
this.ticketsTotalCount = this.tickets.length;
|
||||||
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.activities = project.activities;
|
||||||
|
|
||||||
let endingDate: Date = new Date(project.plannedEnding);
|
let endingDate: Date = new Date(project.plannedEnding);
|
||||||
let today: Date = new Date();
|
let today: Date = new Date();
|
||||||
let plannedEnding: number = Math.abs(
|
let plannedEnding: number = Math.abs(
|
||||||
endingDate.getDate() - today.getDate()
|
endingDate.getDate() - today.getDate()
|
||||||
);
|
);
|
||||||
|
|
||||||
this.remainingDays = plannedEnding;
|
this.remainingDays = plannedEnding;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue