mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-01-23 02:36:05 +00:00
feat(work-view): show day of week in scheduled date group headers
Display weekday alongside date when tasks are grouped by scheduled date (e.g., "Wed 1/15" instead of "2025-01-15"), making it easier to identify weekends at a glance. Closes #5941
This commit is contained in:
parent
2194cb952d
commit
d475d88da3
4 changed files with 158 additions and 1 deletions
|
|
@ -174,7 +174,7 @@
|
|||
@if (customized.grouped) {
|
||||
@for (group of customized.grouped | keyvalue; track group.key) {
|
||||
<collapsible
|
||||
[title]="group.key"
|
||||
[title]="group.key | scheduledDateGroup"
|
||||
[isIconBefore]="true"
|
||||
[isExpanded]="true"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ import { TaskSharedActions } from '../../root-store/meta/task-shared.actions';
|
|||
import { TODAY_TAG } from '../tag/tag.const';
|
||||
import { LS } from '../../core/persistence/storage-keys.const';
|
||||
import { FinishDayBtnComponent } from './finish-day-btn/finish-day-btn.component';
|
||||
import { ScheduledDateGroupPipe } from '../../ui/pipes/scheduled-date-group.pipe';
|
||||
|
||||
@Component({
|
||||
selector: 'work-view',
|
||||
|
|
@ -92,6 +93,7 @@ import { FinishDayBtnComponent } from './finish-day-btn/finish-day-btn.component
|
|||
CollapsibleComponent,
|
||||
CommonModule,
|
||||
FinishDayBtnComponent,
|
||||
ScheduledDateGroupPipe,
|
||||
],
|
||||
})
|
||||
export class WorkViewComponent implements OnInit, OnDestroy {
|
||||
|
|
|
|||
98
src/app/ui/pipes/scheduled-date-group.pipe.spec.ts
Normal file
98
src/app/ui/pipes/scheduled-date-group.pipe.spec.ts
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
import { TestBed } from '@angular/core/testing';
|
||||
import { ScheduledDateGroupPipe } from './scheduled-date-group.pipe';
|
||||
import { DateTimeFormatService } from '../../core/date-time-format/date-time-format.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { getDbDateStr } from '../../util/get-db-date-str';
|
||||
|
||||
describe('ScheduledDateGroupPipe', () => {
|
||||
let pipe: ScheduledDateGroupPipe;
|
||||
let mockDateTimeFormatService: jasmine.SpyObj<DateTimeFormatService>;
|
||||
let mockTranslateService: jasmine.SpyObj<TranslateService>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockDateTimeFormatService = jasmine.createSpyObj('DateTimeFormatService', [], {
|
||||
currentLocale: 'en-US',
|
||||
});
|
||||
mockTranslateService = jasmine.createSpyObj('TranslateService', ['instant']);
|
||||
mockTranslateService.instant.and.callFake((key: string) => {
|
||||
if (key === 'G.TODAY_TAG_TITLE') return 'Today';
|
||||
return key;
|
||||
});
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
ScheduledDateGroupPipe,
|
||||
{ provide: DateTimeFormatService, useValue: mockDateTimeFormatService },
|
||||
{ provide: TranslateService, useValue: mockTranslateService },
|
||||
],
|
||||
});
|
||||
|
||||
pipe = TestBed.inject(ScheduledDateGroupPipe);
|
||||
});
|
||||
|
||||
it('should create the pipe', () => {
|
||||
expect(pipe).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should format date string with weekday', () => {
|
||||
// Wednesday, January 15, 2025
|
||||
const result = pipe.transform('2025-01-15');
|
||||
expect(result).toMatch(/Wed/i);
|
||||
expect(result).toContain('1');
|
||||
expect(result).toContain('15');
|
||||
});
|
||||
|
||||
it('should return "No date" unchanged', () => {
|
||||
const result = pipe.transform('No date');
|
||||
expect(result).toBe('No date');
|
||||
});
|
||||
|
||||
it('should return null for null input', () => {
|
||||
const result = pipe.transform(null);
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('should return null for undefined input', () => {
|
||||
const result = pipe.transform(undefined);
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('should handle "Today" translation for today\'s date', () => {
|
||||
const todayStr = getDbDateStr();
|
||||
const result = pipe.transform(todayStr);
|
||||
expect(result).toBe('Today');
|
||||
expect(mockTranslateService.instant).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should format weekend dates correctly', () => {
|
||||
// Saturday, January 18, 2025
|
||||
const saturdayResult = pipe.transform('2025-01-18');
|
||||
expect(saturdayResult).toMatch(/Sat/i);
|
||||
|
||||
// Sunday, January 19, 2025
|
||||
const sundayResult = pipe.transform('2025-01-19');
|
||||
expect(sundayResult).toMatch(/Sun/i);
|
||||
});
|
||||
|
||||
it('should respect configured locale for weekday names', () => {
|
||||
// Change locale to German
|
||||
Object.defineProperty(mockDateTimeFormatService, 'currentLocale', {
|
||||
get: () => 'de-DE',
|
||||
});
|
||||
|
||||
// Wednesday in German is "Mi" (Mittwoch)
|
||||
const result = pipe.transform('2025-01-15');
|
||||
expect(result).toMatch(/Mi/i);
|
||||
});
|
||||
|
||||
it('should pass through non-date strings that are not "No date"', () => {
|
||||
// "No tag", "No project" etc. should pass through unchanged
|
||||
const result = pipe.transform('No tag');
|
||||
expect(result).toBe('No tag');
|
||||
});
|
||||
|
||||
it('should handle invalid date strings gracefully', () => {
|
||||
const result = pipe.transform('invalid-date');
|
||||
expect(result).toBe('invalid-date');
|
||||
});
|
||||
});
|
||||
57
src/app/ui/pipes/scheduled-date-group.pipe.ts
Normal file
57
src/app/ui/pipes/scheduled-date-group.pipe.ts
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
import { inject, Pipe, PipeTransform } from '@angular/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { T } from 'src/app/t.const';
|
||||
import { getDbDateStr } from '../../util/get-db-date-str';
|
||||
import { dateStrToUtcDate } from '../../util/date-str-to-utc-date';
|
||||
import { DateTimeFormatService } from '../../core/date-time-format/date-time-format.service';
|
||||
|
||||
const DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/;
|
||||
|
||||
/**
|
||||
* Pipe that formats scheduled date group keys with day of week.
|
||||
* Input: YYYY-MM-DD date string or special strings like "No date"
|
||||
* Output: "Wed 1/15" or "Today" or passthrough for non-date strings
|
||||
*/
|
||||
@Pipe({
|
||||
name: 'scheduledDateGroup',
|
||||
standalone: true,
|
||||
pure: false,
|
||||
})
|
||||
export class ScheduledDateGroupPipe implements PipeTransform {
|
||||
private _dateTimeFormatService = inject(DateTimeFormatService);
|
||||
private _translateService = inject(TranslateService);
|
||||
|
||||
transform(value: unknown): string | null {
|
||||
if (value === null || value === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Ensure value is a string
|
||||
if (typeof value !== 'string') {
|
||||
return String(value);
|
||||
}
|
||||
|
||||
// Check if it's a date string (YYYY-MM-DD format)
|
||||
if (!DATE_REGEX.test(value)) {
|
||||
// Pass through non-date strings like "No date", "No tag", etc.
|
||||
return value;
|
||||
}
|
||||
|
||||
const todayStr = getDbDateStr();
|
||||
if (value === todayStr) {
|
||||
return this._translateService.instant(T.G.TODAY_TAG_TITLE);
|
||||
}
|
||||
|
||||
const date = dateStrToUtcDate(value);
|
||||
const locale = this._dateTimeFormatService.currentLocale;
|
||||
|
||||
// Format with weekday and date: "Wed 1/15"
|
||||
const formatter = new Intl.DateTimeFormat(locale, {
|
||||
weekday: 'short',
|
||||
month: 'numeric',
|
||||
day: 'numeric',
|
||||
});
|
||||
|
||||
return formatter.format(date);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue