mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Merged PR 1381: #3326 Tätigkeitskalender Suchfunktion und neues Design
Tätigkeitskalender Suchfunktion und neues Design Zusätzliche Änderungen im PR: - Anpassungen für GitHub package Zugriff - UiModalRef um ein afterChanged$ erweitert, um nach dem schließen zu erkennen ob ein reload notwendig ist - ui-loader funktionierte nicht bei verwendung von ui-scroll-container mit useLoadAnimation false - ui-skeleton-loader um Template für TK Listenitem erweitert Related work items: #3419, #3420, #3421, #3422, #3423
This commit is contained in:
committed by
Lorenz Hilpert
parent
fdaceb9bf8
commit
9dd0954967
4
.npmrc
4
.npmrc
@@ -1,3 +1 @@
|
|||||||
@isa:registry=https://pkgs.dev.azure.com/hugendubel/_packaging/hugendubel%40Local/npm/registry/
|
@paragondata:registry=https://npm.pkg.github.com
|
||||||
@cmf:registry=https://pkgs.dev.azure.com/hugendubel/_packaging/hugendubel%40Local/npm/registry/
|
|
||||||
always-auth=true
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { EISPublicService, FileDTO, ProcessingStatus, QueryTokenDTO } from '@swagger/eis';
|
import { DisplayInfoDTO, EISPublicService, FileDTO, ProcessingStatus, QueryTokenDTO } from '@swagger/eis';
|
||||||
import { DateAdapter } from '@ui/common';
|
import { DateAdapter } from '@ui/common';
|
||||||
import { memorize } from '@utils/common';
|
import { memorize } from '@utils/common';
|
||||||
import { map, shareReplay, switchMap } from 'rxjs/operators';
|
import { map, shareReplay, switchMap } from 'rxjs/operators';
|
||||||
@@ -279,4 +279,80 @@ export class DomainTaskCalendarService {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
moveRemovedToEnd(a: DisplayInfoDTO, b: DisplayInfoDTO) {
|
||||||
|
const statusA = this.getProcessingStatusList(a)?.includes('Removed');
|
||||||
|
const statusB = this.getProcessingStatusList(b)?.includes('Removed');
|
||||||
|
|
||||||
|
if (statusA && statusB) {
|
||||||
|
return 0;
|
||||||
|
} else if (statusA && !statusB) {
|
||||||
|
return 1;
|
||||||
|
} else if (!statusA && statusB) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an Array of DisplayInfoDTO, sorted by ProcessingStatus
|
||||||
|
* Ignores Overdue if Task is already Completed
|
||||||
|
* Compared DisploayInfoDTO is of Type Task and Info Or PreInfo then sort by Type
|
||||||
|
* @param items DisplayInfoDTO Array to sort
|
||||||
|
* @param order Processing Status Order
|
||||||
|
* @returns DisplayInfoDTO Array ordered by Processing Status anf Type
|
||||||
|
*/
|
||||||
|
sort(items: DisplayInfoDTO[], order: ProcessingStatusList) {
|
||||||
|
let result = [...items];
|
||||||
|
const reversedOrder = [...order].reverse();
|
||||||
|
|
||||||
|
for (const status of reversedOrder) {
|
||||||
|
result = result?.sort((a, b) => {
|
||||||
|
const statusA = this.getProcessingStatusList(a);
|
||||||
|
const statusB = this.getProcessingStatusList(b);
|
||||||
|
|
||||||
|
// Ignore Overdue when it is already Completed
|
||||||
|
if (status === 'Overdue' && statusA.includes('Completed')) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const aHasStatus = statusA.includes(status);
|
||||||
|
const bHasStatus = statusB.includes(status);
|
||||||
|
|
||||||
|
if (aHasStatus && bHasStatus) {
|
||||||
|
// If it has the same ProcessingStatus then Sort by Type
|
||||||
|
const aType = this.getInfoType(a);
|
||||||
|
const bType = this.getInfoType(b);
|
||||||
|
if (aType !== bType) {
|
||||||
|
if (aType === 'Info' || aType === 'PreInfo') {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (statusB.includes('Completed')) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
} else if (aHasStatus && !bHasStatus) {
|
||||||
|
return -1;
|
||||||
|
} else if (!aHasStatus && bHasStatus) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDateGroupKey(d: string) {
|
||||||
|
// Get Date as string key to ignore time for grouping
|
||||||
|
const date = new Date(d);
|
||||||
|
return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -212,4 +212,7 @@
|
|||||||
<g id="shiiping_document" transform="matrix(1.11057,0,0,1.11057,2.2921,-1.02614)">
|
<g id="shiiping_document" transform="matrix(1.11057,0,0,1.11057,2.2921,-1.02614)">
|
||||||
<path d="M20.713,0.924L2.151,0.924C1.824,0.924 1.517,1.048 1.288,1.283C1.06,1.512 0.936,1.819 0.936,2.139L0.936,25.634C0.936,26.307 1.478,26.849 2.151,26.849C2.151,26.849 2.758,26.849 2.758,26.849C2.758,26.849 2.758,28.523 2.758,28.523C2.758,29.196 3.3,29.738 3.973,29.738L22.535,29.738C23.208,29.738 23.75,29.196 23.75,28.523L23.75,5.028C23.75,4.361 23.202,3.819 22.535,3.819C22.535,3.819 21.928,3.819 21.928,3.819C21.928,3.819 21.928,2.139 21.928,2.139C21.928,1.466 21.386,0.924 20.713,0.924ZM22.471,5.098L22.471,28.459L4.037,28.459L4.037,26.849C4.037,26.849 20.713,26.849 20.713,26.849C21.341,26.849 21.855,26.371 21.921,25.766L21.928,25.64C21.928,25.64 21.828,25.734 21.828,25.734L21.928,25.634L21.928,5.098L22.471,5.098ZM2.215,2.197L2.215,25.57L20.649,25.57L20.649,2.197L2.215,2.197ZM9.403,22.1L3.397,22.1C3.042,22.1 2.758,22.384 2.758,22.739C2.758,23.094 3.042,23.379 3.397,23.379C3.397,23.379 9.403,23.379 9.403,23.379C9.758,23.379 10.043,23.094 10.043,22.739C10.043,22.384 9.758,22.1 9.403,22.1ZM14.522,19.469L3.277,19.469C2.996,19.469 2.758,19.747 2.758,20.108C2.758,20.469 2.996,20.747 3.277,20.747L14.522,20.747C14.803,20.747 15.041,20.469 15.041,20.108C15.041,19.747 14.803,19.469 14.522,19.469ZM12.352,16.843L3.397,16.843C3.042,16.843 2.758,17.127 2.758,17.483C2.758,17.838 3.042,18.122 3.397,18.122C3.397,18.122 12.352,18.122 12.352,18.122C12.707,18.122 12.992,17.838 12.992,17.483C12.992,17.127 12.707,16.843 12.352,16.843ZM14.478,14.212L3.322,14.212C3.013,14.212 2.758,14.492 2.758,14.851C2.758,15.211 3.013,15.491 3.322,15.491C3.322,15.491 14.478,15.491 14.478,15.491C14.786,15.491 15.041,15.211 15.041,14.851C15.041,14.492 14.786,14.212 14.478,14.212ZM9.593,11.587L3.298,11.587C3.004,11.587 2.758,11.866 2.758,12.226C2.758,12.587 3.004,12.866 3.298,12.866C3.298,12.866 9.593,12.866 9.593,12.866C9.887,12.866 10.133,12.587 10.133,12.226C10.133,11.866 9.887,11.587 9.593,11.587ZM15.163,3.657C12.86,3.657 10.987,5.53 10.987,7.833C10.987,10.136 12.86,12.009 15.163,12.009C17.466,12.009 19.345,10.136 19.345,7.833C19.345,5.53 17.472,3.657 15.163,3.657ZM15.163,4.936C16.762,4.936 18.066,6.234 18.066,7.833C18.066,9.432 16.768,10.73 15.163,10.73C13.564,10.73 12.266,9.432 12.266,7.833C12.266,6.234 13.564,4.936 15.163,4.936ZM9.525,8.956L3.366,8.956C3.03,8.956 2.758,9.238 2.758,9.595C2.758,9.952 3.03,10.234 3.366,10.234L9.525,10.234C9.861,10.234 10.133,9.952 10.133,9.595C10.133,9.238 9.861,8.956 9.525,8.956ZM14.821,7.954L14.61,7.634C14.459,7.403 14.127,7.319 13.867,7.453C13.596,7.593 13.507,7.902 13.664,8.142C13.664,8.142 14.228,9.011 14.228,9.011C14.313,9.141 14.457,9.233 14.626,9.251C14.651,9.256 14.676,9.256 14.701,9.256C14.846,9.256 14.984,9.207 15.087,9.116C15.087,9.116 17.248,7.196 17.248,7.196C17.469,6.999 17.469,6.68 17.248,6.483C17.037,6.291 16.693,6.291 16.481,6.479L14.821,7.954ZM2.164,2.197C2.16,2.197 2.157,2.197 2.154,2.197C2.152,2.197 2.151,2.197 2.151,2.197L2.164,2.197ZM2.151,2.097L2.151,2.097L2.151,2.197L2.151,2.097Z" style="fill:rgb(0,4,0);"/>
|
<path d="M20.713,0.924L2.151,0.924C1.824,0.924 1.517,1.048 1.288,1.283C1.06,1.512 0.936,1.819 0.936,2.139L0.936,25.634C0.936,26.307 1.478,26.849 2.151,26.849C2.151,26.849 2.758,26.849 2.758,26.849C2.758,26.849 2.758,28.523 2.758,28.523C2.758,29.196 3.3,29.738 3.973,29.738L22.535,29.738C23.208,29.738 23.75,29.196 23.75,28.523L23.75,5.028C23.75,4.361 23.202,3.819 22.535,3.819C22.535,3.819 21.928,3.819 21.928,3.819C21.928,3.819 21.928,2.139 21.928,2.139C21.928,1.466 21.386,0.924 20.713,0.924ZM22.471,5.098L22.471,28.459L4.037,28.459L4.037,26.849C4.037,26.849 20.713,26.849 20.713,26.849C21.341,26.849 21.855,26.371 21.921,25.766L21.928,25.64C21.928,25.64 21.828,25.734 21.828,25.734L21.928,25.634L21.928,5.098L22.471,5.098ZM2.215,2.197L2.215,25.57L20.649,25.57L20.649,2.197L2.215,2.197ZM9.403,22.1L3.397,22.1C3.042,22.1 2.758,22.384 2.758,22.739C2.758,23.094 3.042,23.379 3.397,23.379C3.397,23.379 9.403,23.379 9.403,23.379C9.758,23.379 10.043,23.094 10.043,22.739C10.043,22.384 9.758,22.1 9.403,22.1ZM14.522,19.469L3.277,19.469C2.996,19.469 2.758,19.747 2.758,20.108C2.758,20.469 2.996,20.747 3.277,20.747L14.522,20.747C14.803,20.747 15.041,20.469 15.041,20.108C15.041,19.747 14.803,19.469 14.522,19.469ZM12.352,16.843L3.397,16.843C3.042,16.843 2.758,17.127 2.758,17.483C2.758,17.838 3.042,18.122 3.397,18.122C3.397,18.122 12.352,18.122 12.352,18.122C12.707,18.122 12.992,17.838 12.992,17.483C12.992,17.127 12.707,16.843 12.352,16.843ZM14.478,14.212L3.322,14.212C3.013,14.212 2.758,14.492 2.758,14.851C2.758,15.211 3.013,15.491 3.322,15.491C3.322,15.491 14.478,15.491 14.478,15.491C14.786,15.491 15.041,15.211 15.041,14.851C15.041,14.492 14.786,14.212 14.478,14.212ZM9.593,11.587L3.298,11.587C3.004,11.587 2.758,11.866 2.758,12.226C2.758,12.587 3.004,12.866 3.298,12.866C3.298,12.866 9.593,12.866 9.593,12.866C9.887,12.866 10.133,12.587 10.133,12.226C10.133,11.866 9.887,11.587 9.593,11.587ZM15.163,3.657C12.86,3.657 10.987,5.53 10.987,7.833C10.987,10.136 12.86,12.009 15.163,12.009C17.466,12.009 19.345,10.136 19.345,7.833C19.345,5.53 17.472,3.657 15.163,3.657ZM15.163,4.936C16.762,4.936 18.066,6.234 18.066,7.833C18.066,9.432 16.768,10.73 15.163,10.73C13.564,10.73 12.266,9.432 12.266,7.833C12.266,6.234 13.564,4.936 15.163,4.936ZM9.525,8.956L3.366,8.956C3.03,8.956 2.758,9.238 2.758,9.595C2.758,9.952 3.03,10.234 3.366,10.234L9.525,10.234C9.861,10.234 10.133,9.952 10.133,9.595C10.133,9.238 9.861,8.956 9.525,8.956ZM14.821,7.954L14.61,7.634C14.459,7.403 14.127,7.319 13.867,7.453C13.596,7.593 13.507,7.902 13.664,8.142C13.664,8.142 14.228,9.011 14.228,9.011C14.313,9.141 14.457,9.233 14.626,9.251C14.651,9.256 14.676,9.256 14.701,9.256C14.846,9.256 14.984,9.207 15.087,9.116C15.087,9.116 17.248,7.196 17.248,7.196C17.469,6.999 17.469,6.68 17.248,6.483C17.037,6.291 16.693,6.291 16.481,6.479L14.821,7.954ZM2.164,2.197C2.16,2.197 2.157,2.197 2.154,2.197C2.152,2.197 2.151,2.197 2.151,2.197L2.164,2.197ZM2.151,2.097L2.151,2.097L2.151,2.197L2.151,2.097Z" style="fill:rgb(0,4,0);"/>
|
||||||
</g>
|
</g>
|
||||||
|
<g id="filter_new" transform="matrix(1.77778,0,0,2,0.000431998,3.99944)">
|
||||||
|
<path d="M7,12L11,12L11,10L7,10L7,12ZM0,0L0,2L18,2L18,0L0,0ZM3,7L15,7L15,5L3,5L3,7Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 105 KiB |
@@ -46,8 +46,7 @@ export class GoodsOutSearchFilterComponent implements OnInit, OnDestroy {
|
|||||||
private _goodsOutSearchStore: GoodsOutSearchStore,
|
private _goodsOutSearchStore: GoodsOutSearchStore,
|
||||||
private _breadcrumb: BreadcrumbService,
|
private _breadcrumb: BreadcrumbService,
|
||||||
private _cdr: ChangeDetectorRef,
|
private _cdr: ChangeDetectorRef,
|
||||||
private _router: Router,
|
private _router: Router
|
||||||
private readonly _config: Config
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
|||||||
@@ -1,6 +1,16 @@
|
|||||||
import { Clipboard } from '@angular/cdk/clipboard';
|
import { Clipboard } from '@angular/cdk/clipboard';
|
||||||
import { DatePipe } from '@angular/common';
|
import { DatePipe } from '@angular/common';
|
||||||
import { Component, ChangeDetectionStrategy, Input, OnChanges, SimpleChanges, Optional, HostBinding } from '@angular/core';
|
import {
|
||||||
|
Component,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
Input,
|
||||||
|
OnChanges,
|
||||||
|
SimpleChanges,
|
||||||
|
Optional,
|
||||||
|
HostBinding,
|
||||||
|
EventEmitter,
|
||||||
|
Output,
|
||||||
|
} from '@angular/core';
|
||||||
import { DomainTaskCalendarService } from '@domain/task-calendar';
|
import { DomainTaskCalendarService } from '@domain/task-calendar';
|
||||||
import { FileDTO } from '@swagger/checkout';
|
import { FileDTO } from '@swagger/checkout';
|
||||||
import { DisplayInfoDTO } from '@swagger/eis';
|
import { DisplayInfoDTO } from '@swagger/eis';
|
||||||
@@ -27,6 +37,9 @@ export class TaskInfoComponent implements OnChanges {
|
|||||||
@Input()
|
@Input()
|
||||||
info: DisplayInfoDTO;
|
info: DisplayInfoDTO;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
changed = new EventEmitter<boolean>();
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
showTaskDate: boolean;
|
showTaskDate: boolean;
|
||||||
|
|
||||||
@@ -210,5 +223,6 @@ export class TaskInfoComponent implements OnChanges {
|
|||||||
await this.domainTaskCalendarService.addComment({ infoId: this.info.id, text: note }).toPromise();
|
await this.domainTaskCalendarService.addComment({ infoId: this.info.id, text: note }).toPromise();
|
||||||
sessionStorage.removeItem(`INFO_NOTE_${this.info?.id}`);
|
sessionStorage.removeItem(`INFO_NOTE_${this.info?.id}`);
|
||||||
this.noteAdded$.next();
|
this.noteAdded$.next();
|
||||||
|
this.changed.emit(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +1,45 @@
|
|||||||
<div class="date">
|
<div class="task-list-item-wrapper">
|
||||||
<ng-container *ngIf="showDate">
|
<div
|
||||||
{{ item?.taskDate || item?.publicationDate | date }}
|
class="indicator invisible"
|
||||||
</ng-container>
|
[class.show]="(isTask$ | async) && !(hasUpdate$ | async)"
|
||||||
<ng-container *ngIf="!showDate">
|
[style.backgroundColor]="indicatorColor$ | async"
|
||||||
<ng-container [ngSwitch]="showTime$ | async">
|
></div>
|
||||||
<ng-container *ngSwitchCase="true"> {{ item?.timeFrom | date: 'HH:mm' }} - {{ item?.timeTo | date: 'HH:mm' }} </ng-container>
|
<div class="date">
|
||||||
<ng-container *ngSwitchCase="false">
|
<ng-container *ngIf="showDate">
|
||||||
Ganztägig
|
{{ item?.taskDate || item?.publicationDate | date }}
|
||||||
|
</ng-container>
|
||||||
|
<ng-container *ngIf="!showDate">
|
||||||
|
<ng-container [ngSwitch]="showTime$ | async">
|
||||||
|
<ng-container *ngSwitchCase="true"> {{ item?.timeFrom | date: 'HH:mm' }} - {{ item?.timeTo | date: 'HH:mm' }} </ng-container>
|
||||||
|
<ng-container *ngSwitchCase="false">
|
||||||
|
Ganztägig
|
||||||
|
</ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
<ng-container *ngIf="itemType$ | async; let itemType">
|
||||||
|
<div class="icon invisible" [class.show]="hasIcon$ | async">
|
||||||
|
<ui-icon icon="info" *ngIf="isInfoOrPreInfo$ | async" size="16px"></ui-icon>
|
||||||
|
<ui-icon icon="calendar" size="16px" *ngIf="isPreInfo$ | async"></ui-icon>
|
||||||
|
<ui-icon icon="chat" size="16px" *ngIf="hasComments$ | async"></ui-icon>
|
||||||
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
|
||||||
<ng-container *ngIf="itemType$ | async; let itemType">
|
|
||||||
<div class="indicator" *ngIf="(isTask$ | async) && !(hasUpdate$ | async)" [style.backgroundColor]="indicatorColor$ | async"></div>
|
|
||||||
<div class="icon" *ngIf="hasIcon$ | async">
|
|
||||||
<ui-icon icon="info" *ngIf="isInfoOrPreInfo$ | async" size="16px"></ui-icon>
|
|
||||||
<ui-icon icon="calendar" size="16px" *ngIf="isPreInfo$ | async"></ui-icon>
|
|
||||||
<ui-icon icon="chat" size="16px" *ngIf="hasComments$ | async"></ui-icon>
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ui-tshirt class="shirt-size" [effort]="item?.effort"></ui-tshirt>
|
<ui-tshirt class="shirt-size" [effort]="item?.effort"></ui-tshirt>
|
||||||
<div class="task-content">
|
<div class="task-content">
|
||||||
<div class="task-content-title">
|
<div class="task-content-title">
|
||||||
<b>{{ item?.title }}</b>
|
<b>{{ item?.title }}</b>
|
||||||
<ui-icon class="icon" icon="camera" [style.transform]="'rotateX(0deg)'" size="16px" *ngIf="item.requiresImageOnConfirmation"></ui-icon>
|
<ui-icon
|
||||||
<ui-icon class="icon" icon="attachment" size="16px" *ngIf="hasAttachments$ | async"></ui-icon>
|
class="icon"
|
||||||
<ui-icon class="icon icon-update-comment" icon="refresh" size="16px" *ngIf="showUpdateIcon$ | async"></ui-icon>
|
icon="camera"
|
||||||
|
[style.transform]="'rotateX(0deg)'"
|
||||||
|
size="16px"
|
||||||
|
*ngIf="item.requiresImageOnConfirmation"
|
||||||
|
></ui-icon>
|
||||||
|
<ui-icon class="icon" icon="attachment" size="16px" *ngIf="hasAttachments$ | async"></ui-icon>
|
||||||
|
<ui-icon class="icon icon-update-comment" icon="refresh" size="16px" *ngIf="showUpdateIcon$ | async"></ui-icon>
|
||||||
|
</div>
|
||||||
|
<div>{{ item?.category | replace: '##':' / ' }}</div>
|
||||||
|
<div class="task-content-text">{{ item?.text | stripHtmlTags | substr: 70 }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div>{{ item?.category | replace: '##':' / ' }}</div>
|
|
||||||
<div class="task-content-text">{{ item?.text | stripHtmlTags | substr: 70 }}</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,13 +1,20 @@
|
|||||||
:host {
|
.task-list-item-wrapper {
|
||||||
@apply flex flex-row px-4 py-3;
|
@apply flex flex-row pr-4 py-3 bg-white border-solid cursor-pointer;
|
||||||
|
border-radius: 5px 0px 5px 5px;
|
||||||
|
box-shadow: 0px 0px 10px rgba(220, 226, 233, 0.5);
|
||||||
|
|
||||||
.date {
|
.date {
|
||||||
@apply font-bold mr-2 self-start flex-shrink-0 flex-grow-0;
|
@apply font-bold mr-2 self-start flex-shrink-0 flex-grow-0 ml-4;
|
||||||
width: 112px !important;
|
width: 112px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.indicator {
|
.indicator {
|
||||||
@apply w-px-2 bg-gray-100 self-stretch mx-4;
|
@apply bg-gray-100 justify-self-stretch;
|
||||||
|
width: 6px;
|
||||||
|
margin-top: -13px;
|
||||||
|
margin-bottom: -13px;
|
||||||
|
border-top-left-radius: 5px;
|
||||||
|
border-bottom-left-radius: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
@@ -41,3 +48,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.show {
|
||||||
|
@apply visible;
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Component, ChangeDetectionStrategy, Input, OnChanges, SimpleChanges, Ho
|
|||||||
import { DomainTaskCalendarService } from '@domain/task-calendar';
|
import { DomainTaskCalendarService } from '@domain/task-calendar';
|
||||||
import { DisplayInfoDTO } from '@swagger/eis';
|
import { DisplayInfoDTO } from '@swagger/eis';
|
||||||
import { combineLatest, ReplaySubject } from 'rxjs';
|
import { combineLatest, ReplaySubject } from 'rxjs';
|
||||||
import { catchError, map, shareReplay, switchMap } from 'rxjs/operators';
|
import { map, shareReplay } from 'rxjs/operators';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'page-task-list-item',
|
selector: 'page-task-list-item',
|
||||||
|
|||||||
@@ -1,52 +1,55 @@
|
|||||||
<!-- <div class="task-list overdue-list" *ngIf="items | filterType: ['Task'] | filterStatus: ['Overdue']; let overdieItems"> -->
|
<!-- <div class="task-list overdue-list" *ngIf="items | filterType: ['Task'] | filterStatus: ['Overdue']; let overdieItems"> -->
|
||||||
<ng-container *ngIf="isToday$ | async">
|
<ui-scroll-container [loading]="fetching$ | async" [useLoadAnimation]="false">
|
||||||
<div class="task-list overdue-list" *ngIf="overdueItems$ | async; let overdieItems">
|
<ng-container *ngIf="isToday$ | async">
|
||||||
<div class="head-row">
|
<div class="task-list overdue-list" *ngIf="overdueItems$ | async; let overdieItems">
|
||||||
<h4>Überfällig</h4>
|
<div class="head-row">
|
||||||
<span class="muted"> {{ overdieItems?.length }} Aufgaben </span>
|
<div class="head-row-title">
|
||||||
</div>
|
<h4>Überfällig</h4>
|
||||||
<hr />
|
</div>
|
||||||
<ng-container *ngFor="let item of overdieItems">
|
<span class="muted"> {{ overdieItems?.length }} Aufgaben </span>
|
||||||
<page-task-list-item [item]="item" (click)="select.emit(item)"></page-task-list-item>
|
</div>
|
||||||
<hr />
|
<hr />
|
||||||
</ng-container>
|
<ng-container *ngFor="let item of overdieItems">
|
||||||
</div>
|
<page-task-list-item [item]="item" (click)="select.emit(item)"></page-task-list-item>
|
||||||
</ng-container>
|
<hr />
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<ng-container *ngIf="selectedItems$ | async; let itemsForSelectedDate">
|
<ng-container *ngIf="selectedItems$ | async; let itemsForSelectedDate">
|
||||||
<div class="task-list today-list" *ngIf="itemsForSelectedDate?.length">
|
<div class="task-list today-list" *ngIf="itemsForSelectedDate?.length">
|
||||||
<div class="head-row">
|
<div class="head-row">
|
||||||
<div class="head-row-title">
|
<div class="head-row-title">
|
||||||
<h4>{{ selected | date: 'EEEE' }}</h4>
|
<h4>{{ selected | date: 'EEEE' }}, {{ selected | date }}</h4>
|
||||||
<div class="muted">{{ selected | date }}</div>
|
</div>
|
||||||
|
<div class="head-row-info">
|
||||||
|
<button (click)="print()" type="button" class="cta-print">Drucken</button>
|
||||||
|
<span class="muted"> {{ itemsForSelectedDate?.length }} Aufgaben und Infos </span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="head-row-info">
|
|
||||||
<button (click)="print()" type="button" class="cta-print">Drucken</button>
|
|
||||||
<span class="muted"> {{ itemsForSelectedDate?.length }} Aufgaben und Infos </span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr />
|
|
||||||
<ng-container *ngFor="let item of itemsForSelectedDate">
|
|
||||||
<page-task-list-item [item]="item" (click)="select.emit(item)" [showDate]="false"></page-task-list-item>
|
|
||||||
<hr />
|
<hr />
|
||||||
</ng-container>
|
<ng-container *ngFor="let item of itemsForSelectedDate">
|
||||||
</div>
|
<page-task-list-item [item]="item" (click)="select.emit(item)" [showDate]="false"></page-task-list-item>
|
||||||
</ng-container>
|
<hr />
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<ng-container *ngIf="isToday$ | async">
|
<ng-container *ngIf="isToday$ | async">
|
||||||
<div class="task-list ongoing-list" *ngIf="ongoingItems$ | async; let ongoingItems">
|
<div class="task-list ongoing-list" *ngIf="ongoingItems$ | async; let ongoingItems">
|
||||||
<div class="head-row">
|
<div class="head-row">
|
||||||
<div class="head-row-title">
|
<div class="head-row-title">
|
||||||
<h4>Laufende Aufgaben</h4>
|
<h4>Laufende Aufgaben</h4>
|
||||||
|
</div>
|
||||||
|
<div class="head-row-info">
|
||||||
|
<span class="muted"> {{ ongoingItems?.length }} Aufgaben </span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="head-row-info">
|
|
||||||
<span class="muted"> {{ ongoingItems?.length }} Aufgaben </span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr />
|
|
||||||
<ng-container *ngFor="let item of ongoingItems">
|
|
||||||
<page-task-list-item [item]="item" (click)="select.emit(item)"></page-task-list-item>
|
|
||||||
<hr />
|
<hr />
|
||||||
</ng-container>
|
<ng-container *ngFor="let item of ongoingItems">
|
||||||
</div>
|
<page-task-list-item [item]="item" (click)="select.emit(item)"></page-task-list-item>
|
||||||
</ng-container>
|
<hr />
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
</ui-scroll-container>
|
||||||
|
|||||||
@@ -3,13 +3,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.task-list {
|
.task-list {
|
||||||
@apply pt-6;
|
@apply pt-4;
|
||||||
|
|
||||||
.head-row {
|
.head-row {
|
||||||
@apply flex flex-row justify-between items-baseline px-4 py-3;
|
@apply flex flex-row justify-between items-center px-5 py-3;
|
||||||
|
height: 53px;
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
border-radius: 5px 5px 0px 0px;
|
||||||
|
box-shadow: 0px 0px 10px rgba(220, 226, 233, 0.5);
|
||||||
|
|
||||||
.head-row-title {
|
.head-row-title {
|
||||||
@apply flex flex-row self-end;
|
@apply flex flex-row text-lg;
|
||||||
|
|
||||||
.muted {
|
.muted {
|
||||||
@apply ml-4 self-end;
|
@apply ml-4 self-end;
|
||||||
@@ -17,10 +21,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.head-row-info {
|
.head-row-info {
|
||||||
@apply flex flex-col text-right items-end;
|
@apply flex flex-col text-right justify-center items-end;
|
||||||
|
|
||||||
.cta-print {
|
.cta-print {
|
||||||
@apply border-none outline-none bg-transparent text-brand text-cta-l font-bold pr-0 mb-3;
|
@apply border-none outline-none bg-transparent text-brand text-cta-l font-bold pr-0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,3 +54,7 @@
|
|||||||
@apply opacity-30 line-through;
|
@apply opacity-30 line-through;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep ui-scroll-container .scroll-container {
|
||||||
|
@apply flex flex-col;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { DatePipe } from '@angular/common';
|
import { DatePipe } from '@angular/common';
|
||||||
import { Component, ChangeDetectionStrategy, Input, EventEmitter, Output } from '@angular/core';
|
import { Component, ChangeDetectionStrategy, Input, EventEmitter, Output } from '@angular/core';
|
||||||
import { DomainPrinterService } from '@domain/printer';
|
import { DomainPrinterService } from '@domain/printer';
|
||||||
import { DomainTaskCalendarService, ProcessingStatusList } from '@domain/task-calendar';
|
import { DomainTaskCalendarService } from '@domain/task-calendar';
|
||||||
import { PrintModalComponent, PrintModalData } from '@modal/printer';
|
import { PrintModalComponent, PrintModalData } from '@modal/printer';
|
||||||
import { DisplayInfoDTO } from '@swagger/eis';
|
import { DisplayInfoDTO } from '@swagger/eis';
|
||||||
import { DateAdapter } from '@ui/common';
|
import { DateAdapter } from '@ui/common';
|
||||||
@@ -43,6 +43,19 @@ export class TaskListComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* @internal */
|
||||||
|
fetching$ = new BehaviorSubject<boolean>(true);
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
get fetching() {
|
||||||
|
return this.fetching$.value;
|
||||||
|
}
|
||||||
|
set fetching(value) {
|
||||||
|
if (this.fetching !== value) {
|
||||||
|
this.fetching$.next(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
isToday$ = this.selected$.pipe(map((selected) => this.dateAdapter.equals({ first: selected, second: this.today, precision: 'day' })));
|
isToday$ = this.selected$.pipe(map((selected) => this.dateAdapter.equals({ first: selected, second: this.today, precision: 'day' })));
|
||||||
|
|
||||||
overdueItems$ = combineLatest([this.items$, this.selected$]).pipe(
|
overdueItems$ = combineLatest([this.items$, this.selected$]).pipe(
|
||||||
@@ -65,7 +78,7 @@ export class TaskListComponent {
|
|||||||
: item
|
: item
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
map((list) => list.sort(this.moveRemovedToEnd.bind(this)))
|
map((list) => list.sort((a, b) => this.domainTaskCalendarService.moveRemovedToEnd(a, b)))
|
||||||
);
|
);
|
||||||
|
|
||||||
ongoingItems$ = combineLatest([this.items$, this.selected$]).pipe(
|
ongoingItems$ = combineLatest([this.items$, this.selected$]).pipe(
|
||||||
@@ -95,8 +108,8 @@ export class TaskListComponent {
|
|||||||
: item
|
: item
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
map((list) => this.sort(list, ['Overdue', 'InProcess', 'Approved', 'Completed', 'Removed'])),
|
map((list) => this.domainTaskCalendarService.sort(list, ['Overdue', 'InProcess', 'Approved', 'Completed', 'Removed'])),
|
||||||
map((list) => list.sort(this.moveRemovedToEnd.bind(this)))
|
map((list) => list.sort(this.domainTaskCalendarService.moveRemovedToEnd))
|
||||||
);
|
);
|
||||||
|
|
||||||
selectedItems$ = combineLatest([this.items$, this.selected$]).pipe(
|
selectedItems$ = combineLatest([this.items$, this.selected$]).pipe(
|
||||||
@@ -116,8 +129,8 @@ export class TaskListComponent {
|
|||||||
)
|
)
|
||||||
),
|
),
|
||||||
// Sortierung der aufgaben nach Rot => Gelb => Grau => Grün
|
// Sortierung der aufgaben nach Rot => Gelb => Grau => Grün
|
||||||
map((list) => this.sort(list, ['Overdue', 'InProcess', 'Approved', 'Completed', 'Removed'])),
|
map((list) => this.domainTaskCalendarService.sort(list, ['Overdue', 'InProcess', 'Approved', 'Completed', 'Removed'])),
|
||||||
map((list) => list.sort(this.moveRemovedToEnd.bind(this)))
|
map((list) => list.sort((a, b) => this.domainTaskCalendarService.moveRemovedToEnd(a, b)))
|
||||||
);
|
);
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
@@ -131,76 +144,6 @@ export class TaskListComponent {
|
|||||||
private domainPrinterService: DomainPrinterService
|
private domainPrinterService: DomainPrinterService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
moveRemovedToEnd(a: DisplayInfoDTO, b: DisplayInfoDTO) {
|
|
||||||
const statusA = this.domainTaskCalendarService.getProcessingStatusList(a)?.includes('Removed');
|
|
||||||
const statusB = this.domainTaskCalendarService.getProcessingStatusList(b)?.includes('Removed');
|
|
||||||
|
|
||||||
if (statusA && statusB) {
|
|
||||||
return 0;
|
|
||||||
} else if (statusA && !statusB) {
|
|
||||||
return 1;
|
|
||||||
} else if (!statusA && statusB) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an Array of DisplayInfoDTO, sorted by ProcessingStatus
|
|
||||||
* Ignores Overdue if Task is already Completed
|
|
||||||
* Compared DisploayInfoDTO is of Type Task and Info Or PreInfo then sort by Type
|
|
||||||
* @param items DisplayInfoDTO Array to sort
|
|
||||||
* @param order Processing Status Order
|
|
||||||
* @returns DisplayInfoDTO Array ordered by Processing Status anf Type
|
|
||||||
*/
|
|
||||||
sort(items: DisplayInfoDTO[], order: ProcessingStatusList) {
|
|
||||||
let result = [...items];
|
|
||||||
const reversedOrder = [...order].reverse();
|
|
||||||
|
|
||||||
for (const status of reversedOrder) {
|
|
||||||
result = result?.sort((a, b) => {
|
|
||||||
const statusA = this.domainTaskCalendarService.getProcessingStatusList(a);
|
|
||||||
const statusB = this.domainTaskCalendarService.getProcessingStatusList(b);
|
|
||||||
|
|
||||||
// Ignore Overdue when it is already Completed
|
|
||||||
if (status === 'Overdue' && statusA.includes('Completed')) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const aHasStatus = statusA.includes(status);
|
|
||||||
const bHasStatus = statusB.includes(status);
|
|
||||||
|
|
||||||
if (aHasStatus && bHasStatus) {
|
|
||||||
// If it has the same ProcessingStatus then Sort by Type
|
|
||||||
const aType = this.domainTaskCalendarService.getInfoType(a);
|
|
||||||
const bType = this.domainTaskCalendarService.getInfoType(b);
|
|
||||||
if (aType !== bType) {
|
|
||||||
if (aType === 'Info' || aType === 'PreInfo') {
|
|
||||||
return -1;
|
|
||||||
} else {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (statusB.includes('Completed')) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
} else if (aHasStatus && !bHasStatus) {
|
|
||||||
return -1;
|
|
||||||
} else if (!aHasStatus && bHasStatus) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
async print() {
|
async print() {
|
||||||
let displayInfos = await this.selectedItems$.pipe(first()).toPromise();
|
let displayInfos = await this.selectedItems$.pipe(first()).toPromise();
|
||||||
displayInfos = displayInfos.filter((di) => !this.domainTaskCalendarService.getProcessingStatusList(di).includes('Completed'));
|
displayInfos = displayInfos.filter((di) => !this.domainTaskCalendarService.getProcessingStatusList(di).includes('Completed'));
|
||||||
|
|||||||
@@ -7,10 +7,13 @@ import { FilterStatusPipe, FilterTypePipe } from './pipes';
|
|||||||
import { UiCommonModule } from '@ui/common';
|
import { UiCommonModule } from '@ui/common';
|
||||||
import { UiTshirtModule } from '@ui/tshirt';
|
import { UiTshirtModule } from '@ui/tshirt';
|
||||||
import { UiIconModule } from '@ui/icon';
|
import { UiIconModule } from '@ui/icon';
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
|
import { UiSpinnerModule } from '@ui/spinner';
|
||||||
|
import { UiScrollContainerModule } from '@ui/scroll-container';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [CommonModule, UiCommonModule, UiTshirtModule, UiIconModule],
|
imports: [CommonModule, UiCommonModule, UiSpinnerModule, UiTshirtModule, UiIconModule, RouterModule, UiScrollContainerModule],
|
||||||
exports: [TaskListComponent],
|
exports: [TaskListComponent, TaskListItemComponent],
|
||||||
declarations: [TaskListComponent, TaskListItemComponent, FilterStatusPipe, FilterTypePipe],
|
declarations: [TaskListComponent, TaskListItemComponent, FilterStatusPipe, FilterTypePipe],
|
||||||
})
|
})
|
||||||
export class TaskListModule {}
|
export class TaskListModule {}
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<ui-form-field>
|
||||||
|
<input #searchInput uiInput [(ngModel)]="search" (keydown.enter)="searchTasks()" class="w-full" placeholder="Suchen" type="text" />
|
||||||
|
</ui-form-field>
|
||||||
|
<button
|
||||||
|
*ngIf="!(searchActive$ | async); else clearButtonTemplate"
|
||||||
|
class="search"
|
||||||
|
(click)="searchTasks()"
|
||||||
|
[disabled]="searchDisabled$ | async"
|
||||||
|
>
|
||||||
|
<ui-icon icon="search" size="24px"></ui-icon>
|
||||||
|
</button>
|
||||||
|
<ng-template #clearButtonTemplate>
|
||||||
|
<button class="clear" (click)="clearSearch()">
|
||||||
|
<ui-icon icon="close" size="15px"></ui-icon>
|
||||||
|
</button>
|
||||||
|
</ng-template>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
:host {
|
||||||
|
@apply grid relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
@apply py-4 px-4 outline-none h-14 font-bold text-lg;
|
||||||
|
box-shadow: 0px 6px 24px rgba(206, 212, 219, 0.8);
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.search {
|
||||||
|
@apply grid items-center justify-center bg-brand text-white px-2 py-2 rounded-lg -ml-2 w-14 h-14 absolute;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.clear {
|
||||||
|
@apply grid items-center justify-center px-2 py-2 rounded-lg -ml-2 w-14 h-14 absolute;
|
||||||
|
color: #1f466c;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:disabled {
|
||||||
|
@apply bg-disabled-branch cursor-not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
::placeholder {
|
||||||
|
@apply font-bold text-lg;
|
||||||
|
color: #596470;
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
import { Component, OnInit, OnDestroy, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
|
||||||
|
import { ActivatedRoute, Params, Router } from '@angular/router';
|
||||||
|
import { isEqual } from 'lodash';
|
||||||
|
import { BehaviorSubject, combineLatest, Subject } from 'rxjs';
|
||||||
|
import { map, shareReplay, takeUntil, first } from 'rxjs/operators';
|
||||||
|
import { TaskCalendarStore } from '../../task-calendar.store';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'page-task-searchbar',
|
||||||
|
templateUrl: 'task-searchbar.component.html',
|
||||||
|
styleUrls: ['task-searchbar.component.scss'],
|
||||||
|
})
|
||||||
|
export class TaskSearchbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
|
private _onDestroy$ = new Subject();
|
||||||
|
|
||||||
|
@ViewChild('searchInput', { static: true })
|
||||||
|
searchInput: ElementRef;
|
||||||
|
|
||||||
|
filterActive$ = new BehaviorSubject<boolean>(false);
|
||||||
|
|
||||||
|
searchActive$ = new BehaviorSubject<boolean>(false);
|
||||||
|
searchDisabled$ = new BehaviorSubject<boolean>(true);
|
||||||
|
|
||||||
|
search$ = new BehaviorSubject<string>('');
|
||||||
|
|
||||||
|
filter$ = this.taskCalendarStore.selectFilter;
|
||||||
|
|
||||||
|
get search() {
|
||||||
|
return this.search$.value;
|
||||||
|
}
|
||||||
|
set search(search: string) {
|
||||||
|
this.search$.next(search);
|
||||||
|
this.searchDisabled$.next(search?.length < 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(private taskCalendarStore: TaskCalendarStore, private _router: Router, private _activatedRoute: ActivatedRoute) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this._activatedRoute.queryParams.pipe(takeUntil(this._onDestroy$)).subscribe(async (queryParams) => {
|
||||||
|
const filter = await this.taskCalendarStore.selectFilter.pipe(first()).toPromise();
|
||||||
|
const search = filter?.input?.find((_) => true)?.input.find((_) => true)?.value || queryParams?.main_qs || '';
|
||||||
|
|
||||||
|
this.search$.next(search);
|
||||||
|
this.searchActive$.next(!!search);
|
||||||
|
this.focusSearch();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit(): void {
|
||||||
|
this.focusSearch();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this._onDestroy$.next();
|
||||||
|
this._onDestroy$.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
focusSearch() {
|
||||||
|
setTimeout(() => this.searchInput?.nativeElement?.focus(), 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
async clearSearch() {
|
||||||
|
const filter = await this.filter$.pipe(first()).toPromise();
|
||||||
|
filter?.input
|
||||||
|
?.find((_) => true)
|
||||||
|
?.input.find((_) => true)
|
||||||
|
?.setValue('');
|
||||||
|
this.taskCalendarStore.setFilter({ filters: filter });
|
||||||
|
|
||||||
|
this.search = '';
|
||||||
|
this.searchActive$.next(false);
|
||||||
|
this.focusSearch();
|
||||||
|
}
|
||||||
|
|
||||||
|
async searchTasks() {
|
||||||
|
const filter = await this.filter$.pipe(first()).toPromise();
|
||||||
|
|
||||||
|
filter?.input
|
||||||
|
?.find((_) => true)
|
||||||
|
?.input.find((_) => true)
|
||||||
|
?.setValue(this.search);
|
||||||
|
this.taskCalendarStore.setFilter({ filters: filter });
|
||||||
|
|
||||||
|
if (this.search?.length >= 3) {
|
||||||
|
this.navigate('/filiale/task-calendar/search', { ...filter?.getQueryParams() });
|
||||||
|
|
||||||
|
// ActivatedRouteChange wird nicht aufgerufen, wenn sich die URL nicht verändert. In dem Fall erneute Suche ausführen
|
||||||
|
if (isEqual(filter.getQueryParams(), this._activatedRoute.snapshot.queryParams)) {
|
||||||
|
this.taskCalendarStore.search({ clear: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
navigate(uri: string, queryParams?: Params) {
|
||||||
|
this._router.navigate([uri], { queryParams });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { UiFormFieldModule } from '@paragondata/ngx-ui/form-field';
|
||||||
|
import { UiIconModule } from '@ui/icon';
|
||||||
|
|
||||||
|
import { TaskSearchbarComponent } from './task-searchbar.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule, UiFormFieldModule, FormsModule, UiIconModule],
|
||||||
|
exports: [TaskSearchbarComponent],
|
||||||
|
declarations: [TaskSearchbarComponent],
|
||||||
|
providers: [],
|
||||||
|
})
|
||||||
|
export class TaskSearchbarModule {}
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
import { ChangeDetectionStrategy, Component, EventEmitter, Output } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, EventEmitter, OnDestroy, OnInit, Output } from '@angular/core';
|
||||||
|
import { ActivatedRoute, Params, Router } from '@angular/router';
|
||||||
import { UiFilter } from '@ui/filter';
|
import { UiFilter } from '@ui/filter';
|
||||||
|
import { isEqual } from 'lodash';
|
||||||
|
import { BehaviorSubject, Subject } from 'rxjs';
|
||||||
|
import { first, takeUntil } from 'rxjs/operators';
|
||||||
import { TaskCalendarStore } from '../../task-calendar.store';
|
import { TaskCalendarStore } from '../../task-calendar.store';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -8,24 +12,66 @@ import { TaskCalendarStore } from '../../task-calendar.store';
|
|||||||
styleUrls: ['task-calendar-filter.component.scss'],
|
styleUrls: ['task-calendar-filter.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class TaskCalendarFilterComponent {
|
export class TaskCalendarFilterComponent implements OnInit, OnDestroy {
|
||||||
|
private _onDestroy$ = new Subject();
|
||||||
@Output() exitFilter = new EventEmitter<void>();
|
@Output() exitFilter = new EventEmitter<void>();
|
||||||
|
|
||||||
filter$ = this.taskCalendarStore.selectFilter;
|
filter$ = new BehaviorSubject<UiFilter>(undefined);
|
||||||
|
|
||||||
fetching$ = this.taskCalendarStore.selectFetching;
|
fetching$ = this.taskCalendarStore.selectFetching;
|
||||||
|
|
||||||
message$ = this.taskCalendarStore.selectMessage;
|
message$ = this.taskCalendarStore.selectMessage;
|
||||||
|
|
||||||
constructor(private taskCalendarStore: TaskCalendarStore) {}
|
constructor(private taskCalendarStore: TaskCalendarStore, private _router: Router, private _activatedRoute: ActivatedRoute) {}
|
||||||
|
|
||||||
|
private _initFilter(filter: UiFilter) {
|
||||||
|
this.filter$.next(UiFilter.create(filter));
|
||||||
|
}
|
||||||
|
|
||||||
|
async ngOnInit() {
|
||||||
|
this.taskCalendarStore.selectFilter.pipe(takeUntil(this._onDestroy$)).subscribe((filter) => {
|
||||||
|
this._initFilter(filter);
|
||||||
|
});
|
||||||
|
|
||||||
|
const filter = await this.taskCalendarStore.selectFilter.pipe(first()).toPromise();
|
||||||
|
this._initFilter(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this._onDestroy$.next();
|
||||||
|
this._onDestroy$.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
async applyFilter(filters: UiFilter) {
|
||||||
|
const queryParams = { ...filters?.getQueryParams() };
|
||||||
|
|
||||||
applyFilter(filters: UiFilter) {
|
|
||||||
this.taskCalendarStore.setFilter({ filters });
|
this.taskCalendarStore.setFilter({ filters });
|
||||||
this.taskCalendarStore.loadItems();
|
this.taskCalendarStore.setMessage({ message: '' });
|
||||||
this.exitFilter.emit();
|
|
||||||
|
const search = this.getSearch(filters);
|
||||||
|
if (search?.length >= 3 || search === '') {
|
||||||
|
this.navigate('/filiale/task-calendar/search', queryParams);
|
||||||
|
|
||||||
|
// ActivatedRouteChange wird nicht aufgerufen, wenn sich die URL nicht verändert. In dem Fall erneute Suche ausführen
|
||||||
|
if (isEqual(filters.getQueryParams(), this._activatedRoute.snapshot.queryParams)) {
|
||||||
|
this.taskCalendarStore.search({ clear: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.exitFilter.emit();
|
||||||
|
} else {
|
||||||
|
this.taskCalendarStore.setMessage({ message: 'Mindestens 3 Zeichen' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getSearch(filter: UiFilter) {
|
||||||
|
return filter?.input?.find((_) => true)?.input?.find((_) => true)?.value || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
resetFilter() {
|
resetFilter() {
|
||||||
this.taskCalendarStore.loadFilter();
|
this.taskCalendarStore.loadFilter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
navigate(uri: string, queryParams?: Params) {
|
||||||
|
this._router.navigate([uri], { queryParams });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<h1>{{ info?.title }}</h1>
|
<h1>{{ info?.title }}</h1>
|
||||||
<ui-icon *ngIf="showUpdateIcon$ | async" icon="refresh" size="22px"></ui-icon>
|
<ui-icon *ngIf="showUpdateIcon$ | async" icon="refresh" size="22px"></ui-icon>
|
||||||
</div>
|
</div>
|
||||||
<page-task-info [info]="info"></page-task-info>
|
<page-task-info [info]="info" (changed)="changed()"></page-task-info>
|
||||||
|
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<button *ngIf="showOpenSuccessorCta$ | async" class="btn-cta" type="button" (click)="close(info.successor?.id)">
|
<button *ngIf="showOpenSuccessorCta$ | async" class="btn-cta" type="button" (click)="close(info.successor?.id)">
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import { map, shareReplay } from 'rxjs/operators';
|
|||||||
})
|
})
|
||||||
export class InfoModalComponent {
|
export class InfoModalComponent {
|
||||||
info: DisplayInfoDTO;
|
info: DisplayInfoDTO;
|
||||||
|
|
||||||
info$ = new ReplaySubject<DisplayInfoDTO>();
|
info$ = new ReplaySubject<DisplayInfoDTO>();
|
||||||
|
|
||||||
processingStatus$ = this.info$.pipe(
|
processingStatus$ = this.info$.pipe(
|
||||||
@@ -42,6 +41,10 @@ export class InfoModalComponent {
|
|||||||
this.info$.next(this.info);
|
this.info$.next(this.info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
changed() {
|
||||||
|
this.modalRef.markChanged();
|
||||||
|
}
|
||||||
|
|
||||||
close(successorId: number = undefined) {
|
close(successorId: number = undefined) {
|
||||||
this.modalRef.close({ successorId });
|
this.modalRef.close({ successorId });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<h1>{{ info?.title }}</h1>
|
<h1>{{ info?.title }}</h1>
|
||||||
<ui-icon *ngIf="showUpdateIcon$ | async" icon="refresh" size="22px"></ui-icon>
|
<ui-icon *ngIf="showUpdateIcon$ | async" icon="refresh" size="22px"></ui-icon>
|
||||||
</div>
|
</div>
|
||||||
<page-task-info [info]="info" showTaskDate="true"></page-task-info>
|
<page-task-info [info]="info" showTaskDate="true" (changed)="changed()"></page-task-info>
|
||||||
|
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<button *ngIf="showOpenSuccessorCta$ | async" class="btn-cta" type="button" (click)="close(info.successor?.id)">
|
<button *ngIf="showOpenSuccessorCta$ | async" class="btn-cta" type="button" (click)="close(info.successor?.id)">
|
||||||
|
|||||||
@@ -42,6 +42,10 @@ export class PreInfoModalComponent {
|
|||||||
this.info$.next(this.info);
|
this.info$.next(this.info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
changed() {
|
||||||
|
this.modalRef.markChanged();
|
||||||
|
}
|
||||||
|
|
||||||
close(successorId: number = undefined) {
|
close(successorId: number = undefined) {
|
||||||
this.modalRef.close({ successorId });
|
this.modalRef.close({ successorId });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<page-task-info [info]="info" showTaskDate="true"></page-task-info>
|
<page-task-info [info]="info" showTaskDate="true" (changed)="changed()"></page-task-info>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="camera-preview" *ngIf="cameraPreview$ | async; let cameraPreview">
|
<div class="camera-preview" *ngIf="cameraPreview$ | async; let cameraPreview">
|
||||||
|
|||||||
@@ -122,19 +122,26 @@ export class TaskModalComponent {
|
|||||||
this.modalRef.close({ successorId });
|
this.modalRef.close({ successorId });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
changed() {
|
||||||
|
this.modalRef.markChanged();
|
||||||
|
}
|
||||||
|
|
||||||
async startEdit() {
|
async startEdit() {
|
||||||
await this.domainTaskCalendarService.setToEdit({ infoId: this.info.id }).toPromise();
|
await this.domainTaskCalendarService.setToEdit({ infoId: this.info.id }).toPromise();
|
||||||
this.reloadInfo();
|
this.reloadInfo();
|
||||||
|
this.changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
async completeEdit() {
|
async completeEdit() {
|
||||||
await this.domainTaskCalendarService.complete({ infoId: this.info.id }).toPromise();
|
await this.domainTaskCalendarService.complete({ infoId: this.info.id }).toPromise();
|
||||||
|
this.changed();
|
||||||
this.close();
|
this.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
async edit() {
|
async edit() {
|
||||||
await this.domainTaskCalendarService.setToEdit({ infoId: this.info.id }).toPromise();
|
await this.domainTaskCalendarService.setToEdit({ infoId: this.info.id }).toPromise();
|
||||||
this.reloadInfo();
|
this.reloadInfo();
|
||||||
|
this.changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
reloadInfo() {
|
reloadInfo() {
|
||||||
@@ -159,6 +166,7 @@ export class TaskModalComponent {
|
|||||||
if (result.data === '') {
|
if (result.data === '') {
|
||||||
this.captureInput?.nativeElement?.click();
|
this.captureInput?.nativeElement?.click();
|
||||||
} else if (result.data?.length > 0) {
|
} else if (result.data?.length > 0) {
|
||||||
|
this.changed();
|
||||||
this.close();
|
this.close();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { RouterModule, Routes } from '@angular/router';
|
|||||||
import { PageTaskCalendarComponent } from './page-task-calendar.component';
|
import { PageTaskCalendarComponent } from './page-task-calendar.component';
|
||||||
import { CalendarComponent } from './pages/calendar/calendar.component';
|
import { CalendarComponent } from './pages/calendar/calendar.component';
|
||||||
import { CalendarModule } from './pages/calendar/calendar.module';
|
import { CalendarModule } from './pages/calendar/calendar.module';
|
||||||
|
import { TaskSearchComponent } from './pages/task-search';
|
||||||
import { TasksComponent } from './pages/tasks/tasks.component';
|
import { TasksComponent } from './pages/tasks/tasks.component';
|
||||||
import { TasksModule } from './pages/tasks/tasks.module';
|
import { TasksModule } from './pages/tasks/tasks.module';
|
||||||
|
|
||||||
@@ -13,7 +14,8 @@ const routes: Routes = [
|
|||||||
children: [
|
children: [
|
||||||
{ path: 'calendar', component: CalendarComponent },
|
{ path: 'calendar', component: CalendarComponent },
|
||||||
{ path: 'tasks', component: TasksComponent },
|
{ path: 'tasks', component: TasksComponent },
|
||||||
{ path: '', pathMatch: 'full', redirectTo: 'calendar' },
|
{ path: 'search', component: TaskSearchComponent },
|
||||||
|
{ path: '', pathMatch: 'full', redirectTo: 'tasks' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,21 +1,16 @@
|
|||||||
<shell-breadcrumb [key]="taskCalendarKey" [includesTags]="['task-calendar']"></shell-breadcrumb>
|
<div class="pt-2">
|
||||||
|
<shell-breadcrumb [key]="taskCalendarKey" [includesTags]="['task-calendar']"></shell-breadcrumb>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button class="filter" [class.active]="hasFilter$ | async" (click)="filterActive$.next(true); filterOverlay.open()">
|
<div class="content-header">
|
||||||
<ui-icon size="20px" icon="filter_alit"></ui-icon>
|
<page-task-searchbar></page-task-searchbar>
|
||||||
<span class="label">Filter</span>
|
<button class="filter" [class.active]="hasFilter$ | async" (click)="filterActive$.next(true); filterOverlay.open()">
|
||||||
</button>
|
<ui-icon size="16px" icon="filter_new"></ui-icon>
|
||||||
|
<span class="label">Filter</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="content-container">
|
<div class="content-container">
|
||||||
<div class="switch-group">
|
|
||||||
<a class="calendar-switch" [routerLink]="['./calendar']" routerLinkActive="active">
|
|
||||||
<ui-icon icon="calendar" size="16px"></ui-icon>
|
|
||||||
<span>Kalenderansicht</span>
|
|
||||||
</a>
|
|
||||||
<a class="calendar-switch" [routerLink]="['./tasks']" routerLinkActive="active">
|
|
||||||
<ui-icon icon="tasks" size="16px"></ui-icon>
|
|
||||||
<span>Listenansicht</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,27 @@
|
|||||||
:host {
|
:host {
|
||||||
@apply flex flex-col w-full box-content relative;
|
@apply flex flex-col w-full box-content relative;
|
||||||
|
height: calc(100vh - 14rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
shell-breadcrumb {
|
shell-breadcrumb {
|
||||||
@apply sticky z-sticky top-0 py-4;
|
@apply sticky z-sticky top-0 h-12 bg-white p-0;
|
||||||
|
box-shadow: 0px 0px 10px rgba(220, 226, 233, 0.5);
|
||||||
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter {
|
.filter {
|
||||||
@apply absolute font-sans flex items-center font-bold bg-gray-400 border-0 text-regular -top-12 right-0 py-px-8 px-px-15 rounded-filter justify-center z-sticky;
|
@apply font-sans flex items-center font-bold text-lg py-2 px-4 justify-center ml-3 h-14;
|
||||||
|
width: 108px;
|
||||||
right: 0;
|
background-color: #aeb7c1;
|
||||||
top: 10px;
|
box-shadow: 0px 0px 10px rgba(220, 226, 233, 0.5);
|
||||||
min-width: 106px;
|
border-radius: 5px;
|
||||||
|
|
||||||
.label {
|
.label {
|
||||||
@apply ml-px-5;
|
@apply ml-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
ui-icon {
|
||||||
|
width: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
@@ -22,35 +29,27 @@ shell-breadcrumb {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-container {
|
.content-header {
|
||||||
max-height: calc(100vh - 267px);
|
@apply grid items-center mt-4 shadow-input gap-1;
|
||||||
overflow: scroll;
|
height: 56px;
|
||||||
@apply bg-white rounded-card;
|
grid-template-columns: 1fr 120px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.switch-group {
|
.content-container {
|
||||||
@apply flex flex-row justify-center my-9;
|
@apply overflow-auto;
|
||||||
|
max-height: calc(100vh - 267px);
|
||||||
a.calendar-switch {
|
border-radius: 0px 0px 5px 5px;
|
||||||
@apply flex flex-row items-center px-7 py-2 no-underline text-black bg-munsell;
|
|
||||||
&:first-child {
|
|
||||||
@apply rounded-l-card;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
@apply rounded-r-card;
|
|
||||||
}
|
|
||||||
ui-icon {
|
|
||||||
@apply mr-3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
a.calendar-switch.active,
|
|
||||||
a.calendar-switch:active {
|
|
||||||
@apply text-white bg-active-branch;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
:host ::ng-deep ui-calendar .navigation .title {
|
:host ::ng-deep ui-calendar .navigation .title {
|
||||||
margin-left: -140px;
|
margin-left: -140px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep shell-breadcrumb .link-breadcrumb a {
|
||||||
|
color: #596470 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep shell-breadcrumb .link-back {
|
||||||
|
@apply top-3 pl-4;
|
||||||
|
color: #596470 !important;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component, ViewChild, ElementRef } from '@angular/core';
|
||||||
import { Config } from '@core/config';
|
import { Config } from '@core/config';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash';
|
||||||
import { BehaviorSubject, combineLatest } from 'rxjs';
|
import { BehaviorSubject, combineLatest } from 'rxjs';
|
||||||
@@ -12,6 +12,9 @@ import { TaskCalendarStore } from './task-calendar.store';
|
|||||||
providers: [TaskCalendarStore],
|
providers: [TaskCalendarStore],
|
||||||
})
|
})
|
||||||
export class PageTaskCalendarComponent {
|
export class PageTaskCalendarComponent {
|
||||||
|
@ViewChild('searchInput', { static: true })
|
||||||
|
searchInput: ElementRef;
|
||||||
|
|
||||||
filterActive$ = new BehaviorSubject<boolean>(false);
|
filterActive$ = new BehaviorSubject<boolean>(false);
|
||||||
taskCalendarKey = this._config.get('process.ids.taskCalendar');
|
taskCalendarKey = this._config.get('process.ids.taskCalendar');
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { ShellBreadcrumbModule } from '@shell/breadcrumb';
|
|||||||
import { ShellFilterOverlayModule } from '@shell/filter-overlay';
|
import { ShellFilterOverlayModule } from '@shell/filter-overlay';
|
||||||
import { UiFilterNextModule } from '@ui/filter';
|
import { UiFilterNextModule } from '@ui/filter';
|
||||||
import { UiIconModule } from '@ui/icon';
|
import { UiIconModule } from '@ui/icon';
|
||||||
|
import { TaskSearchbarModule } from './components/task-searchbar/task-searchbar.module';
|
||||||
import { TaskCalendarFilterComponent } from './containers/task-calendar-filter/task-calendar-filter.component';
|
import { TaskCalendarFilterComponent } from './containers/task-calendar-filter/task-calendar-filter.component';
|
||||||
import { ModalsModule } from './modals/modals.module';
|
import { ModalsModule } from './modals/modals.module';
|
||||||
import { PageTaskCalendarRoutingModule } from './page-task-calendar-routing.module';
|
import { PageTaskCalendarRoutingModule } from './page-task-calendar-routing.module';
|
||||||
@@ -19,6 +20,7 @@ import { PageTaskCalendarComponent } from './page-task-calendar.component';
|
|||||||
ModalsModule,
|
ModalsModule,
|
||||||
UiFilterNextModule,
|
UiFilterNextModule,
|
||||||
ShellFilterOverlayModule,
|
ShellFilterOverlayModule,
|
||||||
|
TaskSearchbarModule,
|
||||||
],
|
],
|
||||||
exports: [PageTaskCalendarComponent],
|
exports: [PageTaskCalendarComponent],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
<ui-calendar
|
<div class="bg-white pb-8 rounded-lg">
|
||||||
mode="month"
|
<ui-calendar
|
||||||
[selected]="selectedDate$ | async"
|
mode="month"
|
||||||
[displayed]="displayedDate$ | async"
|
[selected]="selectedDate$ | async"
|
||||||
[minDate]="minDate"
|
[displayed]="displayedDate$ | async"
|
||||||
[maxDate]="maxDate"
|
[minDate]="minDate"
|
||||||
[indicators]="indicators$ | async"
|
[maxDate]="maxDate"
|
||||||
(selectedChange)="setSelectedDate($event)"
|
[indicators]="indicators$ | async"
|
||||||
(displayedChange)="setDisplayedDate($event)"
|
(selectedChange)="setSelectedDate($event)"
|
||||||
></ui-calendar>
|
(displayedChange)="setDisplayedDate($event)"
|
||||||
|
></ui-calendar>
|
||||||
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// start:ng42.barrel
|
||||||
|
export * from './task-search.component';
|
||||||
|
export * from './task-search.module';
|
||||||
|
// end:ng42.barrel
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<ng-container *ngIf="!(showEmptyMessage$ | async); else emptyMessage">
|
||||||
|
<ui-scroll-container
|
||||||
|
class="mt-2"
|
||||||
|
skeletonTemplate="task-calendar"
|
||||||
|
[loading]="fetching$ | async"
|
||||||
|
(reachEnd)="search()"
|
||||||
|
[itemLength]="searchResultsLength$ | async"
|
||||||
|
[deltaEnd]="250"
|
||||||
|
>
|
||||||
|
<div class="task-list search-list" *ngFor="let group of displayItems$ | async" [id]="group.group">
|
||||||
|
<div class="head-row">
|
||||||
|
<div class="head-row-title">
|
||||||
|
<h4>{{ group.group | date: 'EEEE' }}, {{ group.group | date }}</h4>
|
||||||
|
</div>
|
||||||
|
<div class="head-row-info">
|
||||||
|
<button (click)="print(group.group)" type="button" class="cta-print">Drucken</button>
|
||||||
|
<span class="muted"> {{ group.items?.length }} Aufgaben und Infos </span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<ng-container *ngFor="let item of group?.items">
|
||||||
|
<page-task-list-item [item]="item" (click)="open(item)"></page-task-list-item>
|
||||||
|
<hr />
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
</ui-scroll-container>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-template #emptyMessage>
|
||||||
|
<div class="empty-message">
|
||||||
|
Keine Suchergebnisse
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<div class="actions">
|
||||||
|
<a class="cta-back cta-action-primary" [routerLink]="['/filiale/task-calendar/tasks']">
|
||||||
|
Zurück zur Übersicht
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
:host {
|
||||||
|
@apply flex flex-col box-border;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-list {
|
||||||
|
@apply pt-4;
|
||||||
|
|
||||||
|
.head-row {
|
||||||
|
@apply flex flex-row justify-between items-center px-5 py-3;
|
||||||
|
height: 53px;
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
border-radius: 5px 5px 0px 0px;
|
||||||
|
box-shadow: 0px 0px 10px rgba(220, 226, 233, 0.5);
|
||||||
|
|
||||||
|
.head-row-title {
|
||||||
|
@apply flex flex-row text-lg;
|
||||||
|
|
||||||
|
.muted {
|
||||||
|
@apply ml-4 self-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.head-row-info {
|
||||||
|
@apply flex flex-col text-right justify-center items-end;
|
||||||
|
|
||||||
|
.cta-print {
|
||||||
|
@apply border-none outline-none bg-transparent text-brand text-cta-l font-bold pr-0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
@apply m-0 font-bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.muted {
|
||||||
|
@apply text-active-branch font-semibold text-sm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-message {
|
||||||
|
@apply bg-white text-center font-semibold text-inactive-customer py-10 rounded-card;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep ui-scroll-container .scroll-container {
|
||||||
|
@apply flex flex-col;
|
||||||
|
max-height: calc(100vh - 23rem) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-list ::ng-deep page-task-list-item.Removed {
|
||||||
|
.date,
|
||||||
|
.shirt-size,
|
||||||
|
.task-content {
|
||||||
|
@apply opacity-30 line-through;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
@apply fixed bottom-28 inline-grid grid-flow-col gap-7;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
|
||||||
|
.cta-back {
|
||||||
|
@apply border-2 border-solid border-brand rounded-full py-3 px-6 font-bold text-lg outline-none self-end whitespace-nowrap;
|
||||||
|
&:disabled {
|
||||||
|
@apply bg-inactive-branch border-inactive-branch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta-action-primary {
|
||||||
|
@apply bg-brand text-white;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
import { DatePipe } from '@angular/common';
|
||||||
|
import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core';
|
||||||
|
import { ActivatedRoute, Params } from '@angular/router';
|
||||||
|
import { BreadcrumbService } from '@core/breadcrumb';
|
||||||
|
import { Config } from '@core/config';
|
||||||
|
import { DomainPrinterService } from '@domain/printer';
|
||||||
|
import { DomainTaskCalendarService } from '@domain/task-calendar';
|
||||||
|
import { PrintModalComponent, PrintModalData } from '@modal/printer';
|
||||||
|
import { DisplayInfoDTO } from '@swagger/eis';
|
||||||
|
import { DateAdapter, groupBy } from '@ui/common';
|
||||||
|
import { UiFilter } from '@ui/filter';
|
||||||
|
import { UiModalService } from '@ui/modal';
|
||||||
|
import { combineLatest, Subject } from 'rxjs';
|
||||||
|
import { first, map, takeUntil, debounceTime } from 'rxjs/operators';
|
||||||
|
import { TaskCalendarStore } from '../../task-calendar.store';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'page-task-search',
|
||||||
|
templateUrl: 'task-search.component.html',
|
||||||
|
styleUrls: ['task-search.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
providers: [DatePipe],
|
||||||
|
})
|
||||||
|
export class TaskSearchComponent implements OnInit {
|
||||||
|
private _onDestroy$ = new Subject();
|
||||||
|
|
||||||
|
today = this.dateAdapter.today();
|
||||||
|
byDate = (item: DisplayInfoDTO) => this.domainTaskCalendarService.getDateGroupKey(item.taskDate || item.publicationDate);
|
||||||
|
|
||||||
|
searchResults$ = this.taskCalendarStore.searchResults$;
|
||||||
|
searchResultsLength$ = this.searchResults$.pipe(map((r) => r.length || 0));
|
||||||
|
fetching$ = this.taskCalendarStore.isSearching$;
|
||||||
|
|
||||||
|
displayItems$ = this.searchResults$.pipe(
|
||||||
|
map((r) => {
|
||||||
|
const grouped = groupBy(r, this.byDate);
|
||||||
|
grouped.sort((a, b) => new Date(b.group).getTime() - new Date(a.group).getTime());
|
||||||
|
return grouped;
|
||||||
|
}),
|
||||||
|
// Sortierung der aufgaben nach Rot => Gelb => Grau => Grün
|
||||||
|
map((grouped) =>
|
||||||
|
grouped.map((g) => ({
|
||||||
|
...g,
|
||||||
|
items: this.domainTaskCalendarService.sort(g.items, ['Overdue', 'InProcess', 'Approved', 'Completed', 'Removed']),
|
||||||
|
}))
|
||||||
|
),
|
||||||
|
// Entfernte ans ende der Gruppe setzen
|
||||||
|
map((grouped) => grouped.map((g) => ({ ...g, items: g.items.sort((a, b) => this.domainTaskCalendarService.moveRemovedToEnd(a, b)) })))
|
||||||
|
);
|
||||||
|
|
||||||
|
showEmptyMessage$ = combineLatest([this.fetching$, this.searchResultsLength$, this.taskCalendarStore.hits$]).pipe(
|
||||||
|
map(([fetching, length, hits]) => !fetching && length <= 0 && hits === 0)
|
||||||
|
);
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private dateAdapter: DateAdapter,
|
||||||
|
private taskCalendarStore: TaskCalendarStore,
|
||||||
|
private domainTaskCalendarService: DomainTaskCalendarService,
|
||||||
|
private uiModal: UiModalService,
|
||||||
|
private domainPrinterService: DomainPrinterService,
|
||||||
|
private datePipe: DatePipe,
|
||||||
|
private _activatedRoute: ActivatedRoute,
|
||||||
|
private _config: Config,
|
||||||
|
private _breadcrumb: BreadcrumbService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this._activatedRoute.queryParams.pipe(takeUntil(this._onDestroy$), debounceTime(500)).subscribe(async (queryParams) => {
|
||||||
|
if (queryParams) {
|
||||||
|
const filters = UiFilter.create(await this.taskCalendarStore.selectFilter.pipe(first()).toPromise());
|
||||||
|
if (queryParams) {
|
||||||
|
filters.fromQueryParams(queryParams);
|
||||||
|
}
|
||||||
|
this.taskCalendarStore.setFilter({ filters });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.taskCalendarStore.search({ clear: true });
|
||||||
|
this.updateBreadcrumb(queryParams);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.taskCalendarStore.searchTarget$.pipe(takeUntil(this._onDestroy$)).subscribe((target) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
document.getElementById(target)?.scrollIntoView();
|
||||||
|
}, 150);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
open(item: DisplayInfoDTO) {
|
||||||
|
this.taskCalendarStore.open(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
async print(date: string) {
|
||||||
|
let displayInfos = await this.searchResults$.pipe(first()).toPromise();
|
||||||
|
displayInfos = displayInfos.filter((di) => !this.domainTaskCalendarService.getProcessingStatusList(di).includes('Completed'));
|
||||||
|
this.uiModal.open({
|
||||||
|
content: PrintModalComponent,
|
||||||
|
data: {
|
||||||
|
printerType: 'Office',
|
||||||
|
print: (printer) =>
|
||||||
|
this.domainPrinterService
|
||||||
|
.printDisplayInfoDTOList({
|
||||||
|
displayInfos,
|
||||||
|
printer,
|
||||||
|
title: `Tätigkeitskalender \n Liste für ${this.datePipe.transform(date, 'EEEE, dd. MMMM yyyy')}`,
|
||||||
|
})
|
||||||
|
.toPromise(),
|
||||||
|
} as PrintModalData,
|
||||||
|
config: {
|
||||||
|
panelClass: [],
|
||||||
|
showScrollbarY: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async search() {
|
||||||
|
const hits = await this.taskCalendarStore.hits$.pipe(first()).toPromise();
|
||||||
|
const results = await this.taskCalendarStore.searchResults$.pipe(first()).toPromise();
|
||||||
|
const fetching = await this.taskCalendarStore.selectFetching.pipe(first()).toPromise();
|
||||||
|
|
||||||
|
if (hits > results.length && !fetching) {
|
||||||
|
this.taskCalendarStore.search({});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateBreadcrumb(queryParams?: Params) {
|
||||||
|
this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({
|
||||||
|
key: this._config.get('process.ids.taskCalendar'),
|
||||||
|
name: queryParams?.main_qs || 'Suchergebnisse',
|
||||||
|
path: '/filiale/task-calendar/search',
|
||||||
|
tags: ['task-calendar', 'search'],
|
||||||
|
section: 'branch',
|
||||||
|
params: queryParams,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
import { UiCommonModule } from '@ui/common';
|
||||||
|
import { UiIconModule } from '@ui/icon';
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
|
import { UiScrollContainerModule } from '@ui/scroll-container';
|
||||||
|
import { TaskSearchComponent } from './task-search.component';
|
||||||
|
import { TaskListModule } from '../../components/task-list';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule, UiCommonModule, UiIconModule, RouterModule, UiScrollContainerModule, TaskListModule],
|
||||||
|
exports: [TaskSearchComponent],
|
||||||
|
declarations: [TaskSearchComponent],
|
||||||
|
})
|
||||||
|
export class TaskSearchModule {}
|
||||||
@@ -1,12 +1,19 @@
|
|||||||
<ui-calendar
|
<div class="bg-white pb-8 rounded-lg">
|
||||||
mode="week"
|
<ui-calendar
|
||||||
[selected]="selectedDate$ | async"
|
mode="week"
|
||||||
[displayed]="displayedDate$ | async"
|
[selected]="selectedDate$ | async"
|
||||||
[minDate]="minDate"
|
[displayed]="displayedDate$ | async"
|
||||||
[maxDate]="maxDate"
|
[minDate]="minDate"
|
||||||
[indicators]="indicators$ | async"
|
[maxDate]="maxDate"
|
||||||
(selectedChange)="setSelectedAndDisplayedDate({ selectedDate: $event })"
|
[indicators]="indicators$ | async"
|
||||||
(displayedChange)="setSelectedAndDisplayedDate({ displayDate: $event })"
|
(selectedChange)="setSelectedAndDisplayedDate({ selectedDate: $event })"
|
||||||
></ui-calendar>
|
(displayedChange)="setSelectedAndDisplayedDate({ displayDate: $event })"
|
||||||
|
></ui-calendar>
|
||||||
|
</div>
|
||||||
|
|
||||||
<page-task-list [items]="items$ | async" [selected]="selectedDate$ | async" (select)="open($event)"></page-task-list>
|
<page-task-list
|
||||||
|
[items]="items$ | async"
|
||||||
|
[selected]="selectedDate$ | async"
|
||||||
|
(select)="open($event)"
|
||||||
|
[fetching]="fetching$ | async"
|
||||||
|
></page-task-list>
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
:host {
|
:host {
|
||||||
@apply block pb-24;
|
@apply block;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ export class TasksComponent implements OnInit {
|
|||||||
|
|
||||||
readonly items$ = this.taskCalendarStore.selectDisplayInfos;
|
readonly items$ = this.taskCalendarStore.selectDisplayInfos;
|
||||||
|
|
||||||
|
readonly searchResults$ = this.taskCalendarStore.searchResults$;
|
||||||
|
|
||||||
|
readonly fetching$ = this.taskCalendarStore.select((s) => s.fetching);
|
||||||
|
|
||||||
readonly minDate = this.dateAdapter.addCalendarMonths(this.dateAdapter.today(), -6);
|
readonly minDate = this.dateAdapter.addCalendarMonths(this.dateAdapter.today(), -6);
|
||||||
|
|
||||||
readonly maxDate = this.dateAdapter.addCalendarMonths(this.dateAdapter.today(), 6);
|
readonly maxDate = this.dateAdapter.addCalendarMonths(this.dateAdapter.today(), 6);
|
||||||
@@ -46,8 +50,11 @@ export class TasksComponent implements OnInit {
|
|||||||
this.updateBreadcrumb({});
|
this.updateBreadcrumb({});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.taskCalendarStore.setMode({ mode: 'week' });
|
|
||||||
this.taskCalendarStore.loadItems();
|
this.taskCalendarStore.loadItems();
|
||||||
|
this.removeSearchBreadcrumbs();
|
||||||
|
|
||||||
|
this.taskCalendarStore.resetSearch();
|
||||||
|
this.taskCalendarStore.setMode({ mode: 'week' });
|
||||||
}
|
}
|
||||||
|
|
||||||
setSelectedAndDisplayedDate({ displayDate, selectedDate }: { displayDate?: Date; selectedDate?: Date }) {
|
setSelectedAndDisplayedDate({ displayDate, selectedDate }: { displayDate?: Date; selectedDate?: Date }) {
|
||||||
@@ -56,7 +63,6 @@ export class TasksComponent implements OnInit {
|
|||||||
if (displayDate && selectedDate) {
|
if (displayDate && selectedDate) {
|
||||||
this.taskCalendarStore.setSelectedDate({ date: selectedDate });
|
this.taskCalendarStore.setSelectedDate({ date: selectedDate });
|
||||||
this.taskCalendarStore.setDisplayedDate({ date: displayDate });
|
this.taskCalendarStore.setDisplayedDate({ date: displayDate });
|
||||||
this.taskCalendarStore.loadItems();
|
|
||||||
this.router.navigate([], {
|
this.router.navigate([], {
|
||||||
queryParams: { displayDate: displayDate?.toJSON(), selectedDate: selectedDate?.toJSON() },
|
queryParams: { displayDate: displayDate?.toJSON(), selectedDate: selectedDate?.toJSON() },
|
||||||
});
|
});
|
||||||
@@ -74,7 +80,6 @@ export class TasksComponent implements OnInit {
|
|||||||
|
|
||||||
if (displayDate) {
|
if (displayDate) {
|
||||||
this.taskCalendarStore.setDisplayedDate({ date: displayDate });
|
this.taskCalendarStore.setDisplayedDate({ date: displayDate });
|
||||||
this.taskCalendarStore.loadItems();
|
|
||||||
this.router.navigate([], {
|
this.router.navigate([], {
|
||||||
queryParams: { ...queryParams, displayDate: displayDate?.toJSON() },
|
queryParams: { ...queryParams, displayDate: displayDate?.toJSON() },
|
||||||
});
|
});
|
||||||
@@ -86,6 +91,10 @@ export class TasksComponent implements OnInit {
|
|||||||
this.taskCalendarStore.open(item);
|
this.taskCalendarStore.open(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeSearchBreadcrumbs() {
|
||||||
|
this.breadcrumb.removeBreadcrumbsByKeyAndTags(this._config.get('process.ids.taskCalendar'), ['task-calendar', 'search']);
|
||||||
|
}
|
||||||
|
|
||||||
updateBreadcrumb({ displayDate, selectedDate }: { displayDate?: Date; selectedDate?: Date }) {
|
updateBreadcrumb({ displayDate, selectedDate }: { displayDate?: Date; selectedDate?: Date }) {
|
||||||
const queryParams = this.activatedRoute.snapshot.queryParams;
|
const queryParams = this.activatedRoute.snapshot.queryParams;
|
||||||
this.breadcrumb.addOrUpdateBreadcrumbIfNotExists({
|
this.breadcrumb.addOrUpdateBreadcrumbIfNotExists({
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { CommonModule } from '@angular/common';
|
|||||||
import { TasksComponent } from './tasks.component';
|
import { TasksComponent } from './tasks.component';
|
||||||
import { UiCalendarModule } from '@ui/calendar';
|
import { UiCalendarModule } from '@ui/calendar';
|
||||||
import { TaskListModule } from '../../components/task-list';
|
import { TaskListModule } from '../../components/task-list';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [CommonModule, UiCalendarModule, TaskListModule],
|
imports: [CommonModule, UiCalendarModule, TaskListModule],
|
||||||
exports: [TasksComponent],
|
exports: [TasksComponent],
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { DomainTaskCalendarService } from '@domain/task-calendar';
|
import { DomainTaskCalendarService } from '@domain/task-calendar';
|
||||||
import { ComponentStore, tapResponse } from '@ngrx/component-store';
|
import { ComponentStore, tapResponse } from '@ngrx/component-store';
|
||||||
import { DisplayInfoDTO, InputDTO, QuerySettingsDTO, ResponseArgsOfIEnumerableOfInputDTO } from '@swagger/eis';
|
import { DisplayInfoDTO, InputDTO, QuerySettingsDTO } from '@swagger/eis';
|
||||||
import { CalendarIndicator } from '@ui/calendar';
|
import { CalendarIndicator } from '@ui/calendar';
|
||||||
import { DateAdapter } from '@ui/common';
|
import { DateAdapter } from '@ui/common';
|
||||||
import { Filter, FilterOption, SelectFilter, UiFilter, UiFilterMappingService } from '@ui/filter';
|
import { Filter, FilterOption, SelectFilter, UiFilter, UiFilterMappingService } from '@ui/filter';
|
||||||
import { UiMessageModalComponent, UiModalRef, UiModalResult, UiModalService } from '@ui/modal';
|
import { UiMessageModalComponent, UiModalRef, UiModalResult, UiModalService } from '@ui/modal';
|
||||||
import { clone } from 'lodash';
|
import { clone } from 'lodash';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { debounceTime, map, switchMap, tap, withLatestFrom } from 'rxjs/operators';
|
import { debounceTime, map, switchMap, tap, withLatestFrom, first, filter } from 'rxjs/operators';
|
||||||
import { InfoModalComponent } from './modals/info/info-modal.component';
|
import { InfoModalComponent } from './modals/info/info-modal.component';
|
||||||
import { PreInfoModalComponent } from './modals/preinfo/preinfo-modal.component';
|
import { PreInfoModalComponent } from './modals/preinfo/preinfo-modal.component';
|
||||||
import { TaskModalComponent } from './modals/task/task-modal.component';
|
import { TaskModalComponent } from './modals/task/task-modal.component';
|
||||||
@@ -21,7 +21,11 @@ export interface TaskCalendarState {
|
|||||||
initialFilter: UiFilter;
|
initialFilter: UiFilter;
|
||||||
filter: UiFilter;
|
filter: UiFilter;
|
||||||
message: string;
|
message: string;
|
||||||
|
searchResults: DisplayInfoDTO[];
|
||||||
|
searchTarget: string;
|
||||||
fetching: boolean;
|
fetching: boolean;
|
||||||
|
isSearching: boolean;
|
||||||
|
hits: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -38,6 +42,14 @@ export class TaskCalendarStore extends ComponentStore<TaskCalendarState> {
|
|||||||
|
|
||||||
readonly selectDisplayInfos = this.select((s) => s.displayInfos);
|
readonly selectDisplayInfos = this.select((s) => s.displayInfos);
|
||||||
|
|
||||||
|
readonly searchResults$ = this.select((s) => s.searchResults);
|
||||||
|
|
||||||
|
readonly hits$ = this.select((s) => s.hits);
|
||||||
|
|
||||||
|
readonly searchTarget$ = this.select((s) => s.searchTarget);
|
||||||
|
|
||||||
|
readonly isSearching$ = this.select((s) => s.isSearching);
|
||||||
|
|
||||||
readonly selectCalendarIndicators = this.select(this.selectDisplayInfos, (displayItems) =>
|
readonly selectCalendarIndicators = this.select(this.selectDisplayInfos, (displayItems) =>
|
||||||
displayItems.reduce<CalendarIndicator[]>((agg, item) => {
|
displayItems.reduce<CalendarIndicator[]>((agg, item) => {
|
||||||
const calendarIndicator = this.mapDisplayInfoToCalendarIndicator(item);
|
const calendarIndicator = this.mapDisplayInfoToCalendarIndicator(item);
|
||||||
@@ -60,6 +72,7 @@ export class TaskCalendarStore extends ComponentStore<TaskCalendarState> {
|
|||||||
readonly selectFilter = this.select((s) => s.filter);
|
readonly selectFilter = this.select((s) => s.filter);
|
||||||
|
|
||||||
readonly selectFetching = this.select((s) => s.fetching);
|
readonly selectFetching = this.select((s) => s.fetching);
|
||||||
|
readonly fetching = this.get((s) => s.fetching);
|
||||||
|
|
||||||
readonly selectMessage = this.select((s) => s.message);
|
readonly selectMessage = this.select((s) => s.message);
|
||||||
|
|
||||||
@@ -88,9 +101,11 @@ export class TaskCalendarStore extends ComponentStore<TaskCalendarState> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
hits = this.get((s) => s.hits);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
public domainTaskCalendarService: DomainTaskCalendarService,
|
||||||
private dateAdapter: DateAdapter,
|
private dateAdapter: DateAdapter,
|
||||||
private domainTaskCalendarService: DomainTaskCalendarService,
|
|
||||||
private uiModal: UiModalService,
|
private uiModal: UiModalService,
|
||||||
private uiFilterMappingService: UiFilterMappingService
|
private uiFilterMappingService: UiFilterMappingService
|
||||||
) {
|
) {
|
||||||
@@ -103,6 +118,10 @@ export class TaskCalendarStore extends ComponentStore<TaskCalendarState> {
|
|||||||
filter: undefined,
|
filter: undefined,
|
||||||
message: undefined,
|
message: undefined,
|
||||||
fetching: false,
|
fetching: false,
|
||||||
|
searchResults: [],
|
||||||
|
isSearching: false,
|
||||||
|
hits: undefined,
|
||||||
|
searchTarget: '',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,11 +145,89 @@ export class TaskCalendarStore extends ComponentStore<TaskCalendarState> {
|
|||||||
mode,
|
mode,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
readonly setMessage = this.updater((s, { message }: { message: string }) => ({
|
||||||
|
...s,
|
||||||
|
message,
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Der Key der Datums-Gruppe, zu der initial navigiert werden soll
|
||||||
|
*/
|
||||||
|
readonly setSearchTarget = this.updater((s, { searchTarget }: { searchTarget: string }) => ({
|
||||||
|
...s,
|
||||||
|
searchTarget,
|
||||||
|
}));
|
||||||
|
|
||||||
|
readonly search = this.effect((options$: Observable<{ clear?: boolean }>) =>
|
||||||
|
options$.pipe(
|
||||||
|
tap(() => this.patchState({ isSearching: true })),
|
||||||
|
debounceTime(500),
|
||||||
|
withLatestFrom(this.domainTaskCalendarService.currentBranchId$, this.selectFilter),
|
||||||
|
switchMap(([options, branchId, filter]) => {
|
||||||
|
const querytoken = {
|
||||||
|
...filter?.getQueryToken(),
|
||||||
|
// Paging ist vorbereitet aber vorerst deaktiviert
|
||||||
|
// skip: results.length || 0,
|
||||||
|
// take: 50,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Im Zeitraum von 6 Monaten in der Vergangenheit und 6 Monate in der Zukunft abfragen
|
||||||
|
const start = this.dateAdapter.addCalendarMonths(this.dateAdapter.today(), -6);
|
||||||
|
const stop = this.dateAdapter.addCalendarMonths(this.dateAdapter.today(), 6);
|
||||||
|
return this.domainTaskCalendarService
|
||||||
|
.getInfos({
|
||||||
|
...querytoken,
|
||||||
|
filter: {
|
||||||
|
timespan: `"${start.toISOString()}"-"${stop?.toISOString()}"`,
|
||||||
|
...querytoken?.filter,
|
||||||
|
branch_id: String(branchId),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.pipe(
|
||||||
|
tapResponse(
|
||||||
|
(response) => {
|
||||||
|
if (!response.error) {
|
||||||
|
response = this.preparePreInfos(response);
|
||||||
|
|
||||||
|
const results = this.get((s) => s.searchResults);
|
||||||
|
const searchResults = results.length > 0 && !options?.clear ? [...results, ...response.result] : [...response.result];
|
||||||
|
const sorted = searchResults.sort((a, b) =>
|
||||||
|
this.dateAdapter.findClosestDate(new Date(a.taskDate || a.publicationDate), new Date(b.taskDate || b.publicationDate))
|
||||||
|
);
|
||||||
|
this.patchState({
|
||||||
|
searchResults,
|
||||||
|
searchTarget:
|
||||||
|
sorted?.length > 0 && response.skip === 0
|
||||||
|
? this.domainTaskCalendarService.getDateGroupKey(sorted[0].taskDate || sorted[0].publicationDate)
|
||||||
|
: '',
|
||||||
|
isSearching: false,
|
||||||
|
message: undefined,
|
||||||
|
hits: response.hits,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.uiModal.open({ content: UiMessageModalComponent, data: response });
|
||||||
|
this.patchState({ searchResults: [], isSearching: false, message: 'Keine Suchergebnisse' });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
console.error(error);
|
||||||
|
this.patchState({ searchResults: [], isSearching: false, message: 'Keine Suchergebnisse' });
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
resetSearch() {
|
||||||
|
this.patchState({ searchResults: [], hits: undefined });
|
||||||
|
}
|
||||||
|
|
||||||
readonly loadItems = this.effect(($: Observable<void>) =>
|
readonly loadItems = this.effect(($: Observable<void>) =>
|
||||||
$.pipe(
|
$.pipe(
|
||||||
tap(() => this.patchState({ fetching: true })),
|
tap(() => this.patchState({ fetching: true })),
|
||||||
debounceTime(500),
|
debounceTime(500),
|
||||||
withLatestFrom(this.domainTaskCalendarService.currentBranchId$, this.selectStartStop, this.selectFilter),
|
withLatestFrom(this.domainTaskCalendarService.currentBranchId$, this.selectStartStop, this.selectInitialFilter),
|
||||||
switchMap(([_, branchId, date, filter]) => {
|
switchMap(([_, branchId, date, filter]) => {
|
||||||
const querytoken = { ...filter?.getQueryToken() };
|
const querytoken = { ...filter?.getQueryToken() };
|
||||||
return this.domainTaskCalendarService
|
return this.domainTaskCalendarService
|
||||||
@@ -146,13 +243,7 @@ export class TaskCalendarStore extends ComponentStore<TaskCalendarState> {
|
|||||||
tapResponse(
|
tapResponse(
|
||||||
(response) => {
|
(response) => {
|
||||||
if (!response.error) {
|
if (!response.error) {
|
||||||
const preInfos = response.result.filter((info) => this.domainTaskCalendarService.getInfoType(info) === 'PreInfo');
|
response = this.preparePreInfos(response);
|
||||||
preInfos.forEach((info) => {
|
|
||||||
const preInfoTask = clone(info);
|
|
||||||
delete preInfoTask.publicationDate;
|
|
||||||
response.result.push(preInfoTask);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.patchState({ displayInfos: response.result, fetching: false, message: undefined });
|
this.patchState({ displayInfos: response.result, fetching: false, message: undefined });
|
||||||
} else {
|
} else {
|
||||||
this.uiModal.open({
|
this.uiModal.open({
|
||||||
@@ -172,6 +263,16 @@ export class TaskCalendarStore extends ComponentStore<TaskCalendarState> {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
preparePreInfos(response) {
|
||||||
|
const preInfos = response.result.filter((info) => this.domainTaskCalendarService.getInfoType(info) === 'PreInfo');
|
||||||
|
preInfos.forEach((info) => {
|
||||||
|
const preInfoTask = clone(info);
|
||||||
|
delete preInfoTask.publicationDate;
|
||||||
|
response.result.push(preInfoTask);
|
||||||
|
});
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
readonly loadFilter = this.effect(($: Observable<void>) =>
|
readonly loadFilter = this.effect(($: Observable<void>) =>
|
||||||
$.pipe(
|
$.pipe(
|
||||||
tap(() => this.patchState({ fetching: true })),
|
tap(() => this.patchState({ fetching: true })),
|
||||||
@@ -227,7 +328,20 @@ export class TaskCalendarStore extends ComponentStore<TaskCalendarState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
complete: () => this.loadItems(),
|
});
|
||||||
|
|
||||||
|
let hasChanged = false;
|
||||||
|
taskModalRef?.afterChanged$.subscribe({
|
||||||
|
next: (changed) => (hasChanged = changed),
|
||||||
|
complete: async () => {
|
||||||
|
if (hasChanged) {
|
||||||
|
this.loadItems();
|
||||||
|
const searchResults = await this.searchResults$.pipe(first()).toPromise();
|
||||||
|
if (searchResults?.length > 0) {
|
||||||
|
this.search({ clear: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
:host {
|
:host {
|
||||||
@apply grid grid-cols-8;
|
@apply grid grid-cols-8 pr-4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cell {
|
.cell {
|
||||||
@@ -11,7 +11,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.cell-day-of-week:not(.cell-first) {
|
.cell-day-of-week:not(.cell-first) {
|
||||||
@apply border-0 border-t-2 border-solid border-gray-200;
|
@apply border-0 border-t-2 border-solid;
|
||||||
|
border-color: #edeff0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cell-first {
|
.cell-first {
|
||||||
|
|||||||
@@ -1,15 +1,26 @@
|
|||||||
<div></div>
|
<div></div>
|
||||||
<div class="nav-wrapper">
|
<div class="nav-wrapper">
|
||||||
<button class="prev" type="button" (click)="prev()">
|
<div class="flex flex-row">
|
||||||
<ui-icon icon="arrow_head" size="20px"></ui-icon>
|
<button class="prev" type="button" (click)="prev()">
|
||||||
</button>
|
<ui-icon icon="arrow_head" size="12px"></ui-icon>
|
||||||
|
</button>
|
||||||
<div class="display-year-month">
|
<div class="display-year-month">
|
||||||
{{ calendar.displayed | date: 'MMMM yyyy' }}
|
{{ calendar.displayed | date: 'MMMM yyyy' }}
|
||||||
|
</div>
|
||||||
|
<button class="next" type="button" (click)="next()">
|
||||||
|
<ui-icon icon="arrow_head" size="12px"></ui-icon>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button class="today" type="button" (click)="today()">Heute</button>
|
<div class="grid grid-flow-col gap-4 col-start-8">
|
||||||
<button class="next" type="button" (click)="next()">
|
<button class="today" type="button" (click)="today()">Heute</button>
|
||||||
<ui-icon icon="arrow_head" size="20px"></ui-icon>
|
<a *ngIf="calendar.mode === 'week'" class="calendar-nav" [routerLink]="['/filiale/task-calendar/calendar']">
|
||||||
</button>
|
<span>zum Kalender</span>
|
||||||
|
<ui-icon icon="calendar" size="18px"></ui-icon>
|
||||||
|
</a>
|
||||||
|
<a *ngIf="calendar.mode === 'month'" class="task-nav" [routerLink]="['/filiale/task-calendar/tasks']">
|
||||||
|
<span>zur Liste</span>
|
||||||
|
<ui-icon icon="tasks" size="18px"></ui-icon>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,26 +1,51 @@
|
|||||||
:host {
|
:host {
|
||||||
@apply grid grid-cols-8 box-border items-center;
|
@apply grid grid-cols-8 box-border items-center pt-2 pr-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
@apply flex flex-row items-center py-2 px-4 no-underline border-2 border-solid font-bold text-lg whitespace-nowrap;
|
||||||
|
border: 1px solid #aeb7c1;
|
||||||
|
border-radius: 5px;
|
||||||
|
|
||||||
|
&.task-nav {
|
||||||
|
width: 141px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.calendar-nav {
|
||||||
|
width: 188px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ui-icon {
|
||||||
|
@apply ml-2;
|
||||||
|
color: #596470;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
@apply outline-none border-none bg-transparent text-xl text-black;
|
@apply grid grid-flow-col items-center py-2 px-4 outline-none font-bold rounded-lg text-lg;
|
||||||
|
|
||||||
&.today {
|
&.today {
|
||||||
@apply mr-11;
|
border: 1px solid #aeb7c1;
|
||||||
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-wrapper {
|
.nav-wrapper {
|
||||||
@apply flex flex-row items-center col-span-7;
|
@apply grid grid-flow-col items-center col-span-7;
|
||||||
}
|
}
|
||||||
|
|
||||||
.display-year-month {
|
.display-year-month {
|
||||||
@apply flex-grow text-center font-bold text-xl;
|
@apply flex flex-row items-center justify-center text-center font-bold whitespace-nowrap text-2xl;
|
||||||
|
width: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prev,
|
.prev,
|
||||||
.next {
|
.next {
|
||||||
@apply text-ucla-blue;
|
@apply grid justify-center w-12 h-8;
|
||||||
|
color: #596470;
|
||||||
|
background: #ffffff;
|
||||||
|
box-shadow: 0px 0px 10px rgba(220, 226, 233, 0.5);
|
||||||
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prev {
|
.prev {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Component, ChangeDetectionStrategy, Host } from '@angular/core';
|
import { Component, ChangeDetectionStrategy, Host } from '@angular/core';
|
||||||
import { DateAdapter } from '@ui/common';
|
|
||||||
import { UiCalendarComponent } from '../ui-calendar.component';
|
import { UiCalendarComponent } from '../ui-calendar.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
|||||||
@@ -1,98 +1,2 @@
|
|||||||
<ui-calendar-header></ui-calendar-header>
|
<ui-calendar-header></ui-calendar-header>
|
||||||
<ui-calendar-body></ui-calendar-body>
|
<ui-calendar-body></ui-calendar-body>
|
||||||
<!-- <div class="calendar" [ngClass]="mode">
|
|
||||||
<div class="header">
|
|
||||||
<ng-container>
|
|
||||||
<div class="placeholder"></div>
|
|
||||||
<div class="navigation">
|
|
||||||
<button class="button back" (click)="navigateBack()">
|
|
||||||
<ui-icon icon="arrow_head" size="18px" rotate="180deg" alt="back button"></ui-icon>
|
|
||||||
</button>
|
|
||||||
<div class="title">{{ dateAdapter.getMonthName(dateAdapter.getMonth(selected)) }} {{ dateAdapter.getYear(selected) }}</div>
|
|
||||||
<button class="button next" (click)="navigateForth()">
|
|
||||||
<ui-icon icon="arrow_head" size="18px" alt="back button"></ui-icon>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<table class="body">
|
|
||||||
<thead>
|
|
||||||
<tr class="row">
|
|
||||||
<th></th>
|
|
||||||
<th>M</th>
|
|
||||||
<th>D</th>
|
|
||||||
<th>M</th>
|
|
||||||
<th>D</th>
|
|
||||||
<th>F</th>
|
|
||||||
<th>S</th>
|
|
||||||
<th>S</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody class="table-body">
|
|
||||||
<ng-container *ngIf="mode === 'month'">
|
|
||||||
<tr class="row" *ngFor="let kw of dateAdapter.getDatesAndCalendarWeeksForMonth(selected)">
|
|
||||||
<td (click)="selectedChange.emit(kw.dates[0])" class="kw-cell" [attr.week]="kw.week">KW {{ kw.week }}</td>
|
|
||||||
<td
|
|
||||||
class="day-cell"
|
|
||||||
[attr.day]="day.getDate()"
|
|
||||||
[class.out-of-month]="day.getMonth() !== dateAdapter.getMonth(selected)"
|
|
||||||
*ngFor="let day of kw.dates"
|
|
||||||
>
|
|
||||||
<div class="day" [class.is-sunday]="day.getDay() === 0" (click)="selectDay(day)">
|
|
||||||
<span [class.today]="dateAdapter.equals(today, day)" [class.today]="dateAdapter.equals(selected, day)">
|
|
||||||
{{ day.getDate() }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="items">
|
|
||||||
<ng-container *ngFor="let item of items | calendarItemsFilter: day | calendarItemsFilterUniqueStatusType">
|
|
||||||
<span
|
|
||||||
*ngIf="item.type === 'Task'"
|
|
||||||
class="indicator"
|
|
||||||
[style.backgroundColor]="item.color"
|
|
||||||
[title]="item.label"
|
|
||||||
(click)="itemSelected.emit(item)"
|
|
||||||
>
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
*ngIf="item.type === 'Info'"
|
|
||||||
class="indicator info"
|
|
||||||
[style.backgroundColor]="item.color"
|
|
||||||
[title]="item.label"
|
|
||||||
(click)="itemSelected.emit(item)"
|
|
||||||
>i</span
|
|
||||||
>
|
|
||||||
</ng-container>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</ng-container>
|
|
||||||
<ng-container *ngIf="mode === 'week'">
|
|
||||||
<tr class="row" *ngIf="dateAdapter.getCalendarWeek(selected); let kw">
|
|
||||||
<td class="kw-cell" [attr.week]="kw">KW {{ kw }}</td>
|
|
||||||
<td
|
|
||||||
class="day-cell"
|
|
||||||
[attr.day]="day.getDate()"
|
|
||||||
*ngFor="let day of dateAdapter.getDatesForCalendarWeek(dateAdapter.getYear(selected), kw)"
|
|
||||||
>
|
|
||||||
<div class="day" (click)="selectDay(day)">
|
|
||||||
<span [class.today]="dateAdapter.equals(today, day)" [class.selected]="dateAdapter.equals(selected, day)">
|
|
||||||
{{ day.getDate() }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="items">
|
|
||||||
<span
|
|
||||||
class="indicator"
|
|
||||||
[style.backgroundColor]="item.color"
|
|
||||||
[title]="item.label"
|
|
||||||
*ngFor="let item of items | calendarItemsFilter: day | calendarItemsFilterUniqueStatusType"
|
|
||||||
(click)="itemSelected.emit(item)"
|
|
||||||
>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</ng-container>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div> -->
|
|
||||||
|
|||||||
@@ -1,125 +1,3 @@
|
|||||||
:host {
|
|
||||||
}
|
|
||||||
|
|
||||||
ui-calendar-header {
|
ui-calendar-header {
|
||||||
@apply my-7;
|
@apply my-4;
|
||||||
}
|
}
|
||||||
|
|
||||||
// :host {
|
|
||||||
// @apply block;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .calendar {
|
|
||||||
// @apply my-0 mx-auto;
|
|
||||||
// width: 95%;
|
|
||||||
|
|
||||||
// .header {
|
|
||||||
// @apply grid items-center;
|
|
||||||
// grid-template-columns: 2.5fr 14fr;
|
|
||||||
|
|
||||||
// .navigation {
|
|
||||||
// @apply flex justify-between items-center mb-5;
|
|
||||||
|
|
||||||
// .button {
|
|
||||||
// @apply appearance-none border-none bg-transparent text-ucla-blue;
|
|
||||||
|
|
||||||
// &:focus {
|
|
||||||
// @apply outline-none;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .title {
|
|
||||||
// @apply text-lg font-bold;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .body {
|
|
||||||
// @apply w-full;
|
|
||||||
|
|
||||||
// > *:not(:empty) {
|
|
||||||
// @apply cursor-pointer;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .row {
|
|
||||||
// @apply grid;
|
|
||||||
// grid-template-columns: 2fr repeat(7, 2fr);
|
|
||||||
// grid-template-rows: 5rem;
|
|
||||||
|
|
||||||
// &:only-child {
|
|
||||||
// grid-template-rows: 6rem;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .kw-cell {
|
|
||||||
// @apply font-bold;
|
|
||||||
// color: rgba(89, 100, 112, 1);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// th {
|
|
||||||
// @apply flex justify-center items-center;
|
|
||||||
// color: rgba(89, 100, 112, 1);
|
|
||||||
// border-top: 1px solid #555;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// th:first-child {
|
|
||||||
// @apply border-t-0;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// th:first-child,
|
|
||||||
// td:first-child {
|
|
||||||
// border-right: 1px solid #555;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// th,
|
|
||||||
// td {
|
|
||||||
// @apply text-center p-2;
|
|
||||||
|
|
||||||
// &:first-child {
|
|
||||||
// @apply text-left;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .day-cell.out-of-month {
|
|
||||||
// .day,
|
|
||||||
// .items,
|
|
||||||
// .indicator {
|
|
||||||
// @apply hidden;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .day-cell {
|
|
||||||
// .day {
|
|
||||||
// @apply text-lg;
|
|
||||||
// color: rgba(0, 0, 0, 1);
|
|
||||||
|
|
||||||
// &.is-sunday {
|
|
||||||
// color: rgba(137, 148, 158, 1);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .selected {
|
|
||||||
// @apply border rounded-full bg-active-branch text-white p-3;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .today {
|
|
||||||
// @apply border rounded-full bg-cool-grey text-white p-3;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .items {
|
|
||||||
// @apply flex justify-center;
|
|
||||||
|
|
||||||
// .indicator {
|
|
||||||
// @apply block m-1 text-white rounded-full;
|
|
||||||
// height: 0.7rem;
|
|
||||||
// width: 0.7rem;
|
|
||||||
|
|
||||||
// &.info {
|
|
||||||
// @apply flex items-center justify-center;
|
|
||||||
// font-size: 0.6rem;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
import { UiIconModule } from '@ui/icon';
|
import { UiIconModule } from '@ui/icon';
|
||||||
import { UiCalendarBodyComponent } from './calendar-body/calendar-body.component';
|
import { UiCalendarBodyComponent } from './calendar-body/calendar-body.component';
|
||||||
import { UiCalendarCellDirective } from './calendar-body/calendar-cell.directive';
|
import { UiCalendarCellDirective } from './calendar-body/calendar-cell.directive';
|
||||||
@@ -15,7 +16,7 @@ import { UiCalendarComponent } from './ui-calendar.component';
|
|||||||
UiCalendarCellDirective,
|
UiCalendarCellDirective,
|
||||||
UiFilterIndicatorsByDatePipe,
|
UiFilterIndicatorsByDatePipe,
|
||||||
],
|
],
|
||||||
imports: [CommonModule, UiIconModule],
|
imports: [CommonModule, UiIconModule, RouterModule],
|
||||||
exports: [UiCalendarComponent],
|
exports: [UiCalendarComponent],
|
||||||
})
|
})
|
||||||
export class UiCalendarModule {}
|
export class UiCalendarModule {}
|
||||||
|
|||||||
@@ -193,6 +193,13 @@ export class DateAdapter {
|
|||||||
return this.getYear(first) - this.getYear(second) || this.getMonth(first) - this.getMonth(second);
|
return this.getYear(first) - this.getYear(second) || this.getMonth(first) - this.getMonth(second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
findClosestDate(a: Date, b: Date, compareDate?: Date) {
|
||||||
|
const date = compareDate || this.today();
|
||||||
|
var distancea = Math.abs(date.getTime() - a.getTime());
|
||||||
|
var distanceb = Math.abs(date.getTime() - b.getTime());
|
||||||
|
return distancea - distanceb;
|
||||||
|
}
|
||||||
|
|
||||||
isLessThan({ first, second, precision }: { first: Date; second: Date; precision?: Precision }): boolean {
|
isLessThan({ first, second, precision }: { first: Date; second: Date; precision?: Precision }): boolean {
|
||||||
return this.formatDate(first, precision) < this.formatDate(second, precision);
|
return this.formatDate(first, precision) < this.formatDate(second, precision);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { QueryTokenDTO } from '@swagger/oms';
|
import { QueryTokenDTO } from '@swagger/oms';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
import { IUiInputGroup, UiInputGroup } from './ui-input-group';
|
import { IUiInputGroup, UiInputGroup } from './ui-input-group';
|
||||||
|
import { UiInputType } from './ui-input-type.enum';
|
||||||
import { IUiOrderBy, UiOrderBy } from './ui-order-by';
|
import { IUiOrderBy, UiOrderBy } from './ui-order-by';
|
||||||
|
|
||||||
export interface IUiFilter {
|
export interface IUiFilter {
|
||||||
@@ -55,7 +56,13 @@ export class UiFilter implements IUiFilter {
|
|||||||
inputGroup.input.forEach((input) => {
|
inputGroup.input.forEach((input) => {
|
||||||
const key = input.key;
|
const key = input.key;
|
||||||
|
|
||||||
params[`${group}_${key}`] = input.toStringValue();
|
const values = inputGroup.input
|
||||||
|
?.filter((i) => i.key === key)
|
||||||
|
?.map((i) => i.toStringValue())
|
||||||
|
?.filter((i) => !!i)
|
||||||
|
?.join(';');
|
||||||
|
|
||||||
|
params[`${group}_${key}`] = values;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -90,13 +97,21 @@ export class UiFilter implements IUiFilter {
|
|||||||
.forEach((inputGroup) => {
|
.forEach((inputGroup) => {
|
||||||
inputGroup.input
|
inputGroup.input
|
||||||
.filter((i) => i.key === key)
|
.filter((i) => i.key === key)
|
||||||
.forEach((input) => {
|
.forEach((input, index) => {
|
||||||
input.fromStringValue(key, params[gk]);
|
let value = params[gk];
|
||||||
|
|
||||||
|
if (input.type === UiInputType.Text && value?.includes(';')) {
|
||||||
|
const splitted = value?.split(';');
|
||||||
|
if (splitted?.length >= index) {
|
||||||
|
value = splitted[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
input.fromStringValue(key, value);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.orderBy.forEach((ob) => {
|
this.orderBy?.forEach((ob) => {
|
||||||
const order = params[`order_by_${ob.by}`];
|
const order = params[`order_by_${ob.by}`];
|
||||||
const selected = (order && ob.desc && order === 'desc') || (!ob.desc && order === 'asc');
|
const selected = (order && ob.desc && order === 'desc') || (!ob.desc && order === 'asc');
|
||||||
ob.setSelected(selected);
|
ob.setSelected(selected);
|
||||||
@@ -133,7 +148,11 @@ export class UiFilter implements IUiFilter {
|
|||||||
for (const inputGroup of this.filter) {
|
for (const inputGroup of this.filter) {
|
||||||
for (const input of inputGroup.input.filter((i) => i.target === 'filter')) {
|
for (const input of inputGroup.input.filter((i) => i.target === 'filter')) {
|
||||||
const key = input.key;
|
const key = input.key;
|
||||||
const value = input.toStringValue();
|
const value = inputGroup.input
|
||||||
|
?.filter((i) => i.key === key)
|
||||||
|
?.map((i) => i.toStringValue())
|
||||||
|
?.filter((i) => !!i)
|
||||||
|
?.join(';');
|
||||||
|
|
||||||
if (!!value) {
|
if (!!value) {
|
||||||
filter[key] = value;
|
filter[key] = value;
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
<div class="spinner" *ngIf="loading">
|
<div class="spinner" *ngIf="loading">
|
||||||
<ui-icon icon="spinner" [size]="size"></ui-icon>
|
<ui-icon icon="spinner" [size]="spinnerSize"></ui-icon>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<ng-content></ng-content>
|
||||||
</div>
|
</div>
|
||||||
<ng-content></ng-content>
|
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
:host {
|
||||||
|
@apply flex flex-col h-full mt-12 text-gray-500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
@apply animate-spin absolute;
|
||||||
|
top: 4rem;
|
||||||
|
left: calc(50% - 14px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
@apply mt-16;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +1,20 @@
|
|||||||
import { OverlayRef } from '@angular/cdk/overlay';
|
import { OverlayRef } from '@angular/cdk/overlay';
|
||||||
import { TemplateRef, Type } from '@angular/core';
|
import { TemplateRef, Type } from '@angular/core';
|
||||||
import { UiModalConfig } from '@ui/modal';
|
import { UiModalConfig } from '@ui/modal';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject, BehaviorSubject } from 'rxjs';
|
||||||
import { UiModalResult } from './modal-result';
|
import { UiModalResult } from './modal-result';
|
||||||
|
|
||||||
export class UiModalRef<TR = any, TD = any> {
|
export class UiModalRef<TR = any, TD = any> {
|
||||||
afterClosed$ = new Subject<UiModalResult<TR>>();
|
afterClosed$ = new Subject<UiModalResult<TR>>();
|
||||||
|
afterChanged$ = new BehaviorSubject<boolean>(false);
|
||||||
|
|
||||||
|
private _result: TR;
|
||||||
|
get result(): TR {
|
||||||
|
return this._result;
|
||||||
|
}
|
||||||
|
set result(value) {
|
||||||
|
this._result = value;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public overlay: OverlayRef,
|
public overlay: OverlayRef,
|
||||||
@@ -22,10 +31,15 @@ export class UiModalRef<TR = any, TD = any> {
|
|||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.afterChanged$.complete();
|
||||||
this.afterClosed$.complete();
|
this.afterClosed$.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
close(data?: TR): void {
|
close(data?: TR): void {
|
||||||
this._close({ type: 'close', data });
|
this._close({ type: 'close', data });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
markChanged() {
|
||||||
|
this.afterChanged$.next(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,8 +13,8 @@
|
|||||||
|
|
||||||
<ng-template #loader>
|
<ng-template #loader>
|
||||||
<ng-container *ngIf="useLoadAnimation; else contentLoader">
|
<ng-container *ngIf="useLoadAnimation; else contentLoader">
|
||||||
<ui-skeleton-loader></ui-skeleton-loader>
|
<ui-skeleton-loader [template]="skeletonTemplate"></ui-skeleton-loader>
|
||||||
<ui-skeleton-loader *ngFor="let skeletons of createSkeletons()"></ui-skeleton-loader>
|
<ui-skeleton-loader [template]="skeletonTemplate" *ngFor="let skeletons of createSkeletons()"></ui-skeleton-loader>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-template #contentLoader>
|
<ng-template #contentLoader>
|
||||||
|
|||||||
@@ -48,6 +48,8 @@ export class UiScrollContainerComponent implements OnInit {
|
|||||||
|
|
||||||
@Input() initialScroll: number;
|
@Input() initialScroll: number;
|
||||||
|
|
||||||
|
@Input() skeletonTemplate?: string;
|
||||||
|
|
||||||
get containerHeightString() {
|
get containerHeightString() {
|
||||||
return `calc(100vh - ${this.containerHeight}rem)`;
|
return `calc(100vh - ${this.containerHeight}rem)`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,48 @@
|
|||||||
<div class="header">
|
<ng-container [ngSwitch]="template">
|
||||||
<div class="left animation"></div>
|
<div *ngSwitchCase="'task-calendar'" class="task-calendar">
|
||||||
<div class="right animation"></div>
|
<div class="task-calendar-header">
|
||||||
</div>
|
<div class="left animation"></div>
|
||||||
|
<div class="right animation"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="task-calendar-details">
|
||||||
<div class="thumbnail animation"></div>
|
<div class="date animation"></div>
|
||||||
|
<div class="icon animation"></div>
|
||||||
<div>
|
<div class="col">
|
||||||
<div class="title animation"></div>
|
<div class="item animation"></div>
|
||||||
<div class="sub-row">
|
<div class="item animation"></div>
|
||||||
<div class="item animation"></div>
|
<div class="item animation"></div>
|
||||||
<div class="item animation"></div>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="sub-row">
|
|
||||||
<div class="item animation"></div>
|
|
||||||
<div class="item animation"></div>
|
|
||||||
</div>
|
|
||||||
<div class="sub-row">
|
|
||||||
<div class="item animation"></div>
|
|
||||||
<div class="item animation"></div>
|
|
||||||
</div>
|
|
||||||
<div class="sub-row">
|
|
||||||
<div class="item animation"></div>
|
|
||||||
<div class="item animation"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
<div *ngSwitchDefault class="default">
|
||||||
|
<div class="header">
|
||||||
|
<div class="left animation"></div>
|
||||||
|
<div class="right animation"></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="thumbnail animation"></div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="title animation"></div>
|
||||||
|
<div class="sub-row">
|
||||||
|
<div class="item animation"></div>
|
||||||
|
<div class="item animation"></div>
|
||||||
|
</div>
|
||||||
|
<div class="sub-row">
|
||||||
|
<div class="item animation"></div>
|
||||||
|
<div class="item animation"></div>
|
||||||
|
</div>
|
||||||
|
<div class="sub-row">
|
||||||
|
<div class="item animation"></div>
|
||||||
|
<div class="item animation"></div>
|
||||||
|
</div>
|
||||||
|
<div class="sub-row">
|
||||||
|
<div class="item animation"></div>
|
||||||
|
<div class="item animation"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
|||||||
@@ -1,8 +1,44 @@
|
|||||||
:host {
|
.default {
|
||||||
@apply flex flex-col pt-4 pb-6 pl-1 pr-4 bg-white;
|
@apply flex flex-col pt-4 pb-6 pl-1 pr-4 bg-white;
|
||||||
height: 300px;
|
height: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.task-calendar {
|
||||||
|
@apply h-40 p-6;
|
||||||
|
|
||||||
|
.task-calendar-header {
|
||||||
|
@apply flex flex-row justify-between w-full;
|
||||||
|
|
||||||
|
.left {
|
||||||
|
@apply h-6 bg-ucla-blue;
|
||||||
|
width: 240px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right {
|
||||||
|
@apply h-6 bg-ucla-blue;
|
||||||
|
width: 130px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-calendar-details {
|
||||||
|
@apply grid mt-6;
|
||||||
|
grid-template-columns: 170px 40px 1fr;
|
||||||
|
|
||||||
|
.date {
|
||||||
|
@apply col-start-1 h-4 bg-ucla-blue;
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
@apply col-start-2 w-6 h-4 bg-ucla-blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
.col {
|
||||||
|
@apply flex flex-col col-start-3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
@apply w-full flex flex-row justify-between mt-5;
|
@apply w-full flex flex-row justify-between mt-5;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
import { Component, ChangeDetectionStrategy, Input } from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ui-skeleton-loader',
|
selector: 'ui-skeleton-loader',
|
||||||
@@ -7,5 +7,8 @@ import { Component, ChangeDetectionStrategy } from '@angular/core';
|
|||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class UiSkeletonLoaderComponent {
|
export class UiSkeletonLoaderComponent {
|
||||||
|
@Input()
|
||||||
|
template?: string;
|
||||||
|
|
||||||
constructor() {}
|
constructor() {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ variables:
|
|||||||
value: "$[counter(format('{0}.{1}', variables['Major'], variables['Minor']),0)]"
|
value: "$[counter(format('{0}.{1}', variables['Major'], variables['Minor']),0)]"
|
||||||
- name: 'BuildUniqueID'
|
- name: 'BuildUniqueID'
|
||||||
value: '$(Build.BuildID)-$(Agent.Id)-$(System.DefinitionId)-$(System.JobId)'
|
value: '$(Build.BuildID)-$(Agent.Id)-$(System.DefinitionId)-$(System.JobId)'
|
||||||
|
- group: 'GithubCMF'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# - job: cibuild_fast
|
# - job: cibuild_fast
|
||||||
@@ -52,6 +53,7 @@ jobs:
|
|||||||
displayName: 'npm auth'
|
displayName: 'npm auth'
|
||||||
inputs:
|
inputs:
|
||||||
workingFile: .npmrc
|
workingFile: .npmrc
|
||||||
|
customEndpoint: GitHub-read-packages
|
||||||
- bash: |
|
- bash: |
|
||||||
echo Build and Run Tests in docker
|
echo Build and Run Tests in docker
|
||||||
docker build . \
|
docker build . \
|
||||||
@@ -110,6 +112,7 @@ jobs:
|
|||||||
displayName: 'npm auth'
|
displayName: 'npm auth'
|
||||||
inputs:
|
inputs:
|
||||||
workingFile: .npmrc
|
workingFile: .npmrc
|
||||||
|
customEndpoint: GitHub-read-packages
|
||||||
- task: Docker@2
|
- task: Docker@2
|
||||||
displayName: 'build ISAClient Debug'
|
displayName: 'build ISAClient Debug'
|
||||||
inputs:
|
inputs:
|
||||||
@@ -170,6 +173,7 @@ jobs:
|
|||||||
displayName: 'npm auth'
|
displayName: 'npm auth'
|
||||||
inputs:
|
inputs:
|
||||||
workingFile: .npmrc
|
workingFile: .npmrc
|
||||||
|
customEndpoint: GitHub-read-packages
|
||||||
- task: Docker@2
|
- task: Docker@2
|
||||||
displayName: 'build ISAClient Prod'
|
displayName: 'build ISAClient Prod'
|
||||||
inputs:
|
inputs:
|
||||||
|
|||||||
1065
package-lock.json
generated
1065
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -59,6 +59,7 @@
|
|||||||
"@ngrx/entity": "~12.5.1",
|
"@ngrx/entity": "~12.5.1",
|
||||||
"@ngrx/store": "~12.5.1",
|
"@ngrx/store": "~12.5.1",
|
||||||
"@ngrx/store-devtools": "~12.5.1",
|
"@ngrx/store-devtools": "~12.5.1",
|
||||||
|
"@paragondata/ngx-ui": "^12.0.0-beta.23",
|
||||||
"angular-oauth2-oidc": "^13.0.1",
|
"angular-oauth2-oidc": "^13.0.1",
|
||||||
"angular-oauth2-oidc-jwks": "^13.0.1",
|
"angular-oauth2-oidc-jwks": "^13.0.1",
|
||||||
"core-js": "^2.6.5",
|
"core-js": "^2.6.5",
|
||||||
|
|||||||
@@ -304,7 +304,10 @@
|
|||||||
],
|
],
|
||||||
"@ui/branch-dropdown": [
|
"@ui/branch-dropdown": [
|
||||||
"apps/ui/branch-dropdown/src/public-api.ts"
|
"apps/ui/branch-dropdown/src/public-api.ts"
|
||||||
]
|
],
|
||||||
|
"@paragondata/ngx-ui/form-field": [
|
||||||
|
"node_modules/@paragondata/ngx-ui/form-field"
|
||||||
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user