Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div class="flex flex-col">
<div class="flex flex-col h-full">
@if ((isLoading$ | async) || (loadingScript | async)) { <proxy-loader></proxy-loader> }
<div class="flex justify-start gap-2 mb-6" [class.items-start]="isEditMode" [class.items-center]="!isEditMode">
<button type="button" matIconButton [routerLink]="['/app', 'features']">
Expand Down Expand Up @@ -341,7 +341,7 @@ <h4 class="my-0 font-semibold text-base text-on-surface">Add New Block</h4>
@if (webhookEventsData?.length) {
<div class="bg-color p-4">
<mat-accordion>
@for (event of webhookEventsData; track event) {
@for (event of webhookEventsData; track event.value) {
<mat-expansion-panel class="!mb-2">
<mat-expansion-panel-header>
<mat-panel-title>
Expand Down Expand Up @@ -558,17 +558,13 @@ <h3 class="template-card__title">Custom Mapping</h3>
<ng-template #configureMethodTemplate let-selectedIndex="selectedIndex">
<div class="flex gap-4 my-6 grow min-h-[400px] max-lg:flex-col">
<div class="max-xl:flex-col flex gap-4 grow">
<mat-card class="shrink-0 self-start max-xl:w-full">
<mat-card-header> Services </mat-card-header>
<mat-card-content class="!p-2">
<proxy-service-list
[services]="(selectedMethod | async)?.method_services ?? []"
[selectedIndex]="selectedServiceIndex"
[getFormAt]="getServiceFormAt"
(serviceSelect)="selectedServiceIndex = $event"
></proxy-service-list>
</mat-card-content>
</mat-card>
<proxy-service-list
class="shrink-0 self-start max-xl:w-full"
[services]="getServiceListItems((selectedMethod | async)?.method_services ?? [])"
[selectedIndex]="selectedServiceIndex"
[getFormAt]="getServiceFormAt"
(serviceSelect)="selectedServiceIndex = $event"
></proxy-service-list>
<ng-container *ngTemplateOutlet="configureMethodFormContent"></ng-container>
</div>
@if (isEditMode) {
Expand Down Expand Up @@ -965,7 +961,7 @@ <h2 class="mat-h2 font-semibold text-light-gray mb-0" [style.color]="getEffectiv
<mat-icon class="!text-base !w-4">settings</mat-icon>
<span class="mat-body-2 font-semibold">Session & Token Settings</span>
</div>
<div class="bg-color flex flex-col gap-1 px-4 pt-4 pb-4">
<div class="bg-color flex flex-col gap-2 px-4 pt-4 pb-4">
<ng-container
*ngTemplateOutlet="
inputField;
Expand Down Expand Up @@ -1071,7 +1067,7 @@ <h2 class="mat-h2 font-semibold text-light-gray mb-0" [style.color]="getEffectiv
<div class="card-with-header">
<div class="card-with-header--header justify-between">
<div class="flex items-center gap-2">
<mat-icon class="!text-base !w-4 text-color">html</mat-icon>
<mat-icon class="!text-base !w-4 text-color">code</mat-icon>
<span class="mat-body-2 font-semibold">Button Container Div</span>
</div>
<proxy-copy-button [copyData]="demoDiv()" tooltip="Copy Code"></proxy-copy-button>
Expand All @@ -1095,7 +1091,6 @@ <h2 class="mat-h2 font-semibold text-light-gray mb-0" [style.color]="getEffectiv
<div class="flex items-center justify-start mt-6">
<button type="button" matButton="filled" (click)="previewFeature()">Preview</button>
</div>
<!-- <div id="userProxyContainer"></div> -->
}
</ng-template>

Expand All @@ -1106,7 +1101,7 @@ <h2 class="mat-h2 font-semibold text-light-gray mb-0" [style.color]="getEffectiv
<mat-icon class="!text-base !w-4">webhook</mat-icon>
<span class="mat-body-2 font-semibold">Webhook Configuration</span>
</div>
<div class="bg-color flex flex-col gap-1 px-4 pt-4 pb-4">
<div class="bg-color flex flex-col gap-2 px-4 pt-4 pb-4">
<ng-container
*ngTemplateOutlet="
inputField;
Expand Down Expand Up @@ -1862,7 +1857,7 @@ <h5 class="mat-h5 my-0">Taxes</h5>
<input
matInput
type="text"
placeholder="Enter logo URL"
placeholder="https://example.com/logo.png"
[formControl]="featureForm.get('brandingDetails.logo_url')"
/>
</mat-form-field>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ import { SimpleDialogComponent } from './simple-dialog/simple-dialog.component';
import { CreatePlanDialogComponent } from './create-plan-dialog/create-plan-dialog.component';
import { CreateTaxDialogComponent } from './create-tax-dialog/create-tax-dialog.component';
import { ConfirmDialogComponent } from '@proxy/ui/confirm-dialog';
import { ServiceListComponent } from '@proxy/ui/service-list';
import { ServiceListComponent, ServiceListItem } from '@proxy/ui/service-list';
import { UiSettingsService } from '../../layout/ui-settings.service';
type ServiceFormGroup = FormGroup<{
requirements: FormGroup<{
[key: string]: FormControl<any>;
Expand Down Expand Up @@ -153,6 +154,7 @@ export class CreateFeatureComponent extends BaseComponent implements OnDestroy,
private ngZone = inject(NgZone);
private dialog = inject(MatDialog);
private http = inject(HttpClient);
private uiSettings = inject(UiSettingsService);

public taxes: any[] = [];
public createPlanForm: any;
Expand Down Expand Up @@ -216,6 +218,22 @@ export class CreateFeatureComponent extends BaseComponent implements OnDestroy,
public selectedServiceIndex = 0;
public selectedSubscriptionServiceIndex = -2;

public getServiceListItems(services: IMethodService[]): ServiceListItem[] {
return (services ?? []).map((s) => ({ name: s.name, icon: this._serviceIcon(s.name) }));
}

private _serviceIcon(name: string): string {
const n = (name ?? '').toLowerCase();
if (n.includes('google')) return 'g_mobiledata';
if (n.includes('apple')) return 'apple';
if (n.includes('password') || n.includes('login')) return 'lock';
if (n.includes('otp') || n.includes('sms') || n.includes('msg')) return 'sms';
if (n.includes('email') || n.includes('mail')) return 'mail';
if (n.includes('whatsapp')) return 'chat';
if (n.includes('phone') || n.includes('mobile')) return 'smartphone';
return 'miscellaneous_services';
}

/** Duplicate of service form used inside configure method dialog; patched back to serviceForm on close */
public configureMethodDialogForm: ServiceFormGroup | null = null;

Expand Down Expand Up @@ -684,19 +702,14 @@ export class CreateFeatureComponent extends BaseComponent implements OnDestroy,

/** Default primary color: black in light theme, white in dark theme */
public getDefaultPrimaryColor(): string {
return typeof localStorage !== 'undefined' && localStorage.getItem('selected-theme') === 'dark-theme'
? '#ffffff'
: '#000000';
return this.uiSettings.theme === 'dark-theme' ? '#ffffff' : '#000000';
}

/** Returns the effective primary color based on the selected branding theme */
public getEffectivePrimaryColor(): string {
const theme = this.featureForm.get('brandingDetails.theme')?.value;
const isDark =
theme === WidgetTheme.Dark ||
(theme === WidgetTheme.System &&
typeof localStorage !== 'undefined' &&
localStorage.getItem('selected-theme') === 'dark-theme');
theme === WidgetTheme.Dark || (theme === WidgetTheme.System && this.uiSettings.theme === 'dark-theme');
if (isDark) {
return this.featureForm.get('brandingDetails.dark_theme_primary_color')?.value || '#ffffff';
}
Expand Down
32 changes: 19 additions & 13 deletions apps/36-blocks/src/app/layout/layout.component.html
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
<div class="flex w-full h-full" [class.collapsed-sidebar]="!isSideNavOpen()">
<div class="flex w-full h-full" [class.collapsed-sidebar]="!sideNavService.isSideNavOpen()">
<div
class="flex flex-col transition-all duration-200 mat-drawer-content max-desktop:fixed max-desktop:z-[999] max-desktop:top-0 max-desktop:bottom-0 bg-color p-2"
[class.mat-drawer-toggle-btn-hover]="isSideNavOpen()"
[class]="!isSideNavOpen() ? 'overflow-hidden !w-[56px] p-1.5' : 'w-[246px]'"
[class.mat-drawer-toggle-btn-hover]="sideNavService.isSideNavOpen()"
[class]="!sideNavService.isSideNavOpen() ? 'overflow-hidden !w-[56px] p-1.5' : 'w-[246px]'"
>
@let clientSettings = clientSettings$ | async; @let clientList = clients$ | async; @let isMultipleClients =
(clientList?.data?.length ?? 0) > 1;
<div class="flex items-center pt-2" [class.flex-col]="!isSideNavOpen()" [class.gap-2]="!isSideNavOpen()">
<div
class="flex items-center pt-2"
[class.flex-col]="!sideNavService.isSideNavOpen()"
[class.gap-2]="!sideNavService.isSideNavOpen()"
>
<div
class="flex items-center grow gap-2 mat-drawer-header-title pr-2"
#clientsMenuTrigger="matMenuTrigger"
[matMenuTriggerFor]="isMultipleClients ? clientsMenu : null"
[class.pl-2]="!isSideNavOpen()"
[class.pl-4]="isSideNavOpen()"
[class.pl-2]="!sideNavService.isSideNavOpen()"
[class.pl-4]="sideNavService.isSideNavOpen()"
>
@if (isSideNavOpen()) {
@if (sideNavService.isSideNavOpen()) {
<div class="flex items-center gap-x-1 mat-drawer-header-subtitle cursor-pointer">
<span class="font-16 font-semibold max-w-[162px] overflow-hidden whitespace-nowrap text-ellipsis">
{{ clientSettings?.client?.name }}
Expand All @@ -26,7 +30,9 @@
}
</div>
} @else {
<mat-icon class="mat-icon-22">apps</mat-icon>
<mat-icon class="mat-icon-22" [matTooltip]="clientSettings?.client?.name" matTooltipPosition="right"
>apps</mat-icon
>
}
</div>
<mat-menu #clientsMenu="matMenu" class="w-[240px]" xPosition="after">
Expand All @@ -53,24 +59,24 @@
matIconButton
class="sidebar-toggle-btn"
(click)="toggleSideBarEvent()"
[matTooltip]="isSideNavOpen() ? 'Collapse Menu' : 'Expand Menu'"
[matTooltip]="sideNavService.isSideNavOpen() ? 'Collapse Menu' : 'Expand Menu'"
matTooltipPosition="right"
type="button"
>
<mat-icon>{{
isSideNavOpen() ? 'keyboard_double_arrow_left' : 'keyboard_double_arrow_right'
sideNavService.isSideNavOpen() ? 'keyboard_double_arrow_left' : 'keyboard_double_arrow_right'
}}</mat-icon>
</button>
</div>
<!-- Proxy Sidenav::START -->
<proxy-main-left-side-nav
class="mat-drawer-nav proxy-left-menu"
[isSideNavOpen]="!isSideNavOpen()"
[isSideNavOpen]="!sideNavService.isSideNavOpen()"
></proxy-main-left-side-nav>
<!-- Proxy Sidenav::END -->
@if (logInData$ | async; as user) {
<div class="mt-auto">
@if (isSideNavOpen()) {
@if (sideNavService.isSideNavOpen()) {
<button
class="!rounded-lg !p-0 w-full"
matButton
Expand All @@ -82,7 +88,7 @@
<div class="flex items-center gap-2">
<ng-container *ngTemplateOutlet="displayPicture; context: { user: user }"></ng-container>
<h6
[class.hidden]="!isSideNavOpen()"
[class.hidden]="!sideNavService.isSideNavOpen()"
class="username m-0 max-w-[162px] overflow-hidden whitespace-nowrap text-ellipsis"
>
{{ user.displayName }}
Expand Down
21 changes: 8 additions & 13 deletions apps/36-blocks/src/app/layout/layout.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import { MatToolbarModule } from '@angular/material/toolbar';
import { MatExpansionModule } from '@angular/material/expansion';
import { MatTooltipModule } from '@angular/material/tooltip';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { CDKScrollComponent } from '@proxy/ui/virtual-scroll';
import { ScrollingModule } from '@angular/cdk/scrolling';
import { MainLeftSideNavComponent } from './main-left-side-nav/main-left-side-nav.component';
import { IClient, IClientSettings, IFirebaseUserModel, IPaginatedResponse } from '@proxy/models/root-models';
Expand All @@ -36,6 +35,8 @@ import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
import { RootService } from '@proxy/services/proxy/root';
import { environment } from '../../environments/environment';
import { AuthService } from '@proxy/services/proxy/auth';
import { SideNavService } from './side-nav.service';
import { UiSettingsService } from './ui-settings.service';
Comment on lines +38 to +39
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SideNavService and UiSettingsService are imported but not included in the component's imports array. Make sure to add them to ensure proper dependency declaration.

@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'proxy-layout',
Expand Down Expand Up @@ -65,7 +66,6 @@ export class LayoutComponent extends BaseComponent implements OnInit, OnDestroy
public clients$: Observable<IPaginatedResponse<IClient[]>>;
public swtichClientSuccess$: Observable<boolean>;

public isSideNavOpen = signal<boolean>(true);
public isDarkMode = signal<boolean>(false);

public toggleMenuSideBar: boolean;
Expand All @@ -81,6 +81,8 @@ export class LayoutComponent extends BaseComponent implements OnInit, OnDestroy
private rootService = inject(RootService);
private authService = inject(AuthService);
private cdr = inject(ChangeDetectorRef);
public sideNavService = inject(SideNavService);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sideNavService is injected as public but should be private since it's only used internally in component methods. This helps encapsulate implementation details.

private uiSettings = inject(UiSettingsService);

constructor() {
super();
Expand Down Expand Up @@ -122,7 +124,7 @@ export class LayoutComponent extends BaseComponent implements OnInit, OnDestroy
this.getCurrentTheme();

if (this.isMobileDevice()) {
this.toggleSideBarEvent();
this.sideNavService.close();
}
this.swtichClientSuccess$.pipe(takeUntil(this.destroy$)).subscribe((res) => {
if (res) {
Expand Down Expand Up @@ -189,19 +191,12 @@ export class LayoutComponent extends BaseComponent implements OnInit, OnDestroy
}

public getCurrentTheme() {
if (
localStorage.getItem('selected-theme') === null ||
localStorage.getItem('selected-theme') === 'light-theme'
) {
this.switchedDarkMode(false);
} else {
this.switchedDarkMode(true);
}
this.switchedDarkMode(this.uiSettings.theme === 'dark-theme');
}

switchedDarkMode(isDarkMode: boolean) {
const hostClass = isDarkMode ? 'dark-theme' : 'light-theme';
localStorage.setItem('selected-theme', hostClass);
this.uiSettings.setTheme(hostClass);
document.body.classList.remove('dark-theme', 'light-theme');
document.body.classList.add(hostClass);
this.isDarkMode.set(isDarkMode);
Expand All @@ -218,6 +213,6 @@ export class LayoutComponent extends BaseComponent implements OnInit, OnDestroy
}

public toggleSideBarEvent(): void {
this.isSideNavOpen.set(!this.isSideNavOpen());
this.sideNavService.toggle();
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +0,0 @@
.service-list {
.mat-mdc-list-item {
border-radius: 8px !important;
height: 42px !important;
margin-bottom: 2px;
}
}
25 changes: 25 additions & 0 deletions apps/36-blocks/src/app/layout/side-nav.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Injectable, inject, signal } from '@angular/core';
import { UiSettingsService } from './ui-settings.service';

@Injectable({ providedIn: 'root' })
export class SideNavService {
private uiSettings = inject(UiSettingsService);

public isSideNavOpen = signal<boolean>(this.uiSettings.sideNavOpen);

public toggle(): void {
const next = !this.isSideNavOpen();
this.isSideNavOpen.set(next);
this.uiSettings.setSideNavOpen(next);
}

public open(): void {
this.isSideNavOpen.set(true);
this.uiSettings.setSideNavOpen(true);
}

public close(): void {
this.isSideNavOpen.set(false);
this.uiSettings.setSideNavOpen(false);
}
}
64 changes: 64 additions & 0 deletions apps/36-blocks/src/app/layout/ui-settings.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { Injectable } from '@angular/core';

export type AppTheme = 'light-theme' | 'dark-theme';

interface UiSettings {
theme: AppTheme;
sideNavOpen: boolean;
_savedAt: number;
}

const STORAGE_KEY = 'ui-settings';
const TTL_MS = 30 * 24 * 60 * 60 * 1000; // 30 days

const DEFAULTS: Omit<UiSettings, '_savedAt'> = {
theme: 'light-theme',
sideNavOpen: true,
};

@Injectable({ providedIn: 'root' })
export class UiSettingsService {
private _settings: UiSettings;

constructor() {
this._settings = this._load();
}

get theme(): AppTheme {
return this._settings.theme;
}

get sideNavOpen(): boolean {
return this._settings.sideNavOpen;
}

setTheme(value: AppTheme): void {
this._settings.theme = value;
this._save();
}

setSideNavOpen(value: boolean): void {
this._settings.sideNavOpen = value;
this._save();
}

private _load(): UiSettings {
try {
const raw = localStorage.getItem(STORAGE_KEY);
if (raw) {
const parsed: UiSettings = JSON.parse(raw);
if (parsed._savedAt && Date.now() - parsed._savedAt < TTL_MS) {
return { ...DEFAULTS, ...parsed };
}
}
} catch {
// corrupted storage — fall through to defaults
}
return { ...DEFAULTS, _savedAt: Date.now() };
}

private _save(): void {
this._settings._savedAt = Date.now();
localStorage.setItem(STORAGE_KEY, JSON.stringify(this._settings));
}
}
Loading
Loading