Compare commits
5 Commits
8a91a862ca
...
c461d9bd5f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c461d9bd5f | ||
|
|
63039b8707 | ||
|
|
908f10e022 | ||
|
|
49aab63124 | ||
|
|
320f1f44b7 |
@@ -1,14 +1,18 @@
|
|||||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { AdminCategoriesFacade } from '../facade/admin-categories.facade';
|
import { AdminCategoriesFacade } from '../facade/admin-categories.facade';
|
||||||
import { AdminCategoriesListComponent } from '../components/admin-categories-list.component';
|
import { AdminCategoriesListComponent } from '../components/admin-categories-list.component';
|
||||||
import { LanguageService } from '../../../../services/language.service';
|
import { LanguageService } from '../../../../services/language.service';
|
||||||
import { TranslateService } from '../../../../i18n/translate.service';
|
import { TranslateService } from '../../../../i18n/translate.service';
|
||||||
|
import { ConfirmDialogComponent } from '../../../../shared/ui/confirm-dialog/confirm-dialog.component';
|
||||||
|
import { DialogComponent } from '../../../../shared/ui/dialog/dialog.component';
|
||||||
|
import { ButtonComponent } from '../../../../shared/ui/button/button.component';
|
||||||
|
import { TranslatePipe } from '../../../../i18n/translate.pipe';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-admin-categories-list-page',
|
selector: 'app-admin-categories-list-page',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [AdminCategoriesListComponent],
|
imports: [AdminCategoriesListComponent, ConfirmDialogComponent, DialogComponent, ButtonComponent, TranslatePipe],
|
||||||
template: `<app-admin-categories-list
|
template: `<app-admin-categories-list
|
||||||
[treeRows]="facade.visibleTreeRows()"
|
[treeRows]="facade.visibleTreeRows()"
|
||||||
[flatCategories]="facade.filteredCategories()"
|
[flatCategories]="facade.filteredCategories()"
|
||||||
@@ -37,14 +41,36 @@ import { TranslateService } from '../../../../i18n/translate.service';
|
|||||||
(selectionChange)="facade.toggleSelection($event.id, $event.checked)"
|
(selectionChange)="facade.toggleSelection($event.id, $event.checked)"
|
||||||
(selectAll)="facade.toggleAll($event)"
|
(selectAll)="facade.toggleAll($event)"
|
||||||
(bulkVisibility)="facade.applyBulkVisibility($event)"
|
(bulkVisibility)="facade.applyBulkVisibility($event)"
|
||||||
(bulkDelete)="facade.applyBulkDelete()"
|
(bulkDelete)="bulkDelete()"
|
||||||
(bulkDuplicate)="facade.applyBulkDuplicate()"
|
(bulkDuplicate)="facade.applyBulkDuplicate()"
|
||||||
(bulkAssignParent)="facade.applyBulkAssignParent($event)"
|
(bulkAssignParent)="facade.applyBulkAssignParent($event)"
|
||||||
(bulkAssignImage)="facade.applyBulkAssignImage($event)"
|
(bulkAssignImage)="facade.applyBulkAssignImage($event)"
|
||||||
(bulkExport)="facade.exportSelectedAsCsv()"
|
(bulkExport)="facade.exportSelectedAsCsv()"
|
||||||
(viewModeChange)="facade.setViewMode($event)"
|
(viewModeChange)="facade.setViewMode($event)"
|
||||||
(densityChange)="facade.setDensity($event)"
|
(densityChange)="facade.setDensity($event)"
|
||||||
(columnToggle)="facade.setColumnVisible($event.column, $event.visible)" />`,
|
(columnToggle)="facade.setColumnVisible($event.column, $event.visible)" />
|
||||||
|
<app-confirm-dialog
|
||||||
|
[open]="!!pendingDeleteId()"
|
||||||
|
[titleText]="'adminCategories.deleteTitle' | translate"
|
||||||
|
[message]="'adminCategories.confirmDelete' | translate"
|
||||||
|
[destructive]="true"
|
||||||
|
(confirmed)="confirmDelete()"
|
||||||
|
(cancelled)="pendingDeleteId.set(null)" />
|
||||||
|
<app-confirm-dialog
|
||||||
|
[open]="bulkDeleteConfirmOpen()"
|
||||||
|
[titleText]="'adminCategories.deleteTitle' | translate"
|
||||||
|
[message]="'adminCategories.confirmBulkDelete' | translate"
|
||||||
|
[destructive]="true"
|
||||||
|
(confirmed)="confirmBulkDelete()"
|
||||||
|
(cancelled)="bulkDeleteConfirmOpen.set(false)" />
|
||||||
|
@if (deleteBlockedMessage()) {
|
||||||
|
<app-dialog [open]="true" [titleText]="'adminCategories.deleteBlocked' | translate" size="sm" (closed)="deleteBlockedMessage.set(null)">
|
||||||
|
<p>{{ deleteBlockedMessage() }}</p>
|
||||||
|
<div class="app-confirm-dialog__actions">
|
||||||
|
<app-button variant="primary" (click)="deleteBlockedMessage.set(null)">{{ 'common.confirm' | translate }}</app-button>
|
||||||
|
</div>
|
||||||
|
</app-dialog>
|
||||||
|
}`,
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
})
|
})
|
||||||
export class AdminCategoriesListPageComponent {
|
export class AdminCategoriesListPageComponent {
|
||||||
@@ -57,6 +83,10 @@ export class AdminCategoriesListPageComponent {
|
|||||||
readonly isExpandedFn = (id: string) => this.facade.isExpanded(id);
|
readonly isExpandedFn = (id: string) => this.facade.isExpanded(id);
|
||||||
readonly canDeleteFn = (id: string) => this.facade.canDelete(id);
|
readonly canDeleteFn = (id: string) => this.facade.canDelete(id);
|
||||||
|
|
||||||
|
readonly pendingDeleteId = signal<string | null>(null);
|
||||||
|
readonly bulkDeleteConfirmOpen = signal(false);
|
||||||
|
readonly deleteBlockedMessage = signal<string | null>(null);
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.facade.loadList();
|
this.facade.loadList();
|
||||||
this.facade.loadDashboardStats();
|
this.facade.loadDashboardStats();
|
||||||
@@ -75,12 +105,27 @@ export class AdminCategoriesListPageComponent {
|
|||||||
|
|
||||||
deleteOne(id: string): void {
|
deleteOne(id: string): void {
|
||||||
if (!this.facade.canDelete(id)) {
|
if (!this.facade.canDelete(id)) {
|
||||||
window.alert(this.translate.t('adminCategories.deleteBlocked'));
|
this.deleteBlockedMessage.set(this.translate.t('adminCategories.deleteBlocked'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (window.confirm(this.translate.t('adminCategories.confirmDelete'))) {
|
this.pendingDeleteId.set(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmDelete(): void {
|
||||||
|
const id = this.pendingDeleteId();
|
||||||
|
if (id) {
|
||||||
this.facade.deleteOne(id);
|
this.facade.deleteOne(id);
|
||||||
}
|
}
|
||||||
|
this.pendingDeleteId.set(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
bulkDelete(): void {
|
||||||
|
this.bulkDeleteConfirmOpen.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmBulkDelete(): void {
|
||||||
|
this.facade.applyBulkDelete();
|
||||||
|
this.bulkDeleteConfirmOpen.set(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private lang(): string {
|
private lang(): string {
|
||||||
|
|||||||
@@ -53,9 +53,16 @@
|
|||||||
<app-button variant="secondary" size="sm" (click)="applyBulkStatus()">{{ 'adminOrders.applyStatus' | translate }}</app-button>
|
<app-button variant="secondary" size="sm" (click)="applyBulkStatus()">{{ 'adminOrders.applyStatus' | translate }}</app-button>
|
||||||
<app-button variant="secondary" size="sm" (click)="facade.applyBulkVisible(false)">{{ 'adminModeration.hideSelected' | translate }}</app-button>
|
<app-button variant="secondary" size="sm" (click)="facade.applyBulkVisible(false)">{{ 'adminModeration.hideSelected' | translate }}</app-button>
|
||||||
<app-button variant="secondary" size="sm" (click)="facade.exportSelectedAsCsv()">{{ 'adminProducts.bulkExportAction' | translate }}</app-button>
|
<app-button variant="secondary" size="sm" (click)="facade.exportSelectedAsCsv()">{{ 'adminProducts.bulkExportAction' | translate }}</app-button>
|
||||||
<app-button variant="danger" size="sm" (click)="facade.applyBulkDelete()">{{ 'adminModeration.archiveSelected' | translate }}</app-button>
|
<app-button variant="danger" size="sm" (click)="bulkDelete()">{{ 'adminModeration.deleteSelected' | translate }}</app-button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
<app-confirm-dialog
|
||||||
|
[open]="bulkDeleteConfirmOpen()"
|
||||||
|
[titleText]="'adminModeration.deleteSelected' | translate"
|
||||||
|
[message]="'adminModeration.confirmBulkDelete' | translate"
|
||||||
|
[destructive]="true"
|
||||||
|
(confirmed)="confirmBulkDelete()"
|
||||||
|
(cancelled)="bulkDeleteConfirmOpen.set(false)" />
|
||||||
|
|
||||||
@if (facade.loading()) {
|
@if (facade.loading()) {
|
||||||
<div class="skeleton-rows" role="status" aria-live="polite" aria-busy="true">
|
<div class="skeleton-rows" role="status" aria-live="polite" aria-busy="true">
|
||||||
|
|||||||
@@ -16,11 +16,12 @@ import { SkeletonComponent } from '../../../../shared/ui/skeleton/skeleton.compo
|
|||||||
import { CardComponent } from '../../../../shared/ui/card/card.component';
|
import { CardComponent } from '../../../../shared/ui/card/card.component';
|
||||||
import { ModerationDashboardComponent } from '../components/moderation-dashboard/moderation-dashboard.component';
|
import { ModerationDashboardComponent } from '../components/moderation-dashboard/moderation-dashboard.component';
|
||||||
import { ReviewHealthWidgetComponent, ReviewHealthItem } from '../components/review-health-widget/review-health-widget.component';
|
import { ReviewHealthWidgetComponent, ReviewHealthItem } from '../components/review-health-widget/review-health-widget.component';
|
||||||
|
import { ConfirmDialogComponent } from '../../../../shared/ui/confirm-dialog/confirm-dialog.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-admin-reviews-list-page',
|
selector: 'app-admin-reviews-list-page',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [CommonModule, FormsModule, TranslatePipe, ButtonComponent, InputComponent, BadgeComponent, TableComponent, PaginationComponent, EmptyStateComponent, SkeletonComponent, CardComponent, ModerationDashboardComponent, ReviewHealthWidgetComponent],
|
imports: [CommonModule, FormsModule, TranslatePipe, ButtonComponent, InputComponent, BadgeComponent, TableComponent, PaginationComponent, EmptyStateComponent, SkeletonComponent, CardComponent, ModerationDashboardComponent, ReviewHealthWidgetComponent, ConfirmDialogComponent],
|
||||||
templateUrl: './admin-reviews-list-page.component.html',
|
templateUrl: './admin-reviews-list-page.component.html',
|
||||||
styleUrls: ['./admin-reviews-list-page.component.scss'],
|
styleUrls: ['./admin-reviews-list-page.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
@@ -35,6 +36,7 @@ export class AdminReviewsListPageComponent {
|
|||||||
readonly allColumns = ALL_REVIEW_COLUMNS;
|
readonly allColumns = ALL_REVIEW_COLUMNS;
|
||||||
protected readonly columnsPanelOpen = signal(false);
|
protected readonly columnsPanelOpen = signal(false);
|
||||||
protected readonly bulkStatusValue = signal<AdminReviewStatus>('approved');
|
protected readonly bulkStatusValue = signal<AdminReviewStatus>('approved');
|
||||||
|
protected readonly bulkDeleteConfirmOpen = signal(false);
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.facade.loadList();
|
this.facade.loadList();
|
||||||
@@ -45,6 +47,15 @@ export class AdminReviewsListPageComponent {
|
|||||||
return Math.max(1, Math.ceil(this.facade.total() / this.facade.filters().pageSize));
|
return Math.max(1, Math.ceil(this.facade.total() / this.facade.filters().pageSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bulkDelete(): void {
|
||||||
|
this.bulkDeleteConfirmOpen.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmBulkDelete(): void {
|
||||||
|
this.facade.applyBulkDelete();
|
||||||
|
this.bulkDeleteConfirmOpen.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
view(id: string): void {
|
view(id: string): void {
|
||||||
void this.router.navigate([this.languageService.currentLanguage(), 'backoffice', 'moderation', id]);
|
void this.router.navigate([this.languageService.currentLanguage(), 'backoffice', 'moderation', id]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -45,9 +45,16 @@
|
|||||||
<app-button variant="secondary" size="sm" (click)="facade.applyBulkArchive(true)">{{ 'adminOrders.archiveSelected' | translate }}</app-button>
|
<app-button variant="secondary" size="sm" (click)="facade.applyBulkArchive(true)">{{ 'adminOrders.archiveSelected' | translate }}</app-button>
|
||||||
<app-button variant="secondary" size="sm" (click)="facade.exportSelectedAsCsv()">{{ 'adminProducts.bulkExportAction' | translate }}</app-button>
|
<app-button variant="secondary" size="sm" (click)="facade.exportSelectedAsCsv()">{{ 'adminProducts.bulkExportAction' | translate }}</app-button>
|
||||||
<app-button variant="secondary" size="sm" (click)="printSelection()">{{ 'adminOrders.printSelected' | translate }}</app-button>
|
<app-button variant="secondary" size="sm" (click)="printSelection()">{{ 'adminOrders.printSelected' | translate }}</app-button>
|
||||||
<app-button variant="danger" size="sm" (click)="facade.applyBulkDelete()">{{ 'adminProducts.bulkDelete' | translate }}</app-button>
|
<app-button variant="danger" size="sm" (click)="bulkDelete()">{{ 'adminProducts.bulkDelete' | translate }}</app-button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
<app-confirm-dialog
|
||||||
|
[open]="bulkDeleteConfirmOpen()"
|
||||||
|
[titleText]="'adminOrders.bulkDeleteTitle' | translate"
|
||||||
|
[message]="'adminOrders.confirmBulkDelete' | translate"
|
||||||
|
[destructive]="true"
|
||||||
|
(confirmed)="confirmBulkDelete()"
|
||||||
|
(cancelled)="bulkDeleteConfirmOpen.set(false)" />
|
||||||
|
|
||||||
@if (facade.loading()) {
|
@if (facade.loading()) {
|
||||||
<div class="skeleton-rows" role="status" aria-live="polite" aria-busy="true">
|
<div class="skeleton-rows" role="status" aria-live="polite" aria-busy="true">
|
||||||
|
|||||||
@@ -15,11 +15,12 @@ import { EmptyStateComponent } from '../../../../shared/ui/empty-state/empty-sta
|
|||||||
import { SkeletonComponent } from '../../../../shared/ui/skeleton/skeleton.component';
|
import { SkeletonComponent } from '../../../../shared/ui/skeleton/skeleton.component';
|
||||||
import { CardComponent } from '../../../../shared/ui/card/card.component';
|
import { CardComponent } from '../../../../shared/ui/card/card.component';
|
||||||
import { OrdersDashboardComponent } from '../components/orders-dashboard/orders-dashboard.component';
|
import { OrdersDashboardComponent } from '../components/orders-dashboard/orders-dashboard.component';
|
||||||
|
import { ConfirmDialogComponent } from '../../../../shared/ui/confirm-dialog/confirm-dialog.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-admin-orders-list-page',
|
selector: 'app-admin-orders-list-page',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [CommonModule, FormsModule, TranslatePipe, ButtonComponent, InputComponent, BadgeComponent, TableComponent, PaginationComponent, EmptyStateComponent, SkeletonComponent, CardComponent, OrdersDashboardComponent],
|
imports: [CommonModule, FormsModule, TranslatePipe, ButtonComponent, InputComponent, BadgeComponent, TableComponent, PaginationComponent, EmptyStateComponent, SkeletonComponent, CardComponent, OrdersDashboardComponent, ConfirmDialogComponent],
|
||||||
templateUrl: './admin-orders-list-page.component.html',
|
templateUrl: './admin-orders-list-page.component.html',
|
||||||
styleUrls: ['./admin-orders-list-page.component.scss'],
|
styleUrls: ['./admin-orders-list-page.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
@@ -35,6 +36,7 @@ export class AdminOrdersListPageComponent {
|
|||||||
readonly allColumns = ALL_ORDER_COLUMNS;
|
readonly allColumns = ALL_ORDER_COLUMNS;
|
||||||
protected readonly columnsPanelOpen = signal(false);
|
protected readonly columnsPanelOpen = signal(false);
|
||||||
protected readonly bulkStatusValue = signal<AdminOrderStatus>('pending');
|
protected readonly bulkStatusValue = signal<AdminOrderStatus>('pending');
|
||||||
|
protected readonly bulkDeleteConfirmOpen = signal(false);
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.facade.loadList();
|
this.facade.loadList();
|
||||||
@@ -61,6 +63,15 @@ export class AdminOrdersListPageComponent {
|
|||||||
this.facade.applyBulkStatus(this.bulkStatusValue());
|
this.facade.applyBulkStatus(this.bulkStatusValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bulkDelete(): void {
|
||||||
|
this.bulkDeleteConfirmOpen.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmBulkDelete(): void {
|
||||||
|
this.facade.applyBulkDelete();
|
||||||
|
this.bulkDeleteConfirmOpen.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
printSelection(): void {
|
printSelection(): void {
|
||||||
window.print();
|
window.print();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { AdminProductsFacade } from '../facade/admin-products.facade';
|
import { AdminProductsFacade } from '../facade/admin-products.facade';
|
||||||
import { AdminProductsListComponent } from '../components/admin-products-list.component';
|
import { AdminProductsListComponent } from '../components/admin-products-list.component';
|
||||||
import { LanguageService } from '../../../../services/language.service';
|
import { LanguageService } from '../../../../services/language.service';
|
||||||
|
import { ConfirmDialogComponent } from '../../../../shared/ui/confirm-dialog/confirm-dialog.component';
|
||||||
|
import { TranslatePipe } from '../../../../i18n/translate.pipe';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-admin-products-list-page',
|
selector: 'app-admin-products-list-page',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [AdminProductsListComponent],
|
imports: [AdminProductsListComponent, ConfirmDialogComponent, TranslatePipe],
|
||||||
template: `<app-admin-products-list
|
template: `<app-admin-products-list
|
||||||
[products]="facade.products()"
|
[products]="facade.products()"
|
||||||
[categories]="facade.categories()"
|
[categories]="facade.categories()"
|
||||||
@@ -25,7 +27,7 @@ import { LanguageService } from '../../../../services/language.service';
|
|||||||
(create)="create()"
|
(create)="create()"
|
||||||
(edit)="edit($event)"
|
(edit)="edit($event)"
|
||||||
(duplicate)="duplicate($event)"
|
(duplicate)="duplicate($event)"
|
||||||
(delete)="facade.deleteOne($event)"
|
(delete)="deleteOne($event)"
|
||||||
(archive)="facade.archiveOne($event)"
|
(archive)="facade.archiveOne($event)"
|
||||||
(restore)="facade.restoreOne($event)"
|
(restore)="facade.restoreOne($event)"
|
||||||
(loadMore)="facade.loadMore()"
|
(loadMore)="facade.loadMore()"
|
||||||
@@ -33,14 +35,28 @@ import { LanguageService } from '../../../../services/language.service';
|
|||||||
(selectionChange)="facade.toggleSelection($event.id, $event.checked)"
|
(selectionChange)="facade.toggleSelection($event.id, $event.checked)"
|
||||||
(selectAll)="facade.toggleAll($event)"
|
(selectAll)="facade.toggleAll($event)"
|
||||||
(bulkVisibility)="facade.applyBulkVisibility($event)"
|
(bulkVisibility)="facade.applyBulkVisibility($event)"
|
||||||
(bulkDelete)="facade.applyBulkDelete()"
|
(bulkDelete)="bulkDelete()"
|
||||||
(bulkDuplicate)="facade.applyBulkDuplicate()"
|
(bulkDuplicate)="facade.applyBulkDuplicate()"
|
||||||
(bulkAssignCategory)="facade.applyBulkAssignCategory($event)"
|
(bulkAssignCategory)="facade.applyBulkAssignCategory($event)"
|
||||||
(bulkAssignTags)="facade.applyBulkAssignTags($event)"
|
(bulkAssignTags)="facade.applyBulkAssignTags($event)"
|
||||||
(bulkExport)="facade.exportSelectedAsCsv()"
|
(bulkExport)="facade.exportSelectedAsCsv()"
|
||||||
(viewModeChange)="facade.setViewMode($event)"
|
(viewModeChange)="facade.setViewMode($event)"
|
||||||
(densityChange)="facade.setDensity($event)"
|
(densityChange)="facade.setDensity($event)"
|
||||||
(columnToggle)="facade.setColumnVisible($event.column, $event.visible)" />`,
|
(columnToggle)="facade.setColumnVisible($event.column, $event.visible)" />
|
||||||
|
<app-confirm-dialog
|
||||||
|
[open]="!!pendingDeleteId()"
|
||||||
|
[titleText]="'adminProducts.deleteTitle' | translate"
|
||||||
|
[message]="'adminProducts.confirmDelete' | translate"
|
||||||
|
[destructive]="true"
|
||||||
|
(confirmed)="confirmDelete()"
|
||||||
|
(cancelled)="pendingDeleteId.set(null)" />
|
||||||
|
<app-confirm-dialog
|
||||||
|
[open]="bulkDeleteConfirmOpen()"
|
||||||
|
[titleText]="'adminProducts.deleteTitle' | translate"
|
||||||
|
[message]="'adminProducts.confirmBulkDelete' | translate"
|
||||||
|
[destructive]="true"
|
||||||
|
(confirmed)="confirmBulkDelete()"
|
||||||
|
(cancelled)="bulkDeleteConfirmOpen.set(false)" />`,
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
})
|
})
|
||||||
export class AdminProductsListPageComponent {
|
export class AdminProductsListPageComponent {
|
||||||
@@ -50,6 +66,9 @@ export class AdminProductsListPageComponent {
|
|||||||
|
|
||||||
readonly healthFn = (product: Parameters<AdminProductsFacade['health']>[0]) => this.facade.health(product);
|
readonly healthFn = (product: Parameters<AdminProductsFacade['health']>[0]) => this.facade.health(product);
|
||||||
|
|
||||||
|
readonly pendingDeleteId = signal<string | null>(null);
|
||||||
|
readonly bulkDeleteConfirmOpen = signal(false);
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.facade.loadCategories();
|
this.facade.loadCategories();
|
||||||
this.facade.loadList();
|
this.facade.loadList();
|
||||||
@@ -60,6 +79,27 @@ export class AdminProductsListPageComponent {
|
|||||||
edit(id: string): void { this.facade.loadForEdit(id, 'edit'); void this.router.navigate([this.lang(), 'backoffice', 'products', id, 'edit']); }
|
edit(id: string): void { this.facade.loadForEdit(id, 'edit'); void this.router.navigate([this.lang(), 'backoffice', 'products', id, 'edit']); }
|
||||||
duplicate(id: string): void { this.facade.loadForEdit(id, 'duplicate'); void this.router.navigate([this.lang(), 'backoffice', 'products', id, 'duplicate']); }
|
duplicate(id: string): void { this.facade.loadForEdit(id, 'duplicate'); void this.router.navigate([this.lang(), 'backoffice', 'products', id, 'duplicate']); }
|
||||||
|
|
||||||
|
deleteOne(id: string): void {
|
||||||
|
this.pendingDeleteId.set(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmDelete(): void {
|
||||||
|
const id = this.pendingDeleteId();
|
||||||
|
if (id) {
|
||||||
|
this.facade.deleteOne(id);
|
||||||
|
}
|
||||||
|
this.pendingDeleteId.set(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
bulkDelete(): void {
|
||||||
|
this.bulkDeleteConfirmOpen.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmBulkDelete(): void {
|
||||||
|
this.facade.applyBulkDelete();
|
||||||
|
this.bulkDeleteConfirmOpen.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
private lang(): string {
|
private lang(): string {
|
||||||
return this.languageService.currentLanguage();
|
return this.languageService.currentLanguage();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -1302,7 +1302,8 @@ export const en: Translations = {
|
|||||||
notAvailable: 'Not available yet',
|
notAvailable: 'Not available yet',
|
||||||
changeStatus: 'Change status',
|
changeStatus: 'Change status',
|
||||||
hideSelected: 'Hide',
|
hideSelected: 'Hide',
|
||||||
archiveSelected: 'Archive',
|
deleteSelected: 'Delete',
|
||||||
|
confirmBulkDelete: 'Delete the selected reviews? This cannot be undone.',
|
||||||
reportsQueue: 'Reports queue',
|
reportsQueue: 'Reports queue',
|
||||||
reportsColumn: 'Reports',
|
reportsColumn: 'Reports',
|
||||||
reportTarget: 'Reported item',
|
reportTarget: 'Reported item',
|
||||||
@@ -1360,6 +1361,8 @@ export const en: Translations = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
adminOrders: {
|
adminOrders: {
|
||||||
|
bulkDeleteTitle: 'Delete orders',
|
||||||
|
confirmBulkDelete: 'Delete the selected orders? This cannot be undone.',
|
||||||
back: 'Back to orders',
|
back: 'Back to orders',
|
||||||
search: 'Search by order number, name, email, or phone…',
|
search: 'Search by order number, name, email, or phone…',
|
||||||
export: 'Export',
|
export: 'Export',
|
||||||
@@ -1549,6 +1552,8 @@ export const en: Translations = {
|
|||||||
saveDraft: 'Save draft',
|
saveDraft: 'Save draft',
|
||||||
publish: 'Publish',
|
publish: 'Publish',
|
||||||
confirmDelete: 'Delete this category? This cannot be undone.',
|
confirmDelete: 'Delete this category? This cannot be undone.',
|
||||||
|
confirmBulkDelete: 'Delete the selected categories? This cannot be undone.',
|
||||||
|
deleteTitle: 'Delete category',
|
||||||
confirmLeaveUnsaved: 'You have unsaved changes. Leave without saving?',
|
confirmLeaveUnsaved: 'You have unsaved changes. Leave without saving?',
|
||||||
deleteBlocked: 'This category can\'t be deleted while it has subcategories or assigned products.',
|
deleteBlocked: 'This category can\'t be deleted while it has subcategories or assigned products.',
|
||||||
emptyTitle: 'No categories yet',
|
emptyTitle: 'No categories yet',
|
||||||
@@ -1615,6 +1620,9 @@ export const en: Translations = {
|
|||||||
},
|
},
|
||||||
adminProducts: {
|
adminProducts: {
|
||||||
confirmLeaveUnsaved: 'You have unsaved changes. Leave without saving?',
|
confirmLeaveUnsaved: 'You have unsaved changes. Leave without saving?',
|
||||||
|
deleteTitle: 'Delete product',
|
||||||
|
confirmDelete: 'Delete this product? This cannot be undone.',
|
||||||
|
confirmBulkDelete: 'Delete the selected products? This cannot be undone.',
|
||||||
emptyTitle: 'No products found',
|
emptyTitle: 'No products found',
|
||||||
emptyDescription: 'Try adjusting your filters, or create a new product.',
|
emptyDescription: 'Try adjusting your filters, or create a new product.',
|
||||||
emptyGuide: 'Good products have a clear title, at least one photo, a price, and a short description — that\'s enough to publish. You can always add more detail later.',
|
emptyGuide: 'Good products have a clear title, at least one photo, a price, and a short description — that\'s enough to publish. You can always add more detail later.',
|
||||||
|
|||||||
@@ -1297,7 +1297,8 @@ export const hy: Translations = {
|
|||||||
notAvailable: 'Դեռ հասանելի չէ',
|
notAvailable: 'Դեռ հասանելի չէ',
|
||||||
changeStatus: 'Փոխել կարգավիճակը',
|
changeStatus: 'Փոխել կարգավիճակը',
|
||||||
hideSelected: 'Թաքցնել',
|
hideSelected: 'Թաքցնել',
|
||||||
archiveSelected: 'Արխիվացնել',
|
deleteSelected: 'Ջնջել',
|
||||||
|
confirmBulkDelete: 'Ջնջե՞լ ընտրված կարծիքները։ Հնարավոր չէ հետարկել։',
|
||||||
reportsQueue: 'Բողոքների հերթ',
|
reportsQueue: 'Բողոքների հերթ',
|
||||||
reportsColumn: 'Բողոքներ',
|
reportsColumn: 'Բողոքներ',
|
||||||
reportTarget: 'Բողոքարկված օբյեկտ',
|
reportTarget: 'Բողոքարկված օբյեկտ',
|
||||||
@@ -1355,6 +1356,8 @@ export const hy: Translations = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
adminOrders: {
|
adminOrders: {
|
||||||
|
bulkDeleteTitle: 'Ջնջել պատվերները',
|
||||||
|
confirmBulkDelete: 'Ջնջե՞լ ընտրված պատվերները։ Հնարավոր չէ հետարկել։',
|
||||||
back: 'Վերադառնալ պատվերներին',
|
back: 'Վերադառնալ պատվերներին',
|
||||||
search: 'Փնտրել ըստ պատվերի համարի, անվան, էլ. փոստի կամ հեռախոսի…',
|
search: 'Փնտրել ըստ պատվերի համարի, անվան, էլ. փոստի կամ հեռախոսի…',
|
||||||
export: 'Արտահանել',
|
export: 'Արտահանել',
|
||||||
@@ -1544,6 +1547,8 @@ export const hy: Translations = {
|
|||||||
saveDraft: 'Պահպանել սևագիրը',
|
saveDraft: 'Պահպանել սևագիրը',
|
||||||
publish: 'Հրապարակել',
|
publish: 'Հրապարակել',
|
||||||
confirmDelete: 'Ջնջե՞լ այս կատեգորիան։ Հնարավոր չէ հետարկել։',
|
confirmDelete: 'Ջնջե՞լ այս կատեգորիան։ Հնարավոր չէ հետարկել։',
|
||||||
|
confirmBulkDelete: 'Ջնջե՞լ ընտրված կատեգորիաները։ Հնարավոր չէ հետարկել։',
|
||||||
|
deleteTitle: 'Ջնջել կատեգորիան',
|
||||||
confirmLeaveUnsaved: 'Դուք ունեք չպահպանված փոփոխություններ։ Դո՞ւրս գալ առանց պահպանելու։',
|
confirmLeaveUnsaved: 'Դուք ունեք չպահպանված փոփոխություններ։ Դո՞ւրս գալ առանց պահպանելու։',
|
||||||
deleteBlocked: 'Այս կատեգորիան հնարավոր չէ ջնջել, քանի դեռ ունի ենթակատեգորիաներ կամ նշանակված ապրանքներ։',
|
deleteBlocked: 'Այս կատեգորիան հնարավոր չէ ջնջել, քանի դեռ ունի ենթակատեգորիաներ կամ նշանակված ապրանքներ։',
|
||||||
emptyTitle: 'Կատեգորիաներ դեռ չկան',
|
emptyTitle: 'Կատեգորիաներ դեռ չկան',
|
||||||
@@ -1610,6 +1615,9 @@ export const hy: Translations = {
|
|||||||
},
|
},
|
||||||
adminProducts: {
|
adminProducts: {
|
||||||
confirmLeaveUnsaved: 'Դուք ունեք չպահպանված փոփոխություններ։ Դո՞ւրս գալ առանց պահպանելու։',
|
confirmLeaveUnsaved: 'Դուք ունեք չպահպանված փոփոխություններ։ Դո՞ւրս գալ առանց պահպանելու։',
|
||||||
|
deleteTitle: 'Ջնջել ապրանքը',
|
||||||
|
confirmDelete: 'Ջնջե՞լ այս ապրանքը։ Հնարավոր չէ հետարկել։',
|
||||||
|
confirmBulkDelete: 'Ջնջե՞լ ընտրված ապրանքները։ Հնարավոր չէ հետարկել։',
|
||||||
emptyTitle: 'Ապրանքներ չեն գտնվել',
|
emptyTitle: 'Ապրանքներ չեն գտնվել',
|
||||||
emptyDescription: 'Փոխեք ֆիլտրերը կամ ստեղծեք նոր ապրանք։',
|
emptyDescription: 'Փոխեք ֆիլտրերը կամ ստեղծեք նոր ապրանք։',
|
||||||
emptyGuide: 'Լավ ապրանքին բավական է հստակ վերնագիր, առնվազն մեկ լուսանկար, գին և կարճ նկարագրություն՝ հրապարակելու համար։ Մնացածը կարող եք ավելացնել հետո։',
|
emptyGuide: 'Լավ ապրանքին բավական է հստակ վերնագիր, առնվազն մեկ լուսանկար, գին և կարճ նկարագրություն՝ հրապարակելու համար։ Մնացածը կարող եք ավելացնել հետո։',
|
||||||
|
|||||||
@@ -1297,7 +1297,8 @@ export const ru: Translations = {
|
|||||||
notAvailable: 'Пока недоступно',
|
notAvailable: 'Пока недоступно',
|
||||||
changeStatus: 'Изменить статус',
|
changeStatus: 'Изменить статус',
|
||||||
hideSelected: 'Скрыть',
|
hideSelected: 'Скрыть',
|
||||||
archiveSelected: 'В архив',
|
deleteSelected: 'Удалить',
|
||||||
|
confirmBulkDelete: 'Удалить выбранные отзывы? Это действие нельзя отменить.',
|
||||||
reportsQueue: 'Очередь жалоб',
|
reportsQueue: 'Очередь жалоб',
|
||||||
reportsColumn: 'Жалобы',
|
reportsColumn: 'Жалобы',
|
||||||
reportTarget: 'Объект жалобы',
|
reportTarget: 'Объект жалобы',
|
||||||
@@ -1355,6 +1356,8 @@ export const ru: Translations = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
adminOrders: {
|
adminOrders: {
|
||||||
|
bulkDeleteTitle: 'Удалить заказы',
|
||||||
|
confirmBulkDelete: 'Удалить выбранные заказы? Это действие нельзя отменить.',
|
||||||
back: 'Назад к заказам',
|
back: 'Назад к заказам',
|
||||||
search: 'Поиск по номеру заказа, имени, email или телефону…',
|
search: 'Поиск по номеру заказа, имени, email или телефону…',
|
||||||
export: 'Экспорт',
|
export: 'Экспорт',
|
||||||
@@ -1544,6 +1547,8 @@ export const ru: Translations = {
|
|||||||
saveDraft: 'Сохранить черновик',
|
saveDraft: 'Сохранить черновик',
|
||||||
publish: 'Опубликовать',
|
publish: 'Опубликовать',
|
||||||
confirmDelete: 'Удалить эту категорию? Это действие нельзя отменить.',
|
confirmDelete: 'Удалить эту категорию? Это действие нельзя отменить.',
|
||||||
|
confirmBulkDelete: 'Удалить выбранные категории? Это действие нельзя отменить.',
|
||||||
|
deleteTitle: 'Удалить категорию',
|
||||||
confirmLeaveUnsaved: 'У вас есть несохранённые изменения. Выйти без сохранения?',
|
confirmLeaveUnsaved: 'У вас есть несохранённые изменения. Выйти без сохранения?',
|
||||||
deleteBlocked: 'Эту категорию нельзя удалить, пока у неё есть подкатегории или назначенные товары.',
|
deleteBlocked: 'Эту категорию нельзя удалить, пока у неё есть подкатегории или назначенные товары.',
|
||||||
emptyTitle: 'Категорий пока нет',
|
emptyTitle: 'Категорий пока нет',
|
||||||
@@ -1610,6 +1615,9 @@ export const ru: Translations = {
|
|||||||
},
|
},
|
||||||
adminProducts: {
|
adminProducts: {
|
||||||
confirmLeaveUnsaved: 'У вас есть несохранённые изменения. Выйти без сохранения?',
|
confirmLeaveUnsaved: 'У вас есть несохранённые изменения. Выйти без сохранения?',
|
||||||
|
deleteTitle: 'Удалить товар',
|
||||||
|
confirmDelete: 'Удалить этот товар? Это действие нельзя отменить.',
|
||||||
|
confirmBulkDelete: 'Удалить выбранные товары? Это действие нельзя отменить.',
|
||||||
emptyTitle: 'Товары не найдены',
|
emptyTitle: 'Товары не найдены',
|
||||||
emptyDescription: 'Измените фильтры или создайте новый товар.',
|
emptyDescription: 'Измените фильтры или создайте новый товар.',
|
||||||
emptyGuide: 'Хорошему товару нужны понятное название, минимум одно фото, цена и короткое описание — этого достаточно для публикации. Остальное можно добавить позже.',
|
emptyGuide: 'Хорошему товару нужны понятное название, минимум одно фото, цена и короткое описание — этого достаточно для публикации. Остальное можно добавить позже.',
|
||||||
|
|||||||
@@ -1301,7 +1301,8 @@ export interface Translations {
|
|||||||
notAvailable: string;
|
notAvailable: string;
|
||||||
changeStatus: string;
|
changeStatus: string;
|
||||||
hideSelected: string;
|
hideSelected: string;
|
||||||
archiveSelected: string;
|
deleteSelected: string;
|
||||||
|
confirmBulkDelete: string;
|
||||||
reportsQueue: string;
|
reportsQueue: string;
|
||||||
reportsColumn: string;
|
reportsColumn: string;
|
||||||
reportTarget: string;
|
reportTarget: string;
|
||||||
@@ -1359,6 +1360,8 @@ export interface Translations {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
adminOrders: {
|
adminOrders: {
|
||||||
|
bulkDeleteTitle: string;
|
||||||
|
confirmBulkDelete: string;
|
||||||
back: string;
|
back: string;
|
||||||
search: string;
|
search: string;
|
||||||
export: string;
|
export: string;
|
||||||
@@ -1557,6 +1560,8 @@ export interface Translations {
|
|||||||
saveDraft: string;
|
saveDraft: string;
|
||||||
publish: string;
|
publish: string;
|
||||||
confirmDelete: string;
|
confirmDelete: string;
|
||||||
|
confirmBulkDelete: string;
|
||||||
|
deleteTitle: string;
|
||||||
confirmLeaveUnsaved: string;
|
confirmLeaveUnsaved: string;
|
||||||
deleteBlocked: string;
|
deleteBlocked: string;
|
||||||
emptyTitle: string;
|
emptyTitle: string;
|
||||||
@@ -1623,6 +1628,9 @@ export interface Translations {
|
|||||||
};
|
};
|
||||||
adminProducts: {
|
adminProducts: {
|
||||||
confirmLeaveUnsaved: string;
|
confirmLeaveUnsaved: string;
|
||||||
|
deleteTitle: string;
|
||||||
|
confirmDelete: string;
|
||||||
|
confirmBulkDelete: string;
|
||||||
emptyTitle: string;
|
emptyTitle: string;
|
||||||
emptyDescription: string;
|
emptyDescription: string;
|
||||||
emptyGuide: string;
|
emptyGuide: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user