feat: redesign task due date manager
This commit is contained in:
		@@ -126,4 +126,8 @@ export default createGlobalStyle`
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ${mixin.placeholderColor(color.textLight)}
 | 
					  ${mixin.placeholderColor(color.textLight)}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .picker-hidden {
 | 
				
			||||||
 | 
					    display: none;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -793,12 +793,12 @@ const ProjectBoard: React.FC<ProjectBoardProps> = ({ projectID, onCardLabelClick
 | 
				
			|||||||
                  <DueDateManager
 | 
					                  <DueDateManager
 | 
				
			||||||
                    task={task}
 | 
					                    task={task}
 | 
				
			||||||
                    onRemoveDueDate={t => {
 | 
					                    onRemoveDueDate={t => {
 | 
				
			||||||
                      updateTaskDueDate({ variables: { taskID: t.id, dueDate: null } });
 | 
					                      updateTaskDueDate({ variables: { taskID: t.id, dueDate: null, hasTime: false } });
 | 
				
			||||||
                      hidePopup();
 | 
					                      // hidePopup();
 | 
				
			||||||
                    }}
 | 
					                    }}
 | 
				
			||||||
                    onDueDateChange={(t, newDueDate) => {
 | 
					                    onDueDateChange={(t, newDueDate, hasTime) => {
 | 
				
			||||||
                      updateTaskDueDate({ variables: { taskID: t.id, dueDate: newDueDate } });
 | 
					                      updateTaskDueDate({ variables: { taskID: t.id, dueDate: newDueDate, hasTime } });
 | 
				
			||||||
                      hidePopup();
 | 
					                      // hidePopup();
 | 
				
			||||||
                    }}
 | 
					                    }}
 | 
				
			||||||
                    onCancel={NOOP}
 | 
					                    onCancel={NOOP}
 | 
				
			||||||
                  />
 | 
					                  />
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -632,12 +632,12 @@ const Details: React.FC<DetailsProps> = ({
 | 
				
			|||||||
                    <DueDateManager
 | 
					                    <DueDateManager
 | 
				
			||||||
                      task={task}
 | 
					                      task={task}
 | 
				
			||||||
                      onRemoveDueDate={t => {
 | 
					                      onRemoveDueDate={t => {
 | 
				
			||||||
                        updateTaskDueDate({ variables: { taskID: t.id, dueDate: null } });
 | 
					                        updateTaskDueDate({ variables: { taskID: t.id, dueDate: null, hasTime: false } });
 | 
				
			||||||
                        hidePopup();
 | 
					                        // hidePopup();
 | 
				
			||||||
                      }}
 | 
					                      }}
 | 
				
			||||||
                      onDueDateChange={(t, newDueDate) => {
 | 
					                      onDueDateChange={(t, newDueDate, hasTime) => {
 | 
				
			||||||
                        updateTaskDueDate({ variables: { taskID: t.id, dueDate: newDueDate } });
 | 
					                        updateTaskDueDate({ variables: { taskID: t.id, dueDate: newDueDate, hasTime } });
 | 
				
			||||||
                        hidePopup();
 | 
					                        // hidePopup();
 | 
				
			||||||
                      }}
 | 
					                      }}
 | 
				
			||||||
                      onCancel={NOOP}
 | 
					                      onCancel={NOOP}
 | 
				
			||||||
                    />
 | 
					                    />
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,8 @@ import styled from 'styled-components';
 | 
				
			|||||||
import Button from 'shared/components/Button';
 | 
					import Button from 'shared/components/Button';
 | 
				
			||||||
import { mixin } from 'shared/utils/styles';
 | 
					import { mixin } from 'shared/utils/styles';
 | 
				
			||||||
import Input from 'shared/components/Input';
 | 
					import Input from 'shared/components/Input';
 | 
				
			||||||
 | 
					import ControlledInput from 'shared/components/ControlledInput';
 | 
				
			||||||
 | 
					import { Clock } from 'shared/icons';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const Wrapper = styled.div`
 | 
					export const Wrapper = styled.div`
 | 
				
			||||||
display: flex
 | 
					display: flex
 | 
				
			||||||
@@ -17,6 +19,11 @@ display: flex
 | 
				
			|||||||
    z-index: 10000;
 | 
					    z-index: 10000;
 | 
				
			||||||
    margin-top: 0;
 | 
					    margin-top: 0;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  & .react-datepicker__close-icon::after {
 | 
				
			||||||
 | 
					    background: none;
 | 
				
			||||||
 | 
					    font-size: 16px;
 | 
				
			||||||
 | 
					    color: ${props => props.theme.colors.text.primary};
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  & .react-datepicker-time__header {
 | 
					  & .react-datepicker-time__header {
 | 
				
			||||||
    color: ${props => props.theme.colors.text.primary};
 | 
					    color: ${props => props.theme.colors.text.primary};
 | 
				
			||||||
@@ -91,6 +98,24 @@ display: flex
 | 
				
			|||||||
    border-bottom: 1px solid ${props => props.theme.colors.border};
 | 
					    border-bottom: 1px solid ${props => props.theme.colors.border};
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  & .react-datepicker__input-container input {
 | 
				
			||||||
 | 
					  border: 1px solid rgba(0, 0, 0, 0.2);
 | 
				
			||||||
 | 
					  border-color: ${props => props.theme.colors.alternate};
 | 
				
			||||||
 | 
					  background: #262c49;
 | 
				
			||||||
 | 
					  box-shadow: 0 0 0 0 rgba(0, 0, 0, 0.15);
 | 
				
			||||||
 | 
					padding: 0.7rem;
 | 
				
			||||||
 | 
					  color: #c2c6dc;
 | 
				
			||||||
 | 
					  position: relative;
 | 
				
			||||||
 | 
					  border-radius: 5px;
 | 
				
			||||||
 | 
					  transition: all 0.3s ease;
 | 
				
			||||||
 | 
					      font-size: 13px;
 | 
				
			||||||
 | 
					    line-height: 20px;
 | 
				
			||||||
 | 
					    padding: 0 12px;
 | 
				
			||||||
 | 
					  &:focus {
 | 
				
			||||||
 | 
					    box-shadow: 0 3px 10px 0 rgba(0, 0, 0, 0.15);
 | 
				
			||||||
 | 
					    border: 1px solid rgba(115, 103, 240);
 | 
				
			||||||
 | 
					    background: ${props => props.theme.colors.bg.primary};
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const DueDatePickerWrapper = styled.div`
 | 
					export const DueDatePickerWrapper = styled.div`
 | 
				
			||||||
@@ -110,6 +135,44 @@ export const RemoveDueDate = styled(Button)`
 | 
				
			|||||||
  margin: 0 0 0 4px;
 | 
					  margin: 0 0 0 4px;
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const AddDateRange = styled.div`
 | 
				
			||||||
 | 
					  opacity: 0.6;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					  font-size: 12px;
 | 
				
			||||||
 | 
					  line-height: 16px;
 | 
				
			||||||
 | 
					  color: ${props => mixin.rgba(props.theme.colors.primary, 0.8)};
 | 
				
			||||||
 | 
					  &:hover {
 | 
				
			||||||
 | 
					    color: ${props => mixin.rgba(props.theme.colors.primary, 1)};
 | 
				
			||||||
 | 
					    text-decoration: underline;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const DateRangeInputs = styled.div`
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  flex-wrap: wrap;
 | 
				
			||||||
 | 
					  justify-content: center;
 | 
				
			||||||
 | 
					  margin-left: -4px;
 | 
				
			||||||
 | 
					  & > div:first-child,
 | 
				
			||||||
 | 
					  & > div:last-child {
 | 
				
			||||||
 | 
					    flex: 1 1 92px;
 | 
				
			||||||
 | 
					    margin-bottom: 4px;
 | 
				
			||||||
 | 
					    margin-left: 4px;
 | 
				
			||||||
 | 
					    min-width: 92px;
 | 
				
			||||||
 | 
					    width: initial;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  & > ${AddDateRange} {
 | 
				
			||||||
 | 
					    margin-left: 4px;
 | 
				
			||||||
 | 
					    padding-left: 4px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  & > .react-datepicker-wrapper input {
 | 
				
			||||||
 | 
					    padding-bottom: 4px;
 | 
				
			||||||
 | 
					    padding-top: 4px;
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const CancelDueDate = styled.div`
 | 
					export const CancelDueDate = styled.div`
 | 
				
			||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
  align-items: center;
 | 
					  align-items: center;
 | 
				
			||||||
@@ -119,15 +182,86 @@ export const CancelDueDate = styled.div`
 | 
				
			|||||||
  cursor: pointer;
 | 
					  cursor: pointer;
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const DueDateInput = styled(Input)`
 | 
					export const DueDateInput = styled(ControlledInput)`
 | 
				
			||||||
  margin-top: 15px;
 | 
					  margin-top: 15px;
 | 
				
			||||||
  margin-bottom: 5px;
 | 
					  margin-bottom: 5px;
 | 
				
			||||||
  padding-right: 10px;
 | 
					  padding-right: 10px;
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const ActionWrapper = styled.div`
 | 
					export const ActionsSeparator = styled.div`
 | 
				
			||||||
  padding-top: 8px;
 | 
					  margin-top: 8px;
 | 
				
			||||||
 | 
					  height: 1px;
 | 
				
			||||||
  width: 100%;
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					  background: #414561;
 | 
				
			||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
  justify-content: space-between;
 | 
					`;
 | 
				
			||||||
 | 
					export const ActionsWrapper = styled.div`
 | 
				
			||||||
 | 
					  margin-top: 8px;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  & .react-datepicker-wrapper {
 | 
				
			||||||
 | 
					    margin-left: auto;
 | 
				
			||||||
 | 
					    width: 82px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  & .react-datepicker__input-container input {
 | 
				
			||||||
 | 
					    padding-bottom: 4px;
 | 
				
			||||||
 | 
					    padding-top: 4px;
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const ActionClock = styled(Clock)`
 | 
				
			||||||
 | 
					  align-self: center;
 | 
				
			||||||
 | 
					  fill: ${props => props.theme.colors.primary};
 | 
				
			||||||
 | 
					  margin: 0 8px;
 | 
				
			||||||
 | 
					  flex: 0 0 auto;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const ActionLabel = styled.div`
 | 
				
			||||||
 | 
					  font-size: 12px;
 | 
				
			||||||
 | 
					  line-height: 14px;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const ActionIcon = styled.div`
 | 
				
			||||||
 | 
					  height: 36px;
 | 
				
			||||||
 | 
					  min-height: 36px;
 | 
				
			||||||
 | 
					  min-width: 36px;
 | 
				
			||||||
 | 
					  width: 36px;
 | 
				
			||||||
 | 
					  border-radius: 6px;
 | 
				
			||||||
 | 
					  background: transparent;
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					  margin-right: 8px;
 | 
				
			||||||
 | 
					  svg {
 | 
				
			||||||
 | 
					    fill: ${props => props.theme.colors.text.primary};
 | 
				
			||||||
 | 
					    transition-duration: 0.2s;
 | 
				
			||||||
 | 
					    transition-property: background, border, box-shadow, fill;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  &:hover svg {
 | 
				
			||||||
 | 
					    fill: ${props => props.theme.colors.text.secondary};
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  display: inline-flex;
 | 
				
			||||||
 | 
					  justify-content: center;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const ClearButton = styled.div`
 | 
				
			||||||
 | 
					  font-weight: 500;
 | 
				
			||||||
 | 
					  font-size: 13px;
 | 
				
			||||||
 | 
					  height: 36px;
 | 
				
			||||||
 | 
					  line-height: 36px;
 | 
				
			||||||
 | 
					  padding: 0 12px;
 | 
				
			||||||
 | 
					  margin-left: auto;
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  border-radius: 6px;
 | 
				
			||||||
 | 
					  display: inline-flex;
 | 
				
			||||||
 | 
					  flex-shrink: 0;
 | 
				
			||||||
 | 
					  justify-content: center;
 | 
				
			||||||
 | 
					  transition-duration: 0.2s;
 | 
				
			||||||
 | 
					  transition-property: background, border, box-shadow, color, fill;
 | 
				
			||||||
 | 
					  color: ${props => props.theme.colors.text.primary};
 | 
				
			||||||
 | 
					  &:hover {
 | 
				
			||||||
 | 
					    color: ${props => props.theme.colors.text.secondary};
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
import React, { useState, useEffect, forwardRef } from 'react';
 | 
					import React, { useState, useEffect, forwardRef, useRef, useCallback } from 'react';
 | 
				
			||||||
import dayjs from 'dayjs';
 | 
					import dayjs from 'dayjs';
 | 
				
			||||||
import styled from 'styled-components';
 | 
					import styled from 'styled-components';
 | 
				
			||||||
import DatePicker from 'react-datepicker';
 | 
					import DatePicker from 'react-datepicker';
 | 
				
			||||||
@@ -8,11 +8,27 @@ import { getYear, getMonth } from 'date-fns';
 | 
				
			|||||||
import { useForm, Controller } from 'react-hook-form';
 | 
					import { useForm, Controller } from 'react-hook-form';
 | 
				
			||||||
import NOOP from 'shared/utils/noop';
 | 
					import NOOP from 'shared/utils/noop';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { Wrapper, ActionWrapper, RemoveDueDate, DueDateInput, DueDatePickerWrapper, ConfirmAddDueDate } from './Styles';
 | 
					import {
 | 
				
			||||||
 | 
					  Wrapper,
 | 
				
			||||||
 | 
					  RemoveDueDate,
 | 
				
			||||||
 | 
					  DueDateInput,
 | 
				
			||||||
 | 
					  DueDatePickerWrapper,
 | 
				
			||||||
 | 
					  ConfirmAddDueDate,
 | 
				
			||||||
 | 
					  DateRangeInputs,
 | 
				
			||||||
 | 
					  AddDateRange,
 | 
				
			||||||
 | 
					  ActionIcon,
 | 
				
			||||||
 | 
					  ActionsWrapper,
 | 
				
			||||||
 | 
					  ClearButton,
 | 
				
			||||||
 | 
					  ActionsSeparator,
 | 
				
			||||||
 | 
					  ActionClock,
 | 
				
			||||||
 | 
					  ActionLabel,
 | 
				
			||||||
 | 
					} from './Styles';
 | 
				
			||||||
 | 
					import { Clock, Cross } from 'shared/icons';
 | 
				
			||||||
 | 
					import Select from 'react-select/src/Select';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type DueDateManagerProps = {
 | 
					type DueDateManagerProps = {
 | 
				
			||||||
  task: Task;
 | 
					  task: Task;
 | 
				
			||||||
  onDueDateChange: (task: Task, newDueDate: Date) => void;
 | 
					  onDueDateChange: (task: Task, newDueDate: Date, hasTime: boolean) => void;
 | 
				
			||||||
  onRemoveDueDate: (task: Task) => void;
 | 
					  onRemoveDueDate: (task: Task) => void;
 | 
				
			||||||
  onCancel: () => void;
 | 
					  onCancel: () => void;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
@@ -52,14 +68,20 @@ const HeaderSelect = styled.select`
 | 
				
			|||||||
  text-decoration: underline;
 | 
					  text-decoration: underline;
 | 
				
			||||||
  font-size: 14px;
 | 
					  font-size: 14px;
 | 
				
			||||||
  text-align: center;
 | 
					  text-align: center;
 | 
				
			||||||
  padding: 4px 6px;
 | 
					 | 
				
			||||||
  background: none;
 | 
					  background: none;
 | 
				
			||||||
  outline: none;
 | 
					  outline: none;
 | 
				
			||||||
  border: none;
 | 
					  border: none;
 | 
				
			||||||
  border-radius: 3px;
 | 
					  border-radius: 3px;
 | 
				
			||||||
  appearance: none;
 | 
					  appearance: none;
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					  display: inline-block;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  &:hover {
 | 
					  & option {
 | 
				
			||||||
 | 
					    color: #c2c6dc;
 | 
				
			||||||
 | 
					    background: ${props => props.theme.colors.bg.primary};
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  & option:hover {
 | 
				
			||||||
    background: ${props => props.theme.colors.bg.secondary};
 | 
					    background: ${props => props.theme.colors.bg.secondary};
 | 
				
			||||||
    border: 1px solid ${props => props.theme.colors.primary};
 | 
					    border: 1px solid ${props => props.theme.colors.primary};
 | 
				
			||||||
    outline: none !important;
 | 
					    outline: none !important;
 | 
				
			||||||
@@ -110,15 +132,34 @@ const HeaderActions = styled.div`
 | 
				
			|||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const DueDateManager: React.FC<DueDateManagerProps> = ({ task, onDueDateChange, onRemoveDueDate, onCancel }) => {
 | 
					const DueDateManager: React.FC<DueDateManagerProps> = ({ task, onDueDateChange, onRemoveDueDate, onCancel }) => {
 | 
				
			||||||
  const now = dayjs();
 | 
					  const currentDueDate = task.dueDate ? dayjs(task.dueDate).toDate() : null;
 | 
				
			||||||
  const { register, handleSubmit, errors, setValue, setError, formState, control } = useForm<DueDateFormData>();
 | 
					  const { register, handleSubmit, errors, setValue, setError, formState, control } = useForm<DueDateFormData>();
 | 
				
			||||||
  const [startDate, setStartDate] = useState(new Date());
 | 
					
 | 
				
			||||||
 | 
					  const [startDate, setStartDate] = useState<Date | null>(currentDueDate);
 | 
				
			||||||
 | 
					  const [endDate, setEndDate] = useState<Date | null>(currentDueDate);
 | 
				
			||||||
 | 
					  const [hasTime, enableTime] = useState(task.hasTime ?? false);
 | 
				
			||||||
 | 
					  const firstRun = useRef<boolean>(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const debouncedFunctionRef = useRef((newDate: Date | null, nowHasTime: boolean) => {
 | 
				
			||||||
 | 
					    if (!firstRun.current) {
 | 
				
			||||||
 | 
					      if (newDate) {
 | 
				
			||||||
 | 
					        onDueDateChange(task, newDate, nowHasTime);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        onRemoveDueDate(task);
 | 
				
			||||||
 | 
					        enableTime(false);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      firstRun.current = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  const debouncedChange = useCallback(
 | 
				
			||||||
 | 
					    _.debounce((newDate, nowHasTime) => debouncedFunctionRef.current(newDate, nowHasTime), 500),
 | 
				
			||||||
 | 
					    [],
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
    const newDate = dayjs(startDate).format('YYYY-MM-DD');
 | 
					    debouncedChange(startDate, hasTime);
 | 
				
			||||||
    setValue('endDate', newDate);
 | 
					  }, [startDate, hasTime]);
 | 
				
			||||||
  }, [startDate]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const years = _.range(2010, getYear(new Date()) + 10, 1);
 | 
					  const years = _.range(2010, getYear(new Date()) + 10, 1);
 | 
				
			||||||
  const months = [
 | 
					  const months = [
 | 
				
			||||||
    'January',
 | 
					    'January',
 | 
				
			||||||
@@ -134,19 +175,21 @@ const DueDateManager: React.FC<DueDateManagerProps> = ({ task, onDueDateChange,
 | 
				
			|||||||
    'November',
 | 
					    'November',
 | 
				
			||||||
    'December',
 | 
					    'December',
 | 
				
			||||||
  ];
 | 
					  ];
 | 
				
			||||||
  const saveDueDate = (data: any) => {
 | 
					
 | 
				
			||||||
    const newDate = dayjs(`${data.endDate} ${dayjs(data.endTime).format('h:mm A')}`, 'YYYY-MM-DD h:mm A');
 | 
					  const onChange = (dates: any) => {
 | 
				
			||||||
    if (newDate.isValid()) {
 | 
					    const [start, end] = dates;
 | 
				
			||||||
      onDueDateChange(task, newDate.toDate());
 | 
					    setStartDate(start);
 | 
				
			||||||
    }
 | 
					    setEndDate(end);
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
  const CustomTimeInput = forwardRef(({ value, onClick }: any, $ref: any) => {
 | 
					  const [isRange, setIsRange] = useState(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const CustomTimeInput = forwardRef(({ value, onClick, onChange, onBlur, onFocus }: any, $ref: any) => {
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <DueDateInput
 | 
					      <DueDateInput
 | 
				
			||||||
        id="endTime"
 | 
					        id="endTime"
 | 
				
			||||||
        value={value}
 | 
					        value={value}
 | 
				
			||||||
        name="endTime"
 | 
					        name="endTime"
 | 
				
			||||||
        ref={$ref}
 | 
					        onChange={onChange}
 | 
				
			||||||
        width="100%"
 | 
					        width="100%"
 | 
				
			||||||
        variant="alternate"
 | 
					        variant="alternate"
 | 
				
			||||||
        label="Time"
 | 
					        label="Time"
 | 
				
			||||||
@@ -154,114 +197,119 @@ const DueDateManager: React.FC<DueDateManagerProps> = ({ task, onDueDateChange,
 | 
				
			|||||||
      />
 | 
					      />
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Wrapper>
 | 
					    <Wrapper>
 | 
				
			||||||
      <Form onSubmit={handleSubmit(saveDueDate)}>
 | 
					      <DateRangeInputs>
 | 
				
			||||||
        <FormField>
 | 
					        <DatePicker
 | 
				
			||||||
          <DueDateInput
 | 
					          selected={startDate}
 | 
				
			||||||
            id="endDate"
 | 
					          onChange={date => setStartDate(date)}
 | 
				
			||||||
            name="endDate"
 | 
					          popperClassName="picker-hidden"
 | 
				
			||||||
            width="100%"
 | 
					          dateFormat="yyyy-MM-dd"
 | 
				
			||||||
            variant="alternate"
 | 
					          disabledKeyboardNavigation
 | 
				
			||||||
            label="Date"
 | 
					          isClearable
 | 
				
			||||||
            defaultValue={now.format('YYYY-MM-DD')}
 | 
					          placeholderText="Select due date"
 | 
				
			||||||
            ref={register({
 | 
					        />
 | 
				
			||||||
              required: 'End date is required.',
 | 
					        {isRange ? (
 | 
				
			||||||
            })}
 | 
					 | 
				
			||||||
          />
 | 
					 | 
				
			||||||
        </FormField>
 | 
					 | 
				
			||||||
        <FormField>
 | 
					 | 
				
			||||||
          <Controller
 | 
					 | 
				
			||||||
            control={control}
 | 
					 | 
				
			||||||
            defaultValue={now.toDate()}
 | 
					 | 
				
			||||||
            name="endTime"
 | 
					 | 
				
			||||||
            render={({ onChange, onBlur, value }) => (
 | 
					 | 
				
			||||||
              <DatePicker
 | 
					 | 
				
			||||||
                onChange={onChange}
 | 
					 | 
				
			||||||
                selected={value}
 | 
					 | 
				
			||||||
                onBlur={onBlur}
 | 
					 | 
				
			||||||
                showTimeSelect
 | 
					 | 
				
			||||||
                showTimeSelectOnly
 | 
					 | 
				
			||||||
                timeIntervals={15}
 | 
					 | 
				
			||||||
                timeCaption="Time"
 | 
					 | 
				
			||||||
                dateFormat="h:mm aa"
 | 
					 | 
				
			||||||
                customInput={<CustomTimeInput />}
 | 
					 | 
				
			||||||
              />
 | 
					 | 
				
			||||||
            )}
 | 
					 | 
				
			||||||
          />
 | 
					 | 
				
			||||||
        </FormField>
 | 
					 | 
				
			||||||
        <DueDatePickerWrapper>
 | 
					 | 
				
			||||||
          <DatePicker
 | 
					          <DatePicker
 | 
				
			||||||
            useWeekdaysShort
 | 
					 | 
				
			||||||
            renderCustomHeader={({
 | 
					 | 
				
			||||||
              date,
 | 
					 | 
				
			||||||
              changeYear,
 | 
					 | 
				
			||||||
              changeMonth,
 | 
					 | 
				
			||||||
              decreaseMonth,
 | 
					 | 
				
			||||||
              increaseMonth,
 | 
					 | 
				
			||||||
              prevMonthButtonDisabled,
 | 
					 | 
				
			||||||
              nextMonthButtonDisabled,
 | 
					 | 
				
			||||||
            }) => (
 | 
					 | 
				
			||||||
              <HeaderActions>
 | 
					 | 
				
			||||||
                <HeaderButton onClick={decreaseMonth} disabled={prevMonthButtonDisabled}>
 | 
					 | 
				
			||||||
                  Prev
 | 
					 | 
				
			||||||
                </HeaderButton>
 | 
					 | 
				
			||||||
                <HeaderSelectLabel>
 | 
					 | 
				
			||||||
                  {months[date.getMonth()]}
 | 
					 | 
				
			||||||
                  <HeaderSelect
 | 
					 | 
				
			||||||
                    value={getYear(date)}
 | 
					 | 
				
			||||||
                    onChange={({ target: { value } }) => changeYear(parseInt(value, 10))}
 | 
					 | 
				
			||||||
                  >
 | 
					 | 
				
			||||||
                    {years.map(option => (
 | 
					 | 
				
			||||||
                      <option key={option} value={option}>
 | 
					 | 
				
			||||||
                        {option}
 | 
					 | 
				
			||||||
                      </option>
 | 
					 | 
				
			||||||
                    ))}
 | 
					 | 
				
			||||||
                  </HeaderSelect>
 | 
					 | 
				
			||||||
                </HeaderSelectLabel>
 | 
					 | 
				
			||||||
                <HeaderSelectLabel>
 | 
					 | 
				
			||||||
                  {date.getFullYear()}
 | 
					 | 
				
			||||||
                  <HeaderSelect
 | 
					 | 
				
			||||||
                    value={months[getMonth(date)]}
 | 
					 | 
				
			||||||
                    onChange={({ target: { value } }) => changeMonth(months.indexOf(value))}
 | 
					 | 
				
			||||||
                  >
 | 
					 | 
				
			||||||
                    {months.map(option => (
 | 
					 | 
				
			||||||
                      <option key={option} value={option}>
 | 
					 | 
				
			||||||
                        {option}
 | 
					 | 
				
			||||||
                      </option>
 | 
					 | 
				
			||||||
                    ))}
 | 
					 | 
				
			||||||
                  </HeaderSelect>
 | 
					 | 
				
			||||||
                </HeaderSelectLabel>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <HeaderButton onClick={increaseMonth} disabled={nextMonthButtonDisabled}>
 | 
					 | 
				
			||||||
                  Next
 | 
					 | 
				
			||||||
                </HeaderButton>
 | 
					 | 
				
			||||||
              </HeaderActions>
 | 
					 | 
				
			||||||
            )}
 | 
					 | 
				
			||||||
            selected={startDate}
 | 
					            selected={startDate}
 | 
				
			||||||
            inline
 | 
					            isClearable
 | 
				
			||||||
            onChange={date => {
 | 
					            onChange={date => setStartDate(date)}
 | 
				
			||||||
              if (date) {
 | 
					            popperClassName="picker-hidden"
 | 
				
			||||||
                setStartDate(date);
 | 
					            dateFormat="yyyy-MM-dd"
 | 
				
			||||||
              }
 | 
					            placeholderText="Select from date"
 | 
				
			||||||
            }}
 | 
					 | 
				
			||||||
          />
 | 
					          />
 | 
				
			||||||
        </DueDatePickerWrapper>
 | 
					        ) : (
 | 
				
			||||||
        <ActionWrapper>
 | 
					          <AddDateRange>Add date range</AddDateRange>
 | 
				
			||||||
          <ConfirmAddDueDate type="submit" onClick={NOOP}>
 | 
					        )}
 | 
				
			||||||
            Save
 | 
					      </DateRangeInputs>
 | 
				
			||||||
          </ConfirmAddDueDate>
 | 
					      <DatePicker
 | 
				
			||||||
          <RemoveDueDate
 | 
					        selected={startDate}
 | 
				
			||||||
            variant="outline"
 | 
					        onChange={date => setStartDate(date)}
 | 
				
			||||||
            color="danger"
 | 
					        startDate={startDate}
 | 
				
			||||||
 | 
					        useWeekdaysShort
 | 
				
			||||||
 | 
					        renderCustomHeader={({
 | 
				
			||||||
 | 
					          date,
 | 
				
			||||||
 | 
					          changeYear,
 | 
				
			||||||
 | 
					          changeMonth,
 | 
				
			||||||
 | 
					          decreaseMonth,
 | 
				
			||||||
 | 
					          increaseMonth,
 | 
				
			||||||
 | 
					          prevMonthButtonDisabled,
 | 
				
			||||||
 | 
					          nextMonthButtonDisabled,
 | 
				
			||||||
 | 
					        }) => (
 | 
				
			||||||
 | 
					          <HeaderActions>
 | 
				
			||||||
 | 
					            <HeaderButton onClick={decreaseMonth} disabled={prevMonthButtonDisabled}>
 | 
				
			||||||
 | 
					              Prev
 | 
				
			||||||
 | 
					            </HeaderButton>
 | 
				
			||||||
 | 
					            <HeaderSelectLabel>
 | 
				
			||||||
 | 
					              {months[date.getMonth()]}
 | 
				
			||||||
 | 
					              <HeaderSelect
 | 
				
			||||||
 | 
					                value={months[getMonth(date)]}
 | 
				
			||||||
 | 
					                onChange={({ target: { value } }) => changeMonth(months.indexOf(value))}
 | 
				
			||||||
 | 
					              >
 | 
				
			||||||
 | 
					                {months.map(option => (
 | 
				
			||||||
 | 
					                  <option key={option} value={option}>
 | 
				
			||||||
 | 
					                    {option}
 | 
				
			||||||
 | 
					                  </option>
 | 
				
			||||||
 | 
					                ))}
 | 
				
			||||||
 | 
					              </HeaderSelect>
 | 
				
			||||||
 | 
					            </HeaderSelectLabel>
 | 
				
			||||||
 | 
					            <HeaderSelectLabel>
 | 
				
			||||||
 | 
					              {date.getFullYear()}
 | 
				
			||||||
 | 
					              <HeaderSelect value={getYear(date)} onChange={({ target: { value } }) => changeYear(parseInt(value, 10))}>
 | 
				
			||||||
 | 
					                {years.map(option => (
 | 
				
			||||||
 | 
					                  <option key={option} value={option}>
 | 
				
			||||||
 | 
					                    {option}
 | 
				
			||||||
 | 
					                  </option>
 | 
				
			||||||
 | 
					                ))}
 | 
				
			||||||
 | 
					              </HeaderSelect>
 | 
				
			||||||
 | 
					            </HeaderSelectLabel>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <HeaderButton onClick={increaseMonth} disabled={nextMonthButtonDisabled}>
 | 
				
			||||||
 | 
					              Next
 | 
				
			||||||
 | 
					            </HeaderButton>
 | 
				
			||||||
 | 
					          </HeaderActions>
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
 | 
					        inline
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					      <ActionsSeparator />
 | 
				
			||||||
 | 
					      {hasTime && (
 | 
				
			||||||
 | 
					        <ActionsWrapper>
 | 
				
			||||||
 | 
					          <ActionClock width={16} height={16} />
 | 
				
			||||||
 | 
					          <ActionLabel>Due Time</ActionLabel>
 | 
				
			||||||
 | 
					          <DatePicker
 | 
				
			||||||
 | 
					            selected={startDate}
 | 
				
			||||||
 | 
					            onChange={date => {
 | 
				
			||||||
 | 
					              setStartDate(date);
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
 | 
					            showTimeSelect
 | 
				
			||||||
 | 
					            showTimeSelectOnly
 | 
				
			||||||
 | 
					            timeIntervals={15}
 | 
				
			||||||
 | 
					            timeCaption="Time"
 | 
				
			||||||
 | 
					            dateFormat="h:mm aa"
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					          <ActionIcon onClick={() => enableTime(false)}>
 | 
				
			||||||
 | 
					            <Cross width={16} height={16} />
 | 
				
			||||||
 | 
					          </ActionIcon>
 | 
				
			||||||
 | 
					        </ActionsWrapper>
 | 
				
			||||||
 | 
					      )}
 | 
				
			||||||
 | 
					      <ActionsWrapper>
 | 
				
			||||||
 | 
					        {!hasTime && (
 | 
				
			||||||
 | 
					          <ActionIcon
 | 
				
			||||||
            onClick={() => {
 | 
					            onClick={() => {
 | 
				
			||||||
              onRemoveDueDate(task);
 | 
					              if (startDate === null) {
 | 
				
			||||||
 | 
					                const today = new Date();
 | 
				
			||||||
 | 
					                today.setHours(12, 30, 0);
 | 
				
			||||||
 | 
					                setStartDate(today);
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					              enableTime(true);
 | 
				
			||||||
            }}
 | 
					            }}
 | 
				
			||||||
          >
 | 
					          >
 | 
				
			||||||
            Remove
 | 
					            <Clock width={16} height={16} />
 | 
				
			||||||
          </RemoveDueDate>
 | 
					          </ActionIcon>
 | 
				
			||||||
        </ActionWrapper>
 | 
					        )}
 | 
				
			||||||
      </Form>
 | 
					        <ClearButton onClick={() => setStartDate(null)}>{hasTime ? 'Clear all' : 'Clear'}</ClearButton>
 | 
				
			||||||
 | 
					      </ActionsWrapper>
 | 
				
			||||||
    </Wrapper>
 | 
					    </Wrapper>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -341,7 +341,9 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
 | 
				
			|||||||
              }}
 | 
					              }}
 | 
				
			||||||
            >
 | 
					            >
 | 
				
			||||||
              {task.dueDate ? (
 | 
					              {task.dueDate ? (
 | 
				
			||||||
                <SidebarButtonText>{dayjs(task.dueDate).format('MMM D [at] h:mm A')}</SidebarButtonText>
 | 
					                <SidebarButtonText>
 | 
				
			||||||
 | 
					                  {dayjs(task.dueDate).format(task.hasTime ? 'MMM D [at] h:mm A' : 'MMMM D')}
 | 
				
			||||||
 | 
					                </SidebarButtonText>
 | 
				
			||||||
              ) : (
 | 
					              ) : (
 | 
				
			||||||
                <SidebarButtonText>No due date</SidebarButtonText>
 | 
					                <SidebarButtonText>No due date</SidebarButtonText>
 | 
				
			||||||
              )}
 | 
					              )}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -215,6 +215,7 @@ export type Task = {
 | 
				
			|||||||
  position: Scalars['Float'];
 | 
					  position: Scalars['Float'];
 | 
				
			||||||
  description?: Maybe<Scalars['String']>;
 | 
					  description?: Maybe<Scalars['String']>;
 | 
				
			||||||
  dueDate?: Maybe<Scalars['Time']>;
 | 
					  dueDate?: Maybe<Scalars['Time']>;
 | 
				
			||||||
 | 
					  hasTime: Scalars['Boolean'];
 | 
				
			||||||
  complete: Scalars['Boolean'];
 | 
					  complete: Scalars['Boolean'];
 | 
				
			||||||
  completedAt?: Maybe<Scalars['Time']>;
 | 
					  completedAt?: Maybe<Scalars['Time']>;
 | 
				
			||||||
  assigned: Array<Member>;
 | 
					  assigned: Array<Member>;
 | 
				
			||||||
@@ -883,6 +884,7 @@ export type UpdateTaskLocationPayload = {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export type UpdateTaskDueDate = {
 | 
					export type UpdateTaskDueDate = {
 | 
				
			||||||
  taskID: Scalars['UUID'];
 | 
					  taskID: Scalars['UUID'];
 | 
				
			||||||
 | 
					  hasTime: Scalars['Boolean'];
 | 
				
			||||||
  dueDate?: Maybe<Scalars['Time']>;
 | 
					  dueDate?: Maybe<Scalars['Time']>;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1451,7 +1453,7 @@ export type FindTaskQuery = (
 | 
				
			|||||||
  { __typename?: 'Query' }
 | 
					  { __typename?: 'Query' }
 | 
				
			||||||
  & { findTask: (
 | 
					  & { findTask: (
 | 
				
			||||||
    { __typename?: 'Task' }
 | 
					    { __typename?: 'Task' }
 | 
				
			||||||
    & Pick<Task, 'id' | 'name' | 'description' | 'dueDate' | 'position' | 'complete'>
 | 
					    & Pick<Task, 'id' | 'name' | 'description' | 'dueDate' | 'position' | 'complete' | 'hasTime'>
 | 
				
			||||||
    & { taskGroup: (
 | 
					    & { taskGroup: (
 | 
				
			||||||
      { __typename?: 'TaskGroup' }
 | 
					      { __typename?: 'TaskGroup' }
 | 
				
			||||||
      & Pick<TaskGroup, 'id' | 'name'>
 | 
					      & Pick<TaskGroup, 'id' | 'name'>
 | 
				
			||||||
@@ -2314,6 +2316,7 @@ export type UpdateTaskDescriptionMutation = (
 | 
				
			|||||||
export type UpdateTaskDueDateMutationVariables = Exact<{
 | 
					export type UpdateTaskDueDateMutationVariables = Exact<{
 | 
				
			||||||
  taskID: Scalars['UUID'];
 | 
					  taskID: Scalars['UUID'];
 | 
				
			||||||
  dueDate?: Maybe<Scalars['Time']>;
 | 
					  dueDate?: Maybe<Scalars['Time']>;
 | 
				
			||||||
 | 
					  hasTime: Scalars['Boolean'];
 | 
				
			||||||
}>;
 | 
					}>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -2321,7 +2324,7 @@ export type UpdateTaskDueDateMutation = (
 | 
				
			|||||||
  { __typename?: 'Mutation' }
 | 
					  { __typename?: 'Mutation' }
 | 
				
			||||||
  & { updateTaskDueDate: (
 | 
					  & { updateTaskDueDate: (
 | 
				
			||||||
    { __typename?: 'Task' }
 | 
					    { __typename?: 'Task' }
 | 
				
			||||||
    & Pick<Task, 'id' | 'dueDate'>
 | 
					    & Pick<Task, 'id' | 'dueDate' | 'hasTime'>
 | 
				
			||||||
  ) }
 | 
					  ) }
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -3017,6 +3020,7 @@ export const FindTaskDocument = gql`
 | 
				
			|||||||
    dueDate
 | 
					    dueDate
 | 
				
			||||||
    position
 | 
					    position
 | 
				
			||||||
    complete
 | 
					    complete
 | 
				
			||||||
 | 
					    hasTime
 | 
				
			||||||
    taskGroup {
 | 
					    taskGroup {
 | 
				
			||||||
      id
 | 
					      id
 | 
				
			||||||
      name
 | 
					      name
 | 
				
			||||||
@@ -4692,10 +4696,13 @@ export type UpdateTaskDescriptionMutationHookResult = ReturnType<typeof useUpdat
 | 
				
			|||||||
export type UpdateTaskDescriptionMutationResult = ApolloReactCommon.MutationResult<UpdateTaskDescriptionMutation>;
 | 
					export type UpdateTaskDescriptionMutationResult = ApolloReactCommon.MutationResult<UpdateTaskDescriptionMutation>;
 | 
				
			||||||
export type UpdateTaskDescriptionMutationOptions = ApolloReactCommon.BaseMutationOptions<UpdateTaskDescriptionMutation, UpdateTaskDescriptionMutationVariables>;
 | 
					export type UpdateTaskDescriptionMutationOptions = ApolloReactCommon.BaseMutationOptions<UpdateTaskDescriptionMutation, UpdateTaskDescriptionMutationVariables>;
 | 
				
			||||||
export const UpdateTaskDueDateDocument = gql`
 | 
					export const UpdateTaskDueDateDocument = gql`
 | 
				
			||||||
    mutation updateTaskDueDate($taskID: UUID!, $dueDate: Time) {
 | 
					    mutation updateTaskDueDate($taskID: UUID!, $dueDate: Time, $hasTime: Boolean!) {
 | 
				
			||||||
  updateTaskDueDate(input: {taskID: $taskID, dueDate: $dueDate}) {
 | 
					  updateTaskDueDate(
 | 
				
			||||||
 | 
					    input: {taskID: $taskID, dueDate: $dueDate, hasTime: $hasTime}
 | 
				
			||||||
 | 
					  ) {
 | 
				
			||||||
    id
 | 
					    id
 | 
				
			||||||
    dueDate
 | 
					    dueDate
 | 
				
			||||||
 | 
					    hasTime
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
    `;
 | 
					    `;
 | 
				
			||||||
@@ -4716,6 +4723,7 @@ export type UpdateTaskDueDateMutationFn = ApolloReactCommon.MutationFunction<Upd
 | 
				
			|||||||
 *   variables: {
 | 
					 *   variables: {
 | 
				
			||||||
 *      taskID: // value for 'taskID'
 | 
					 *      taskID: // value for 'taskID'
 | 
				
			||||||
 *      dueDate: // value for 'dueDate'
 | 
					 *      dueDate: // value for 'dueDate'
 | 
				
			||||||
 | 
					 *      hasTime: // value for 'hasTime'
 | 
				
			||||||
 *   },
 | 
					 *   },
 | 
				
			||||||
 * });
 | 
					 * });
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,7 @@ query findTask($taskID: UUID!) {
 | 
				
			|||||||
    dueDate
 | 
					    dueDate
 | 
				
			||||||
    position
 | 
					    position
 | 
				
			||||||
    complete
 | 
					    complete
 | 
				
			||||||
 | 
					    hasTime
 | 
				
			||||||
    taskGroup {
 | 
					    taskGroup {
 | 
				
			||||||
      id
 | 
					      id
 | 
				
			||||||
      name
 | 
					      name
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,11 +1,13 @@
 | 
				
			|||||||
mutation updateTaskDueDate($taskID: UUID!, $dueDate: Time) {
 | 
					mutation updateTaskDueDate($taskID: UUID!, $dueDate: Time, $hasTime: Boolean!) {
 | 
				
			||||||
  updateTaskDueDate (
 | 
					  updateTaskDueDate (
 | 
				
			||||||
    input: {
 | 
					    input: {
 | 
				
			||||||
      taskID: $taskID
 | 
					      taskID: $taskID
 | 
				
			||||||
      dueDate: $dueDate
 | 
					      dueDate: $dueDate
 | 
				
			||||||
 | 
					      hasTime: $hasTime
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
    id
 | 
					    id
 | 
				
			||||||
    dueDate
 | 
					    dueDate
 | 
				
			||||||
 | 
					    hasTime
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1
									
								
								frontend/src/types.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								frontend/src/types.d.ts
									
									
									
									
										vendored
									
									
								
							@@ -102,6 +102,7 @@ type Task = {
 | 
				
			|||||||
  name: string;
 | 
					  name: string;
 | 
				
			||||||
  badges?: TaskBadges;
 | 
					  badges?: TaskBadges;
 | 
				
			||||||
  position: number;
 | 
					  position: number;
 | 
				
			||||||
 | 
					  hasTime?: boolean;
 | 
				
			||||||
  dueDate?: string;
 | 
					  dueDate?: string;
 | 
				
			||||||
  complete?: boolean;
 | 
					  complete?: boolean;
 | 
				
			||||||
  completedAt?: string | null;
 | 
					  completedAt?: string | null;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -102,6 +102,7 @@ type Task struct {
 | 
				
			|||||||
	DueDate     sql.NullTime   `json:"due_date"`
 | 
						DueDate     sql.NullTime   `json:"due_date"`
 | 
				
			||||||
	Complete    bool           `json:"complete"`
 | 
						Complete    bool           `json:"complete"`
 | 
				
			||||||
	CompletedAt sql.NullTime   `json:"completed_at"`
 | 
						CompletedAt sql.NullTime   `json:"completed_at"`
 | 
				
			||||||
 | 
						HasTime     bool           `json:"has_time"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type TaskActivity struct {
 | 
					type TaskActivity struct {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,7 +34,7 @@ UPDATE task SET name = $2 WHERE task_id = $1 RETURNING *;
 | 
				
			|||||||
DELETE FROM task where task_group_id = $1;
 | 
					DELETE FROM task where task_group_id = $1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- name: UpdateTaskDueDate :one
 | 
					-- name: UpdateTaskDueDate :one
 | 
				
			||||||
UPDATE task SET due_date = $2 WHERE task_id = $1 RETURNING *;
 | 
					UPDATE task SET due_date = $2, has_time = $3 WHERE task_id = $1 RETURNING *;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- name: SetTaskComplete :one
 | 
					-- name: SetTaskComplete :one
 | 
				
			||||||
UPDATE task SET complete = $2, completed_at = $3 WHERE task_id = $1 RETURNING *;
 | 
					UPDATE task SET complete = $2, completed_at = $3 WHERE task_id = $1 RETURNING *;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,7 +13,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const createTask = `-- name: CreateTask :one
 | 
					const createTask = `-- name: CreateTask :one
 | 
				
			||||||
INSERT INTO task (task_group_id, created_at, name, position)
 | 
					INSERT INTO task (task_group_id, created_at, name, position)
 | 
				
			||||||
  VALUES($1, $2, $3, $4) RETURNING task_id, task_group_id, created_at, name, position, description, due_date, complete, completed_at
 | 
					  VALUES($1, $2, $3, $4) RETURNING task_id, task_group_id, created_at, name, position, description, due_date, complete, completed_at, has_time
 | 
				
			||||||
`
 | 
					`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type CreateTaskParams struct {
 | 
					type CreateTaskParams struct {
 | 
				
			||||||
@@ -41,13 +41,14 @@ func (q *Queries) CreateTask(ctx context.Context, arg CreateTaskParams) (Task, e
 | 
				
			|||||||
		&i.DueDate,
 | 
							&i.DueDate,
 | 
				
			||||||
		&i.Complete,
 | 
							&i.Complete,
 | 
				
			||||||
		&i.CompletedAt,
 | 
							&i.CompletedAt,
 | 
				
			||||||
 | 
							&i.HasTime,
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
	return i, err
 | 
						return i, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const createTaskAll = `-- name: CreateTaskAll :one
 | 
					const createTaskAll = `-- name: CreateTaskAll :one
 | 
				
			||||||
INSERT INTO task (task_group_id, created_at, name, position, description, complete, due_date)
 | 
					INSERT INTO task (task_group_id, created_at, name, position, description, complete, due_date)
 | 
				
			||||||
  VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING task_id, task_group_id, created_at, name, position, description, due_date, complete, completed_at
 | 
					  VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING task_id, task_group_id, created_at, name, position, description, due_date, complete, completed_at, has_time
 | 
				
			||||||
`
 | 
					`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type CreateTaskAllParams struct {
 | 
					type CreateTaskAllParams struct {
 | 
				
			||||||
@@ -81,6 +82,7 @@ func (q *Queries) CreateTaskAll(ctx context.Context, arg CreateTaskAllParams) (T
 | 
				
			|||||||
		&i.DueDate,
 | 
							&i.DueDate,
 | 
				
			||||||
		&i.Complete,
 | 
							&i.Complete,
 | 
				
			||||||
		&i.CompletedAt,
 | 
							&i.CompletedAt,
 | 
				
			||||||
 | 
							&i.HasTime,
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
	return i, err
 | 
						return i, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -158,7 +160,7 @@ func (q *Queries) DeleteTasksByTaskGroupID(ctx context.Context, taskGroupID uuid
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const getAllTasks = `-- name: GetAllTasks :many
 | 
					const getAllTasks = `-- name: GetAllTasks :many
 | 
				
			||||||
SELECT task_id, task_group_id, created_at, name, position, description, due_date, complete, completed_at FROM task
 | 
					SELECT task_id, task_group_id, created_at, name, position, description, due_date, complete, completed_at, has_time FROM task
 | 
				
			||||||
`
 | 
					`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (q *Queries) GetAllTasks(ctx context.Context) ([]Task, error) {
 | 
					func (q *Queries) GetAllTasks(ctx context.Context) ([]Task, error) {
 | 
				
			||||||
@@ -180,6 +182,7 @@ func (q *Queries) GetAllTasks(ctx context.Context) ([]Task, error) {
 | 
				
			|||||||
			&i.DueDate,
 | 
								&i.DueDate,
 | 
				
			||||||
			&i.Complete,
 | 
								&i.Complete,
 | 
				
			||||||
			&i.CompletedAt,
 | 
								&i.CompletedAt,
 | 
				
			||||||
 | 
								&i.HasTime,
 | 
				
			||||||
		); err != nil {
 | 
							); err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -243,7 +246,7 @@ func (q *Queries) GetProjectIDForTask(ctx context.Context, taskID uuid.UUID) (uu
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const getTaskByID = `-- name: GetTaskByID :one
 | 
					const getTaskByID = `-- name: GetTaskByID :one
 | 
				
			||||||
SELECT task_id, task_group_id, created_at, name, position, description, due_date, complete, completed_at FROM task WHERE task_id = $1
 | 
					SELECT task_id, task_group_id, created_at, name, position, description, due_date, complete, completed_at, has_time FROM task WHERE task_id = $1
 | 
				
			||||||
`
 | 
					`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (q *Queries) GetTaskByID(ctx context.Context, taskID uuid.UUID) (Task, error) {
 | 
					func (q *Queries) GetTaskByID(ctx context.Context, taskID uuid.UUID) (Task, error) {
 | 
				
			||||||
@@ -259,12 +262,13 @@ func (q *Queries) GetTaskByID(ctx context.Context, taskID uuid.UUID) (Task, erro
 | 
				
			|||||||
		&i.DueDate,
 | 
							&i.DueDate,
 | 
				
			||||||
		&i.Complete,
 | 
							&i.Complete,
 | 
				
			||||||
		&i.CompletedAt,
 | 
							&i.CompletedAt,
 | 
				
			||||||
 | 
							&i.HasTime,
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
	return i, err
 | 
						return i, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const getTasksForTaskGroupID = `-- name: GetTasksForTaskGroupID :many
 | 
					const getTasksForTaskGroupID = `-- name: GetTasksForTaskGroupID :many
 | 
				
			||||||
SELECT task_id, task_group_id, created_at, name, position, description, due_date, complete, completed_at FROM task WHERE task_group_id = $1
 | 
					SELECT task_id, task_group_id, created_at, name, position, description, due_date, complete, completed_at, has_time FROM task WHERE task_group_id = $1
 | 
				
			||||||
`
 | 
					`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (q *Queries) GetTasksForTaskGroupID(ctx context.Context, taskGroupID uuid.UUID) ([]Task, error) {
 | 
					func (q *Queries) GetTasksForTaskGroupID(ctx context.Context, taskGroupID uuid.UUID) ([]Task, error) {
 | 
				
			||||||
@@ -286,6 +290,7 @@ func (q *Queries) GetTasksForTaskGroupID(ctx context.Context, taskGroupID uuid.U
 | 
				
			|||||||
			&i.DueDate,
 | 
								&i.DueDate,
 | 
				
			||||||
			&i.Complete,
 | 
								&i.Complete,
 | 
				
			||||||
			&i.CompletedAt,
 | 
								&i.CompletedAt,
 | 
				
			||||||
 | 
								&i.HasTime,
 | 
				
			||||||
		); err != nil {
 | 
							); err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -301,7 +306,7 @@ func (q *Queries) GetTasksForTaskGroupID(ctx context.Context, taskGroupID uuid.U
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const setTaskComplete = `-- name: SetTaskComplete :one
 | 
					const setTaskComplete = `-- name: SetTaskComplete :one
 | 
				
			||||||
UPDATE task SET complete = $2, completed_at = $3 WHERE task_id = $1 RETURNING task_id, task_group_id, created_at, name, position, description, due_date, complete, completed_at
 | 
					UPDATE task SET complete = $2, completed_at = $3 WHERE task_id = $1 RETURNING task_id, task_group_id, created_at, name, position, description, due_date, complete, completed_at, has_time
 | 
				
			||||||
`
 | 
					`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type SetTaskCompleteParams struct {
 | 
					type SetTaskCompleteParams struct {
 | 
				
			||||||
@@ -323,12 +328,13 @@ func (q *Queries) SetTaskComplete(ctx context.Context, arg SetTaskCompleteParams
 | 
				
			|||||||
		&i.DueDate,
 | 
							&i.DueDate,
 | 
				
			||||||
		&i.Complete,
 | 
							&i.Complete,
 | 
				
			||||||
		&i.CompletedAt,
 | 
							&i.CompletedAt,
 | 
				
			||||||
 | 
							&i.HasTime,
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
	return i, err
 | 
						return i, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const updateTaskComment = `-- name: UpdateTaskComment :one
 | 
					const updateTaskComment = `-- name: UpdateTaskComment :one
 | 
				
			||||||
UPDATE task_comment SET message = $2, updated_at = $3 WHERE task_comment_id = $1 RETURNING task_comment_id, task_id, created_at, updated_at, created_by, pinned, message
 | 
					UPDATE task_comment SET message = $2, updated_at = COALESCE($3, updated_at) WHERE task_comment_id = $1 RETURNING task_comment_id, task_id, created_at, updated_at, created_by, pinned, message
 | 
				
			||||||
`
 | 
					`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type UpdateTaskCommentParams struct {
 | 
					type UpdateTaskCommentParams struct {
 | 
				
			||||||
@@ -353,7 +359,7 @@ func (q *Queries) UpdateTaskComment(ctx context.Context, arg UpdateTaskCommentPa
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const updateTaskDescription = `-- name: UpdateTaskDescription :one
 | 
					const updateTaskDescription = `-- name: UpdateTaskDescription :one
 | 
				
			||||||
UPDATE task SET description = $2 WHERE task_id = $1 RETURNING task_id, task_group_id, created_at, name, position, description, due_date, complete, completed_at
 | 
					UPDATE task SET description = $2 WHERE task_id = $1 RETURNING task_id, task_group_id, created_at, name, position, description, due_date, complete, completed_at, has_time
 | 
				
			||||||
`
 | 
					`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type UpdateTaskDescriptionParams struct {
 | 
					type UpdateTaskDescriptionParams struct {
 | 
				
			||||||
@@ -374,21 +380,23 @@ func (q *Queries) UpdateTaskDescription(ctx context.Context, arg UpdateTaskDescr
 | 
				
			|||||||
		&i.DueDate,
 | 
							&i.DueDate,
 | 
				
			||||||
		&i.Complete,
 | 
							&i.Complete,
 | 
				
			||||||
		&i.CompletedAt,
 | 
							&i.CompletedAt,
 | 
				
			||||||
 | 
							&i.HasTime,
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
	return i, err
 | 
						return i, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const updateTaskDueDate = `-- name: UpdateTaskDueDate :one
 | 
					const updateTaskDueDate = `-- name: UpdateTaskDueDate :one
 | 
				
			||||||
UPDATE task SET due_date = $2 WHERE task_id = $1 RETURNING task_id, task_group_id, created_at, name, position, description, due_date, complete, completed_at
 | 
					UPDATE task SET due_date = $2, has_time = $3 WHERE task_id = $1 RETURNING task_id, task_group_id, created_at, name, position, description, due_date, complete, completed_at, has_time
 | 
				
			||||||
`
 | 
					`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type UpdateTaskDueDateParams struct {
 | 
					type UpdateTaskDueDateParams struct {
 | 
				
			||||||
	TaskID  uuid.UUID    `json:"task_id"`
 | 
						TaskID  uuid.UUID    `json:"task_id"`
 | 
				
			||||||
	DueDate sql.NullTime `json:"due_date"`
 | 
						DueDate sql.NullTime `json:"due_date"`
 | 
				
			||||||
 | 
						HasTime bool         `json:"has_time"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (q *Queries) UpdateTaskDueDate(ctx context.Context, arg UpdateTaskDueDateParams) (Task, error) {
 | 
					func (q *Queries) UpdateTaskDueDate(ctx context.Context, arg UpdateTaskDueDateParams) (Task, error) {
 | 
				
			||||||
	row := q.db.QueryRowContext(ctx, updateTaskDueDate, arg.TaskID, arg.DueDate)
 | 
						row := q.db.QueryRowContext(ctx, updateTaskDueDate, arg.TaskID, arg.DueDate, arg.HasTime)
 | 
				
			||||||
	var i Task
 | 
						var i Task
 | 
				
			||||||
	err := row.Scan(
 | 
						err := row.Scan(
 | 
				
			||||||
		&i.TaskID,
 | 
							&i.TaskID,
 | 
				
			||||||
@@ -400,12 +408,13 @@ func (q *Queries) UpdateTaskDueDate(ctx context.Context, arg UpdateTaskDueDatePa
 | 
				
			|||||||
		&i.DueDate,
 | 
							&i.DueDate,
 | 
				
			||||||
		&i.Complete,
 | 
							&i.Complete,
 | 
				
			||||||
		&i.CompletedAt,
 | 
							&i.CompletedAt,
 | 
				
			||||||
 | 
							&i.HasTime,
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
	return i, err
 | 
						return i, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const updateTaskLocation = `-- name: UpdateTaskLocation :one
 | 
					const updateTaskLocation = `-- name: UpdateTaskLocation :one
 | 
				
			||||||
UPDATE task SET task_group_id = $2, position = $3 WHERE task_id = $1 RETURNING task_id, task_group_id, created_at, name, position, description, due_date, complete, completed_at
 | 
					UPDATE task SET task_group_id = $2, position = $3 WHERE task_id = $1 RETURNING task_id, task_group_id, created_at, name, position, description, due_date, complete, completed_at, has_time
 | 
				
			||||||
`
 | 
					`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type UpdateTaskLocationParams struct {
 | 
					type UpdateTaskLocationParams struct {
 | 
				
			||||||
@@ -427,12 +436,13 @@ func (q *Queries) UpdateTaskLocation(ctx context.Context, arg UpdateTaskLocation
 | 
				
			|||||||
		&i.DueDate,
 | 
							&i.DueDate,
 | 
				
			||||||
		&i.Complete,
 | 
							&i.Complete,
 | 
				
			||||||
		&i.CompletedAt,
 | 
							&i.CompletedAt,
 | 
				
			||||||
 | 
							&i.HasTime,
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
	return i, err
 | 
						return i, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const updateTaskName = `-- name: UpdateTaskName :one
 | 
					const updateTaskName = `-- name: UpdateTaskName :one
 | 
				
			||||||
UPDATE task SET name = $2 WHERE task_id = $1 RETURNING task_id, task_group_id, created_at, name, position, description, due_date, complete, completed_at
 | 
					UPDATE task SET name = $2 WHERE task_id = $1 RETURNING task_id, task_group_id, created_at, name, position, description, due_date, complete, completed_at, has_time
 | 
				
			||||||
`
 | 
					`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type UpdateTaskNameParams struct {
 | 
					type UpdateTaskNameParams struct {
 | 
				
			||||||
@@ -453,12 +463,13 @@ func (q *Queries) UpdateTaskName(ctx context.Context, arg UpdateTaskNameParams)
 | 
				
			|||||||
		&i.DueDate,
 | 
							&i.DueDate,
 | 
				
			||||||
		&i.Complete,
 | 
							&i.Complete,
 | 
				
			||||||
		&i.CompletedAt,
 | 
							&i.CompletedAt,
 | 
				
			||||||
 | 
							&i.HasTime,
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
	return i, err
 | 
						return i, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const updateTaskPosition = `-- name: UpdateTaskPosition :one
 | 
					const updateTaskPosition = `-- name: UpdateTaskPosition :one
 | 
				
			||||||
UPDATE task SET position = $2 WHERE task_id = $1 RETURNING task_id, task_group_id, created_at, name, position, description, due_date, complete, completed_at
 | 
					UPDATE task SET position = $2 WHERE task_id = $1 RETURNING task_id, task_group_id, created_at, name, position, description, due_date, complete, completed_at, has_time
 | 
				
			||||||
`
 | 
					`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type UpdateTaskPositionParams struct {
 | 
					type UpdateTaskPositionParams struct {
 | 
				
			||||||
@@ -479,6 +490,7 @@ func (q *Queries) UpdateTaskPosition(ctx context.Context, arg UpdateTaskPosition
 | 
				
			|||||||
		&i.DueDate,
 | 
							&i.DueDate,
 | 
				
			||||||
		&i.Complete,
 | 
							&i.Complete,
 | 
				
			||||||
		&i.CompletedAt,
 | 
							&i.CompletedAt,
 | 
				
			||||||
 | 
							&i.HasTime,
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
	return i, err
 | 
						return i, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -383,6 +383,7 @@ type ComplexityRoot struct {
 | 
				
			|||||||
		CreatedAt   func(childComplexity int) int
 | 
							CreatedAt   func(childComplexity int) int
 | 
				
			||||||
		Description func(childComplexity int) int
 | 
							Description func(childComplexity int) int
 | 
				
			||||||
		DueDate     func(childComplexity int) int
 | 
							DueDate     func(childComplexity int) int
 | 
				
			||||||
 | 
							HasTime     func(childComplexity int) int
 | 
				
			||||||
		ID          func(childComplexity int) int
 | 
							ID          func(childComplexity int) int
 | 
				
			||||||
		Labels      func(childComplexity int) int
 | 
							Labels      func(childComplexity int) int
 | 
				
			||||||
		Name        func(childComplexity int) int
 | 
							Name        func(childComplexity int) int
 | 
				
			||||||
@@ -2376,6 +2377,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		return e.complexity.Task.DueDate(childComplexity), true
 | 
							return e.complexity.Task.DueDate(childComplexity), true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						case "Task.hasTime":
 | 
				
			||||||
 | 
							if e.complexity.Task.HasTime == nil {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return e.complexity.Task.HasTime(childComplexity), true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	case "Task.id":
 | 
						case "Task.id":
 | 
				
			||||||
		if e.complexity.Task.ID == nil {
 | 
							if e.complexity.Task.ID == nil {
 | 
				
			||||||
			break
 | 
								break
 | 
				
			||||||
@@ -3135,6 +3143,7 @@ type Task {
 | 
				
			|||||||
  position: Float!
 | 
					  position: Float!
 | 
				
			||||||
  description: String
 | 
					  description: String
 | 
				
			||||||
  dueDate: Time
 | 
					  dueDate: Time
 | 
				
			||||||
 | 
					  hasTime: Boolean!
 | 
				
			||||||
  complete: Boolean!
 | 
					  complete: Boolean!
 | 
				
			||||||
  completedAt: Time
 | 
					  completedAt: Time
 | 
				
			||||||
  assigned: [Member!]!
 | 
					  assigned: [Member!]!
 | 
				
			||||||
@@ -3477,6 +3486,7 @@ type UpdateTaskLocationPayload {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
input UpdateTaskDueDate  {
 | 
					input UpdateTaskDueDate  {
 | 
				
			||||||
  taskID: UUID!
 | 
					  taskID: UUID!
 | 
				
			||||||
 | 
					  hasTime: Boolean!
 | 
				
			||||||
  dueDate: Time
 | 
					  dueDate: Time
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -13558,6 +13568,40 @@ func (ec *executionContext) _Task_dueDate(ctx context.Context, field graphql.Col
 | 
				
			|||||||
	return ec.marshalOTime2ᚖtimeᚐTime(ctx, field.Selections, res)
 | 
						return ec.marshalOTime2ᚖtimeᚐTime(ctx, field.Selections, res)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ec *executionContext) _Task_hasTime(ctx context.Context, field graphql.CollectedField, obj *db.Task) (ret graphql.Marshaler) {
 | 
				
			||||||
 | 
						defer func() {
 | 
				
			||||||
 | 
							if r := recover(); r != nil {
 | 
				
			||||||
 | 
								ec.Error(ctx, ec.Recover(ctx, r))
 | 
				
			||||||
 | 
								ret = graphql.Null
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
						fc := &graphql.FieldContext{
 | 
				
			||||||
 | 
							Object:   "Task",
 | 
				
			||||||
 | 
							Field:    field,
 | 
				
			||||||
 | 
							Args:     nil,
 | 
				
			||||||
 | 
							IsMethod: false,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx = graphql.WithFieldContext(ctx, fc)
 | 
				
			||||||
 | 
						resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
 | 
				
			||||||
 | 
							ctx = rctx // use context from middleware stack in children
 | 
				
			||||||
 | 
							return obj.HasTime, nil
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ec.Error(ctx, err)
 | 
				
			||||||
 | 
							return graphql.Null
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if resTmp == nil {
 | 
				
			||||||
 | 
							if !graphql.HasFieldError(ctx, fc) {
 | 
				
			||||||
 | 
								ec.Errorf(ctx, "must not be null")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return graphql.Null
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						res := resTmp.(bool)
 | 
				
			||||||
 | 
						fc.Result = res
 | 
				
			||||||
 | 
						return ec.marshalNBoolean2bool(ctx, field.Selections, res)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (ec *executionContext) _Task_complete(ctx context.Context, field graphql.CollectedField, obj *db.Task) (ret graphql.Marshaler) {
 | 
					func (ec *executionContext) _Task_complete(ctx context.Context, field graphql.CollectedField, obj *db.Task) (ret graphql.Marshaler) {
 | 
				
			||||||
	defer func() {
 | 
						defer func() {
 | 
				
			||||||
		if r := recover(); r != nil {
 | 
							if r := recover(); r != nil {
 | 
				
			||||||
@@ -18596,6 +18640,12 @@ func (ec *executionContext) unmarshalInputUpdateTaskDueDate(ctx context.Context,
 | 
				
			|||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return it, err
 | 
									return it, err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
							case "hasTime":
 | 
				
			||||||
 | 
								var err error
 | 
				
			||||||
 | 
								it.HasTime, err = ec.unmarshalNBoolean2bool(ctx, v)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return it, err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		case "dueDate":
 | 
							case "dueDate":
 | 
				
			||||||
			var err error
 | 
								var err error
 | 
				
			||||||
			it.DueDate, err = ec.unmarshalOTime2ᚖtimeᚐTime(ctx, v)
 | 
								it.DueDate, err = ec.unmarshalOTime2ᚖtimeᚐTime(ctx, v)
 | 
				
			||||||
@@ -20968,6 +21018,11 @@ func (ec *executionContext) _Task(ctx context.Context, sel ast.SelectionSet, obj
 | 
				
			|||||||
				res = ec._Task_dueDate(ctx, field, obj)
 | 
									res = ec._Task_dueDate(ctx, field, obj)
 | 
				
			||||||
				return res
 | 
									return res
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
 | 
							case "hasTime":
 | 
				
			||||||
 | 
								out.Values[i] = ec._Task_hasTime(ctx, field, obj)
 | 
				
			||||||
 | 
								if out.Values[i] == graphql.Null {
 | 
				
			||||||
 | 
									atomic.AddUint32(&invalids, 1)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		case "complete":
 | 
							case "complete":
 | 
				
			||||||
			out.Values[i] = ec._Task_complete(ctx, field, obj)
 | 
								out.Values[i] = ec._Task_complete(ctx, field, obj)
 | 
				
			||||||
			if out.Values[i] == graphql.Null {
 | 
								if out.Values[i] == graphql.Null {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -519,6 +519,7 @@ type UpdateTaskDescriptionInput struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
type UpdateTaskDueDate struct {
 | 
					type UpdateTaskDueDate struct {
 | 
				
			||||||
	TaskID  uuid.UUID  `json:"taskID"`
 | 
						TaskID  uuid.UUID  `json:"taskID"`
 | 
				
			||||||
 | 
						HasTime bool       `json:"hasTime"`
 | 
				
			||||||
	DueDate *time.Time `json:"dueDate"`
 | 
						DueDate *time.Time `json:"dueDate"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -175,6 +175,7 @@ type Task {
 | 
				
			|||||||
  position: Float!
 | 
					  position: Float!
 | 
				
			||||||
  description: String
 | 
					  description: String
 | 
				
			||||||
  dueDate: Time
 | 
					  dueDate: Time
 | 
				
			||||||
 | 
					  hasTime: Boolean!
 | 
				
			||||||
  complete: Boolean!
 | 
					  complete: Boolean!
 | 
				
			||||||
  completedAt: Time
 | 
					  completedAt: Time
 | 
				
			||||||
  assigned: [Member!]!
 | 
					  assigned: [Member!]!
 | 
				
			||||||
@@ -517,6 +518,7 @@ type UpdateTaskLocationPayload {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
input UpdateTaskDueDate  {
 | 
					input UpdateTaskDueDate  {
 | 
				
			||||||
  taskID: UUID!
 | 
					  taskID: UUID!
 | 
				
			||||||
 | 
					  hasTime: Boolean!
 | 
				
			||||||
  dueDate: Time
 | 
					  dueDate: Time
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -943,3 +945,4 @@ type DeleteUserAccountPayload {
 | 
				
			|||||||
  ok: Boolean!
 | 
					  ok: Boolean!
 | 
				
			||||||
  userAccount: UserAccount!
 | 
					  userAccount: UserAccount!
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -423,28 +423,35 @@ func (r *mutationResolver) UpdateTaskDueDate(ctx context.Context, input UpdateTa
 | 
				
			|||||||
		activityType = TASK_DUE_DATE_CHANGED
 | 
							activityType = TASK_DUE_DATE_CHANGED
 | 
				
			||||||
		data["PrevDueDate"] = prevTask.DueDate.Time.String()
 | 
							data["PrevDueDate"] = prevTask.DueDate.Time.String()
 | 
				
			||||||
		data["CurDueDate"] = input.DueDate.String()
 | 
							data["CurDueDate"] = input.DueDate.String()
 | 
				
			||||||
	} else {
 | 
						} else if input.DueDate != nil {
 | 
				
			||||||
		data["DueDate"] = input.DueDate.String()
 | 
							data["DueDate"] = input.DueDate.String()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	var dueDate sql.NullTime
 | 
						var dueDate sql.NullTime
 | 
				
			||||||
 | 
						log.WithField("dueDate", input.DueDate).Info("before ptr!")
 | 
				
			||||||
	if input.DueDate == nil {
 | 
						if input.DueDate == nil {
 | 
				
			||||||
		dueDate = sql.NullTime{Valid: false, Time: time.Now()}
 | 
							dueDate = sql.NullTime{Valid: false, Time: time.Now()}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		dueDate = sql.NullTime{Valid: true, Time: *input.DueDate}
 | 
							dueDate = sql.NullTime{Valid: true, Time: *input.DueDate}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	task, err := r.Repository.UpdateTaskDueDate(ctx, db.UpdateTaskDueDateParams{
 | 
						var task db.Task
 | 
				
			||||||
		TaskID:  input.TaskID,
 | 
						if !(input.DueDate == nil && !prevTask.DueDate.Valid) {
 | 
				
			||||||
		DueDate: dueDate,
 | 
							task, err = r.Repository.UpdateTaskDueDate(ctx, db.UpdateTaskDueDateParams{
 | 
				
			||||||
	})
 | 
								TaskID:  input.TaskID,
 | 
				
			||||||
	createdAt := time.Now().UTC()
 | 
								DueDate: dueDate,
 | 
				
			||||||
	d, err := json.Marshal(data)
 | 
								HasTime: input.HasTime,
 | 
				
			||||||
	_, err = r.Repository.CreateTaskActivity(ctx, db.CreateTaskActivityParams{
 | 
							})
 | 
				
			||||||
		TaskID:         task.TaskID,
 | 
							createdAt := time.Now().UTC()
 | 
				
			||||||
		Data:           d,
 | 
							d, _ := json.Marshal(data)
 | 
				
			||||||
		CausedBy:       userID,
 | 
							_, err = r.Repository.CreateTaskActivity(ctx, db.CreateTaskActivityParams{
 | 
				
			||||||
		CreatedAt:      createdAt,
 | 
								TaskID:         task.TaskID,
 | 
				
			||||||
		ActivityTypeID: activityType,
 | 
								Data:           d,
 | 
				
			||||||
	})
 | 
								CausedBy:       userID,
 | 
				
			||||||
 | 
								CreatedAt:      createdAt,
 | 
				
			||||||
 | 
								ActivityTypeID: activityType,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							task, err = r.Repository.GetTaskByID(ctx, input.TaskID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return &task, err
 | 
						return &task, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -175,6 +175,7 @@ type Task {
 | 
				
			|||||||
  position: Float!
 | 
					  position: Float!
 | 
				
			||||||
  description: String
 | 
					  description: String
 | 
				
			||||||
  dueDate: Time
 | 
					  dueDate: Time
 | 
				
			||||||
 | 
					  hasTime: Boolean!
 | 
				
			||||||
  complete: Boolean!
 | 
					  complete: Boolean!
 | 
				
			||||||
  completedAt: Time
 | 
					  completedAt: Time
 | 
				
			||||||
  assigned: [Member!]!
 | 
					  assigned: [Member!]!
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -49,6 +49,7 @@ type UpdateTaskLocationPayload {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
input UpdateTaskDueDate  {
 | 
					input UpdateTaskDueDate  {
 | 
				
			||||||
  taskID: UUID!
 | 
					  taskID: UUID!
 | 
				
			||||||
 | 
					  hasTime: Boolean!
 | 
				
			||||||
  dueDate: Time
 | 
					  dueDate: Time
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								migrations/0063_add-use_time-to-task-due_date.up.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								migrations/0063_add-use_time-to-task-due_date.up.sql
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					ALTER TABLE task ADD COLUMN has_time boolean NOT NULL DEFAULT false;
 | 
				
			||||||
 | 
					UPDATE task SET has_time = true;
 | 
				
			||||||
		Reference in New Issue
	
	Block a user