init to pos domain
This commit is contained in:
@@ -17,6 +17,7 @@
|
|||||||
"flatpickr-wrap": "^1.0.0",
|
"flatpickr-wrap": "^1.0.0",
|
||||||
"jalaliday": "^3.1.1",
|
"jalaliday": "^3.1.1",
|
||||||
"jest-editor-support": "*",
|
"jest-editor-support": "*",
|
||||||
|
"ngx-cookie-service": "^21.3.1",
|
||||||
"primeclt": "^0.1.5",
|
"primeclt": "^0.1.5",
|
||||||
"primeicons": "^7.0.0",
|
"primeicons": "^7.0.0",
|
||||||
"primeng": "^20",
|
"primeng": "^20",
|
||||||
|
|||||||
Generated
+338
-291
File diff suppressed because it is too large
Load Diff
+3
-1
@@ -1,10 +1,11 @@
|
|||||||
import { CONSUMER_ROUTES } from '@/domains/consumer/routes';
|
import { CONSUMER_ROUTES } from '@/domains/consumer/routes';
|
||||||
import { PARTNER_ROUTES } from '@/domains/partner/routes';
|
import { PARTNER_ROUTES } from '@/domains/partner/routes';
|
||||||
|
import { POS_ROUTES } from '@/domains/pos/routes';
|
||||||
import { PROVIDER_ROUTES } from '@/domains/provider/routes';
|
import { PROVIDER_ROUTES } from '@/domains/provider/routes';
|
||||||
import { SUPER_ADMIN_ROUTES } from '@/domains/superAdmin/routes';
|
import { SUPER_ADMIN_ROUTES } from '@/domains/superAdmin/routes';
|
||||||
import { AuthComponent } from '@/modules/auth/pages/auth.component';
|
import { AuthComponent } from '@/modules/auth/pages/auth.component';
|
||||||
import { Routes } from '@angular/router';
|
import { Routes } from '@angular/router';
|
||||||
import { AppLayout } from './app/layout/component/app.layout.component';
|
import { AppLayout } from './app/layout/default/app.layout.component';
|
||||||
import { Dashboard } from './app/pages/dashboard/dashboard';
|
import { Dashboard } from './app/pages/dashboard/dashboard';
|
||||||
import { Documentation } from './app/pages/documentation/documentation';
|
import { Documentation } from './app/pages/documentation/documentation';
|
||||||
import { Notfound } from './app/pages/notfound/notfound.component';
|
import { Notfound } from './app/pages/notfound/notfound.component';
|
||||||
@@ -18,6 +19,7 @@ export const appRoutes: Routes = [
|
|||||||
CONSUMER_ROUTES,
|
CONSUMER_ROUTES,
|
||||||
PROVIDER_ROUTES,
|
PROVIDER_ROUTES,
|
||||||
PARTNER_ROUTES,
|
PARTNER_ROUTES,
|
||||||
|
POS_ROUTES,
|
||||||
{ path: 'ng', component: Dashboard },
|
{ path: 'ng', component: Dashboard },
|
||||||
{ path: 'uikit', loadChildren: () => import('./app/pages/uikit/uikit.routes') },
|
{ path: 'uikit', loadChildren: () => import('./app/pages/uikit/uikit.routes') },
|
||||||
{ path: 'documentation', component: Documentation },
|
{ path: 'documentation', component: Documentation },
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
|
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
|
||||||
import { Injectable } from '@angular/core';
|
import { inject, Injectable } from '@angular/core';
|
||||||
|
import { CookieService } from 'ngx-cookie-service';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { environment } from 'src/environments/environment';
|
import { environment } from 'src/environments/environment';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ApiBaseUrlInterceptor implements HttpInterceptor {
|
export class ApiBaseUrlInterceptor implements HttpInterceptor {
|
||||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||||
|
console.log('first');
|
||||||
|
|
||||||
|
const cookieService = inject(CookieService);
|
||||||
// Prepare default headers but don't overwrite existing ones
|
// Prepare default headers but don't overwrite existing ones
|
||||||
const defaultHeaders: Record<string, string> = {
|
const defaultHeaders: Record<string, string> = {
|
||||||
Accept: 'application/json',
|
Accept: 'application/json',
|
||||||
@@ -14,6 +18,13 @@ export class ApiBaseUrlInterceptor implements HttpInterceptor {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const headersToSet: Record<string, string> = {};
|
const headersToSet: Record<string, string> = {};
|
||||||
|
|
||||||
|
if (req.url === '/api/v1/pos' || req.url.startsWith('/api/v1/pos/')) {
|
||||||
|
headersToSet['pos_Id'] = cookieService.get('posId');
|
||||||
|
|
||||||
|
// req.headers.set('posId', cookieService.get('posId'));
|
||||||
|
}
|
||||||
|
|
||||||
const isFormData = req.body instanceof FormData;
|
const isFormData = req.body instanceof FormData;
|
||||||
Object.keys(defaultHeaders).forEach((k) => {
|
Object.keys(defaultHeaders).forEach((k) => {
|
||||||
if (k === 'Content-Type' && isFormData) return;
|
if (k === 'Content-Type' && isFormData) return;
|
||||||
@@ -22,6 +33,9 @@ export class ApiBaseUrlInterceptor implements HttpInterceptor {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log('req.url');
|
||||||
|
console.log(req.url);
|
||||||
|
|
||||||
// Only prepend base URL if the request URL is relative (does not start with http or https)
|
// Only prepend base URL if the request URL is relative (does not start with http or https)
|
||||||
if (!/^https?:\/\//i.test(req.url)) {
|
if (!/^https?:\/\//i.test(req.url)) {
|
||||||
const apiReq = req.clone({ url: environment.apiBaseUrl + req.url, setHeaders: headersToSet });
|
const apiReq = req.clone({ url: environment.apiBaseUrl + req.url, setHeaders: headersToSet });
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
<consumer-complex-goods-list [businessId]="businessId()" [complexId]="complexId()" />
|
<consumer-complex-goods-list [businessId]="businessId()" [complexId]="complexId()" [guildId]="business()?.guild!.id" />
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ export class ConsumerComplexGoodsComponent {
|
|||||||
businessId = signal<string>(this.pageParams()['businessActivityId']);
|
businessId = signal<string>(this.pageParams()['businessActivityId']);
|
||||||
complexId = signal<string>(this.pageParams()['complexId']);
|
complexId = signal<string>(this.pageParams()['complexId']);
|
||||||
|
|
||||||
|
business = computed(() => this.businessStore.entity());
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.setBreadcrumb();
|
this.setBreadcrumb();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
<div class="flex flex-col gap-6">
|
<div class="flex flex-col gap-6">
|
||||||
<app-card-data cardTitle="اطلاعات پایانهی فروش" [editable]="true" [(editMode)]="editMode">
|
<app-card-data cardTitle="اطلاعات پایانهی فروش" [editable]="true" [(editMode)]="editMode">
|
||||||
|
<ng-template #moreAction>
|
||||||
|
<p-button type="button" variant="outlined" (onClick)="toPosLanding()"> ورود به پایانه </p-button>
|
||||||
|
</ng-template>
|
||||||
<div class="flex flex-col gap-4">
|
<div class="flex flex-col gap-4">
|
||||||
<div class="grid grid-cols-3 gap-4 items-center">
|
<div class="grid grid-cols-3 gap-4 items-center">
|
||||||
<app-key-value label="عنوان" [value]="pos()?.name" />
|
<app-key-value label="عنوان" [value]="pos()?.name" />
|
||||||
|
|||||||
+14
-1
@@ -3,6 +3,8 @@ import { AppCardComponent, KeyValueComponent } from '@/shared/components';
|
|||||||
import pageParamsUtils from '@/utils/page-params.utils';
|
import pageParamsUtils from '@/utils/page-params.utils';
|
||||||
import { Component, computed, effect, inject, signal } from '@angular/core';
|
import { Component, computed, effect, inject, signal } from '@angular/core';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { CookieService } from 'ngx-cookie-service';
|
||||||
|
import { Button } from 'primeng/button';
|
||||||
import { ConsumerPosFormComponent } from '../../components/poses/form.component';
|
import { ConsumerPosFormComponent } from '../../components/poses/form.component';
|
||||||
import { BusinessActivityStore } from '../../store/businessActivity.store';
|
import { BusinessActivityStore } from '../../store/businessActivity.store';
|
||||||
import { ConsumerComplexStore } from '../../store/complex.store';
|
import { ConsumerComplexStore } from '../../store/complex.store';
|
||||||
@@ -11,7 +13,7 @@ import { PosStore } from '../../store/pos.store';
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'superAdmin-user-pos',
|
selector: 'superAdmin-user-pos',
|
||||||
templateUrl: './single.component.html',
|
templateUrl: './single.component.html',
|
||||||
imports: [AppCardComponent, KeyValueComponent, ConsumerPosFormComponent],
|
imports: [AppCardComponent, KeyValueComponent, ConsumerPosFormComponent, Button],
|
||||||
})
|
})
|
||||||
export class SuperAdminUserPosComponent {
|
export class SuperAdminUserPosComponent {
|
||||||
private readonly businessStore = inject(BusinessActivityStore);
|
private readonly businessStore = inject(BusinessActivityStore);
|
||||||
@@ -19,6 +21,7 @@ export class SuperAdminUserPosComponent {
|
|||||||
private readonly store = inject(PosStore);
|
private readonly store = inject(PosStore);
|
||||||
private readonly route = inject(ActivatedRoute);
|
private readonly route = inject(ActivatedRoute);
|
||||||
private readonly breadcrumbService = inject(BreadcrumbService);
|
private readonly breadcrumbService = inject(BreadcrumbService);
|
||||||
|
private readonly cookieService = inject(CookieService);
|
||||||
|
|
||||||
pageParams = computed(() => pageParamsUtils(this.route));
|
pageParams = computed(() => pageParamsUtils(this.route));
|
||||||
|
|
||||||
@@ -42,6 +45,16 @@ export class SuperAdminUserPosComponent {
|
|||||||
this.store.getData(this.businessId(), this.complexId(), this.posId());
|
this.store.getData(this.businessId(), this.complexId(), this.posId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toPosLanding() {
|
||||||
|
this.cookieService.set('posId', this.posId(), {
|
||||||
|
sameSite: 'Lax', // or 'Strict' for same-site requests only
|
||||||
|
secure: false,
|
||||||
|
path: '/',
|
||||||
|
domain: 'localhost',
|
||||||
|
});
|
||||||
|
window.open('/pos', '_blank');
|
||||||
|
}
|
||||||
|
|
||||||
setBreadcrumb() {
|
setBreadcrumb() {
|
||||||
this.breadcrumbService.setItems([
|
this.breadcrumbService.setItems([
|
||||||
...this.businessStore.breadcrumbItems(),
|
...this.businessStore.breadcrumbItems(),
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './menuItems.const';
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { MenuItem } from 'primeng/api';
|
||||||
|
export const CONSUMER_MENU_ITEMS = [
|
||||||
|
{
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
label: 'فروش',
|
||||||
|
icon: 'pi pi-fw pi-home',
|
||||||
|
routerLink: ['/'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'فاکتورها',
|
||||||
|
icon: 'pi pi-fw pi-home',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
] as MenuItem[];
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
@if (loading()) {
|
||||||
|
<shared-page-loading />
|
||||||
|
} @else {
|
||||||
|
<router-outlet></router-outlet>
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { PageLoadingComponent } from '@/shared/components/page-loading.component';
|
||||||
|
import { Component, computed, inject } from '@angular/core';
|
||||||
|
import { RouterOutlet } from '@angular/router';
|
||||||
|
import { PosStore } from '../pos.store';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'pos-layout',
|
||||||
|
templateUrl: './layout.component.html',
|
||||||
|
imports: [PageLoadingComponent, RouterOutlet],
|
||||||
|
})
|
||||||
|
export class PosLayoutComponent {
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
private readonly store = inject(PosStore);
|
||||||
|
|
||||||
|
readonly loading = computed(() => this.store.loading());
|
||||||
|
readonly posInfo = computed(() => this.store.entity());
|
||||||
|
|
||||||
|
getData() {
|
||||||
|
this.store.getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.getData();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
export interface IPosInfoRawResponse {
|
||||||
|
name: true;
|
||||||
|
complex: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
tax_id: string;
|
||||||
|
};
|
||||||
|
businessActivity: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
guild: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IPosInfoResponse extends IPosInfoRawResponse {}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
const baseUrl = '/api/v1/pos';
|
||||||
|
|
||||||
|
export const POS_API_ROUTES = {
|
||||||
|
info: () => `${baseUrl}`,
|
||||||
|
goods: () => `${baseUrl}/goods`,
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './apiRoutes';
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { TAccountType } from '@/core/constants/accountTypes.const';
|
||||||
|
|
||||||
|
export interface IAccountRawResponse {
|
||||||
|
username: string;
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
export interface IAccountResponse extends IAccountRawResponse {}
|
||||||
|
|
||||||
|
export interface IAccountRequest {
|
||||||
|
username: string;
|
||||||
|
password?: string;
|
||||||
|
type: TAccountType;
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './accounts_io';
|
||||||
|
export * from './io';
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { TRoles } from '@/core';
|
||||||
|
|
||||||
|
export interface IUserRawResponse {
|
||||||
|
mobile_number: string;
|
||||||
|
first_name: string;
|
||||||
|
last_name: string;
|
||||||
|
fullname: string;
|
||||||
|
id: string;
|
||||||
|
role: TRoles;
|
||||||
|
}
|
||||||
|
export interface IUserResponse extends IUserRawResponse {}
|
||||||
|
|
||||||
|
export interface IUserRequest {
|
||||||
|
first_name: string;
|
||||||
|
last_name: string;
|
||||||
|
mobile_number: string;
|
||||||
|
role: TRoles;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { IPosInfoRawResponse, IPosInfoResponse } from '@/domains/pos/models/pos.io';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { POS_API_ROUTES } from '../constants';
|
||||||
|
import { IUserRawResponse, IUserResponse } from '../models';
|
||||||
|
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class PosService {
|
||||||
|
constructor(private http: HttpClient) {}
|
||||||
|
|
||||||
|
private apiRoutes = POS_API_ROUTES;
|
||||||
|
|
||||||
|
getInfo(): Observable<IPosInfoResponse> {
|
||||||
|
return this.http.get<IPosInfoRawResponse>(this.apiRoutes.info());
|
||||||
|
}
|
||||||
|
getGoods(): Observable<IUserResponse> {
|
||||||
|
return this.http.get<IUserRawResponse>(this.apiRoutes.goods());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './root.component';
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<span>صفحهی pos</span>
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
// import { CatalogRoleTagComponent } from '@/shared/catalog/roles';
|
||||||
|
import { PosStore } from '@/domains/pos/pos.store';
|
||||||
|
import { LayoutService } from '@/layout/service/layout.service';
|
||||||
|
import { Component, computed, inject } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'pos-landing',
|
||||||
|
templateUrl: './root.component.html',
|
||||||
|
})
|
||||||
|
export class PosLandingComponent {
|
||||||
|
private readonly store = inject(PosStore);
|
||||||
|
private readonly layoutService = inject(LayoutService);
|
||||||
|
|
||||||
|
private readonly posInfo = computed(() => this.store.entity());
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.layoutService.setPanelInfo({
|
||||||
|
title: `فروشگاه ${this.posInfo()?.complex.name} - ${this.posInfo()?.name}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import { EntityState, EntityStore } from '@/core/state';
|
||||||
|
import { computed, inject, Injectable } from '@angular/core';
|
||||||
|
import { CookieService } from 'ngx-cookie-service';
|
||||||
|
import { catchError, finalize, throwError } from 'rxjs';
|
||||||
|
import { IPosInfoResponse } from './models/pos.io';
|
||||||
|
import { PosService } from './modules/landing/services/main.service';
|
||||||
|
|
||||||
|
interface PosState extends EntityState<IPosInfoResponse> {
|
||||||
|
posId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class PosStore extends EntityStore<IPosInfoResponse, PosState> {
|
||||||
|
private readonly service = inject(PosService);
|
||||||
|
|
||||||
|
constructor(private readonly cookieService: CookieService) {
|
||||||
|
super({
|
||||||
|
loading: false,
|
||||||
|
error: null,
|
||||||
|
entity: null,
|
||||||
|
initialized: false,
|
||||||
|
isRefreshing: false,
|
||||||
|
posId: cookieService.get('posId'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
posId = computed(() => this._state().posId);
|
||||||
|
|
||||||
|
getData() {
|
||||||
|
this.patchState({ loading: true });
|
||||||
|
this.service
|
||||||
|
.getInfo()
|
||||||
|
.pipe(
|
||||||
|
finalize(() => {
|
||||||
|
this.patchState({ loading: false });
|
||||||
|
}),
|
||||||
|
catchError(() => {
|
||||||
|
this.patchState({
|
||||||
|
error: '',
|
||||||
|
});
|
||||||
|
return throwError('');
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.subscribe((entity) => {
|
||||||
|
this.patchState({ entity });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
override reset(): void {
|
||||||
|
this.setState({
|
||||||
|
loading: false,
|
||||||
|
error: null,
|
||||||
|
entity: null,
|
||||||
|
initialized: false,
|
||||||
|
isRefreshing: false,
|
||||||
|
posId: this.cookieService.get('posId'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { Route } from '@angular/router';
|
||||||
|
|
||||||
|
export const POS_ROUTES = {
|
||||||
|
path: 'pos',
|
||||||
|
loadComponent: () => import('./layouts/layout.component').then((m) => m.PosLayoutComponent),
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
loadComponent: () =>
|
||||||
|
import('./modules/landing/views/root.component').then((m) => m.PosLandingComponent),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
} as Route;
|
||||||
@@ -49,6 +49,12 @@ export class SuperAdminUserPosComponent {
|
|||||||
this.store.getData(this.businessId(), this.complexId(), this.posId());
|
this.store.getData(this.businessId(), this.complexId(), this.posId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toPosLanding() {
|
||||||
|
// @ts-ignore
|
||||||
|
window.cookieStore.set('posId', this.posId());
|
||||||
|
// window.open('pos')
|
||||||
|
}
|
||||||
|
|
||||||
setBreadcrumb() {
|
setBreadcrumb() {
|
||||||
this.breadcrumbService.setItems([
|
this.breadcrumbService.setItems([
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
<div class="flex flex-col gap-4">
|
|
||||||
<div>
|
|
||||||
<span class="text-sm text-muted-color font-semibold">Primary</span>
|
|
||||||
<div class="pt-2 flex gap-2 flex-wrap justify-start">
|
|
||||||
@for (primaryColor of primaryColors(); track primaryColor.name) {
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
[title]="primaryColor.name"
|
|
||||||
(click)="updateColors($event, 'primary', primaryColor)"
|
|
||||||
[ngClass]="{
|
|
||||||
'outline outline-primary': primaryColor.name === selectedPrimaryColor()
|
|
||||||
}"
|
|
||||||
class="cursor-pointer w-5 h-5 rounded-full flex shrink-0 items-center justify-center outline-offset-1 shadow"
|
|
||||||
[style]="{
|
|
||||||
'background-color': primaryColor?.name === 'noir' ? 'var(--text-color)' : primaryColor?.palette?.['500']
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
</button>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span class="text-sm text-muted-color font-semibold">Surface</span>
|
|
||||||
<div class="pt-2 flex gap-2 flex-wrap justify-start">
|
|
||||||
@for (surface of surfaces; track surface.name) {
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
[title]="surface.name"
|
|
||||||
(click)="updateColors($event, 'surface', surface)"
|
|
||||||
class="cursor-pointer w-5 h-5 rounded-full flex shrink-0 items-center justify-center p-0 outline-offset-1"
|
|
||||||
[ngClass]="{
|
|
||||||
'outline outline-primary': selectedSurfaceColor() ? selectedSurfaceColor() === surface.name : layoutService.layoutConfig().darkTheme ? surface.name === 'zinc' : surface.name === 'slate'
|
|
||||||
}"
|
|
||||||
[style]="{
|
|
||||||
'background-color': surface?.palette?.['500']
|
|
||||||
}"
|
|
||||||
></button>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col gap-2">
|
|
||||||
<span class="text-sm text-muted-color font-semibold">Presets</span>
|
|
||||||
<p-selectbutton [options]="presets" [ngModel]="selectedPreset()" (ngModelChange)="onPresetChange($event)" [allowEmpty]="false" size="small"></p-selectbutton>
|
|
||||||
</div>
|
|
||||||
<div *ngIf="showMenuModeButton()" class="flex flex-col gap-2">
|
|
||||||
<span class="text-sm text-muted-color font-semibold">Menu Mode</span>
|
|
||||||
<p-selectbutton [ngModel]="menuMode()" (ngModelChange)="onMenuModeChange($event)" [options]="menuModeOptions" [allowEmpty]="false" size="small"></p-selectbutton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -1,424 +0,0 @@
|
|||||||
import { CommonModule, isPlatformBrowser } from '@angular/common';
|
|
||||||
import { Component, computed, inject, PLATFORM_ID, signal } from '@angular/core';
|
|
||||||
import { FormsModule } from '@angular/forms';
|
|
||||||
import { Router } from '@angular/router';
|
|
||||||
import { $t, updatePreset, updateSurfacePalette } from '@primeuix/themes';
|
|
||||||
import Aura from '@primeuix/themes/aura';
|
|
||||||
import Lara from '@primeuix/themes/lara';
|
|
||||||
import Nora from '@primeuix/themes/nora';
|
|
||||||
import { PrimeNG } from 'primeng/config';
|
|
||||||
import { SelectButtonModule } from 'primeng/selectbutton';
|
|
||||||
import { LayoutService } from '../service/layout.service';
|
|
||||||
|
|
||||||
const presets = {
|
|
||||||
Aura,
|
|
||||||
Lara,
|
|
||||||
Nora,
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
declare type KeyOfType<T> = keyof T extends infer U ? U : never;
|
|
||||||
|
|
||||||
declare type SurfacesType = {
|
|
||||||
name?: string;
|
|
||||||
palette?: {
|
|
||||||
0?: string;
|
|
||||||
50?: string;
|
|
||||||
100?: string;
|
|
||||||
200?: string;
|
|
||||||
300?: string;
|
|
||||||
400?: string;
|
|
||||||
500?: string;
|
|
||||||
600?: string;
|
|
||||||
700?: string;
|
|
||||||
800?: string;
|
|
||||||
900?: string;
|
|
||||||
950?: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-configurator',
|
|
||||||
standalone: true,
|
|
||||||
imports: [CommonModule, FormsModule, SelectButtonModule],
|
|
||||||
templateUrl: './app.configurator.component.html',
|
|
||||||
host: {
|
|
||||||
class:
|
|
||||||
'hidden absolute top-13 right-0 w-72 p-4 bg-surface-0 dark:bg-surface-900 border border-surface rounded-border origin-top shadow-[0px_3px_5px_rgba(0,0,0,0.02),0px_0px_2px_rgba(0,0,0,0.05),0px_1px_4px_rgba(0,0,0,0.08)]',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
export class AppConfigurator {
|
|
||||||
router = inject(Router);
|
|
||||||
|
|
||||||
config: PrimeNG = inject(PrimeNG);
|
|
||||||
|
|
||||||
layoutService: LayoutService = inject(LayoutService);
|
|
||||||
|
|
||||||
platformId = inject(PLATFORM_ID);
|
|
||||||
|
|
||||||
primeng = inject(PrimeNG);
|
|
||||||
|
|
||||||
presets = Object.keys(presets);
|
|
||||||
|
|
||||||
showMenuModeButton = signal(!this.router.url.includes('auth'));
|
|
||||||
|
|
||||||
menuModeOptions = [
|
|
||||||
{ label: 'Static', value: 'static' },
|
|
||||||
{ label: 'Overlay', value: 'overlay' },
|
|
||||||
];
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
if (isPlatformBrowser(this.platformId)) {
|
|
||||||
this.onPresetChange(this.layoutService.layoutConfig().preset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
surfaces: SurfacesType[] = [
|
|
||||||
{
|
|
||||||
name: 'slate',
|
|
||||||
palette: {
|
|
||||||
0: '#ffffff',
|
|
||||||
50: '#f8fafc',
|
|
||||||
100: '#f1f5f9',
|
|
||||||
200: '#e2e8f0',
|
|
||||||
300: '#cbd5e1',
|
|
||||||
400: '#94a3b8',
|
|
||||||
500: '#64748b',
|
|
||||||
600: '#475569',
|
|
||||||
700: '#334155',
|
|
||||||
800: '#1e293b',
|
|
||||||
900: '#0f172a',
|
|
||||||
950: '#020617',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'gray',
|
|
||||||
palette: {
|
|
||||||
0: '#ffffff',
|
|
||||||
50: '#f9fafb',
|
|
||||||
100: '#f3f4f6',
|
|
||||||
200: '#e5e7eb',
|
|
||||||
300: '#d1d5db',
|
|
||||||
400: '#9ca3af',
|
|
||||||
500: '#6b7280',
|
|
||||||
600: '#4b5563',
|
|
||||||
700: '#374151',
|
|
||||||
800: '#1f2937',
|
|
||||||
900: '#111827',
|
|
||||||
950: '#030712',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'zinc',
|
|
||||||
palette: {
|
|
||||||
0: '#ffffff',
|
|
||||||
50: '#fafafa',
|
|
||||||
100: '#f4f4f5',
|
|
||||||
200: '#e4e4e7',
|
|
||||||
300: '#d4d4d8',
|
|
||||||
400: '#a1a1aa',
|
|
||||||
500: '#71717a',
|
|
||||||
600: '#52525b',
|
|
||||||
700: '#3f3f46',
|
|
||||||
800: '#27272a',
|
|
||||||
900: '#18181b',
|
|
||||||
950: '#09090b',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'neutral',
|
|
||||||
palette: {
|
|
||||||
0: '#ffffff',
|
|
||||||
50: '#fafafa',
|
|
||||||
100: '#f5f5f5',
|
|
||||||
200: '#e5e5e5',
|
|
||||||
300: '#d4d4d4',
|
|
||||||
400: '#a3a3a3',
|
|
||||||
500: '#737373',
|
|
||||||
600: '#525252',
|
|
||||||
700: '#404040',
|
|
||||||
800: '#262626',
|
|
||||||
900: '#171717',
|
|
||||||
950: '#0a0a0a',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'stone',
|
|
||||||
palette: {
|
|
||||||
0: '#ffffff',
|
|
||||||
50: '#fafaf9',
|
|
||||||
100: '#f5f5f4',
|
|
||||||
200: '#e7e5e4',
|
|
||||||
300: '#d6d3d1',
|
|
||||||
400: '#a8a29e',
|
|
||||||
500: '#78716c',
|
|
||||||
600: '#57534e',
|
|
||||||
700: '#44403c',
|
|
||||||
800: '#292524',
|
|
||||||
900: '#1c1917',
|
|
||||||
950: '#0c0a09',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'soho',
|
|
||||||
palette: {
|
|
||||||
0: '#ffffff',
|
|
||||||
50: '#ececec',
|
|
||||||
100: '#dedfdf',
|
|
||||||
200: '#c4c4c6',
|
|
||||||
300: '#adaeb0',
|
|
||||||
400: '#97979b',
|
|
||||||
500: '#7f8084',
|
|
||||||
600: '#6a6b70',
|
|
||||||
700: '#55565b',
|
|
||||||
800: '#3f4046',
|
|
||||||
900: '#2c2c34',
|
|
||||||
950: '#16161d',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'viva',
|
|
||||||
palette: {
|
|
||||||
0: '#ffffff',
|
|
||||||
50: '#f3f3f3',
|
|
||||||
100: '#e7e7e8',
|
|
||||||
200: '#cfd0d0',
|
|
||||||
300: '#b7b8b9',
|
|
||||||
400: '#9fa1a1',
|
|
||||||
500: '#87898a',
|
|
||||||
600: '#6e7173',
|
|
||||||
700: '#565a5b',
|
|
||||||
800: '#3e4244',
|
|
||||||
900: '#262b2c',
|
|
||||||
950: '#0e1315',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'ocean',
|
|
||||||
palette: {
|
|
||||||
0: '#ffffff',
|
|
||||||
50: '#fbfcfc',
|
|
||||||
100: '#F7F9F8',
|
|
||||||
200: '#EFF3F2',
|
|
||||||
300: '#DADEDD',
|
|
||||||
400: '#B1B7B6',
|
|
||||||
500: '#828787',
|
|
||||||
600: '#5F7274',
|
|
||||||
700: '#415B61',
|
|
||||||
800: '#29444E',
|
|
||||||
900: '#183240',
|
|
||||||
950: '#0c1920',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
selectedPrimaryColor = computed(() => {
|
|
||||||
return this.layoutService.layoutConfig().primary;
|
|
||||||
});
|
|
||||||
|
|
||||||
selectedSurfaceColor = computed(() => this.layoutService.layoutConfig().surface);
|
|
||||||
|
|
||||||
selectedPreset = computed(() => this.layoutService.layoutConfig().preset);
|
|
||||||
|
|
||||||
menuMode = computed(() => this.layoutService.layoutConfig().menuMode);
|
|
||||||
|
|
||||||
primaryColors = computed<SurfacesType[]>(() => {
|
|
||||||
const presetPalette =
|
|
||||||
presets[this.layoutService.layoutConfig().preset as KeyOfType<typeof presets>].primitive;
|
|
||||||
const colors = [
|
|
||||||
'emerald',
|
|
||||||
'green',
|
|
||||||
'lime',
|
|
||||||
'orange',
|
|
||||||
'amber',
|
|
||||||
'yellow',
|
|
||||||
'teal',
|
|
||||||
'cyan',
|
|
||||||
'sky',
|
|
||||||
'blue',
|
|
||||||
'indigo',
|
|
||||||
'violet',
|
|
||||||
'purple',
|
|
||||||
'fuchsia',
|
|
||||||
'pink',
|
|
||||||
'rose',
|
|
||||||
];
|
|
||||||
const palettes: SurfacesType[] = [{ name: 'noir', palette: {} }];
|
|
||||||
|
|
||||||
colors.forEach((color) => {
|
|
||||||
palettes.push({
|
|
||||||
name: color,
|
|
||||||
palette: presetPalette?.[
|
|
||||||
color as KeyOfType<typeof presetPalette>
|
|
||||||
] as SurfacesType['palette'],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return palettes;
|
|
||||||
});
|
|
||||||
|
|
||||||
getPresetExt() {
|
|
||||||
const color: SurfacesType =
|
|
||||||
this.primaryColors().find((c) => c.name === this.selectedPrimaryColor()) || {};
|
|
||||||
const preset = this.layoutService.layoutConfig().preset;
|
|
||||||
|
|
||||||
if (color.name === 'noir') {
|
|
||||||
return {
|
|
||||||
semantic: {
|
|
||||||
primary: {
|
|
||||||
50: '{surface.50}',
|
|
||||||
100: '{surface.100}',
|
|
||||||
200: '{surface.200}',
|
|
||||||
300: '{surface.300}',
|
|
||||||
400: '{surface.400}',
|
|
||||||
500: '{surface.500}',
|
|
||||||
600: '{surface.600}',
|
|
||||||
700: '{surface.700}',
|
|
||||||
800: '{surface.800}',
|
|
||||||
900: '{surface.900}',
|
|
||||||
950: '{surface.950}',
|
|
||||||
},
|
|
||||||
colorScheme: {
|
|
||||||
light: {
|
|
||||||
primary: {
|
|
||||||
color: '{primary.950}',
|
|
||||||
contrastColor: '#ffffff',
|
|
||||||
hoverColor: '{primary.800}',
|
|
||||||
activeColor: '{primary.700}',
|
|
||||||
},
|
|
||||||
highlight: {
|
|
||||||
background: '{primary.950}',
|
|
||||||
focusBackground: '{primary.700}',
|
|
||||||
color: '#ffffff',
|
|
||||||
focusColor: '#ffffff',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dark: {
|
|
||||||
primary: {
|
|
||||||
color: '{primary.50}',
|
|
||||||
contrastColor: '{primary.950}',
|
|
||||||
hoverColor: '{primary.200}',
|
|
||||||
activeColor: '{primary.300}',
|
|
||||||
},
|
|
||||||
highlight: {
|
|
||||||
background: '{primary.50}',
|
|
||||||
focusBackground: '{primary.300}',
|
|
||||||
color: '{primary.950}',
|
|
||||||
focusColor: '{primary.950}',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
if (preset === 'Nora') {
|
|
||||||
return {
|
|
||||||
semantic: {
|
|
||||||
primary: color.palette,
|
|
||||||
colorScheme: {
|
|
||||||
light: {
|
|
||||||
primary: {
|
|
||||||
color: '{primary.600}',
|
|
||||||
contrastColor: '#ffffff',
|
|
||||||
hoverColor: '{primary.700}',
|
|
||||||
activeColor: '{primary.800}',
|
|
||||||
},
|
|
||||||
highlight: {
|
|
||||||
background: '{primary.600}',
|
|
||||||
focusBackground: '{primary.700}',
|
|
||||||
color: '#ffffff',
|
|
||||||
focusColor: '#ffffff',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dark: {
|
|
||||||
primary: {
|
|
||||||
color: '{primary.500}',
|
|
||||||
contrastColor: '{surface.900}',
|
|
||||||
hoverColor: '{primary.400}',
|
|
||||||
activeColor: '{primary.300}',
|
|
||||||
},
|
|
||||||
highlight: {
|
|
||||||
background: '{primary.500}',
|
|
||||||
focusBackground: '{primary.400}',
|
|
||||||
color: '{surface.900}',
|
|
||||||
focusColor: '{surface.900}',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
semantic: {
|
|
||||||
primary: color.palette,
|
|
||||||
colorScheme: {
|
|
||||||
light: {
|
|
||||||
primary: {
|
|
||||||
color: '{primary.500}',
|
|
||||||
contrastColor: '#ffffff',
|
|
||||||
hoverColor: '{primary.600}',
|
|
||||||
activeColor: '{primary.700}',
|
|
||||||
},
|
|
||||||
highlight: {
|
|
||||||
background: '{primary.50}',
|
|
||||||
focusBackground: '{primary.100}',
|
|
||||||
color: '{primary.700}',
|
|
||||||
focusColor: '{primary.800}',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dark: {
|
|
||||||
primary: {
|
|
||||||
color: '{primary.400}',
|
|
||||||
contrastColor: '{surface.900}',
|
|
||||||
hoverColor: '{primary.300}',
|
|
||||||
activeColor: '{primary.200}',
|
|
||||||
},
|
|
||||||
highlight: {
|
|
||||||
background: 'color-mix(in srgb, {primary.400}, transparent 84%)',
|
|
||||||
focusBackground: 'color-mix(in srgb, {primary.400}, transparent 76%)',
|
|
||||||
color: 'rgba(255,255,255,.87)',
|
|
||||||
focusColor: 'rgba(255,255,255,.87)',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateColors(event: any, type: string, color: any) {
|
|
||||||
if (type === 'primary') {
|
|
||||||
this.layoutService.layoutConfig.update((state) => ({ ...state, primary: color.name }));
|
|
||||||
} else if (type === 'surface') {
|
|
||||||
this.layoutService.layoutConfig.update((state) => ({ ...state, surface: color.name }));
|
|
||||||
}
|
|
||||||
this.applyTheme(type, color);
|
|
||||||
|
|
||||||
event.stopPropagation();
|
|
||||||
}
|
|
||||||
|
|
||||||
applyTheme(type: string, color: any) {
|
|
||||||
if (type === 'primary') {
|
|
||||||
updatePreset(this.getPresetExt());
|
|
||||||
} else if (type === 'surface') {
|
|
||||||
updateSurfacePalette(color.palette);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onPresetChange(event: any) {
|
|
||||||
this.layoutService.layoutConfig.update((state) => ({ ...state, preset: event }));
|
|
||||||
const preset = presets[event as KeyOfType<typeof presets>];
|
|
||||||
const surfacePalette = this.surfaces.find(
|
|
||||||
(s) => s.name === this.selectedSurfaceColor(),
|
|
||||||
)?.palette;
|
|
||||||
$t()
|
|
||||||
.preset(preset)
|
|
||||||
.preset(this.getPresetExt())
|
|
||||||
.surfacePalette(surfacePalette)
|
|
||||||
.use({ useDefaultOptions: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
onMenuModeChange(event: string) {
|
|
||||||
this.layoutService.layoutConfig.update((prev) => ({ ...prev, menuMode: event }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
<div class="flex gap-4 top-8 right-8" [ngClass]="{'fixed':float()}">
|
|
||||||
<p-button type="button" (onClick)="toggleDarkMode()" [rounded]="true" [icon]="isDarkTheme() ? 'pi pi-moon' : 'pi pi-sun'" severity="secondary"></p-button>
|
|
||||||
<div class="relative">
|
|
||||||
<p-button icon="pi pi-palette" pStyleClass="@next" enterFromClass="hidden" enterActiveClass="animate-scalein" leaveToClass="hidden" leaveActiveClass="animate-fadeout" [hideOnOutsideClick]="true" type="button" rounded></p-button>
|
|
||||||
<app-configurator></app-configurator>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import { Component, computed, inject, input } from '@angular/core';
|
|
||||||
import { ButtonModule } from 'primeng/button';
|
|
||||||
import { StyleClassModule } from 'primeng/styleclass';
|
|
||||||
import { LayoutService } from '../service/layout.service';
|
|
||||||
import { AppConfigurator } from './app.configurator.component';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-floating-configurator',
|
|
||||||
imports: [CommonModule, ButtonModule, StyleClassModule, AppConfigurator],
|
|
||||||
templateUrl: './app.floatingconfigurator.component.html',
|
|
||||||
})
|
|
||||||
export class AppFloatingConfigurator {
|
|
||||||
LayoutService = inject(LayoutService);
|
|
||||||
|
|
||||||
float = input<boolean>(true);
|
|
||||||
|
|
||||||
isDarkTheme = computed(() => this.LayoutService.layoutConfig().darkTheme);
|
|
||||||
|
|
||||||
toggleDarkMode() {
|
|
||||||
this.LayoutService.layoutConfig.update((state) => ({ ...state, darkTheme: !state.darkTheme }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+4
-2
@@ -1,5 +1,7 @@
|
|||||||
<div class="layout-wrapper" [ngClass]="containerClass">
|
<div class="layout-wrapper" [ngClass]="containerClass">
|
||||||
<app-topbar></app-topbar>
|
<app-topbar [showMenu]="showMenu()">
|
||||||
|
<ng-container [ngTemplateOutlet]="topBarMoreAction"></ng-container>
|
||||||
|
</app-topbar>
|
||||||
@if (fullLoading) {
|
@if (fullLoading) {
|
||||||
<div class="flex justify-center align-items-center h-svh items-center">
|
<div class="flex justify-center align-items-center h-svh items-center">
|
||||||
<p-progressSpinner></p-progressSpinner>
|
<p-progressSpinner></p-progressSpinner>
|
||||||
@@ -8,7 +10,7 @@
|
|||||||
@if (showMenu()) {
|
@if (showMenu()) {
|
||||||
<app-sidebar></app-sidebar>
|
<app-sidebar></app-sidebar>
|
||||||
}
|
}
|
||||||
<div [class]="`layout-main-container ${!showMenu ? 'hideMenu' : ''}`">
|
<div [class]="`layout-main-container ${!showMenu() ? 'hideMenu' : ''}`">
|
||||||
<div class="layout-main flex flex-col gap-4">
|
<div class="layout-main flex flex-col gap-4">
|
||||||
@if (showBreadcrumb) {
|
@if (showBreadcrumb) {
|
||||||
<app-breadcrumb class="rounded-md overflow-hidden shrink-0" />
|
<app-breadcrumb class="rounded-md overflow-hidden shrink-0" />
|
||||||
+2
-2
@@ -11,7 +11,7 @@ import {
|
|||||||
TemplateRef,
|
TemplateRef,
|
||||||
ViewChild,
|
ViewChild,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { ActivatedRoute, NavigationEnd, Router, RouterModule } from '@angular/router';
|
import { NavigationEnd, Router, RouterModule } from '@angular/router';
|
||||||
import { ProgressSpinner } from 'primeng/progressspinner';
|
import { ProgressSpinner } from 'primeng/progressspinner';
|
||||||
import { filter, Subscription } from 'rxjs';
|
import { filter, Subscription } from 'rxjs';
|
||||||
import { LayoutService } from '../service/layout.service';
|
import { LayoutService } from '../service/layout.service';
|
||||||
@@ -44,10 +44,10 @@ export class AppLayout {
|
|||||||
|
|
||||||
@ViewChild(AppTopbar) appTopBar!: AppTopbar;
|
@ViewChild(AppTopbar) appTopBar!: AppTopbar;
|
||||||
@ContentChild('content', { static: true }) content?: TemplateRef<any> | null;
|
@ContentChild('content', { static: true }) content?: TemplateRef<any> | null;
|
||||||
|
@ContentChild('topBarMoreAction', { static: true }) topBarMoreAction?: TemplateRef<any> | null;
|
||||||
|
|
||||||
private breadcrumbService = inject(BreadcrumbService);
|
private breadcrumbService = inject(BreadcrumbService);
|
||||||
private authService = inject(AuthService);
|
private authService = inject(AuthService);
|
||||||
private route = inject(ActivatedRoute);
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public layoutService: LayoutService,
|
public layoutService: LayoutService,
|
||||||
+7
-4
@@ -1,15 +1,18 @@
|
|||||||
<div class="layout-topbar">
|
<div class="layout-topbar">
|
||||||
<div class="layout-topbar-logo-container">
|
<div class="layout-topbar-logo-container">
|
||||||
<button class="layout-menu-button layout-topbar-action" (click)="layoutService.onMenuToggle()">
|
@if (showMenu) {
|
||||||
<i class="pi pi-bars"></i>
|
<button class="layout-menu-button layout-topbar-action" (click)="layoutService.onMenuToggle()">
|
||||||
</button>
|
<i class="pi pi-bars"></i>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
<div class="layout-topbar-logo">
|
<div class="layout-topbar-logo">
|
||||||
<img [src]="logo" width="32" />
|
<img [src]="logo" width="32" />
|
||||||
<span class="text-xl">مدیریت کسبوکار</span>
|
<span class="text-xl">{{ panelTitle() }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="layout-topbar-actions">
|
<div class="layout-topbar-actions">
|
||||||
|
<ng-content></ng-content>
|
||||||
<div class="layout-config-menu">
|
<div class="layout-config-menu">
|
||||||
<button type="button" class="layout-topbar-action" (click)="toggleDarkMode()">
|
<button type="button" class="layout-topbar-action" (click)="toggleDarkMode()">
|
||||||
<i
|
<i
|
||||||
+3
-2
@@ -1,6 +1,6 @@
|
|||||||
import { AuthService } from '@/core/services/auth.service';
|
import { AuthService } from '@/core/services/auth.service';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { Component, computed, inject } from '@angular/core';
|
import { Component, computed, inject, Input } from '@angular/core';
|
||||||
import { RouterModule } from '@angular/router';
|
import { RouterModule } from '@angular/router';
|
||||||
import { MenuItem } from 'primeng/api';
|
import { MenuItem } from 'primeng/api';
|
||||||
import { Button } from 'primeng/button';
|
import { Button } from 'primeng/button';
|
||||||
@@ -16,7 +16,7 @@ import { LayoutService } from '../service/layout.service';
|
|||||||
templateUrl: './app.topbar.component.html',
|
templateUrl: './app.topbar.component.html',
|
||||||
})
|
})
|
||||||
export class AppTopbar {
|
export class AppTopbar {
|
||||||
items!: MenuItem[];
|
@Input() showMenu: boolean = false;
|
||||||
|
|
||||||
constructor(public layoutService: LayoutService) {}
|
constructor(public layoutService: LayoutService) {}
|
||||||
private authService: AuthService = inject(AuthService);
|
private authService: AuthService = inject(AuthService);
|
||||||
@@ -28,6 +28,7 @@ export class AppTopbar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
username = computed(() => this.authService.currentAccount()?.username || 'کاربر ناشناس');
|
username = computed(() => this.authService.currentAccount()?.username || 'کاربر ناشناس');
|
||||||
|
panelTitle = computed(() => this.layoutService.panelInfo()?.title || 'پنل مدیریت کسبوکار');
|
||||||
|
|
||||||
logout = () => {
|
logout = () => {
|
||||||
this.authService.logout();
|
this.authService.logout();
|
||||||
@@ -10,6 +10,10 @@ export interface layoutConfig {
|
|||||||
menuMode?: string;
|
menuMode?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface IPanelInfo {
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
interface LayoutState {
|
interface LayoutState {
|
||||||
staticMenuDesktopInactive?: boolean;
|
staticMenuDesktopInactive?: boolean;
|
||||||
overlayMenuActive?: boolean;
|
overlayMenuActive?: boolean;
|
||||||
@@ -30,7 +34,7 @@ interface MenuChangeEvent {
|
|||||||
export class LayoutService {
|
export class LayoutService {
|
||||||
_config: layoutConfig = {
|
_config: layoutConfig = {
|
||||||
preset: 'Aura',
|
preset: 'Aura',
|
||||||
primary: 'blue',
|
primary: 'surface',
|
||||||
surface: null,
|
surface: null,
|
||||||
darkTheme: localStorage.getItem('isDarkTheme') === 'true',
|
darkTheme: localStorage.getItem('isDarkTheme') === 'true',
|
||||||
menuMode: 'static',
|
menuMode: 'static',
|
||||||
@@ -59,6 +63,10 @@ export class LayoutService {
|
|||||||
|
|
||||||
public menuItems = signal<MenuItem[]>([]);
|
public menuItems = signal<MenuItem[]>([]);
|
||||||
|
|
||||||
|
public panelInfo = signal<IPanelInfo>({
|
||||||
|
title: 'پنل مدیریت کسبوکار',
|
||||||
|
});
|
||||||
|
|
||||||
menuSource$ = this.menuSource.asObservable();
|
menuSource$ = this.menuSource.asObservable();
|
||||||
|
|
||||||
resetSource$ = this.resetSource.asObservable();
|
resetSource$ = this.resetSource.asObservable();
|
||||||
@@ -206,4 +214,8 @@ export class LayoutService {
|
|||||||
setMenuItems(items: MenuItem[]) {
|
setMenuItems(items: MenuItem[]) {
|
||||||
this.menuItems.set(items);
|
this.menuItems.set(items);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setPanelInfo(info: IPanelInfo) {
|
||||||
|
this.panelInfo.set(info);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user