pushing this stuff up so carter can take a look
This commit is contained in:
parent
7e79d0859b
commit
a2bc841510
13 changed files with 660 additions and 184 deletions
160
src/field/FieldTaskList.tsx
Normal file
160
src/field/FieldTaskList.tsx
Normal file
|
|
@ -0,0 +1,160 @@
|
||||||
|
import { Fab, Theme } from "@mui/material"
|
||||||
|
import { Task } from "models"
|
||||||
|
import { useGlobalState, useTaskAPI } from "providers"
|
||||||
|
import React, { useCallback, useEffect, useState } from "react"
|
||||||
|
import TaskDrawer from "tasks/TaskDrawer"
|
||||||
|
import TaskList from "tasks/TaskList"
|
||||||
|
import TaskSettings from "tasks/TaskSettings"
|
||||||
|
import AddIcon from "@mui/icons-material/Add";
|
||||||
|
import { makeStyles } from "@mui/styles";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
field: string
|
||||||
|
}
|
||||||
|
|
||||||
|
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 FieldTaskList(props: Props) {
|
||||||
|
const { field } = props
|
||||||
|
const [{ as }] = useGlobalState()
|
||||||
|
const taskAPI = useTaskAPI()
|
||||||
|
const classes = useStyles()
|
||||||
|
const [selectedTask, setSelectedTask] = useState<Task | undefined>(undefined)
|
||||||
|
const [openTaskDrawer, setOpenTaskDrawer] = useState(false)
|
||||||
|
const [opentaskSettings, setOpenTaskSettings] = useState(false)
|
||||||
|
const [taskMap, setTaskMap] = useState<Map<string, Task>>(new Map())
|
||||||
|
//load the tasks
|
||||||
|
const loadTasks = useCallback(() => {
|
||||||
|
taskAPI.listTasks(50, 0, "asc", "start", undefined, undefined, undefined, undefined, undefined, [field], ["field"]).then(resp => {
|
||||||
|
if(resp.data.tasks){
|
||||||
|
let newMap: Map<string,Task> = new Map()
|
||||||
|
resp.data.tasks.forEach(task => {
|
||||||
|
newMap.set(task.key, Task.create(task))
|
||||||
|
})
|
||||||
|
setTaskMap(newMap)
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
|
||||||
|
})
|
||||||
|
},[as, field])
|
||||||
|
|
||||||
|
useEffect(()=>{
|
||||||
|
loadTasks()
|
||||||
|
},[loadTasks])
|
||||||
|
|
||||||
|
const addNewTask = (task: Task) => {
|
||||||
|
console.log()
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteTask = () => {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const markComplete = () => {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateTask = () => {
|
||||||
|
|
||||||
|
}
|
||||||
|
//return the task list
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
{selectedTask &&
|
||||||
|
<TaskDrawer
|
||||||
|
task={selectedTask}
|
||||||
|
open={openTaskDrawer}
|
||||||
|
deleteTask={deleteTask}
|
||||||
|
completeTask={markComplete}
|
||||||
|
closeCallback={() => {
|
||||||
|
setOpenTaskDrawer(false)
|
||||||
|
setSelectedTask(undefined)
|
||||||
|
}}
|
||||||
|
keys={[field]}
|
||||||
|
types={["field"]}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
<TaskList
|
||||||
|
deleteTask={deleteTask}
|
||||||
|
editTaskMethod={updateTask}
|
||||||
|
markComplete={markComplete}
|
||||||
|
openTask={id => {
|
||||||
|
let task = taskMap.get(id)
|
||||||
|
if(task){
|
||||||
|
setSelectedTask(task)
|
||||||
|
setOpenTaskDrawer(true)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
tasks={Array.from(taskMap.values())}
|
||||||
|
reLoad={() => {
|
||||||
|
loadTasks()
|
||||||
|
}}
|
||||||
|
keys={[field]}
|
||||||
|
types={["field"]}
|
||||||
|
/>
|
||||||
|
<TaskSettings
|
||||||
|
open={opentaskSettings}
|
||||||
|
onClose={() => {
|
||||||
|
setOpenTaskSettings(false)
|
||||||
|
}}
|
||||||
|
keys={[field]}
|
||||||
|
types={["field"]}
|
||||||
|
task={selectedTask}
|
||||||
|
/>
|
||||||
|
<Fab
|
||||||
|
onClick={() => {setOpenTaskSettings(true)}}
|
||||||
|
aria-label="Create Task"
|
||||||
|
className={classes.overlayFab}
|
||||||
|
size={"medium"}>
|
||||||
|
<AddIcon />
|
||||||
|
</Fab>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -24,6 +24,7 @@ import { useMobile } from "hooks";
|
||||||
import { useGlobalState, useHarvestPlanAPI, useUserAPI } from "providers";
|
import { useGlobalState, useHarvestPlanAPI, useUserAPI } from "providers";
|
||||||
import { makeStyles } from "@mui/styles";
|
import { makeStyles } from "@mui/styles";
|
||||||
import FieldActions from "field/FieldActions";
|
import FieldActions from "field/FieldActions";
|
||||||
|
import FieldTaskList from "field/FieldTaskList";
|
||||||
|
|
||||||
interface TabPanelProps {
|
interface TabPanelProps {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
|
|
@ -273,7 +274,7 @@ export default function FieldDrawer(props: Props) {
|
||||||
<Weather longitude={field.center().longitude} latitude={field.center().latitude} />
|
<Weather longitude={field.center().longitude} latitude={field.center().latitude} />
|
||||||
</TabPanelMine>
|
</TabPanelMine>
|
||||||
<TabPanelMine value={value} index={2}>
|
<TabPanelMine value={value} index={2}>
|
||||||
<TaskViewer drawerView objectKey={field.key()} loadKeys={taskLoadKeys} overlayButton />
|
<FieldTaskList field={field.key()}/>
|
||||||
</TabPanelMine>
|
</TabPanelMine>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,9 @@ export interface IPermissionAPIContext {
|
||||||
shareObjectByKey: (
|
shareObjectByKey: (
|
||||||
scope: Scope,
|
scope: Scope,
|
||||||
key: string,
|
key: string,
|
||||||
permissions: pond.Permission[]
|
permissions: pond.Permission[],
|
||||||
|
keys?: string[],
|
||||||
|
type?: string[]
|
||||||
) => Promise<AxiosResponse<any>>;
|
) => Promise<AxiosResponse<any>>;
|
||||||
addShareableLink: (scope: Scope, expiration: string) => Promise<AxiosResponse<any>>;
|
addShareableLink: (scope: Scope, expiration: string) => Promise<AxiosResponse<any>>;
|
||||||
removeShareableLink: (scope: Scope, code: string) => Promise<AxiosResponse<any>>;
|
removeShareableLink: (scope: Scope, code: string) => Promise<AxiosResponse<any>>;
|
||||||
|
|
@ -136,9 +138,11 @@ export default function PermissionProvider(props: PropsWithChildren<Props>) {
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
const shareObjectByKey = (scope: Scope, key: string, permissions: pond.Permission[]) => {
|
const shareObjectByKey = (scope: Scope, key: string, permissions: pond.Permission[], keys?: string[], types?: string[]) => {
|
||||||
let url = pondURL("/" + scope.kind + "s/" + scope.key + "/shareByKey")
|
let url = pondURL("/" + scope.kind + "s/" + scope.key + "/shareByKey") + (keys ? "?keys=" + keys.join(",") : "") +
|
||||||
if (as) url = pondURL(`/${scope.kind}s/${scope.key}/shareByKey?as=${as}`)
|
(types ? "&types=" + types.join(",") : "")
|
||||||
|
if (as) url = pondURL(`/${scope.kind}s/${scope.key}/shareByKey?as=${as}`) + (keys ? "&keys=" + keys.join(",") : "") +
|
||||||
|
(types ? "&types=" + types.join(",") : "")
|
||||||
return new Promise<AxiosResponse>((resolve, reject) => {
|
return new Promise<AxiosResponse>((resolve, reject) => {
|
||||||
post(url, {
|
post(url, {
|
||||||
key: key,
|
key: key,
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import { createContext, PropsWithChildren, useContext } from "react";
|
||||||
import { pondURL } from "./pond";
|
import { pondURL } from "./pond";
|
||||||
|
|
||||||
export interface ITaskAPIContext {
|
export interface ITaskAPIContext {
|
||||||
addTask: (task: pond.TaskSettings, otherTeam?: string) => Promise<AxiosResponse<pond.AddTaskResponse>>;
|
addTask: (task: pond.TaskSettings, otherTeam?: string, keys?: string[], types?: string[]) => Promise<AxiosResponse<pond.AddTaskResponse>>;
|
||||||
getTask: (taskID: string, otherTeam?: string) => Promise<AxiosResponse<pond.Task>>;
|
getTask: (taskID: string, otherTeam?: string) => Promise<AxiosResponse<pond.Task>>;
|
||||||
listTasks: (
|
listTasks: (
|
||||||
limit: number,
|
limit: number,
|
||||||
|
|
@ -17,7 +17,9 @@ export interface ITaskAPIContext {
|
||||||
asRoot?: boolean,
|
asRoot?: boolean,
|
||||||
otherTeam?: string,
|
otherTeam?: string,
|
||||||
from?: string,
|
from?: string,
|
||||||
to?: string
|
to?: string,
|
||||||
|
keys?: string[],
|
||||||
|
types?: string[]
|
||||||
) => Promise<AxiosResponse<pond.ListTasksResponse>>;
|
) => Promise<AxiosResponse<pond.ListTasksResponse>>;
|
||||||
removeTask: (taskID: string, otherTeam?: string) => Promise<AxiosResponse<pond.RemoveTaskResponse>>;
|
removeTask: (taskID: string, otherTeam?: string) => Promise<AxiosResponse<pond.RemoveTaskResponse>>;
|
||||||
updateTask: (
|
updateTask: (
|
||||||
|
|
@ -38,10 +40,20 @@ export default function TaskProvider(props: PropsWithChildren<Props>) {
|
||||||
const { get, del, post, put } = useHTTP();
|
const { get, del, post, put } = useHTTP();
|
||||||
const [{ as }] = useGlobalState();
|
const [{ as }] = useGlobalState();
|
||||||
|
|
||||||
const addTask = (task: pond.TaskSettings, otherTeam?: string) => {
|
const addTask = (task: pond.TaskSettings, otherTeam?: string, keys?: string[], types?: string[]) => {
|
||||||
const view = otherTeam ? otherTeam : as
|
const view = otherTeam ? otherTeam : as
|
||||||
if (view) return post<pond.AddTaskResponse>(pondURL("/tasks?as=" + view), task);
|
if (view) return post<pond.AddTaskResponse>(
|
||||||
return post<pond.AddTaskResponse>(pondURL("/tasks"), task);
|
pondURL(
|
||||||
|
"/tasks?as=" + view +
|
||||||
|
(keys ? "&keys=" + keys.join(",") : "") +
|
||||||
|
(types ? "&types=" + types.join(",") : "")
|
||||||
|
), task);
|
||||||
|
return post<pond.AddTaskResponse>(
|
||||||
|
pondURL(
|
||||||
|
"/tasks" +
|
||||||
|
(keys ? "?keys=" + keys.join(",") : "") +
|
||||||
|
(types ? "&types=" + types.join(",") : "")
|
||||||
|
), task);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getTask = (taskID: string, otherTeam?: string) => {
|
const getTask = (taskID: string, otherTeam?: string) => {
|
||||||
|
|
@ -65,7 +77,9 @@ export default function TaskProvider(props: PropsWithChildren<Props>) {
|
||||||
asRoot?: boolean,
|
asRoot?: boolean,
|
||||||
otherTeam?: string,
|
otherTeam?: string,
|
||||||
from?: string,
|
from?: string,
|
||||||
to?: string
|
to?: string,
|
||||||
|
keys?: string[],
|
||||||
|
types?: string[]
|
||||||
) => {
|
) => {
|
||||||
const view = otherTeam ? otherTeam : as
|
const view = otherTeam ? otherTeam : as
|
||||||
return get<pond.ListTasksResponse>(
|
return get<pond.ListTasksResponse>(
|
||||||
|
|
@ -81,7 +95,9 @@ export default function TaskProvider(props: PropsWithChildren<Props>) {
|
||||||
(asRoot ? "&asRoot=" + asRoot.toString() : "") +
|
(asRoot ? "&asRoot=" + asRoot.toString() : "") +
|
||||||
(view ? "&as=" + view : "") +
|
(view ? "&as=" + view : "") +
|
||||||
(from ? "&from=" + from : "") +
|
(from ? "&from=" + from : "") +
|
||||||
(to ? "&to=" + to : "")
|
(to ? "&to=" + to : "") +
|
||||||
|
(keys ? "&keys=" + keys.join(",") : "") +
|
||||||
|
(types ? "&types=" + types.join(",") : "")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ export interface ITeamAPIContext {
|
||||||
prefixSearch?: string,
|
prefixSearch?: string,
|
||||||
) => Promise<AxiosResponse<pond.ListTeamsResponse>>;
|
) => Promise<AxiosResponse<pond.ListTeamsResponse>>;
|
||||||
listAllTeams: () => Promise<AxiosResponse<pond.ListTeamsResponse>>;
|
listAllTeams: () => Promise<AxiosResponse<pond.ListTeamsResponse>>;
|
||||||
listObjectTeams: (scope: Scope, otherTeam?: string) => Promise<any>;
|
listObjectTeams: (scope: Scope, otherTeam?: string, keys?: string[], types?: string[]) => Promise<any>;
|
||||||
removeTeam: (key: string) => Promise<AxiosResponse<pond.RemoveTeamResponse>>;
|
removeTeam: (key: string) => Promise<AxiosResponse<pond.RemoveTeamResponse>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -156,10 +156,12 @@ export default function TeamProvider(props: PropsWithChildren<Props>) {
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
const listObjectTeams = (scope: Scope, otherTeam?: string) => {
|
const listObjectTeams = (scope: Scope, otherTeam?: string, keys?: string[], types?: string[]) => {
|
||||||
let url = "/" + scope.kind + "s/" + scope.key + "/teams"
|
let url = "/" + scope.kind + "s/" + scope.key + "/teams" + (keys ? "?keys=" + keys.join(",") : "") +
|
||||||
|
(types ? "&types=" + types.join(",") : "")
|
||||||
const view = otherTeam ? otherTeam : as
|
const view = otherTeam ? otherTeam : as
|
||||||
if (view) url = `/${scope.kind}s/${scope.key}/teams?as=${view}`
|
if (view) url = `/${scope.kind}s/${scope.key}/teams?as=${view}` + (keys ? "&keys=" + keys.join(",") : "") +
|
||||||
|
(types ? "&types=" + types.join(",") : "")
|
||||||
return new Promise<AxiosResponse>((resolve, reject) => {
|
return new Promise<AxiosResponse>((resolve, reject) => {
|
||||||
get(pondURL(url)).then(resp => {
|
get(pondURL(url)).then(resp => {
|
||||||
return resolve(resp)
|
return resolve(resp)
|
||||||
|
|
|
||||||
|
|
@ -3,25 +3,17 @@ import {
|
||||||
Card,
|
Card,
|
||||||
CardActionArea,
|
CardActionArea,
|
||||||
Grid2 as Grid,
|
Grid2 as Grid,
|
||||||
IconButton,
|
|
||||||
ListItemIcon,
|
|
||||||
ListItemText,
|
|
||||||
Menu,
|
|
||||||
MenuItem,
|
|
||||||
Typography
|
Typography
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { makeStyles } from "@mui/styles";
|
import { makeStyles } from "@mui/styles";
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import { Task } from "models";
|
import { Task } from "models";
|
||||||
import { useGlobalState } from "providers";
|
import { useGlobalState } from "providers";
|
||||||
import { useUserAPI } from "hooks";
|
import { usePermissionAPI } from "hooks";
|
||||||
import { Done, MoreVert } from "@mui/icons-material";
|
|
||||||
import Edit from "@mui/icons-material/Edit";
|
|
||||||
import DeleteIcon from "@mui/icons-material/Delete";
|
|
||||||
import { red } from "@mui/material/colors";
|
import { red } from "@mui/material/colors";
|
||||||
import { pond } from "protobuf-ts/pond";
|
import { pond } from "protobuf-ts/pond";
|
||||||
import { taskScope, teamScope } from "models/Scope";
|
|
||||||
import EventBlocker from "common/EventBlocker";
|
import EventBlocker from "common/EventBlocker";
|
||||||
|
import TaskActions from "./taskActions";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
task: Task;
|
task: Task;
|
||||||
|
|
@ -30,6 +22,8 @@ interface Props {
|
||||||
markComplete: (task: Task) => void;
|
markComplete: (task: Task) => void;
|
||||||
deleteTask: (task: Task) => void;
|
deleteTask: (task: Task) => void;
|
||||||
openTaskPage: (taskId: string) => void;
|
openTaskPage: (taskId: string) => void;
|
||||||
|
keys: string[]
|
||||||
|
types: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const useStyles = makeStyles(() => ({
|
const useStyles = makeStyles(() => ({
|
||||||
|
|
@ -59,21 +53,14 @@ export default function TaskCard(props: Props) {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const [permissions, setPermissions] = useState<pond.Permission[]>([]);
|
const [permissions, setPermissions] = useState<pond.Permission[]>([]);
|
||||||
const [{ user, as }] = useGlobalState();
|
const [{ user, as }] = useGlobalState();
|
||||||
const userAPI = useUserAPI();
|
const permissionAPI = usePermissionAPI();
|
||||||
const [menuAnchorEl, setMenuAnchorEl] = useState<Element | null>(null);
|
|
||||||
|
|
||||||
const loadPermissions = useCallback(() => {
|
const loadPermissions = useCallback(() => {
|
||||||
let scope = taskScope(props.task.key);
|
permissionAPI.getPermissions(user.id(), props.keys, props.types).then(resp => {
|
||||||
if(as){//if viewing as a team use the permissions to the team, and not the task itself
|
setPermissions(pond.EvaluatePermissionsResponse.fromObject(resp.data).permissions)
|
||||||
scope = teamScope(as)
|
})
|
||||||
}
|
|
||||||
userAPI
|
}, [props.task, permissionAPI, user, as]);
|
||||||
.getUser(user.id(), scope)
|
|
||||||
.then(resp => {
|
|
||||||
setPermissions(resp.permissions);
|
|
||||||
})
|
|
||||||
.catch(err => {});
|
|
||||||
}, [props.task, userAPI, user, as]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadPermissions();
|
loadPermissions();
|
||||||
|
|
@ -89,51 +76,51 @@ export default function TaskCard(props: Props) {
|
||||||
setDay(date.getUTCDate());
|
setDay(date.getUTCDate());
|
||||||
}, [props.task]);
|
}, [props.task]);
|
||||||
|
|
||||||
const taskActions = () => {
|
// const taskActions = () => {
|
||||||
return (
|
// return (
|
||||||
<Menu
|
// <Menu
|
||||||
id="viewMenu"
|
// id="viewMenu"
|
||||||
anchorEl={menuAnchorEl ? menuAnchorEl : null}
|
// anchorEl={menuAnchorEl ? menuAnchorEl : null}
|
||||||
open={menuAnchorEl !== null}
|
// open={menuAnchorEl !== null}
|
||||||
onClose={() => setMenuAnchorEl(null)}
|
// onClose={() => setMenuAnchorEl(null)}
|
||||||
disableAutoFocusItem>
|
// disableAutoFocusItem>
|
||||||
<MenuItem
|
// <MenuItem
|
||||||
onClick={() => {
|
// onClick={() => {
|
||||||
props.markComplete(props.task);
|
// props.markComplete(props.task);
|
||||||
setMenuAnchorEl(null);
|
// setMenuAnchorEl(null);
|
||||||
}}>
|
// }}>
|
||||||
<ListItemIcon>
|
// <ListItemIcon>
|
||||||
<Done style={{ color: "green" }} />
|
// <Done style={{ color: "green" }} />
|
||||||
</ListItemIcon>
|
// </ListItemIcon>
|
||||||
<ListItemText primary="Mark Complete" />
|
// <ListItemText primary="Mark Complete" />
|
||||||
</MenuItem>
|
// </MenuItem>
|
||||||
{permissions.includes(pond.Permission.PERMISSION_WRITE) && (
|
// {permissions.includes(pond.Permission.PERMISSION_WRITE) && (
|
||||||
<MenuItem
|
// <MenuItem
|
||||||
onClick={() => {
|
// onClick={() => {
|
||||||
props.editTaskMethod(props.task);
|
// props.editTaskMethod(props.task);
|
||||||
setMenuAnchorEl(null);
|
// setMenuAnchorEl(null);
|
||||||
}}>
|
// }}>
|
||||||
<ListItemIcon>
|
// <ListItemIcon>
|
||||||
<Edit />
|
// <Edit />
|
||||||
</ListItemIcon>
|
// </ListItemIcon>
|
||||||
<ListItemText primary="Edit" />
|
// <ListItemText primary="Edit" />
|
||||||
</MenuItem>
|
// </MenuItem>
|
||||||
)}
|
// )}
|
||||||
{permissions.includes(pond.Permission.PERMISSION_WRITE) && (
|
// {permissions.includes(pond.Permission.PERMISSION_WRITE) && (
|
||||||
<MenuItem
|
// <MenuItem
|
||||||
onClick={() => {
|
// onClick={() => {
|
||||||
props.deleteTask(props.task);
|
// props.deleteTask(props.task);
|
||||||
setMenuAnchorEl(null);
|
// setMenuAnchorEl(null);
|
||||||
}}>
|
// }}>
|
||||||
<ListItemIcon>
|
// <ListItemIcon>
|
||||||
<DeleteIcon className={classes.red} />
|
// <DeleteIcon className={classes.red} />
|
||||||
</ListItemIcon>
|
// </ListItemIcon>
|
||||||
<ListItemText primary="Delete" />
|
// <ListItemText primary="Delete" />
|
||||||
</MenuItem>
|
// </MenuItem>
|
||||||
)}
|
// )}
|
||||||
</Menu>
|
// </Menu>
|
||||||
);
|
// );
|
||||||
};
|
// };
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card style={{ width: "100%", paddingLeft: 5 }}>
|
<Card style={{ width: "100%", paddingLeft: 5 }}>
|
||||||
|
|
@ -164,18 +151,31 @@ export default function TaskCard(props: Props) {
|
||||||
</Box>
|
</Box>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid size={1}>
|
<Grid size={1}>
|
||||||
<EventBlocker style={{ margin: "auto" }}>
|
{/* <EventBlocker style={{ margin: "auto" }}>
|
||||||
<IconButton
|
<IconButton
|
||||||
aria-owns={"taskOptions"}
|
aria-owns={"taskOptions"}
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
onClick={event => setMenuAnchorEl(event.currentTarget)}>
|
onClick={event => setMenuAnchorEl(event.currentTarget)}>
|
||||||
<MoreVert />
|
<MoreVert />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
</EventBlocker> */}
|
||||||
|
<EventBlocker style={{ margin: "auto" }}>
|
||||||
|
<TaskActions
|
||||||
|
markComplete={props.markComplete}
|
||||||
|
removeTask={props.deleteTask}
|
||||||
|
task={props.task}
|
||||||
|
permissions={permissions}
|
||||||
|
refreshCallback={() => {
|
||||||
|
props.reLoad()
|
||||||
|
}}
|
||||||
|
keys={props.keys}
|
||||||
|
types={props.types}
|
||||||
|
/>
|
||||||
</EventBlocker>
|
</EventBlocker>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</CardActionArea>
|
</CardActionArea>
|
||||||
{taskActions()}
|
{/* {taskActions()} */}
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,17 +22,21 @@ import NotesIcon from "@mui/icons-material/Notes";
|
||||||
import DeleteIcon from "@mui/icons-material/Delete";
|
import DeleteIcon from "@mui/icons-material/Delete";
|
||||||
import Chat from "chat/Chat";
|
import Chat from "chat/Chat";
|
||||||
import { pond } from "protobuf-ts/pond";
|
import { pond } from "protobuf-ts/pond";
|
||||||
import { useMobile, useUserAPI } from "hooks";
|
import { useMobile, usePermissionAPI, useUserAPI } from "hooks";
|
||||||
import { getThemeType } from "theme";
|
import { getThemeType } from "theme";
|
||||||
import { makeStyles } from "@mui/styles";
|
import { makeStyles } from "@mui/styles";
|
||||||
|
import ObjectTeamsIcon from "@mui/icons-material/SupervisedUserCircle";
|
||||||
|
import TaskActions from "./taskActions";
|
||||||
|
import { useGlobalState } from "providers";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
task: Task;
|
task: Task;
|
||||||
open: boolean;
|
open: boolean;
|
||||||
closeCallback: () => void;
|
closeCallback: () => void;
|
||||||
editTask: (task: Task) => void;
|
|
||||||
deleteTask: (task: Task) => void;
|
deleteTask: (task: Task) => void;
|
||||||
completeTask: (task: Task) => void;
|
completeTask: (task: Task) => void;
|
||||||
|
keys: string[]
|
||||||
|
types: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
// interface TabPanelProps {
|
// interface TabPanelProps {
|
||||||
|
|
@ -103,17 +107,23 @@ export default function TaskDrawer(props: Props) {
|
||||||
"Nov",
|
"Nov",
|
||||||
"Dec"
|
"Dec"
|
||||||
];
|
];
|
||||||
const { task, open, closeCallback, completeTask, deleteTask, editTask } = props;
|
const { task, open, closeCallback, completeTask, deleteTask } = props;
|
||||||
const [month, setMonth] = useState(0);
|
const [month, setMonth] = useState(0);
|
||||||
const [day, setDay] = useState(0);
|
const [day, setDay] = useState(0);
|
||||||
const [menuAnchorEl, setMenuAnchorEl] = useState<Element | null>(null);
|
const [permissions, setPermissions] = useState<pond.Permission[]>([])
|
||||||
|
const [{user}] = useGlobalState();
|
||||||
|
// const [menuAnchorEl, setMenuAnchorEl] = useState<Element | null>(null);
|
||||||
const [openNote, setOpenNote] = useState(false);
|
const [openNote, setOpenNote] = useState(false);
|
||||||
const isMobile = useMobile();
|
const isMobile = useMobile();
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const userAPI = useUserAPI();
|
const userAPI = useUserAPI();
|
||||||
|
const permissionAPI = usePermissionAPI();
|
||||||
const [worker, setWorker] = useState<pond.UserProfile>();
|
const [worker, setWorker] = useState<pond.UserProfile>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
permissionAPI.getPermissions(user.id(), [task.key], ["task"]).then(resp => {
|
||||||
|
setPermissions(pond.EvaluatePermissionsResponse.fromObject(resp.data).permissions)
|
||||||
|
})
|
||||||
if (task.start()) {
|
if (task.start()) {
|
||||||
let date = new Date(task.start());
|
let date = new Date(task.start());
|
||||||
setMonth(date.getUTCMonth());
|
setMonth(date.getUTCMonth());
|
||||||
|
|
@ -122,40 +132,40 @@ export default function TaskDrawer(props: Props) {
|
||||||
userAPI.getProfile(task.worker()).then(resp => {
|
userAPI.getProfile(task.worker()).then(resp => {
|
||||||
setWorker(resp);
|
setWorker(resp);
|
||||||
});
|
});
|
||||||
}, [task, userAPI]);
|
}, [user, task, userAPI, permissionAPI]);
|
||||||
|
|
||||||
const taskActions = () => {
|
// const taskActions = () => {
|
||||||
return (
|
// return (
|
||||||
<Menu
|
// <Menu
|
||||||
id="viewMenu"
|
// id="viewMenu"
|
||||||
anchorEl={menuAnchorEl ? menuAnchorEl : null}
|
// anchorEl={menuAnchorEl ? menuAnchorEl : null}
|
||||||
open={menuAnchorEl !== null}
|
// open={menuAnchorEl !== null}
|
||||||
onClose={() => setMenuAnchorEl(null)}
|
// onClose={() => setMenuAnchorEl(null)}
|
||||||
disableAutoFocusItem>
|
// disableAutoFocusItem>
|
||||||
<MenuItem
|
// <MenuItem
|
||||||
onClick={() => {
|
// onClick={() => {
|
||||||
editTask(props.task);
|
// editTask(props.task);
|
||||||
setMenuAnchorEl(null);
|
// setMenuAnchorEl(null);
|
||||||
}}>
|
// }}>
|
||||||
<ListItemIcon>
|
// <ListItemIcon>
|
||||||
<Edit />
|
// <Edit />
|
||||||
</ListItemIcon>
|
// </ListItemIcon>
|
||||||
<ListItemText primary="Edit" />
|
// <ListItemText primary="Edit" />
|
||||||
</MenuItem>
|
// </MenuItem>
|
||||||
<MenuItem
|
// <MenuItem
|
||||||
onClick={() => {
|
// onClick={() => {
|
||||||
deleteTask(props.task);
|
// deleteTask(props.task);
|
||||||
setMenuAnchorEl(null);
|
// setMenuAnchorEl(null);
|
||||||
closeCallback();
|
// closeCallback();
|
||||||
}}>
|
// }}>
|
||||||
<ListItemIcon>
|
// <ListItemIcon>
|
||||||
<DeleteIcon style={{ color: "red" }} />
|
// <DeleteIcon style={{ color: "red" }} />
|
||||||
</ListItemIcon>
|
// </ListItemIcon>
|
||||||
<ListItemText primary="Delete" />
|
// <ListItemText primary="Delete" />
|
||||||
</MenuItem>
|
// </MenuItem>
|
||||||
</Menu>
|
// </Menu>
|
||||||
);
|
// );
|
||||||
};
|
// };
|
||||||
|
|
||||||
const bodyButtons = () => {
|
const bodyButtons = () => {
|
||||||
return (
|
return (
|
||||||
|
|
@ -175,11 +185,20 @@ export default function TaskDrawer(props: Props) {
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip title="More">
|
<Tooltip title="More">
|
||||||
<IconButton
|
{/* <IconButton
|
||||||
className={classes.avatar}
|
className={classes.avatar}
|
||||||
onClick={event => setMenuAnchorEl(event.currentTarget)}>
|
onClick={event => setMenuAnchorEl(event.currentTarget)}>
|
||||||
<MoreVert />
|
<MoreVert />
|
||||||
</IconButton>
|
</IconButton> */}
|
||||||
|
<TaskActions
|
||||||
|
markComplete={(t) => completeTask(t)}
|
||||||
|
permissions={permissions}
|
||||||
|
task={task}
|
||||||
|
removeTask={deleteTask}
|
||||||
|
refreshCallback={closeCallback}
|
||||||
|
keys={props.keys}
|
||||||
|
types={props.types}
|
||||||
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
@ -303,7 +322,7 @@ export default function TaskDrawer(props: Props) {
|
||||||
{taskTime()}
|
{taskTime()}
|
||||||
{assignees()}
|
{assignees()}
|
||||||
</Box>
|
</Box>
|
||||||
{taskActions()}
|
{/* {taskActions()} */}
|
||||||
{noteDrawer()}
|
{noteDrawer()}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@ interface Props {
|
||||||
listHeight?: string | number;
|
listHeight?: string | number;
|
||||||
label?: string;
|
label?: string;
|
||||||
dateToView?: Date;
|
dateToView?: Date;
|
||||||
|
keys: string[]
|
||||||
|
types: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function TaskList(props: Props) {
|
export default function TaskList(props: Props) {
|
||||||
|
|
@ -31,7 +33,9 @@ export default function TaskList(props: Props) {
|
||||||
reLoad,
|
reLoad,
|
||||||
listHeight,
|
listHeight,
|
||||||
label,
|
label,
|
||||||
dateToView
|
dateToView,
|
||||||
|
keys,
|
||||||
|
types
|
||||||
} = props;
|
} = props;
|
||||||
const [tasks, setTasks] = useState(props.tasks);
|
const [tasks, setTasks] = useState(props.tasks);
|
||||||
const [incomplete, setIncomplete] = useState<Task[]>([]);
|
const [incomplete, setIncomplete] = useState<Task[]>([]);
|
||||||
|
|
@ -128,6 +132,8 @@ export default function TaskList(props: Props) {
|
||||||
deleteTask={(task: Task) => deleteTask(task)}
|
deleteTask={(task: Task) => deleteTask(task)}
|
||||||
reLoad={reLoad}
|
reLoad={reLoad}
|
||||||
openTaskPage={(taskId: string) => openTask(taskId)}
|
openTaskPage={(taskId: string) => openTask(taskId)}
|
||||||
|
keys={keys}
|
||||||
|
types={types}
|
||||||
/>
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
));
|
));
|
||||||
|
|
@ -141,6 +147,8 @@ export default function TaskList(props: Props) {
|
||||||
deleteTask={(task: Task) => deleteTask(task)}
|
deleteTask={(task: Task) => deleteTask(task)}
|
||||||
reLoad={reLoad}
|
reLoad={reLoad}
|
||||||
openTaskPage={(taskId: string) => openTask(taskId)}
|
openTaskPage={(taskId: string) => openTask(taskId)}
|
||||||
|
keys={keys}
|
||||||
|
types={types}
|
||||||
/>
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
));
|
));
|
||||||
|
|
|
||||||
|
|
@ -24,14 +24,15 @@ import ResponsiveDialog from "common/ResponsiveDialog";
|
||||||
interface Props {
|
interface Props {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onClose: (reLoad?: boolean) => void;
|
onClose: (reLoad?: boolean) => void;
|
||||||
markComplete?: boolean;
|
|
||||||
startDate?: any;
|
startDate?: any;
|
||||||
task?: Task;
|
task?: Task;
|
||||||
objectKey?: string;
|
objectKey?: string; //deprecated, with the new permission structure keys and types will be what determine what a task is connected to
|
||||||
type?: string;
|
type?: string;
|
||||||
hasCost?: boolean;
|
hasCost?: boolean;
|
||||||
costTitle?: string;
|
costTitle?: string;
|
||||||
secondaryCostTitle?: string;
|
secondaryCostTitle?: string;
|
||||||
|
keys?: string[]
|
||||||
|
types?: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function TaskSettings(props: Props) {
|
export default function TaskSettings(props: Props) {
|
||||||
|
|
@ -104,7 +105,7 @@ export default function TaskSettings(props: Props) {
|
||||||
startTime: startTime,
|
startTime: startTime,
|
||||||
end: endDate,
|
end: endDate,
|
||||||
endTime: endTime,
|
endTime: endTime,
|
||||||
objectKey: props.objectKey ? props.objectKey : user.id(),
|
objectKey: props.objectKey ? props.objectKey : user.id(), //deprecated
|
||||||
description: taskDescription,
|
description: taskDescription,
|
||||||
worker: worker,
|
worker: worker,
|
||||||
complete: false,
|
complete: false,
|
||||||
|
|
@ -120,7 +121,7 @@ export default function TaskSettings(props: Props) {
|
||||||
returnTask.settings = taskSettings;
|
returnTask.settings = taskSettings;
|
||||||
|
|
||||||
taskAPI
|
taskAPI
|
||||||
.addTask(taskSettings, as)
|
.addTask(taskSettings, as, props.keys, props.types)
|
||||||
.then(resp => {
|
.then(resp => {
|
||||||
props.onClose(true);
|
props.onClose(true);
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,12 @@ import moment from "moment";
|
||||||
import { makeStyles } from "@mui/styles";
|
import { makeStyles } from "@mui/styles";
|
||||||
|
|
||||||
interface ViewProps {
|
interface ViewProps {
|
||||||
objectKey?: string; //only used to save it for the object
|
// 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;
|
label?: string;
|
||||||
drawerView?: boolean;
|
drawerView?: boolean;
|
||||||
loadKeys?: string[]; //if you want to load tasks for a specific object(s)
|
// 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;
|
overlayButton?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -87,7 +89,15 @@ const useStyles = makeStyles((theme: Theme) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
export default function TaskViewer(props: ViewProps) {
|
export default function TaskViewer(props: ViewProps) {
|
||||||
const { objectKey, label, drawerView, loadKeys, overlayButton } = props;
|
const {
|
||||||
|
//objectKey,
|
||||||
|
label,
|
||||||
|
drawerView,
|
||||||
|
//loadKeys,
|
||||||
|
keys,
|
||||||
|
types,
|
||||||
|
overlayButton
|
||||||
|
} = props;
|
||||||
const [{ user, as }] = useGlobalState();
|
const [{ user, as }] = useGlobalState();
|
||||||
//const [{ as }] = useGlobalState();
|
//const [{ as }] = useGlobalState();
|
||||||
const taskAPI = useTaskAPI();
|
const taskAPI = useTaskAPI();
|
||||||
|
|
@ -143,36 +153,36 @@ export default function TaskViewer(props: ViewProps) {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const loadMultitask = useCallback(() => {
|
// const loadMultitask = useCallback(() => {
|
||||||
if (!loadKeys) return;
|
// if (!loadKeys) return;
|
||||||
if (loadKeys.length > 0) {
|
// if (loadKeys.length > 0) {
|
||||||
let temp = new Map<string, Task>();
|
// let temp = new Map<string, Task>();
|
||||||
setLoaded(false);
|
// setLoaded(false);
|
||||||
taskAPI
|
// taskAPI
|
||||||
.getMultiTasks(loadKeys, as)
|
// .getMultiTasks(loadKeys, as)
|
||||||
.then(resp => {
|
// .then(resp => {
|
||||||
if(resp.data.tasks){
|
// if(resp.data.tasks){
|
||||||
resp.data.tasks.forEach(task => {
|
// resp.data.tasks.forEach(task => {
|
||||||
if (task.settings) {
|
// if (task.settings) {
|
||||||
temp.set(task.key, Task.any(task));
|
// temp.set(task.key, Task.any(task));
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
setTasks(temp);
|
// setTasks(temp);
|
||||||
setLoaded(true);
|
// setLoaded(true);
|
||||||
})
|
// })
|
||||||
.catch(err => {
|
// .catch(err => {
|
||||||
openSnack("Failed to load");
|
// openSnack("Failed to load");
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
}, [loadKeys, openSnack, taskAPI, as]);
|
// }, [loadKeys, openSnack, taskAPI, as]);
|
||||||
|
|
||||||
//loads tasks from the backend database
|
//loads tasks from the backend database
|
||||||
const loadTasks = useCallback(() => {
|
const loadTasks = useCallback(() => {
|
||||||
if (!user.id()) return;
|
if (!user.id()) return;
|
||||||
let temp = new Map<string, Task>();
|
let temp = new Map<string, Task>();
|
||||||
taskAPI
|
taskAPI
|
||||||
.listTasks(50, 0, "asc", "start", undefined, undefined, as, prevMonth, nextMonth)
|
.listTasks(50, 0, "asc", "start", undefined, undefined, as, prevMonth, nextMonth, keys, types)
|
||||||
.then(resp => {
|
.then(resp => {
|
||||||
if(resp.data.tasks){
|
if(resp.data.tasks){
|
||||||
resp.data.tasks.forEach(task => {
|
resp.data.tasks.forEach(task => {
|
||||||
|
|
@ -185,20 +195,20 @@ export default function TaskViewer(props: ViewProps) {
|
||||||
setLoaded(true);
|
setLoaded(true);
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.log(err)
|
|
||||||
openSnack("Failed to load tasks");
|
openSnack("Failed to load tasks");
|
||||||
});
|
});
|
||||||
}, [taskAPI, as, user, openSnack, nextMonth, prevMonth]);
|
}, [taskAPI, as, user, openSnack, nextMonth, prevMonth, keys, types]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (drawerView) {
|
// if (drawerView) {
|
||||||
setTasks(new Map<string, Task>());
|
// setTasks(new Map<string, Task>());
|
||||||
setValue(1);
|
// setValue(1);
|
||||||
loadMultitask();
|
// loadMultitask();
|
||||||
} else {
|
// } else {
|
||||||
loadTasks();
|
// loadTasks();
|
||||||
}
|
// }
|
||||||
}, [loadTasks, loadMultitask, as, drawerView]);
|
loadTasks()
|
||||||
|
}, [loadTasks, as, drawerView]);
|
||||||
|
|
||||||
const markComplete = (task: Task) => {
|
const markComplete = (task: Task) => {
|
||||||
task.settings.complete = !task.settings.complete;
|
task.settings.complete = !task.settings.complete;
|
||||||
|
|
@ -320,6 +330,8 @@ export default function TaskViewer(props: ViewProps) {
|
||||||
reLoad={() => loadTasks()}
|
reLoad={() => loadTasks()}
|
||||||
dateToView={drawerView ? undefined : currentDate}
|
dateToView={drawerView ? undefined : currentDate}
|
||||||
openTask={(taskId: string) => openSelectedTask(taskId)}
|
openTask={(taskId: string) => openSelectedTask(taskId)}
|
||||||
|
keys={[]}
|
||||||
|
types={[]}
|
||||||
/>
|
/>
|
||||||
</TabPanelMine>
|
</TabPanelMine>
|
||||||
<Fab
|
<Fab
|
||||||
|
|
@ -380,6 +392,8 @@ export default function TaskViewer(props: ViewProps) {
|
||||||
listHeight={"40vh"}
|
listHeight={"40vh"}
|
||||||
dateToView={currentDate}
|
dateToView={currentDate}
|
||||||
openTask={(taskId: string) => openSelectedTask(taskId)}
|
openTask={(taskId: string) => openSelectedTask(taskId)}
|
||||||
|
keys={[]}
|
||||||
|
types={[]}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
@ -423,16 +437,19 @@ export default function TaskViewer(props: ViewProps) {
|
||||||
<TaskSettings
|
<TaskSettings
|
||||||
open={newTaskDialog}
|
open={newTaskDialog}
|
||||||
task={editTask}
|
task={editTask}
|
||||||
objectKey={objectKey}
|
//objectKey={objectKey}
|
||||||
|
keys={keys}
|
||||||
|
types={types}
|
||||||
startDate={startDate}
|
startDate={startDate}
|
||||||
onClose={r => {
|
onClose={r => {
|
||||||
if (r) {
|
// if (r) {
|
||||||
if (drawerView) {
|
// if (drawerView) {
|
||||||
loadMultitask();
|
// loadMultitask();
|
||||||
} else {
|
// } else {
|
||||||
loadTasks();
|
// loadTasks();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
loadTasks();
|
||||||
setEditTask(undefined);
|
setEditTask(undefined);
|
||||||
setNewTaskDialog(false);
|
setNewTaskDialog(false);
|
||||||
}}
|
}}
|
||||||
|
|
@ -441,10 +458,14 @@ export default function TaskViewer(props: ViewProps) {
|
||||||
<TaskDrawer
|
<TaskDrawer
|
||||||
task={viewTask}
|
task={viewTask}
|
||||||
open={openDrawer}
|
open={openDrawer}
|
||||||
closeCallback={() => setOpenDrawer(false)}
|
closeCallback={() => {
|
||||||
|
setOpenDrawer(false)
|
||||||
|
loadTasks()
|
||||||
|
}}
|
||||||
completeTask={markComplete}
|
completeTask={markComplete}
|
||||||
deleteTask={deleteTask}
|
deleteTask={deleteTask}
|
||||||
editTask={setTaskToEdit}
|
keys={[]}
|
||||||
|
types={[]}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
||||||
236
src/tasks/taskActions.tsx
Normal file
236
src/tasks/taskActions.tsx
Normal file
|
|
@ -0,0 +1,236 @@
|
||||||
|
import {
|
||||||
|
IconButton,
|
||||||
|
ListItemIcon,
|
||||||
|
ListItemText,
|
||||||
|
Menu,
|
||||||
|
MenuItem,
|
||||||
|
Theme
|
||||||
|
} from "@mui/material";
|
||||||
|
import { blue } from "@mui/material/colors";
|
||||||
|
import MoreIcon from "@mui/icons-material/MoreVert";
|
||||||
|
import ObjectTeamsIcon from "@mui/icons-material/SupervisedUserCircle";
|
||||||
|
import { pond } from "protobuf-ts/pond";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { isOffline } from "utils/environment";
|
||||||
|
import ObjectTeams from "teams/ObjectTeams";
|
||||||
|
import { Task, taskScope } from "models";
|
||||||
|
import { makeStyles } from "@mui/styles";
|
||||||
|
import { Done, Edit } from "@mui/icons-material";
|
||||||
|
import DeleteIcon from "@mui/icons-material/Delete";
|
||||||
|
import TaskSettings from "./TaskSettings";
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme: Theme) => ({
|
||||||
|
shareIcon: {
|
||||||
|
color: blue["500"],
|
||||||
|
"&:hover": {
|
||||||
|
color: blue["600"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
removeIcon: {
|
||||||
|
color: "var(--status-alert)"
|
||||||
|
},
|
||||||
|
red: {
|
||||||
|
color: "var(--status-alert)"
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
task: Task;
|
||||||
|
permissions: pond.Permission[];
|
||||||
|
keys: string[]
|
||||||
|
types: string[]
|
||||||
|
refreshCallback: () => void;
|
||||||
|
markComplete: (task: Task) => void;
|
||||||
|
removeTask: (task: Task) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OpenState {
|
||||||
|
// share: boolean;
|
||||||
|
// users: boolean;
|
||||||
|
teams: boolean;
|
||||||
|
settings: boolean;
|
||||||
|
// removeSelf: boolean;
|
||||||
|
delete: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function TaskActions(props: Props) {
|
||||||
|
const classes = useStyles();
|
||||||
|
const { task, permissions, refreshCallback, keys, types } = props;
|
||||||
|
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||||
|
const [openState, setOpenState] = useState<OpenState>({
|
||||||
|
// share: false,
|
||||||
|
// users: false,
|
||||||
|
teams: false,
|
||||||
|
settings: false,
|
||||||
|
// removeSelf: false
|
||||||
|
delete: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const groupMenu = () => {
|
||||||
|
// const canShare = permissions.includes(pond.Permission.PERMISSION_SHARE);
|
||||||
|
const canEdit = permissions.includes(pond.Permission.PERMISSION_WRITE);
|
||||||
|
const canManageUsers = permissions.includes(pond.Permission.PERMISSION_USERS);
|
||||||
|
return (
|
||||||
|
<Menu
|
||||||
|
id="groupMenu"
|
||||||
|
anchorEl={anchorEl}
|
||||||
|
open={Boolean(anchorEl)}
|
||||||
|
onClose={() => setAnchorEl(null)}
|
||||||
|
keepMounted
|
||||||
|
disableAutoFocusItem>
|
||||||
|
{/* {!isOffline() && canShare && (
|
||||||
|
<MenuItem
|
||||||
|
dense
|
||||||
|
onClick={() => {
|
||||||
|
setOpenState({ ...openState, share: true });
|
||||||
|
setAnchorEl(null);
|
||||||
|
}}>
|
||||||
|
<ListItemIcon>
|
||||||
|
<ShareObjectIcon className={classes.shareIcon} />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText primary="Share" />
|
||||||
|
</MenuItem>
|
||||||
|
)} */}
|
||||||
|
{/* {!isOffline() && canManageUsers && (
|
||||||
|
<MenuItem
|
||||||
|
dense
|
||||||
|
onClick={() => {
|
||||||
|
setOpenState({ ...openState, users: true });
|
||||||
|
setAnchorEl(null);
|
||||||
|
}}>
|
||||||
|
<ListItemIcon>
|
||||||
|
<ObjectUsersIcon />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText primary="Users" />
|
||||||
|
</MenuItem>
|
||||||
|
)} */}
|
||||||
|
{!isOffline() && canEdit && (
|
||||||
|
<MenuItem
|
||||||
|
onClick={() => {
|
||||||
|
props.markComplete(props.task);
|
||||||
|
setAnchorEl(null);
|
||||||
|
}}>
|
||||||
|
<ListItemIcon>
|
||||||
|
<Done style={{ color: "green" }} />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText primary="Mark Complete" />
|
||||||
|
</MenuItem>
|
||||||
|
)}
|
||||||
|
{!isOffline() && canEdit && (
|
||||||
|
<MenuItem
|
||||||
|
dense
|
||||||
|
onClick={() => {
|
||||||
|
setOpenState({ ...openState, settings: true });
|
||||||
|
setAnchorEl(null);
|
||||||
|
}}>
|
||||||
|
<ListItemIcon>
|
||||||
|
<Edit />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText primary="Edit" />
|
||||||
|
</MenuItem>
|
||||||
|
)}
|
||||||
|
{!isOffline() && canManageUsers && (
|
||||||
|
<MenuItem
|
||||||
|
dense
|
||||||
|
onClick={() => {
|
||||||
|
setOpenState({ ...openState, teams: true });
|
||||||
|
setAnchorEl(null);
|
||||||
|
}}>
|
||||||
|
<ListItemIcon>
|
||||||
|
<ObjectTeamsIcon />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText primary="Teams" />
|
||||||
|
</MenuItem>
|
||||||
|
)}
|
||||||
|
{permissions.includes(pond.Permission.PERMISSION_WRITE) && (
|
||||||
|
<MenuItem
|
||||||
|
onClick={() => {
|
||||||
|
props.removeTask(props.task);
|
||||||
|
setAnchorEl(null);
|
||||||
|
}}>
|
||||||
|
<ListItemIcon>
|
||||||
|
<DeleteIcon className={classes.red} />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText primary="Delete" />
|
||||||
|
</MenuItem>
|
||||||
|
)}
|
||||||
|
{/* <MenuItem
|
||||||
|
dense
|
||||||
|
onClick={() => {
|
||||||
|
setOpenState({ ...openState, removeSelf: true });
|
||||||
|
setAnchorEl(null);
|
||||||
|
}}>
|
||||||
|
<ListItemIcon>
|
||||||
|
<RemoveSelfIcon className={classes.red} />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText primary="Leave" />
|
||||||
|
</MenuItem> */}
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const dialogs = () => {
|
||||||
|
const key = task.key;
|
||||||
|
// const label = task.title();
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
{/* <ShareObject
|
||||||
|
scope={fieldScope(key)}
|
||||||
|
label={label}
|
||||||
|
permissions={permissions}
|
||||||
|
isDialogOpen={openState.share}
|
||||||
|
closeDialogCallback={() => setOpenState({ ...openState, share: false })}
|
||||||
|
/> */}
|
||||||
|
{/* <ObjectUsers
|
||||||
|
scope={fieldScope(key)}
|
||||||
|
label={label}
|
||||||
|
permissions={permissions}
|
||||||
|
isDialogOpen={openState.users}
|
||||||
|
closeDialogCallback={() => setOpenState({ ...openState, users: false })}
|
||||||
|
refreshCallback={refreshCallback}
|
||||||
|
/> */}
|
||||||
|
{/* <RemoveSelfFromObject
|
||||||
|
scope={fieldScope(key)}
|
||||||
|
label={label}
|
||||||
|
isDialogOpen={openState.removeSelf}
|
||||||
|
closeDialogCallback={() => setOpenState({ ...openState, removeSelf: false })}
|
||||||
|
/> */}
|
||||||
|
<ObjectTeams
|
||||||
|
scope={taskScope(key)}
|
||||||
|
label="Teams"
|
||||||
|
permissions={permissions}
|
||||||
|
isDialogOpen={openState.teams}
|
||||||
|
refreshCallback={refreshCallback}
|
||||||
|
closeDialogCallback={() => setOpenState({ ...openState, teams: false })}
|
||||||
|
keys={keys}
|
||||||
|
types={types}
|
||||||
|
/>
|
||||||
|
<TaskSettings
|
||||||
|
open={openState.settings}
|
||||||
|
task={task}
|
||||||
|
//objectKey={objectKey}
|
||||||
|
//startDate={startDate}
|
||||||
|
onClose={r => {
|
||||||
|
setOpenState({ ...openState, settings: false})
|
||||||
|
if(r){
|
||||||
|
refreshCallback()
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<IconButton
|
||||||
|
aria-owns={anchorEl ? "groupMenu" : undefined}
|
||||||
|
aria-haspopup="true"
|
||||||
|
onClick={(event: React.MouseEvent<HTMLButtonElement>) => setAnchorEl(event.currentTarget)}>
|
||||||
|
<MoreIcon />
|
||||||
|
</IconButton>
|
||||||
|
{groupMenu()}
|
||||||
|
{dialogs()}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -100,6 +100,8 @@ interface Props {
|
||||||
userCallback?: (users?: User[] | Team[] | undefined) => void;
|
userCallback?: (users?: User[] | Team[] | undefined) => void;
|
||||||
dialog?: string;
|
dialog?: string;
|
||||||
cardMode?: boolean;
|
cardMode?: boolean;
|
||||||
|
keys?: string[];
|
||||||
|
types?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ObjectTeams(props: Props) {
|
export default function ObjectTeams(props: Props) {
|
||||||
|
|
@ -120,7 +122,9 @@ export default function ObjectTeams(props: Props) {
|
||||||
refreshCallback,
|
refreshCallback,
|
||||||
userCallback,
|
userCallback,
|
||||||
dialog,
|
dialog,
|
||||||
cardMode
|
cardMode,
|
||||||
|
keys,
|
||||||
|
types
|
||||||
} = props;
|
} = props;
|
||||||
const prevPermissions = usePrevious(permissions);
|
const prevPermissions = usePrevious(permissions);
|
||||||
const prevIsDialogOpen = usePrevious(isDialogOpen);
|
const prevIsDialogOpen = usePrevious(isDialogOpen);
|
||||||
|
|
@ -146,7 +150,7 @@ export default function ObjectTeams(props: Props) {
|
||||||
const load = useCallback(() => {
|
const load = useCallback(() => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
teamAPI
|
teamAPI
|
||||||
.listObjectTeams(scope, as)
|
.listObjectTeams(scope, as, keys, types)
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
let rTeams: Team[] = [];
|
let rTeams: Team[] = [];
|
||||||
or(response.data, { teams: [] }).teams.forEach((user: any) => {
|
or(response.data, { teams: [] }).teams.forEach((user: any) => {
|
||||||
|
|
@ -588,6 +592,8 @@ export default function ObjectTeams(props: Props) {
|
||||||
permissions={permissions}
|
permissions={permissions}
|
||||||
isDialogOpen={isShareObjectDialogOpen}
|
isDialogOpen={isShareObjectDialogOpen}
|
||||||
closeDialogCallback={closeShareObjectDialog}
|
closeDialogCallback={closeShareObjectDialog}
|
||||||
|
keys={keys}
|
||||||
|
types={types}
|
||||||
/>
|
/>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,8 @@ interface Props {
|
||||||
permissions: pond.Permission[];
|
permissions: pond.Permission[];
|
||||||
isDialogOpen: boolean;
|
isDialogOpen: boolean;
|
||||||
closeDialogCallback: Function;
|
closeDialogCallback: Function;
|
||||||
|
keys?: string[],
|
||||||
|
types?: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ShareObject(props: Props) {
|
export default function ShareObject(props: Props) {
|
||||||
|
|
@ -96,7 +98,7 @@ export default function ShareObject(props: Props) {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const permissionAPI = usePermissionAPI();
|
const permissionAPI = usePermissionAPI();
|
||||||
const { info, success } = useSnackbar();
|
const { info, success } = useSnackbar();
|
||||||
const { scope, label, permissions, isDialogOpen, closeDialogCallback } = props;
|
const { scope, label, permissions, isDialogOpen, closeDialogCallback, keys, types } = props;
|
||||||
const [sharedPermissions, setSharedPermissions] = useState<pond.Permission[]>([
|
const [sharedPermissions, setSharedPermissions] = useState<pond.Permission[]>([
|
||||||
pond.Permission.PERMISSION_READ
|
pond.Permission.PERMISSION_READ
|
||||||
]);
|
]);
|
||||||
|
|
@ -109,7 +111,7 @@ export default function ShareObject(props: Props) {
|
||||||
const [teamKey, setTeamKey] = useState<string>("");
|
const [teamKey, setTeamKey] = useState<string>("");
|
||||||
|
|
||||||
const share = () => {
|
const share = () => {
|
||||||
permissionAPI.shareObjectByKey(scope, teamKey, sharedPermissions).then(resp => {
|
permissionAPI.shareObjectByKey(scope, teamKey, sharedPermissions, keys, types).then(resp => {
|
||||||
let shareBins = true;
|
let shareBins = true;
|
||||||
if (resp && resp.data && resp.data.existing) {
|
if (resp && resp.data && resp.data.existing) {
|
||||||
success(label + " was shared with team");
|
success(label + " was shared with team");
|
||||||
|
|
@ -123,7 +125,7 @@ export default function ShareObject(props: Props) {
|
||||||
resp.data.bins.forEach(bin => {
|
resp.data.bins.forEach(bin => {
|
||||||
if (bin.settings) {
|
if (bin.settings) {
|
||||||
let newScope = binScope(bin.settings?.key);
|
let newScope = binScope(bin.settings?.key);
|
||||||
permissionAPI.shareObjectByKey(newScope, teamKey, sharedPermissions).catch(_err => {
|
permissionAPI.shareObjectByKey(newScope, teamKey, sharedPermissions, keys, types).catch(_err => {
|
||||||
successBins = false;
|
successBins = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -143,7 +145,7 @@ export default function ShareObject(props: Props) {
|
||||||
resp.data.gates.forEach(gate => {
|
resp.data.gates.forEach(gate => {
|
||||||
if (gate) {
|
if (gate) {
|
||||||
let newScope = { key: gate.key, kind: "gate" } as Scope;
|
let newScope = { key: gate.key, kind: "gate" } as Scope;
|
||||||
permissionAPI.shareObjectByKey(newScope, teamKey, sharedPermissions).catch(_err => {
|
permissionAPI.shareObjectByKey(newScope, teamKey, sharedPermissions, keys, types).catch(_err => {
|
||||||
successGates = false;
|
successGates = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue