feat: drop react-datepicker and change due date labels to time ago
This commit is contained in:
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## 2026-07-20
|
||||
|
||||
### Changed
|
||||
- Labels in tasks due date to use the time ago format.
|
||||
|
||||
### Removed
|
||||
- React Date Picker dependency in favour of regular browser input date UI.
|
||||
|
||||
## app-v2026.07.01.140 - 2026-07-01
|
||||
|
||||
### Added
|
||||
|
||||
Generated
-169
@@ -20,7 +20,6 @@
|
||||
"react-bootstrap": "^2.10.10",
|
||||
"react-bootstrap-icons": "^1.11.6",
|
||||
"react-charts": "^3.0.0-beta.57",
|
||||
"react-datepicker": "^9.1.0",
|
||||
"react-dom": "^19.2.7",
|
||||
"react-i18next": "^17.0.8",
|
||||
"react-markdown": "^10.1.0",
|
||||
@@ -705,59 +704,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/core": {
|
||||
"version": "1.7.5",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz",
|
||||
"integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/utils": "^0.2.11"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/dom": {
|
||||
"version": "1.7.6",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz",
|
||||
"integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/core": "^1.7.5",
|
||||
"@floating-ui/utils": "^0.2.11"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/react": {
|
||||
"version": "0.27.19",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.27.19.tgz",
|
||||
"integrity": "sha512-31B8h5mm8YxotlE7/AU/PhNAl8eWxAmjL/v2QOxroDNkTFLk3Uu82u63N3b6TXa4EGJeeZLVcd/9AlNlVqzeog==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/react-dom": "^2.1.8",
|
||||
"@floating-ui/utils": "^0.2.11",
|
||||
"tabbable": "^6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=17.0.0",
|
||||
"react-dom": ">=17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/react-dom": {
|
||||
"version": "2.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.8.tgz",
|
||||
"integrity": "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.7.6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/utils": {
|
||||
"version": "0.2.11",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz",
|
||||
"integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@humanfs/core": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz",
|
||||
@@ -1108,9 +1054,6 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1131,9 +1074,6 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1154,9 +1094,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1177,9 +1114,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1200,9 +1134,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1223,9 +1154,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1476,9 +1404,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1495,9 +1420,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1514,9 +1436,6 @@
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1533,9 +1452,6 @@
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1552,9 +1468,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1571,9 +1484,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2569,9 +2479,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2586,9 +2493,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2603,9 +2507,6 @@
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2620,9 +2521,6 @@
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2637,9 +2535,6 @@
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2654,9 +2549,6 @@
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2671,9 +2563,6 @@
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2688,9 +2577,6 @@
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2705,9 +2591,6 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2722,9 +2605,6 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -4191,16 +4071,6 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/date-fns": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.4.0.tgz",
|
||||
"integrity": "sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/kossnocorp"
|
||||
}
|
||||
},
|
||||
"node_modules/dayjs": {
|
||||
"version": "1.11.21",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.21.tgz",
|
||||
@@ -7248,9 +7118,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -7271,9 +7138,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -7294,9 +7158,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -7317,9 +7178,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -9334,27 +9192,6 @@
|
||||
"@types/react": "^17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-datepicker": {
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-9.1.0.tgz",
|
||||
"integrity": "sha512-lOp+m5bc+ttgtB5MHEjwiVu4nlp4CvJLS/PG1OiOe5pmg9kV73pEqO8H0Geqvg2E8gjqTaL9eRhSe+ZpeKP3nA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/react": "^0.27.15",
|
||||
"clsx": "^2.1.1",
|
||||
"date-fns": "^4.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"date-fns-tz": "^3.0.0",
|
||||
"react": "^16.9.0 || ^17 || ^18 || ^19 || ^19.0.0-rc",
|
||||
"react-dom": "^16.9.0 || ^17 || ^18 || ^19 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"date-fns-tz": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "19.2.7",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz",
|
||||
@@ -10653,12 +10490,6 @@
|
||||
"url": "https://www.buymeacoffee.com/systeminfo"
|
||||
}
|
||||
},
|
||||
"node_modules/tabbable": {
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.5.0.tgz",
|
||||
"integrity": "sha512-wieBHXygIm7OyQOu5hQlkk62/WyCFYGlWg7L6/ZCUZwx0o398Zkn4pVmMyfYhfMG8kGrj/Krt8eIk6UKC6VzwA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tapable": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz",
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
"react-bootstrap": "^2.10.10",
|
||||
"react-bootstrap-icons": "^1.11.6",
|
||||
"react-charts": "^3.0.0-beta.57",
|
||||
"react-datepicker": "^9.1.0",
|
||||
"react-dom": "^19.2.7",
|
||||
"react-i18next": "^17.0.8",
|
||||
"react-markdown": "^10.1.0",
|
||||
|
||||
@@ -28,6 +28,8 @@ describe('Portuguese Utils unit tests', () => {
|
||||
expect(translateTimeMessage('1 month left', 'pt_br')).toBe('1 mês restante');
|
||||
expect(translateTimeMessage('2 days left', 'pt_br')).toBe('2 dias restantes');
|
||||
expect(translateTimeMessage('1 day left', 'pt_br')).toBe('1 dia restante');
|
||||
expect(translateTimeMessage('Due tomorrow', 'pt_br')).toBe('Vence amanhã');
|
||||
expect(translateTimeMessage('Due today', 'pt_br')).toBe('Vence hoje');
|
||||
expect(translateTimeMessage('lala', 'pt_br')).toBe('lala');
|
||||
expect(translateTimeMessage('null', 'pt_br')).toBe('null');
|
||||
});
|
||||
|
||||
@@ -49,6 +49,8 @@ describe('Russian Utils unit tests', () => {
|
||||
expect(translateTimeMessage('7 days left', 'ru')).toBe('осталось 7 дней');
|
||||
expect(translateTimeMessage('8 days left', 'ru')).toBe('осталось 8 дней');
|
||||
expect(translateTimeMessage('9 days left', 'ru')).toBe('осталось 9 дней');
|
||||
expect(translateTimeMessage('Due tomorrow', 'ru')).toBe('Срок завтра');
|
||||
expect(translateTimeMessage('Due today', 'ru')).toBe('Срок сегодня');
|
||||
expect(translateTimeMessage('lala', 'ru')).toBe('lala');
|
||||
expect(translateTimeMessage('null', 'ru')).toBe('null');
|
||||
});
|
||||
|
||||
@@ -28,6 +28,8 @@ describe('Spanish Utils unit tests', () => {
|
||||
expect(translateTimeMessage('1 month left', 'es')).toBe('Falta 1 mes');
|
||||
expect(translateTimeMessage('2 days left', 'es')).toBe('Faltan 2 días');
|
||||
expect(translateTimeMessage('1 day left', 'es')).toBe('Falta 1 día');
|
||||
expect(translateTimeMessage('Due tomorrow', 'es')).toBe('Vence mañana');
|
||||
expect(translateTimeMessage('Due today', 'es')).toBe('Vence hoy');
|
||||
expect(translateTimeMessage('lala', 'es')).toBe('lala');
|
||||
expect(translateTimeMessage('null', 'es')).toBe('null');
|
||||
});
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
@import '../../styles/theme.scss';
|
||||
|
||||
.form-control[type="date"] {
|
||||
font-size: 16px; /* Prevents iOS zoom on focus */
|
||||
border-left: none;
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
@import '../../styles/theme.scss';
|
||||
|
||||
/* custom-datepicker.scss */
|
||||
.react-datepicker-wrapper,
|
||||
.react-datepicker__input-container {
|
||||
flex: 1 1 auto !important;
|
||||
width: 1% !important;
|
||||
display: flex !important;
|
||||
}
|
||||
|
||||
.react-datepicker__input-container {
|
||||
/* Ensure it takes full width of the flex item */
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
.react-datepicker__input-container input {
|
||||
font-size: 16px; /* Prevents iOS zoom on focus */
|
||||
width: 100%;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--bs-border-color);
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.react-datepicker-popper {
|
||||
z-index: 9999 !important;
|
||||
}
|
||||
|
||||
.react-datepicker__day {
|
||||
margin: 0.2rem;
|
||||
padding: 0.5rem;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* Larger touch targets on mobile */
|
||||
@media (max-width: 768px) {
|
||||
.react-datepicker__day,
|
||||
.react-datepicker__month-text,
|
||||
.react-datepicker__quarter-text,
|
||||
.react-datepicker__year-text {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
line-height: 2.5rem;
|
||||
margin: 0.2rem;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,7 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Col, Form, InputGroup, Row } from 'react-bootstrap';
|
||||
import * as Icons from 'react-bootstrap-icons';
|
||||
import DatePicker from 'react-datepicker';
|
||||
import { MiddlewareReturn } from '@floating-ui/core';
|
||||
import { MiddlewareState } from '@floating-ui/dom';
|
||||
import 'react-datepicker/dist/react-datepicker.css';
|
||||
import './custom-datepicker.scss';
|
||||
import './FormInput.scss';
|
||||
|
||||
type IconName = keyof typeof Icons;
|
||||
|
||||
@@ -17,9 +13,7 @@ interface Props {
|
||||
name: string;
|
||||
placeholder?: string;
|
||||
value?: string;
|
||||
valueDate?: Date | null;
|
||||
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
onChangeDate?: (date: Date | null) => void;
|
||||
dataTestId?: string;
|
||||
pwdHideText?: string;
|
||||
pwdShowText?: string;
|
||||
@@ -67,36 +61,16 @@ function FormInput(props: React.PropsWithChildren<Props>): React.ReactNode {
|
||||
<InputGroup.Text>
|
||||
<Icon />
|
||||
</InputGroup.Text>
|
||||
{props.type == 'date'
|
||||
{props.type === 'date'
|
||||
? (
|
||||
<DatePicker
|
||||
selected={props?.valueDate}
|
||||
onChange={(date: Date | null) => {
|
||||
if (props.onChangeDate) {
|
||||
props.onChangeDate(date);
|
||||
}
|
||||
}}
|
||||
dateFormat="MMMM d, yyyy"
|
||||
className="form-control"
|
||||
id="date-input"
|
||||
placeholderText={props.placeholder}
|
||||
popperPlacement="bottom"
|
||||
popperModifiers={[
|
||||
{
|
||||
name: 'preventOverflow',
|
||||
options: {
|
||||
enabled: true,
|
||||
boundariesElement: 'viewport'
|
||||
},
|
||||
fn: function (state: MiddlewareState): MiddlewareReturn | Promise<MiddlewareReturn> {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
]}
|
||||
withPortal
|
||||
showYearDropdown
|
||||
showMonthDropdown
|
||||
dropdownMode="select"
|
||||
<Form.Control
|
||||
required={props.required}
|
||||
type="date"
|
||||
name={props.name}
|
||||
placeholder={props.placeholder ? props.placeholder : ''}
|
||||
value={props?.value}
|
||||
onChange={props.onChange}
|
||||
data-testid={props.dataTestId}
|
||||
/>
|
||||
)
|
||||
: (
|
||||
|
||||
@@ -19,6 +19,8 @@ export const timeAgoTranslations: Record<string, string> = {
|
||||
'month left_pt_br': '{X} mês restante',
|
||||
'days left_pt_br': '{X} dias restantes',
|
||||
'day left_pt_br': '{X} dia restante',
|
||||
'due tomorrow_pt_br': 'Vence amanhã',
|
||||
'due today_pt_br': 'Vence hoje',
|
||||
|
||||
'years ago_es': 'Hace {X} años',
|
||||
'year ago_es': 'Hace {X} año',
|
||||
@@ -40,6 +42,8 @@ export const timeAgoTranslations: Record<string, string> = {
|
||||
'month left_es': 'Falta {X} mes',
|
||||
'days left_es': 'Faltan {X} días',
|
||||
'day left_es': 'Falta {X} día',
|
||||
'due tomorrow_es': 'Vence mañana',
|
||||
'due today_es': 'Vence hoy',
|
||||
|
||||
'years ago_ru': '{X} года назад',
|
||||
'year ago_ru': '{X} год назад',
|
||||
@@ -60,7 +64,9 @@ export const timeAgoTranslations: Record<string, string> = {
|
||||
'months left_ru': 'осталось {X} месяца',
|
||||
'month left_ru': 'Остался {X} месяц',
|
||||
'days left_ru': 'осталось {X} дня',
|
||||
'day left_ru': 'Остался {X} день'
|
||||
'day left_ru': 'Остался {X} день',
|
||||
'due tomorrow_ru': 'Срок завтра',
|
||||
'due today_ru': 'Срок сегодня'
|
||||
};
|
||||
|
||||
export const serverResponsesTranslations: Record<string, string> = {
|
||||
|
||||
@@ -277,8 +277,7 @@ code {
|
||||
padding: 0.375rem 0.10rem 0.375rem 0.75rem;
|
||||
}
|
||||
|
||||
.input-group > .form-control,
|
||||
.react-datepicker__input-container > .form-control {
|
||||
.input-group > .form-control {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,14 @@ function translateTimeMessage(message: string, target: string): string {
|
||||
return message;
|
||||
}
|
||||
|
||||
if (message === 'Due tomorrow') {
|
||||
return timeAgoTranslations[`due tomorrow_${target}`] ?? message;
|
||||
}
|
||||
|
||||
if (message === 'Due today') {
|
||||
return timeAgoTranslations[`due today_${target}`] ?? message;
|
||||
}
|
||||
|
||||
const firstSpace = message.indexOf(' ');
|
||||
const numberValue = message.substring(0, firstSpace);
|
||||
const textValue = message.substring(firstSpace).trim();
|
||||
|
||||
@@ -45,7 +45,7 @@ function TaskAdd(): React.ReactNode {
|
||||
const [taskUrl, setTaskUrl] = useState<string>('');
|
||||
const [taskDone, setTaskDone] = useState<boolean>(false);
|
||||
const [action, setAction] = useState<TaskAction>('add');
|
||||
const [dueDate, setDueDate] = useState<Date | null>(null);
|
||||
const [dueDate, setDueDate] = useState<string>('');
|
||||
const [highPriority, setHighPriority] = useState<boolean>(false);
|
||||
const [currentTag, setCurrentTag] = useState<string>('');
|
||||
const [selectedTags, setSelectedTags] = useState<string[]>([]);
|
||||
@@ -127,7 +127,7 @@ function TaskAdd(): React.ReactNode {
|
||||
setTaskDescription('');
|
||||
setTaskDone(false);
|
||||
setTaskUrl('');
|
||||
setDueDate(null);
|
||||
setDueDate('');
|
||||
setHighPriority(false);
|
||||
setCurrentTag('');
|
||||
setSelectedTags([]);
|
||||
@@ -138,7 +138,7 @@ function TaskAdd(): React.ReactNode {
|
||||
const saveDraft = (
|
||||
description: string,
|
||||
taskUrl: string,
|
||||
due: Date | null,
|
||||
due: string,
|
||||
priority: boolean,
|
||||
draftTags: string[]
|
||||
): void => {
|
||||
@@ -148,7 +148,7 @@ function TaskAdd(): React.ReactNode {
|
||||
const draft: TaskDraft = {
|
||||
description,
|
||||
taskUrl,
|
||||
dueDate: due ? due.toISOString() : null,
|
||||
dueDate: due || null,
|
||||
highPriority: priority,
|
||||
tags: draftTags
|
||||
};
|
||||
@@ -168,8 +168,8 @@ function TaskAdd(): React.ReactNode {
|
||||
const draft: TaskDraft = JSON.parse(raw);
|
||||
setTaskDescription(draft.description ?? '');
|
||||
setTaskUrl(draft.taskUrl ?? '');
|
||||
const parsedDate = draft.dueDate ? new Date(draft.dueDate) : null;
|
||||
setDueDate(parsedDate && !isNaN(parsedDate.getTime()) ? parsedDate : null);
|
||||
const parsedDate = draft.dueDate ? draft.dueDate : '';
|
||||
setDueDate(parsedDate);
|
||||
setHighPriority(draft.highPriority ?? false);
|
||||
setSelectedTags(draft.tags ?? []);
|
||||
setDraftBanner(true);
|
||||
@@ -185,7 +185,7 @@ function TaskAdd(): React.ReactNode {
|
||||
setTaskUrl(task.urls.length ? task.urls[0] : '');
|
||||
setTaskDone(task.done);
|
||||
if (task.dueDateFmt) {
|
||||
setDueDate(new Date(task.dueDate));
|
||||
setDueDate(task.dueDate);
|
||||
}
|
||||
setHighPriority(task.highPriority);
|
||||
if (task.tags) {
|
||||
@@ -249,10 +249,7 @@ function TaskAdd(): React.ReactNode {
|
||||
return;
|
||||
}
|
||||
|
||||
let dueDateFormatted: string = '';
|
||||
if (dueDate) {
|
||||
dueDateFormatted = dueDate.toISOString().substring(0, 10);
|
||||
}
|
||||
const dueDateFormatted: string = dueDate;
|
||||
|
||||
const finalTags = [...selectedTags];
|
||||
if (currentTag.trim()) {
|
||||
@@ -427,11 +424,10 @@ function TaskAdd(): React.ReactNode {
|
||||
type="date"
|
||||
name="dueDate"
|
||||
placeholder={t('task_form_duedate_placeholder')}
|
||||
valueDate={dueDate}
|
||||
onChangeDate={(date: Date | null) => {
|
||||
setDueDate(date);
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setDueDate(e.target.value);
|
||||
hasUserEdited.current = true;
|
||||
saveDraft(taskDescription, taskUrl, date, highPriority, selectedTags);
|
||||
saveDraft(taskDescription, taskUrl, e.target.value, highPriority, selectedTags);
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
|
||||
@@ -89,9 +89,9 @@ public class TimeAgoUtil {
|
||||
} else if (period.getDays() > 1) {
|
||||
sb.append(String.format("%d days left", period.getDays()));
|
||||
} else if (period.getDays() > 0) {
|
||||
sb.append(String.format("%d day left", period.getDays()));
|
||||
sb.append("Due tomorrow");
|
||||
} else if (period.getDays() == 0) {
|
||||
sb.append("0 days left");
|
||||
sb.append("Due today");
|
||||
} else {
|
||||
sb.append("Due");
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ class TimeAgoUtilTest {
|
||||
Assertions.assertNull(TimeAgoUtil.formatDueDate(null));
|
||||
|
||||
LocalDate localDate1 = LocalDate.now().plusDays(1L);
|
||||
String expected1 = "1 day left" + getFormattedSuffix(localDate1);
|
||||
String expected1 = "Due tomorrow" + getFormattedSuffix(localDate1);
|
||||
Assertions.assertEquals(expected1, TimeAgoUtil.formatDueDate(localDate1));
|
||||
|
||||
LocalDate localDate2 = LocalDate.now().plusDays(12L);
|
||||
@@ -74,7 +74,7 @@ class TimeAgoUtilTest {
|
||||
void formatDueDateEdgeCasesTest() {
|
||||
// Test for today
|
||||
LocalDate today = LocalDate.now();
|
||||
String expectedToday = "0 days left" + getFormattedSuffix(today);
|
||||
String expectedToday = "Due today" + getFormattedSuffix(today);
|
||||
Assertions.assertEquals(expectedToday, TimeAgoUtil.formatDueDate(today));
|
||||
|
||||
// Test for a past date
|
||||
|
||||
Reference in New Issue
Block a user