diff --git a/docs/ADMIN.md b/docs/ADMIN.md index 2d7ac87..d3328b8 100644 --- a/docs/ADMIN.md +++ b/docs/ADMIN.md @@ -416,6 +416,36 @@ routes at all, this is a net-new admin section. - **Export**: CSV of the sales series (client-side `Blob` download, same pattern as Orders/Transactions). +## Sprint 28 - Marketplace Polish (scoped to admin/*) + +Scoped down (user decision) from a full marketplace-wide audit to the 8 +admin features built in Sprints 20-27, since those were the newest and +least-reviewed surface. Not a rewrite - targeted fixes only: + +- **Accessibility**: every bare ` + + - - - - {{ 'adminMonitoring.category' | translate }} - {{ 'adminMonitoring.level' | translate }} - {{ 'adminMonitoring.message' | translate }} - {{ 'adminUsers.name' | translate }} - {{ 'adminOrders.createdAt' | translate }} - - - - @for (event of facade.events(); track event.id) { - - {{ ('adminMonitoring.category.' + event.category) | translate }} - {{ event.level }} - {{ event.message }} - {{ event.actor }} - {{ event.timestamp | date:'short' }} - + @if (facade.loading()) { +
+ @for (i of [1,2,3]; track i) { + } - - +
+ } @else if (facade.events().length === 0) { + + } @else { + + + + {{ 'adminMonitoring.category' | translate }} + {{ 'adminMonitoring.level' | translate }} + {{ 'adminMonitoring.message' | translate }} + {{ 'adminUsers.name' | translate }} + {{ 'adminOrders.createdAt' | translate }} + + + + @for (event of facade.events(); track event.id) { + + {{ ('adminMonitoring.category.' + event.category) | translate }} + {{ event.level }} + {{ event.message }} + {{ event.actor }} + {{ event.timestamp | date:'short' }} + + } + + + } diff --git a/src/app/features/admin/monitoring/pages/admin-monitoring-page.component.scss b/src/app/features/admin/monitoring/pages/admin-monitoring-page.component.scss index 81fc390..70d3277 100644 --- a/src/app/features/admin/monitoring/pages/admin-monitoring-page.component.scss +++ b/src/app/features/admin/monitoring/pages/admin-monitoring-page.component.scss @@ -7,3 +7,4 @@ .filters { display: flex; flex-wrap: wrap; gap: 10px; } select { min-height: 40px; padding: 0 10px; border: 1px solid var(--border-color, #d3dad9); border-radius: 10px; } @media (max-width: 700px) { .queue-grid { grid-template-columns: 1fr; } .filters { flex-direction: column; align-items: stretch; } } +.skeleton-rows { display: grid; gap: 8px; } diff --git a/src/app/features/admin/monitoring/pages/admin-monitoring-page.component.ts b/src/app/features/admin/monitoring/pages/admin-monitoring-page.component.ts index a21b6d8..747980d 100644 --- a/src/app/features/admin/monitoring/pages/admin-monitoring-page.component.ts +++ b/src/app/features/admin/monitoring/pages/admin-monitoring-page.component.ts @@ -7,11 +7,13 @@ import { TranslatePipe } from '../../../../i18n/translate.pipe'; import { InputComponent } from '../../../../shared/ui/input/input.component'; import { BadgeComponent } from '../../../../shared/ui/badge/badge.component'; import { TableComponent } from '../../../../shared/ui/table/table.component'; +import { SkeletonComponent } from '../../../../shared/ui/skeleton/skeleton.component'; +import { EmptyStateComponent } from '../../../../shared/ui/empty-state/empty-state.component'; @Component({ selector: 'app-admin-monitoring-page', standalone: true, - imports: [CommonModule, FormsModule, TranslatePipe, InputComponent, BadgeComponent, TableComponent], + imports: [CommonModule, FormsModule, TranslatePipe, InputComponent, BadgeComponent, TableComponent, SkeletonComponent, EmptyStateComponent], templateUrl: './admin-monitoring-page.component.html', styleUrls: ['./admin-monitoring-page.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush diff --git a/src/app/features/admin/orders/pages/admin-order-detail-page.component.html b/src/app/features/admin/orders/pages/admin-order-detail-page.component.html index a2fe8e9..bb22639 100644 --- a/src/app/features/admin/orders/pages/admin-order-detail-page.component.html +++ b/src/app/features/admin/orders/pages/admin-order-detail-page.component.html @@ -28,7 +28,7 @@

{{ 'adminOrders.changeStatus' | translate }}

- @for (status of statuses; track status) { } diff --git a/src/app/features/admin/orders/pages/admin-orders-list-page.component.html b/src/app/features/admin/orders/pages/admin-orders-list-page.component.html index 1cdd0ba..3fddf31 100644 --- a/src/app/features/admin/orders/pages/admin-orders-list-page.component.html +++ b/src/app/features/admin/orders/pages/admin-orders-list-page.component.html @@ -2,7 +2,7 @@
- @for (status of statuses; track status) { } @@ -11,7 +11,11 @@ {{ 'adminOrders.export' | translate }}
- @if (!facade.loading() && facade.orders().length === 0) { + @if (facade.loading()) { +
+ @for (i of [1,2,3,4]; track i) { } +
+ } @else if (facade.orders().length === 0) { } @else { diff --git a/src/app/features/admin/orders/pages/admin-orders-list-page.component.scss b/src/app/features/admin/orders/pages/admin-orders-list-page.component.scss index 106689c..73a2c80 100644 --- a/src/app/features/admin/orders/pages/admin-orders-list-page.component.scss +++ b/src/app/features/admin/orders/pages/admin-orders-list-page.component.scss @@ -3,4 +3,5 @@ .filters { display: flex; flex-wrap: wrap; gap: 10px; flex: 1; } select { min-height: 40px; padding: 0 10px; border: 1px solid var(--border-color, #d3dad9); border-radius: 10px; } .pager { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 10px; } +.skeleton-rows { display: grid; gap: 8px; } @media (max-width: 640px) { .filters { flex-direction: column; align-items: stretch; } } diff --git a/src/app/features/admin/orders/pages/admin-orders-list-page.component.ts b/src/app/features/admin/orders/pages/admin-orders-list-page.component.ts index 09b3540..2f47ae0 100644 --- a/src/app/features/admin/orders/pages/admin-orders-list-page.component.ts +++ b/src/app/features/admin/orders/pages/admin-orders-list-page.component.ts @@ -11,11 +11,12 @@ import { BadgeComponent } from '../../../../shared/ui/badge/badge.component'; import { TableComponent } from '../../../../shared/ui/table/table.component'; import { PaginationComponent } from '../../../../shared/ui/pagination/pagination.component'; import { EmptyStateComponent } from '../../../../shared/ui/empty-state/empty-state.component'; +import { SkeletonComponent } from '../../../../shared/ui/skeleton/skeleton.component'; @Component({ selector: 'app-admin-orders-list-page', standalone: true, - imports: [CommonModule, FormsModule, TranslatePipe, ButtonComponent, InputComponent, BadgeComponent, TableComponent, PaginationComponent, EmptyStateComponent], + imports: [CommonModule, FormsModule, TranslatePipe, ButtonComponent, InputComponent, BadgeComponent, TableComponent, PaginationComponent, EmptyStateComponent, SkeletonComponent], templateUrl: './admin-orders-list-page.component.html', styleUrls: ['./admin-orders-list-page.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush diff --git a/src/app/features/admin/products/components/admin-products-list.component.html b/src/app/features/admin/products/components/admin-products-list.component.html index 14af360..f51fb7c 100644 --- a/src/app/features/admin/products/components/admin-products-list.component.html +++ b/src/app/features/admin/products/components/admin-products-list.component.html @@ -2,24 +2,24 @@
- @for (category of categories; track category.id) { } - - - @@ -40,56 +40,66 @@
} - - - - - {{ 'adminProducts.name' | translate }} - {{ 'backoffice.sku' | translate }} - {{ 'adminProducts.brand' | translate }} - {{ 'backoffice.price' | translate }} - {{ 'backoffice.status' | translate }} - {{ 'adminProducts.visibility' | translate }} - {{ 'adminProducts.actions' | translate }} - - - - @for (product of products; track product.id) { + @if (loading) { +
+ @for (i of [1,2,3,4,5]; track i) { + + } +
+ } @else if (products.length === 0) { + + } @else { + + - - {{ product.name }} - {{ product.sku }} - {{ product.brand }} - {{ product.price }} {{ product.currency }} - {{ product.stockStatus }} - - - {{ product.visible ? ('adminProducts.visible' | translate) : ('adminProducts.hidden' | translate) }} - - - - {{ 'adminProducts.edit' | translate }} - {{ 'adminProducts.duplicate' | translate }} - @if (product.archived) { - {{ 'adminCategories.restore' | translate }} - } @else { - {{ 'adminProducts.archive' | translate }} - } - {{ 'adminProducts.delete' | translate }} - + + {{ 'adminProducts.name' | translate }} + {{ 'backoffice.sku' | translate }} + {{ 'adminProducts.brand' | translate }} + {{ 'backoffice.price' | translate }} + {{ 'backoffice.status' | translate }} + {{ 'adminProducts.visibility' | translate }} + {{ 'adminProducts.actions' | translate }} - } - - + + + @for (product of products; track product.id) { + + + {{ product.name }} + {{ product.sku }} + {{ product.brand }} + {{ product.price }} {{ product.currency }} + {{ product.stockStatus }} + + + {{ product.visible ? ('adminProducts.visible' | translate) : ('adminProducts.hidden' | translate) }} + + + + {{ 'adminProducts.edit' | translate }} + {{ 'adminProducts.duplicate' | translate }} + @if (product.archived) { + {{ 'adminCategories.restore' | translate }} + } @else { + {{ 'adminProducts.archive' | translate }} + } + {{ 'adminProducts.delete' | translate }} + + + } + +
-
- {{ products.length }} / {{ total }} {{ 'adminProducts.items' | translate }} - @if (infiniteScroll) { - @if (products.length < total) { - {{ 'adminProducts.loadMore' | translate }} +
+ {{ products.length }} / {{ total }} {{ 'adminProducts.items' | translate }} + @if (infiniteScroll) { + @if (products.length < total) { + {{ 'adminProducts.loadMore' | translate }} + } + } @else { + } - } @else { - - } -
+
+ } diff --git a/src/app/features/admin/products/components/admin-products-list.component.scss b/src/app/features/admin/products/components/admin-products-list.component.scss index ed0b292..2c3b6c9 100644 --- a/src/app/features/admin/products/components/admin-products-list.component.scss +++ b/src/app/features/admin/products/components/admin-products-list.component.scss @@ -6,3 +6,4 @@ select { min-height: 40px; padding: 0 10px; border: 1px solid var(--border-color .pager { justify-content: space-between; flex-wrap: wrap; } @media (max-width: 960px) { .filters { grid-template-columns: repeat(2, minmax(140px, 1fr)); } } @media (max-width: 640px) { .filters { grid-template-columns: 1fr; } .actions { flex-direction: column; align-items: stretch; } } +.skeleton-rows { display: grid; gap: 8px; } diff --git a/src/app/features/admin/products/components/admin-products-list.component.ts b/src/app/features/admin/products/components/admin-products-list.component.ts index 29e425f..fcd7086 100644 --- a/src/app/features/admin/products/components/admin-products-list.component.ts +++ b/src/app/features/admin/products/components/admin-products-list.component.ts @@ -7,11 +7,13 @@ import { InputComponent } from '../../../../shared/ui/input/input.component'; import { BadgeComponent } from '../../../../shared/ui/badge/badge.component'; import { TableComponent } from '../../../../shared/ui/table/table.component'; import { PaginationComponent } from '../../../../shared/ui/pagination/pagination.component'; +import { SkeletonComponent } from '../../../../shared/ui/skeleton/skeleton.component'; +import { EmptyStateComponent } from '../../../../shared/ui/empty-state/empty-state.component'; @Component({ selector: 'app-admin-products-list', standalone: true, - imports: [FormsModule, TranslatePipe, ButtonComponent, InputComponent, BadgeComponent, TableComponent, PaginationComponent], + imports: [FormsModule, TranslatePipe, ButtonComponent, InputComponent, BadgeComponent, TableComponent, PaginationComponent, SkeletonComponent, EmptyStateComponent], templateUrl: './admin-products-list.component.html', styleUrls: ['./admin-products-list.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush diff --git a/src/app/features/admin/transactions/pages/admin-transactions-list-page.component.html b/src/app/features/admin/transactions/pages/admin-transactions-list-page.component.html index 31f0481..1a8b2b7 100644 --- a/src/app/features/admin/transactions/pages/admin-transactions-list-page.component.html +++ b/src/app/features/admin/transactions/pages/admin-transactions-list-page.component.html @@ -2,12 +2,12 @@
- @for (status of statuses; track status) { } - @for (type of types; track type) { } @@ -16,7 +16,11 @@ {{ 'adminOrders.export' | translate }}
- @if (!facade.loading() && facade.transactions().length === 0) { + @if (facade.loading()) { +
+ @for (i of [1,2,3,4]; track i) { } +
+ } @else if (facade.transactions().length === 0) { } @else { diff --git a/src/app/features/admin/transactions/pages/admin-transactions-list-page.component.scss b/src/app/features/admin/transactions/pages/admin-transactions-list-page.component.scss index 418b67f..ad8ba00 100644 --- a/src/app/features/admin/transactions/pages/admin-transactions-list-page.component.scss +++ b/src/app/features/admin/transactions/pages/admin-transactions-list-page.component.scss @@ -4,4 +4,5 @@ select { min-height: 40px; padding: 0 10px; border: 1px solid var(--border-color, #d3dad9); border-radius: 10px; } .pager { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 10px; } .actions { display: flex; gap: 8px; flex-wrap: wrap; } +.skeleton-rows { display: grid; gap: 8px; } @media (max-width: 640px) { .filters { flex-direction: column; align-items: stretch; } } diff --git a/src/app/features/admin/transactions/pages/admin-transactions-list-page.component.ts b/src/app/features/admin/transactions/pages/admin-transactions-list-page.component.ts index 16696a5..d7861ab 100644 --- a/src/app/features/admin/transactions/pages/admin-transactions-list-page.component.ts +++ b/src/app/features/admin/transactions/pages/admin-transactions-list-page.component.ts @@ -11,11 +11,12 @@ import { TableComponent } from '../../../../shared/ui/table/table.component'; import { PaginationComponent } from '../../../../shared/ui/pagination/pagination.component'; import { EmptyStateComponent } from '../../../../shared/ui/empty-state/empty-state.component'; import { DialogComponent } from '../../../../shared/ui/dialog/dialog.component'; +import { SkeletonComponent } from '../../../../shared/ui/skeleton/skeleton.component'; @Component({ selector: 'app-admin-transactions-list-page', standalone: true, - imports: [CommonModule, FormsModule, TranslatePipe, ButtonComponent, InputComponent, BadgeComponent, TableComponent, PaginationComponent, EmptyStateComponent, DialogComponent], + imports: [CommonModule, FormsModule, TranslatePipe, ButtonComponent, InputComponent, BadgeComponent, TableComponent, PaginationComponent, EmptyStateComponent, DialogComponent, SkeletonComponent], templateUrl: './admin-transactions-list-page.component.html', styleUrls: ['./admin-transactions-list-page.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush diff --git a/src/app/features/admin/users/pages/admin-users-page.component.html b/src/app/features/admin/users/pages/admin-users-page.component.html index 69d3525..2ceb180 100644 --- a/src/app/features/admin/users/pages/admin-users-page.component.html +++ b/src/app/features/admin/users/pages/admin-users-page.component.html @@ -1,6 +1,13 @@

{{ 'adminUsers.title' | translate }}

+ @if (facade.loading()) { +
+ @for (i of [1,2,3]; track i) { } +
+ } @else if (facade.users().length === 0) { + + } @else { @@ -18,7 +25,7 @@ {{ user.name }}
{{ user.telegramUsername }} {{ ('adminUsers.scopeValue.' + user.scope) | translate }} - @for (role of facade.roles(); track role.id) { } @@ -35,6 +42,7 @@ }
+ }
@@ -42,12 +50,12 @@

{{ 'adminUsers.passwordlessHint' | translate }}

- @for (role of facade.roles(); track role.id) { } - @@ -79,6 +87,8 @@ } + } @else { + }
diff --git a/src/app/features/admin/users/pages/admin-users-page.component.scss b/src/app/features/admin/users/pages/admin-users-page.component.scss index 72ab668..c39fd01 100644 --- a/src/app/features/admin/users/pages/admin-users-page.component.scss +++ b/src/app/features/admin/users/pages/admin-users-page.component.scss @@ -5,4 +5,5 @@ .invite-form { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; } select { min-height: 40px; padding: 0 10px; border: 1px solid var(--border-color, #d3dad9); border-radius: 10px; } .actions { display: flex; gap: 8px; flex-wrap: wrap; } +.skeleton-rows { display: grid; gap: 8px; } @media (max-width: 640px) { .invite-form { flex-direction: column; align-items: stretch; } } diff --git a/src/app/features/admin/users/pages/admin-users-page.component.ts b/src/app/features/admin/users/pages/admin-users-page.component.ts index 4459f22..7018e24 100644 --- a/src/app/features/admin/users/pages/admin-users-page.component.ts +++ b/src/app/features/admin/users/pages/admin-users-page.component.ts @@ -10,11 +10,13 @@ import { InputComponent } from '../../../../shared/ui/input/input.component'; import { BadgeComponent } from '../../../../shared/ui/badge/badge.component'; import { TableComponent } from '../../../../shared/ui/table/table.component'; import { DialogComponent } from '../../../../shared/ui/dialog/dialog.component'; +import { SkeletonComponent } from '../../../../shared/ui/skeleton/skeleton.component'; +import { EmptyStateComponent } from '../../../../shared/ui/empty-state/empty-state.component'; @Component({ selector: 'app-admin-users-page', standalone: true, - imports: [CommonModule, FormsModule, TranslatePipe, ButtonComponent, InputComponent, BadgeComponent, TableComponent, DialogComponent], + imports: [CommonModule, FormsModule, TranslatePipe, ButtonComponent, InputComponent, BadgeComponent, TableComponent, DialogComponent, SkeletonComponent, EmptyStateComponent], templateUrl: './admin-users-page.component.html', styleUrls: ['./admin-users-page.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush