fix: Remade menu editor loading logic

This commit is contained in:
2025-06-15 11:41:02 +02:00
parent 2a117262df
commit 4c7639ee4c
6 changed files with 177 additions and 114 deletions

View File

@@ -43,7 +43,7 @@ export class AccountMgmtComponent implements AfterViewInit {
}
this.ac.refresh()
this.ac.accs.subscribe(d => {
this.users.data = d ?? []
this.users.data = d
})
}

View File

@@ -1,96 +1,110 @@
<div id="upper-bar">
<mat-form-field>
<mat-form-field subscriptSizing="dynamic">
<mat-label>Wybierz tydzień</mat-label>
<mat-date-range-input [rangePicker]="picker" [formGroup]="range">
<input matStartDate formControlName="start" (dateChange)="requestData()">
<input matEndDate formControlName="end" (dateChange)="requestData()">
<input matStartDate formControlName="start">
<input matEndDate formControlName="end">
</mat-date-range-input>
<mat-datepicker-toggle matIconSuffix [for]="picker"></mat-datepicker-toggle>
<mat-date-range-picker #picker></mat-date-range-picker>
</mat-form-field>
<button mat-icon-button (click)="requestData()"><mat-icon>refresh</mat-icon></button>
<button mat-icon-button (click)="ac.refresh()"><mat-icon>search</mat-icon></button>
<button mat-icon-button (click)="addDate()"><mat-icon>add</mat-icon></button>
<button mat-icon-button (click)="print()"><mat-icon>print</mat-icon></button>
</div>
@if (loading) {
<mat-spinner></mat-spinner>
}
<table mat-table [dataSource]="dataSource">
<div matColumnDef="day">
<th mat-header-cell *matHeaderCellDef>Dzień</th>
<td mat-cell *matCellDef="let element">
<span>{{element.day.toFormat('dd.LL.yyyy')}}r.</span>
<p>{{element.day.toFormat('cccc') | titlecase }}</p>
<app-field-editor category="Nazwa" [(word)]="element.dayTitle" (wordChange)="editTitle(element._id)"/><br><hr>
<button (click)="remove(element._id)">Usuń dzień</button>
</td>
</div>
<div matColumnDef="sn">
<th mat-header-cell *matHeaderCellDef>Śniadanie</th>
<td mat-cell *matCellDef="let element">
<ul class="non-editable">
@for (i of ls.defaultItems.sn; track i) {
<li>{{i}}</li>
}
</ul><hr>
<app-list-editor [(list)]="element.sn.fancy" (edit)="editSn(element._id)" dataList="sn-fancy"/><hr>
<ul>
<li><app-field-editor category="II Śniadanie" [(word)]="element.sn.second" list="sn-second" (wordChange)="editSn(element._id)"/></li>
</ul>
</td>
</div>
<div matColumnDef="ob">
<th mat-header-cell *matHeaderCellDef>Obiad</th>
<td mat-cell *matCellDef="let element">
<ul>
<li><app-field-editor category="Zupa" [(word)]="element.ob.soup" list="ob-soup" (wordChange)="editOb(element._id)"/></li>
<li><app-field-editor category="Vege" [(word)]="element.ob.vege" list="ob-vege" (wordChange)="editOb(element._id)"/></li>
<li><app-field-editor category="Danie główne" [(word)]="element.ob.meal" list="ob-meal" (wordChange)="editOb(element._id)"/></li>
</ul><hr>
<app-list-editor [(list)]="element.ob.condiments" (edit)="editOb(element._id)" dataList="ob-condiments"/><hr>
<ul>
<li><app-field-editor category="Napój" [(word)]="element.ob.drink" list="ob-drink" (wordChange)="editOb(element._id)"/></li>
</ul><hr>
<app-list-editor [(list)]="element.ob.other" (edit)="editOb(element._id)" dataList="ob-other"/>
<button (click)="getStat(element.day, 'ob')">
Opinie wychowanków
</button>
</td>
</div>
<div matColumnDef="kol">
<th mat-header-cell *matHeaderCellDef>Kolacja</th>
<td mat-cell *matCellDef="let element">
<div>
@switch (element.day.weekday) {
@default {
<div>
<ul class="non-editable">
@for (i of ls.defaultItems.kol; track i) {
<li>{{i}}</li>
}
</ul><hr>
<ul>
<li><app-field-editor category="Kolacja" [(word)]="element.kol" list="kol" (wordChange)="editKol(element._id)"/></li>
</ul>
<button (click)="getStat(element.day, 'kol')">
Opinie wychowanków
</button>
</div>
}
@case (5) {
<div class="non-editable">
<p>Kolacja w domu!</p>
<p>(Nie edytowalne)</p>
</div>
}
}
<div class="mainc">
@switch (ac.state()) {
@case (0) {
<p>Wybierz zakres dat powyżej i kliknij szukaj</p>
}
@case (1) {
<div class="spinner">
<mat-spinner/>
</div>
</td>
</div>
}
@case (2) {
<table mat-table [dataSource]="dataSource">
<div matColumnDef="day">
<th mat-header-cell *matHeaderCellDef>Dzień</th>
<td mat-cell *matCellDef="let element">
<span>{{element.day.toFormat('dd.LL.yyyy')}}r.</span>
<p>{{element.day.toFormat('cccc') | titlecase }}</p>
<app-field-editor category="Nazwa" [(word)]="element.dayTitle" (wordChange)="editTitle(element._id)"/><br><hr>
<button (click)="remove(element._id)">Usuń dzień</button>
</td>
</div>
<div matColumnDef="sn">
<th mat-header-cell *matHeaderCellDef>Śniadanie</th>
<td mat-cell *matCellDef="let element">
<ul class="non-editable">
@for (i of ls.defaultItems.sn; track i) {
<li>{{i}}</li>
}
</ul><hr>
<app-list-editor [(list)]="element.sn.fancy" (edit)="editSn(element._id)" dataList="sn-fancy"/><hr>
<ul>
<li><app-field-editor category="II Śniadanie" [(word)]="element.sn.second" list="sn-second" (wordChange)="editSn(element._id)"/></li>
</ul>
</td>
</div>
<div matColumnDef="ob">
<th mat-header-cell *matHeaderCellDef>Obiad</th>
<td mat-cell *matCellDef="let element">
<ul>
<li><app-field-editor category="Zupa" [(word)]="element.ob.soup" list="ob-soup" (wordChange)="editOb(element._id)"/></li>
<li><app-field-editor category="Vege" [(word)]="element.ob.vege" list="ob-vege" (wordChange)="editOb(element._id)"/></li>
<li><app-field-editor category="Danie główne" [(word)]="element.ob.meal" list="ob-meal" (wordChange)="editOb(element._id)"/></li>
</ul><hr>
<app-list-editor [(list)]="element.ob.condiments" (edit)="editOb(element._id)" dataList="ob-condiments"/><hr>
<ul>
<li><app-field-editor category="Napój" [(word)]="element.ob.drink" list="ob-drink" (wordChange)="editOb(element._id)"/></li>
</ul><hr>
<app-list-editor [(list)]="element.ob.other" (edit)="editOb(element._id)" dataList="ob-other"/>
<button (click)="getStat(element.day, 'ob')">
Opinie wychowanków
</button>
</td>
</div>
<div matColumnDef="kol">
<th mat-header-cell *matHeaderCellDef>Kolacja</th>
<td mat-cell *matCellDef="let element">
<div>
@switch (element.day.weekday) {
@default {
<div>
<ul class="non-editable">
@for (i of ls.defaultItems.kol; track i) {
<li>{{i}}</li>
}
</ul><hr>
<ul>
<li><app-field-editor category="Kolacja" [(word)]="element.kol" list="kol" (wordChange)="editKol(element._id)"/></li>
</ul>
<button (click)="getStat(element.day, 'kol')">
Opinie wychowanków
</button>
</div>
}
@case (5) {
<div class="non-editable">
<p>Kolacja w domu!</p>
<p>(Nie edytowalne)</p>
</div>
}
}
</div>
</td>
</div>
<tr mat-header-row *matHeaderRowDef="dcols"></tr>
<tr mat-row *matRowDef="let row; columns: dcols"></tr>
</table>
<tr mat-header-row *matHeaderRowDef="dcols"></tr>
<tr mat-row *matRowDef="let row; columns: dcols"></tr>
</table>
}
}
</div>
@if (options) {
<datalist id="sn-fancy">
@@ -138,4 +152,4 @@
<option>{{i}}</option>
}
</datalist>
}
}

View File

@@ -16,3 +16,19 @@ button[mat-icon-button] {
color: gray;
font-style: italic;
}
.mainc {
height: 100%;
position: relative;
}
.spinner {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: flex;
align-items: center;
justify-content: center;
}

View File

@@ -33,11 +33,18 @@ export class MenuEditComponent {
public options: any
constructor(
private ac: MenuEditService,
protected ac: MenuEditService,
private dialog: MatDialog,
private sb: MatSnackBar,
readonly ls: LocalStorageService
) {}
) {
this.range.valueChanges.subscribe(v => {
ac.setDates(v.start!, v.end!)
})
ac.menuItems.subscribe(v => {
this.dataSource.data = v
})
}
print() {
this.ac
@@ -74,7 +81,7 @@ export class MenuEditComponent {
.subscribe(s => this.refreshIfGood(s))
break
case 'file':
this.requestData()
this.refresh()
break
default:
break
@@ -83,29 +90,15 @@ export class MenuEditComponent {
})
}
requestData() {
this.loading = true
refresh() {
this.ac.refresh()
this.ac.getOpts().subscribe(o => {
this.options = o
})
this.ac
.getMenu(this.range.value.start, this.range.value.end)
?.subscribe(data => {
this.loading = false
this.dataSource.data = data.map(v => {
let newMenu: Menu = {
...v,
day: DateTime.fromISO(v.day),
}
return newMenu
})
})
}
private refreshIfGood(s: Status) {
if (s.status.toString().match(/2\d\d/)) {
this.requestData()
}
if (s.status.toString().match(/2\d\d/)) this.refresh()
}
activateUpload() {
@@ -113,9 +106,7 @@ export class MenuEditComponent {
.open(MenuUploadComponent)
.afterClosed()
.subscribe(data => {
if (data) {
this.requestData()
}
if (data) this.refresh()
})
}

View File

@@ -1,7 +1,9 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Injectable, signal } from '@angular/core';
import { DateTime } from 'luxon';
import { Menu } from 'src/app/types/menu';
import { BehaviorSubject, catchError, map, of } from 'rxjs';
import { Menu, MenuAPI } from 'src/app/types/menu';
import { STATE } from 'src/app/types/state';
import { Status } from 'src/app/types/status';
import { environment } from 'src/environments/environment';
@@ -10,19 +12,57 @@ import { environment } from 'src/environments/environment';
})
export class MenuEditService {
constructor(private http: HttpClient) { }
private _menuItems = new BehaviorSubject<Menu[]>([])
public readonly menuItems = this._menuItems.asObservable()
private _state = signal(STATE.NOT_LOADED);
public readonly state = this._state.asReadonly();
private _error = signal<string | undefined>(undefined);
public readonly error = this._error.asReadonly();
getMenu(start?: DateTime | null, end?: DateTime | null) {
if (start && end) {
const body = { start: start.toString(), end: end.toString() }
return this.http.get<(Omit<Menu, 'day'> & { day: string })[]>(
environment.apiEndpoint + '/admin/menu',
{ withCredentials: true, params: body }
)
private seDates: {
start: DateTime | null,
end: DateTime | null
} = {
start: null,
end: null
}
return
public setDates(start: DateTime | null, end: DateTime | null) {
this.seDates.start = start
this.seDates.end = end
}
public refresh() {
this.getMenu()
}
private getMenu() {
if (!(this.seDates.start && this.seDates.end)) return
this._state.set(STATE.PENDING)
const body = { start: this.seDates.start.toString(), end: this.seDates.end.toString() }
this.http.get
<MenuAPI[]>
(environment.apiEndpoint + `/admin/menu`, { withCredentials: true, params: body })
.pipe(
catchError((err: Error) => {
this._state.set(STATE.ERROR)
this._error.set(err.message)
return of()
}),
map<MenuAPI[], Menu[]>(v =>
v.map(i => ({
...i,
day: DateTime.fromISO(i.day)
})))
).subscribe(v => {
this._error.set(undefined)
this._menuItems.next(v ?? [])
this._state.set(STATE.LOADED)
})
}
constructor(private http: HttpClient) { }
getOpts() {
return this.http.get<any>(environment.apiEndpoint + `/admin/menu/opts`, {
withCredentials: true,

View File

@@ -1,5 +1,7 @@
import { DateTime } from 'luxon'
export type MenuAPI = Omit<Menu, "day"> & {day: string}
export interface Menu {
_id: string
day: DateTime