frontend/src/tasks/TaskSettings.tsx

352 lines
10 KiB
TypeScript

import {
Avatar,
Box,
Button,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
Grid2 as Grid,
InputAdornment,
MenuItem,
TextField
} from "@mui/material";
import moment from "moment";
import React, { useState } from "react";
import { pond } from "protobuf-ts/pond";
import { useGlobalState, useTaskAPI } from "providers";
import { useSnackbar, useUserAPI } from "hooks";
import { useEffect } from "react";
import { Task, teamScope, User } from "models";
import ColourPicker from "common/ColourPicker";
import ResponsiveDialog from "common/ResponsiveDialog";
interface Props {
open: boolean;
onClose: (reLoad?: boolean) => void;
markComplete?: boolean;
startDate?: any;
task?: Task;
objectKey?: string;
type?: string;
hasCost?: boolean;
costTitle?: string;
secondaryCostTitle?: string;
}
export default function TaskSettings(props: Props) {
const [taskName, setTaskName] = useState("");
const [startDate, setStartDate] = useState(moment().format("YYYY-MM-DD"));
const [startTime, setStartTime] = useState("00:00");
const [endDate, setEndDate] = useState("");
const [endTime, setEndTime] = useState("00:00");
const [taskDescription, setTaskDescription] = useState("");
const [{ user, as }] = useGlobalState();
const [worker, setWorker] = useState(user.id());
const [colour, setColour] = useState("");
const taskAPI = useTaskAPI();
const { openSnack } = useSnackbar();
const [cost, setCost] = useState("");
const [secondaryCost, setSecondaryCost] = useState("");
const [seedCost, setSeedCost] = useState("");
const [poundPerAcre, setPoundPerAcre] = useState(0);
const userAPI = useUserAPI();
const [users, setUsers] = useState<User[]>([]);
useEffect(() => {
if (props.task) {
setTaskName(props.task.title());
setStartDate(props.task.start());
setStartTime(props.task.startTime());
setEndDate(props.task.end());
setEndTime(props.task.endTime());
setTaskDescription(props.task.description());
setWorker(props.task.worker());
setColour(props.task.settings.colour);
setCost(props.task.cost().toString());
setSecondaryCost(props.task.secondaryCost().toString());
}
if (props.startDate) {
setStartDate(props.startDate);
}
}, [props.startDate, props.task]);
useEffect(() => {
if (as === "") {
setWorker(user.id());
setUsers([user]);
} else {
userAPI.listObjectUsers(teamScope(as)).then(resp => {
setUsers(resp.data.users.map((u: pond.User) => User.any(u)));
});
}
}, [as, userAPI, user]);
const formComplete = () => {
if (
taskName.length < 1 ||
taskDescription.length < 1 ||
worker.length < 1 ||
endDate.length < 1
) {
return false;
} else {
return true;
}
};
//saves new tasks into the database
const newTask = () => {
let taskSettings = pond.TaskSettings.create({
userId: user.id(),
title: taskName,
start: startDate,
startTime: startTime,
end: endDate,
endTime: endTime,
objectKey: props.objectKey ? props.objectKey : user.id(),
description: taskDescription,
worker: worker,
complete: false,
colour: colour,
type: props.type,
cost: !isNaN(parseFloat(cost)) ? Math.round((parseFloat(cost) * 100) / 100) : 0,
secondaryCost: !isNaN(parseFloat(secondaryCost))
? Math.round((parseFloat(secondaryCost) * 100) / 100)
: 0
});
let returnTask = Task.create();
returnTask.settings = taskSettings;
taskAPI
.addTask(taskSettings, as)
.then(resp => {
props.onClose(true);
})
.catch(err => {
openSnack("Failed to add new task");
});
props.onClose();
};
const updateTask = () => {
let task: Task = Task.any(props.task);
task.settings.title = taskName;
task.settings.start = startDate;
task.settings.startTime = startTime;
task.settings.end = endDate;
task.settings.endTime = endTime;
task.settings.description = taskDescription;
task.settings.worker = worker;
task.settings.colour = colour;
task.settings.cost = !isNaN(parseFloat(cost)) ? Math.round(parseFloat(cost) * 100) / 100 : 0;
task.settings.secondaryCost = !isNaN(parseFloat(secondaryCost))
? Math.round(parseFloat(secondaryCost) * 100) / 100
: 0;
taskAPI
.updateTask(task.key, task.settings, undefined, as)
.then(resp => {
props.onClose(true);
})
.catch(err => {
openSnack("Failed to update task");
});
};
const closeDialog = () => {
props.onClose();
};
return (
<ResponsiveDialog open={props.open} onClose={closeDialog}>
<DialogTitle id="form-dialog-title">New Task</DialogTitle>
<DialogContent>
<TextField
autoFocus
margin="dense"
id="name"
label="Task Title"
type="text"
fullWidth
value={taskName}
onChange={e => setTaskName(e.target.value)}
/>
<TextField
margin="dense"
id="description"
label="Task Description"
type="text"
fullWidth
multiline
value={taskDescription}
onChange={e => setTaskDescription(e.target.value)}
/>
<TextField
disabled={as === ""}
margin="dense"
id="worker"
label="Assigned Worker"
select
fullWidth
value={worker}
onChange={e => setWorker(e.target.value)}>
{users.map(u => (
<MenuItem key={u.id()} value={u.id()}>
<Grid container direction="row" alignItems="center">
<Grid>
<Avatar
alt={u.name()}
src={
u.settings.avatar && u.settings.avatar !== "" ? u.settings.avatar : undefined
}
/>
</Grid>
<Grid>
<Box marginLeft={2}>{u.name()}</Box>
</Grid>
</Grid>
</MenuItem>
))}
</TextField>
{props.type === "seed" && (
<React.Fragment>
<TextField
style={{ width: "45%" }}
margin="normal"
id="costPerPound"
label="Seed Cost Per Pound"
type="text"
helperText="Must be a valid number"
value={seedCost}
error={isNaN(+seedCost) && seedCost !== ""}
InputProps={{
startAdornment: <InputAdornment position="start">$</InputAdornment>,
endAdornment: <InputAdornment position="end">/Lb</InputAdornment>
}}
onChange={e => {
setSeedCost(e.target.value);
setCost((parseFloat(e.target.value) * poundPerAcre).toString());
}}
/>
<TextField
style={{ width: "45%", marginLeft: "10%" }}
margin="normal"
id="applicationRate"
label="Application Rate"
type="number"
value={poundPerAcre}
InputProps={{
endAdornment: <InputAdornment position="end">Lb/Acre</InputAdornment>
}}
onChange={e => {
setPoundPerAcre(+e.target.value);
setCost((parseFloat(seedCost) * +e.target.value).toString());
}}
/>
</React.Fragment>
)}
{props.hasCost && (
<TextField
margin="dense"
id="cost"
label={props.costTitle ? props.costTitle : "Cost"}
type="text"
fullWidth
helperText="Must be a valid number"
value={cost}
error={isNaN(+cost) && cost !== ""}
InputProps={{
startAdornment: <InputAdornment position="start">$</InputAdornment>
}}
onChange={e => {
setCost(e.target.value);
}}
/>
)}
{props.hasCost && (
<TextField
margin="dense"
id="secondaryCost"
label={props.secondaryCostTitle ? props.secondaryCostTitle : "Secondary Cost"}
type="text"
fullWidth
helperText="Must be a valid number"
value={secondaryCost}
error={isNaN(+secondaryCost) && secondaryCost !== ""}
InputProps={{
startAdornment: <InputAdornment position="start">$</InputAdornment>
}}
onChange={e => {
setSecondaryCost(e.target.value);
}}
/>
)}
{/* if the calendar was clicked gets the start date from that otherwise it will default to today */}
<TextField
style={{ width: "45%" }}
margin="normal"
type="date"
label="Start Date"
value={startDate}
onChange={e => setStartDate(e.target.value)}
/>
<TextField
style={{ width: "45%", marginLeft: "10%" }}
margin="normal"
type="time"
label="Start Time"
value={startTime}
onChange={e => setStartTime(e.target.value)}
/>
<TextField
style={{ width: "45%" }}
margin="normal"
type="date"
label="End Date"
value={endDate}
InputLabelProps={{ shrink: true }}
onChange={e => setEndDate(e.target.value)}
/>
<TextField
style={{ width: "45%", marginLeft: "10%" }}
margin="normal"
type="time"
label="End Time"
value={endTime}
onChange={e => setEndTime(e.target.value)}
/>
<Box marginTop={3}>
Colour
<ColourPicker onChange={color => setColour(color)} />
</Box>
</DialogContent>
<DialogActions>
<Button onClick={closeDialog} variant="contained" style={{ margin: 5 }} color="primary">
Cancel
</Button>
{props.task ? (
<Button
onClick={updateTask}
variant="contained"
style={{ margin: 5 }}
color="primary"
disabled={!formComplete()}>
Update
</Button>
) : (
<Button
onClick={newTask}
variant="contained"
style={{ margin: 5 }}
color="primary"
disabled={!formComplete()}>
Create Task
</Button>
)}
</DialogActions>
</ResponsiveDialog>
);
}