diff --git a/backend/i18n/frontend_en.json b/backend/i18n/frontend_en.json index 114c164bc..bbaab29ad 100644 --- a/backend/i18n/frontend_en.json +++ b/backend/i18n/frontend_en.json @@ -210,7 +210,7 @@ "common.created": "Created", "common.date": "Date", "common.dateTimeEditor.now": "Now", - "common.dateTimeEditor.nowTooltip": "Use Now (UTC)", + "common.dateTimeEditor.nowTooltip": "Use Now", "common.dateTimeEditor.today": "Today", "common.dateTimeEditor.todayTooltip": "Use Today (UTC)", "common.delete": "Delete", diff --git a/backend/i18n/frontend_nl.json b/backend/i18n/frontend_nl.json index f492bb661..7911670f6 100644 --- a/backend/i18n/frontend_nl.json +++ b/backend/i18n/frontend_nl.json @@ -210,7 +210,7 @@ "common.created": "Gemaakt", "common.date": "Datum", "common.dateTimeEditor.now": "Nu", - "common.dateTimeEditor.nowTooltip": "Nu gebruiken (UTC)", + "common.dateTimeEditor.nowTooltip": "Nu gebruiken", "common.dateTimeEditor.today": "Vandaag", "common.dateTimeEditor.todayTooltip": "Gebruik vandaag (UTC)", "common.delete": "Verwijderen", diff --git a/backend/src/Squidex/Areas/Api/Controllers/UI/MyUIOptions.cs b/backend/src/Squidex/Areas/Api/Controllers/UI/MyUIOptions.cs index 13eca6150..937e3151e 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/UI/MyUIOptions.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/UI/MyUIOptions.cs @@ -29,6 +29,8 @@ namespace Squidex.Areas.Api.Controllers.UI public bool HideDateButtons { get; set; } + public bool HideDateTimeModeButton { get; set; } + public bool DisableScheduledChanges { get; set; } public bool RedirectToLogin { get; set; } diff --git a/backend/src/Squidex/appsettings.json b/backend/src/Squidex/appsettings.json index d9d802ee7..e7e7d5f6d 100644 --- a/backend/src/Squidex/appsettings.json +++ b/backend/src/Squidex/appsettings.json @@ -118,6 +118,10 @@ */ "hideDateButtons": false, + /* + * Hide the Local/UTC button + */ + "hideDateTimeModeButton": false, /* * True to disable scheduled content status changed, for example when you have your own scheduling system. */ diff --git a/frontend/app/features/content/shared/content-status.component.ts b/frontend/app/features/content/shared/content-status.component.ts index 3e0b50d71..9bdb4dd53 100644 --- a/frontend/app/features/content/shared/content-status.component.ts +++ b/frontend/app/features/content/shared/content-status.component.ts @@ -43,7 +43,7 @@ export class ContentStatusComponent { public get tooltipText() { if (this.scheduled) { - return `Will be set to '${this.scheduled.status}' at ${this.scheduled.dueTime.toStringFormatUTC('PPpp')}`; + return `Will be set to '${this.scheduled.status}' at ${this.scheduled.dueTime.toStringFormat('PPpp')}`; } else { return this.status; } diff --git a/frontend/app/framework/angular/forms/editors/date-time-editor.component.html b/frontend/app/framework/angular/forms/editors/date-time-editor.component.html index 63c510fe6..f83ce504f 100644 --- a/frontend/app/framework/angular/forms/editors/date-time-editor.component.html +++ b/frontend/app/framework/angular/forms/editors/date-time-editor.component.html @@ -1,25 +1,37 @@ -
+
+
+ + +
- + - +
-
-
+
-
+
-
+
\ No newline at end of file diff --git a/frontend/app/framework/angular/forms/editors/date-time-editor.component.scss b/frontend/app/framework/angular/forms/editors/date-time-editor.component.scss index 8c5a4912a..74673c3c0 100644 --- a/frontend/app/framework/angular/forms/editors/date-time-editor.component.scss +++ b/frontend/app/framework/angular/forms/editors/date-time-editor.component.scss @@ -33,16 +33,27 @@ width: 8.5rem; } + .form-date-time-only { + padding-left: 3.3rem; + width: 11rem; + } + + .form-time { width: 7rem; } } -.btn-clear { - & { +.btn { + &-clear { @include absolute(auto, 4px, 3px, auto); } + &-time-mode { + @include absolute(1px, auto, -1px, auto); + z-index: 1000; + } + &:focus { box-shadow: none; } diff --git a/frontend/app/framework/angular/forms/editors/date-time-editor.component.ts b/frontend/app/framework/angular/forms/editors/date-time-editor.component.ts index 61e51ea9a..f673d1074 100644 --- a/frontend/app/framework/angular/forms/editors/date-time-editor.component.ts +++ b/frontend/app/framework/angular/forms/editors/date-time-editor.component.ts @@ -32,6 +32,7 @@ export class DateTimeEditorComponent extends StatefulControlComponent<{}, string private picker: any; private dateTime: DateTime | null; private hideDateButtonsSettings: boolean; + private hideDateTimeModeButtonSetting: boolean; private suppressEvents = false; @Input() @@ -46,17 +47,29 @@ export class DateTimeEditorComponent extends StatefulControlComponent<{}, string @Input() public hideDateButtons: boolean; + @Input() + public hideDateTimeModeButton: boolean; + + @Input() + public isCompact: boolean; + @ViewChild('dateInput', { static: false }) public dateInput: ElementRef; public timeControl = new FormControl(); public dateControl = new FormControl(); + public isLocalMode = true; + public get shouldShowDateButtons() { return !this.hideDateButtonsSettings && !this.hideDateButtons; } - public get showTime() { + public get shouldShowDateTimeModeButton() { + return !this.hideDateTimeModeButtonSetting && !this.hideDateTimeModeButton; + } + + public get isDateTimeMode() { return this.mode === 'DateTime'; } @@ -68,23 +81,28 @@ export class DateTimeEditorComponent extends StatefulControlComponent<{}, string super(changeDetector, {}); this.hideDateButtonsSettings = !!uiOptions.get('hideDateButtons'); + this.hideDateTimeModeButtonSetting = !!uiOptions.get('hideDateTimeModeButton'); } public ngOnInit() { this.own( this.timeControl.valueChanges.subscribe(() => { + this.dateTime = this.getValue(); + this.callChangeFormatted(); })); this.own( this.dateControl.valueChanges.subscribe(() => { + this.dateTime = this.getValue(); + this.callChangeFormatted(); })); } public writeValue(obj: any) { try { - this.dateTime = DateTime.parseISO(obj); + this.dateTime = DateTime.parseISO(obj, false); } catch (ex) { this.dateTime = null; } @@ -109,7 +127,7 @@ export class DateTimeEditorComponent extends StatefulControlComponent<{}, string } public ngAfterViewInit() { - this.picker = new Pikaday({ field: this.dateInput.nativeElement, format: 'YYYY-MM-DD', + this.picker = new Pikaday({field: this.dateInput.nativeElement, format: 'YYYY-MM-DD', onSelect: () => { if (this.suppressEvents) { return; @@ -125,7 +143,7 @@ export class DateTimeEditorComponent extends StatefulControlComponent<{}, string } public writeNow() { - this.writeValue(DateTime.now().toISOString()); + this.dateTime = DateTime.now(); this.updateControls(); this.callChangeFormatted(); @@ -138,58 +156,55 @@ export class DateTimeEditorComponent extends StatefulControlComponent<{}, string this.dateTime = null; this.updateControls(); - - this.callChange(null); + this.callChangeFormatted(); this.callTouched(); return false; } private callChangeFormatted() { - this.callChange(this.getValue()); + this.callChange(this.dateTime?.toISOString()); } - private getValue(): string | null { + private getValue(): DateTime | null { if (!this.dateControl.value) { return null; } - let result: string | null = null; - - if (this.showTime && this.timeControl.value) { + if (this.isDateTimeMode && this.timeControl.value) { const combined = `${this.dateControl.value}T${this.timeControl.value}`; - const parsed = DateTime.tryParseISO(combined, true); - - if (parsed) { - result = parsed.toISOString(); - } - } - - if (!result) { - const parsed = DateTime.tryParseISO(this.dateControl.value, true); - - if (parsed) { - result = parsed.toISOString(); - } + return DateTime.tryParseISO(combined, !this.isLocalMode); } - return result; + return DateTime.tryParseISO(this.dateControl.value); } private updateControls() { this.suppressEvents = true; - if (this.dateTime && this.mode === 'DateTime') { - this.timeControl.setValue(this.dateTime.toStringFormatUTC('HH:mm:ss'), NO_EMIT); + if (this.dateTime && this.isDateTimeMode) { + if (this.isLocalMode) { + this.timeControl.setValue(this.dateTime.toStringFormat('HH:mm:ss'), NO_EMIT); + } else { + this.timeControl.setValue(this.dateTime.toStringFormatUTC('HH:mm:ss'), NO_EMIT); + } } else { this.timeControl.setValue(null, NO_EMIT); } if (this.dateTime && this.picker) { - const dateString = this.dateTime.toStringFormatUTC('yyyy-MM-dd'); + let dateString: string; + + if (this.isDateTimeMode && this.isLocalMode) { + dateString = this.dateTime.toStringFormat('yyyy-MM-dd'); + + this.picker.setDate(DateHelper.getLocalDate(this.dateTime.raw), true); + } else { + dateString = this.dateTime.toStringFormatUTC('yyyy-MM-dd'); - this.picker.setDate(DateHelper.getUTCDate(this.dateTime.raw), true); + this.picker.setDate(DateHelper.getUTCDate(this.dateTime.raw), true); + } this.dateControl.setValue(dateString, NO_EMIT); } else { @@ -198,4 +213,14 @@ export class DateTimeEditorComponent extends StatefulControlComponent<{}, string this.suppressEvents = false; } + + public setLocalMode(isLocalMode: boolean) { + this.isLocalMode = isLocalMode; + + this.updateControls(); + } + + public setCompact(isCompact: boolean) { + this.next(s => ({ ...s, isCompact })); + } } \ No newline at end of file diff --git a/frontend/app/framework/utils/date-time.ts b/frontend/app/framework/utils/date-time.ts index 4f8234a9f..f237c0687 100644 --- a/frontend/app/framework/utils/date-time.ts +++ b/frontend/app/framework/utils/date-time.ts @@ -5,7 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { addDays, addHours, addMilliseconds, addMinutes, addMonths, addSeconds, addYears, format, formatDistanceToNow, parse, parseISO, startOfDay, startOfMonth, startOfTomorrow, startOfWeek, startOfYesterday } from 'date-fns'; +import { addDays, addHours, addMilliseconds, addMinutes, addMonths, addSeconds, addYears, format, formatDistanceToNow, formatISO, parse, parseISO, startOfDay, startOfMonth, startOfTomorrow, startOfWeek, startOfYesterday } from 'date-fns'; import { DateHelper } from './date-helper'; const DATE_FORMAT = 'yyyy-MM-dd'; @@ -177,6 +177,10 @@ export class DateTime { return format(this.value, DATE_FORMAT); } + public toISODateTime(): string { + return formatISO(this.value); + } + public toStringFormat(pattern: string): string { return format(this.value, pattern); } diff --git a/frontend/app/shared/state/contents.forms.spec.ts b/frontend/app/shared/state/contents.forms.spec.ts index 882d4e7e6..efc2df609 100644 --- a/frontend/app/shared/state/contents.forms.spec.ts +++ b/frontend/app/shared/state/contents.forms.spec.ts @@ -250,19 +250,19 @@ describe('DateTimeField', () => { it('should format old format to date', () => { const dateField = createField({ properties: createProperties('DateTime', { editor: 'Date' }) }); - expect(FieldFormatter.format(dateField, '2017-12-12')).toBe('2017-12-12'); + expect(FieldFormatter.format(dateField, '2017-12-12')).toBe('12/12/2017'); }); it('should format to date', () => { const dateField = createField({ properties: createProperties('DateTime', { editor: 'Date' }) }); - expect(FieldFormatter.format(dateField, '2017-12-12T16:00:00Z')).toBe('2017-12-12'); + expect(FieldFormatter.format(dateField, '2017-12-12T16:00:00Z')).toBe('12/12/2017'); }); it('should format to date time', () => { const field2 = createField({ properties: createProperties('DateTime', { editor: 'DateTime' }) }); - expect(FieldFormatter.format(field2, '2017-12-12T16:00:00Z')).toBe('2017-12-12 16:00:00'); + expect(FieldFormatter.format(field2, '2017-12-12T16:00:00Z')).toBe('12/12/2017, 4:00:00 PM'); }); it('should return default for DateFieldProperties', () => { diff --git a/frontend/app/shared/state/contents.forms.visitors.ts b/frontend/app/shared/state/contents.forms.visitors.ts index 9b75ee2ea..2b426a72d 100644 --- a/frontend/app/shared/state/contents.forms.visitors.ts +++ b/frontend/app/shared/state/contents.forms.visitors.ts @@ -130,9 +130,9 @@ export class FieldFormatter implements FieldPropertiesVisitor { const parsed = DateTime.parseISO(this.value); if (properties.editor === 'Date') { - return parsed.toStringFormatUTC('yyyy-MM-dd'); + return parsed.toStringFormat('P'); } else { - return parsed.toStringFormatUTC('yyyy-MM-dd HH:mm:ss'); + return parsed.toStringFormat('Ppp'); } } catch (ex) { return this.value;