Add branding assets for TIS tenant with logo and login image

This commit is contained in:
2026-05-11 13:36:06 +03:30
parent 13c791d86f
commit 8e1a021eec
32 changed files with 430 additions and 309 deletions
@@ -1,101 +1,37 @@
<ng-template #topbarStart>
<p-button (click)="toggleMenu()" icon="pi pi-bars" outlined size="large" />
</ng-template>
<ng-template #topbarCenter>
@if (posInfo()) {
<a [routerLink]="homeRouteLink" class="flex flex-col items-center justify-center gap-2">
<div class="w-8 h-8">
<img
[src]="posInfo()?.partner?.logo_url ?? placeholderLogo"
alt="Logo"
class="w-full h-full object-cover rounded-md bg-surface-card"
/>
</div>
<span class="text-base font-semibold">{{ posInfo()?.partner?.name }}</span>
</a>
}
</ng-template>
<ng-template #topbarEnd>
<div class="">
<p-menu #menu [model]="profileMenuItems" [popup]="true" />
<p-button (click)="menu.toggle($event)" icon="pi pi-user" outlined size="large" />
</div>
</ng-template>
<div
class="h-full relative"
(touchstart)="onTouchStart($event)"
(touchmove)="onTouchMove($event)"
(touchend)="onTouchEnd()"
>
@if (pullDistance() >= 0) {
@if (showSplash()) {
<pos-splash (onComplete)="onSplashCompleted()" />
} @else {
@defer (when !showSplash()) {
<div
class="w-full flex justify-center items-center py-2 min-h-10 text-xs text-surface-500 overflow-hidden fixed top-0 inset-x-0 z-1000 translate-y-[-20px]"
[ngStyle]="{ height: pullDistance() + 'px' }"
class="h-full relative"
(touchstart)="onTouchStart($event)"
(touchmove)="onTouchMove($event)"
(touchend)="onTouchEnd()"
>
<div
class="inline-flex p-3 rounded-full opacity-0 transition-opacity shadow-xl bg-surface-card"
[ngStyle]="{ opacity: pullDistance() / 100 }"
>
<i
class="pi pi-refresh text-[1.25rem] text-primary"
[ngStyle]="{ fontSize: `${1.25 + pullDistance() * 0.007}rem`, rotate: pullDistance() * 2 + 'deg' }"
></i>
</div>
<!-- [ngStyle]="{ transform: 'scale(1.' + pullDistance() * 10 + ')' }" -->
</div>
}
@if (loading()) {
<shared-page-loading />
} @else {
<!-- <div class="w-svw h-svh bg-surface-ground flex flex-col gap-4 p-4"> -->
<!-- <div class="w-full flex items-center gap-4 shrink-0 justify-between">
<div class="flex gap-2 items-center">
@if (posInfo()) {
<div class="w-10 h-10">
<img [src]="placeholderLogo" alt="Logo" class="w-full h-full object-cover rounded-md bg-surface-card" />
@if (pullDistance() >= 0) {
<div
class="w-full flex justify-center items-center py-2 min-h-10 text-xs text-surface-500 overflow-hidden fixed top-0 inset-x-0 z-1000 translate-y-[-20px]"
[ngStyle]="{ height: pullDistance() + 'px' }"
>
<div
class="inline-flex p-3 rounded-full opacity-0 transition-opacity shadow-xl bg-surface-card"
[ngStyle]="{ opacity: pullDistance() / 100 }"
>
<i
class="pi pi-refresh text-[1.25rem] text-primary"
[ngStyle]="{ fontSize: `${1.25 + pullDistance() * 0.007}rem`, rotate: pullDistance() * 2 + 'deg' }"
></i>
</div>
<span class="text-lg font-bold">
{{ posInfo()?.name }} ({{ posInfo()?.complex!.name }} - {{ posInfo()?.businessActivity!.name }})
</span>
}
</div>
<div class="flex gap-2 items-center">
<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>
<!-- [ngStyle]="{ transform: 'scale(1.' + pullDistance() * 10 + ')' }" -->
</div>
<div class="">
<p-menu #menu [model]="profileMenuItems" [popup]="true" />
<button pButton (click)="menu.toggle($event)" icon="pi pi-user" severity="contrast">
{{ posProfile()?.account?.username }}
</button>
</div>
</div>
</div> -->
@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>
}
@if (isAuth()) {
<router-outlet></router-outlet>
} @else {
@defer (when !isAuth()) {
<pos-pages-layout />
}
}
} @else {
<pos-main-menu-sidebar [(visible)]="mainMenuVisible" />
<router-outlet></router-outlet>
}
<!-- </div> -->
</div>
}
</div>
}
+12 -131
View File
@@ -1,143 +1,25 @@
import { AuthService } from '@/core';
import { LayoutService } from '@/layout/service/layout.service';
import { PageLoadingComponent } from '@/shared/components/page-loading.component';
import { CommonModule } from '@angular/common';
import {
AfterViewInit,
Component,
TemplateRef,
ViewChild,
computed,
inject,
signal,
} from '@angular/core';
import { RouterLinkWithHref, RouterOutlet } from '@angular/router';
import { MenuItem } from 'primeng/api';
import { Button, ButtonDirective } from 'primeng/button';
import { Card } from 'primeng/card';
import { Menu } from 'primeng/menu';
import { finalize } from 'rxjs';
import images from 'src/assets/images';
import { PosInfoStore, PosProfileStore } from '../store';
import { DeviceInfoStore } from '../store/device.store';
import { PosChooseCardsComponent } from './choose-pos.component';
import { PosMainMenuSidebarComponent } from './mainMenuSidebar/main-menu-sidebar.component';
import { Component, computed, signal } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { PosSplashComponent } from '../components/splash.component';
import { PosPagesLayoutComponent } from './pagesLayout/layout.component';
@Component({
selector: 'pos-layout',
templateUrl: './layout.component.html',
imports: [
PageLoadingComponent,
RouterOutlet,
ButtonDirective,
Card,
PosChooseCardsComponent,
Button,
PosMainMenuSidebarComponent,
Menu,
CommonModule,
RouterLinkWithHref,
],
templateUrl: 'layout.component.html',
imports: [PosSplashComponent, PosPagesLayoutComponent, RouterOutlet, CommonModule],
})
export class PosLayoutComponent implements AfterViewInit {
constructor() {}
export class PosLayoutComponent {
showSplash = signal(true);
isAuth = computed(() => window.location.pathname === '/auth');
// private readonly nativeBridgeService = inject(NativeBridgeService);
// private readonly toastService = inject(ToastService);
@ViewChild('topbarStart', { static: true }) topbarStart!: TemplateRef<any>;
@ViewChild('topbarCenter', { static: true }) topbarCenter!: TemplateRef<any>;
@ViewChild('topbarEnd', { static: true }) topbarEnd!: TemplateRef<any>;
private readonly posProfileStore = inject(PosProfileStore);
private readonly posInfoStore = inject(PosInfoStore);
private readonly deviceInfoStore = inject(DeviceInfoStore);
private readonly authService = inject(AuthService);
private readonly layoutService = inject(LayoutService);
mainMenuVisible = signal(false);
readonly posProfileLoading = computed(() => this.posProfileStore.loading());
readonly posProfile = computed(() => this.posProfileStore.entity());
readonly posProfileError = computed(() => this.posProfileStore.error());
readonly posInfoLoading = computed(() => this.posInfoStore.loading());
readonly posInfo = computed(() => this.posInfoStore.entity());
readonly posInfoError = computed(() => this.posInfoStore.error());
readonly loading = computed(() => this.posProfileLoading());
readonly error = computed(() => this.posProfileError());
readonly deviceInfo = computed(() => this.deviceInfoStore.entity());
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();
private touchStartY = 0;
private pulling = false;
pullDistance = signal(0);
readonly pullThreshold = 80;
homeRouteLink = '/pos';
async getData() {
await this.layoutService.changeFullPageLoading(true);
await this.deviceInfoStore.getData();
await this.posProfileStore.getData().subscribe({
next: () => {
this.posInfoStore
.getData()
.pipe(
finalize(() => {
this.layoutService.changeFullPageLoading(false);
}),
)
.subscribe();
},
});
}
toggleMenu() {
this.mainMenuVisible.update((v) => !v);
}
onChoosePos() {
this.getData();
}
ngOnInit() {
this.layoutService.changeIsFullPage(true);
this.getData();
}
ngAfterViewInit() {
this.layoutService.setTopbarStartSlot(this.topbarStart);
this.layoutService.setTopbarCenterSlot(this.topbarCenter);
this.layoutService.setTopbarEndSlot(this.topbarEnd);
}
ngOnDestroy() {
this.layoutService.changeIsFullPage(false);
this.layoutService.setTopbarStartSlot(null);
this.layoutService.setTopbarCenterSlot(null);
this.layoutService.setTopbarEndSlot(null);
onSplashCompleted() {
this.showSplash.set(false);
}
onTouchStart(event: TouchEvent) {
@@ -165,8 +47,7 @@ export class PosLayoutComponent implements AfterViewInit {
this.pulling = false;
this.pullDistance.set(0);
if (shouldRefresh) {
this.layoutService.changeIsFullPage(true);
this.getData();
this.showSplash.set(true);
}
}
}
@@ -8,8 +8,8 @@ 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';
import { IPosAccessibleResponse } from '../../models/pos.io';
import { PosService } from '../../modules/landing/services/main.service';
@Component({
selector: 'pos-choose-cards',
@@ -0,0 +1,77 @@
<ng-template #topbarStart>
<p-button (click)="toggleMenu()" icon="pi pi-bars" outlined size="large" />
</ng-template>
<ng-template #topbarCenter>
@if (posInfo()) {
<a [routerLink]="homeRouteLink" class="flex flex-col items-center justify-center gap-2">
<div class="w-8 h-8">
<img
[src]="posInfo()?.partner?.logo_url ?? placeholderLogo"
alt="Logo"
class="w-full h-full object-cover rounded-md bg-surface-card"
/>
</div>
<span class="text-base font-semibold">{{ posInfo()?.partner?.name }}</span>
</a>
}
</ng-template>
<ng-template #topbarEnd>
<div class="">
<p-menu #menu [model]="profileMenuItems" [popup]="true" />
<p-button (click)="menu.toggle($event)" icon="pi pi-user" outlined size="large" />
</div>
</ng-template>
@if (loading()) {
<shared-page-loading />
} @else {
<!-- <div class="w-svw h-svh bg-surface-ground flex flex-col gap-4 p-4"> -->
<!-- <div class="w-full flex items-center gap-4 shrink-0 justify-between">
<div class="flex gap-2 items-center">
@if (posInfo()) {
<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 }} - {{ posInfo()?.businessActivity!.name }})
</span>
}
</div>
<div class="flex gap-2 items-center">
<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="">
<p-menu #menu [model]="profileMenuItems" [popup]="true" />
<button pButton (click)="menu.toggle($event)" icon="pi pi-user" severity="contrast">
{{ posProfile()?.account?.username }}
</button>
</div>
</div>
</div> -->
@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 {
<pos-main-menu-sidebar [(visible)]="mainMenuVisible" />
<router-outlet></router-outlet>
}
<!-- </div> -->
}
@@ -0,0 +1,131 @@
import { AuthService } from '@/core';
import { LayoutService } from '@/layout/service/layout.service';
import { PageLoadingComponent } from '@/shared/components/page-loading.component';
import { CommonModule } from '@angular/common';
import {
AfterViewInit,
Component,
TemplateRef,
ViewChild,
computed,
inject,
signal,
} from '@angular/core';
import { RouterLinkWithHref, RouterOutlet } from '@angular/router';
import { MenuItem } from 'primeng/api';
import { Button, ButtonDirective } from 'primeng/button';
import { Card } from 'primeng/card';
import { Menu } from 'primeng/menu';
import images from 'src/assets/images';
import { PosInfoStore, PosProfileStore } from '../../store';
import { DeviceInfoStore } from '../../store/device.store';
import { PosMainMenuSidebarComponent } from '../mainMenuSidebar/main-menu-sidebar.component';
import { PosChooseCardsComponent } from './choose-pos.component';
@Component({
selector: 'pos-pages-layout',
templateUrl: './layout.component.html',
imports: [
PageLoadingComponent,
ButtonDirective,
Card,
PosChooseCardsComponent,
Button,
PosMainMenuSidebarComponent,
Menu,
CommonModule,
RouterLinkWithHref,
RouterOutlet,
],
})
export class PosPagesLayoutComponent implements AfterViewInit {
constructor() {}
// private readonly nativeBridgeService = inject(NativeBridgeService);
// private readonly toastService = inject(ToastService);
@ViewChild('topbarStart', { static: true }) topbarStart!: TemplateRef<any>;
@ViewChild('topbarCenter', { static: true }) topbarCenter!: TemplateRef<any>;
@ViewChild('topbarEnd', { static: true }) topbarEnd!: TemplateRef<any>;
private readonly posProfileStore = inject(PosProfileStore);
private readonly posInfoStore = inject(PosInfoStore);
private readonly deviceInfoStore = inject(DeviceInfoStore);
private readonly authService = inject(AuthService);
private readonly layoutService = inject(LayoutService);
mainMenuVisible = signal(false);
readonly posProfileLoading = computed(() => this.posProfileStore.loading());
readonly posProfile = computed(() => this.posProfileStore.entity());
readonly posProfileError = computed(() => this.posProfileStore.error());
readonly posInfoLoading = computed(() => this.posInfoStore.loading());
readonly posInfo = computed(() => this.posInfoStore.entity());
readonly posInfoError = computed(() => this.posInfoStore.error());
readonly loading = computed(() => this.posProfileLoading());
readonly error = computed(() => this.posProfileError());
readonly deviceInfo = computed(() => this.deviceInfoStore.entity());
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();
homeRouteLink = '/pos';
async getData() {
// await this.layoutService.changeFullPageLoading(true);
// await this.posProfileStore.getData().subscribe({
// next: () => {
// this.posInfoStore
// .getData()
// .pipe(
// finalize(() => {
// this.layoutService.changeFullPageLoading(false);
// }),
// )
// .subscribe();
// },
// });
}
toggleMenu() {
this.mainMenuVisible.update((v) => !v);
}
onChoosePos() {
this.getData();
}
ngAfterViewInit() {
this.layoutService.setTopbarStartSlot(this.topbarStart);
this.layoutService.setTopbarCenterSlot(this.topbarCenter);
this.layoutService.setTopbarEndSlot(this.topbarEnd);
}
ngOnDestroy() {
this.layoutService.changeIsFullPage(false);
this.layoutService.setTopbarStartSlot(null);
this.layoutService.setTopbarCenterSlot(null);
this.layoutService.setTopbarEndSlot(null);
}
}