final adjustments to the field task list and the field drawer and field page using the tasks with the permissions in the correct way

This commit is contained in:
csawatzky 2026-03-31 13:37:49 -06:00
parent 8633c197ab
commit 186604dccf
11 changed files with 97 additions and 177 deletions

View file

@ -7,6 +7,8 @@ import TaskList from "tasks/TaskList"
import TaskSettings from "tasks/TaskSettings"
import AddIcon from "@mui/icons-material/Add";
import { makeStyles } from "@mui/styles";
import { openSnackbar } from "providers/Snackbar"
import { cloneDeep } from "lodash"
@ -70,8 +72,10 @@ export default function FieldTaskList(props: Props) {
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(() => {
console.log("loading tasks")
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()
@ -89,20 +93,24 @@ export default function FieldTaskList(props: Props) {
loadTasks()
},[loadTasks])
const addNewTask = (task: Task) => {
console.log()
const deleteTask = (task: Task) => {
taskAPI.removeTask(task.key).then(resp => {
openSnackbar("success", "Task has been removed")
loadTasks()
}).catch(err => {
openSnackbar("error", "There was a problem removing the task")
})
}
const deleteTask = () => {
}
const markComplete = () => {
}
const updateTask = () => {
const markComplete = (task: Task) => {
let settings = cloneDeep(task.settings)
settings.complete = !task.settings.complete
taskAPI.updateTask(task.key, settings).then(resp => {
openSnackbar("success", "Task has been updated")
loadTasks()
}).catch(err => {
openSnackbar("error", "There was a problem updating the task")
})
}
//return the task list
return (
@ -113,9 +121,12 @@ export default function FieldTaskList(props: Props) {
open={openTaskDrawer}
deleteTask={deleteTask}
completeTask={markComplete}
closeCallback={() => {
closeCallback={(refresh) => {
setOpenTaskDrawer(false)
setSelectedTask(undefined)
if (refresh) {
loadTasks()
}
}}
keys={[field]}
types={["field"]}
@ -123,7 +134,6 @@ export default function FieldTaskList(props: Props) {
}
<TaskList
deleteTask={deleteTask}
editTaskMethod={updateTask}
markComplete={markComplete}
openTask={id => {
let task = taskMap.get(id)
@ -141,8 +151,11 @@ export default function FieldTaskList(props: Props) {
/>
<TaskSettings
open={opentaskSettings}
onClose={() => {
onClose={(reload) => {
setOpenTaskSettings(false)
if(reload){
loadTasks()
}
}}
keys={[field]}
types={["field"]}

View file

@ -14,7 +14,6 @@ import DisplayDrawer from "common/DisplayDrawer";
import HarvestPlanDisplay from "harvestPlan/HarvestPlanDisplay";
import { Field, fieldScope, HarvestPlan, teamScope } from "models";
import React, { useEffect, useState } from "react";
import TaskViewer from "tasks/TaskViewer";
import Weather from "weather/weather";
import { getThemeType } from "theme";
import GrainDescriber from "grain/GrainDescriber";
@ -219,7 +218,7 @@ export default function FieldDrawer(props: Props) {
TabIndicatorProps={{ style: { background: "rgba(255,255,0,255)" } }}>
<Tab label="Field Overview" />
<Tab label="Weather" />
<Tab label="Field Activities" />
<Tab label="Field Tasks" />
</Tabs>
<TabPanelMine value={value} index={0}>
<Box paddingLeft={2} paddingRight={2} paddingBottom={5}>

View file

@ -512,7 +512,7 @@ export default function Bin(props: Props) {
if (showTasks()) {
return (
<React.Fragment>
<TaskViewer drawerView objectKey={binID} loadKeys={[binID]} />
<TaskViewer drawerView keys={[binID]} types={["bin"]} overlayButton />
</React.Fragment>
);
}
@ -1156,7 +1156,7 @@ export default function Bin(props: Props) {
<IconButton style={{ width: 50 }} onClick={() => setTaskDrawer(false)}>
<Close />
</IconButton>
<TaskViewer drawerView objectKey={binID} loadKeys={[binID]} />
<TaskViewer drawerView keys={[binID]} types={["bin"]} overlayButton />
</Box>
</Drawer>
)}

View file

@ -10,12 +10,12 @@ import { Settings } from "@mui/icons-material";
import { pond } from "protobuf-ts/pond";
import FieldMinimap from "field/Fieldminimap";
import Weather from "weather/weather";
import TaskViewer from "tasks/TaskViewer";
import HarvestPlanDisplay from "harvestPlan/HarvestPlanDisplay";
import { cloneDeep } from "lodash";
import HarvestPlanTable from "harvestPlan/HarvestPlanTable";
import { makeStyles } from "@mui/styles";
import FieldSettings from "field/FieldSettings";
import FieldTaskList from "field/FieldTaskList";
const useStyles = makeStyles((theme: Theme) => {
return ({
@ -185,12 +185,9 @@ export default function FieldPage() {
}
const tasks = () => {
let taskLoadKeys: string[] = [];
if (!planLoading) {
field.key() !== "" && taskLoadKeys.push(field.key());
//hPlan.key() !== "" && taskLoadKeys.push(hPlan.key());
}
return (<TaskViewer drawerView objectKey={field.key()} loadKeys={taskLoadKeys} overlayButton={isMobile}/>)
return (
<FieldTaskList field={field.key()}/>
)
}
const desktopView = () => {

View file

@ -7,10 +7,16 @@ import { useGlobalState } from "providers";
import { createContext, PropsWithChildren, useContext } from "react";
import { objectQueryParams, pondURL } from "./pond";
export interface PermissionChanges {
key: string; //the key of the object having its permissions changed (parent)
permissions: string[] //the new permissions the parent will have to the child
}
export interface IPermissionAPIContext {
getPermissions: (user: string, keys: string[], types: string[]) => Promise<AxiosResponse<pond.EvaluatePermissionsResponse>>;
removePermissions: (user: string, scope: Scope) => Promise<AxiosResponse<any>>;
updatePermissions: (scope: Scope, users: User[] | Team[]) => Promise<AxiosResponse<any>>;
updateObjectPermissions: (key: string, type: string, parentType: string, changes: PermissionChanges[], keys: string[], types: string[]) => Promise<AxiosResponse<any>>;
updateRelativePermissions: (
parentScope: Scope,
childScope: Scope,
@ -96,6 +102,28 @@ export default function PermissionProvider(props: PropsWithChildren<Props>) {
})
};
const updateObjectPermissions = (key: string, type: string, parentType: string, changes: PermissionChanges[], keys: string[], types: string[]) => {
let body = {
childKey: key,
childType: type,
parentType: parentType,
permissionChanges: changes
};
if (as) {
keys.unshift(as)
types.unshift("team")
}
return new Promise<AxiosResponse>((resolve, reject) => {
put(pondURL("/" + type + "s/" + key + "/objectPermissions?keys=" + keys + "&types=" + types ), body).then(resp => {
return resolve(resp)
}).catch(err => {
return reject(err)
})
})
}
const shareObject = (
scope: Scope,
email: string,
@ -207,6 +235,7 @@ export default function PermissionProvider(props: PropsWithChildren<Props>) {
getPermissions,
removePermissions,
updatePermissions,
updateObjectPermissions,
updateRelativePermissions,
shareObject,
shareObjectByKey,

View file

@ -18,7 +18,6 @@ import TaskActions from "./taskActions";
interface Props {
task: Task;
reLoad: () => void;
editTaskMethod: (task: Task) => void;
markComplete: (task: Task) => void;
deleteTask: (task: Task) => void;
openTaskPage: (taskId: string) => void;

View file

@ -32,7 +32,7 @@ import { useGlobalState } from "providers";
interface Props {
task: Task;
open: boolean;
closeCallback: () => void;
closeCallback: (refresh: boolean) => void;
deleteTask: (task: Task) => void;
completeTask: (task: Task) => void;
keys: string[]
@ -121,7 +121,7 @@ export default function TaskDrawer(props: Props) {
const [worker, setWorker] = useState<pond.UserProfile>();
useEffect(() => {
permissionAPI.getPermissions(user.id(), [task.key], ["task"]).then(resp => {
permissionAPI.getPermissions(user.id(), props.keys, props.types).then(resp => {
setPermissions(pond.EvaluatePermissionsResponse.fromObject(resp.data).permissions)
})
if (task.start()) {
@ -134,39 +134,6 @@ export default function TaskDrawer(props: Props) {
});
}, [user, task, userAPI, permissionAPI]);
// const taskActions = () => {
// return (
// <Menu
// id="viewMenu"
// anchorEl={menuAnchorEl ? menuAnchorEl : null}
// open={menuAnchorEl !== null}
// onClose={() => setMenuAnchorEl(null)}
// disableAutoFocusItem>
// <MenuItem
// onClick={() => {
// editTask(props.task);
// setMenuAnchorEl(null);
// }}>
// <ListItemIcon>
// <Edit />
// </ListItemIcon>
// <ListItemText primary="Edit" />
// </MenuItem>
// <MenuItem
// onClick={() => {
// deleteTask(props.task);
// setMenuAnchorEl(null);
// closeCallback();
// }}>
// <ListItemIcon>
// <DeleteIcon style={{ color: "red" }} />
// </ListItemIcon>
// <ListItemText primary="Delete" />
// </MenuItem>
// </Menu>
// );
// };
const bodyButtons = () => {
return (
<Grid container direction="row" alignItems="center" justifyContent="space-between">
@ -195,7 +162,9 @@ export default function TaskDrawer(props: Props) {
permissions={permissions}
task={task}
removeTask={deleteTask}
refreshCallback={closeCallback}
refreshCallback={() => {
closeCallback(true)
}}
keys={props.keys}
types={props.types}
/>
@ -335,7 +304,9 @@ export default function TaskDrawer(props: Props) {
displayNext={() => {}}
displayPrev={() => {}}
drawerBody={drawerBody()}
onClose={closeCallback}
onClose={() => {
closeCallback(false)}
}
open={open}
/>
);

View file

@ -12,7 +12,6 @@ import ButtonGroup from "common/ButtonGroup";
interface Props {
tasks: Task[];
editTaskMethod: (task: Task) => void;
markComplete: (task: Task) => void;
deleteTask: (task: Task) => void;
openTask: (taskId: string) => void;
@ -26,7 +25,6 @@ interface Props {
export default function TaskList(props: Props) {
const {
editTaskMethod,
markComplete,
deleteTask,
openTask,
@ -74,60 +72,10 @@ export default function TaskList(props: Props) {
setComplete(complete);
}, [tasks, dateToView]);
// const StyledToggleButtonGroup = withStyles(theme => ({
// grouped: {
// margin: theme.spacing(-0.5),
// border: "none",
// padding: theme.spacing(1),
// "&:not(:first-child):not(:last-child)": {
// borderRadius: 24,
// marginRight: theme.spacing(0.5),
// marginLeft: theme.spacing(0.5)
// },
// "&:first-child": {
// borderRadius: 24,
// marginLeft: theme.spacing(0.25)
// },
// "&:last-child": {
// borderRadius: 24,
// marginRight: theme.spacing(0.25)
// }
// },
// root: {
// backgroundColor: darken(
// theme.palette.background.paper,
// getThemeType() === "light" ? 0.05 : 0.25
// ),
// borderRadius: 24,
// content: "border-box"
// }
// }))(ToggleButtonGroup);
// const StyledToggle = withStyles({
// root: {
// backgroundColor: "transparent",
// overflow: "visible",
// content: "content-box",
// "&$selected": {
// backgroundColor: "gold",
// color: "black",
// borderRadius: 24,
// fontWeight: "bold"
// },
// "&$selected:hover": {
// backgroundColor: "rgb(255, 255, 0)",
// color: "black",
// borderRadius: 24
// }
// },
// selected: {}
// })(ToggleButton);
const incompleteTasks = incomplete.map((task, index) => (
<ListItem divider key={index} disableGutters>
<TaskCard
task={task}
editTaskMethod={editTaskMethod}
markComplete={(task: Task) => markComplete(task)}
deleteTask={(task: Task) => deleteTask(task)}
reLoad={reLoad}
@ -142,7 +90,6 @@ export default function TaskList(props: Props) {
<ListItem divider key={index} disableGutters>
<TaskCard
task={task}
editTaskMethod={editTaskMethod}
markComplete={(task: Task) => markComplete(task)}
deleteTask={(task: Task) => deleteTask(task)}
reLoad={reLoad}
@ -170,20 +117,6 @@ export default function TaskList(props: Props) {
}
]}
/>
{/* <ToggleButtonGroup value={viewing} exclusive size="small" aria-label="Bin Mode">
<ToggleButton
value={"upcoming"}
aria-label="upcoming"
onClick={() => setViewing("upcoming")}>
Upcoming
</ToggleButton>
<ToggleButton
onClick={() => setViewing("complete")}
value={"complete"}
aria-label="complete">
Complete
</ToggleButton>
</ToggleButtonGroup> */}
{location !== "/tasks" && (
<IconButton id="tasks" component={Link} to="/tasks">
<TasksIcon />

View file

@ -99,7 +99,6 @@ export default function TaskViewer(props: ViewProps) {
overlayButton
} = props;
const [{ user, as }] = useGlobalState();
//const [{ as }] = useGlobalState();
const taskAPI = useTaskAPI();
const { openSnack } = useSnackbar();
const [tasks, setTasks] = useState<Map<string, Task>>(new Map<string, Task>([]));
@ -147,36 +146,6 @@ export default function TaskViewer(props: ViewProps) {
}
};
const setTaskToEdit = (task: Task) => {
setEditTask(task);
openDialog();
};
// const loadMultitask = useCallback(() => {
// if (!loadKeys) return;
// if (loadKeys.length > 0) {
// let temp = new Map<string, Task>();
// setLoaded(false);
// taskAPI
// .getMultiTasks(loadKeys, as)
// .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");
// });
// }
// }, [loadKeys, openSnack, taskAPI, as]);
//loads tasks from the backend database
const loadTasks = useCallback(() => {
if (!user.id()) return;
@ -324,7 +293,6 @@ export default function TaskViewer(props: ViewProps) {
<TaskList
tasks={Array.from(tasks.values())}
label={label}
editTaskMethod={setTaskToEdit}
markComplete={markComplete}
deleteTask={deleteTask}
reLoad={() => loadTasks()}
@ -385,7 +353,6 @@ export default function TaskViewer(props: ViewProps) {
<TaskList
tasks={Array.from(tasks.values())}
label={label}
editTaskMethod={setTaskToEdit}
markComplete={markComplete}
deleteTask={deleteTask}
reLoad={() => loadTasks()}
@ -437,18 +404,10 @@ export default function TaskViewer(props: ViewProps) {
<TaskSettings
open={newTaskDialog}
task={editTask}
//objectKey={objectKey}
keys={keys}
types={types}
startDate={startDate}
onClose={r => {
// if (r) {
// if (drawerView) {
// loadMultitask();
// } else {
// loadTasks();
// }
// }
loadTasks();
setEditTask(undefined);
setNewTaskDialog(false);
@ -464,8 +423,8 @@ export default function TaskViewer(props: ViewProps) {
}}
completeTask={markComplete}
deleteTask={deleteTask}
keys={[]}
types={[]}
keys={keys ?? []}
types={types ?? []}
/>
}
</Box>

View file

@ -224,6 +224,7 @@ export default function TaskActions(props: Props) {
return (
<React.Fragment>
<IconButton
disabled={!permissions.includes(pond.Permission.PERMISSION_WRITE)}
aria-owns={anchorEl ? "groupMenu" : undefined}
aria-haspopup="true"
onClick={(event: React.MouseEvent<HTMLButtonElement>) => setAnchorEl(event.currentTarget)}>

View file

@ -41,6 +41,8 @@ import { useNavigate } from "react-router-dom";
import { makeStyles } from "@mui/styles";
import { Share, RemoveCircle as RemoveUserIcon } from "@mui/icons-material";
import CancelSubmit from "common/CancelSubmit";
import { permissionToString } from "pbHelpers/Permission";
import { PermissionChanges } from "providers/pond/permissionAPI";
const useStyles = makeStyles((theme: Theme) => {
const avatarBG = theme.palette.secondary["700" as keyof PaletteColor];
@ -188,7 +190,21 @@ export default function ObjectTeams(props: Props) {
};
const submit = () => {
permissionAPI
if(keys && types){
let changes: PermissionChanges[] = []
users.forEach((user: Team) => {
changes.push({
key: user.id(),
permissions: user.permissions.map(permission => permissionToString(permission))
});
});
permissionAPI.updateObjectPermissions(scope.key, scope.kind, "team", changes, keys, types).then(resp => {
console.log("no error")
}).catch(err => {
console.log("error")
})
}else{
permissionAPI
.updatePermissions(scope, users)
.then((_response: any) => {
success("Users were sucessfully updated for " + label);
@ -201,10 +217,13 @@ export default function ObjectTeams(props: Props) {
})
.catch((err: any) => {
err.response.data.error
? warning(err.response.data.error)
: error("Error occured when updating users for " + label);
? warning(err.response.data.error)
: error("Error occured when updating users for " + label);
close();
});
}
};
const changeUserPermissions = (user: pond.ITeam) => (event: any) => {