css generalized 'tile' (ScheduleList, ScheduleEditor)
This commit is contained in:
parent
0f91c35771
commit
8915d9a8d4
@ -1,28 +1,9 @@
|
|||||||
import {prefix} from "../helpers";
|
|
||||||
|
|
||||||
export class Timestamp {
|
export class Timestamp {
|
||||||
|
|
||||||
public readonly WEEKDAY: string[] = ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"];
|
|
||||||
|
|
||||||
public readonly dayName;
|
|
||||||
|
|
||||||
public readonly timeString;
|
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
readonly date: Date,
|
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 {
|
public static fromDateOrNull(date: Date | null): Timestamp | null {
|
||||||
|
|||||||
65
src/main/angular/src/app/api/time.service.ts
Normal file
65
src/main/angular/src/app/api/time.service.ts
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,13 +1,17 @@
|
|||||||
<ng-container *ngIf="schedule">
|
<ng-container *ngIf="schedule">
|
||||||
|
|
||||||
<div id="title">
|
<div id="title">
|
||||||
<app-text [initial]="schedule.title" (valueChange)="scheduleService.set(schedule, 'title', $event)"></app-text>
|
<app-text [initial]="schedule.title" (valueChange)="scheduleService.set(schedule, 'title', $event)"></app-text>
|
||||||
</div>
|
</div>
|
||||||
<div id="entries">
|
|
||||||
<div class="entry" *ngFor="let entry of schedule.entries; trackBy: ScheduleEntry.trackBy">
|
|
||||||
|
|
||||||
<div class="section">
|
<div class="tiles">
|
||||||
<div class="enabled">
|
|
||||||
<app-bool [value]="entry.enabled" (onChange)="entryService.set(entry, 'enabled', $event)"></app-bool>
|
<div class="tile" *ngFor="let entry of schedule.entries; trackBy: ScheduleEntry.trackBy">
|
||||||
|
|
||||||
|
<div class="tileHead disabledBack" [class.enabledBack]="entry.executable" [class.skipBack]="entry.skip">
|
||||||
|
<div class="enabled" (click)="set(entry, 'enabled', !entry.enabled)">
|
||||||
|
<fa-icon *ngIf="entry.enabled" [icon]="faCheckCircle"></fa-icon>
|
||||||
|
<fa-icon *ngIf="!entry.enabled" [icon]="faCircle"></fa-icon>
|
||||||
</div>
|
</div>
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<select [ngModel]="entry.bulk?.id" (ngModelChange)="entryService.set(entry, 'bulk', $event)">
|
<select [ngModel]="entry.bulk?.id" (ngModelChange)="entryService.set(entry, 'bulk', $event)">
|
||||||
@ -17,7 +21,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="section">
|
<div class="tileBodyFlex">
|
||||||
<div class="weekdays">
|
<div class="weekdays">
|
||||||
<div>
|
<div>
|
||||||
<app-bool label="Mo" [value]="entry.monday" (onChange)="entryService.set(entry, 'monday', $event)"></app-bool>
|
<app-bool label="Mo" [value]="entry.monday" (onChange)="entryService.set(entry, 'monday', $event)"></app-bool>
|
||||||
@ -56,8 +60,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="section" *ngIf="entry.type === 'TIME'">
|
<div class="tileBodyFlex" *ngIf="entry.type === 'TIME'">
|
||||||
<div class="time">
|
<div class="flexFull time">
|
||||||
<button class="buttonPlus" (click)="dayMinuteAdd(entry, +60)">+</button>
|
<button class="buttonPlus" (click)="dayMinuteAdd(entry, +60)">+</button>
|
||||||
<button class="buttonMinus" (click)="dayMinuteAdd(entry, -60)">-</button>
|
<button class="buttonMinus" (click)="dayMinuteAdd(entry, -60)">-</button>
|
||||||
<input type="time" [ngModel]="entry.time" (ngModelChange)="timeFromString(entry, $event)">
|
<input type="time" [ngModel]="entry.time" (ngModelChange)="timeFromString(entry, $event)">
|
||||||
@ -66,8 +70,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="section" *ngIf="entry.type === 'SUNRISE' || entry.type === 'SUNSET'">
|
<div class="tileBodyFlex" *ngIf="entry.type === 'SUNRISE' || entry.type === 'SUNSET'">
|
||||||
<div class="sun">
|
<div class="flexFull sun">
|
||||||
<div *ngFor="let zenith of getZenithEntries(entry.type); trackBy: trackByZenith">
|
<div *ngFor="let zenith of getZenithEntries(entry.type); trackBy: trackByZenith">
|
||||||
<app-bool [label]="zenith.title" [value]="entry.zenith === zenith.value" (onChange)="entryService.set(entry, 'zenith', zenith.value)"></app-bool>
|
<app-bool [label]="zenith.title" [value]="entry.zenith === zenith.value" (onChange)="entryService.set(entry, 'zenith', zenith.value)"></app-bool>
|
||||||
</div>
|
</div>
|
||||||
@ -77,37 +81,39 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="section">
|
<div class="tileBodyFlex">
|
||||||
<div class="flexHalf">
|
<div class="flexHalf">
|
||||||
<div class="flexIcon">
|
<div class="flexIcon">
|
||||||
<img src="assets/dice.svg" alt="+/-" title="Zufallsabweichung +/-">
|
<img class="icon" src="assets/dice.svg" alt="+/-" title="Zufallsabweichung +/-">
|
||||||
</div>
|
</div>
|
||||||
<div class="flexInput">
|
<div class="flexIconInput">
|
||||||
<app-duration [duration]="Duration.ofCode(entry.fuzzySeconds + 's')" [bgcolor]="entry.fuzzySeconds ? '#88c0ff' : 'white'" [min]="Duration.ofCode('')" (onChange)="entryService.set(entry, 'fuzzySeconds', $event.totalSeconds)"></app-duration>
|
<app-duration [duration]="Duration.ofCode(entry.fuzzySeconds + 's')" [inputClass]="entry.fuzzySeconds ? 'fuzzyBack' : ''" [min]="Duration.ofCode('')" (onChange)="entryService.set(entry, 'fuzzySeconds', $event.totalSeconds)"></app-duration>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flexHalf">
|
<div class="flexHalf">
|
||||||
<div class="flexIcon">
|
<div class="flexIcon">
|
||||||
<img src="assets/skip.svg" alt="Überspringen">
|
<img class="icon" src="assets/skip.svg" alt="Überspringen">
|
||||||
</div>
|
</div>
|
||||||
<div class="flexInput flexInputLast">
|
<div class="flexIconInput flexInputLast">
|
||||||
<input type="number" min="0" [class.skipBack]="entry.skip" [ngModel]="entry.skip" (ngModelChange)="entryService.set(entry, 'skip', $event || 0)">
|
<input type="number" min="0" [class.skipBack]="entry.skip" [ngModel]="entry.skip" (ngModelChange)="entryService.set(entry, 'skip', $event || 0)">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="section">
|
<div class="tileBodyFlex">
|
||||||
<div class="nextFuzzyTimestamp" [class.skipFont]="entry.skip" *ngIf="entry.executable">
|
<div class="flexFull timestamp" [class.skipFont]="entry.skip" *ngIf="entry.executable">
|
||||||
{{ relativeDate(entry.nextFuzzyTimestamp?.date) }}
|
{{ timeService.relativeDate(entry.nextFuzzyTimestamp) }}
|
||||||
<span [class.fuzzyFont]="entry.fuzzySeconds" *ngIf="entry.fuzzySeconds">
|
<span [class.fuzzyFont]="entry.fuzzySeconds" *ngIf="entry.fuzzySeconds">
|
||||||
(eig: {{ entry.nextClearTimestamp.date | date:'HH:mm' }})
|
(eig: {{ entry.nextClearTimestamp.date | date:'HH:mm' }})
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="inactive" *ngIf="entry.todo">
|
<div class="flexFull inactive" *ngIf="entry.todo">
|
||||||
{{ entry.todo }}
|
{{ entry.todo }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|||||||
@ -1,49 +1,15 @@
|
|||||||
|
@import "../../../../config";
|
||||||
|
|
||||||
|
@time_input_width: 35%;
|
||||||
|
|
||||||
#title {
|
#title {
|
||||||
margin: 0.5em;
|
margin: @margin;
|
||||||
}
|
|
||||||
|
|
||||||
#entries {
|
|
||||||
margin-top: 0.5em;
|
|
||||||
margin-left: 0.5em;
|
|
||||||
|
|
||||||
img {
|
|
||||||
display: inline;
|
|
||||||
height: 1em;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
.entry {
|
|
||||||
margin-bottom: 0.5em;
|
|
||||||
margin-right: 0.5em;
|
|
||||||
height: 8.2em;
|
|
||||||
border-radius: 0.2em;
|
|
||||||
background-color: #ececec;
|
|
||||||
|
|
||||||
@media (min-width: 1001px) {
|
|
||||||
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 {
|
.weekdays {
|
||||||
float: left;
|
float: left;
|
||||||
width: 75%;
|
width: 75%;
|
||||||
border-radius: 0.2em;
|
border-radius: @border-radius;
|
||||||
|
|
||||||
div {
|
div {
|
||||||
float: left;
|
float: left;
|
||||||
@ -57,8 +23,8 @@
|
|||||||
width: 25%;
|
width: 25%;
|
||||||
|
|
||||||
._inner_ {
|
._inner_ {
|
||||||
margin-left: 0.25em;
|
margin-left: @margin;
|
||||||
border-radius: 0.2em;
|
border-radius: @border-radius;
|
||||||
|
|
||||||
div {
|
div {
|
||||||
float: left;
|
float: left;
|
||||||
@ -69,19 +35,26 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.time {
|
.time {
|
||||||
width: 50%;
|
width: 100%;
|
||||||
|
|
||||||
button {
|
|
||||||
width: 16.25%;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
input {
|
||||||
width: 35%;
|
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 {
|
.sun {
|
||||||
border-radius: 0.2em;
|
|
||||||
|
|
||||||
div {
|
div {
|
||||||
float: left;
|
float: left;
|
||||||
@ -90,45 +63,11 @@
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.flexHalf {
|
.timestamp {
|
||||||
float: left;
|
|
||||||
width: 50%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flexIcon {
|
|
||||||
width: 2.5em;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flexInput {
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nextFuzzyTimestamp {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.skipBack {
|
|
||||||
background-color: #ffc059;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skipFont {
|
|
||||||
color: #ff9a00;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fuzzyFont {
|
|
||||||
color: #489dff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.inactive {
|
.inactive {
|
||||||
color: gray;
|
color: gray;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@ -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 {ScheduleService} from "../../../api/schedule/schedule.service";
|
||||||
import {Schedule} from "../../../api/schedule/Schedule";
|
import {Schedule} from "../../../api/schedule/Schedule";
|
||||||
import {ScheduleEntry} from "../../../api/schedule/entry/ScheduleEntry";
|
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 {ActivatedRoute, Router} from "@angular/router";
|
||||||
import {Update} from "../../../api/Update";
|
import {Update} from "../../../api/Update";
|
||||||
import {NO_OP} from "../../../api/api.service";
|
import {NO_OP} from "../../../api/api.service";
|
||||||
import {DatePipe} from "@angular/common";
|
|
||||||
|
|
||||||
import {Duration} from "../../../api/Duration";
|
import {Duration} from "../../../api/Duration";
|
||||||
import {Bulk} from "../../../api/bulk/Bulk";
|
import {Bulk} from "../../../api/bulk/Bulk";
|
||||||
import {BulkService} from "../../../api/bulk/BulkService";
|
import {BulkService} from "../../../api/bulk/BulkService";
|
||||||
import {Zenith} from "../../../api/schedule/entry/Zenith";
|
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;
|
const DAY_MINUTES: number = 24 * 60;
|
||||||
|
|
||||||
@ -22,8 +23,6 @@ const DAY_MINUTES: number = 24 * 60;
|
|||||||
})
|
})
|
||||||
export class ScheduleEditorComponent implements OnInit {
|
export class ScheduleEditorComponent implements OnInit {
|
||||||
|
|
||||||
protected readonly datePipe: DatePipe = new DatePipe(this.locale);
|
|
||||||
|
|
||||||
protected readonly ScheduleEntry = ScheduleEntry;
|
protected readonly ScheduleEntry = ScheduleEntry;
|
||||||
|
|
||||||
protected readonly Schedule = Schedule;
|
protected readonly Schedule = Schedule;
|
||||||
@ -54,7 +53,7 @@ export class ScheduleEditorComponent implements OnInit {
|
|||||||
readonly scheduleService: ScheduleService,
|
readonly scheduleService: ScheduleService,
|
||||||
readonly entryService: ScheduleEntryService,
|
readonly entryService: ScheduleEntryService,
|
||||||
readonly bulkService: BulkService,
|
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 {
|
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);
|
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[] {
|
getZenithEntries(type: string): Zenith[] {
|
||||||
if (type === 'SUNRISE') {
|
if (type === 'SUNRISE') {
|
||||||
return this.ZENITH_ENTRIES.filter(zenith => zenith.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);
|
this.entryService.set(entry, 'daySecond', newMinutes * 60);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected readonly console = console;
|
protected readonly faCheckCircle = faCheckCircle;
|
||||||
|
|
||||||
|
protected readonly faCircle = faCircle;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,59 +1,51 @@
|
|||||||
<p>
|
<div class="tiles">
|
||||||
<button (click)="create()">+ Hinzufügen</button>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class="schedules">
|
<div class="tile" *ngFor="let schedule of schedules; trackBy: Schedule.trackBy">
|
||||||
<div class="scheduleBox" *ngFor="let schedule of schedules; trackBy: Schedule.trackBy">
|
|
||||||
<div class="schedule" [class.scheduleEnabled]="schedule.enabled">
|
|
||||||
|
|
||||||
<div class="header" [class.skipActive]="schedule.next?.skip">
|
<div class="tileHead disabledBack" [class.enabledBack]="schedule.next" [class.skipBack]="schedule.next?.skip">
|
||||||
|
|
||||||
<div class="enabled" (click)="set(schedule, 'enabled', !schedule.enabled)" [class.true]="schedule.enabled" [class.false]="!schedule.enabled">
|
<div class="enabled" (click)="set(schedule, 'enabled', !schedule.enabled)">
|
||||||
<fa-icon *ngIf="schedule.enabled" [icon]="faCheckCircle"></fa-icon>
|
<fa-icon *ngIf="schedule.enabled" [icon]="faCheckCircle"></fa-icon>
|
||||||
<fa-icon *ngIf="!schedule.enabled" [icon]="faCircle"></fa-icon>
|
<fa-icon *ngIf="!schedule.enabled" [icon]="faCircle"></fa-icon>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="skip" (click)="skip(schedule.next)">
|
|
||||||
<img src="assets/skip.svg" [alt]="schedule.next?.skip">
|
|
||||||
{{ schedule.next?.skip }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="title" [routerLink]="['/Schedule', {id: schedule.id}]">
|
<div class="title" [routerLink]="['/Schedule', {id: schedule.id}]">
|
||||||
{{ schedule.title }}
|
{{ schedule.title }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="skip" (click)="skip(schedule.next)" *ngIf="schedule.next">
|
||||||
|
<img class="icon" src="assets/skip.svg" [alt]="'Über.'">{{ schedule.next?.skip }}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="delete" (click)="delete(schedule)">
|
<div class="delete" (click)="delete(schedule)">
|
||||||
<fa-icon title="Löschen" [icon]="faTimes"></fa-icon>
|
<fa-icon title="Löschen" [icon]="faTimes"></fa-icon>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="timestampBox timestampBoxNext">
|
<div class="tileBody next">
|
||||||
<div class="timestampTimestamp">
|
<div class="timestamp">
|
||||||
<ng-container *ngIf="schedule.next">{{ schedule.next?.nextFuzzyTimestamp.dayName }}: {{ schedule.next?.nextFuzzyTimestamp.timeString }}</ng-container>
|
<ng-container *ngIf="schedule.next">{{ timeService.relativeDate(schedule.next?.nextFuzzyTimestamp) }}</ng-container>
|
||||||
<ng-container *ngIf="!schedule.next">- - -</ng-container>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="timestampTitle">
|
<div class="bulk" [class.timestampBulkEmpty]="!schedule.next?.bulk" *ngIf="schedule.next?.bulk">
|
||||||
Nächste Ausführung:
|
|
||||||
</div>
|
|
||||||
<div class="timestampBulk" [class.timestampBulkEmpty]="!schedule.next?.bulk" *ngIf="schedule.next?.bulk" (click)="execute(schedule.next?.bulk)">
|
|
||||||
{{ schedule.next?.bulk?.name }}
|
{{ schedule.next?.bulk?.name }}
|
||||||
|
<fa-icon [icon]="faPlayCircle" (click)="execute(schedule.next?.bulk)"></fa-icon>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="timestampBox timestampBoxLast">
|
<div class="tileBody last">
|
||||||
<div class="timestampTitle">
|
<div class="timestamp">
|
||||||
Zuletzt:
|
<ng-container *ngIf="schedule.last">{{ timeService.relativeDate(schedule.last?.lastFuzzyTimestamp) }}</ng-container>
|
||||||
</div>
|
|
||||||
<div class="timestampTimestamp">
|
|
||||||
<ng-container *ngIf="schedule.last">{{ schedule.last?.lastFuzzyTimestamp.dayName }}: {{ schedule.last?.lastFuzzyTimestamp.timeString }}</ng-container>
|
|
||||||
<ng-container *ngIf="!schedule.last">- - -</ng-container>
|
<ng-container *ngIf="!schedule.last">- - -</ng-container>
|
||||||
</div>
|
</div>
|
||||||
<div class="timestampBulk" [class.timestampBulkEmpty]="!schedule.last?.bulk" *ngIf="schedule.last?.bulk" (click)="execute(schedule.last?.bulk)">
|
<div class="bulk" [class.timestampBulkEmpty]="!schedule.last?.bulk" *ngIf="schedule.last?.bulk">
|
||||||
{{ schedule.last?.bulk?.name }}
|
{{ schedule.last?.bulk?.name }}
|
||||||
|
<fa-icon [icon]="faPlayCircle" (click)="execute(schedule.last?.bulk)"></fa-icon>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
<button (click)="create()">+ Hinzufügen</button>
|
||||||
|
|||||||
@ -1,134 +1,23 @@
|
|||||||
.schedules {
|
@import "../../../../config";
|
||||||
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 {
|
.skip {
|
||||||
padding: 0.45em;
|
float: right !important;
|
||||||
float: left;
|
|
||||||
|
|
||||||
img {
|
|
||||||
display: inline;
|
|
||||||
vertical-align: bottom;
|
|
||||||
height: 1.3em;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.delete {
|
.delete {
|
||||||
float: right;
|
float: right !important;
|
||||||
padding: 0.5em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
.last {
|
||||||
|
|
||||||
.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;
|
color: gray;
|
||||||
|
font-size: 60%;
|
||||||
|
border-top: @border solid gray;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
.timestamp {
|
||||||
|
|
||||||
.timestampBoxLast {
|
|
||||||
clear: left;
|
|
||||||
float: left;
|
float: left;
|
||||||
width: 100%;
|
|
||||||
color: gray;
|
|
||||||
font-size: 80%;
|
|
||||||
|
|
||||||
div {
|
|
||||||
float: left;
|
|
||||||
padding: 0.1em;
|
|
||||||
height: 2em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.timestampTitle {
|
.bulk {
|
||||||
width: 4em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timestampTimestamp {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timestampBulk {
|
|
||||||
float: right;
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,14 @@
|
|||||||
import {Component, OnInit} from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
import {ScheduleService} from "../../../api/schedule/schedule.service";
|
import {ScheduleService} from "../../../api/schedule/schedule.service";
|
||||||
import {Schedule} from "../../../api/schedule/Schedule";
|
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 {NO_OP} from "../../../api/api.service";
|
||||||
import {Update} from "../../../api/Update";
|
import {Update} from "../../../api/Update";
|
||||||
import {ScheduleEntryService} from "../../../api/schedule/entry/schedule-entry.service";
|
import {ScheduleEntryService} from "../../../api/schedule/entry/schedule-entry.service";
|
||||||
import {ScheduleEntry} from "../../../api/schedule/entry/ScheduleEntry";
|
import {ScheduleEntry} from "../../../api/schedule/entry/ScheduleEntry";
|
||||||
import {Bulk} from "../../../api/bulk/Bulk";
|
import {Bulk} from "../../../api/bulk/Bulk";
|
||||||
import {BulkService} from "../../../api/bulk/BulkService";
|
import {BulkService} from "../../../api/bulk/BulkService";
|
||||||
|
import {TimeService} from "../../../api/time.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-schedule-list',
|
selector: 'app-schedule-list',
|
||||||
@ -30,6 +31,7 @@ export class ScheduleListComponent implements OnInit {
|
|||||||
readonly scheduleService: ScheduleService,
|
readonly scheduleService: ScheduleService,
|
||||||
readonly entryService: ScheduleEntryService,
|
readonly entryService: ScheduleEntryService,
|
||||||
readonly bulkService: BulkService,
|
readonly bulkService: BulkService,
|
||||||
|
readonly timeService: TimeService,
|
||||||
) {
|
) {
|
||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
@ -83,4 +85,5 @@ export class ScheduleListComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected readonly faPlayCircle = faPlayCircle;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
<input type="text" [(ngModel)]="code" (focus)="focus = true" (blur)="apply()" (keydown.enter)="apply()" (keydown.escape)="cancel()" [style.background-color]="bgcolor">
|
<input type="text" [class]="inputClass" [(ngModel)]="code" (focus)="focus = true" (blur)="apply()" (keydown.enter)="apply()" (keydown.escape)="cancel()">
|
||||||
|
|||||||
@ -23,7 +23,7 @@ export class DurationComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
bgcolor: string = '';
|
inputClass: string = '';
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
min: Duration | undefined = undefined;
|
min: Duration | undefined = undefined;
|
||||||
|
|||||||
36
src/main/angular/src/config.less
Normal file
36
src/main/angular/src/config.less
Normal file
@ -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;
|
||||||
|
}
|
||||||
@ -1,3 +1,7 @@
|
|||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
// font
|
// font
|
||||||
body, input, select, button {
|
body, input, select, button {
|
||||||
font-size: 5vw;
|
font-size: 5vw;
|
||||||
@ -14,13 +18,12 @@ input, select, button {
|
|||||||
border: none;
|
border: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
padding: 0.1em;
|
padding: 0.1em;
|
||||||
border-radius: 0.1em;
|
border-radius: @border-radius;
|
||||||
box-sizing: border-box;
|
|
||||||
background-color: white;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
margin-top: -0.1em;
|
margin-top: -0.1em;
|
||||||
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@ -35,7 +38,6 @@ a {
|
|||||||
div {
|
div {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
box-sizing: border-box;
|
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,3 +161,5 @@ table.vertical {
|
|||||||
.buttonMinus {
|
.buttonMinus {
|
||||||
background-color: #ef8787;
|
background-color: #ef8787;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@import "tile";
|
||||||
|
|||||||
69
src/main/angular/src/tile.less
Normal file
69
src/main/angular/src/tile.less
Normal file
@ -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%;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user