Skip to content
Snippets Groups Projects
Commit 51947458 authored by Bernard Roland (Student Com20)'s avatar Bernard Roland (Student Com20)
Browse files

Added a button to quickly add/change assignment

parent 91567604
No related branches found
No related tags found
No related merge requests found
import { useCallback, useState } from "react";
import Popup from 'components/ui/Popup';
import Button from 'components/ui/Button';
import TimeInput from "components/ui/TimeInput";
import '../form.scss';
interface Props {
onAssign: (duration: number) => any;
initialTime?: number;
}
export default function AssignForm({ onAssign, initialTime }: Props) {
const [popup, setPopup] = useState(false);
const [selectedTime, setSelectedTime] = useState<number | undefined>(initialTime);
const addAssignee = useCallback((e) => {
e.preventDefault();
setPopup(false);
if (selectedTime && !Number.isNaN(selectedTime)) {
onAssign(selectedTime * 60);
} else {
onAssign(0);
}
}, [onAssign, selectedTime])
return <>
<Button className="expanded" onClick={() => setPopup(true)}>
{initialTime ? 'Change assignment' : 'Assign yourself'}
</Button>
{
popup && (
<Popup onClose={() => setPopup(false)}>
<form onSubmit={addAssignee}>
<TimeInput initialTime={initialTime && (initialTime / 60)} onChange={value => setSelectedTime(value)} />
<div>
<Button type="submit" className="expanded">
{initialTime ? 'Change assignment' : 'Assign yourself'}
</Button>
</div>
</form>
</Popup>
)
}
</>;
}
...@@ -7,18 +7,25 @@ import './time-input.scss'; ...@@ -7,18 +7,25 @@ import './time-input.scss';
interface Props { interface Props {
onChange: (state: number) => void; onChange: (state: number) => void;
initialTime?: number;
} }
export default function TimeInput({ onChange: userOnChange }: Props) { function getFormatted(hours: number) {
const [formatted, setFormatted] = useState(''); if (hours > 0) {
return formatDuration(durationFor(hours, 'hour'), 'second', 2, true);
} else {
return 'none';
}
}
export default function TimeInput({ onChange: userOnChange, initialTime }: Props) {
const [formatted, setFormatted] = useState(initialTime ? getFormatted(initialTime) : '');
const onChange = useCallback(event => { const onChange = useCallback(event => {
const value = parseFloat(event.target.value); const value = parseFloat(event.target.value);
userOnChange(value); userOnChange(value);
if (!Number.isNaN(value)) { if (!Number.isNaN(value)) {
setFormatted( setFormatted(getFormatted(value));
formatDuration(durationFor(value, 'hour'), 'second', 2, true)
);
} else { } else {
setFormatted(''); setFormatted('');
} }
...@@ -27,7 +34,7 @@ export default function TimeInput({ onChange: userOnChange }: Props) { ...@@ -27,7 +34,7 @@ export default function TimeInput({ onChange: userOnChange }: Props) {
return ( return (
<div className="time-field"> <div className="time-field">
<label htmlFor="time">Time in hours</label> <label htmlFor="time">Time in hours</label>
<input type="number" name="time" min={0} onChange={onChange} /> <input name="time" min={0} onChange={onChange} defaultValue={initialTime} />
<span className="formatted">{formatted}</span> <span className="formatted">{formatted}</span>
</div> </div>
); );
......
...@@ -6,28 +6,18 @@ ...@@ -6,28 +6,18 @@
margin: 20px 0; margin: 20px 0;
display: flex; display: flex;
align-items: baseline; align-items: baseline;
border-radius: 15px;
color: s.$text;
background: s.$background-light;
.formatted { .formatted {
flex: 0 0 auto; flex: 0 0 auto;
content: 'min'; content: 'min';
margin-left: 10px; min-width: 75px;
min-width: 64px;
white-space: nowrap; white-space: nowrap;
text-align: center; text-align: center;
} }
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* Firefox */
input[type=number] {
-moz-appearance: textfield;
}
label { label {
&:after { &:after {
content: ' *'; content: ' *';
......
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
import { useHistory, useParams } from 'react-router'; import { useHistory, useParams } from 'react-router';
import { getTeam } from 'adapters/team'; import { getTeam } from 'adapters/team';
...@@ -8,7 +8,7 @@ import { AssignedUser } from 'adapters/user'; ...@@ -8,7 +8,7 @@ import { AssignedUser } from 'adapters/user';
import { StatusColors } from 'adapters/common'; import { StatusColors } from 'adapters/common';
import { getLoggedInUser } from 'adapters/auth'; import { getLoggedInUser } from 'adapters/auth';
import { getProject, Project } from 'adapters/project'; import { getProject, Project } from 'adapters/project';
import { getTask, getTaskAssignees, Task, TaskAssignment } from 'adapters/task'; import { getTask, getTaskAssignees, Task, updateTask } from 'adapters/task';
import Tag from 'components/ui/Tag'; import Tag from 'components/ui/Tag';
import LongText from 'components/ui/LongText'; import LongText from 'components/ui/LongText';
...@@ -21,6 +21,7 @@ import TaskAssignees from './TaskAssignees'; ...@@ -21,6 +21,7 @@ import TaskAssignees from './TaskAssignees';
import TaskComments from './TaskComments'; import TaskComments from './TaskComments';
import './task-detail.scss'; import './task-detail.scss';
import AssignForm from 'components/forms/AssignForm';
export interface Params { export interface Params {
taskId: string; taskId: string;
...@@ -31,11 +32,11 @@ export default function TaskDetail() { ...@@ -31,11 +32,11 @@ export default function TaskDetail() {
const [project, setProject] = useState<Project>(); const [project, setProject] = useState<Project>();
const [teamNames, setTeamNames] = useState<string[]>([]); const [teamNames, setTeamNames] = useState<string[]>([]);
const [assignees, setAssignees] = useState<AssignedUser[]>([]); const [assignees, setAssignees] = useState<AssignedUser[]>([]);
const [assignment, setAssignment] = useState<TaskAssignment>();
const history = useHistory(); const history = useHistory();
const { taskId } = useParams<Params>(); const { taskId } = useParams<Params>();
const userId = getLoggedInUser(); const userId = getLoggedInUser();
const assignment = task?.assigned.find(a => a.user === userId);
useEffect(() => { useEffect(() => {
getTask(taskId).then((task) => { getTask(taskId).then((task) => {
...@@ -47,11 +48,30 @@ export default function TaskDetail() { ...@@ -47,11 +48,30 @@ export default function TaskDetail() {
.map(team => team.name) .map(team => team.name)
); );
}); });
getTaskAssignees(taskId).then(setAssignees);
setAssignment(task.assigned.find(a => a.user === userId))
}).catch(() => {}); }).catch(() => {});
getTaskAssignees(taskId).then(setAssignees);
}, [taskId, userId, history]); }, [taskId, userId, history]);
const onAssign = useCallback((time: number) => {
const reloadData = () => {
getTask(taskId).then(setTask);
getTaskAssignees(taskId).then(setAssignees);
};
if (time > 0) {
updateTask(taskId, {
remove_assigned: [ userId ],
add_assigned: [{
user: userId,
time: time,
finished: assignment?.finished ?? false,
}],
}).then(reloadData);
} else {
updateTask(taskId, { remove_assigned: [ userId ] })
.then(reloadData);
}
}, [taskId, userId, assignment]);
if (task) { if (task) {
return ( return (
<div className={'tasks-detail-page theme-' + task.color}> <div className={'tasks-detail-page theme-' + task.color}>
...@@ -80,6 +100,7 @@ export default function TaskDetail() { ...@@ -80,6 +100,7 @@ export default function TaskDetail() {
</ButtonLink> </ButtonLink>
) )
} }
<AssignForm onAssign={onAssign} initialTime={assignment && assignment.time} />
<ButtonLink href={'/tasks/' + taskId + '/edit'} className="dark expanded"> <ButtonLink href={'/tasks/' + taskId + '/edit'} className="dark expanded">
Edit Edit
</ButtonLink> </ButtonLink>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment