fix(icons): migrate admin dashboard to Lucide
Replace every PrimeIcons pi-* class (static and data-driven) in the admin dashboard area with app-icon: dashboard-card, dashboard-shortcut- card, dashboard-status-row, dashboard-timeline, and the icon data in admin-dashboard.facade.ts / admin-dashboard-page.component.ts. Icon fields on AdminDashboardQuickAction/Shortcut/DashboardTimelineEntry are now typed AppIconName instead of string, so a typo or unmapped icon name is a compile error instead of a silently blank icon. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
<app-card class="dashboard-card" padding="lg">
|
||||
@if (titleKey(); as title) {
|
||||
<header class="dashboard-card__header">
|
||||
@if (icon(); as iconClass) {
|
||||
<span class="pi {{ iconClass }} dashboard-card__icon" aria-hidden="true"></span>
|
||||
@if (icon(); as iconName) {
|
||||
<app-icon [name]="iconName" [size]="18" class="dashboard-card__icon" />
|
||||
}
|
||||
<h3 class="dashboard-card__title">{{ title | translate }}</h3>
|
||||
<div class="dashboard-card__header-actions">
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { ChangeDetectionStrategy, Component, input } from '@angular/core';
|
||||
import { TranslatePipe } from '../../../../i18n/translate.pipe';
|
||||
import { CardComponent } from '../../../../shared/ui/card/card.component';
|
||||
import { IconComponent } from '../../../../shared/ui/icon/icon.component';
|
||||
import { AppIconName } from '../../../../shared/ui/icon/icon-registry';
|
||||
|
||||
@Component({
|
||||
selector: 'app-dashboard-card',
|
||||
standalone: true,
|
||||
imports: [TranslatePipe, CardComponent],
|
||||
imports: [TranslatePipe, CardComponent, IconComponent],
|
||||
templateUrl: './dashboard-card.component.html',
|
||||
styleUrl: './dashboard-card.component.scss',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
@@ -15,7 +17,7 @@ import { CardComponent } from '../../../../shared/ui/card/card.component';
|
||||
})
|
||||
export class DashboardCardComponent {
|
||||
readonly titleKey = input<string | null>(null);
|
||||
readonly icon = input<string | null>(null);
|
||||
readonly icon = input<AppIconName | null>(null);
|
||||
/** 12-column grid span at desktop width, e.g. 'span 12' or 'span 4'. */
|
||||
readonly span = input<string>('span 4');
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@if (comingSoon()) {
|
||||
<button type="button" class="dashboard-shortcut-card dashboard-shortcut-card--disabled" disabled>
|
||||
<span class="pi {{ icon() }} dashboard-shortcut-card__icon" aria-hidden="true"></span>
|
||||
<app-icon [name]="icon()" [size]="22" class="dashboard-shortcut-card__icon" />
|
||||
<span class="dashboard-shortcut-card__title">{{ labelKey() | translate }}</span>
|
||||
@if (descriptionKey(); as description) {
|
||||
<span class="dashboard-shortcut-card__description">{{ description | translate }}</span>
|
||||
@@ -9,7 +9,7 @@
|
||||
</button>
|
||||
} @else {
|
||||
<a class="dashboard-shortcut-card" [routerLink]="route()">
|
||||
<span class="pi {{ icon() }} dashboard-shortcut-card__icon" aria-hidden="true"></span>
|
||||
<app-icon [name]="icon()" [size]="22" class="dashboard-shortcut-card__icon" />
|
||||
<span class="dashboard-shortcut-card__title">{{ labelKey() | translate }}</span>
|
||||
@if (descriptionKey(); as description) {
|
||||
<span class="dashboard-shortcut-card__description">{{ description | translate }}</span>
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
import { ChangeDetectionStrategy, Component, input } from '@angular/core';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import { TranslatePipe } from '../../../../i18n/translate.pipe';
|
||||
import { IconComponent } from '../../../../shared/ui/icon/icon.component';
|
||||
import { AppIconName } from '../../../../shared/ui/icon/icon-registry';
|
||||
|
||||
@Component({
|
||||
selector: 'app-dashboard-shortcut-card',
|
||||
standalone: true,
|
||||
imports: [RouterLink, TranslatePipe],
|
||||
imports: [RouterLink, TranslatePipe, IconComponent],
|
||||
templateUrl: './dashboard-shortcut-card.component.html',
|
||||
styleUrl: './dashboard-shortcut-card.component.scss',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class DashboardShortcutCardComponent {
|
||||
readonly icon = input.required<string>();
|
||||
readonly icon = input.required<AppIconName>();
|
||||
readonly labelKey = input.required<string>();
|
||||
readonly descriptionKey = input<string | null>(null);
|
||||
readonly route = input<string[] | null>(null);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
</div>
|
||||
} @else {
|
||||
<div class="dashboard-status-row dashboard-status-row--{{ status() }}">
|
||||
<span class="pi {{ icon() }} dashboard-status-row__icon" aria-hidden="true"></span>
|
||||
<app-icon [name]="icon()" [size]="16" class="dashboard-status-row__icon" />
|
||||
<span class="dashboard-status-row__label">{{ labelKey() | translate }}</span>
|
||||
@if (displayValue() !== null) {
|
||||
<span class="dashboard-status-row__value">{{ displayValue() }}</span>
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
|
||||
import { TranslatePipe } from '../../../../i18n/translate.pipe';
|
||||
import { SkeletonComponent } from '../../../../shared/ui/skeleton/skeleton.component';
|
||||
import { IconComponent } from '../../../../shared/ui/icon/icon.component';
|
||||
import { AppIconName } from '../../../../shared/ui/icon/icon-registry';
|
||||
import { AdminDashboardHealthStatus } from '../models/admin-dashboard.model';
|
||||
|
||||
const STATUS_ICON: Record<AdminDashboardHealthStatus, string> = {
|
||||
healthy: 'pi-check-circle',
|
||||
attention: 'pi-exclamation-triangle',
|
||||
unhealthy: 'pi-times-circle',
|
||||
unknown: 'pi-question-circle',
|
||||
loading: 'pi-spin pi-spinner',
|
||||
const STATUS_ICON: Record<AdminDashboardHealthStatus, AppIconName> = {
|
||||
healthy: 'checkCircle',
|
||||
attention: 'warning',
|
||||
unhealthy: 'xCircle',
|
||||
unknown: 'help',
|
||||
loading: 'spinner',
|
||||
};
|
||||
|
||||
const STATUS_TEXT_KEY: Record<AdminDashboardHealthStatus, string> = {
|
||||
@@ -22,7 +24,7 @@ const STATUS_TEXT_KEY: Record<AdminDashboardHealthStatus, string> = {
|
||||
@Component({
|
||||
selector: 'app-dashboard-status-row',
|
||||
standalone: true,
|
||||
imports: [TranslatePipe, SkeletonComponent],
|
||||
imports: [TranslatePipe, SkeletonComponent, IconComponent],
|
||||
templateUrl: './dashboard-status-row.component.html',
|
||||
styleUrl: './dashboard-status-row.component.scss',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<ol class="dashboard-timeline">
|
||||
@for (entry of entries(); track entry.id) {
|
||||
<li class="dashboard-timeline__item">
|
||||
<span class="pi {{ entry.icon }} dashboard-timeline__icon" aria-hidden="true"></span>
|
||||
<app-icon [name]="entry.icon" [size]="18" class="dashboard-timeline__icon" />
|
||||
<span class="dashboard-timeline__label">{{ entry.labelKey | translate }}</span>
|
||||
<time class="dashboard-timeline__time">{{ entry.timeText }}</time>
|
||||
</li>
|
||||
|
||||
@@ -2,12 +2,14 @@ import { ChangeDetectionStrategy, Component, input } from '@angular/core';
|
||||
import { TranslatePipe } from '../../../../i18n/translate.pipe';
|
||||
import { SkeletonComponent } from '../../../../shared/ui/skeleton/skeleton.component';
|
||||
import { EmptyStateComponent } from '../../../../shared/ui/empty-state/empty-state.component';
|
||||
import { IconComponent } from '../../../../shared/ui/icon/icon.component';
|
||||
import { AppIconName } from '../../../../shared/ui/icon/icon-registry';
|
||||
|
||||
export interface DashboardTimelineEntry {
|
||||
id: string;
|
||||
labelKey: string;
|
||||
timeText: string;
|
||||
icon: string;
|
||||
icon: AppIconName;
|
||||
}
|
||||
|
||||
export type DashboardTimelineStatus = 'loading' | 'ready' | 'unavailable' | 'error';
|
||||
@@ -15,7 +17,7 @@ export type DashboardTimelineStatus = 'loading' | 'ready' | 'unavailable' | 'err
|
||||
@Component({
|
||||
selector: 'app-dashboard-timeline',
|
||||
standalone: true,
|
||||
imports: [TranslatePipe, SkeletonComponent, EmptyStateComponent],
|
||||
imports: [TranslatePipe, SkeletonComponent, EmptyStateComponent, IconComponent],
|
||||
templateUrl: './dashboard-timeline.component.html',
|
||||
styleUrl: './dashboard-timeline.component.scss',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
|
||||
@@ -16,24 +16,24 @@ import {
|
||||
} from '../models/admin-dashboard.model';
|
||||
|
||||
const QUICK_ACTIONS: AdminDashboardQuickAction[] = [
|
||||
{ id: 'add-product', icon: 'pi-plus-circle', labelKey: 'dashboard.actionAddProduct', descriptionKey: 'dashboard.actionAddProductDescription', route: ['backoffice', 'products', 'create'] },
|
||||
{ id: 'create-category', icon: 'pi-sitemap', labelKey: 'dashboard.actionCreateCategory', descriptionKey: 'dashboard.actionCreateCategoryDescription', route: ['backoffice', 'categories', 'create'] },
|
||||
{ id: 'edit-project', icon: 'pi-th-large', labelKey: 'dashboard.actionEditProject', descriptionKey: 'dashboard.actionEditProjectDescription', route: ['edit'] },
|
||||
{ id: 'edit-homepage', icon: 'pi-home', labelKey: 'dashboard.actionEditHomepage', descriptionKey: 'dashboard.actionEditHomepageDescription', route: ['edit', 'homepage'] },
|
||||
{ id: 'media-library', icon: 'pi-images', labelKey: 'dashboard.actionMediaLibrary', descriptionKey: 'dashboard.actionMediaLibraryDescription', route: ['backoffice', 'media'] },
|
||||
{ id: 'orders', icon: 'pi-shopping-cart', labelKey: 'dashboard.actionOrders', descriptionKey: 'dashboard.actionOrdersDescription', route: ['backoffice', 'orders'] },
|
||||
{ id: 'add-product', icon: 'plusCircle', labelKey: 'dashboard.actionAddProduct', descriptionKey: 'dashboard.actionAddProductDescription', route: ['backoffice', 'products', 'create'] },
|
||||
{ id: 'create-category', icon: 'network', labelKey: 'dashboard.actionCreateCategory', descriptionKey: 'dashboard.actionCreateCategoryDescription', route: ['backoffice', 'categories', 'create'] },
|
||||
{ id: 'edit-project', icon: 'layoutGrid', labelKey: 'dashboard.actionEditProject', descriptionKey: 'dashboard.actionEditProjectDescription', route: ['edit'] },
|
||||
{ id: 'edit-homepage', icon: 'home', labelKey: 'dashboard.actionEditHomepage', descriptionKey: 'dashboard.actionEditHomepageDescription', route: ['edit', 'homepage'] },
|
||||
{ id: 'media-library', icon: 'images', labelKey: 'dashboard.actionMediaLibrary', descriptionKey: 'dashboard.actionMediaLibraryDescription', route: ['backoffice', 'media'] },
|
||||
{ id: 'orders', icon: 'cart', labelKey: 'dashboard.actionOrders', descriptionKey: 'dashboard.actionOrdersDescription', route: ['backoffice', 'orders'] },
|
||||
];
|
||||
|
||||
const SHORTCUTS: AdminDashboardShortcut[] = [
|
||||
{ id: 'products', icon: 'pi-box', labelKey: 'dashboard.actionProducts', route: ['backoffice', 'products'] },
|
||||
{ id: 'categories', icon: 'pi-tags', labelKey: 'dashboard.actionCategories', route: ['backoffice', 'categories'] },
|
||||
{ id: 'marketplace-builder', icon: 'pi-sitemap', labelKey: 'dashboard.shortcutMarketplaceBuilder', route: ['edit'] },
|
||||
{ id: 'static-pages', icon: 'pi-file-edit', labelKey: 'dashboard.actionStaticPages', route: ['backoffice', 'static-pages'] },
|
||||
{ id: 'orders', icon: 'pi-shopping-cart', labelKey: 'dashboard.actionOrders', route: ['backoffice', 'orders'] },
|
||||
{ id: 'users', icon: 'pi-users', labelKey: 'dashboard.actionUsers', route: ['backoffice', 'users'] },
|
||||
{ id: 'settings', icon: 'pi-cog', labelKey: 'dashboard.shortcutSettings', route: [], comingSoon: true },
|
||||
{ id: 'media-library', icon: 'pi-images', labelKey: 'dashboard.actionMediaLibrary', route: ['backoffice', 'media'] },
|
||||
{ id: 'content', icon: 'pi-align-left', labelKey: 'dashboard.shortcutContent', route: ['edit', 'static-pages'] },
|
||||
{ id: 'products', icon: 'package', labelKey: 'dashboard.actionProducts', route: ['backoffice', 'products'] },
|
||||
{ id: 'categories', icon: 'tags', labelKey: 'dashboard.actionCategories', route: ['backoffice', 'categories'] },
|
||||
{ id: 'marketplace-builder', icon: 'network', labelKey: 'dashboard.shortcutMarketplaceBuilder', route: ['edit'] },
|
||||
{ id: 'static-pages', icon: 'edit', labelKey: 'dashboard.actionStaticPages', route: ['backoffice', 'static-pages'] },
|
||||
{ id: 'orders', icon: 'cart', labelKey: 'dashboard.actionOrders', route: ['backoffice', 'orders'] },
|
||||
{ id: 'users', icon: 'users', labelKey: 'dashboard.actionUsers', route: ['backoffice', 'users'] },
|
||||
{ id: 'settings', icon: 'settings', labelKey: 'dashboard.shortcutSettings', route: [], comingSoon: true },
|
||||
{ id: 'media-library', icon: 'images', labelKey: 'dashboard.actionMediaLibrary', route: ['backoffice', 'media'] },
|
||||
{ id: 'content', icon: 'alignLeft', labelKey: 'dashboard.shortcutContent', route: ['edit', 'static-pages'] },
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { AppIconName } from '../../../../shared/ui/icon/icon-registry';
|
||||
|
||||
export type AdminDashboardCardStatus = 'loading' | 'ready' | 'empty' | 'error' | 'pending-backend';
|
||||
|
||||
export interface AdminDashboardCardState<T> {
|
||||
@@ -28,7 +30,7 @@ export type AdminDashboardQuickActionId =
|
||||
|
||||
export interface AdminDashboardQuickAction {
|
||||
id: AdminDashboardQuickActionId;
|
||||
icon: string;
|
||||
icon: AppIconName;
|
||||
labelKey: string;
|
||||
descriptionKey: string;
|
||||
route: string[];
|
||||
@@ -73,7 +75,7 @@ export interface AdminDashboardDraftField {
|
||||
|
||||
export interface AdminDashboardShortcut {
|
||||
id: string;
|
||||
icon: string;
|
||||
icon: AppIconName;
|
||||
labelKey: string;
|
||||
route: string[];
|
||||
comingSoon?: boolean;
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
</app-dashboard-section>
|
||||
|
||||
<!-- Draft Status -->
|
||||
<app-dashboard-card [titleKey]="'dashboard.draftStatusTitle'" [icon]="'pi-file-edit'" [span]="'span 4'">
|
||||
<app-dashboard-card [titleKey]="'dashboard.draftStatusTitle'" [icon]="'edit'" [span]="'span 4'">
|
||||
@if (facade.dirty()) {
|
||||
<p class="dashboard-draft__lead">{{ 'dashboard.draftStatusHasChanges' | translate }}</p>
|
||||
@if (facade.modifiedFieldLabelKeys().length > 0) {
|
||||
@@ -66,21 +66,21 @@
|
||||
</div>
|
||||
} @else {
|
||||
<p class="dashboard-draft__all-clear">
|
||||
<span class="pi pi-check-circle" aria-hidden="true"></span>
|
||||
<app-icon name="checkCircle" [size]="18" />
|
||||
{{ 'dashboard.draftStatusAllPublished' | translate }}
|
||||
</p>
|
||||
}
|
||||
</app-dashboard-card>
|
||||
|
||||
<!-- Marketplace Health -->
|
||||
<app-dashboard-card [titleKey]="'dashboard.healthTitle'" [icon]="'pi-heart'" [span]="'span 4'">
|
||||
<app-dashboard-card [titleKey]="'dashboard.healthTitle'" [icon]="'heart'" [span]="'span 4'">
|
||||
@for (check of facade.homeHealthChecks(); track check.code) {
|
||||
<app-dashboard-status-row [labelKey]="check.labelKey" [status]="check.status" [displayValue]="check.displayValue ?? null" />
|
||||
}
|
||||
</app-dashboard-card>
|
||||
|
||||
<!-- Recent Activity -->
|
||||
<app-dashboard-card [titleKey]="'dashboard.activityTitle'" [icon]="'pi-clock'" [span]="'span 4'">
|
||||
<app-dashboard-card [titleKey]="'dashboard.activityTitle'" [icon]="'clock'" [span]="'span 4'">
|
||||
<app-dashboard-timeline [status]="activityStatus()" [entries]="activityTimelineEntries()" />
|
||||
</app-dashboard-card>
|
||||
|
||||
@@ -99,12 +99,12 @@
|
||||
</app-dashboard-section>
|
||||
|
||||
<!-- Documentation -->
|
||||
<app-dashboard-card [titleKey]="'dashboard.docTitle'" [icon]="'pi-question-circle'" [span]="'span 4'">
|
||||
<app-dashboard-card [titleKey]="'dashboard.docTitle'" [icon]="'help'" [span]="'span 4'">
|
||||
<ul class="dashboard-doc-list">
|
||||
@for (link of documentationLinks; track link.id) {
|
||||
<li>
|
||||
<button type="button" class="dashboard-doc-list__item" disabled>
|
||||
<span class="pi {{ link.icon }}" aria-hidden="true"></span>
|
||||
<app-icon [name]="link.icon" [size]="18" />
|
||||
<span>{{ link.labelKey | translate }}</span>
|
||||
<span class="dashboard-doc-list__badge">{{ 'adminShell.nav.comingSoonBadge' | translate }}</span>
|
||||
</button>
|
||||
|
||||
@@ -11,19 +11,21 @@ import { DashboardStatusRowComponent } from '../components/dashboard-status-row.
|
||||
import { DashboardShortcutCardComponent } from '../components/dashboard-shortcut-card.component';
|
||||
import { DashboardTimelineComponent, DashboardTimelineEntry, DashboardTimelineStatus } from '../components/dashboard-timeline.component';
|
||||
import { ButtonComponent } from '../../../../shared/ui/button/button.component';
|
||||
import { IconComponent } from '../../../../shared/ui/icon/icon.component';
|
||||
import { AdminDashboardQuickAction, AdminDashboardShortcut } from '../models/admin-dashboard.model';
|
||||
import { AppIconName } from '../../../../shared/ui/icon/icon-registry';
|
||||
|
||||
interface LinkedAction {
|
||||
icon: string;
|
||||
icon: AppIconName;
|
||||
labelKey: string;
|
||||
descriptionKey?: string;
|
||||
route: string[];
|
||||
comingSoon: boolean;
|
||||
}
|
||||
|
||||
const ACTIVITY_ICON: Record<string, string> = {
|
||||
'draft-saved': 'pi-save',
|
||||
published: 'pi-upload',
|
||||
const ACTIVITY_ICON: Record<string, AppIconName> = {
|
||||
'draft-saved': 'save',
|
||||
published: 'upload',
|
||||
};
|
||||
|
||||
const ACTIVITY_LABEL_KEY: Record<string, string> = {
|
||||
@@ -31,11 +33,11 @@ const ACTIVITY_LABEL_KEY: Record<string, string> = {
|
||||
published: 'dashboard.activityPublished',
|
||||
};
|
||||
|
||||
const DOCUMENTATION_LINKS = [
|
||||
{ id: 'documentation', icon: 'pi-book', labelKey: 'dashboard.docDocumentation' },
|
||||
{ id: 'shortcuts', icon: 'pi-bolt', labelKey: 'dashboard.docKeyboardShortcuts' },
|
||||
{ id: 'report-issue', icon: 'pi-flag', labelKey: 'dashboard.docReportIssue' },
|
||||
{ id: 'release-notes', icon: 'pi-megaphone', labelKey: 'dashboard.docReleaseNotes' },
|
||||
const DOCUMENTATION_LINKS: Array<{ id: string; icon: AppIconName; labelKey: string }> = [
|
||||
{ id: 'documentation', icon: 'book', labelKey: 'dashboard.docDocumentation' },
|
||||
{ id: 'shortcuts', icon: 'zap', labelKey: 'dashboard.docKeyboardShortcuts' },
|
||||
{ id: 'report-issue', icon: 'flag', labelKey: 'dashboard.docReportIssue' },
|
||||
{ id: 'release-notes', icon: 'megaphone', labelKey: 'dashboard.docReleaseNotes' },
|
||||
];
|
||||
|
||||
@Component({
|
||||
@@ -51,6 +53,7 @@ const DOCUMENTATION_LINKS = [
|
||||
DashboardShortcutCardComponent,
|
||||
DashboardTimelineComponent,
|
||||
ButtonComponent,
|
||||
IconComponent,
|
||||
],
|
||||
templateUrl: './admin-dashboard-page.component.html',
|
||||
styleUrls: ['./admin-dashboard-page.component.scss'],
|
||||
@@ -98,7 +101,7 @@ export class AdminDashboardPageComponent {
|
||||
id: entry.id,
|
||||
labelKey: ACTIVITY_LABEL_KEY[entry.type] ?? entry.type,
|
||||
timeText: new Date(entry.timestamp).toLocaleString(this.languageService.currentLanguage()),
|
||||
icon: ACTIVITY_ICON[entry.type] ?? 'pi-circle',
|
||||
icon: ACTIVITY_ICON[entry.type] ?? 'circle',
|
||||
})),
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user