notifyMe() just called toggleWishlist() - no actual subscription
mechanism existed. Now calls a new subscribeToRestock() API method
(POST /items/{id}/notify-me, not yet built server-side - see
BACKEND-API-REFERENCE.md §12.5) and falls back to a local-only record
in localStorage on failure, so the request isn't silently dropped
while the backend catches up.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
JSON-LD was absent entirely (sitemap generation is separate backend
work, out of scope here). Added Product schema (name/description/
image/offers with price+availability) on item pages via setItemMeta(),
and a site-wide Organization schema via resetToDefaults(), both
injected as a single #seo-json-ld <script type=application/ld+json>
tag that gets replaced on navigation.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
CartService.addItem() fired an async dynamic import + API call for
new items but returned void immediately - buyNow() navigated to /cart
before the item was actually added, landing the user on an empty or
stale cart. addItem() now returns a Promise that resolves once the
cart signal actually contains the item; buyNow() awaits it before
navigating.
Also wired SeoService.setItemMeta()/resetToDefaults() into the product
detail page - built and working, but never called anywhere, so every
product page rendered the site-wide default OG/Twitter tags instead of
per-product ones.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Both setItemMeta() and resetToDefaults() hardcoded 'ru_RU'. Added a
LanguageService-driven mapping (ru/en/hy -> ru_RU/en_US/hy_AM).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
createPayment() and recordOrder() hardcoded currency: 'RUB' regardless
of LanguageService.currentCurrency() (app supports RUB/USD/EUR/AMD).
Widened CartPaymentRequest.currency from a 'RUB' literal to string and
use the actual selected currency in both calls.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
createOrder() sent a discount-applied price per line item that the
client computed itself, with no server revalidation. Items now only
carry productId/name/quantity - the backend must price from its own
catalog. createPayment()'s amount (required to actually charge the
payment gateway) is unchanged; backend must revalidate it instead,
tracked in BACKEND-API-REFERENCE.md §12.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Ran knip to find unused exports/dependencies (deps already clean, no
unused packages/files found).
Removed genuinely dead code (verified zero references anywhere,
including templates):
- 4 unused constants in config/constants.ts (scroll/pagination/search
thresholds never consumed)
- isAdminRole(), toSearchResult(), createInitialSearchState(),
getTranslatedCategoryName() - unused utility functions
- DEFAULT_EDITOR_HEADER_CONFIG - unused constant
- TelegramService - entire file deleted; cart.component.ts/
cart.service.ts already implement the same window.Telegram.WebApp
access directly, this was an unused duplicate
Real bug fix found during the sweep: SeoService has providedIn:'root'
with a live effect() meant to sync <title>/OG/canonical tags to
tenant bootstrap config, but nothing in the app ever injected it, so
Angular never instantiated it and the effect never ran - the SEO sync
a prior sprint reported as "done and verified" was actually dead on
arrival. Fixed by injecting SeoService in the root App component.
Left alone: ~125 knip-flagged "unused exported types" - overwhelmingly
config/schema interfaces for the widget/theme/admin domain models,
high false-positive rate for this kind of interface-heavy Angular app,
deleting blind risks breaking structural type contracts. Also left
locally-used-but-over-exported helpers (toCssColor/toBackendColor,
HTML_EDITOR_TOOLBAR*, HISTORY_LIMIT, DEFAULT_CATALOG_PAGE_SIZE) - real
code, not dead, just exported wider than needed.
tsc --noEmit and ng build --configuration=production both clean (only
pre-existing bundle-budget warning, unrelated).
Files changed: src/app/app.ts, src/app/config/constants.ts,
src/app/core/auth/models/permission.model.ts,
src/app/core/products/models/catalog-experience.model.ts,
src/app/core/search/models/search-state.model.ts,
src/app/features/project-editor/models/project-editor.model.ts,
src/app/services/index.ts, src/app/utils/item.utils.ts,
src/app/services/telegram.service.ts (deleted)
- CartPaymentRequest gains qrDescription (brandName > hostname > fallback
text) and customerID (telegram id)
- QrCreateResponse gains qrTTL; polling window now derived from it
(min 60s) instead of a fixed 3-minute/36-check cap
- PAYMENT_MAX_CHECKS replaced by PAYMENT_MIN_POLL_SECONDS
Added in an earlier commit this session (branding OG image + gallery
field), but SeoService.resetToDefaults() never actually read it -
defaultImage fell back straight to appIconUrl/logoUrl, so the field the
editor calls 'Social Share Image' had no runtime effect. Now it's
checked first.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Completes the rest of Sprint 28 on top of the earlier admin-scoped
a11y/skeleton pass (576f260):
- SeoService.resetToDefaults() now reads real bootstrap.seo.default /
branding instead of hardcoded placeholder text + a broken
/og-image.jpg reference; auto-reapplies via an effect() whenever
bootstrap (re)loads, same pattern as UiRuntimeFacade.
- New public/sitemap.xml (static baseline, documented per-tenant-dynamic
limitation) + public/robots.txt Sitemap directive and admin/editor
Disallow rules.
- Global prefers-reduced-motion override in styles.scss covering every
existing hover-transform/fade-in/shimmer animation in one place.
- New adminProducts/adminUsers/adminMonitoring/adminAnalytics
empty-state i18n keys (en/ru/hy) for this sprint's skeleton/empty-state
consistency fixes.
- docs/KNOWN-ISSUES.md: logged a newly-found, much larger pre-existing
gap (~178 missing adminXxx.* i18n keys across the whole admin
backoffice) - deferred to Sprint 29's translation validation, not
fixed here.
- docs/BACKEND.md: new item 17 (sitemap generation gap).
- docs/ADMIN.md, docs/SPRINT-PLAN.md: rewritten Sprint 28 sections to
describe the full, combined scope (both commits) instead of the
earlier admin-only framing.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sprint 2 high-priority cleanup: cart/language/location services called
localStorage directly, bypassing the try/catch safety and core/<domain>
pattern used elsewhere (e.g. ProjectEditorDraftStorageService). New
core/storage/LocalStorageService centralizes get/set/remove and JSON
helpers with private-mode/quota error handling, reused across all three.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Removed invented adminAuthApiUrl endpoint and separate AdminLoginComponent.
Admin login now uses the exact same Telegram session backend
(TelegramSessionApiService, {authApiUrl}/users/sessions) and the exact
same TelegramLoginComponent (mode="customer" | "admin" input) as customer
login - only the storage (cookie/localStorage/signals) stays separate.
- Extracted the shared HTTP+normalization logic from AuthService into
TelegramSessionApiService so both AuthService and AdminAuthService call it
instead of duplicating request/parsing code.
- Documented the resulting backend gap in docs/Project-Editor.md: since the
session API has no concept of "admin", server-side role enforcement is
required when admin API calls are made - the frontend only decides where
to store the session, not whether the user is actually an admin.
- Project editor: persist draft to localStorage, restore on reload,
last-saved/draft-restored status indicators, section/whole-draft reset
with confirmation.
- Extract shared QR/polling/expiry engine from TelegramLoginComponent
(shared/qr-login) and reuse it for a new admin login flow.
- Admin authentication kept fully separate from customer session:
own cookie/localStorage keys, signals, guard, and header interceptor
(core/admin-auth).
- ?login=true / ?adminLogin=true open the respective login dialog for
manual testing.
- Ed25519 challenge/verify interfaces (fail-closed no-op binding) ready
for backend delivery.
- Document autosave/reset/admin-auth/QR-reuse/Ed25519 model and the
remaining full-field-coverage gap in docs/Project-Editor.md.
Unify product details modules behind config-driven contracts so teams can
extend UX without changing runtime architecture or bootstrap flow.
Keep backward compatibility with existing product payloads by treating new
media/specification/variant/related structures as optional extensions.
Improve conversion and content discoverability with reusable actions,
typed media rendering, grouped specifications, dynamic variants, and
multi-collection related products.