From 9a2c7e8c2c23e749915a646acdf7f1c47d60e32a Mon Sep 17 00:00:00 2001 From: Chandan-walker Date: Fri, 27 Feb 2026 11:35:12 +0530 Subject: [PATCH] fixed issues create new block --- .../create-feature.component.html | 146 +++++++++++------- .../create-feature.component.ts | 76 ++++++--- .../create-feature/create-feature.store.ts | 29 ++++ .../src/lib/services-proxy-features.module.ts | 6 + libs/urls/features-url/src/index.ts | 1 + 5 files changed, 183 insertions(+), 75 deletions(-) diff --git a/apps/proxy/src/app/features/create-feature/create-feature.component.html b/apps/proxy/src/app/features/create-feature/create-feature.component.html index 121018b4..d4199616 100644 --- a/apps/proxy/src/app/features/create-feature/create-feature.component.html +++ b/apps/proxy/src/app/features/create-feature/create-feature.component.html @@ -124,28 +124,7 @@

Add New Blocks

- Branding -
- -
- - -
-
-
- - Authorization Setup
@@ -173,6 +152,27 @@

Add New Blocks

+ + Branding +
+ +
+ + + +
+
+
+ @@ -478,7 +478,7 @@

Add New Blocks

*ngTemplateOutlet=" inputField; context: { - fieldControl: featureForm.get('authorizationDetails.redirect_url'), + fieldControl: featureForm.get('primaryDetails.redirect_url'), fieldConfig: { label: 'Redirect URL', is_required: true, @@ -779,9 +779,7 @@

{{ getSelectedServiceName() }}

class="auth-credentials px-3 width-md-350 w-md-50 border rounded-8 ml-3" [class.bg-dark]="featureForm.get('brandingDetails.theme')?.value === 'dark'" style="max-height: 650px" - [style.--branding-primary-color]=" - featureForm.get('brandingDetails.primary_color')?.value || getDefaultPrimaryColor() - " + [style.--branding-primary-color]="getEffectivePrimaryColor()" [style.--branding-button-color]="featureForm.get('brandingDetails.button_color')?.value || '#19E6CE'" [style.--branding-button-text-color]="featureForm.get('brandingDetails.button_text_color')?.value || '#000000'" [style.--branding-button-hover-color]=" @@ -793,17 +791,15 @@

{{ getSelectedServiceName() }}

-

+

{{ featureForm.get('brandingDetails.title')?.value }}

@@ -845,7 +841,7 @@

{{ getSelectedServiceName() }}

Are you a new user? {{ featureForm.get('brandingDetails.sign_up_button_text')?.value }} @@ -906,10 +902,7 @@

{{ getSelectedServiceName() }}

[class.text-secondary]="featureForm.get('brandingDetails.theme')?.value === 'dark'" style="white-space: nowrap" [class.bg-dark]="featureForm.get('brandingDetails.theme')?.value === 'dark'" - [style.color]=" - (featureForm.get('brandingDetails.primary_color')?.value || getDefaultPrimaryColor()) + - ' !important' - " + [style.color]="getEffectivePrimaryColor() + ' !important'" >Or continue with
@@ -2003,13 +1996,21 @@

Taxes

Enter URL - Upload file + + Upload file + info +
@@ -2029,13 +2030,21 @@

Taxes

hidden (change)="onBrandingLogoFileSelected($event)" /> - - Logo selected + + {{ logoFileInput.files?.[0]?.name || 'Logo selected' }} + + Taxes > - +
- + + {{ + featureForm.get('brandingDetails.light_theme_primary_color')?.value || '#000000' + }} + (Light theme) +
+ +
+ + {{ - featureForm.get('brandingDetails.primary_color')?.value || getDefaultPrimaryColor() + featureForm.get('brandingDetails.dark_theme_primary_color')?.value || '#ffffff' }} + (Dark theme)
diff --git a/apps/proxy/src/app/features/create-feature/create-feature.component.ts b/apps/proxy/src/app/features/create-feature/create-feature.component.ts index 05b11c2e..21480d06 100644 --- a/apps/proxy/src/app/features/create-feature/create-feature.component.ts +++ b/apps/proxy/src/app/features/create-feature/create-feature.component.ts @@ -149,6 +149,7 @@ export class CreateFeatureComponent extends BaseComponent implements OnDestroy, public paymentDetailsById$: Observable = this.componentStore.paymentDetailsById$; public updatePaymentDetails$: Observable = this.componentStore.updatePaymentDetails$; public webhookEvents$: Observable = this.componentStore.webhookEvents$; + public uploadLogo$: Observable = this.componentStore.uploadLogo$; public isEditMode = false; public previewInputPosition: 'top' | 'bottom' = 'top'; public selectedServiceIndex = 0; @@ -187,8 +188,11 @@ export class CreateFeatureComponent extends BaseComponent implements OnDestroy, /** Logo input: 'url' = enter URL, 'file' = upload file */ public logoInputMode: 'url' | 'file' = 'url'; + public isLogoUploading = false; + // Options cache for select fields private optionsCache: { [key: string]: any[] } = {}; + public logoUrl: string = null; public featureForm = new FormGroup({ primaryDetails: new FormGroup({ @@ -201,6 +205,7 @@ export class CreateFeatureComponent extends BaseComponent implements OnDestroy, ]), feature_id: new FormControl(1, [Validators.required]), method_id: new FormControl(null, [Validators.required]), + redirect_url: new FormControl(null, [Validators.required, Validators.pattern(URL_REGEX)]), }), serviceDetails: new FormArray([]), planDetails: new FormArray([]), @@ -221,7 +226,6 @@ export class CreateFeatureComponent extends BaseComponent implements OnDestroy, CustomValidators.minLengthThreeWithoutSpace, ]), encryptionKey: new FormControl(null, []), - redirect_url: new FormControl(null, [Validators.required, Validators.pattern(URL_REGEX)]), blockNewUserSignUps: new FormControl(false, []), }), webhookDetails: new FormGroup({ @@ -232,11 +236,12 @@ export class CreateFeatureComponent extends BaseComponent implements OnDestroy, brandingDetails: new FormGroup({ icons: new FormControl(false, []), logo_url: new FormControl(null, []), - primary_color: new FormControl(null, []), + light_theme_primary_color: new FormControl('#000000', []), + dark_theme_primary_color: new FormControl('#ffffff', []), button_color: new FormControl('#19E6CE', []), button_hover_color: new FormControl('#19E6CE', []), button_text_color: new FormControl('#000000', []), - border_radius: new FormControl('medium', []), + border_radius: new FormControl('small', []), title: new FormControl('Sign in to your account', []), sign_up_button_text: new FormControl('Create an account', [Validators.required]), create_account_link: new FormControl(true, []), @@ -268,7 +273,8 @@ export class CreateFeatureComponent extends BaseComponent implements OnDestroy, this.getFeatureDetalis(); } else { this.getFeatureType(); - this.featureForm.get('brandingDetails.primary_color')?.setValue(this.getDefaultPrimaryColor()); + this.featureForm.get('brandingDetails.light_theme_primary_color')?.setValue('#000000'); + this.featureForm.get('brandingDetails.dark_theme_primary_color')?.setValue('#ffffff'); } }); @@ -333,7 +339,8 @@ export class CreateFeatureComponent extends BaseComponent implements OnDestroy, }, brandingDetails: { logo_url: feature.ui_preferences?.logo_url, - primary_color: feature.ui_preferences?.primary_color, + light_theme_primary_color: feature.ui_preferences?.light_theme_primary_color, + dark_theme_primary_color: feature.ui_preferences?.dark_theme_primary_color, button_color: feature.ui_preferences?.button_color, button_hover_color: feature.ui_preferences?.button_hover_color, button_text_color: @@ -348,8 +355,8 @@ export class CreateFeatureComponent extends BaseComponent implements OnDestroy, }, }); // Clear redirect_url validators in edit mode since the field is hidden - this.featureForm.get('authorizationDetails.redirect_url')?.clearValidators(); - this.featureForm.get('authorizationDetails.redirect_url')?.updateValueAndValidity(); + this.featureForm.get('primaryDetails.redirect_url')?.clearValidators(); + this.featureForm.get('primaryDetails.redirect_url')?.updateValueAndValidity(); this.previewInputPosition = feature.ui_preferences?.input_fields || 'top'; }); }); @@ -413,7 +420,7 @@ export class CreateFeatureComponent extends BaseComponent implements OnDestroy, if ( !this.isEditMode && this.featureForm.get('primaryDetails.feature_id')?.value === 1 && - this.featureForm.get('authorizationDetails.redirect_url')?.value + this.featureForm.get('primaryDetails.redirect_url')?.value ) { this.setRedirectUrlInServiceDetails(); } @@ -441,6 +448,8 @@ export class CreateFeatureComponent extends BaseComponent implements OnDestroy, this.stepper?.first?.next(); }, 10); } + this.featureId = obj.id; + this.demoDiv$ = of(`
`); }); this.createBillableMetric$.pipe(filter(Boolean), takeUntil(this.destroy$)).subscribe((metric) => { @@ -567,19 +576,28 @@ export class CreateFeatureComponent extends BaseComponent implements OnDestroy, } } }); + this.uploadLogo$.pipe(filter(Boolean), takeUntil(this.destroy$)).subscribe((data) => { + const url = data?.logo_url ?? data?.url; + if (url) { + // this.featureForm.get('brandingDetails.logo_url')?.setValue(url); + this.logoUrl = url; + } + this.isLogoUploading = false; + this.cdr.markForCheck(); + }); } public validateFirstStepAndNext(nameControl: AbstractControl): void { nameControl.markAllAsTouched(); const featureId = this.featureForm.get('primaryDetails.feature_id')?.value; if (featureId === 1) { - this.featureForm.get('authorizationDetails.redirect_url')?.markAllAsTouched(); + this.featureForm.get('primaryDetails.redirect_url')?.markAllAsTouched(); } if (nameControl.invalid) { return; } if (featureId === 1) { - const redirectUrlControl = this.featureForm.get('authorizationDetails.redirect_url'); + const redirectUrlControl = this.featureForm.get('primaryDetails.redirect_url'); if (redirectUrlControl?.invalid) { return; } @@ -592,11 +610,12 @@ export class CreateFeatureComponent extends BaseComponent implements OnDestroy, public get validateFirstStep(): boolean { const nameControl = this.featureForm.get('primaryDetails.name'); const featureId = this.featureForm.get('primaryDetails.feature_id')?.value; - if (nameControl.invalid) { + const redirectUrlControl = this.featureForm.get('primaryDetails.redirect_url'); + if (nameControl.invalid || redirectUrlControl?.invalid) { return false; } if (featureId === 1) { - const redirectUrlControl = this.featureForm.get('authorizationDetails.redirect_url'); + const redirectUrlControl = this.featureForm.get('primaryDetails.redirect_url'); if (redirectUrlControl?.invalid) { return false; } @@ -613,6 +632,20 @@ export class CreateFeatureComponent extends BaseComponent implements OnDestroy, : '#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 === 'dark' || + (theme === 'system' && + typeof localStorage !== 'undefined' && + localStorage.getItem('selected-theme') === 'dark-theme'); + if (isDark) { + return this.featureForm.get('brandingDetails.dark_theme_primary_color')?.value || '#ffffff'; + } + return this.featureForm.get('brandingDetails.light_theme_primary_color')?.value || '#000000'; + } + public getBrandingBorderRadiusValue(): string { const v = this.featureForm.get('brandingDetails.border_radius')?.value; switch (v) { @@ -632,19 +665,18 @@ export class CreateFeatureComponent extends BaseComponent implements OnDestroy, public onBrandingLogoFileSelected(event: Event): void { const input = event.target as HTMLInputElement; const file = input?.files?.[0]; - if (!file) return; - const reader = new FileReader(); - reader.onload = () => { - this.featureForm.get('brandingDetails.logo_url')?.setValue(reader.result as string); - this.cdr.markForCheck(); - }; - reader.readAsDataURL(file); + if (!file || !this.featureId) return; input.value = ''; + const formData = new FormData(); + formData.append('logo', file); + this.isLogoUploading = true; + this.cdr.markForCheck(); + this.componentStore.uploadLogo({ id: this.featureId, formData }); } public setRedirectUrlInServiceDetails(): void { const serviceDetailsForm = this.featureForm.controls.serviceDetails; - const redirectUrl = this.featureForm.controls.authorizationDetails.value.redirect_url; + const redirectUrl = this.featureForm.controls.primaryDetails.value.redirect_url; serviceDetailsForm.controls.forEach((formGroup) => { const redirectUrlControl = formGroup.controls.configurations.controls['redirect_uri'] as FormControl; if (redirectUrlControl && !redirectUrlControl.value) { @@ -749,11 +781,13 @@ export class CreateFeatureComponent extends BaseComponent implements OnDestroy, const existingUiPrefs = (featureDetails as unknown as Record)?.ui_preferences as | Record | undefined; + const { primary_color: _removed, ...cleanedUiPrefs } = existingUiPrefs || ({} as any); payload = { ui_preferences: { - ...(existingUiPrefs || {}), + ...cleanedUiPrefs, ...brandingDetailsForm.value, input_fields: this.previewInputPosition, + logo_url: this.logoInputMode === 'file' ? this.logoUrl : brandingDetailsForm.value.logo_url, }, }; } else { diff --git a/apps/proxy/src/app/features/create-feature/create-feature.store.ts b/apps/proxy/src/app/features/create-feature/create-feature.store.ts index 635292be..c0afe600 100644 --- a/apps/proxy/src/app/features/create-feature/create-feature.store.ts +++ b/apps/proxy/src/app/features/create-feature/create-feature.store.ts @@ -29,6 +29,7 @@ export interface ICreateFeatureInitialState { paymentDetailsById: any; updatePaymentDetails: any; webhookEvents: any; + uploadLogo: any; } @Injectable() @@ -58,6 +59,7 @@ export class CreateFeatureComponentStore extends ComponentStore = this.select((state) => state.updatePaymentDetails); /** Selector for webhook events data */ readonly webhookEvents$: Observable = this.select((state) => state.webhookEvents); + /** Selector for upload logo data */ + readonly uploadLogo$: Observable = this.select((state) => state.uploadLogo); /** Get feature type data */ readonly getFeatureType = this.effect((data) => { return data.pipe( @@ -690,6 +694,31 @@ export class CreateFeatureComponentStore extends ComponentStore) => { + return data.pipe( + switchMap((req) => { + this.patchState({ isLoading: true, uploadLogo: null }); + return this.service.uploadLogo(req.id, req.formData).pipe( + tapResponse( + (res: BaseResponse) => { + if (res?.hasError) { + this.showError(res.errors); + return this.patchState({ isLoading: false }); + } + this.toast.success('Logo uploaded successfully'); + return this.patchState({ isLoading: false, uploadLogo: res.data }); + }, + (error: any) => { + this.showError(error.errors); + this.patchState({ isLoading: false }); + } + ), + catchError((err) => EMPTY) + ); + }) + ); + }); + private showError(error): void { const errorMessage = errorResolver(error); errorMessage.forEach((error) => { diff --git a/libs/services/proxy/features/src/lib/services-proxy-features.module.ts b/libs/services/proxy/features/src/lib/services-proxy-features.module.ts index 3dcc3e01..f165e2b0 100644 --- a/libs/services/proxy/features/src/lib/services-proxy-features.module.ts +++ b/libs/services/proxy/features/src/lib/services-proxy-features.module.ts @@ -122,4 +122,10 @@ export class FeaturesService { public getWebhookEvents(): Observable> { return this.http.get>(FeaturesUrls.getWebhookEvents(this.baseURL)); } + + public uploadLogo(id: string | number, formData: FormData): Observable> { + return this.http.post>(FeaturesUrls.uploadLogo(this.baseURL, id), formData, { + headers: {}, + }); + } } diff --git a/libs/urls/features-url/src/index.ts b/libs/urls/features-url/src/index.ts index 373f99ee..c1fde911 100644 --- a/libs/urls/features-url/src/index.ts +++ b/libs/urls/features-url/src/index.ts @@ -20,4 +20,5 @@ export const FeaturesUrls = { getPaymentDetailsFormById: (baseUrl, refid) => createUrl(baseUrl, `${refid}/paymentForm`), updatePaymentDetails: (baseUrl, refid) => createUrl(baseUrl, `subscription/${refid}/updateCredentials`), getWebhookEvents: (baseUrl) => createUrl(baseUrl, `getWebhookTriggerEvents`), + uploadLogo: (baseUrl, id) => createUrl(baseUrl, `features/${id}/upload-logo`), };