From 997ef6f7d64eb3936f47d8e7012beeb35aefc850 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Thu, 22 Oct 2026 00:58:47 -0700 Subject: [PATCH] Ok do it on two lines, separate fields --- ...ustom-fields-query-dropdown.component.html | 66 +++++++++-------- .../custom-fields-query-dropdown.component.ts | 70 ++++++++++++++++--- 2 files changed, 95 insertions(+), 41 deletions(-) diff --git a/src-ui/src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html b/src-ui/src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html index d39ca8ef0..424a6a29a 100644 --- a/src-ui/src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html +++ b/src-ui/src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html @@ -33,36 +33,42 @@ @if (getCustomFieldByID(atom.field)?.data_type === CustomFieldDataType.Date) { -
- - @if (isRelativeDate(atom)) { - - } @else { - - - -
- - -
-
- } -
+ @let relativeDate = getRelativeDate(atom); + + @if (relativeDate) { +
+ + +
+ } @else { + + + +
+ + +
+
+ } } @else if (getCustomFieldByID(atom.field)?.data_type === CustomFieldDataType.Float || getCustomFieldByID(atom.field)?.data_type === CustomFieldDataType.Integer) { } @else if (getCustomFieldByID(atom.field)?.data_type === CustomFieldDataType.Boolean) { diff --git a/src-ui/src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.ts b/src-ui/src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.ts index 75ba99c9b..7cff55161 100644 --- a/src-ui/src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.ts +++ b/src-ui/src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.ts @@ -49,6 +49,15 @@ import { LoadingComponentWithPermissions } from '../../loading-component/loading import { ClearableBadgeComponent } from '../clearable-badge/clearable-badge.component' import { DocumentLinkComponent } from '../input/document-link/document-link.component' +// Matches the English expressions built by updateRelativeDate, e.g. "30 days ago" / "in 2 weeks" +const RELATIVE_DATE_PATTERN = + /^(?:(\d+) (days|weeks|months|years) ago|in (\d+) (days|weeks|months|years))$/ + +interface RelativeDate { + amount: number | null + unit: string +} + export class CustomFieldQueriesModel { private readonly _queries = signal([]) private rootSubscriptions: Subscription[] = [] @@ -305,20 +314,59 @@ export class CustomFieldsQueryDropdownComponent extends LoadingComponentWithPerm public readonly today: string = new Date().toLocaleDateString('en-CA') - private readonly relativeDateModes = new Map() + public readonly relativeDateUnits = [ + { id: 'days ago', label: $localize`days ago` }, + { id: 'weeks ago', label: $localize`weeks ago` }, + { id: 'months ago', label: $localize`months ago` }, + { id: 'years ago', label: $localize`years ago` }, + { id: 'days from now', label: $localize`days from now` }, + { id: 'weeks from now', label: $localize`weeks from now` }, + { id: 'months from now', label: $localize`months from now` }, + { id: 'years from now', label: $localize`years from now` }, + ] - isRelativeDate(atom: CustomFieldQueryAtom): boolean { - return ( - this.relativeDateModes.get(atom.id) ?? - (typeof atom.value === 'string' && - !!atom.value && - !/^\d{4}-\d{2}-\d{2}$/.test(atom.value)) - ) + private readonly relativeDates = new Map() + + getRelativeDate(atom: CustomFieldQueryAtom): RelativeDate | undefined { + if (!this.relativeDates.has(atom.id)) { + const match = + typeof atom.value === 'string' && + atom.value.match(RELATIVE_DATE_PATTERN) + if (match) { + this.relativeDates.set( + atom.id, + match[1] + ? { amount: +match[1], unit: `${match[2]} ago` } + : { amount: +match[3], unit: `${match[4]} from now` } + ) + } + } + return this.relativeDates.get(atom.id) } - setDateQueryMode(atom: CustomFieldQueryAtom, relative: boolean) { - this.relativeDateModes.set(atom.id, relative) - atom.value = null + toggleRelativeDate(atom: CustomFieldQueryAtom) { + if (this.getRelativeDate(atom)) { + this.relativeDates.delete(atom.id) + atom.value = null + } else { + this.relativeDates.set(atom.id, { amount: null, unit: 'days ago' }) + this.updateRelativeDate(atom, {}) + } + } + + updateRelativeDate( + atom: CustomFieldQueryAtom, + change: Partial + ) { + const relativeDate = Object.assign(this.relativeDates.get(atom.id), change) + const { amount, unit } = relativeDate + const [period, direction] = unit.split(' ', 2) + atom.value = + amount === null + ? null + : direction === 'ago' + ? `${amount} ${unit}` + : `in ${amount} ${period}` } public customFieldSearchFn = (term: string, field: CustomField): boolean =>