fix: kill remaining native confirm() in admin, standardize on themed dialog
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
Order cancel/refund and user suspend used window.confirm(). Migrated all three to app-confirm-dialog, matching products/categories/orders delete gates from earlier in this phase. Remaining window.confirm() usages are the three canDeactivate dirty guards (categories/products/project-editor) - left as-is, since CanDeactivate needs a synchronous or Observable/Promise return and browser navigation guards conventionally use the native dialog there; converting those is a separate, larger refactor. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -88,6 +88,20 @@
|
|||||||
<app-button variant="secondary" size="sm" (click)="submitInternalNote(order.id)">{{ 'adminOrders.addNote' | translate }}</app-button>
|
<app-button variant="secondary" size="sm" (click)="submitInternalNote(order.id)">{{ 'adminOrders.addNote' | translate }}</app-button>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
<app-confirm-dialog
|
||||||
|
[open]="!!pendingCancelId()"
|
||||||
|
[titleText]="'adminOrders.cancelOrder' | translate"
|
||||||
|
[message]="'adminOrders.confirmCancel' | translate"
|
||||||
|
[destructive]="true"
|
||||||
|
(confirmed)="confirmCancel()"
|
||||||
|
(cancelled)="pendingCancelId.set(null)" />
|
||||||
|
<app-confirm-dialog
|
||||||
|
[open]="!!pendingRefundId()"
|
||||||
|
[titleText]="'adminOrders.requestRefund' | translate"
|
||||||
|
[message]="'adminOrders.confirmRefund' | translate"
|
||||||
|
[destructive]="true"
|
||||||
|
(confirmed)="confirmRefund()"
|
||||||
|
(cancelled)="pendingRefundId.set(null)" />
|
||||||
</main>
|
</main>
|
||||||
} @else {
|
} @else {
|
||||||
<p>{{ 'common.loading' | translate }}</p>
|
<p>{{ 'common.loading' | translate }}</p>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { LanguageService } from '../../../../services/language.service';
|
|||||||
import { ButtonComponent } from '../../../../shared/ui/button/button.component';
|
import { ButtonComponent } from '../../../../shared/ui/button/button.component';
|
||||||
import { BadgeComponent } from '../../../../shared/ui/badge/badge.component';
|
import { BadgeComponent } from '../../../../shared/ui/badge/badge.component';
|
||||||
import { OrderTimelineComponent, OrderTimelineEntry } from '../components/order-timeline/order-timeline.component';
|
import { OrderTimelineComponent, OrderTimelineEntry } from '../components/order-timeline/order-timeline.component';
|
||||||
|
import { ConfirmDialogComponent } from '../../../../shared/ui/confirm-dialog/confirm-dialog.component';
|
||||||
|
|
||||||
const WORKFLOW_STEPS: AdminOrderStatus[] = ['pending', 'processing', 'shipped', 'delivered'];
|
const WORKFLOW_STEPS: AdminOrderStatus[] = ['pending', 'processing', 'shipped', 'delivered'];
|
||||||
const TERMINAL_STATUSES: AdminOrderStatus[] = ['cancelled', 'refunded'];
|
const TERMINAL_STATUSES: AdminOrderStatus[] = ['cancelled', 'refunded'];
|
||||||
@@ -17,7 +18,7 @@ const TERMINAL_STATUSES: AdminOrderStatus[] = ['cancelled', 'refunded'];
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'app-admin-order-detail-page',
|
selector: 'app-admin-order-detail-page',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [CommonModule, FormsModule, TranslatePipe, ButtonComponent, BadgeComponent, OrderTimelineComponent],
|
imports: [CommonModule, FormsModule, TranslatePipe, ButtonComponent, BadgeComponent, OrderTimelineComponent, ConfirmDialogComponent],
|
||||||
templateUrl: './admin-order-detail-page.component.html',
|
templateUrl: './admin-order-detail-page.component.html',
|
||||||
styleUrls: ['./admin-order-detail-page.component.scss'],
|
styleUrls: ['./admin-order-detail-page.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
@@ -34,6 +35,8 @@ export class AdminOrderDetailPageComponent {
|
|||||||
readonly selectableStatuses: AdminOrderStatus[] = this.statuses.filter(status => !TERMINAL_STATUSES.includes(status));
|
readonly selectableStatuses: AdminOrderStatus[] = this.statuses.filter(status => !TERMINAL_STATUSES.includes(status));
|
||||||
readonly workflowSteps = WORKFLOW_STEPS;
|
readonly workflowSteps = WORKFLOW_STEPS;
|
||||||
readonly noteDraft = signal('');
|
readonly noteDraft = signal('');
|
||||||
|
readonly pendingCancelId = signal<string | null>(null);
|
||||||
|
readonly pendingRefundId = signal<string | null>(null);
|
||||||
readonly internalNoteDraft = signal('');
|
readonly internalNoteDraft = signal('');
|
||||||
|
|
||||||
readonly isTerminal = computed(() => {
|
readonly isTerminal = computed(() => {
|
||||||
@@ -85,15 +88,27 @@ export class AdminOrderDetailPageComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cancel(id: string): void {
|
cancel(id: string): void {
|
||||||
if (window.confirm(this.translate.t('adminOrders.confirmCancel'))) {
|
this.pendingCancelId.set(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmCancel(): void {
|
||||||
|
const id = this.pendingCancelId();
|
||||||
|
if (id) {
|
||||||
this.facade.cancelOrder(id);
|
this.facade.cancelOrder(id);
|
||||||
}
|
}
|
||||||
|
this.pendingCancelId.set(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
requestRefund(id: string): void {
|
requestRefund(id: string): void {
|
||||||
if (window.confirm(this.translate.t('adminOrders.confirmRefund'))) {
|
this.pendingRefundId.set(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmRefund(): void {
|
||||||
|
const id = this.pendingRefundId();
|
||||||
|
if (id) {
|
||||||
this.facade.requestRefund(id);
|
this.facade.requestRefund(id);
|
||||||
}
|
}
|
||||||
|
this.pendingRefundId.set(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
submitNote(id: string): void {
|
submitNote(id: string): void {
|
||||||
|
|||||||
@@ -120,4 +120,12 @@
|
|||||||
<p>{{ entry.timestamp | date:'short' }} — {{ ('adminUsers.actor.' + entry.actor) | translate }} — {{ auditText(entry) }}</p>
|
<p>{{ entry.timestamp | date:'short' }} — {{ ('adminUsers.actor.' + entry.actor) | translate }} — {{ auditText(entry) }}</p>
|
||||||
}
|
}
|
||||||
</app-dialog>
|
</app-dialog>
|
||||||
|
|
||||||
|
<app-confirm-dialog
|
||||||
|
[open]="!!pendingSuspendUserId()"
|
||||||
|
[titleText]="'adminUsers.suspend' | translate"
|
||||||
|
[message]="'adminUsers.confirmSuspend' | translate"
|
||||||
|
[destructive]="true"
|
||||||
|
(confirmed)="confirmSuspend()"
|
||||||
|
(cancelled)="pendingSuspendUserId.set(null)" />
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -12,11 +12,12 @@ import { TableComponent } from '../../../../shared/ui/table/table.component';
|
|||||||
import { DialogComponent } from '../../../../shared/ui/dialog/dialog.component';
|
import { DialogComponent } from '../../../../shared/ui/dialog/dialog.component';
|
||||||
import { SkeletonComponent } from '../../../../shared/ui/skeleton/skeleton.component';
|
import { SkeletonComponent } from '../../../../shared/ui/skeleton/skeleton.component';
|
||||||
import { EmptyStateComponent } from '../../../../shared/ui/empty-state/empty-state.component';
|
import { EmptyStateComponent } from '../../../../shared/ui/empty-state/empty-state.component';
|
||||||
|
import { ConfirmDialogComponent } from '../../../../shared/ui/confirm-dialog/confirm-dialog.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-admin-users-page',
|
selector: 'app-admin-users-page',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [CommonModule, FormsModule, TranslatePipe, ButtonComponent, InputComponent, BadgeComponent, TableComponent, DialogComponent, SkeletonComponent, EmptyStateComponent],
|
imports: [CommonModule, FormsModule, TranslatePipe, ButtonComponent, InputComponent, BadgeComponent, TableComponent, DialogComponent, SkeletonComponent, EmptyStateComponent, ConfirmDialogComponent],
|
||||||
templateUrl: './admin-users-page.component.html',
|
templateUrl: './admin-users-page.component.html',
|
||||||
styleUrls: ['./admin-users-page.component.scss'],
|
styleUrls: ['./admin-users-page.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
@@ -28,6 +29,7 @@ export class AdminUsersPageComponent {
|
|||||||
readonly inviteEmail = signal('');
|
readonly inviteEmail = signal('');
|
||||||
readonly inviteRoleId = signal('viewer');
|
readonly inviteRoleId = signal('viewer');
|
||||||
readonly inviteScope = signal<AdminUserScope>('office');
|
readonly inviteScope = signal<AdminUserScope>('office');
|
||||||
|
readonly pendingSuspendUserId = signal<string | null>(null);
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.facade.loadAll();
|
this.facade.loadAll();
|
||||||
@@ -40,12 +42,21 @@ export class AdminUsersPageComponent {
|
|||||||
|
|
||||||
toggleStatus(userId: string, current: AdminUserStatus): void {
|
toggleStatus(userId: string, current: AdminUserStatus): void {
|
||||||
const next: AdminUserStatus = current === 'suspended' ? 'active' : 'suspended';
|
const next: AdminUserStatus = current === 'suspended' ? 'active' : 'suspended';
|
||||||
if (next === 'suspended' && !window.confirm(this.translate.t('adminUsers.confirmSuspend'))) {
|
if (next === 'suspended') {
|
||||||
|
this.pendingSuspendUserId.set(userId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.facade.setStatus(userId, next);
|
this.facade.setStatus(userId, next);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
confirmSuspend(): void {
|
||||||
|
const userId = this.pendingSuspendUserId();
|
||||||
|
if (userId) {
|
||||||
|
this.facade.setStatus(userId, 'suspended');
|
||||||
|
}
|
||||||
|
this.pendingSuspendUserId.set(null);
|
||||||
|
}
|
||||||
|
|
||||||
private static readonly PERMISSION_KEYS: Record<string, string> = {
|
private static readonly PERMISSION_KEYS: Record<string, string> = {
|
||||||
'*': 'all',
|
'*': 'all',
|
||||||
'products.manage': 'productsManage',
|
'products.manage': 'productsManage',
|
||||||
|
|||||||
Reference in New Issue
Block a user