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.refresh()
this.ac.accs.subscribe(d => { this.ac.accs.subscribe(d => {
this.users.data = d ?? [] this.users.data = d
}) })
} }

View File

@@ -1,21 +1,32 @@
<div id="upper-bar"> <div id="upper-bar">
<mat-form-field> <mat-form-field subscriptSizing="dynamic">
<mat-label>Wybierz tydzień</mat-label> <mat-label>Wybierz tydzień</mat-label>
<mat-date-range-input [rangePicker]="picker" [formGroup]="range"> <mat-date-range-input [rangePicker]="picker" [formGroup]="range">
<input matStartDate formControlName="start" (dateChange)="requestData()"> <input matStartDate formControlName="start">
<input matEndDate formControlName="end" (dateChange)="requestData()"> <input matEndDate formControlName="end">
</mat-date-range-input> </mat-date-range-input>
<mat-datepicker-toggle matIconSuffix [for]="picker"></mat-datepicker-toggle> <mat-datepicker-toggle matIconSuffix [for]="picker"></mat-datepicker-toggle>
<mat-date-range-picker #picker></mat-date-range-picker> <mat-date-range-picker #picker></mat-date-range-picker>
</mat-form-field> </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)="addDate()"><mat-icon>add</mat-icon></button>
<button mat-icon-button (click)="print()"><mat-icon>print</mat-icon></button> <button mat-icon-button (click)="print()"><mat-icon>print</mat-icon></button>
</div> </div>
@if (loading) { @if (loading) {
<mat-spinner></mat-spinner> <mat-spinner></mat-spinner>
} }
<table mat-table [dataSource]="dataSource"> <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>
}
@case (2) {
<table mat-table [dataSource]="dataSource">
<div matColumnDef="day"> <div matColumnDef="day">
<th mat-header-cell *matHeaderCellDef>Dzień</th> <th mat-header-cell *matHeaderCellDef>Dzień</th>
<td mat-cell *matCellDef="let element"> <td mat-cell *matCellDef="let element">
@@ -90,7 +101,10 @@
<tr mat-header-row *matHeaderRowDef="dcols"></tr> <tr mat-header-row *matHeaderRowDef="dcols"></tr>
<tr mat-row *matRowDef="let row; columns: dcols"></tr> <tr mat-row *matRowDef="let row; columns: dcols"></tr>
</table> </table>
}
}
</div>
@if (options) { @if (options) {
<datalist id="sn-fancy"> <datalist id="sn-fancy">

View File

@@ -16,3 +16,19 @@ button[mat-icon-button] {
color: gray; color: gray;
font-style: italic; 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 public options: any
constructor( constructor(
private ac: MenuEditService, protected ac: MenuEditService,
private dialog: MatDialog, private dialog: MatDialog,
private sb: MatSnackBar, private sb: MatSnackBar,
readonly ls: LocalStorageService readonly ls: LocalStorageService
) {} ) {
this.range.valueChanges.subscribe(v => {
ac.setDates(v.start!, v.end!)
})
ac.menuItems.subscribe(v => {
this.dataSource.data = v
})
}
print() { print() {
this.ac this.ac
@@ -74,7 +81,7 @@ export class MenuEditComponent {
.subscribe(s => this.refreshIfGood(s)) .subscribe(s => this.refreshIfGood(s))
break break
case 'file': case 'file':
this.requestData() this.refresh()
break break
default: default:
break break
@@ -83,29 +90,15 @@ export class MenuEditComponent {
}) })
} }
requestData() { refresh() {
this.loading = true this.ac.refresh()
this.ac.getOpts().subscribe(o => { this.ac.getOpts().subscribe(o => {
this.options = 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) { private refreshIfGood(s: Status) {
if (s.status.toString().match(/2\d\d/)) { if (s.status.toString().match(/2\d\d/)) this.refresh()
this.requestData()
}
} }
activateUpload() { activateUpload() {
@@ -113,9 +106,7 @@ export class MenuEditComponent {
.open(MenuUploadComponent) .open(MenuUploadComponent)
.afterClosed() .afterClosed()
.subscribe(data => { .subscribe(data => {
if (data) { if (data) this.refresh()
this.requestData()
}
}) })
} }

View File

@@ -1,7 +1,9 @@
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable, signal } from '@angular/core';
import { DateTime } from 'luxon'; 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 { Status } from 'src/app/types/status';
import { environment } from 'src/environments/environment'; import { environment } from 'src/environments/environment';
@@ -10,19 +12,57 @@ import { environment } from 'src/environments/environment';
}) })
export class MenuEditService { 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) { private seDates: {
if (start && end) { start: DateTime | null,
const body = { start: start.toString(), end: end.toString() } end: DateTime | null
return this.http.get<(Omit<Menu, 'day'> & { day: string })[]>( } = {
environment.apiEndpoint + '/admin/menu', start: null,
{ withCredentials: true, params: body } 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() { getOpts() {
return this.http.get<any>(environment.apiEndpoint + `/admin/menu/opts`, { return this.http.get<any>(environment.apiEndpoint + `/admin/menu/opts`, {
withCredentials: true, withCredentials: true,

View File

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