From 37bd4d1349acf181be2bff23667401b20a4d863f Mon Sep 17 00:00:00 2001
From: Steven Correia <45617825+StevenJCorreia@users.noreply.github.com>
Date: Fri, 5 Sep 2025 07:55:20 -0400
Subject: [PATCH] feat: Restore toggleable due dates (#1332)
---
.../components/cards/Card/ProjectContent.jsx | 7 +++-
.../cards/CardModal/ProjectContent.jsx | 37 ++++++++++++++-----
.../CardModal/ProjectContent.module.scss | 6 +++
.../cards/DueDateChip/DueDateChip.jsx | 26 ++++++++++---
.../cards/DueDateChip/DueDateChip.module.scss | 5 +++
client/src/models/Card.js | 12 ++++++
server/api/controllers/cards/create.js | 5 +++
server/api/controllers/cards/update.js | 6 +++
.../api/helpers/boards/import-from-trello.js | 2 +-
server/api/helpers/cards/create-one.js | 8 ++++
server/api/helpers/cards/duplicate-one.js | 1 +
server/api/helpers/cards/update-one.js | 14 +++++++
server/api/models/Card.js | 5 +++
...0905101408_restore_toggleable_due_dates.js | 23 ++++++++++++
14 files changed, 140 insertions(+), 17 deletions(-)
create mode 100644 server/db/migrations/20250905101408_restore_toggleable_due_dates.js
diff --git a/client/src/components/cards/Card/ProjectContent.jsx b/client/src/components/cards/Card/ProjectContent.jsx
index 952acc86..267549f0 100644
--- a/client/src/components/cards/Card/ProjectContent.jsx
+++ b/client/src/components/cards/Card/ProjectContent.jsx
@@ -187,7 +187,12 @@ const ProjectContent = React.memo(({ cardId }) => {
)}
{card.dueDate && (
-
+
)}
{card.stopwatch && (
diff --git a/client/src/components/cards/CardModal/ProjectContent.jsx b/client/src/components/cards/CardModal/ProjectContent.jsx
index 1fd7024e..668a0a19 100644
--- a/client/src/components/cards/CardModal/ProjectContent.jsx
+++ b/client/src/components/cards/CardModal/ProjectContent.jsx
@@ -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',
})}
-
+
{canEditDueDate ? (
-
-
-
+ <>
+ {!card.isClosed && (
+
+ )}
+
+
+
+ >
) : (
)}
diff --git a/client/src/components/cards/CardModal/ProjectContent.module.scss b/client/src/components/cards/CardModal/ProjectContent.module.scss
index 63d442b9..8bdc3890 100644
--- a/client/src/components/cards/CardModal/ProjectContent.module.scss
+++ b/client/src/components/cards/CardModal/ProjectContent.module.scss
@@ -60,6 +60,12 @@
vertical-align: top;
}
+ .attachmentDueDate {
+ align-items: center;
+ display: flex;
+ gap: 4px;
+ }
+
.attachments {
display: inline-block;
margin: 0 8px 8px 0;
diff --git a/client/src/components/cards/DueDateChip/DueDateChip.jsx b/client/src/components/cards/DueDateChip/DueDateChip.jsx
index bff0aa3a..6d9ce31b 100644
--- a/client/src/components/cards/DueDateChip/DueDateChip.jsx
+++ b/client/src/components/cards/DueDateChip/DueDateChip.jsx
@@ -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 = (
{
+ await knex.schema.alterTable('card', (table) => {
+ /* Columns */
+
+ table.boolean('is_due_completed');
+ });
+
+ return knex('card')
+ .update({
+ isDueCompleted: false,
+ })
+ .whereNotNull('due_date');
+};
+
+exports.down = (knex) =>
+ knex.schema.table('card', (table) => {
+ table.dropColumn('is_due_completed');
+ });