Files
marketplaces/src/app/app.routes.ts

389 lines
17 KiB
TypeScript
Raw Normal View History

2026-01-18 18:57:06 +04:00
import { Routes } from '@angular/router';
2026-02-26 22:23:08 +04:00
import { languageGuard } from './guards/language.guard';
import { projectEditorDirtyGuard } from './features/project-editor/guards/project-editor-dirty.guard';
import { adminAuthGuard } from '@marketplaces/auth';
import { requireAdminPermission } from './core/admin-auth/admin-auth.guard';
import { authRoutes } from './core/auth/auth.routes';
import { adminCategoryDirtyGuard } from './features/admin/categories/guards/admin-category-dirty.guard';
fix(admin): products canDeactivate guard, unblock npm, drop dead deps RC-01 Phase 1 mechanical fixes (verified against current repo state, not blindly reapplied from TODO.md): - admin/products create/edit/duplicate now protected by an unsaved- changes guard (adminProductDirtyGuard), mirroring the existing categories pattern. AdminProductsFacade had zero dirty-tracking before this - added a dirty signal, set true on updateDraft(), cleared on load/create/successful save. Added confirmLeaveUnsaved to the adminProducts i18n section (en/ru/hy) - categories already had its own copy of this key, products didn't. - barry-cache bumped ^0.1.0 -> ^0.9.3 (the pinned range no longer resolved on the registry - ETARGET - which had been silently blocking every npm install/uninstall all cycle). - Removed primeng/primeicons (npm uninstall, now unblocked) - the only consumer (items-carousel) was already deleted in RC PERF-01. - Removed core/search/services/search-history.service.ts, a dead 1-line re-export with zero importers (verified: the real implementation is features/search/services/search-history.service.ts, used by search.facade.ts). Left core/search/models/* alone - those ARE live, imported by catalog components. Verified before touching: HeaderConfig.showProfile toggle is already removed from the header-section editor template (TODO.md was stale on this one) - no change needed, will correct the tracking doc separately. tsc --noEmit clean, npm run build green (bundle unchanged, primeng was already tree-shaken out, this just removes the dead dependency declaration itself). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-25 20:04:51 +04:00
import { adminProductDirtyGuard } from './features/admin/products/guards/admin-product-dirty.guard';
2026-07-10 13:34:25 +04:00
import { environment } from '../environments/environment';
2026-01-18 18:57:06 +04:00
// Core routes (same across all brands)
const coreRoutes: Routes = [
2026-01-18 18:57:06 +04:00
{
path: '',
loadComponent: () => import('./pages/home/home.component').then(m => m.HomeComponent)
},
2026-07-05 01:36:21 +04:00
{
path: 'catalog',
loadComponent: () => import('./features/website/catalog/containers/catalog-container.component').then(m => m.CatalogContainerComponent)
},
{
path: 'catalog/:id',
loadComponent: () => import('./features/website/catalog/containers/catalog-container.component').then(m => m.CatalogContainerComponent)
},
2026-01-18 18:57:06 +04:00
{
path: 'category/:id',
2026-07-05 01:36:21 +04:00
redirectTo: 'catalog/:id',
pathMatch: 'full'
2026-01-18 18:57:06 +04:00
},
{
path: 'category/:id/items',
2026-07-05 01:36:21 +04:00
redirectTo: 'catalog/:id',
pathMatch: 'full'
2026-01-18 18:57:06 +04:00
},
2026-07-05 01:43:56 +04:00
{
path: 'product/:id',
loadComponent: () => import('./features/website/product/containers/product-details-container.component').then(m => m.ProductDetailsContainerComponent)
},
2026-01-18 18:57:06 +04:00
{
path: 'item/:id',
2026-07-05 01:43:56 +04:00
redirectTo: 'product/:id',
pathMatch: 'full'
2026-01-18 18:57:06 +04:00
},
{
path: 'search',
loadComponent: () => import('./features/website/catalog/containers/catalog-container.component').then(m => m.CatalogContainerComponent)
2026-01-18 18:57:06 +04:00
},
feat(builder): redesign marketplace builder experience and navigation Renamed the experience consistently to Marketplace Builder everywhere visible (page title/subtitle, breadcrumbs, sidebar, dashboard quick action/shortcut copy) - internal ProjectEditor class/selector names kept as-is to avoid regressions. builder.title/subtitle no longer say 'bootstrap-config editing surface' to end users. New business-oriented IA (builder-groups.model.ts): 12 existing ProjectEditorSectionIds regrouped into 8 groups (Marketplace, Branding and Design, Homepage, Content, Languages, Marketplace Features, Navigation and Search, Preview) - every existing section mapped to exactly one group, none dropped, no group points at a page that doesn't exist. New Builder landing page at /edit (was a redirect straight into the General form): readiness percent, recommended next step, one overview card per group (purpose + real complete/in-progress/not-started/unknown status, icon+text never color-alone), a Marketplace Readiness checklist, quick links. All derived from real facade/schema signals (required-field fill state, modifiedSections, staticPages, catalog counts via BackofficeDataService) or explicitly marked unknown (the "preview reviewed" check has no tracking - shown as unknown, never guessed). Section pages (/edit/:section) now share one consistent header: breadcrumb (Builder > Group > Section), draft/published + modified badges, and a contextual help panel (what is this / where visible / what happens if you skip it) sourced per group. Sidebar nav rewritten as grouped, icon-led sections with per-section and per-group status indicators; layout converted from a top pill-tab bar to a responsive sidebar (desktop 280px, tablet 72px icon rail, mobile drawer with Escape-to-close), matching the Sprint 1 admin shell pattern. Section form components themselves untouched. Routes: 'edit' is now its own landing route instead of redirecting to 'edit/general'; /builder and /project-editor redirect to 'edit'. Sprint 1/2 destinations that pointed at edit/general now point at the new edit landing page. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 09:43:03 +04:00
{
path: 'edit',
canActivate: [adminAuthGuard],
feat(builder): redesign marketplace builder experience and navigation Renamed the experience consistently to Marketplace Builder everywhere visible (page title/subtitle, breadcrumbs, sidebar, dashboard quick action/shortcut copy) - internal ProjectEditor class/selector names kept as-is to avoid regressions. builder.title/subtitle no longer say 'bootstrap-config editing surface' to end users. New business-oriented IA (builder-groups.model.ts): 12 existing ProjectEditorSectionIds regrouped into 8 groups (Marketplace, Branding and Design, Homepage, Content, Languages, Marketplace Features, Navigation and Search, Preview) - every existing section mapped to exactly one group, none dropped, no group points at a page that doesn't exist. New Builder landing page at /edit (was a redirect straight into the General form): readiness percent, recommended next step, one overview card per group (purpose + real complete/in-progress/not-started/unknown status, icon+text never color-alone), a Marketplace Readiness checklist, quick links. All derived from real facade/schema signals (required-field fill state, modifiedSections, staticPages, catalog counts via BackofficeDataService) or explicitly marked unknown (the "preview reviewed" check has no tracking - shown as unknown, never guessed). Section pages (/edit/:section) now share one consistent header: breadcrumb (Builder > Group > Section), draft/published + modified badges, and a contextual help panel (what is this / where visible / what happens if you skip it) sourced per group. Sidebar nav rewritten as grouped, icon-led sections with per-section and per-group status indicators; layout converted from a top pill-tab bar to a responsive sidebar (desktop 280px, tablet 72px icon rail, mobile drawer with Escape-to-close), matching the Sprint 1 admin shell pattern. Section form components themselves untouched. Routes: 'edit' is now its own landing route instead of redirecting to 'edit/general'; /builder and /project-editor redirect to 'edit'. Sprint 1/2 destinations that pointed at edit/general now point at the new edit landing page. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 09:43:03 +04:00
loadComponent: () => import('./features/project-editor/pages/builder-overview-page.component').then(m => m.BuilderOverviewPageComponent)
},
2026-07-10 13:43:53 +04:00
{
path: 'edit/:section',
canActivate: [adminAuthGuard],
loadComponent: () => import('./features/project-editor/pages/project-editor-page.component').then(m => m.ProjectEditorPageComponent),
canDeactivate: [projectEditorDirtyGuard]
2026-07-10 13:43:53 +04:00
},
{
path: 'backoffice',
canActivate: [adminAuthGuard],
perf(routing): lazy-load AdminLayoutComponent shell Phase 3 (Performance). Bundle-stats analysis (esbuild metafile) found AdminLayoutComponent statically imported and used as component: in app.routes.ts, the only route in the file not using loadComponent - pulled the whole backoffice shell into the initial bundle even for storefront-only visitors, even though every child route under it was already lazy. Fixed: component: AdminLayoutComponent -> loadComponent(). Verified live at /backoffice/dashboard - admin-layout-component now its own 21.86kB lazy chunk, no console errors, dashboard renders correctly. Initial bundle over-budget shrank from 438.68kB to 417.53kB. Investigated and deliberately left as-is (not bugs, documented/ legitimate): - src/app/i18n/ru.ts (272kB) eagerly bundled - explicit, commented tradeoff in translate.service.ts (ru is platform default language, avoids extra round-trip for majority of users; en/hy already code-split). Changing this trades bundle size for default-language UX regression - a product call, not a cleanup item. - @lucide/angular (182kB) - verified tree-shaking works correctly (1750 icons in the package, ~85 actually imported by name in icon-registry.ts, sideEffects:false). Cost is genuine icon usage, not dead weight. A further win exists (splitting the icon registry into storefront-critical vs admin-only sets so backoffice-only icons don't ride the eager header/footer import chain) but touches every icon consumer across the app - flagging as a scoped follow-up rather than attempting blind in this pass. Remaining bundle-budget warning after this fix: 1.12MB vs 700kB budget. Given ~350kB is unavoidable Angular framework/router/zone.js floor, +272kB deliberate ru.ts, +182kB legitimate icon usage, the 700kB budget itself looks stale/unrealistic for this app's actual floor - flagging for a business decision on raising it rather than chasing further cuts. tsc --noEmit clean, ng build clean (warning only, no errors), live browser-verified.
2026-07-26 15:45:51 +04:00
loadComponent: () => import('./features/admin/shell/admin-layout.component').then(m => m.AdminLayoutComponent),
children: [
{ path: '', redirectTo: 'dashboard', pathMatch: 'full' },
{
path: 'dashboard',
loadComponent: () => import('./features/admin/dashboard/pages/admin-dashboard-page.component').then(m => m.AdminDashboardPageComponent),
data: {
titleKey: 'adminShell.pages.dashboard.title',
descriptionKey: 'adminShell.pages.dashboard.description',
breadcrumb: [{ labelKey: 'adminShell.pages.dashboard.title' }]
}
},
{
path: 'products',
loadComponent: () => import('./features/admin/products/pages/admin-products-list-page.component').then(m => m.AdminProductsListPageComponent),
data: {
titleKey: 'adminShell.pages.products.title',
descriptionKey: 'adminShell.pages.products.description',
breadcrumb: [{ labelKey: 'adminShell.nav.products' }]
}
},
{
path: 'products/create',
loadComponent: () => import('./features/admin/products/pages/admin-product-editor-page.component').then(m => m.AdminProductEditorPageComponent),
fix(admin): products canDeactivate guard, unblock npm, drop dead deps RC-01 Phase 1 mechanical fixes (verified against current repo state, not blindly reapplied from TODO.md): - admin/products create/edit/duplicate now protected by an unsaved- changes guard (adminProductDirtyGuard), mirroring the existing categories pattern. AdminProductsFacade had zero dirty-tracking before this - added a dirty signal, set true on updateDraft(), cleared on load/create/successful save. Added confirmLeaveUnsaved to the adminProducts i18n section (en/ru/hy) - categories already had its own copy of this key, products didn't. - barry-cache bumped ^0.1.0 -> ^0.9.3 (the pinned range no longer resolved on the registry - ETARGET - which had been silently blocking every npm install/uninstall all cycle). - Removed primeng/primeicons (npm uninstall, now unblocked) - the only consumer (items-carousel) was already deleted in RC PERF-01. - Removed core/search/services/search-history.service.ts, a dead 1-line re-export with zero importers (verified: the real implementation is features/search/services/search-history.service.ts, used by search.facade.ts). Left core/search/models/* alone - those ARE live, imported by catalog components. Verified before touching: HeaderConfig.showProfile toggle is already removed from the header-section editor template (TODO.md was stale on this one) - no change needed, will correct the tracking doc separately. tsc --noEmit clean, npm run build green (bundle unchanged, primeng was already tree-shaken out, this just removes the dead dependency declaration itself). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-25 20:04:51 +04:00
canDeactivate: [adminProductDirtyGuard],
data: {
titleKey: 'adminShell.pages.productCreate.title',
descriptionKey: 'adminShell.pages.productCreate.description',
breadcrumb: [{ labelKey: 'adminShell.nav.products', path: ['products'] }, { labelKey: 'adminShell.pages.productCreate.title' }]
}
},
{
path: 'products/:id/edit',
loadComponent: () => import('./features/admin/products/pages/admin-product-editor-page.component').then(m => m.AdminProductEditorPageComponent),
fix(admin): products canDeactivate guard, unblock npm, drop dead deps RC-01 Phase 1 mechanical fixes (verified against current repo state, not blindly reapplied from TODO.md): - admin/products create/edit/duplicate now protected by an unsaved- changes guard (adminProductDirtyGuard), mirroring the existing categories pattern. AdminProductsFacade had zero dirty-tracking before this - added a dirty signal, set true on updateDraft(), cleared on load/create/successful save. Added confirmLeaveUnsaved to the adminProducts i18n section (en/ru/hy) - categories already had its own copy of this key, products didn't. - barry-cache bumped ^0.1.0 -> ^0.9.3 (the pinned range no longer resolved on the registry - ETARGET - which had been silently blocking every npm install/uninstall all cycle). - Removed primeng/primeicons (npm uninstall, now unblocked) - the only consumer (items-carousel) was already deleted in RC PERF-01. - Removed core/search/services/search-history.service.ts, a dead 1-line re-export with zero importers (verified: the real implementation is features/search/services/search-history.service.ts, used by search.facade.ts). Left core/search/models/* alone - those ARE live, imported by catalog components. Verified before touching: HeaderConfig.showProfile toggle is already removed from the header-section editor template (TODO.md was stale on this one) - no change needed, will correct the tracking doc separately. tsc --noEmit clean, npm run build green (bundle unchanged, primeng was already tree-shaken out, this just removes the dead dependency declaration itself). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-25 20:04:51 +04:00
canDeactivate: [adminProductDirtyGuard],
data: {
titleKey: 'adminShell.pages.productEdit.title',
descriptionKey: 'adminShell.pages.productEdit.description',
breadcrumb: [{ labelKey: 'adminShell.nav.products', path: ['products'] }, { labelKey: 'adminShell.pages.productEdit.title' }]
}
},
{
path: 'products/:id/duplicate',
loadComponent: () => import('./features/admin/products/pages/admin-product-editor-page.component').then(m => m.AdminProductEditorPageComponent),
fix(admin): products canDeactivate guard, unblock npm, drop dead deps RC-01 Phase 1 mechanical fixes (verified against current repo state, not blindly reapplied from TODO.md): - admin/products create/edit/duplicate now protected by an unsaved- changes guard (adminProductDirtyGuard), mirroring the existing categories pattern. AdminProductsFacade had zero dirty-tracking before this - added a dirty signal, set true on updateDraft(), cleared on load/create/successful save. Added confirmLeaveUnsaved to the adminProducts i18n section (en/ru/hy) - categories already had its own copy of this key, products didn't. - barry-cache bumped ^0.1.0 -> ^0.9.3 (the pinned range no longer resolved on the registry - ETARGET - which had been silently blocking every npm install/uninstall all cycle). - Removed primeng/primeicons (npm uninstall, now unblocked) - the only consumer (items-carousel) was already deleted in RC PERF-01. - Removed core/search/services/search-history.service.ts, a dead 1-line re-export with zero importers (verified: the real implementation is features/search/services/search-history.service.ts, used by search.facade.ts). Left core/search/models/* alone - those ARE live, imported by catalog components. Verified before touching: HeaderConfig.showProfile toggle is already removed from the header-section editor template (TODO.md was stale on this one) - no change needed, will correct the tracking doc separately. tsc --noEmit clean, npm run build green (bundle unchanged, primeng was already tree-shaken out, this just removes the dead dependency declaration itself). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-25 20:04:51 +04:00
canDeactivate: [adminProductDirtyGuard],
data: {
titleKey: 'adminShell.pages.productDuplicate.title',
descriptionKey: 'adminShell.pages.productDuplicate.description',
breadcrumb: [{ labelKey: 'adminShell.nav.products', path: ['products'] }, { labelKey: 'adminShell.pages.productDuplicate.title' }]
}
},
{
path: 'categories',
loadComponent: () => import('./features/admin/categories/pages/admin-categories-list-page.component').then(m => m.AdminCategoriesListPageComponent),
data: {
titleKey: 'adminShell.pages.categories.title',
descriptionKey: 'adminShell.pages.categories.description',
breadcrumb: [{ labelKey: 'adminShell.nav.categories' }]
}
},
{
path: 'categories/create',
loadComponent: () => import('./features/admin/categories/pages/admin-category-editor-page.component').then(m => m.AdminCategoryEditorPageComponent),
canDeactivate: [adminCategoryDirtyGuard],
data: {
titleKey: 'adminShell.pages.categoryCreate.title',
descriptionKey: 'adminShell.pages.categoryCreate.description',
breadcrumb: [{ labelKey: 'adminShell.nav.categories', path: ['categories'] }, { labelKey: 'adminShell.pages.categoryCreate.title' }]
}
},
{
path: 'categories/:id/edit',
loadComponent: () => import('./features/admin/categories/pages/admin-category-editor-page.component').then(m => m.AdminCategoryEditorPageComponent),
canDeactivate: [adminCategoryDirtyGuard],
data: {
titleKey: 'adminShell.pages.categoryEdit.title',
descriptionKey: 'adminShell.pages.categoryEdit.description',
breadcrumb: [{ labelKey: 'adminShell.nav.categories', path: ['categories'] }, { labelKey: 'adminShell.pages.categoryEdit.title' }]
}
},
{
docs(static-pages): backoffice redirect + StaticPages.md Milestone 7 of the Static Pages Module sprint. - app.routes.ts: /backoffice/static-pages now redirects to /edit/static-pages (absolute redirectTo) instead of rendering BackofficeComingSoonPageComponent - Static Pages is a first-class Project Editor module, not a second CRUD surface over the same bootstrap.staticPages data. - New docs/StaticPages.md: full field reference (General/Localization/SEO/ Media/Publishing), the enabled+status storefront-gating story and its backward-compat default (existing/legacy data normalizes to enabled+published so nothing gets silently un-published; only new pages default to draft), CRUD/search/filter/bulk, the "mutate from the unfiltered list" implementation note, rich-text/HTML-mode contract (pointer to EDITOR.md), nav integration, and the export/import/draft/ publish compatibility statement. - docs/EDITOR.md: Static Pages row in the Sections table (was previously absent - the row lived implicitly in Footer's description), HTML editor section updated with the Sprint X+2 toolbar additions + validation contract, redirect noted near the route line. `docs/Project-Editor.md` (named in the original brief) no longer exists - superseded by EDITOR.md per that file's own header; documentation went there + the new file instead. - docs/PROJECT.md: doc index entry for StaticPages.md. Gate: tsc --noEmit, npm test (57/57), arch:check, build all green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 10:24:18 +04:00
// Static Pages is a first-class Project Editor module (Sprint X+2), not a
// separate backoffice CRUD surface - redirect here rather than build a
// second UI over the same bootstrap.staticPages data.
path: 'static-pages',
docs(static-pages): backoffice redirect + StaticPages.md Milestone 7 of the Static Pages Module sprint. - app.routes.ts: /backoffice/static-pages now redirects to /edit/static-pages (absolute redirectTo) instead of rendering BackofficeComingSoonPageComponent - Static Pages is a first-class Project Editor module, not a second CRUD surface over the same bootstrap.staticPages data. - New docs/StaticPages.md: full field reference (General/Localization/SEO/ Media/Publishing), the enabled+status storefront-gating story and its backward-compat default (existing/legacy data normalizes to enabled+published so nothing gets silently un-published; only new pages default to draft), CRUD/search/filter/bulk, the "mutate from the unfiltered list" implementation note, rich-text/HTML-mode contract (pointer to EDITOR.md), nav integration, and the export/import/draft/ publish compatibility statement. - docs/EDITOR.md: Static Pages row in the Sections table (was previously absent - the row lived implicitly in Footer's description), HTML editor section updated with the Sprint X+2 toolbar additions + validation contract, redirect noted near the route line. `docs/Project-Editor.md` (named in the original brief) no longer exists - superseded by EDITOR.md per that file's own header; documentation went there + the new file instead. - docs/PROJECT.md: doc index entry for StaticPages.md. Gate: tsc --noEmit, npm test (57/57), arch:check, build all green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 10:24:18 +04:00
redirectTo: '/edit/static-pages',
pathMatch: 'full'
},
{
path: 'transactions',
loadComponent: () => import('./features/admin/transactions/pages/admin-transactions-list-page.component').then(m => m.AdminTransactionsListPageComponent),
data: {
titleKey: 'adminShell.pages.transactions.title',
descriptionKey: 'adminShell.pages.transactions.description',
breadcrumb: [{ labelKey: 'adminShell.nav.transactions' }]
}
},
{
path: 'orders',
loadComponent: () => import('./features/admin/orders/pages/admin-orders-list-page.component').then(m => m.AdminOrdersListPageComponent),
data: {
titleKey: 'adminShell.pages.orders.title',
descriptionKey: 'adminShell.pages.orders.description',
breadcrumb: [{ labelKey: 'adminShell.nav.orders' }]
}
},
{
path: 'orders/:id',
loadComponent: () => import('./features/admin/orders/pages/admin-order-detail-page.component').then(m => m.AdminOrderDetailPageComponent),
data: {
titleKey: 'adminShell.pages.orderDetail.title',
descriptionKey: 'adminShell.pages.orderDetail.description',
breadcrumb: [{ labelKey: 'adminShell.nav.orders', path: ['orders'] }, { labelKey: 'adminShell.pages.orderDetail.title' }]
}
},
{
path: 'customers',
loadComponent: () => import('./features/admin/customers/pages/admin-customers-list-page.component').then(m => m.AdminCustomersListPageComponent),
data: {
titleKey: 'adminShell.pages.customers.title',
descriptionKey: 'adminShell.pages.customers.description',
breadcrumb: [{ labelKey: 'adminShell.nav.customers' }]
}
},
{
path: 'customers/:email',
loadComponent: () => import('./features/admin/customers/pages/admin-customer-detail-page.component').then(m => m.AdminCustomerDetailPageComponent),
data: {
titleKey: 'adminShell.pages.customerDetail.title',
descriptionKey: 'adminShell.pages.customerDetail.description',
breadcrumb: [{ labelKey: 'adminShell.nav.customers', path: ['customers'] }, { labelKey: 'adminShell.pages.customerDetail.title' }]
}
},
{
path: 'notifications',
loadComponent: () => import('./features/admin/notifications/pages/admin-notifications-page.component').then(m => m.AdminNotificationsPageComponent),
data: {
titleKey: 'adminShell.nav.notifications',
descriptionKey: 'adminShell.nav.notifications',
breadcrumb: [{ labelKey: 'adminShell.nav.notifications' }]
}
},
{
path: 'integrations',
loadComponent: () => import('./features/admin/integrations/pages/admin-integrations-page.component').then(m => m.AdminIntegrationsPageComponent),
data: {
titleKey: 'adminShell.nav.integrations',
descriptionKey: 'adminShell.nav.integrations',
breadcrumb: [{ labelKey: 'adminShell.nav.integrations' }]
}
},
{
path: 'finance',
loadComponent: () => import('./features/admin/finance/pages/admin-finance-page.component').then(m => m.AdminFinancePageComponent),
data: {
titleKey: 'adminShell.nav.finance',
descriptionKey: 'adminShell.nav.finance',
breadcrumb: [{ labelKey: 'adminShell.nav.finance' }]
}
},
{
path: 'marketplaces',
loadComponent: () => import('./features/admin/marketplaces/pages/admin-marketplaces-page.component').then(m => m.AdminMarketplacesPageComponent),
data: {
titleKey: 'adminShell.nav.marketplaces',
descriptionKey: 'adminShell.nav.marketplaces',
breadcrumb: [{ labelKey: 'adminShell.nav.marketplaces' }]
}
},
feat: Track S frontend - permission core + Audit & Security section core/permissions (SessionPermissions/AuditEvent models, gateway/token, requiresScope() CanActivateFn) against docs/backend/ TRACK-S-SECURITY-RBAC-CONTRACT.md §1-3. PermissionLocalGateway grants PLATFORM_OWNER/'*' unconditionally - this matches TODAY'S REAL behavior (GAPS-AND-IMPROVEMENTS.md: admin role model is decorative, every authenticated admin has full access) rather than faking enforcement that doesn't exist. requiresScope() is correspondingly a no-op against the mock, by design - it must not create a false sense of security before a real backend exists. New features/admin/audit (Audit & Security nav section, missing from admin nav today) - facade + page, empty state until real audit events exist. Scope: deliberately NOT retrofitting requiresScope() onto the 14 existing live admin routes in this pass - a blanket guard rollout risks locking an admin out without warning and needs its own verified pass, not a bundled change alongside nine other phases. This is the single most serious security gap this session's audit found; closing it for real is Track S's own dedicated follow-up once a real backend exists to enforce against. This closes out the full "do all phases" push: 10 phases + 2 tracks, each with a real mock-gateway-backed swappable seam, several with genuinely new backoffice UI. Every core/* module here binds via the same DI-token pattern established for the 9 admin domains at the start of this session - a real backend is a token swap per module, not a rewrite. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-18 00:08:28 +04:00
{
path: 'audit',
loadComponent: () => import('./features/admin/audit/pages/admin-audit-page.component').then(m => m.AdminAuditPageComponent),
data: {
titleKey: 'adminShell.nav.audit',
descriptionKey: 'adminShell.nav.audit',
breadcrumb: [{ labelKey: 'adminShell.nav.audit' }]
}
},
{
path: 'moderation',
loadComponent: () => import('./features/admin/moderation/pages/admin-reviews-list-page.component').then(m => m.AdminReviewsListPageComponent),
data: {
titleKey: 'adminShell.pages.moderation.title',
descriptionKey: 'adminShell.pages.moderation.description',
breadcrumb: [{ labelKey: 'adminShell.nav.moderation' }]
}
},
{
path: 'moderation/reports',
loadComponent: () => import('./features/admin/moderation/pages/admin-reports-list-page.component').then(m => m.AdminReportsListPageComponent),
data: {
titleKey: 'adminShell.pages.reportsQueue.title',
descriptionKey: 'adminShell.pages.reportsQueue.description',
breadcrumb: [{ labelKey: 'adminShell.nav.moderation', path: ['moderation'] }, { labelKey: 'adminShell.pages.reportsQueue.title' }]
}
},
{
path: 'moderation/:id',
loadComponent: () => import('./features/admin/moderation/pages/admin-review-detail-page.component').then(m => m.AdminReviewDetailPageComponent),
data: {
titleKey: 'adminShell.pages.reviewDetail.title',
descriptionKey: 'adminShell.pages.reviewDetail.description',
breadcrumb: [{ labelKey: 'adminShell.nav.moderation', path: ['moderation'] }, { labelKey: 'adminShell.pages.reviewDetail.title' }]
}
},
{
path: 'media',
loadComponent: () => import('./features/backoffice/media/media-library-page.component').then(m => m.MediaLibraryPageComponent),
data: {
titleKey: 'adminShell.pages.media.title',
descriptionKey: 'adminShell.pages.media.description',
breadcrumb: [{ labelKey: 'adminShell.nav.mediaLibrary' }]
}
},
{
path: 'users',
canActivate: [requireAdminPermission('users.manage')],
loadComponent: () => import('./features/admin/users/pages/admin-users-page.component').then(m => m.AdminUsersPageComponent),
data: {
titleKey: 'adminShell.pages.users.title',
descriptionKey: 'adminShell.pages.users.description',
breadcrumb: [{ labelKey: 'adminShell.nav.users' }]
}
},
{
path: 'monitoring',
loadComponent: () => import('./features/admin/monitoring/pages/admin-monitoring-page.component').then(m => m.AdminMonitoringPageComponent),
data: {
titleKey: 'adminShell.pages.monitoring.title',
descriptionKey: 'adminShell.pages.monitoring.description',
breadcrumb: [{ labelKey: 'adminShell.nav.monitoring' }]
}
},
{
path: 'analytics',
loadComponent: () => import('./features/admin/analytics/pages/admin-analytics-page.component').then(m => m.AdminAnalyticsPageComponent),
data: {
titleKey: 'adminShell.pages.analytics.title',
descriptionKey: 'adminShell.pages.analytics.description',
breadcrumb: [{ labelKey: 'adminShell.nav.analytics' }]
}
},
{
path: 'reports',
loadComponent: () => import('./features/admin/reports/pages/admin-reports-page.component').then(m => m.AdminReportsPageComponent),
data: {
titleKey: 'adminShell.pages.reports.title',
descriptionKey: 'adminShell.pages.reports.description',
breadcrumb: [{ labelKey: 'adminShell.nav.reports' }]
}
},
{
path: 'settings',
loadComponent: () => import('./features/admin/settings/pages/admin-settings-page.component').then(m => m.AdminSettingsPageComponent),
data: {
titleKey: 'adminShell.pages.settings.title',
descriptionKey: 'adminShell.pages.settings.description',
breadcrumb: [{ labelKey: 'adminShell.nav.settings' }]
}
},
feat(admin): Seller Management Phase 1 UI - Partners section, empty state, request/learn-more dialogs No backend, no CRUD, no API, no business logic - production-quality UI only, built entirely from existing shared components (app-dialog, app-empty-state, app-button, app-form-field, app-input, app-icon, app-badge). Gated per ADR-011: reads modules.sellerManagement.enabled from bootstrap (always false today, no backend sets it) rather than hardcoding disabled state. New: - AdminSellerManagementPageComponent (features/admin/seller-management/ pages/) - renders the specified empty state (title/description/ Request Access + Learn More buttons) using existing shared/ui primitives only, no new UI infrastructure. - Request Access dialog: Company/Email/Message form via app-form-field + app-input + a plain textarea (no dedicated textarea component exists yet, styled to match app-input's own tokens exactly). Submission is mocked (setTimeout), no API call. On submit: closes and opens a success dialog ("Thank you..."). - Learn More dialog: 6 capability bullets (seller dashboards, storefronts, permissions, analytics, product ownership, marketplace administration) under a "Coming Soon" badge. - New admin nav group "Partners" > "Seller Management" link (admin-nav.model.ts), new route /backoffice/partners/seller-management (app.routes.ts), using the same loadComponent/breadcrumb pattern as every other admin route. Translations: full en/ru/hy coverage, zero hardcoded strings - new adminShell.nav.{partnersGroup,sellerManagement}, adminShell.pages.sellerManagement, and a new adminSellerManagement.* namespace (emptyState/requestDialog/requestSuccessDialog/ learnMoreDialog) added to translations.ts (types) and all three locale files. Accessibility: inherited from app-dialog (role="dialog", aria-modal, focus trap on Tab/Shift+Tab, Escape to close, focus restored to trigger on close) - no new a11y code needed, reused as-is. Responsive: existing --space-*/--font-size-* tokens throughout, flex-wrap on button row, mobile breakpoint stacks actions full-width. Verified live (ru locale, devBypassAdmin): nav group/link render correctly, breadcrumb shows "Управление продавцами", empty state copy matches spec exactly, Request Access dialog opens with all 3 fields + Cancel/Send Request, filled + submitted -> success dialog with exact spec copy, Learn More dialog shows all 6 bullets + Coming Soon badge, no console errors, verified again at 375px mobile viewport. tsc --noEmit clean, ng build clean (pre-existing bundle- budget warning only), arch:check (boundaries + cycles) clean.
2026-07-26 20:47:18 +04:00
{
path: 'partners/seller-management',
loadComponent: () => import('./features/admin/seller-management/pages/admin-seller-management-page.component').then(m => m.AdminSellerManagementPageComponent),
data: {
titleKey: 'adminShell.pages.sellerManagement.title',
descriptionKey: 'adminShell.pages.sellerManagement.description',
breadcrumb: [{ labelKey: 'adminShell.nav.sellerManagement' }]
}
},
{ path: '**', redirectTo: 'dashboard' }
]
},
{
path: 'builder',
feat(builder): redesign marketplace builder experience and navigation Renamed the experience consistently to Marketplace Builder everywhere visible (page title/subtitle, breadcrumbs, sidebar, dashboard quick action/shortcut copy) - internal ProjectEditor class/selector names kept as-is to avoid regressions. builder.title/subtitle no longer say 'bootstrap-config editing surface' to end users. New business-oriented IA (builder-groups.model.ts): 12 existing ProjectEditorSectionIds regrouped into 8 groups (Marketplace, Branding and Design, Homepage, Content, Languages, Marketplace Features, Navigation and Search, Preview) - every existing section mapped to exactly one group, none dropped, no group points at a page that doesn't exist. New Builder landing page at /edit (was a redirect straight into the General form): readiness percent, recommended next step, one overview card per group (purpose + real complete/in-progress/not-started/unknown status, icon+text never color-alone), a Marketplace Readiness checklist, quick links. All derived from real facade/schema signals (required-field fill state, modifiedSections, staticPages, catalog counts via BackofficeDataService) or explicitly marked unknown (the "preview reviewed" check has no tracking - shown as unknown, never guessed). Section pages (/edit/:section) now share one consistent header: breadcrumb (Builder > Group > Section), draft/published + modified badges, and a contextual help panel (what is this / where visible / what happens if you skip it) sourced per group. Sidebar nav rewritten as grouped, icon-led sections with per-section and per-group status indicators; layout converted from a top pill-tab bar to a responsive sidebar (desktop 280px, tablet 72px icon rail, mobile drawer with Escape-to-close), matching the Sprint 1 admin shell pattern. Section form components themselves untouched. Routes: 'edit' is now its own landing route instead of redirecting to 'edit/general'; /builder and /project-editor redirect to 'edit'. Sprint 1/2 destinations that pointed at edit/general now point at the new edit landing page. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 09:43:03 +04:00
redirectTo: 'edit',
pathMatch: 'full'
},
2026-07-10 13:43:53 +04:00
{
path: 'project-editor',
feat(builder): redesign marketplace builder experience and navigation Renamed the experience consistently to Marketplace Builder everywhere visible (page title/subtitle, breadcrumbs, sidebar, dashboard quick action/shortcut copy) - internal ProjectEditor class/selector names kept as-is to avoid regressions. builder.title/subtitle no longer say 'bootstrap-config editing surface' to end users. New business-oriented IA (builder-groups.model.ts): 12 existing ProjectEditorSectionIds regrouped into 8 groups (Marketplace, Branding and Design, Homepage, Content, Languages, Marketplace Features, Navigation and Search, Preview) - every existing section mapped to exactly one group, none dropped, no group points at a page that doesn't exist. New Builder landing page at /edit (was a redirect straight into the General form): readiness percent, recommended next step, one overview card per group (purpose + real complete/in-progress/not-started/unknown status, icon+text never color-alone), a Marketplace Readiness checklist, quick links. All derived from real facade/schema signals (required-field fill state, modifiedSections, staticPages, catalog counts via BackofficeDataService) or explicitly marked unknown (the "preview reviewed" check has no tracking - shown as unknown, never guessed). Section pages (/edit/:section) now share one consistent header: breadcrumb (Builder > Group > Section), draft/published + modified badges, and a contextual help panel (what is this / where visible / what happens if you skip it) sourced per group. Sidebar nav rewritten as grouped, icon-led sections with per-section and per-group status indicators; layout converted from a top pill-tab bar to a responsive sidebar (desktop 280px, tablet 72px icon rail, mobile drawer with Escape-to-close), matching the Sprint 1 admin shell pattern. Section form components themselves untouched. Routes: 'edit' is now its own landing route instead of redirecting to 'edit/general'; /builder and /project-editor redirect to 'edit'. Sprint 1/2 destinations that pointed at edit/general now point at the new edit landing page. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 09:43:03 +04:00
redirectTo: 'edit',
2026-07-10 13:43:53 +04:00
pathMatch: 'full'
},
{
path: 'wishlist',
loadComponent: () => import('./features/website/user-experience/wishlist/containers/wishlist-page.component').then(m => m.WishlistPageComponent)
},
{
path: 'compare',
loadComponent: () => import('./features/website/user-experience/compare/containers/compare-page.component').then(m => m.ComparePageComponent)
},
2026-01-18 18:57:06 +04:00
{
path: 'cart',
loadComponent: () => import('./pages/cart/cart.component').then(m => m.CartComponent)
},
{
path: 'page/:key',
loadComponent: () => import('./pages/static-page/static-page.component').then(m => m.StaticPageComponent)
},
{
path: ':staticPath',
loadComponent: () => import('./pages/static-page/static-page.component').then(m => m.StaticPageComponent)
}
];
2026-07-05 00:51:29 +04:00
// TODO(CMS): Resolve informational/legal pages from backend content configuration here.
// Disabled hardcoded pages: about, contacts, faq, delivery, guarantee,
// company-details, payment-terms, return-policy, public-offer, privacy-policy.
const cmsContentRoutes: Routes = [];
2026-07-05 01:43:56 +04:00
// All routes sit under a :lang prefix (e.g. /ru/cart, /en/product/5)
export const routes: Routes = [
2026-07-10 13:34:25 +04:00
...(environment.production ? [] : [{
path: '__diagnostics',
loadComponent: () => import('./features/diagnostics/components/diagnostics-page.component').then(m => m.DiagnosticsPageComponent)
}]),
...authRoutes,
2026-01-18 18:57:06 +04:00
{
2026-02-26 22:23:08 +04:00
path: ':lang',
canActivate: [languageGuard],
children: [
...coreRoutes,
2026-07-05 00:51:29 +04:00
...cmsContentRoutes,
2026-02-26 22:23:08 +04:00
{ path: '**', redirectTo: '' }
]
},
// URLs without a language prefix → redirect to default language
{ path: '**', redirectTo: 'ru' }
];