From 8200b6c703a91aa1c567615e93e543ee29f07575 Mon Sep 17 00:00:00 2001 From: Devendra Singh Rana <132900359+devendrafyle@users.noreply.github.com> Date: Tue, 27 Aug 2024 15:16:15 +0530 Subject: [PATCH] feat: Dismiss Dialog for duplicate expenses (#3175) * feat: Dismiss dialog for potential duplicate expenses * minor fixes * minor comment fixes * removed not required model * removed not used model file * test: Added unit test cases for dismiss dialog (#3176) * test: Added unit test cases for dismiss dialog * added more test cases and fixed minor comments --------- Co-authored-by: Devendra Singh Rana * improve unit test coverage --------- Co-authored-by: Devendra Singh Rana --- .../dismiss-dialog.component.html | 30 ++++ .../dismiss-dialog.component.scss | 43 +++++ .../dismiss-dialog.component.spec.ts | 100 ++++++++++++ .../dismiss-dialog.component.ts | 45 ++++++ .../fyle/my-expenses/my-expenses.module.ts | 3 +- .../potential-duplicates.page.spec.ts | 152 ++++++++++++++++-- .../potential-duplicates.page.ts | 57 +++++-- src/global.scss | 11 ++ 8 files changed, 411 insertions(+), 30 deletions(-) create mode 100644 src/app/fyle/dashboard/tasks/dismiss-dialog/dismiss-dialog.component.html create mode 100644 src/app/fyle/dashboard/tasks/dismiss-dialog/dismiss-dialog.component.scss create mode 100644 src/app/fyle/dashboard/tasks/dismiss-dialog/dismiss-dialog.component.spec.ts create mode 100644 src/app/fyle/dashboard/tasks/dismiss-dialog/dismiss-dialog.component.ts diff --git a/src/app/fyle/dashboard/tasks/dismiss-dialog/dismiss-dialog.component.html b/src/app/fyle/dashboard/tasks/dismiss-dialog/dismiss-dialog.component.html new file mode 100644 index 0000000000..31408ad944 --- /dev/null +++ b/src/app/fyle/dashboard/tasks/dismiss-dialog/dismiss-dialog.component.html @@ -0,0 +1,30 @@ +
+ + + + +
Dismiss duplicate expenses
+
+
+ + +
Are you sure you want to take this action?
+
+
+ + +
+ No, go back +
+
+ +
Yes, Dismiss
+
+
+
+
+
diff --git a/src/app/fyle/dashboard/tasks/dismiss-dialog/dismiss-dialog.component.scss b/src/app/fyle/dashboard/tasks/dismiss-dialog/dismiss-dialog.component.scss new file mode 100644 index 0000000000..73856c000b --- /dev/null +++ b/src/app/fyle/dashboard/tasks/dismiss-dialog/dismiss-dialog.component.scss @@ -0,0 +1,43 @@ +@import '../../../../../theme/colors.scss'; + +.dismiss-dialog { + &--header { + font-size: 16px; + line-height: 20px; + color: $black; + font-weight: 500; + } + + &--body { + font-size: 14px; + line-height: 18px; + color: $blue-black; + font-weight: 400; + margin-top: 16px; + } + + &--cta-container { + margin-top: 16px; + } + + &--dismiss { + display: flex; + padding: 12px 16px; + justify-content: center; + color: $pure-white; + align-items: center; + border-radius: 4px; + background: $pink-gradient; + } + + &--cancel { + display: flex; + padding: 12px 16px; + justify-content: center; + align-items: center; + color: $blue-black; + &__disabled { + opacity: 0.2; + } + } +} diff --git a/src/app/fyle/dashboard/tasks/dismiss-dialog/dismiss-dialog.component.spec.ts b/src/app/fyle/dashboard/tasks/dismiss-dialog/dismiss-dialog.component.spec.ts new file mode 100644 index 0000000000..92786dd0f3 --- /dev/null +++ b/src/app/fyle/dashboard/tasks/dismiss-dialog/dismiss-dialog.component.spec.ts @@ -0,0 +1,100 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { IonicModule, PopoverController } from '@ionic/angular'; + +import { DismissDialogComponent } from './dismiss-dialog.component'; +import { FormButtonValidationDirective } from 'src/app/shared/directive/form-button-validation.directive'; +import { MatIconModule } from '@angular/material/icon'; +import { MatIconTestingModule } from '@angular/material/icon/testing'; +import { FormsModule } from '@angular/forms'; +import { of, throwError } from 'rxjs'; +import { click, getElementBySelector, getTextContent } from 'src/app/core/dom-helpers'; + +describe('DismissDialogComponent', () => { + let component: DismissDialogComponent; + let fixture: ComponentFixture; + let popoverController: jasmine.SpyObj; + + const dismissMethod = () => of(true); + const errMethod = () => throwError(() => new Error('error')); + + beforeEach(waitForAsync(() => { + const popoverControllerSpy = jasmine.createSpyObj('PopoverController', ['dismiss']); + TestBed.configureTestingModule({ + declarations: [DismissDialogComponent, FormButtonValidationDirective], + imports: [IonicModule.forRoot(), FormsModule, MatIconTestingModule, MatIconModule], + providers: [ + { + provide: PopoverController, + useValue: popoverControllerSpy, + }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(DismissDialogComponent); + component = fixture.componentInstance; + popoverController = TestBed.inject(PopoverController) as jasmine.SpyObj; + fixture.detectChanges(); + })); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should display header and CTA text correctly', () => { + fixture.detectChanges(); + + expect(getTextContent(getElementBySelector(fixture, '.dismiss-dialog--header'))).toEqual( + 'Dismiss duplicate expenses' + ); + expect(getTextContent(getElementBySelector(fixture, '.dismiss-dialog--dismiss'))).toEqual('Yes, Dismiss'); + }); + + it('cancel(): should cancel the CTA', () => { + popoverController.dismiss.and.callThrough(); + component.dismissCallInProgress = false; + fixture.detectChanges(); + + component.cancel(); + expect(popoverController.dismiss).toHaveBeenCalledTimes(1); + }); + + describe('dismiss():', () => { + it('should dismiss expense', (done) => { + popoverController.dismiss.and.callThrough(); + component.dismissCallInProgress = false; + component.dismissMethod = dismissMethod; + fixture.detectChanges(); + + component.dismiss(); + expect(popoverController.dismiss).toHaveBeenCalledWith({ status: 'success' }); + done(); + }); + + it('should not dismiss expenses if dismiss method throws error', (done) => { + popoverController.dismiss.and.callThrough(); + component.dismissCallInProgress = false; + component.dismissMethod = errMethod; + fixture.detectChanges(); + + component.dismiss(); + expect(popoverController.dismiss).toHaveBeenCalledWith({ status: 'error' }); + done(); + }); + }); + + it('should call cancel() if button is clicked', () => { + const cancelFn = spyOn(component, 'cancel'); + + const cancelButton = getElementBySelector(fixture, '.dismiss-dialog--cancel') as HTMLElement; + click(cancelButton); + expect(cancelFn).toHaveBeenCalledTimes(1); + }); + + it('should call dismiss() if card is clicked', () => { + const dismissFn = spyOn(component, 'dismiss'); + + const dismissCard = getElementBySelector(fixture, '.dismiss-dialog--dismiss') as HTMLElement; + click(dismissCard); + expect(dismissFn).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/app/fyle/dashboard/tasks/dismiss-dialog/dismiss-dialog.component.ts b/src/app/fyle/dashboard/tasks/dismiss-dialog/dismiss-dialog.component.ts new file mode 100644 index 0000000000..15c00796b8 --- /dev/null +++ b/src/app/fyle/dashboard/tasks/dismiss-dialog/dismiss-dialog.component.ts @@ -0,0 +1,45 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { Observable } from 'rxjs/internal/Observable'; +import { PopoverController } from '@ionic/angular'; +import { catchError, finalize, map } from 'rxjs/operators'; +import { of } from 'rxjs'; + +@Component({ + selector: 'app-dismiss-dialog', + templateUrl: './dismiss-dialog.component.html', + styleUrls: ['./dismiss-dialog.component.scss'], +}) +export class DismissDialogComponent implements OnInit { + @Input() dismissMethod: () => Observable<{}>; + + dismissCallInProgress: boolean; + + constructor(private popoverController: PopoverController) {} + + ngOnInit(): void { + this.dismissCallInProgress = false; + } + + cancel(): void { + if (!this.dismissCallInProgress) { + this.popoverController.dismiss(); + } + } + + dismiss(): void { + this.dismissCallInProgress = true; + this.dismissMethod() + .pipe( + map(() => ({ status: 'success' })), + catchError(() => + of({ + status: 'error', + }) + ), + finalize(() => (this.dismissCallInProgress = false)) + ) + .subscribe((res) => { + this.popoverController.dismiss(res); + }); + } +} diff --git a/src/app/fyle/my-expenses/my-expenses.module.ts b/src/app/fyle/my-expenses/my-expenses.module.ts index 62f7335f52..34fc6b8516 100644 --- a/src/app/fyle/my-expenses/my-expenses.module.ts +++ b/src/app/fyle/my-expenses/my-expenses.module.ts @@ -16,6 +16,7 @@ import { AddTxnToReportDialogComponent } from './add-txn-to-report-dialog/add-tx import { MatBottomSheetModule } from '@angular/material/bottom-sheet'; import { MatSnackBarModule } from '@angular/material/snack-bar'; import { MatCheckboxModule } from '@angular/material/checkbox'; +import { DismissDialogComponent } from '../dashboard/tasks/dismiss-dialog/dismiss-dialog.component'; @NgModule({ imports: [ @@ -40,6 +41,6 @@ import { MatCheckboxModule } from '@angular/material/checkbox'; SharedModule, MatCheckboxModule, ], - declarations: [MyExpensesPage, AddTxnToReportDialogComponent], + declarations: [MyExpensesPage, AddTxnToReportDialogComponent, DismissDialogComponent], }) export class MyExpensesPageModule {} diff --git a/src/app/fyle/potential-duplicates/potential-duplicates.page.spec.ts b/src/app/fyle/potential-duplicates/potential-duplicates.page.spec.ts index ad7212040e..f160cd6177 100644 --- a/src/app/fyle/potential-duplicates/potential-duplicates.page.spec.ts +++ b/src/app/fyle/potential-duplicates/potential-duplicates.page.spec.ts @@ -14,6 +14,8 @@ import { ExpensesService } from 'src/app/core/services/platform/v1/spender/expen import { cloneDeep } from 'lodash'; import { OrgSettingsService } from 'src/app/core/services/org-settings.service'; import { expenseDuplicateSet } from 'src/app/core/mock-data/platform/v1/expense-duplicate-sets.data'; +import { PopoverController } from '@ionic/angular'; +import { DismissDialogComponent } from '../dashboard/tasks/dismiss-dialog/dismiss-dialog.component'; describe('PotentialDuplicatesPage', () => { let component: PotentialDuplicatesPage; @@ -23,6 +25,7 @@ describe('PotentialDuplicatesPage', () => { let matSnackBar: jasmine.SpyObj; let trackingService: jasmine.SpyObj; let expensesService: jasmine.SpyObj; + let popoverController: jasmine.SpyObj; beforeEach(waitForAsync(() => { const routerSpy = jasmine.createSpyObj('Router', ['navigate']); @@ -39,6 +42,7 @@ describe('PotentialDuplicatesPage', () => { 'dismissDuplicates', ]); const orgSettingsServiceSpy = jasmine.createSpyObj('OrgSettingsService', ['get']); + const popoverControllerSpy = jasmine.createSpyObj('PopoverController', ['create']); TestBed.configureTestingModule({ declarations: [PotentialDuplicatesPage], @@ -68,6 +72,10 @@ describe('PotentialDuplicatesPage', () => { provide: OrgSettingsService, useValue: orgSettingsServiceSpy, }, + { + provide: PopoverController, + useValue: popoverControllerSpy, + }, ], schemas: [CUSTOM_ELEMENTS_SCHEMA], }).compileComponents(); @@ -81,6 +89,7 @@ describe('PotentialDuplicatesPage', () => { matSnackBar = TestBed.inject(MatSnackBar) as jasmine.SpyObj; trackingService = TestBed.inject(TrackingService) as jasmine.SpyObj; expensesService = TestBed.inject(ExpensesService) as jasmine.SpyObj; + popoverController = TestBed.inject(PopoverController) as jasmine.SpyObj; component.loadData$ = new BehaviorSubject(null); component.duplicateSets$ = of([]); @@ -157,47 +166,160 @@ describe('PotentialDuplicatesPage', () => { expect(component.selectedSet).toEqual(0); }); + it('dismissDuplicates(): should call dismissDuplicates on expensesService with the expenses ids', () => { + const duplicateExpenseIds = ['txDDLtRaflUW']; + const targetExpenseIds = ['txcSFe6efB6R']; + + expensesService.dismissDuplicates.and.returnValue(of(null)); + + component.dismissDuplicates(duplicateExpenseIds, targetExpenseIds).subscribe(() => { + expect(expensesService.dismissDuplicates).toHaveBeenCalledOnceWith(duplicateExpenseIds, targetExpenseIds); + }); + }); + describe('dismiss():', () => { beforeEach(() => { component.duplicateSetData = cloneDeep([['txcSFe6efB6R', 'txDDLtRaflUW']]); component.selectedSet = 0; spyOn(component, 'showDismissedSuccessToast'); + spyOn(component, 'dismissDuplicates'); component.duplicateExpenses = [[apiExpenses1[0], expenseData]]; expensesService.dismissDuplicates.and.returnValue(of(null)); }); - it('should dismiss a duplicate expense', () => { - component.dismiss(apiExpenses1[0]); + it('should dismiss a duplicate expense and show the dialog', async () => { + const popoverResponse = { data: { status: 'success' } }; + const popoverSpy = jasmine.createSpyObj('Popover', ['present', 'onDidDismiss']); + popoverSpy.onDidDismiss.and.resolveTo(popoverResponse); + popoverController.create.and.resolveTo(popoverSpy); - expect(component.duplicateExpenses[0].length).toEqual(1); - expect(expensesService.dismissDuplicates).toHaveBeenCalledOnceWith( - ['txcSFe6efB6R', 'txDDLtRaflUW'], - ['txDDLtRaflUW'] - ); - expect(component.showDismissedSuccessToast).toHaveBeenCalledTimes(1); + await component.dismiss(apiExpenses1[0]); + + expect(popoverController.create).toHaveBeenCalledWith({ + component: DismissDialogComponent, + cssClass: 'dismiss-dialog', + backdropDismiss: false, + componentProps: { + dismissMethod: jasmine.any(Function), + }, + }); + + expect(popoverSpy.present).toHaveBeenCalledTimes(1); + expect(popoverSpy.onDidDismiss).toHaveBeenCalledTimes(1); + + const dismissMethod = popoverController.create.calls.mostRecent().args[0].componentProps.dismissMethod; + await dismissMethod(); + + expect(component.dismissDuplicates).toHaveBeenCalledWith(['txcSFe6efB6R', 'txDDLtRaflUW'], ['txDDLtRaflUW']); + + if (popoverResponse.data?.status === 'success') { + expect(component.duplicateExpenses[0].length).toEqual(1); + expect(component.showDismissedSuccessToast).toHaveBeenCalledTimes(1); + } + }); + + it('should not dismiss a duplicate expense if popover is dismissed without success status', async () => { + const popoverResponse = { data: { status: 'error' } }; + const popoverSpy = jasmine.createSpyObj('Popover', ['present', 'onDidDismiss']); + popoverSpy.onDidDismiss.and.resolveTo(popoverResponse); + popoverController.create.and.resolveTo(popoverSpy); + + await component.dismiss(apiExpenses1[0]); + + expect(popoverController.create).toHaveBeenCalledWith({ + component: DismissDialogComponent, + cssClass: 'dismiss-dialog', + backdropDismiss: false, + componentProps: { + dismissMethod: jasmine.any(Function), + }, + }); + + expect(popoverSpy.present).toHaveBeenCalledTimes(1); + expect(popoverSpy.onDidDismiss).toHaveBeenCalledTimes(1); + expect(component.duplicateExpenses[0].length).toEqual(2); + expect(component.showDismissedSuccessToast).not.toHaveBeenCalled(); }); }); describe('dismissAll(): ', () => { - it('should dismiss all transactions', () => { + it('should dismiss all transactions and show the dialog', async () => { component.duplicateSetData = [['tx5fBcPBAxLv'], ['tx5fBcPBAxLv', 'tx3nHShG60zq']]; component.selectedSet = 1; + const popoverResponse = { data: { status: 'success' } }; + expensesService.dismissDuplicates.and.returnValue(of(null)); spyOn(component, 'showDismissedSuccessToast'); spyOn(component.loadData$, 'next'); + spyOn(component, 'dismissDuplicates'); expensesService.getExpenses.and.returnValue(of([apiExpenses1[0], expenseData])); component.duplicateSets$ = of([[apiExpenses1[0]], [expenseData]]); - component.dismissAll(); + const popoverSpy = jasmine.createSpyObj('Popover', ['present', 'onDidDismiss']); + popoverSpy.onDidDismiss.and.resolveTo(popoverResponse); + popoverController.create.and.resolveTo(popoverSpy); - expect(expensesService.dismissDuplicates).toHaveBeenCalledOnceWith( + await component.dismissAll(); + + expect(popoverController.create).toHaveBeenCalledWith({ + component: DismissDialogComponent, + cssClass: 'dismiss-dialog', + backdropDismiss: false, + componentProps: { + dismissMethod: jasmine.any(Function), + }, + }); + + const dismissMethod = popoverController.create.calls.mostRecent().args[0].componentProps.dismissMethod; + await dismissMethod(); + + expect(component.dismissDuplicates).toHaveBeenCalledWith( ['tx5fBcPBAxLv', 'tx3nHShG60zq'], ['tx5fBcPBAxLv', 'tx3nHShG60zq'] ); - expect(component.selectedSet).toEqual(0); - expect(trackingService.dismissedDuplicateSet).toHaveBeenCalledTimes(1); - expect(component.showDismissedSuccessToast).toHaveBeenCalledTimes(1); - expect(component.loadData$.next).toHaveBeenCalledTimes(1); + + expect(popoverSpy.present).toHaveBeenCalledTimes(1); + expect(popoverSpy.onDidDismiss).toHaveBeenCalledTimes(1); + + if (popoverResponse.data?.status === 'success') { + expect(component.selectedSet).toEqual(0); + expect(trackingService.dismissedDuplicateSet).toHaveBeenCalledTimes(1); + expect(component.showDismissedSuccessToast).toHaveBeenCalledTimes(1); + expect(component.loadData$.next).toHaveBeenCalledTimes(1); + } + }); + + it('should not perform actions if popover is dismissed without success status', async () => { + component.duplicateSetData = [['tx5fBcPBAxLv'], ['tx5fBcPBAxLv', 'tx3nHShG60zq']]; + component.selectedSet = 1; + const popoverResponse = { data: { status: 'error' } }; + + spyOn(component, 'showDismissedSuccessToast'); + spyOn(component.loadData$, 'next'); + expensesService.getExpenses.and.returnValue(of([apiExpenses1[0], expenseData])); + component.duplicateSets$ = of([[apiExpenses1[0]], [expenseData]]); + + const popoverSpy = jasmine.createSpyObj('Popover', ['present', 'onDidDismiss']); + popoverSpy.onDidDismiss.and.resolveTo(popoverResponse); + popoverController.create.and.resolveTo(popoverSpy); + + await component.dismissAll(); + + expect(popoverController.create).toHaveBeenCalledWith({ + component: DismissDialogComponent, + cssClass: 'dismiss-dialog', + backdropDismiss: false, + componentProps: { + dismissMethod: jasmine.any(Function), + }, + }); + + expect(popoverSpy.present).toHaveBeenCalledTimes(1); + expect(popoverSpy.onDidDismiss).toHaveBeenCalledTimes(1); + expect(component.selectedSet).toEqual(1); + expect(trackingService.dismissedDuplicateSet).not.toHaveBeenCalled(); + expect(component.showDismissedSuccessToast).not.toHaveBeenCalled(); + expect(component.loadData$.next).not.toHaveBeenCalled(); }); }); diff --git a/src/app/fyle/potential-duplicates/potential-duplicates.page.ts b/src/app/fyle/potential-duplicates/potential-duplicates.page.ts index 065386fa85..68afe6b4f4 100644 --- a/src/app/fyle/potential-duplicates/potential-duplicates.page.ts +++ b/src/app/fyle/potential-duplicates/potential-duplicates.page.ts @@ -8,8 +8,9 @@ import { SnackbarPropertiesService } from 'src/app/core/services/snackbar-proper import { TrackingService } from 'src/app/core/services/tracking.service'; import { ExpensesService } from 'src/app/core/services/platform/v1/spender/expenses.service'; import { ToastMessageComponent } from 'src/app/shared/components/toast-message/toast-message.component'; - -type Expenses = Expense[]; +import { DismissDialogComponent } from '../dashboard/tasks/dismiss-dialog/dismiss-dialog.component'; +import { PopoverController } from '@ionic/angular'; +import { OverlayResponse } from 'src/app/core/models/overlay-response.modal'; @Component({ selector: 'app-potential-duplicates', @@ -17,7 +18,7 @@ type Expenses = Expense[]; styleUrls: ['./potential-duplicates.page.scss'], }) export class PotentialDuplicatesPage { - duplicateSets$: Observable; + duplicateSets$: Observable; loadData$ = new BehaviorSubject(null); @@ -25,7 +26,7 @@ export class PotentialDuplicatesPage { duplicateSetData: string[][]; - duplicateExpenses: Expenses[]; + duplicateExpenses: Expense[][]; isLoading = true; @@ -34,7 +35,8 @@ export class PotentialDuplicatesPage { private router: Router, private snackbarProperties: SnackbarPropertiesService, private matSnackBar: MatSnackBar, - private trackingService: TrackingService + private trackingService: TrackingService, + private popoverController: PopoverController ) {} ionViewWillEnter(): void { @@ -106,11 +108,24 @@ export class PotentialDuplicatesPage { return this.expensesService.dismissDuplicates(duplicateExpenseIds, targetExpenseIds); } - dismiss(expense: Expense): void { - const transactionIds = [expense.id]; - const duplicateTxnIds = this.duplicateSetData[this.selectedSet]; + async dismiss(expense: Expense): Promise { + const targetExpenseIds = [expense.id]; + const duplicateExpenseIds = this.duplicateSetData[this.selectedSet]; + + const popover = await this.popoverController.create({ + component: DismissDialogComponent, + cssClass: 'dismiss-dialog', + backdropDismiss: false, + componentProps: { + dismissMethod: () => this.dismissDuplicates(duplicateExpenseIds, targetExpenseIds), + }, + }); + + await popover.present(); - this.dismissDuplicates(duplicateTxnIds, transactionIds).subscribe(() => { + const popoverResponse = (await popover.onDidDismiss()) as OverlayResponse<{ status: string }>; + + if (popoverResponse.data?.status === 'success') { this.trackingService.dismissedIndividualExpenses(); this.showDismissedSuccessToast(); this.duplicateSetData[this.selectedSet] = this.duplicateSetData[this.selectedSet].filter( @@ -119,12 +134,26 @@ export class PotentialDuplicatesPage { this.duplicateExpenses[this.selectedSet] = this.duplicateExpenses[this.selectedSet].filter( (exp) => exp.id !== expense.id ); - }); + } } - dismissAll(): void { - const txnIds = this.duplicateSetData[this.selectedSet]; - this.dismissDuplicates(txnIds, txnIds).subscribe(() => { + async dismissAll(): Promise { + const expenseIds = this.duplicateSetData[this.selectedSet]; + + const popover = await this.popoverController.create({ + component: DismissDialogComponent, + cssClass: 'dismiss-dialog', + backdropDismiss: false, + componentProps: { + dismissMethod: () => this.dismissDuplicates(expenseIds, expenseIds), + }, + }); + + await popover.present(); + + const popoverResponse = (await popover.onDidDismiss()) as OverlayResponse<{ status: string }>; + + if (popoverResponse.data?.status === 'success') { if (this.selectedSet !== 0) { this.selectedSet--; } @@ -134,7 +163,7 @@ export class PotentialDuplicatesPage { this.duplicateSets$.subscribe((duplicateExpenses) => { this.duplicateExpenses = duplicateExpenses; }); - }); + } } mergeExpense(): void { diff --git a/src/global.scss b/src/global.scss index 23654bcc17..50cbba3a74 100644 --- a/src/global.scss +++ b/src/global.scss @@ -952,6 +952,17 @@ ion-modal.flag-unflag-modal { padding-top: 0 !important; } +ion-popover.dismiss-dialog { + --width: 298px; + + &::part(content) { + border-radius: 8px; + padding: 24px; + } + + background: rgba(0, 0, 0, 0.6); +} + ion-popover.delete-dialog { --width: 296px; --max-height: 346px;