feat: update branding and localization for various components and configurations
Production CI / validate-and-build (push) Failing after 1m15s

This commit is contained in:
2026-06-15 21:36:09 +03:30
parent f7f8a91a85
commit d1dd67aee7
22 changed files with 84 additions and 63 deletions
+8 -3
View File
@@ -1,5 +1,6 @@
import { inject, Injectable } from '@angular/core'; import { inject, Injectable } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import config from 'src/config';
import { AuthService } from '../../services/auth.service'; import { AuthService } from '../../services/auth.service';
import { BaseState, BaseStore } from '../base-store'; import { BaseState, BaseStore } from '../base-store';
@@ -123,7 +124,7 @@ export class GlobalStore extends BaseStore<GlobalState> {
readonly isOnline = this.computed((state) => state.isOnline); readonly isOnline = this.computed((state) => state.isOnline);
readonly notifications = this.computed((state) => state.notifications); readonly notifications = this.computed((state) => state.notifications);
readonly unreadNotifications = this.computed((state) => readonly unreadNotifications = this.computed((state) =>
state.notifications.filter((n) => !n.read), state.notifications.filter((n) => !n.read)
); );
readonly breadcrumbs = this.computed((state) => state.breadcrumbs); readonly breadcrumbs = this.computed((state) => state.breadcrumbs);
readonly preferences = this.computed((state) => state.preferences); readonly preferences = this.computed((state) => state.preferences);
@@ -144,7 +145,11 @@ export class GlobalStore extends BaseStore<GlobalState> {
} }
// Load theme from localStorage // Load theme from localStorage
const savedTheme = localStorage.getItem('app_theme') as 'light' | 'dark'; const savedTheme = config.isPosApplication
? 'light'
: (localStorage.getItem('app_theme') as 'light' | 'dark');
console.log('savedTheme', savedTheme);
if (savedTheme) { if (savedTheme) {
this.patchState({ theme: savedTheme }); this.patchState({ theme: savedTheme });
} }
@@ -250,7 +255,7 @@ export class GlobalStore extends BaseStore<GlobalState> {
*/ */
markNotificationRead(id: string): void { markNotificationRead(id: string): void {
const notifications = this._state().notifications.map((n) => const notifications = this._state().notifications.map((n) =>
n.id === id ? { ...n, read: true } : n, n.id === id ? { ...n, read: true } : n
); );
this.patchState({ notifications }); this.patchState({ notifications });
} }
@@ -18,12 +18,12 @@ export const CONSUMER_MENU_ITEMS = [
routerLink: ['/consumer/poses'], routerLink: ['/consumer/poses'],
}, },
{ {
label: 'صورت‌حساب‌ها', label: 'صورت‌حساب‌',
icon: 'pi pi-fw pi-receipt', icon: 'pi pi-fw pi-receipt',
routerLink: ['/consumer/sale_invoices'], routerLink: ['/consumer/sale_invoices'],
}, },
{ {
label: 'مشتری‌ها', label: 'مشتری',
icon: 'pi pi-fw pi-users', icon: 'pi pi-fw pi-users',
routerLink: ['/consumer/customers'], routerLink: ['/consumer/customers'],
}, },
@@ -28,7 +28,17 @@ export class ConsumerStatisticsLatestInvoicesComponent extends AbstractList<ISta
readonly invoicesPageRoute = consumerSaleInvoicesNamedRoutes.saleInvoices.meta.pagePath!(); readonly invoicesPageRoute = consumerSaleInvoicesNamedRoutes.saleInvoices.meta.pagePath!();
override setColumns(): void { override setColumns(): void {
this.columns = saleInvoiceListConfig.columns.filter((column) => column.field !== 'created_at'); this.columns = saleInvoiceListConfig.columns
.filter((column) => column.field !== 'created_at')
.map((header) => {
if (header.field === 'status') {
return {
...header,
customDataModel: this.status,
};
}
return header;
});
} }
override getDataRequest() { override getDataRequest() {
@@ -35,7 +35,7 @@ export class ConsumerSaleInvoiceStore extends EntityStore<
routerLink: consumerSaleInvoicesNamedRoutes.saleInvoices.meta.pagePath!(), routerLink: consumerSaleInvoicesNamedRoutes.saleInvoices.meta.pagePath!(),
}, },
{ {
title: this.entity()?.code, title: `صورت‌حساب ${this.entity()?.invoice_number}`,
routerLink: consumerSaleInvoicesNamedRoutes.saleInvoice.meta.pagePath!(invoiceId), routerLink: consumerSaleInvoicesNamedRoutes.saleInvoice.meta.pagePath!(invoiceId),
}, },
], ],
@@ -53,7 +53,7 @@ export class ConsumerSaleInvoiceStore extends EntityStore<
catchError((error) => { catchError((error) => {
this.setError(error); this.setError(error);
throw error; throw error;
}), })
) )
.subscribe((entity) => { .subscribe((entity) => {
this.patchState({ entity }); this.patchState({ entity });
@@ -8,20 +8,21 @@ export const PARTNER_MENU_ITEMS = [
routerLink: ['/partner'], routerLink: ['/partner'],
}, },
{ {
label: 'حساب‌های کاربری', label: 'مشتری',
icon: 'pi pi-fw pi-user',
routerLink: ['/partner/accounts'],
},
{
label: 'مشتری‌ها',
icon: 'pi pi-fw pi-users', icon: 'pi pi-fw pi-users',
routerLink: ['/partner/consumers'], routerLink: ['/partner/consumers'],
}, },
{ {
label: 'لایسنس‌ها', label: 'لایسنس',
icon: 'pi pi-fw pi-key', icon: 'pi pi-fw pi-key',
routerLink: ['/partner/licenses'], routerLink: ['/partner/licenses'],
}, },
{
label: 'حساب‌های کاربری',
icon: 'pi pi-fw pi-user',
routerLink: ['/partner/accounts'],
},
], ],
}, },
] as MenuItem[]; ] as MenuItem[];
@@ -5,7 +5,7 @@
<ng-template #topbarCenter> <ng-template #topbarCenter>
@if (posInfo()) { @if (posInfo()) {
<a [routerLink]="homeRouteLink" class="flex flex-col items-center justify-center gap-2"> <a [routerLink]="homeRouteLink" class="flex flex-col items-center justify-center gap-2">
<div class="h-8 w-8"> <div class="h-8 max-w-20">
<img <img
[src]="posInfo()?.partner?.logo_url ?? placeholderLogo" [src]="posInfo()?.partner?.logo_url ?? placeholderLogo"
alt="Logo" alt="Logo"
@@ -7,11 +7,11 @@ export const SUPER_ADMIN_MENU_ITEMS = [
icon: 'pi pi-fw pi-home', icon: 'pi pi-fw pi-home',
routerLink: ['/'], routerLink: ['/'],
}, },
{ // {
label: 'مشتری‌', // label: 'مشتری‌',
icon: 'pi pi-fw pi-id-card', // icon: 'pi pi-fw pi-id-card',
routerLink: ['/super_admin/consumers'], // routerLink: ['/super_admin/consumers'],
}, // },
{ {
label: 'شریک تجاری', label: 'شریک تجاری',
@@ -40,11 +40,11 @@ export const SUPER_ADMIN_MENU_ITEMS = [
// icon: 'pi pi-fw pi-barcode', // icon: 'pi pi-fw pi-barcode',
// routerLink: [stockKeepingUnitsNamedRoutes.stockKeepingUnits.meta.pagePath!()], // routerLink: [stockKeepingUnitsNamedRoutes.stockKeepingUnits.meta.pagePath!()],
// }, // },
{ // {
label: 'لایسنس‌', // label: 'لایسنس‌',
icon: 'pi pi-fw pi-key', // icon: 'pi pi-fw pi-key',
routerLink: ['/super_admin/licenses'], // routerLink: ['/super_admin/licenses'],
}, // },
{ {
label: 'برند دستگاه', label: 'برند دستگاه',
icon: 'pi pi-fw pi-receipt', icon: 'pi pi-fw pi-receipt',
@@ -3,13 +3,12 @@
[(visible)]="visible" [(visible)]="visible"
[modal]="true" [modal]="true"
[style]="{ width: '500px' }" [style]="{ width: '500px' }"
[closable]="true" [closable]="true">
>
<form [formGroup]="form" (submit)="submit()" class="flex flex-col gap-4"> <form [formGroup]="form" (submit)="submit()" class="flex flex-col gap-4">
<field-code [control]="form.controls.code" /> <field-code [control]="form.controls.code" />
<field-name [control]="form.controls.name" /> <field-name [control]="form.controls.name" />
<field-vat [control]="form.controls.VAT" /> <field-vat [control]="form.controls.VAT" />
<field-sku-type [control]="form.controls.type" /> <!-- <field-sku-type [control]="form.controls.type" /> -->
<app-form-footer-actions [submitLabel]="'ذخیره'" [loading]="submitLoading()" (onCancel)="close()" /> <app-form-footer-actions [submitLabel]="'ذخیره'" [loading]="submitLoading()" (onCancel)="close()" />
</form> </form>
@@ -9,16 +9,15 @@
[fullHeight]="fullHeight" [fullHeight]="fullHeight"
[showAdd]="true" [showAdd]="true"
[showEdit]="true" [showEdit]="true"
[showAll]="showAllBtn"
[allItemsPageRoute]="allItemsPageRoute()" [allItemsPageRoute]="allItemsPageRoute()"
(onEdit)="onEditClick($event)" (onEdit)="onEditClick($event)"
(onAdd)="openAddForm()" (onAdd)="openAddForm()"
(onRefresh)="refresh()" (onRefresh)="refresh()" />
/>
<stock-keeping-unit-form <stock-keeping-unit-form
[(visible)]="visibleForm" [(visible)]="visibleForm"
[initialValues]="selectedItemForEdit() || undefined" [initialValues]="selectedItemForEdit() || undefined"
[guildId]="guildId" [guildId]="guildId"
[editMode]="editMode()" [editMode]="editMode()"
(onSubmit)="refresh()" (onSubmit)="refresh()" />
/>
@@ -19,13 +19,14 @@ import { StockKeepingUnitFormComponent } from './form.component';
export class GuildStockKeepingUnitsListComponent extends AbstractList<IStockKeepingUnitResponse> { export class GuildStockKeepingUnitsListComponent extends AbstractList<IStockKeepingUnitResponse> {
@Input() guildId!: string; @Input() guildId!: string;
@Input() fullHeight?: boolean; @Input() fullHeight?: boolean;
@Input() showAllBtn?: boolean;
@Input() header: IColumn[] = skuListConfig.columns; @Input() header: IColumn[] = skuListConfig.columns;
listConfig = skuListConfig; listConfig = skuListConfig;
service = inject(StockKeepingUnitsService); service = inject(StockKeepingUnitsService);
allItemsPageRoute = computed(() => allItemsPageRoute = computed(() =>
stockKeepingUnitsNamedRoutes.stockKeepingUnits.meta.pagePath!(this.guildId), stockKeepingUnitsNamedRoutes.stockKeepingUnits.meta.pagePath!(this.guildId)
); );
override setColumns(): void { override setColumns(): void {
@@ -8,11 +8,11 @@ export const stockKeepingUnitsNamedRoutes: NamedRoutes<TStockKeepingUnitsRouteNa
path: 'stock-keeping-units', path: 'stock-keeping-units',
loadComponent: () => loadComponent: () =>
import('../../views/stockKeepingUnits/list.component').then( import('../../views/stockKeepingUnits/list.component').then(
(m) => m.GuildStockKeepingUnitsComponent, (m) => m.GuildStockKeepingUnitsComponent
), ),
meta: { meta: {
title: 'شناسه کالاها', title: 'شناسه کالاها',
pagePath: () => '/super_admin/guilds/:guildId/stock-keeping-units', pagePath: (guildId: string) => `/super_admin/guilds/${guildId}/stock-keeping-units`,
}, },
}, },
}; };
@@ -15,7 +15,7 @@
<admin-guild-goods-list [guildId]="guildId()" [showAllBtn]="true" class="w-full" /> <admin-guild-goods-list [guildId]="guildId()" [showAllBtn]="true" class="w-full" />
</div> </div>
<div class="flex max-h-125"> <div class="flex max-h-125">
<admin-guild-stock-keeping-units-list [guildId]="guildId()" class="w-full" /> <admin-guild-stock-keeping-units-list [guildId]="guildId()" [showAllBtn]="true" class="w-full" />
</div> </div>
<admin-guild-form <admin-guild-form
@@ -7,12 +7,11 @@
(onHide)="close()"> (onHide)="close()">
<form [formGroup]="form" (submit)="submit()" class="flex flex-col gap-4"> <form [formGroup]="form" (submit)="submit()" class="flex flex-col gap-4">
<app-input label="تعداد شارژ" [control]="form.controls.quantity" type="number" /> <app-input label="تعداد شارژ" [control]="form.controls.quantity" type="number" />
<!-- <uikit-datepicker <app-datepicker
label="انقضای فروش" label="انقضای فروش"
[control]="form.controls.activated_expires_at" [control]="form.controls.activated_expires_at"
name="activated_expires_at" name="activated_expires_at"
[minDate]="minDate" [min]="minDate" />
/> -->
<app-form-footer-actions [submitLabel]="'ذخیره'" [loading]="submitLoading()" (onCancel)="close()" /> <app-form-footer-actions [submitLabel]="'ذخیره'" [loading]="submitLoading()" (onCancel)="close()" />
</form> </form>
</shared-dialog> </shared-dialog>
@@ -3,6 +3,7 @@ import { InputComponent } from '@/shared/components';
import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component';
// import { UikitFlatpickrJalaliComponent } from '@/uikit'; // import { UikitFlatpickrJalaliComponent } from '@/uikit';
import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component';
import { DatepickerComponent } from '@/uikit';
import { nowJalali } from '@/utils'; import { nowJalali } from '@/utils';
import { Component, inject, Input } from '@angular/core'; import { Component, inject, Input } from '@angular/core';
import { ReactiveFormsModule, Validators } from '@angular/forms'; import { ReactiveFormsModule, Validators } from '@angular/forms';
@@ -17,7 +18,7 @@ import { AdminPartnerChargeLicenseTransactionsService } from '../services/charge
ReactiveFormsModule, ReactiveFormsModule,
InputComponent, InputComponent,
FormFooterActionsComponent, FormFooterActionsComponent,
// UikitFlatpickrJalaliComponent, DatepickerComponent,
], ],
}) })
export class PartnerChargeLicenseFormDialogComponent extends AbstractFormDialog< export class PartnerChargeLicenseFormDialogComponent extends AbstractFormDialog<
@@ -13,8 +13,7 @@
(onAdd)="openAddForm()" (onAdd)="openAddForm()"
(onDetails)="toSinglePage($event)" (onDetails)="toSinglePage($event)"
(onEdit)="onEditClick($event)" (onEdit)="onEditClick($event)"
(onRefresh)="refresh()" (onRefresh)="refresh()">
>
<ng-template #name let-item> <ng-template #name let-item>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<p-avatar <p-avatar
@@ -22,8 +21,7 @@
[title]="item.name" [title]="item.name"
[label]="item.logo_url ? '' : item.name.slice(0, 2)" [label]="item.logo_url ? '' : item.name.slice(0, 2)"
shape="square" shape="square"
size="large" size="large"></p-avatar>
></p-avatar>
{{ item.name }} {{ item.name }}
</div> </div>
</ng-template> </ng-template>
@@ -51,5 +49,4 @@
[editMode]="editMode()" [editMode]="editMode()"
[partnerId]="selectedItemForEdit()?.id" [partnerId]="selectedItemForEdit()?.id"
[initialValues]="selectedItemForEdit() || undefined" [initialValues]="selectedItemForEdit() || undefined"
(onSubmit)="refresh()" (onSubmit)="refresh()" />
/>
+6 -3
View File
@@ -1,6 +1,7 @@
import { computed, effect, Injectable, signal, TemplateRef } from '@angular/core'; import { computed, effect, Injectable, signal, TemplateRef } from '@angular/core';
import { MenuItem } from 'primeng/api'; import { MenuItem } from 'primeng/api';
import { BehaviorSubject, Subject } from 'rxjs'; import { BehaviorSubject, Subject } from 'rxjs';
import config from 'src/config';
export interface layoutConfig { export interface layoutConfig {
preset?: string; preset?: string;
@@ -39,7 +40,7 @@ export class LayoutService {
preset: 'Aura', preset: 'Aura',
primary: 'surface', primary: 'surface',
surface: null, surface: null,
darkTheme: localStorage.getItem('isDarkTheme') === 'true', darkTheme: config.isPosApplication ? false : localStorage.getItem('isDarkTheme') === 'true',
menuMode: 'static', menuMode: 'static',
}; };
@@ -81,10 +82,12 @@ export class LayoutService {
overlayOpen$ = this.overlayOpen.asObservable(); overlayOpen$ = this.overlayOpen.asObservable();
theme = computed(() => (this.layoutConfig()?.darkTheme ? 'light' : 'dark')); theme = computed(() =>
config.isPosApplication ? 'light' : this.layoutConfig()?.darkTheme ? 'light' : 'dark'
);
isSidebarActive = computed( isSidebarActive = computed(
() => this.layoutState().overlayMenuActive || this.layoutState().staticMenuMobileActive, () => this.layoutState().overlayMenuActive || this.layoutState().staticMenuMobileActive
); );
isDarkTheme = computed(() => this.layoutConfig().darkTheme); isDarkTheme = computed(() => this.layoutConfig().darkTheme);
@@ -7,7 +7,7 @@
class="bg-surface-card border-surface-border flex h-auto w-full max-w-md flex-col items-center justify-center rounded-lg border px-12 py-10 shadow"> class="bg-surface-card border-surface-border flex h-auto w-full max-w-md flex-col items-center justify-center rounded-lg border px-12 py-10 shadow">
<div class="mb-10 flex flex-col items-center justify-center gap-4 text-center"> <div class="mb-10 flex flex-col items-center justify-center gap-4 text-center">
<img [src]="logo" alt="مدیریت صورت‌حساب‌های مالیاتی" class="h-auto w-20" /> <img [src]="logo" alt="مدیریت صورت‌حساب‌های مالیاتی" class="h-auto w-20" />
<span class="text-lg font-bold"> به پنل مدیریت صورت‌حساب‌های مالیاتی سپاس خوش آمدید. </span> <span class="text-lg font-bold"> {{ authTitle }}</span>
</div> </div>
<!-- @if (activeStep() === "login") { --> <!-- @if (activeStep() === "login") { -->
@@ -1,3 +1,4 @@
import { brandingConfig } from '@/branding/branding.config';
import { IAuthResponse, TRoles } from '@/core'; import { IAuthResponse, TRoles } from '@/core';
import { AuthService } from '@/core/services/auth.service'; import { AuthService } from '@/core/services/auth.service';
import { ToastService } from '@/core/services/toast.service'; import { ToastService } from '@/core/services/toast.service';
@@ -34,6 +35,8 @@ export class AuthComponent {
selectedRole = signal<TRoles | undefined>(this.role); selectedRole = signal<TRoles | undefined>(this.role);
accountRole = signal<string>(''); accountRole = signal<string>('');
authTitle = `به پنل مدیریت صورت‌حساب‌های مالیاتی ${brandingConfig.title} خوش آمدید.`;
toSignUp = () => { toSignUp = () => {
this.activeStep.set('signup'); this.activeStep.set('signup');
}; };
@@ -60,12 +60,14 @@
</div> </div>
</shared-light-bottomsheet> </shared-light-bottomsheet>
<shared-invoice-payment-form-dialog @if (showCorrectionPaymentDialog()) {
[(visible)]="showCorrectionPaymentDialog" <shared-invoice-payment-form-dialog
[totalAmount]="correctionRequiredPayment()" [(visible)]="showCorrectionPaymentDialog"
[submitOrderLoading]="false" [totalAmount]="correctionRequiredPayment()"
[isGoldGuild]="false" [submitOrderLoading]="false"
[isUnknownCustomer]="true" [isGoldGuild]="false"
(onSubmitPayment)="confirmCorrectionPayment($event)" [isUnknownCustomer]="true"
(onClose)="cancelCorrectionPayment()" /> (onSubmitPayment)="confirmCorrectionPayment($event)"
(onClose)="cancelCorrectionPayment()" />
}
} }
@@ -1,12 +1,13 @@
import { IListConfig } from './list-config.model'; import { IListConfig } from './list-config.model';
export const categoryListConfig: IListConfig = { export const categoryListConfig: IListConfig = {
pageTitle: 'مدیریت دسته‌بندی‌های کالا', pageTitle: 'مدیریت دسته‌بندی‌ کالا',
addNewCtaLabel: 'افزودن دسته‌بندی', addNewCtaLabel: 'افزودن دسته‌بندی',
emptyPlaceholderTitle: 'دسته‌بندی‌ یافت نشد', emptyPlaceholderTitle: 'دسته‌بندی‌ یافت نشد',
emptyPlaceholderDescription: 'برای افزودن دسته‌بندی، روی دکمهٔ بالا کلیک کنید.', emptyPlaceholderDescription: 'برای افزودن دسته‌بندی، روی دکمهٔ بالا کلیک کنید.',
columns: [ columns: [
{ field: 'image_url', header: 'تصویر', type: 'thumbnail' }, { field: 'image_url', header: 'تصویر', type: 'thumbnail' },
{ field: 'name', header: 'عنوان' }, { field: 'name', header: 'عنوان' },
{ field: 'goods_count', header: 'تعداد کالاها' },
], ],
}; };
+1 -1
View File
@@ -19,7 +19,7 @@ export const brandingConfig: BrandingConfig = {
manifestPath: '/favicon/site.webmanifest', manifestPath: '/favicon/site.webmanifest',
themeColor: '#ffffff', themeColor: '#ffffff',
enableInstallPrompt: false, enableInstallPrompt: false,
title: 'پنل مدیریت', title: 'سپاس',
fullTitle: 'پنل مدیریت صورت‌حساب‌های مالیاتی', fullTitle: 'پنل مدیریت صورت‌حساب‌های مالیاتی',
support: { support: {
phones: [ phones: [
+2 -2
View File
@@ -1,5 +1,5 @@
import login from 'src/tenants/tis/assets/images/login.jpg'; import login from 'src/tenants/sepehr/assets/images/login.jpg';
import logo from 'src/tenants/tis/assets/images/logo.png'; import logo from 'src/tenants/sepehr/assets/images/logo.png';
export default { export default {
logo, logo,