diff --git a/src/main/angular/src/app/api/Timestamp.ts b/src/main/angular/src/app/api/Timestamp.ts index 8d736e5..8516668 100644 --- a/src/main/angular/src/app/api/Timestamp.ts +++ b/src/main/angular/src/app/api/Timestamp.ts @@ -1,28 +1,9 @@ -import {prefix} from "../helpers"; - export class Timestamp { - public readonly WEEKDAY: string[] = ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"]; - - public readonly dayName; - - public readonly timeString; - public constructor( readonly date: Date, ) { - const now = new Date(); - const minutes: string = prefix(this.date.getMinutes(), '0', 2); - if (date.getDate() === now.getDate()) { - this.dayName = "Heute"; - this.timeString = date.getHours() + ":" + minutes; - } else if (date.getDate() === now.getDate() + 1) { - this.dayName = "Morgen"; - this.timeString = date.getHours() + ":" + minutes; - } else { - this.dayName = this.WEEKDAY[date.getDay()]; - this.timeString = date.getHours() + ":" + minutes; - } + // - } public static fromDateOrNull(date: Date | null): Timestamp | null { diff --git a/src/main/angular/src/app/api/time.service.ts b/src/main/angular/src/app/api/time.service.ts new file mode 100644 index 0000000..7736437 --- /dev/null +++ b/src/main/angular/src/app/api/time.service.ts @@ -0,0 +1,65 @@ +import {Inject, Injectable, LOCALE_ID} from '@angular/core'; +import {DatePipe} from "@angular/common"; +import {Timestamp} from "./Timestamp"; + +@Injectable({ + providedIn: 'root' +}) +export class TimeService { + + readonly datePipe: DatePipe = new DatePipe(this.locale); + + private _now: Date = new Date(); + + constructor( + @Inject(LOCALE_ID) readonly locale: string, + ) { + // - + } + + get now(): Date { + return this._now; + } + + relativeDate(timestamp: Timestamp | undefined) { + const date: Date = timestamp?.date; + if (date === undefined || date === null) { + return ""; + } + const relativeName = this.relativeCalendarDaysName(date); + return relativeName + " " + this.datePipe.transform(date, 'HH:mm'); + } + + relativeCalendarDaysName(date: Date): string { + const prefix = this.relativeCalendarDaysPrefix(date); + const weekday = date.toLocaleDateString(this.locale, {weekday: 'long'}); + return prefix + ", " + weekday; + } + + private relativeCalendarDaysPrefix(date: Date): string { + const days = this.calendarDays(date); + if (days < -2) { + return "Vor " + -days + " Tagen"; + } else if (days === -2) { + return "Vorgestern"; + } else if (days === -1) { + return "Gestern"; + } else if (days === 1) { + return "Morgen"; + } else if (days === 2) { + return "Übermorgen"; + } else if (days > 2) { + return "In " + days + " Tagen"; + } + return "Heute"; + } + + private calendarDays(date: Date) { + const DAY_MS = 1000 * 60 * 60 * 24; + const aMidnight = new Date(this._now.getFullYear(), this._now.getMonth(), this._now.getDate()); + const bMidnight = new Date(date.getFullYear(), date.getMonth(), date.getDate()); + const milliseconds = bMidnight.getTime() - aMidnight.getTime(); + return Math.floor(milliseconds / DAY_MS); + } + +} diff --git a/src/main/angular/src/app/pages/schedule/editor/schedule-editor.component.html b/src/main/angular/src/app/pages/schedule/editor/schedule-editor.component.html index 5572775..dd14887 100644 --- a/src/main/angular/src/app/pages/schedule/editor/schedule-editor.component.html +++ b/src/main/angular/src/app/pages/schedule/editor/schedule-editor.component.html @@ -1,13 +1,17 @@ +
-
-
-
-
- +
+ +
+ +
+
+ +
@@ -66,8 +70,8 @@
-
-
+
+
@@ -77,37 +81,39 @@
-
+
- +/- + +/-
-
- +
+
- Überspringen + Überspringen
-
+
-
-
- {{ relativeDate(entry.nextFuzzyTimestamp?.date) }} +
+
+ {{ timeService.relativeDate(entry.nextFuzzyTimestamp) }} (eig: {{ entry.nextClearTimestamp.date | date:'HH:mm' }})
-
+
{{ entry.todo }}
+
+ diff --git a/src/main/angular/src/app/pages/schedule/editor/schedule-editor.component.less b/src/main/angular/src/app/pages/schedule/editor/schedule-editor.component.less index c1539c7..7384738 100644 --- a/src/main/angular/src/app/pages/schedule/editor/schedule-editor.component.less +++ b/src/main/angular/src/app/pages/schedule/editor/schedule-editor.component.less @@ -1,134 +1,73 @@ +@import "../../../../config"; + +@time_input_width: 35%; + #title { - margin: 0.5em; + margin: @margin; } -#entries { - margin-top: 0.5em; - margin-left: 0.5em; +.weekdays { + float: left; + width: 75%; + border-radius: @border-radius; - img { - display: inline; - height: 1em; - vertical-align: top; + div { + float: left; + width: 14.2857%; } - .entry { - margin-bottom: 0.5em; - margin-right: 0.5em; - height: 8.2em; - border-radius: 0.2em; - background-color: #ececec; +} - @media (min-width: 1001px) { +.modes { + float: left; + width: 25%; + + ._inner_ { + margin-left: @margin; + border-radius: @border-radius; + + div { float: left; - width: 400px; - } - - .section { - margin: 0.25em; - - .enabled { - float: left; - width: 1.4em; - border-radius: 0.2em; - margin-right: 0.25em; - } - - .title { - float: left; - padding: 0.1em; - width: calc(100% - 1.65em); - } - - .weekdays { - float: left; - width: 75%; - border-radius: 0.2em; - - div { - float: left; - width: 14.2857%; - } - - } - - .modes { - float: left; - width: 25%; - - ._inner_ { - margin-left: 0.25em; - border-radius: 0.2em; - - div { - float: left; - width: 33.3333%; - } - - } - } - - .time { - width: 50%; - - button { - width: 16.25%; - } - - input { - width: 35%; - } - } - - .sun { - border-radius: 0.2em; - - div { - float: left; - width: 20%; - } - - } - - .flexHalf { - float: left; - width: 50%; - display: flex; - align-items: center; - } - - .flexIcon { - width: 2.5em; - text-align: center; - } - - .flexInput { - flex-grow: 1; - } - - .nextFuzzyTimestamp { - text-align: center; - } - + width: 33.3333%; } } +} - .skipBack { - background-color: #ffc059; - } +.time { + width: 100%; - .skipFont { - color: #ff9a00; - } - - .fuzzyFont { - color: #489dff; - } - - .inactive { - color: gray; + input { + width: @time_input_width; text-align: center; + margin-right: @margin; + } + + button { + width: calc(((100% - @time_input_width) - 4 * @margin) / 4); + margin-right: @margin; + } + + button:last-child { + margin-right: 0; } } + +.sun { + + div { + float: left; + width: 20%; + } + +} + +.timestamp { + text-align: center; +} + +.inactive { + color: gray; + text-align: center; +} diff --git a/src/main/angular/src/app/pages/schedule/editor/schedule-editor.component.ts b/src/main/angular/src/app/pages/schedule/editor/schedule-editor.component.ts index 948b679..6604a98 100644 --- a/src/main/angular/src/app/pages/schedule/editor/schedule-editor.component.ts +++ b/src/main/angular/src/app/pages/schedule/editor/schedule-editor.component.ts @@ -1,4 +1,4 @@ -import {Component, Inject, LOCALE_ID, OnInit} from '@angular/core'; +import {Component, OnInit} from '@angular/core'; import {ScheduleService} from "../../../api/schedule/schedule.service"; import {Schedule} from "../../../api/schedule/Schedule"; import {ScheduleEntry} from "../../../api/schedule/entry/ScheduleEntry"; @@ -6,12 +6,13 @@ import {ScheduleEntryService} from "../../../api/schedule/entry/schedule-entry.s import {ActivatedRoute, Router} from "@angular/router"; import {Update} from "../../../api/Update"; import {NO_OP} from "../../../api/api.service"; -import {DatePipe} from "@angular/common"; import {Duration} from "../../../api/Duration"; import {Bulk} from "../../../api/bulk/Bulk"; import {BulkService} from "../../../api/bulk/BulkService"; import {Zenith} from "../../../api/schedule/entry/Zenith"; +import {TimeService} from "../../../api/time.service"; +import {faCheckCircle, faCircle} from "@fortawesome/free-regular-svg-icons"; const DAY_MINUTES: number = 24 * 60; @@ -22,8 +23,6 @@ const DAY_MINUTES: number = 24 * 60; }) export class ScheduleEditorComponent implements OnInit { - protected readonly datePipe: DatePipe = new DatePipe(this.locale); - protected readonly ScheduleEntry = ScheduleEntry; protected readonly Schedule = Schedule; @@ -54,7 +53,7 @@ export class ScheduleEditorComponent implements OnInit { readonly scheduleService: ScheduleService, readonly entryService: ScheduleEntryService, readonly bulkService: BulkService, - @Inject(LOCALE_ID) private locale: string, + readonly timeService: TimeService, ) { // - } @@ -87,7 +86,7 @@ export class ScheduleEditorComponent implements OnInit { } delete(entry: ScheduleEntry): void { - if (confirm("Eintrag \"" + entry.nextClearTimestamp?.timeString + " +/-" + entry.fuzzySeconds + "\" wirklich löschen?")) { + if (confirm("Eintrag \"" + this.timeService.relativeDate(entry.nextClearTimestamp) + " +/-" + entry.fuzzySeconds + "\" wirklich löschen?")) { this.entryService.delete(entry, NO_OP); } } @@ -100,46 +99,6 @@ export class ScheduleEditorComponent implements OnInit { } } - relativeDate(date: Date | undefined) { - if (date === undefined || date === null) { - return ""; - } - const relativeName = this.relativeCalendarDaysName(date); - return relativeName + " " + this.datePipe.transform(date, 'HH:mm'); - } - - relativeCalendarDaysName(date: Date): string { - const prefix = this.relativeCalendarDaysPrefix(date); - const weekday = date.toLocaleDateString(this.locale, {weekday: 'long'}); - return prefix + ", " + weekday; - } - - private relativeCalendarDaysPrefix(date: Date): string { - const days = this.calendarDays(date); - if (days < -2) { - return "Vor " + -days + " Tagen"; - } else if (days === -2) { - return "Vorgestern"; - } else if (days === -1) { - return "Gestern"; - } else if (days === 1) { - return "Morgen"; - } else if (days === 2) { - return "Übermorgen"; - } else if (days > 2) { - return "In " + days + " Tagen"; - } - return "Heute"; - } - - private calendarDays(date: Date) { - const DAY_MS = 1000 * 60 * 60 * 24; - const aMidnight = new Date(this.now.getFullYear(), this.now.getMonth(), this.now.getDate()); - const bMidnight = new Date(date.getFullYear(), date.getMonth(), date.getDate()); - const milliseconds = bMidnight.getTime() - aMidnight.getTime(); - return Math.floor(milliseconds / DAY_MS); - } - getZenithEntries(type: string): Zenith[] { if (type === 'SUNRISE') { return this.ZENITH_ENTRIES.filter(zenith => zenith.sunrise); @@ -172,5 +131,7 @@ export class ScheduleEditorComponent implements OnInit { this.entryService.set(entry, 'daySecond', newMinutes * 60); } - protected readonly console = console; + protected readonly faCheckCircle = faCheckCircle; + + protected readonly faCircle = faCircle; } diff --git a/src/main/angular/src/app/pages/schedule/list/schedule-list.component.html b/src/main/angular/src/app/pages/schedule/list/schedule-list.component.html index 8537d61..eee7eba 100644 --- a/src/main/angular/src/app/pages/schedule/list/schedule-list.component.html +++ b/src/main/angular/src/app/pages/schedule/list/schedule-list.component.html @@ -1,59 +1,51 @@ -

- -

+
-
-
-
+
-
- -
- - -
- - - -
- {{ schedule.title }} -
- -
- -
+
+
+ +
-
-
- {{ schedule.next?.nextFuzzyTimestamp.dayName }}: {{ schedule.next?.nextFuzzyTimestamp.timeString }} - - - - -
-
- Nächste Ausführung: -
-
- {{ schedule.next?.bulk?.name }} -
+
+ {{ schedule.title }}
-
-
- Zuletzt: -
-
- {{ schedule.last?.lastFuzzyTimestamp.dayName }}: {{ schedule.last?.lastFuzzyTimestamp.timeString }} - - - - -
-
- {{ schedule.last?.bulk?.name }} -
+ + +
+
+ + + +
+
+ {{ timeService.relativeDate(schedule.last?.lastFuzzyTimestamp) }} + - - - +
+
+ {{ schedule.last?.bulk?.name }} + +
+
+
+
+ + diff --git a/src/main/angular/src/app/pages/schedule/list/schedule-list.component.less b/src/main/angular/src/app/pages/schedule/list/schedule-list.component.less index bbf9dc5..e14ceef 100644 --- a/src/main/angular/src/app/pages/schedule/list/schedule-list.component.less +++ b/src/main/angular/src/app/pages/schedule/list/schedule-list.component.less @@ -1,134 +1,23 @@ -.schedules { - padding: 0.25em; - - .scheduleBox { - - .schedule { - margin: 0.25em; - border: 0.1em solid gray; - border-radius: 0.2em; - background-color: #FFF0F0; - - .header { - border-bottom: 0.1em solid gray; - - .enabled { - float: left; - padding: 0.5em; - } - - .title { - float: left; - padding: 0.5em; - font-weight: bold; - } - - .skip { - padding: 0.45em; - float: left; - - img { - display: inline; - vertical-align: bottom; - height: 1.3em; - } - } - - .delete { - float: right; - padding: 0.5em; - } - - } - - .timestampBoxNext { - clear: left; - float: left; - width: 100%; - height: 4em; - border-bottom: 0.1em solid lightgray; - - div { - float: left; - padding: 0.1em; - height: 2em; - } - - .timestampTitle { - // - - } - - .timestampTimestamp { - float: right; - text-align: right; - } - - .timestampBulk { - clear: both; - float: right; - width: 100%; - text-align: right; - } - - .timestampBulkEmpty { - clear: both; - float: right; - width: 100%; - text-align: right; - color: gray; - } - - } - - .timestampBoxLast { - clear: left; - float: left; - width: 100%; - color: gray; - font-size: 80%; - - div { - float: left; - padding: 0.1em; - height: 2em; - } - - .timestampTitle { - width: 4em; - } - - .timestampTimestamp { - text-align: right; - } - - .timestampBulk { - float: right; - text-align: right; - } - - .timestampBulkEmpty { - float: right; - text-align: right; - color: gray; - } - - } - - } - - .scheduleEnabled { - background-color: #F0FFF0; - } - - .skipActive { - background-color: orange; - } - - @media (min-width: 1000px) { - float: left; - width: 400px; - } - - } +@import "../../../../config"; +.skip { + float: right !important; +} + +.delete { + float: right !important; +} + +.last { + color: gray; + font-size: 60%; + border-top: @border solid gray; +} + +.timestamp { + float: left; +} + +.bulk { + float: right; } diff --git a/src/main/angular/src/app/pages/schedule/list/schedule-list.component.ts b/src/main/angular/src/app/pages/schedule/list/schedule-list.component.ts index 2273f4d..35ee450 100644 --- a/src/main/angular/src/app/pages/schedule/list/schedule-list.component.ts +++ b/src/main/angular/src/app/pages/schedule/list/schedule-list.component.ts @@ -1,13 +1,14 @@ import {Component, OnInit} from '@angular/core'; import {ScheduleService} from "../../../api/schedule/schedule.service"; import {Schedule} from "../../../api/schedule/Schedule"; -import {faCheckCircle, faCircle, faTimesCircle} from '@fortawesome/free-regular-svg-icons'; +import {faCheckCircle, faCircle, faPlayCircle, faTimesCircle} from '@fortawesome/free-regular-svg-icons'; import {NO_OP} from "../../../api/api.service"; import {Update} from "../../../api/Update"; import {ScheduleEntryService} from "../../../api/schedule/entry/schedule-entry.service"; import {ScheduleEntry} from "../../../api/schedule/entry/ScheduleEntry"; import {Bulk} from "../../../api/bulk/Bulk"; import {BulkService} from "../../../api/bulk/BulkService"; +import {TimeService} from "../../../api/time.service"; @Component({ selector: 'app-schedule-list', @@ -30,6 +31,7 @@ export class ScheduleListComponent implements OnInit { readonly scheduleService: ScheduleService, readonly entryService: ScheduleEntryService, readonly bulkService: BulkService, + readonly timeService: TimeService, ) { // nothing } @@ -83,4 +85,5 @@ export class ScheduleListComponent implements OnInit { } } + protected readonly faPlayCircle = faPlayCircle; } diff --git a/src/main/angular/src/app/shared/duration/duration.component.html b/src/main/angular/src/app/shared/duration/duration.component.html index db609bd..38fee38 100644 --- a/src/main/angular/src/app/shared/duration/duration.component.html +++ b/src/main/angular/src/app/shared/duration/duration.component.html @@ -1 +1 @@ - + diff --git a/src/main/angular/src/app/shared/duration/duration.component.ts b/src/main/angular/src/app/shared/duration/duration.component.ts index 820d2cc..8afcab4 100644 --- a/src/main/angular/src/app/shared/duration/duration.component.ts +++ b/src/main/angular/src/app/shared/duration/duration.component.ts @@ -23,7 +23,7 @@ export class DurationComponent implements OnInit { } @Input() - bgcolor: string = ''; + inputClass: string = ''; @Input() min: Duration | undefined = undefined; diff --git a/src/main/angular/src/config.less b/src/main/angular/src/config.less new file mode 100644 index 0000000..2fef0e6 --- /dev/null +++ b/src/main/angular/src/config.less @@ -0,0 +1,36 @@ +@margin: 0.5em; +@padding: 0.2em; +@border: 0.05em; +@border-radius: 0.2em; + +.disabledFont { + color: gray; +} + +.disabledBack { + background-color: gray; +} + +.enabledFont { + color: #8fbc8f; +} + +.enabledBack { + background-color: #8fbc8f; +} + +.skipBack { + background-color: #ffc059; +} + +.skipFont { + color: #ff9a00; +} + +.fuzzyFont { + color: #489dff; +} + +.fuzzyBack { + background-color: #88c0ff; +} diff --git a/src/main/angular/src/styles.less b/src/main/angular/src/styles.less index bc98c42..1c78d52 100644 --- a/src/main/angular/src/styles.less +++ b/src/main/angular/src/styles.less @@ -1,3 +1,7 @@ +* { + box-sizing: border-box; +} + // font body, input, select, button { font-size: 5vw; @@ -14,13 +18,12 @@ input, select, button { border: none; outline: none; padding: 0.1em; - border-radius: 0.1em; - box-sizing: border-box; - background-color: white; + border-radius: @border-radius; } select { margin-top: -0.1em; + background-color: transparent; } body { @@ -35,7 +38,6 @@ a { div { overflow: hidden; white-space: nowrap; - box-sizing: border-box; text-overflow: ellipsis; } @@ -159,3 +161,5 @@ table.vertical { .buttonMinus { background-color: #ef8787; } + +@import "tile"; diff --git a/src/main/angular/src/tile.less b/src/main/angular/src/tile.less new file mode 100644 index 0000000..fa7817d --- /dev/null +++ b/src/main/angular/src/tile.less @@ -0,0 +1,69 @@ +@import "config"; + +.tiles { + margin-top: @margin; + margin-left: @margin; + + .tile { + border-radius: @border-radius; + margin-right: @margin; + margin-bottom: @margin; + background-color: #ececec; + + @media (min-width: 1001px) { + width: 400px; + float: left; + } + + .tileHead { + display: flex; + + div { + float: left; + padding: @padding; + } + + .title { + flex-grow: 1; + } + + } + + .tileBody { + padding: @padding; + } + + .tileBodyFlex { + display: flex; + padding: @padding; + } + + .icon { + display: inline; + vertical-align: bottom; + height: 1.1em; + } + + } + + .flexHalf { + width: 50%; + } + + .flexIcon { + float: left; + width: 1.5em; + text-align: center; + } + + .flexIconInput { + float: left; + width: calc(100% - 1.5em); + text-align: center; + } + + .flexFull { + width: 100%; + } + +}