diff --git a/apps/proxy-auth/src/app/app.component.ts b/apps/proxy-auth/src/app/app.component.ts
index 5ec761d3..6239c45c 100644
--- a/apps/proxy-auth/src/app/app.component.ts
+++ b/apps/proxy-auth/src/app/app.component.ts
@@ -26,15 +26,15 @@ export class AppComponent extends BaseComponent implements OnInit, OnDestroy {
public initOtpProvider() {
if (!environment.production) {
const sendOTPConfig = {
- referenceId: '4512365m176216342869087ae458e09',
- type: 'authorization',
+ // referenceId: '4512365m176216342869087ae458e09',
+ type: 'organization-details',
// loginRedirectUrl: 'https://www.google.com',
// showCompanyDetails: false,
authToken:
- 'clV0YUt4UURVbzJYZTRwMHdBNkZ6QjZoay9qMmRRcjZhMGVXMGtCT1ZtdGNaelFxMmlNaGdNcEJuRy9UWmFSZHQvMHc0YnJYUHExakh5NDNGVjZMOEdXVmg3OG82R094Yk5tdE9XckxjUTV1dlNzUERXRWxaOWIwWm5JRmlMVHl5UmpZUHVDK2piOURJUi9IdytncFZBRWc5QnRyRDRVeUFOZlBCY1FST0FOZStISUVtK055VWNxaGduZWpGeUZxVWxYWjd6YXI2YTF0aGxHZTNka1BlQT09',
+ 'ZVlWU2U4cnlOVUh5M1lYcTZLUUVaczZGdFlHN2lKOXNIU24rTWx3WWpnQzE5YXJVaTF0R215UkEvNGpIS2tJVC83Q01EQlk2QWZ6Z1UxYlQvZCtSeThxdDdiUHVuNm9RbVhPNDVnTFFUN3dKZkRIT294a3BvWFFNSGIxUFV6Wk5yZkpmYXk0MzVmUzlrTXp1bkRYTkRUdzBKMW9yRi8vTDgrak9ESzlKblVXU1hvWCtHSytkaW9nemYxTTFwNEVPSThlNk9ZRXd0YTJUanJqRk1sZUdGUT09',
// type: 'user-management',
isHidden: true,
- theme: 'light',
+ theme: 'dark',
// isPreview: true,
isLogin: true,
target: '_self',
diff --git a/apps/proxy-auth/src/app/otp/organization-details/organization-details.component.html b/apps/proxy-auth/src/app/otp/organization-details/organization-details.component.html
new file mode 100644
index 00000000..ba4f5f1e
--- /dev/null
+++ b/apps/proxy-auth/src/app/otp/organization-details/organization-details.component.html
@@ -0,0 +1,174 @@
+
+
+
+
+
+
+
+
+
+
+
+ Company Name
+
+
{{ organizationForm.get('companyName')?.value || '—' }}
+
+
+
+
+
{{ organizationForm.get('email')?.value || '—' }}
+
+
+
+
+
{{ organizationForm.get('phoneNumber')?.value || '—' }}
+
+
+
+
+
{{ organizationForm.get('timeZoneName')?.value || '—' }}
+
+
+
+
+
+
+
diff --git a/apps/proxy-auth/src/app/otp/organization-details/organization-details.component.scss b/apps/proxy-auth/src/app/otp/organization-details/organization-details.component.scss
new file mode 100644
index 00000000..dae0ddf1
--- /dev/null
+++ b/apps/proxy-auth/src/app/otp/organization-details/organization-details.component.scss
@@ -0,0 +1,386 @@
+// ── Container ─────────────────────────────────────────────────────────
+.container {
+ padding: 15px 60px;
+ text-align: left;
+ position: relative;
+ min-height: 600px;
+ height: 100vh;
+ width: 100%;
+ z-index: 10;
+ max-width: 2000px;
+ background: transparent;
+}
+
+.organization-details-container {
+ padding: 0;
+ width: 100%;
+ max-width: 600px;
+ background: transparent;
+}
+
+// ── Header ────────────────────────────────────────────────────────────
+.page-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ flex-wrap: wrap;
+ gap: 12px;
+ margin-bottom: 1.5rem;
+}
+
+.page-title {
+ margin: 0;
+ font-size: 1.5rem;
+ font-weight: 600;
+}
+
+.header-actions {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+// Edit button — fully native, no mat-stroked-button dependency
+.edit-btn {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ font-family: Roboto, sans-serif;
+ font-size: 14px;
+ font-weight: 500;
+ letter-spacing: 0.03em;
+ padding: 6px 14px;
+ height: 36px;
+ border-radius: 4px;
+ cursor: pointer;
+ background: transparent;
+ transition: background 0.15s ease;
+ outline: none;
+}
+
+// Cancel button — fully native, no mat-button dependency
+.cancel-btn {
+ display: inline-flex;
+ align-items: center;
+ font-family: Roboto, sans-serif;
+ font-size: 14px;
+ font-weight: 500;
+ letter-spacing: 0.03em;
+ padding: 6px 14px;
+ height: 36px;
+ border-radius: 4px;
+ cursor: pointer;
+ background: transparent;
+ border: none;
+ transition: background 0.15s ease;
+ outline: none;
+
+ &:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+ }
+}
+
+// Save button — fully native, no mat-flat-button dependency
+.save-btn {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ font-family: Roboto, sans-serif;
+ font-size: 14px;
+ font-weight: 500;
+ letter-spacing: 0.03em;
+ padding: 6px 20px;
+ height: 36px;
+ border-radius: 4px;
+ cursor: pointer;
+ border: none;
+ background: #1976d2;
+ color: #ffffff;
+ transition: background 0.15s ease;
+ outline: none;
+
+ &:hover:not(:disabled) {
+ background: #1565c0;
+ }
+ &:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+ }
+}
+
+// ── View mode ─────────────────────────────────────────────────────────
+.view-container {
+ border-radius: 8px;
+ overflow: hidden;
+ width: 100%;
+ max-width: 600px;
+ background: transparent;
+}
+
+.field-row {
+ display: grid;
+ grid-template-columns: 160px 1fr;
+ align-items: center;
+ gap: 16px;
+ padding: 16px 20px;
+ border-bottom: 1px solid var(--field-border);
+ transition: background 0.15s ease;
+
+ &:last-child {
+ border-bottom: none;
+ }
+ &:hover {
+ background: var(--field-row-hover);
+ }
+
+ @media (max-width: 480px) {
+ grid-template-columns: 1fr;
+ gap: 4px;
+ padding: 12px 16px;
+ }
+}
+
+.field-label {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 13px;
+ color: var(--label-color);
+ white-space: nowrap;
+}
+
+// Inline SVG button icon
+.btn-icon {
+ width: 15px;
+ height: 15px;
+ flex-shrink: 0;
+}
+
+// Inline SVG field icons
+.field-icon {
+ width: 15px;
+ height: 15px;
+ flex-shrink: 0;
+ opacity: 0.5;
+ color: inherit;
+}
+
+.field-value {
+ font-size: 14px;
+ color: var(--value-color);
+ word-break: break-all;
+ line-height: 1.4;
+}
+
+// ── Edit form ─────────────────────────────────────────────────────────
+.organization-form {
+ gap: 0.5rem;
+ width: 100%;
+ background: transparent;
+}
+
+.full-width {
+ width: 100%;
+}
+
+.form-actions {
+ display: flex;
+ justify-content: flex-end;
+ align-items: center;
+ gap: 8px;
+ margin-top: 8px;
+ width: 100%;
+}
+
+// ── Light theme ───────────────────────────────────────────────────────
+.light-theme {
+ --field-border: rgba(0, 0, 0, 0.12);
+ --field-row-hover: rgba(0, 0, 0, 0.02);
+ --label-color: rgba(0, 0, 0, 0.5);
+ --value-color: rgba(0, 0, 0, 0.87);
+
+ background: transparent !important;
+
+ .page-title {
+ color: #000000;
+ }
+
+ .view-container {
+ border: 1px solid rgba(0, 0, 0, 0.12);
+ }
+
+ .edit-btn {
+ color: rgba(0, 0, 0, 0.7);
+ border: 1px solid rgba(0, 0, 0, 0.23);
+ &:hover {
+ background: rgba(0, 0, 0, 0.04);
+ }
+ }
+
+ .cancel-btn {
+ color: rgba(0, 0, 0, 0.6);
+ &:hover {
+ background: rgba(0, 0, 0, 0.04);
+ }
+ }
+
+ .save-btn {
+ background: #1976d2;
+ color: #ffffff;
+ }
+}
+
+// ── Dark theme ────────────────────────────────────────────────────────
+.dark-theme {
+ --field-border: rgba(255, 255, 255, 0.15);
+ --field-row-hover: rgba(255, 255, 255, 0.04);
+ --label-color: rgba(255, 255, 255, 0.5);
+ --value-color: rgba(255, 255, 255, 0.87);
+
+ background: transparent !important;
+ color: #ffffff !important;
+
+ .page-title {
+ color: #ffffff !important;
+ }
+
+ .view-container {
+ border: 1px solid rgba(255, 255, 255, 0.15);
+ }
+
+ .edit-btn {
+ color: rgba(255, 255, 255, 0.8);
+ border: 1px solid rgba(255, 255, 255, 0.3);
+ &:hover {
+ background: rgba(255, 255, 255, 0.06);
+ }
+ }
+
+ .cancel-btn {
+ color: rgba(255, 255, 255, 0.6);
+ &:hover {
+ background: rgba(255, 255, 255, 0.06);
+ }
+ }
+
+ // Material form field overrides for dark inside ShadowDom
+ .mat-mdc-text-field-wrapper {
+ background-color: transparent !important;
+ }
+ .mat-mdc-input-element {
+ color: #ffffff !important;
+ }
+ .mat-mdc-floating-label {
+ color: #ffffff !important;
+ }
+ .mdc-floating-label {
+ color: #ffffff !important;
+ }
+ mat-label {
+ color: #ffffff !important;
+ }
+ .mat-mdc-form-field-flex .mdc-floating-label {
+ color: #ffffff !important;
+ }
+ .mat-mdc-form-field .mat-mdc-floating-label {
+ color: #ffffff !important;
+ }
+ .mdc-text-field--outlined .mdc-floating-label {
+ color: #ffffff !important;
+ }
+ .mat-form-field-label {
+ color: #ffffff !important;
+ }
+ .mat-form-field-outline {
+ color: rgba(255, 255, 255, 0.5) !important;
+ }
+ .mat-form-field .mat-input-element {
+ color: #ffffff !important;
+ }
+ .mat-form-field-hint-wrapper,
+ .mat-form-field-error-wrapper {
+ color: rgba(255, 255, 255, 0.7) !important;
+ }
+
+ // mat-select: fix selected value + arrow color inside shadow root
+ // Material 14 legacy select uses .mat-select-value, .mat-select-value-text
+ .mat-select-value,
+ .mat-select-value-text,
+ .mat-select-value-text span {
+ color: #ffffff !important;
+ }
+ .mat-select-placeholder {
+ color: rgba(255, 255, 255, 0.42) !important;
+ }
+ .mat-select-arrow {
+ color: rgba(255, 255, 255, 0.7) !important;
+ }
+ .mat-select-arrow svg {
+ fill: rgba(255, 255, 255, 0.7) !important;
+ }
+ .mat-select-trigger {
+ color: #ffffff !important;
+ }
+ .mat-select-trigger .mat-select-value {
+ color: #ffffff !important;
+ }
+
+ // MDC select (if used elsewhere)
+ .mat-mdc-select-value {
+ color: #ffffff !important;
+ }
+ .mat-mdc-select-value-text {
+ color: #ffffff !important;
+ }
+ .mat-mdc-select-value-text span {
+ color: #ffffff !important;
+ }
+ .mat-mdc-select-placeholder {
+ color: rgba(255, 255, 255, 0.42) !important;
+ }
+ .mat-mdc-select-arrow svg {
+ fill: rgba(255, 255, 255, 0.7) !important;
+ }
+ .mat-mdc-select-arrow {
+ color: rgba(255, 255, 255, 0.7) !important;
+ }
+ .mdc-text-field .mat-mdc-select-value {
+ color: #ffffff !important;
+ }
+ .mat-mdc-form-field .mat-mdc-select-value {
+ color: #ffffff !important;
+ }
+
+ // Outline border color for select + input fields
+ .mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__leading,
+ .mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__notch,
+ .mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__trailing {
+ border-color: rgba(255, 255, 255, 0.3) !important;
+ }
+}
+
+// ── Snackbars ─────────────────────────────────────────────────────────
+::ng-deep .success-snackbar {
+ background-color: #59a75c !important;
+ color: #ffffff !important;
+
+ .mat-mdc-snack-bar-label {
+ color: #ffffff !important;
+ }
+ .mat-button {
+ color: #ffffff !important;
+ }
+}
+
+::ng-deep .error-snackbar {
+ background-color: #d32f2f !important;
+ color: #ffffff !important;
+
+ .mat-snack-bar-label {
+ color: #ffffff !important;
+ }
+ .mat-button {
+ color: #ffffff !important;
+ }
+}
diff --git a/apps/proxy-auth/src/app/otp/organization-details/organization-details.component.ts b/apps/proxy-auth/src/app/otp/organization-details/organization-details.component.ts
new file mode 100644
index 00000000..35d57d65
--- /dev/null
+++ b/apps/proxy-auth/src/app/otp/organization-details/organization-details.component.ts
@@ -0,0 +1,194 @@
+import { Input, OnDestroy, OnInit } from '@angular/core';
+
+import { Component, ViewEncapsulation } from '@angular/core';
+import { FormControl, FormGroup, Validators } from '@angular/forms';
+import { BaseComponent } from 'libs/ui/base-component/src/lib/base-component/base.component';
+import { OtpService } from '../service/otp.service';
+import { finalize, takeUntil } from 'rxjs';
+import { MatSnackBar } from '@angular/material/snack-bar';
+
+@Component({
+ selector: 'organization-details',
+ templateUrl: './organization-details.component.html',
+ encapsulation: ViewEncapsulation.ShadowDom,
+ styleUrls: ['../../../styles.scss', './organization-details.component.scss'],
+})
+export class OrganizationDetailsComponent extends BaseComponent implements OnInit, OnDestroy {
+ @Input() public authToken: string;
+ @Input() public theme: string;
+
+ public organizationForm = new FormGroup({
+ companyName: new FormControl('', [Validators.required, Validators.minLength(3)]),
+ email: new FormControl('', [Validators.required, Validators.email]),
+ phoneNumber: new FormControl('', [Validators.pattern(/^$|^[0-9]{10,15}$/)]),
+ timezone: new FormControl('', [Validators.required]),
+ timeZoneName: new FormControl('', [Validators.required]),
+ });
+
+ public updateInProgress = false;
+
+ /** Timezone options from API (e.g. { value: string, label?: string }[] or string[]) */
+ public timezones: any[] = [];
+
+ // ── NEW: controls view vs edit mode ──────────────────────────
+ public isEditing = false;
+ // ─────────────────────────────────────────────────────────────
+
+ private initialFormValue: {
+ companyName: string;
+ email: string;
+ phoneNumber: string;
+ timezone: string;
+ timeZoneName: string;
+ } | null = null;
+
+ // Snapshot taken when the user clicks Edit, so Cancel can restore it
+ private editSnapshot: typeof this.initialFormValue = null;
+
+ constructor(private otpService: OtpService, private snackBar: MatSnackBar) {
+ super();
+ }
+
+ ngOnInit(): void {
+ if (this.authToken) {
+ this.otpService
+ .getOrganizationDetails(this.authToken)
+ .pipe(takeUntil(this.destroy$))
+ .subscribe({
+ next: (res) => {
+ const company = res?.data?.[0]?.currentCompany;
+ if (company && typeof company === 'object') {
+ const timeZoneName =
+ company.timeZoneName ??
+ this.timezones.find((tz) => tz?.offset === company.timezone)?.label ??
+ '';
+ const value = {
+ companyName: company.name ?? '',
+ email: company.email ?? '',
+ phoneNumber: company.mobile ?? '',
+ timezone: company.timezone ?? '',
+ timeZoneName: timeZoneName ?? '',
+ };
+ this.organizationForm.patchValue(value);
+ this.initialFormValue = value;
+ }
+ },
+ error: () => {},
+ });
+
+ this.otpService
+ .getTimezones(this.authToken)
+ .pipe(takeUntil(this.destroy$))
+ .subscribe({
+ next: (res) => {
+ const raw = res?.data ?? res;
+ if (Array.isArray(raw)) {
+ this.timezones = res.data;
+ }
+ },
+ error: () => {},
+ });
+ }
+ this.organizationForm
+ .get('timeZoneName')
+ ?.valueChanges.pipe(takeUntil(this.destroy$))
+ .subscribe((value) => {
+ this.organizationForm
+ .get('timezone')
+ ?.setValue(this.timezones.find((tz) => tz?.label === value)?.offset ?? '');
+ });
+ }
+
+ ngOnDestroy(): void {
+ super.ngOnDestroy();
+ }
+
+ // ── NEW: enter edit mode ──────────────────────────────────────
+ public startEdit(): void {
+ // Snapshot current values so Cancel can restore them
+ this.editSnapshot = { ...this.organizationForm.value } as typeof this.initialFormValue;
+ this.isEditing = true;
+ }
+
+ // ── NEW: cancel and restore form to pre-edit state ────────────
+ public cancelEdit(): void {
+ if (this.editSnapshot) {
+ this.organizationForm.patchValue(this.editSnapshot);
+ }
+ this.organizationForm.markAsPristine();
+ this.organizationForm.markAsUntouched();
+ this.isEditing = false;
+ }
+
+ public getTimezoneValue(tz): string {
+ return typeof tz === 'string' ? tz : tz.label ?? '';
+ }
+
+ public getTimezoneLabel(tz): string {
+ return typeof tz === 'string' ? tz : tz.label ?? '';
+ }
+
+ public onSubmit(): void {
+ if (!this.organizationForm.valid || !this.authToken || this.updateInProgress) {
+ this.organizationForm.markAllAsTouched();
+ return;
+ }
+
+ const organizationDetails = this.organizationForm.value;
+ const current = {
+ companyName: organizationDetails.companyName ?? '',
+ email: organizationDetails.email ?? '',
+ phoneNumber: organizationDetails.phoneNumber ?? '',
+ timezone: organizationDetails.timezone ?? '',
+ timeZoneName: organizationDetails.timeZoneName ?? '',
+ };
+
+ // No-op if nothing changed
+ if (
+ this.initialFormValue &&
+ this.initialFormValue.companyName === current.companyName &&
+ this.initialFormValue.email === current.email &&
+ this.initialFormValue.phoneNumber === current.phoneNumber &&
+ this.initialFormValue.timezone === current.timezone &&
+ this.initialFormValue.timeZoneName === current.timeZoneName
+ ) {
+ this.isEditing = false; // just close edit mode silently
+ return;
+ }
+
+ this.updateInProgress = true;
+ this.otpService
+ .updateCompany(this.authToken, {
+ name: organizationDetails.companyName ?? '',
+ email: organizationDetails.email ?? '',
+ mobile: organizationDetails.phoneNumber ?? '',
+ timezone: organizationDetails.timezone ?? '',
+ timeZoneName: organizationDetails.timeZoneName ?? '',
+ })
+ .pipe(
+ takeUntil(this.destroy$),
+ finalize(() => (this.updateInProgress = false))
+ )
+ .subscribe({
+ next: (res) => {
+ this.initialFormValue = { ...current };
+ this.isEditing = false; // ← close edit mode on success
+ this.snackBar.open(res?.data?.message ?? 'Information successfully updated', '✕', {
+ duration: 3000,
+ horizontalPosition: 'center',
+ verticalPosition: 'top',
+ panelClass: ['success-snackbar'],
+ });
+ },
+ error: () => {
+ // Stay in edit mode so user can retry
+ this.snackBar.open('Something went wrong', '✕', {
+ duration: 3000,
+ horizontalPosition: 'center',
+ verticalPosition: 'top',
+ panelClass: ['error-snackbar'],
+ });
+ },
+ });
+ }
+}
diff --git a/apps/proxy-auth/src/app/otp/otp.module.ts b/apps/proxy-auth/src/app/otp/otp.module.ts
index d9057006..9a32662a 100644
--- a/apps/proxy-auth/src/app/otp/otp.module.ts
+++ b/apps/proxy-auth/src/app/otp/otp.module.ts
@@ -44,6 +44,8 @@ import { MatTabsModule } from '@angular/material/tabs';
import { SubscriptionCenterComponent } from './component/subscription-center/subscription-center.component';
import { MatDividerModule } from '@angular/material/divider';
import { UiConfirmDialogModule } from '@proxy/ui/confirm-dialog';
+import { OrganizationDetailsComponent } from './organization-details/organization-details.component';
+import { MatSnackBarModule } from '@angular/material/snack-bar';
export const CHAT_COMPONENTS: any[] = [
SendOtpComponent,
@@ -53,6 +55,7 @@ export const CHAT_COMPONENTS: any[] = [
UserProfileComponent,
UserManagementComponent,
SubscriptionCenterComponent,
+ OrganizationDetailsComponent,
];
@NgModule({
@@ -95,6 +98,7 @@ export const CHAT_COMPONENTS: any[] = [
}),
ServicesHttpWrapperNoAuthModule,
DirectivesMarkAllAsTouchedModule,
+ MatSnackBarModule,
],
declarations: [...CHAT_COMPONENTS],
providers: [
diff --git a/apps/proxy-auth/src/app/otp/send-otp/send-otp.component.html b/apps/proxy-auth/src/app/otp/send-otp/send-otp.component.html
index 52dd5c13..e95ddf11 100644
--- a/apps/proxy-auth/src/app/otp/send-otp/send-otp.component.html
+++ b/apps/proxy-auth/src/app/otp/send-otp/send-otp.component.html
@@ -4,7 +4,7 @@
[ngClass]="{
'with-reference-id': referenceId,
'without-reference-id': authToken,
- 'dark-theme': theme === 'dark' && (type === 'user-management' || authToken)
+ 'dark-theme': (theme === 'dark' && (type === 'user-management' || authToken)) || type === 'organization-details'
}"
>
+