ui update, init to consumer statistics and manage pos user types

This commit is contained in:
2026-04-13 13:22:40 +03:30
parent d4dff4ebfd
commit af3123e61e
85 changed files with 1054 additions and 407 deletions
@@ -0,0 +1,24 @@
@if (loading()) {
<shared-page-loading />
} @else if (items()) {
<div class="h-full w-full max-w-6xl mx-auto flex flex-col items-center justify-center gap-10">
<p-message severity="warn"> برای ادامه می‌بایست یکی از پایانه‌های زیر را انتخاب کنید. </p-message>
<div class="grid grid-cols-2 gap-10 items-stretch w-full">
@for (item of items(); track $index) {
<p-card
class="border border-surface-border bg-surface-card rounded-2xl cursor-pointer hover:bg-surface-50! transition-colors"
(click)="selectPos(item)"
>
<div class="flex flex-col gap-2 items-center justify-between shrink-0">
<i class="pi pi-select text-6xl! mb-3 text-surface-card" aria-hidden="true"></i>
<span class="text-xl font-semibold mb-2 block"> {{ item.name }} </span>
<span class="inline-block text-base">
{{ item.complex.business_activity.name }} - {{ item.complex.name }}
</span>
<button pButton class="w-full max-w-xs mx-auto mt-4">انتخاب پایانه</button>
</div>
</p-card>
}
</div>
</div>
}
@@ -0,0 +1,59 @@
import { Maybe } from '@/core';
import { PageLoadingComponent } from '@/shared/components/page-loading.component';
import { HttpErrorResponse } from '@angular/common/http';
import { Component, EventEmitter, inject, Output, signal } from '@angular/core';
import { CookieService } from 'ngx-cookie-service';
import { ButtonDirective } from 'primeng/button';
import { Card } from 'primeng/card';
import { Message } from 'primeng/message';
import { catchError, finalize } from 'rxjs';
import { COOKIE_KEYS } from 'src/assets/constants';
import { IPosAccessibleResponse } from '../models/pos.io';
import { PosService } from '../modules/landing/services/main.service';
@Component({
selector: 'pos-choose-cards',
templateUrl: './choose-pos.component.html',
imports: [PageLoadingComponent, Card, ButtonDirective, Message],
})
export class PosChooseCardsComponent {
@Output() onChoose = new EventEmitter();
private readonly service = inject(PosService);
private readonly cookieService = inject(CookieService);
loading = signal(true);
items = signal<IPosAccessibleResponse[]>([]);
error = signal<Maybe<HttpErrorResponse>>(null);
getData() {
this.loading.set(true);
this.service
.getAccessible()
.pipe(
finalize(() => this.loading.set(false)),
catchError((err) => {
this.error.set(err);
throw err;
}),
)
.subscribe((res) => {
this.items.set(res?.data || []);
});
}
ngOnInit() {
this.getData();
}
selectPos(pos: IPosAccessibleResponse) {
this.cookieService.set(COOKIE_KEYS.POS_ID, pos.id, {
sameSite: 'Lax', // or 'Strict' for same-site requests only
secure: false,
path: '/',
domain: 'localhost',
});
this.onChoose.emit();
}
}
@@ -7,17 +7,42 @@
<div class="w-10 h-10">
<img [src]="placeholderLogo" alt="Logo" class="w-full h-full object-cover rounded-md bg-surface-card" />
</div>
<span class="text-lg font-bold">{{ posInfo()?.name }} - فروشگاه {{ posInfo()?.complex?.name }}</span>
@if (posInfo()) {
<span class="text-lg font-bold">{{ posInfo()?.name }} - فروشگاه {{ posInfo()?.complex?.name }}</span>
}
</div>
<div class="flex gap-2 items-center">
<div class="bg-surface-card py-1 px-3 rounded-md shadow-sm">
<div class="bg-surface-card px-3 rounded-md shadow-sm h-9 flex items-center">
<span [jalaliDate]="now" jalaliFormat="dddd YYYY/MM/DD" class="text-base"></span>
</div>
<div class="bg-surface-card py-1 px-3 rounded-md shadow-sm">
<span class="text-base"> {{ userInfo()?.username }} </span>
<div class="">
<p-menu #menu [model]="profileMenuItems" [popup]="true" />
<button pButton (click)="menu.toggle($event)" icon="pi pi-user" severity="contrast">
{{ userInfo()?.username }}
</button>
</div>
</div>
</div>
<router-outlet></router-outlet>
@if (error()) {
@switch (error()?.status) {
@case (412) {
<pos-choose-cards class="h-full" (onChoose)="onChoosePos()" />
}
@default {
<div class="h-full w-full flex items-center justify-center">
<p-card class="border border-surface-border">
<div class="flex flex-col gap-2 items-center justify-between shrink-0">
<i class="pi pi-exclamation-triangle text-6xl! mb-3 text-error" aria-hidden="true"></i>
<span class="text-xl font-semibold mb-2 text-error block"> عدم دسترسی </span>
<p class="inline-block text-base">متاسفانه امکان دسترسی برای کاربر شما فراهم نیست</p>
<button pButton outlined severity="danger" class="mt-5 w-32" (click)="logout()">خروج</button>
</div>
</p-card>
</div>
}
}
} @else {
<router-outlet></router-outlet>
}
</div>
}
@@ -3,13 +3,26 @@ import { PageLoadingComponent } from '@/shared/components/page-loading.component
import { JalaliDateDirective } from '@/shared/directives';
import { Component, computed, inject } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { MenuItem } from 'primeng/api';
import { ButtonDirective } from 'primeng/button';
import { Card } from 'primeng/card';
import { Menu } from 'primeng/menu';
import images from 'src/assets/images';
import { PosStore } from '../pos.store';
import { PosChooseCardsComponent } from './choose-pos.component';
@Component({
selector: 'pos-layout',
templateUrl: './layout.component.html',
imports: [PageLoadingComponent, RouterOutlet, JalaliDateDirective],
imports: [
PageLoadingComponent,
RouterOutlet,
JalaliDateDirective,
Menu,
ButtonDirective,
Card,
PosChooseCardsComponent,
],
})
export class PosLayoutComponent {
constructor() {}
@@ -19,8 +32,26 @@ export class PosLayoutComponent {
readonly loading = computed(() => this.store.loading());
readonly posInfo = computed(() => this.store.entity());
readonly error = computed(() => this.store.error());
readonly userInfo = computed(() => this.authService.currentAccount());
logout = () => {
this.authService.logout();
};
profileMenuItems: MenuItem[] = [
{
label: 'راهنمای سامانه',
icon: 'pi pi-question-circle',
disabled: true,
},
{
label: 'خروج',
icon: 'pi pi-sign-out',
command: this.logout,
},
];
placeholderLogo = images.placeholders.logo;
now = new Date();
@@ -29,6 +60,10 @@ export class PosLayoutComponent {
this.store.getData().subscribe();
}
onChoosePos() {
this.getData();
}
ngOnInit() {
this.getData();
}