432 lines
13 KiB
TypeScript
432 lines
13 KiB
TypeScript
import React, { useState } from "react";
|
|
import { EventClickArg } from "@fullcalendar/core";
|
|
import { DateClickArg } from "@fullcalendar/interaction";
|
|
import { useGlobalState } from "providers";
|
|
import { useMobile, useSnackbar } from "hooks";
|
|
import { useTaskAPI } from "providers";
|
|
import { useEffect, useCallback } from "react";
|
|
import TaskCalendar from "./TaskCalendar";
|
|
import TaskList from "./TaskList";
|
|
import {
|
|
Box,
|
|
Button,
|
|
Fab,
|
|
Grid2 as Grid,
|
|
LinearProgress,
|
|
Theme,
|
|
Typography
|
|
} from "@mui/material";
|
|
import { Task } from "models";
|
|
import TaskSettings from "./TaskSettings";
|
|
import { DayPicker } from "react-day-picker";
|
|
import "react-day-picker/dist/style.css";
|
|
import AddIcon from "@mui/icons-material/Add";
|
|
import TaskDrawer from "./TaskDrawer";
|
|
import moment from "moment";
|
|
import { makeStyles } from "@mui/styles";
|
|
|
|
interface ViewProps {
|
|
// objectKey?: string; //only used to save it for the object --Deprecated with new permission structure--
|
|
keys?: string[]//the keys and types are used to give th object permissions to the task, that is how they will be linked now
|
|
types?: string[]
|
|
label?: string;
|
|
drawerView?: boolean;
|
|
// loadKeys?: string[]; //if you want to load tasks for a specific object(s) --Deprecated with new permission structure-- it should use the keys and types now
|
|
overlayButton?: boolean;
|
|
}
|
|
|
|
interface TabPanelProps {
|
|
children?: React.ReactNode;
|
|
index: any;
|
|
value: any;
|
|
}
|
|
|
|
const useStyles = makeStyles((theme: Theme) => {
|
|
return ({
|
|
active: {
|
|
color: theme.palette.getContrastText(theme.palette.secondary.main),
|
|
backgroundColor: theme.palette.secondary.main,
|
|
width: theme.spacing(5),
|
|
height: theme.spacing(5),
|
|
border: 0,
|
|
"&:hover": {
|
|
backgroundColor: theme.palette.secondary.main
|
|
}
|
|
},
|
|
fab: {
|
|
zIndex: 20,
|
|
background: theme.palette.primary.main,
|
|
"&:hover": {
|
|
background: theme.palette.primary.main
|
|
},
|
|
"&:focus": {
|
|
background: theme.palette.primary.main
|
|
},
|
|
position: "absolute",
|
|
bottom: theme.spacing(8), //for mobile navigator
|
|
right: theme.spacing(2),
|
|
[theme.breakpoints.up("sm")]: {
|
|
bottom: theme.spacing(2)
|
|
}
|
|
},
|
|
overlayFab: {
|
|
zIndex: 20,
|
|
background: theme.palette.primary.main,
|
|
"&:hover": {
|
|
background: theme.palette.primary.main
|
|
},
|
|
"&:focus": {
|
|
background: theme.palette.primary.main
|
|
},
|
|
position: "fixed",
|
|
bottom: theme.spacing(8), //for mobile navigator
|
|
right: theme.spacing(2),
|
|
[theme.breakpoints.up("sm")]: {
|
|
bottom: theme.spacing(2)
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
export default function TaskViewer(props: ViewProps) {
|
|
const {
|
|
//objectKey,
|
|
label,
|
|
drawerView,
|
|
//loadKeys,
|
|
keys,
|
|
types,
|
|
overlayButton
|
|
} = props;
|
|
const [{ user, as }] = useGlobalState();
|
|
const taskAPI = useTaskAPI();
|
|
const { openSnack } = useSnackbar();
|
|
const [tasks, setTasks] = useState<Map<string, Task>>(new Map<string, Task>([]));
|
|
const [viewTask, setViewTask] = useState<Task>();
|
|
const [newTaskDialog, setNewTaskDialog] = useState(false);
|
|
const [editTask, setEditTask] = useState<Task>();
|
|
const [loaded, setLoaded] = useState(false);
|
|
const [currentDate, setCurrentDate] = useState(new Date());
|
|
const isMobile = useMobile();
|
|
const [value, setValue] = useState(0);
|
|
const classes = useStyles();
|
|
const [expand, setExpand] = useState(false);
|
|
const [hideCal, setHideCal] = useState(false);
|
|
const [openDrawer, setOpenDrawer] = useState(false);
|
|
const [nextMonth, setNextMonth] = useState(
|
|
moment(currentDate)
|
|
.add(2, "month")
|
|
.format("YYYY-MM")
|
|
);
|
|
const [prevMonth, setPrevMonth] = useState(
|
|
moment(currentDate)
|
|
.subtract(1, "month")
|
|
.format("YYYY-MM")
|
|
);
|
|
|
|
//initial states for the task variables
|
|
const [startDate, setStartDate] = useState<string>();
|
|
|
|
//functions for opening and closing the newTaskDialog
|
|
const openCalendarDialog = (args: DateClickArg) => {
|
|
//assign the date of task when the dialog box is opened
|
|
setStartDate(args.dateStr);
|
|
openDialog();
|
|
};
|
|
const openDialog = () => {
|
|
setNewTaskDialog(true);
|
|
};
|
|
|
|
//functions for opening and closing the selectedTaskDialog if selected from the calendar
|
|
const openSelectedTask = (taskId: string) => {
|
|
let task = tasks.get(taskId);
|
|
if (task) {
|
|
setOpenDrawer(true);
|
|
setViewTask(task);
|
|
}
|
|
};
|
|
|
|
//loads tasks from the backend database
|
|
const loadTasks = useCallback(() => {
|
|
if (!user.id()) return;
|
|
let temp = new Map<string, Task>();
|
|
taskAPI
|
|
.listTasks(50, 0, "asc", "start", undefined, undefined, as, prevMonth, nextMonth, keys, types)
|
|
.then(resp => {
|
|
if(resp.data.tasks){
|
|
resp.data.tasks.forEach(task => {
|
|
if (task.settings) {
|
|
temp.set(task.key, Task.any(task));
|
|
}
|
|
});
|
|
}
|
|
setTasks(temp);
|
|
setLoaded(true);
|
|
})
|
|
.catch(err => {
|
|
openSnack("Failed to load tasks");
|
|
});
|
|
}, [taskAPI, as, user, openSnack, nextMonth, prevMonth, keys, types]);
|
|
|
|
useEffect(() => {
|
|
// if (drawerView) {
|
|
// setTasks(new Map<string, Task>());
|
|
// setValue(1);
|
|
// loadMultitask();
|
|
// } else {
|
|
// loadTasks();
|
|
// }
|
|
loadTasks()
|
|
}, [loadTasks, as, drawerView]);
|
|
|
|
const markComplete = (task: Task) => {
|
|
task.settings.complete = !task.settings.complete;
|
|
|
|
taskAPI
|
|
.updateTask(task.key, task.settings, undefined, as)
|
|
.then(resp => {
|
|
openSnack("Task Updated");
|
|
loadTasks();
|
|
})
|
|
.catch(err => {
|
|
openSnack("Failed to update task");
|
|
});
|
|
};
|
|
|
|
const deleteTask = (task: Task) => {
|
|
taskAPI
|
|
.removeTask(task.key, as)
|
|
.then(resp => {
|
|
openSnack("Task Removed");
|
|
loadTasks();
|
|
})
|
|
.catch(err => {
|
|
openSnack("Failed to remove task");
|
|
});
|
|
};
|
|
|
|
function TabPanelMine(props: TabPanelProps) {
|
|
const { children, value, index, ...other } = props;
|
|
|
|
return (
|
|
<div
|
|
role="tabpanel"
|
|
hidden={value !== index}
|
|
aria-labelledby={`simple-tab-${index}`}
|
|
{...other}>
|
|
{value === index && <React.Fragment>{children}</React.Fragment>}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const resize = () => {
|
|
setHideCal(!hideCal);
|
|
setExpand(!expand);
|
|
};
|
|
|
|
const viewer = () => {
|
|
return (
|
|
<React.Fragment>
|
|
{isMobile || drawerView ? (
|
|
<Box height={"100%"} position={"relative"}>
|
|
<TabPanelMine value={value} index={0}>
|
|
<Box paddingBottom={2}>
|
|
<TaskCalendar
|
|
tasks={Array.from(tasks.values())}
|
|
dateClickMethod={openCalendarDialog}
|
|
taskClickMethod={(arg: EventClickArg) => openSelectedTask(arg.event.id)}
|
|
centerToolbar={"hide"}
|
|
initialView="dayGridWeek"
|
|
initialDate={currentDate}
|
|
calHeight={"22vh"}
|
|
hideToday
|
|
hideCal={hideCal}
|
|
hideCalCallback={resize}
|
|
changeDateCallback={date => {
|
|
if (currentDate.getMonth() !== date.getMonth()) {
|
|
setCurrentDate(date);
|
|
setNextMonth(
|
|
moment(date)
|
|
.add(2, "month")
|
|
.format("YYYY-MM")
|
|
);
|
|
setPrevMonth(
|
|
moment(date)
|
|
.subtract(1, "month")
|
|
.format("YYYY-MM")
|
|
);
|
|
loadTasks();
|
|
}
|
|
setCurrentDate(date);
|
|
}}
|
|
/>
|
|
</Box>
|
|
|
|
<TaskCalendar
|
|
tasks={Array.from(tasks.values())}
|
|
dateClickMethod={openCalendarDialog}
|
|
taskClickMethod={(arg: EventClickArg) => openSelectedTask(arg.event.id)}
|
|
initialView="timeGridDay"
|
|
initialDate={currentDate}
|
|
calHeight={expand ? "59vh" : "37vh"}
|
|
hideTitle
|
|
changeDateCallback={date => {
|
|
if (currentDate.getMonth() !== date.getMonth()) {
|
|
setCurrentDate(date);
|
|
setNextMonth(
|
|
moment(date)
|
|
.add(2, "month")
|
|
.format("YYYY-MM")
|
|
);
|
|
setPrevMonth(
|
|
moment(date)
|
|
.subtract(1, "month")
|
|
.format("YYYY-MM")
|
|
);
|
|
loadTasks();
|
|
}
|
|
setCurrentDate(date);
|
|
}}
|
|
/>
|
|
</TabPanelMine>
|
|
<TabPanelMine value={value} index={1}>
|
|
<TaskList
|
|
tasks={Array.from(tasks.values())}
|
|
label={label}
|
|
markComplete={markComplete}
|
|
deleteTask={deleteTask}
|
|
reLoad={() => loadTasks()}
|
|
dateToView={drawerView ? undefined : currentDate}
|
|
openTask={(taskId: string) => openSelectedTask(taskId)}
|
|
keys={[]}
|
|
types={[]}
|
|
/>
|
|
</TabPanelMine>
|
|
<Fab
|
|
onClick={openDialog}
|
|
aria-label="Create Task"
|
|
className={overlayButton ? classes.overlayFab : classes.fab}
|
|
size={isMobile ? "medium" : "large"}>
|
|
<AddIcon />
|
|
</Fab>
|
|
</Box>
|
|
) : (
|
|
<Grid container direction="row" alignContent="center" alignItems="center">
|
|
<Grid size={{lg: 3, md: 4}}>
|
|
<Box textAlign={"center"}>
|
|
<Button
|
|
onClick={openDialog}
|
|
style={{ margin: 5, borderRadius: 20, backgroundColor: "green", width: "50%" }}>
|
|
<Typography style={{ fontWeight: 750, color: "white" }}>+ Task</Typography>
|
|
</Button>
|
|
</Box>
|
|
<Box
|
|
style={{
|
|
width: "300px",
|
|
height: "280px",
|
|
margin: "auto"
|
|
}}>
|
|
<DayPicker
|
|
mode="single"
|
|
selected={currentDate}
|
|
onSelect={date => {
|
|
if (!date) return;
|
|
if (currentDate.getMonth() !== date.getMonth()) {
|
|
setCurrentDate(date);
|
|
setNextMonth(
|
|
moment(date)
|
|
.add(2, "month")
|
|
.format("YYYY-MM")
|
|
);
|
|
setPrevMonth(
|
|
moment(date)
|
|
.subtract(1, "month")
|
|
.format("YYYY-MM")
|
|
);
|
|
loadTasks();
|
|
}
|
|
setCurrentDate(date);
|
|
}}
|
|
/>
|
|
</Box>
|
|
<Box marginY={2}>
|
|
<TaskList
|
|
tasks={Array.from(tasks.values())}
|
|
label={label}
|
|
markComplete={markComplete}
|
|
deleteTask={deleteTask}
|
|
reLoad={() => loadTasks()}
|
|
listHeight={"40vh"}
|
|
dateToView={currentDate}
|
|
openTask={(taskId: string) => openSelectedTask(taskId)}
|
|
keys={[]}
|
|
types={[]}
|
|
/>
|
|
</Box>
|
|
</Grid>
|
|
<Grid size={{lg:9, md:8}}>
|
|
<TaskCalendar
|
|
tasks={Array.from(tasks.values())}
|
|
dateClickMethod={openCalendarDialog}
|
|
taskClickMethod={(arg: EventClickArg) => openSelectedTask(arg.event.id)}
|
|
initialView="timeGridWeek"
|
|
centerToolbar="dayGridMonth,timeGridWeek,timeGridDay"
|
|
initialDate={currentDate}
|
|
calHeight={"80vh"}
|
|
changeDateCallback={date => {
|
|
if (currentDate.getMonth() !== date.getMonth()) {
|
|
setCurrentDate(date);
|
|
setNextMonth(
|
|
moment(date)
|
|
.add(2, "month")
|
|
.format("YYYY-MM")
|
|
);
|
|
setPrevMonth(
|
|
moment(date)
|
|
.subtract(1, "month")
|
|
.format("YYYY-MM")
|
|
);
|
|
loadTasks();
|
|
}
|
|
setCurrentDate(date);
|
|
}}
|
|
/>
|
|
</Grid>
|
|
</Grid>
|
|
)}
|
|
</React.Fragment>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<Box height={"100%"}>
|
|
{!loaded ? <LinearProgress style={{ marginTop: 20 }} /> : viewer()}
|
|
<TaskSettings
|
|
open={newTaskDialog}
|
|
task={editTask}
|
|
keys={keys}
|
|
types={types}
|
|
startDate={startDate}
|
|
onClose={r => {
|
|
loadTasks();
|
|
setEditTask(undefined);
|
|
setNewTaskDialog(false);
|
|
}}
|
|
/>
|
|
{viewTask &&
|
|
<TaskDrawer
|
|
task={viewTask}
|
|
open={openDrawer}
|
|
closeCallback={() => {
|
|
setOpenDrawer(false)
|
|
loadTasks()
|
|
}}
|
|
completeTask={markComplete}
|
|
deleteTask={deleteTask}
|
|
keys={keys ?? []}
|
|
types={types ?? []}
|
|
/>
|
|
}
|
|
</Box>
|
|
);
|
|
}
|