feat: Restore toggleable due dates (#1332)

This commit is contained in:
Steven Correia
2025-09-05 13:55:20 +02:00
committed by GitHub
parent 7da241c5a3
commit 37bd4d1349
14 changed files with 140 additions and 17 deletions
@@ -187,7 +187,12 @@ const ProjectContent = React.memo(({ cardId }) => {
)}
{card.dueDate && (
<span className={classNames(styles.attachment, styles.attachmentLeft)}>
<DueDateChip value={card.dueDate} size="tiny" withStatus={!card.isClosed} />
<DueDateChip
value={card.dueDate}
size="tiny"
isCompleted={card.isDueCompleted}
withStatus={!card.isClosed}
/>
</span>
)}
{card.stopwatch && (
@@ -7,7 +7,7 @@ import React, { useCallback, useContext, useMemo, useState } from 'react';
import classNames from 'classnames';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import { useTranslation } from 'react-i18next';
import { Button, Grid, Icon } from 'semantic-ui-react';
import { Button, Checkbox, Grid, Icon } from 'semantic-ui-react';
import { useDidUpdate } from '../../../lib/hooks';
import selectors from '../../../selectors';
@@ -175,6 +175,14 @@ const ProjectContent = React.memo(() => {
[dispatch],
);
const handleDueCompletionChange = useCallback(() => {
dispatch(
entryActions.updateCurrentCard({
isDueCompleted: !card.isDueCompleted,
}),
);
}, [card.isDueCompleted, dispatch]);
const handleToggleStopwatchClick = useCallback(() => {
dispatch(
entryActions.updateCurrentCard({
@@ -410,19 +418,30 @@ const ProjectContent = React.memo(() => {
context: 'title',
})}
</div>
<span className={styles.attachment}>
<span className={classNames(styles.attachment, styles.attachmentDueDate)}>
{canEditDueDate ? (
<EditDueDatePopup cardId={card.id}>
<DueDateChip
withStatusIcon
value={card.dueDate}
withStatus={!card.isClosed}
/>
</EditDueDatePopup>
<>
{!card.isClosed && (
<Checkbox
checked={card.isDueCompleted}
disabled={!canEditDueDate}
onChange={handleDueCompletionChange}
/>
)}
<EditDueDatePopup cardId={card.id}>
<DueDateChip
withStatusIcon
value={card.dueDate}
isCompleted={card.isDueCompleted}
withStatus={!card.isClosed}
/>
</EditDueDatePopup>
</>
) : (
<DueDateChip
withStatusIcon
value={card.dueDate}
isCompleted={card.isDueCompleted}
withStatus={!card.isClosed}
/>
)}
@@ -60,6 +60,12 @@
vertical-align: top;
}
.attachmentDueDate {
align-items: center;
display: flex;
gap: 4px;
}
.attachments {
display: inline-block;
margin: 0 8px 8px 0;
@@ -24,6 +24,7 @@ const Sizes = {
const Statuses = {
DUE_SOON: 'dueSoon',
OVERDUE: 'overdue',
COMPLETED: 'completed',
};
const LONG_DATE_FORMAT_BY_SIZE = {
@@ -47,9 +48,17 @@ const STATUS_ICON_PROPS_BY_STATUS = {
name: 'hourglass end',
color: 'red',
},
[Statuses.COMPLETED]: {
name: 'checkmark',
color: 'green',
},
};
const getStatus = (date) => {
const getStatus = (date, isCompleted) => {
if (isCompleted) {
return Statuses.COMPLETED;
}
const secondsLeft = Math.floor((date.getTime() - new Date().getTime()) / 1000);
if (secondsLeft <= 0) {
@@ -64,12 +73,12 @@ const getStatus = (date) => {
};
const DueDateChip = React.memo(
({ value, size, isDisabled, withStatus, withStatusIcon, onClick }) => {
({ value, size, isCompleted, isDisabled, withStatus, withStatusIcon, onClick }) => {
const [t] = useTranslation();
const forceUpdate = useForceUpdate();
const statusRef = useRef(null);
statusRef.current = withStatus ? getStatus(value) : null;
statusRef.current = withStatus ? getStatus(value, isCompleted) : null;
const intervalRef = useRef(null);
@@ -80,9 +89,13 @@ const DueDateChip = React.memo(
);
useEffect(() => {
if (withStatus && statusRef.current !== Statuses.OVERDUE) {
if (
withStatus &&
statusRef.current !== Statuses.OVERDUE &&
statusRef.current !== Statuses.COMPLETED
) {
intervalRef.current = setInterval(() => {
const status = getStatus(value);
const status = getStatus(value, isCompleted);
if (status !== statusRef.current) {
forceUpdate();
@@ -99,7 +112,7 @@ const DueDateChip = React.memo(
clearInterval(intervalRef.current);
}
};
}, [value, withStatus, forceUpdate]);
}, [value, isCompleted, withStatus, forceUpdate]);
const contentNode = (
<span
@@ -134,6 +147,7 @@ const DueDateChip = React.memo(
DueDateChip.propTypes = {
value: PropTypes.instanceOf(Date).isRequired,
size: PropTypes.oneOf(Object.values(Sizes)),
isCompleted: PropTypes.bool.isRequired,
isDisabled: PropTypes.bool,
withStatus: PropTypes.bool.isRequired,
withStatusIcon: PropTypes.bool,
@@ -62,4 +62,9 @@
background: #db2828;
color: #fff;
}
.wrapperCompleted {
background: #21ba45;
color: #fff;
}
}