Enhancement: improve clarity of date picker options

This commit is contained in:
shamoon 2025-02-05 22:16:28 -08:00
parent 2103a499eb
commit c32d776911
No known key found for this signature in database
11 changed files with 408 additions and 212 deletions

View File

@ -1167,7 +1167,7 @@
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">166</context>
<context context-type="linenumber">170</context>
</context-group>
</trans-unit>
<trans-unit id="7314814725704332646" datatype="html">
@ -3425,8 +3425,8 @@
<context context-type="linenumber">89</context>
</context-group>
</trans-unit>
<trans-unit id="6371576811194810854" datatype="html">
<source>After</source>
<trans-unit id="5203279511751768967" datatype="html">
<source>From</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/dates-dropdown/dates-dropdown.component.html</context>
<context context-type="linenumber">42</context>
@ -3436,8 +3436,8 @@
<context context-type="linenumber">106</context>
</context-group>
</trans-unit>
<trans-unit id="1218334388194408974" datatype="html">
<source>Before</source>
<trans-unit id="1640609344969975994" datatype="html">
<source>To</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/dates-dropdown/dates-dropdown.component.html</context>
<context context-type="linenumber">62</context>
@ -3470,41 +3470,33 @@
<context context-type="linenumber">93</context>
</context-group>
</trans-unit>
<trans-unit id="4873149362496451858" datatype="html">
<source>Last 7 days</source>
<trans-unit id="9129856334122659953" datatype="html">
<source>Within 1 week</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/dates-dropdown/dates-dropdown.component.ts</context>
<context context-type="linenumber">67</context>
</context-group>
</trans-unit>
<trans-unit id="4463380307954693363" datatype="html">
<source>Last month</source>
<trans-unit id="123064370501514576" datatype="html">
<source>Within 1 month</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/dates-dropdown/dates-dropdown.component.ts</context>
<context context-type="linenumber">72</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pipes/custom-date.pipe.ts</context>
<context context-type="linenumber">19</context>
</context-group>
</trans-unit>
<trans-unit id="8697368973702409683" datatype="html">
<source>Last 3 months</source>
<trans-unit id="1027161426440526546" datatype="html">
<source>Within 3 months</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/dates-dropdown/dates-dropdown.component.ts</context>
<context context-type="linenumber">77</context>
</context-group>
</trans-unit>
<trans-unit id="3566342898065860218" datatype="html">
<source>Last year</source>
<trans-unit id="226779700214642230" datatype="html">
<source>Within 1 year</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/dates-dropdown/dates-dropdown.component.ts</context>
<context context-type="linenumber">82</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pipes/custom-date.pipe.ts</context>
<context context-type="linenumber">14</context>
</context-group>
</trans-unit>
<trans-unit id="8743659855412792665" datatype="html">
<source>Matching algorithm</source>
@ -6298,7 +6290,7 @@
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">154</context>
<context context-type="linenumber">158</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/data/document.ts</context>
@ -6926,7 +6918,7 @@
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">162</context>
<context context-type="linenumber">166</context>
</context-group>
</trans-unit>
<trans-unit id="6475890479659129881" datatype="html">
@ -7528,7 +7520,7 @@
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">159</context>
<context context-type="linenumber">163</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/data/document.ts</context>
@ -7717,147 +7709,147 @@
<source>Title &amp; content</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">157</context>
<context context-type="linenumber">161</context>
</context-group>
</trans-unit>
<trans-unit id="2649431021108393503" datatype="html">
<source>More like</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">172</context>
<context context-type="linenumber">176</context>
</context-group>
</trans-unit>
<trans-unit id="3697582909018473071" datatype="html">
<source>equals</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">178</context>
<context context-type="linenumber">182</context>
</context-group>
</trans-unit>
<trans-unit id="5325481293405718739" datatype="html">
<source>is empty</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">182</context>
<context context-type="linenumber">186</context>
</context-group>
</trans-unit>
<trans-unit id="6166785695326182482" datatype="html">
<source>is not empty</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">186</context>
<context context-type="linenumber">190</context>
</context-group>
</trans-unit>
<trans-unit id="4686622206659266699" datatype="html">
<source>greater than</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">190</context>
<context context-type="linenumber">194</context>
</context-group>
</trans-unit>
<trans-unit id="8014012170270529279" datatype="html">
<source>less than</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">194</context>
<context context-type="linenumber">198</context>
</context-group>
</trans-unit>
<trans-unit id="5195932016807797291" datatype="html">
<source>Correspondent: <x id="PH" equiv-text="this.correspondents.find((c) =&gt; c.id == +rule.value)?.name"/></source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">226,228</context>
<context context-type="linenumber">230,232</context>
</context-group>
</trans-unit>
<trans-unit id="8170755470576301659" datatype="html">
<source>Without correspondent</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">230</context>
<context context-type="linenumber">234</context>
</context-group>
</trans-unit>
<trans-unit id="317796810569008208" datatype="html">
<source>Document type: <x id="PH" equiv-text="this.documentTypes.find((dt) =&gt; dt.id == +rule.value)?.name"/></source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">236,238</context>
<context context-type="linenumber">240,242</context>
</context-group>
</trans-unit>
<trans-unit id="4362173610367509215" datatype="html">
<source>Without document type</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">240</context>
<context context-type="linenumber">244</context>
</context-group>
</trans-unit>
<trans-unit id="232202047340644471" datatype="html">
<source>Storage path: <x id="PH" equiv-text="this.storagePaths.find((sp) =&gt; sp.id == +rule.value)?.name"/></source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">246,248</context>
<context context-type="linenumber">250,252</context>
</context-group>
</trans-unit>
<trans-unit id="1562820715074533164" datatype="html">
<source>Without storage path</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">250</context>
<context context-type="linenumber">254</context>
</context-group>
</trans-unit>
<trans-unit id="8180755793012580465" datatype="html">
<source>Tag: <x id="PH" equiv-text="this.tags.find((t) =&gt; t.id == +rule.value)?.name"/></source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">254,256</context>
<context context-type="linenumber">258,260</context>
</context-group>
</trans-unit>
<trans-unit id="6494566478302448576" datatype="html">
<source>Without any tag</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">260</context>
<context context-type="linenumber">264</context>
</context-group>
</trans-unit>
<trans-unit id="8644099678903817943" datatype="html">
<source>Custom fields query</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">264</context>
<context context-type="linenumber">268</context>
</context-group>
</trans-unit>
<trans-unit id="6523384805359286307" datatype="html">
<source>Title: <x id="PH" equiv-text="rule.value"/></source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">267</context>
<context context-type="linenumber">271</context>
</context-group>
</trans-unit>
<trans-unit id="1872523635812236432" datatype="html">
<source>ASN: <x id="PH" equiv-text="rule.value"/></source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">270</context>
<context context-type="linenumber">274</context>
</context-group>
</trans-unit>
<trans-unit id="102674688969746976" datatype="html">
<source>Owner: <x id="PH" equiv-text="rule.value"/></source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">273</context>
<context context-type="linenumber">277</context>
</context-group>
</trans-unit>
<trans-unit id="3550877650686009106" datatype="html">
<source>Owner not in: <x id="PH" equiv-text="rule.value"/></source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">276</context>
<context context-type="linenumber">280</context>
</context-group>
</trans-unit>
<trans-unit id="1082034558646673343" datatype="html">
<source>Without an owner</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">279</context>
<context context-type="linenumber">283</context>
</context-group>
</trans-unit>
<trans-unit id="7210076240260527720" datatype="html">
@ -9149,6 +9141,13 @@
<context context-type="linenumber">36</context>
</context-group>
</trans-unit>
<trans-unit id="3566342898065860218" datatype="html">
<source>Last year</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pipes/custom-date.pipe.ts</context>
<context context-type="linenumber">14</context>
</context-group>
</trans-unit>
<trans-unit id="3393387677918927062" datatype="html">
<source>%s years ago</source>
<context-group purpose="location">
@ -9156,6 +9155,13 @@
<context context-type="linenumber">15</context>
</context-group>
</trans-unit>
<trans-unit id="4463380307954693363" datatype="html">
<source>Last month</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pipes/custom-date.pipe.ts</context>
<context context-type="linenumber">19</context>
</context-group>
</trans-unit>
<trans-unit id="1158628882375251480" datatype="html">
<source>%s months ago</source>
<context-group purpose="location">

View File

@ -1,5 +1,5 @@
<div class="btn-group w-100" ngbDropdown role="group" [popperOptions]="popperOptions">
<button class="btn btn-sm" id="dropdown{{title}}" ngbDropdownToggle [ngClass]="createdDateBefore || createdDateAfter ? 'btn-primary' : 'btn-outline-primary'" [disabled]="disabled">
<button class="btn btn-sm" id="dropdown{{title}}" ngbDropdownToggle [ngClass]="createdDateTo || createdDateFrom ? 'btn-primary' : 'btn-outline-primary'" [disabled]="disabled">
<i-bs width="1em" height="1em" name="calendar-event-fill"></i-bs>
<div class="d-none d-sm-inline">&nbsp;{{title}}</div>
<pngx-clearable-badge [selected]="isActive" (cleared)="reset()"></pngx-clearable-badge><span class="visually-hidden">selected</span>
@ -31,18 +31,18 @@
<div class="list-group-item d-flex p-2" role="menuitem">
<div class="selected-icon">
@if (createdDateAfter) {
<a class="text-light focus-variants" href="javascript:void(0)" (click)="clearCreatedAfter()">
@if (createdDateFrom) {
<a class="text-light focus-variants" href="javascript:void(0)" (click)="clearCreatedFrom()">
<i-bs width="1em" height="1em" name="check" class="variant-unfocused"></i-bs>
<i-bs width="1em" height="1em" name="x" class="variant-focused text-primary"></i-bs>
</a>
}
</div>
<div class="input-group input-group-sm small ps-1 pe-2">
<span class="input-group-text w-25 small text-muted" i18n>After</span>
<span class="input-group-text w-25 small text-muted" i18n>From</span>
<input class="form-control small" [placeholder]="datePlaceHolder" (dateSelect)="onChangeDebounce()" (change)="onChangeDebounce()" (keypress)="onKeyPress($event)"
maxlength="10" [(ngModel)]="createdDateAfter" ngbDatepicker #createdDateAfterPicker="ngbDatepicker">
<button class="btn btn-outline-secondary" (click)="createdDateAfterPicker.toggle()" type="button">
maxlength="10" [(ngModel)]="createdDateFrom" ngbDatepicker #createdDateFromPicker="ngbDatepicker">
<button class="btn btn-outline-secondary" (click)="createdDateFromPicker.toggle()" type="button">
<i-bs width="1em" height="1em" name="calendar"></i-bs>
</button>
</div>
@ -51,18 +51,18 @@
<div class="list-group-item d-flex p-2" role="menuitem">
<div class="selected-icon">
@if (createdDateBefore) {
<a class="text-light focus-variants" href="javascript:void(0)" (click)="clearCreatedBefore()">
@if (createdDateTo) {
<a class="text-light focus-variants" href="javascript:void(0)" (click)="clearCreatedTo()">
<i-bs width="1em" height="1em" name="check" class="variant-unfocused"></i-bs>
<i-bs width="1em" height="1em" name="x" class="variant-focused text-primary"></i-bs>
</a>
}
</div>
<div class="input-group input-group-sm small ps-1 pe-2">
<span class="input-group-text w-25 small text-muted" i18n>Before</span>
<span class="input-group-text w-25 small text-muted" i18n>To</span>
<input class="form-control small" [placeholder]="datePlaceHolder" (dateSelect)="onChangeDebounce()" (change)="onChangeDebounce()" (keypress)="onKeyPress($event)"
maxlength="10" [(ngModel)]="createdDateBefore" ngbDatepicker #createdDateBeforePicker="ngbDatepicker">
<button class="btn btn-outline-secondary" (click)="createdDateBeforePicker.toggle()" type="button">
maxlength="10" [(ngModel)]="createdDateTo" ngbDatepicker #createdDateToPicker="ngbDatepicker">
<button class="btn btn-outline-secondary" (click)="createdDateToPicker.toggle()" type="button">
<i-bs width="1em" height="1em" name="calendar"></i-bs>
</button>
</div>
@ -95,18 +95,18 @@
<div class="list-group-item d-flex p-2" role="menuitem">
<div class="selected-icon">
@if (addedDateAfter) {
<a class="text-light focus-variants" href="javascript:void(0)" (click)="clearAddedAfter()">
@if (addedDateFrom) {
<a class="text-light focus-variants" href="javascript:void(0)" (click)="clearAddedFrom()">
<i-bs width="1em" height="1em" name="check" class="variant-unfocused"></i-bs>
<i-bs width="1em" height="1em" name="x" class="variant-focused text-primary"></i-bs>
</a>
}
</div>
<div class="input-group input-group-sm small ps-1 pe-2">
<span class="input-group-text w-25 small text-muted" i18n>After</span>
<span class="input-group-text w-25 small text-muted" i18n>From</span>
<input class="form-control small" [placeholder]="datePlaceHolder" (dateSelect)="onChangeDebounce()" (change)="onChangeDebounce()" (keypress)="onKeyPress($event)"
maxlength="10" [(ngModel)]="addedDateAfter" ngbDatepicker #addedDateAfterPicker="ngbDatepicker">
<button class="btn btn-outline-secondary" (click)="addedDateAfterPicker.toggle()" type="button">
maxlength="10" [(ngModel)]="addedDateFrom" ngbDatepicker #addedDateFromPicker="ngbDatepicker">
<button class="btn btn-outline-secondary" (click)="addedDateFromPicker.toggle()" type="button">
<i-bs width="1em" height="1em" name="calendar"></i-bs>
</button>
</div>
@ -115,18 +115,18 @@
<div class="list-group-item d-flex p-2" role="menuitem">
<div class="selected-icon">
@if (addedDateBefore) {
<a class="text-light focus-variants" href="javascript:void(0)" (click)="clearAddedBefore()">
@if (addedDateTo) {
<a class="text-light focus-variants" href="javascript:void(0)" (click)="clearAddedTo()">
<i-bs width="1em" height="1em" name="check" class="variant-unfocused"></i-bs>
<i-bs width="1em" height="1em" name="x" class="variant-focused text-primary"></i-bs>
</a>
}
</div>
<div class="input-group input-group-sm small ps-1 pe-2">
<span class="input-group-text w-25 small text-muted" i18n>Before</span>
<span class="input-group-text w-25 small text-muted" i18n>To</span>
<input class="form-control small" [placeholder]="datePlaceHolder" (dateSelect)="onChangeDebounce()" (change)="onChangeDebounce()" (keypress)="onKeyPress($event)"
maxlength="10" [(ngModel)]="addedDateBefore" ngbDatepicker #addedDateBeforePicker="ngbDatepicker">
<button class="btn btn-outline-secondary" (click)="addedDateBeforePicker.toggle()" type="button">
maxlength="10" [(ngModel)]="addedDateTo" ngbDatepicker #addedDateToPicker="ngbDatepicker">
<button class="btn btn-outline-secondary" (click)="addedDateToPicker.toggle()" type="button">
<i-bs width="1em" height="1em" name="calendar"></i-bs>
</button>
</div>

View File

@ -61,7 +61,7 @@ describe('DatesDropdownComponent', () => {
it('should support date input, emit change', fakeAsync(() => {
let result: string
component.createdDateAfterChange.subscribe((date) => (result = date))
component.createdDateFromChange.subscribe((date) => (result = date))
const input: HTMLInputElement = fixture.nativeElement.querySelector('input')
input.value = '5/30/2023'
input.dispatchEvent(new Event('change'))
@ -83,68 +83,68 @@ describe('DatesDropdownComponent', () => {
let result: DateSelection
component.datesSet.subscribe((date) => (result = date))
component.setCreatedRelativeDate(null)
component.setCreatedRelativeDate(RelativeDate.LAST_7_DAYS)
component.setCreatedRelativeDate(RelativeDate.WITHIN_1_WEEK)
component.setAddedRelativeDate(null)
component.setAddedRelativeDate(RelativeDate.LAST_7_DAYS)
component.setAddedRelativeDate(RelativeDate.WITHIN_1_WEEK)
tick(500)
expect(result).toEqual({
createdAfter: null,
createdBefore: null,
createdRelativeDateID: RelativeDate.LAST_7_DAYS,
addedAfter: null,
addedBefore: null,
addedRelativeDateID: RelativeDate.LAST_7_DAYS,
createdFrom: null,
createdTo: null,
createdRelativeDateID: RelativeDate.WITHIN_1_WEEK,
addedFrom: null,
addedTo: null,
addedRelativeDateID: RelativeDate.WITHIN_1_WEEK,
})
}))
it('should support report if active', () => {
component.createdRelativeDate = RelativeDate.LAST_7_DAYS
component.createdRelativeDate = RelativeDate.WITHIN_1_WEEK
expect(component.isActive).toBeTruthy()
component.createdRelativeDate = null
component.createdDateAfter = '2023-05-30'
component.createdDateFrom = '2023-05-30'
expect(component.isActive).toBeTruthy()
component.createdDateAfter = null
component.createdDateBefore = '2023-05-30'
component.createdDateFrom = null
component.createdDateTo = '2023-05-30'
expect(component.isActive).toBeTruthy()
component.createdDateBefore = null
component.createdDateTo = null
component.addedRelativeDate = RelativeDate.LAST_7_DAYS
component.addedRelativeDate = RelativeDate.WITHIN_1_WEEK
expect(component.isActive).toBeTruthy()
component.addedRelativeDate = null
component.addedDateAfter = '2023-05-30'
component.addedDateFrom = '2023-05-30'
expect(component.isActive).toBeTruthy()
component.addedDateAfter = null
component.addedDateBefore = '2023-05-30'
component.addedDateFrom = null
component.addedDateTo = '2023-05-30'
expect(component.isActive).toBeTruthy()
component.addedDateBefore = null
component.addedDateTo = null
expect(component.isActive).toBeFalsy()
})
it('should support reset', () => {
component.createdDateAfter = '2023-05-30'
component.createdDateFrom = '2023-05-30'
component.reset()
expect(component.createdDateAfter).toBeNull()
expect(component.createdDateFrom).toBeNull()
})
it('should support clearAfter', () => {
component.createdDateAfter = '2023-05-30'
component.clearCreatedAfter()
expect(component.createdDateAfter).toBeNull()
it('should support clearFrom', () => {
component.createdDateFrom = '2023-05-30'
component.clearCreatedFrom()
expect(component.createdDateFrom).toBeNull()
component.addedDateAfter = '2023-05-30'
component.clearAddedAfter()
expect(component.addedDateAfter).toBeNull()
component.addedDateFrom = '2023-05-30'
component.clearAddedFrom()
expect(component.addedDateFrom).toBeNull()
})
it('should support clearBefore', () => {
component.createdDateBefore = '2023-05-30'
component.clearCreatedBefore()
expect(component.createdDateBefore).toBeNull()
it('should support clearTo', () => {
component.createdDateTo = '2023-05-30'
component.clearCreatedTo()
expect(component.createdDateTo).toBeNull()
component.addedDateBefore = '2023-05-30'
component.clearAddedBefore()
expect(component.addedDateBefore).toBeNull()
component.addedDateTo = '2023-05-30'
component.clearAddedTo()
expect(component.addedDateTo).toBeNull()
})
it('should limit keyboard events', () => {

View File

@ -23,19 +23,19 @@ import { popperOptionsReenablePreventOverflow } from 'src/app/utils/popper-optio
import { ClearableBadgeComponent } from '../clearable-badge/clearable-badge.component'
export interface DateSelection {
createdBefore?: string
createdAfter?: string
createdTo?: string
createdFrom?: string
createdRelativeDateID?: number
addedBefore?: string
addedAfter?: string
addedTo?: string
addedFrom?: string
addedRelativeDateID?: number
}
export enum RelativeDate {
LAST_7_DAYS = 0,
LAST_MONTH = 1,
LAST_3_MONTHS = 2,
LAST_YEAR = 3,
WITHIN_1_WEEK = 0,
WITHIN_1_MONTH = 1,
WITHIN_3_MONTHS = 2,
WITHIN_1_YEAR = 3,
}
@Component({
@ -63,23 +63,23 @@ export class DatesDropdownComponent implements OnInit, OnDestroy {
relativeDates = [
{
id: RelativeDate.LAST_7_DAYS,
name: $localize`Last 7 days`,
id: RelativeDate.WITHIN_1_WEEK,
name: $localize`Within 1 week`,
date: new Date().setDate(new Date().getDate() - 7),
},
{
id: RelativeDate.LAST_MONTH,
name: $localize`Last month`,
id: RelativeDate.WITHIN_1_MONTH,
name: $localize`Within 1 month`,
date: new Date().setMonth(new Date().getMonth() - 1),
},
{
id: RelativeDate.LAST_3_MONTHS,
name: $localize`Last 3 months`,
id: RelativeDate.WITHIN_3_MONTHS,
name: $localize`Within 3 months`,
date: new Date().setMonth(new Date().getMonth() - 3),
},
{
id: RelativeDate.LAST_YEAR,
name: $localize`Last year`,
id: RelativeDate.WITHIN_1_YEAR,
name: $localize`Within 1 year`,
date: new Date().setFullYear(new Date().getFullYear() - 1),
},
]
@ -88,16 +88,16 @@ export class DatesDropdownComponent implements OnInit, OnDestroy {
// created
@Input()
createdDateBefore: string
createdDateTo: string
@Output()
createdDateBeforeChange = new EventEmitter<string>()
createdDateToChange = new EventEmitter<string>()
@Input()
createdDateAfter: string
createdDateFrom: string
@Output()
createdDateAfterChange = new EventEmitter<string>()
createdDateFromChange = new EventEmitter<string>()
@Input()
createdRelativeDate: RelativeDate
@ -107,16 +107,16 @@ export class DatesDropdownComponent implements OnInit, OnDestroy {
// added
@Input()
addedDateBefore: string
addedDateTo: string
@Output()
addedDateBeforeChange = new EventEmitter<string>()
addedDateToChange = new EventEmitter<string>()
@Input()
addedDateAfter: string
addedDateFrom: string
@Output()
addedDateAfterChange = new EventEmitter<string>()
addedDateFromChange = new EventEmitter<string>()
@Input()
addedRelativeDate: RelativeDate
@ -136,11 +136,11 @@ export class DatesDropdownComponent implements OnInit, OnDestroy {
get isActive(): boolean {
return (
this.createdRelativeDate !== null ||
this.createdDateAfter?.length > 0 ||
this.createdDateBefore?.length > 0 ||
this.createdDateFrom?.length > 0 ||
this.createdDateTo?.length > 0 ||
this.addedRelativeDate !== null ||
this.addedDateAfter?.length > 0 ||
this.addedDateBefore?.length > 0
this.addedDateFrom?.length > 0 ||
this.addedDateTo?.length > 0
)
}
@ -161,42 +161,42 @@ export class DatesDropdownComponent implements OnInit, OnDestroy {
}
reset() {
this.createdDateBefore = null
this.createdDateAfter = null
this.createdDateTo = null
this.createdDateFrom = null
this.createdRelativeDate = null
this.addedDateBefore = null
this.addedDateAfter = null
this.addedDateTo = null
this.addedDateFrom = null
this.addedRelativeDate = null
this.onChange()
}
setCreatedRelativeDate(rd: RelativeDate) {
this.createdDateBefore = null
this.createdDateAfter = null
this.createdDateTo = null
this.createdDateFrom = null
this.createdRelativeDate = this.createdRelativeDate == rd ? null : rd
this.onChange()
}
setAddedRelativeDate(rd: RelativeDate) {
this.addedDateBefore = null
this.addedDateAfter = null
this.addedDateTo = null
this.addedDateFrom = null
this.addedRelativeDate = this.addedRelativeDate == rd ? null : rd
this.onChange()
}
onChange() {
this.createdDateBeforeChange.emit(this.createdDateBefore)
this.createdDateAfterChange.emit(this.createdDateAfter)
this.createdDateToChange.emit(this.createdDateTo)
this.createdDateFromChange.emit(this.createdDateFrom)
this.createdRelativeDateChange.emit(this.createdRelativeDate)
this.addedDateBeforeChange.emit(this.addedDateBefore)
this.addedDateAfterChange.emit(this.addedDateAfter)
this.addedDateToChange.emit(this.addedDateTo)
this.addedDateFromChange.emit(this.addedDateFrom)
this.addedRelativeDateChange.emit(this.addedRelativeDate)
this.datesSet.emit({
createdAfter: this.createdDateAfter,
createdBefore: this.createdDateBefore,
createdFrom: this.createdDateFrom,
createdTo: this.createdDateTo,
createdRelativeDateID: this.createdRelativeDate,
addedAfter: this.addedDateAfter,
addedBefore: this.addedDateBefore,
addedFrom: this.addedDateFrom,
addedTo: this.addedDateTo,
addedRelativeDateID: this.addedRelativeDate,
})
}
@ -205,30 +205,30 @@ export class DatesDropdownComponent implements OnInit, OnDestroy {
this.createdRelativeDate = null
this.addedRelativeDate = null
this.datesSetDebounce$.next({
createdAfter: this.createdDateAfter,
createdBefore: this.createdDateBefore,
addedAfter: this.addedDateAfter,
addedBefore: this.addedDateBefore,
createdAfter: this.createdDateFrom,
createdBefore: this.createdDateTo,
addedAfter: this.addedDateFrom,
addedBefore: this.addedDateTo,
})
}
clearCreatedBefore() {
this.createdDateBefore = null
clearCreatedTo() {
this.createdDateTo = null
this.onChange()
}
clearCreatedAfter() {
this.createdDateAfter = null
clearCreatedFrom() {
this.createdDateFrom = null
this.onChange()
}
clearAddedBefore() {
this.addedDateBefore = null
clearAddedTo() {
this.addedDateTo = null
this.onChange()
}
clearAddedAfter() {
this.addedDateAfter = null
clearAddedFrom() {
this.addedDateFrom = null
this.onChange()
}

View File

@ -94,11 +94,11 @@
<pngx-dates-dropdown class="flex-fill fade" [class.show]="show"
title="Dates" i18n-title
(datesSet)="updateRules()"
[(createdDateBefore)]="dateCreatedBefore"
[(createdDateAfter)]="dateCreatedAfter"
[(createdDateTo)]="dateCreatedTo"
[(createdDateFrom)]="dateCreatedFrom"
[(createdRelativeDate)]="dateCreatedRelativeDate"
[(addedDateBefore)]="dateAddedBefore"
[(addedDateAfter)]="dateAddedAfter"
[(addedDateTo)]="dateAddedTo"
[(addedDateFrom)]="dateAddedFrom"
[(addedRelativeDate)]="dateAddedRelativeDate">
</pngx-dates-dropdown>
<pngx-permissions-filter-dropdown class="flex-fill fade" [class.show]="show"

View File

@ -32,6 +32,8 @@ import { DocumentType } from 'src/app/data/document-type'
import {
FILTER_ADDED_AFTER,
FILTER_ADDED_BEFORE,
FILTER_ADDED_FROM,
FILTER_ADDED_TO,
FILTER_ASN,
FILTER_ASN_GT,
FILTER_ASN_ISNULL,
@ -39,6 +41,8 @@ import {
FILTER_CORRESPONDENT,
FILTER_CREATED_AFTER,
FILTER_CREATED_BEFORE,
FILTER_CREATED_FROM,
FILTER_CREATED_TO,
FILTER_CUSTOM_FIELDS_QUERY,
FILTER_CUSTOM_FIELDS_TEXT,
FILTER_DOCUMENT_TYPE,
@ -465,48 +469,92 @@ describe('FilterEditorComponent', () => {
])
}))
it('should ingest filter rules for date created after', fakeAsync(() => {
expect(component.dateCreatedAfter).toBeNull()
it('should ingest filter rules for date created after and adjust date by 1 day', fakeAsync(() => {
expect(component.dateCreatedFrom).toBeNull()
component.filterRules = [
{
rule_type: FILTER_CREATED_AFTER,
value: '2023-05-14',
},
]
expect(component.dateCreatedAfter).toEqual('2023-05-14')
expect(component.dateCreatedFrom).toEqual('2023-05-15')
}))
it('should ingest filter rules for date created before', fakeAsync(() => {
expect(component.dateCreatedBefore).toBeNull()
it('should ingest filter rules for date created from', fakeAsync(() => {
expect(component.dateCreatedFrom).toBeNull()
component.filterRules = [
{
rule_type: FILTER_CREATED_FROM,
value: '2023-05-14',
},
]
expect(component.dateCreatedFrom).toEqual('2023-05-14')
}))
it('should ingest filter rules for date created before and adjust date by 1 day', fakeAsync(() => {
expect(component.dateCreatedTo).toBeNull()
component.filterRules = [
{
rule_type: FILTER_CREATED_BEFORE,
value: '2023-05-14',
},
]
expect(component.dateCreatedBefore).toEqual('2023-05-14')
expect(component.dateCreatedTo).toEqual('2023-05-13')
}))
it('should ingest filter rules for date added after', fakeAsync(() => {
expect(component.dateAddedAfter).toBeNull()
it('should ingest filter rules for date created to', fakeAsync(() => {
expect(component.dateCreatedTo).toBeNull()
component.filterRules = [
{
rule_type: FILTER_CREATED_TO,
value: '2023-05-14',
},
]
expect(component.dateCreatedTo).toEqual('2023-05-14')
}))
it('should ingest filter rules for date added after and adjust date by 1 day', fakeAsync(() => {
expect(component.dateAddedFrom).toBeNull()
component.filterRules = [
{
rule_type: FILTER_ADDED_AFTER,
value: '2023-05-14',
},
]
expect(component.dateAddedAfter).toEqual('2023-05-14')
expect(component.dateAddedFrom).toEqual('2023-05-15')
}))
it('should ingest filter rules for date added before', fakeAsync(() => {
expect(component.dateAddedBefore).toBeNull()
it('should ingest filter rules for date added from', fakeAsync(() => {
expect(component.dateAddedFrom).toBeNull()
component.filterRules = [
{
rule_type: FILTER_ADDED_FROM,
value: '2023-05-14',
},
]
expect(component.dateAddedFrom).toEqual('2023-05-14')
}))
it('should ingest filter rules for date added before and adjust date by 1 day', fakeAsync(() => {
expect(component.dateAddedTo).toBeNull()
component.filterRules = [
{
rule_type: FILTER_ADDED_BEFORE,
value: '2023-05-14',
},
]
expect(component.dateAddedBefore).toEqual('2023-05-14')
expect(component.dateAddedTo).toEqual('2023-05-13')
}))
it('should ingest filter rules for date added to', fakeAsync(() => {
expect(component.dateAddedTo).toBeNull()
component.filterRules = [
{
rule_type: FILTER_ADDED_TO,
value: '2023-05-14',
},
]
expect(component.dateAddedTo).toEqual('2023-05-14')
}))
it('should ingest filter rules for has all tags', fakeAsync(() => {
@ -1464,7 +1512,7 @@ describe('FilterEditorComponent', () => {
])
}))
it('should convert user input to correct filter rules on date created after', fakeAsync(() => {
it('should convert user input to correct filter rules on date created from', fakeAsync(() => {
const dateCreatedDropdown = fixture.debugElement.queryAll(
By.directive(DatesDropdownComponent)
)[0]
@ -1473,18 +1521,18 @@ describe('FilterEditorComponent', () => {
dateCreatedAfter.nativeElement.value = '05/14/2023'
// dateCreatedAfter.triggerEventHandler('change')
// TODO: why isn't ngModel triggering this on change?
component.dateCreatedAfter = '2023-05-14'
component.dateCreatedFrom = '2023-05-14'
fixture.detectChanges()
tick(400)
expect(component.filterRules).toEqual([
{
rule_type: FILTER_CREATED_AFTER,
rule_type: FILTER_CREATED_FROM,
value: '2023-05-14',
},
])
}))
it('should convert user input to correct filter rules on date created before', fakeAsync(() => {
it('should convert user input to correct filter rules on date created to', fakeAsync(() => {
const dateCreatedDropdown = fixture.debugElement.queryAll(
By.directive(DatesDropdownComponent)
)[0]
@ -1493,12 +1541,12 @@ describe('FilterEditorComponent', () => {
dateCreatedBefore.nativeElement.value = '05/14/2023'
// dateCreatedBefore.triggerEventHandler('change')
// TODO: why isn't ngModel triggering this on change?
component.dateCreatedBefore = '2023-05-14'
component.dateCreatedTo = '2023-05-14'
fixture.detectChanges()
tick(400)
expect(component.filterRules).toEqual([
{
rule_type: FILTER_CREATED_BEFORE,
rule_type: FILTER_CREATED_TO,
value: '2023-05-14',
},
])
@ -1578,12 +1626,12 @@ describe('FilterEditorComponent', () => {
dateAddedAfter.nativeElement.value = '05/14/2023'
// dateAddedAfter.triggerEventHandler('change')
// TODO: why isn't ngModel triggering this on change?
component.dateAddedAfter = '2023-05-14'
component.dateAddedFrom = '2023-05-14'
fixture.detectChanges()
tick(400)
expect(component.filterRules).toEqual([
{
rule_type: FILTER_ADDED_AFTER,
rule_type: FILTER_ADDED_FROM,
value: '2023-05-14',
},
])
@ -1598,12 +1646,12 @@ describe('FilterEditorComponent', () => {
dateAddedBefore.nativeElement.value = '05/14/2023'
// dateAddedBefore.triggerEventHandler('change')
// TODO: why isn't ngModel triggering this on change?
component.dateAddedBefore = '2023-05-14'
component.dateAddedTo = '2023-05-14'
fixture.detectChanges()
tick(400)
expect(component.filterRules).toEqual([
{
rule_type: FILTER_ADDED_BEFORE,
rule_type: FILTER_ADDED_TO,
value: '2023-05-14',
},
])

View File

@ -38,6 +38,8 @@ import { FilterRule } from 'src/app/data/filter-rule'
import {
FILTER_ADDED_AFTER,
FILTER_ADDED_BEFORE,
FILTER_ADDED_FROM,
FILTER_ADDED_TO,
FILTER_ASN,
FILTER_ASN_GT,
FILTER_ASN_ISNULL,
@ -45,6 +47,8 @@ import {
FILTER_CORRESPONDENT,
FILTER_CREATED_AFTER,
FILTER_CREATED_BEFORE,
FILTER_CREATED_FROM,
FILTER_CREATED_TO,
FILTER_CUSTOM_FIELDS_QUERY,
FILTER_CUSTOM_FIELDS_TEXT,
FILTER_DOCUMENT_TYPE,
@ -133,19 +137,19 @@ const RELATIVE_DATE_QUERY_REGEXP_CREATED = /created:\[([^\]]+)\]/g
const RELATIVE_DATE_QUERY_REGEXP_ADDED = /added:\[([^\]]+)\]/g
const RELATIVE_DATE_QUERYSTRINGS = [
{
relativeDate: RelativeDate.LAST_7_DAYS,
relativeDate: RelativeDate.WITHIN_1_WEEK,
dateQuery: '-1 week to now',
},
{
relativeDate: RelativeDate.LAST_MONTH,
relativeDate: RelativeDate.WITHIN_1_MONTH,
dateQuery: '-1 month to now',
},
{
relativeDate: RelativeDate.LAST_3_MONTHS,
relativeDate: RelativeDate.WITHIN_3_MONTHS,
dateQuery: '-3 month to now',
},
{
relativeDate: RelativeDate.LAST_YEAR,
relativeDate: RelativeDate.WITHIN_1_YEAR,
dateQuery: '-1 year to now',
},
]
@ -349,10 +353,10 @@ export class FilterEditorComponent
storagePathSelectionModel = new FilterableDropdownSelectionModel()
customFieldQueriesModel = new CustomFieldQueriesModel()
dateCreatedBefore: string
dateCreatedAfter: string
dateAddedBefore: string
dateAddedAfter: string
dateCreatedTo: string
dateCreatedFrom: string
dateAddedTo: string
dateAddedFrom: string
dateCreatedRelativeDate: RelativeDate
dateAddedRelativeDate: RelativeDate
@ -385,10 +389,10 @@ export class FilterEditorComponent
this.customFieldQueriesModel.clear(false)
this._textFilter = null
this._moreLikeId = null
this.dateAddedBefore = null
this.dateAddedAfter = null
this.dateCreatedBefore = null
this.dateCreatedAfter = null
this.dateAddedTo = null
this.dateAddedFrom = null
this.dateCreatedTo = null
this.dateCreatedFrom = null
this.dateCreatedRelativeDate = null
this.dateAddedRelativeDate = null
this.textFilterModifier = TEXT_FILTER_MODIFIER_EQUALS
@ -458,16 +462,40 @@ export class FilterEditorComponent
})
break
case FILTER_CREATED_AFTER:
this.dateCreatedAfter = rule.value
// Old rules require adjusting date by a day
const createdAfter = new Date(rule.value)
createdAfter.setDate(createdAfter.getDate() + 1)
this.dateCreatedFrom = createdAfter.toISOString().split('T')[0]
break
case FILTER_CREATED_BEFORE:
this.dateCreatedBefore = rule.value
// Old rules require adjusting date by a day
const createdBefore = new Date(rule.value)
createdBefore.setDate(createdBefore.getDate() - 1)
this.dateCreatedTo = createdBefore.toISOString().split('T')[0]
break
case FILTER_ADDED_AFTER:
this.dateAddedAfter = rule.value
// Old rules require adjusting date by a day
const addedAfter = new Date(rule.value)
addedAfter.setDate(addedAfter.getDate() + 1)
this.dateAddedFrom = addedAfter.toISOString().split('T')[0]
break
case FILTER_ADDED_BEFORE:
this.dateAddedBefore = rule.value
// Old rules require adjusting date by a day
const addedBefore = new Date(rule.value)
addedBefore.setDate(addedBefore.getDate() - 1)
this.dateAddedTo = addedBefore.toISOString().split('T')[0]
break
case FILTER_CREATED_FROM:
this.dateCreatedFrom = rule.value
break
case FILTER_CREATED_TO:
this.dateCreatedTo = rule.value
break
case FILTER_ADDED_FROM:
this.dateAddedFrom = rule.value
break
case FILTER_ADDED_TO:
this.dateAddedTo = rule.value
break
case FILTER_HAS_TAGS_ALL:
this.tagSelectionModel.logicalOperator = LogicalOperator.And
@ -814,28 +842,28 @@ export class FilterEditorComponent
value: JSON.stringify(queries[0]),
})
}
if (this.dateCreatedBefore) {
if (this.dateCreatedTo) {
filterRules.push({
rule_type: FILTER_CREATED_BEFORE,
value: this.dateCreatedBefore,
rule_type: FILTER_CREATED_TO,
value: this.dateCreatedTo,
})
}
if (this.dateCreatedAfter) {
if (this.dateCreatedFrom) {
filterRules.push({
rule_type: FILTER_CREATED_AFTER,
value: this.dateCreatedAfter,
rule_type: FILTER_CREATED_FROM,
value: this.dateCreatedFrom,
})
}
if (this.dateAddedBefore) {
if (this.dateAddedTo) {
filterRules.push({
rule_type: FILTER_ADDED_BEFORE,
value: this.dateAddedBefore,
rule_type: FILTER_ADDED_TO,
value: this.dateAddedTo,
})
}
if (this.dateAddedAfter) {
if (this.dateAddedFrom) {
filterRules.push({
rule_type: FILTER_ADDED_AFTER,
value: this.dateAddedAfter,
rule_type: FILTER_ADDED_FROM,
value: this.dateAddedFrom,
})
}
if (

View File

@ -36,6 +36,11 @@ export const FILTER_CREATED_DAY = 12
export const FILTER_ADDED_BEFORE = 13
export const FILTER_ADDED_AFTER = 14
export const FILTER_CREATED_TO = 43
export const FILTER_CREATED_FROM = 44
export const FILTER_ADDED_TO = 45
export const FILTER_ADDED_FROM = 46
export const FILTER_MODIFIED_BEFORE = 15
export const FILTER_MODIFIED_AFTER = 16
@ -179,6 +184,18 @@ export const FILTER_RULE_TYPES: FilterRuleType[] = [
datatype: 'date',
multi: false,
},
{
id: FILTER_CREATED_TO,
filtervar: 'created__date__lte',
datatype: 'date',
multi: false,
},
{
id: FILTER_CREATED_FROM,
filtervar: 'created__date__gte',
datatype: 'date',
multi: false,
},
{
id: FILTER_CREATED_YEAR,
filtervar: 'created__year',
@ -210,6 +227,18 @@ export const FILTER_RULE_TYPES: FilterRuleType[] = [
datatype: 'date',
multi: false,
},
{
id: FILTER_ADDED_TO,
filtervar: 'added__date__lte',
datatype: 'date',
multi: false,
},
{
id: FILTER_ADDED_FROM,
filtervar: 'added__date__gte',
datatype: 'date',
multi: false,
},
{
id: FILTER_MODIFIED_BEFORE,
filtervar: 'modified__date__lt',

View File

@ -41,7 +41,19 @@ from documents.models import Tag
CHAR_KWARGS = ["istartswith", "iendswith", "icontains", "iexact"]
ID_KWARGS = ["in", "exact"]
INT_KWARGS = ["exact", "gt", "gte", "lt", "lte", "isnull"]
DATE_KWARGS = ["year", "month", "day", "date__gt", "gt", "date__lt", "lt"]
DATE_KWARGS = [
"year",
"month",
"day",
"date__gt",
"date__gte",
"gt",
"gte",
"date__lt",
"date__lte",
"lt",
"lte",
]
CUSTOM_FIELD_QUERY_MAX_DEPTH = 10
CUSTOM_FIELD_QUERY_MAX_ATOMS = 20

View File

@ -0,0 +1,69 @@
# Generated by Django 5.1.4 on 2025-02-06 05:54
from django.db import migrations
from django.db import models
class Migration(migrations.Migration):
dependencies = [
("documents", "1061_workflowactionwebhook_as_json"),
]
operations = [
migrations.AlterField(
model_name="savedviewfilterrule",
name="rule_type",
field=models.PositiveIntegerField(
choices=[
(0, "title contains"),
(1, "content contains"),
(2, "ASN is"),
(3, "correspondent is"),
(4, "document type is"),
(5, "is in inbox"),
(6, "has tag"),
(7, "has any tag"),
(8, "created before"),
(9, "created after"),
(10, "created year is"),
(11, "created month is"),
(12, "created day is"),
(13, "added before"),
(14, "added after"),
(15, "modified before"),
(16, "modified after"),
(17, "does not have tag"),
(18, "does not have ASN"),
(19, "title or content contains"),
(20, "fulltext query"),
(21, "more like this"),
(22, "has tags in"),
(23, "ASN greater than"),
(24, "ASN less than"),
(25, "storage path is"),
(26, "has correspondent in"),
(27, "does not have correspondent in"),
(28, "has document type in"),
(29, "does not have document type in"),
(30, "has storage path in"),
(31, "does not have storage path in"),
(32, "owner is"),
(33, "has owner in"),
(34, "does not have owner"),
(35, "does not have owner in"),
(36, "has custom field value"),
(37, "is shared by me"),
(38, "has custom fields"),
(39, "has custom field in"),
(40, "does not have custom field in"),
(41, "does not have custom field"),
(42, "custom fields query"),
(43, "created to"),
(44, "created from"),
(45, "added to"),
(46, "added from"),
],
verbose_name="rule type",
),
),
]

View File

@ -522,6 +522,10 @@ class SavedViewFilterRule(models.Model):
(40, _("does not have custom field in")),
(41, _("does not have custom field")),
(42, _("custom fields query")),
(43, _("created to")),
(44, _("created from")),
(45, _("added to")),
(46, _("added from")),
]
saved_view = models.ForeignKey(