Apply the user's explicit address correction to the PHP recipient, Help links, contact form and tests. Refresh the form URL and PWA cache. Exact-recipient PHP tests and support/Help browser tests passed in isolated run 35575721587. No real mail sent, delivery not claimed. Keep standard main QA and production publication gates unchanged; no auth, database or unrelated feature changes.
Add Help contact form and PHP mail endpoint for support@katerion.ru, with validated Reply-To, explicit diagnostics consent, CSRF/origin checks, hashed rate limits and duplicate protection. Preserve drafts on error and avoid serializing customer data or SDK internals. Full PR QA passed in 35559317001; isolated PHP and 30 browser cases passed in 35559179372. Standard production gates unchanged. Publication checks do not send real mail; inbox receipt remains unverified. No training photo assets or unfinished training lifecycle changes.
Load the missing promo module, add a prominent creation button and place Promos beside Overview. Use existing AAL2-guarded server RPCs with selected plan, subscription duration and code validity; preserve form input, honest clipboard feedback and scope async responses. Full PR QA succeeded in run 35547013076. No live data, SQL, MFA or unfinished training branch changes. Preserve normal main QA and exact-asset/browser publication verification.
Keep notification-read state personal to user/company, preserve server read-only sections during staff synchronization, and respect separate order-create/edit/delete rights. Add visible mobile header session actions and a sticky logout that survives profile RPC failure; scope asynchronous profile/branding to user and workspace. Targeted browser suites and isolated SQL recovery tests passed in run 35506145405, iPhone screenshots reviewed. Full main QA remains required before production promotion. Workspace branding RPC migration is included but has NOT been applied to production Supabase; older servers retain safe owner-only fallback. No live membership/business-data repair is claimed without identifying the reported employee.
Add a single-page A4 preview and PDF for all selected banquet dishes or a ready-menu selection, with company branding and optional costs. Preserve draft/order state and saved line prices, enforce access and tenant scope, and fail clearly rather than clipping oversized menus. Nineteen targeted browser scenarios passed twice and the generated iPhone PDF was visually checked. Full main QA must pass before the existing automatic production promotion; production verification includes the new renderer and real-asset client menu export.
Inline handlers built as onclick="fn('${esc(id)}')" were injectable:
esc() turns ' into ', which the browser decodes back to ' before
the JS runs, so an id like x');alert(1);// broke out of the string.
Ids can come from a restored backup file or a synced catalog. Add
SunSafe.jsArg (JSON.stringify + HTML escape) and use it in all 23
handlers in app-runtime.js and index.html. Verified in a browser: a
payload id is passed through as a plain string and nothing executes.
Also replace the Settings version label that still showed
v17.6.0 · 2026.09.07, and bump the cache-busting version of the two
changed scripts (sun-safe.js, app-runtime.js).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add Settings > Учебный каталог, off by default and available on ordinary writable profiles. Load ready example boxes, photos, TTKs and linked sample inventory additively; preserve own data, saved orders and edited examples when hiding or re-enabling. Respect company/profile scope, read-only permissions, failed downloads and tenant changes. Preserve the opened recipe guide across real catalog refreshes on iPhone. Integrated feature checks and full pull-request QA passed. Standard main QA and exact-asset production UI verification remain in place.
On phones place the existing menu editor above the catalog and add the same return-to-top control used by New Order. Preserve form nodes, unsaved values and active input focus; keep desktop/tablet layout and read-only permissions. Full QA passed, including iPhone WebKit and existing promotion focus regression. Extend real-asset production verification with a backend-blocked mobile-menu scenario.
Avoid rebuilding the offer template picker during the five-second maintenance pass unless the selected template or template list actually changed. Includes a regression test for repeated maintenance cycles.
The Поддержка tab opens an assistant that answers how-to questions from
the handbook. The chat is a sandboxed iframe on the assistant's own
origin and is only loaded after an explicit click, so Help and its search
stay local and no third-party script runs inside the app.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add compact accessible client summaries and menu discounts in percent or rubles with explicit durations. Preserve ordinary catalog prices and order line snapshots; derive current prices and expire promotions automatically without background writes. Keep fractional prices, legacy sale compatibility, and regression coverage. Full pull-request QA passed. Publication remains gated by full main QA and byte-for-byte production asset and UI verification.
Add a question-circle SVG mask to Help using the existing sidebar pseudo-element. Preserve shared icon geometry, theme colors and navigation behavior. Add full-app icon regression and correct the previous loading-recovery fixture. Production promotion remains gated by full main QA.
* Make workspace loading a calm progress-only screen
* Cover quiet workspace loading and error recovery on desktop and mobile
* Run workspace loading regressions in desktop, mobile and WebKit QA
Two separate Set-based whitelists (OFFER_TEMPLATE_IDS in app-runtime.js
and TEMPLATE_IDS in ux-fixes-v1764.js) validated any explicit template
selection before persisting/rendering it, but neither had been updated
when premium-dark/premium-emerald were added. The per-order "Оформление
PDF" picker's click handler calls persistOfferTemplate(), which silently
returned false when the id failed this check -- so clicking either
premium card in an order's offer modal did nothing, no error, no toast.
Verified end-to-end against a real order: both ids now persist and the
picker shows the selected state correctly.
Also bumped performance.js's internal RELEASE tag, since it gates the
cache-busting query string used to dynamically load ux-fixes-v1764.js
and eight other core modules -- otherwise this fix would hit the same
stale-cache issue just fixed in index.html.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Replace the plain white "МЕНЮ" text label with a gold icon badge,
item count, and divider line, matching the icon-badge language already
used elsewhere in the template instead of a bare heading.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Two related fixes reported directly against live screenshots:
1. addonRow rendered each addon ("Напитки", "Чайная станция", ...) as
its own tall bordered pill sitting side by side -- disjointed, and
unlike the single-card "Полезные дополнения" treatment in the
referenced design. Now draws one card with a heading
("Рекомендуем добавить") containing all the icon+label items,
matching how checklistCard already works.
2. The addon list and the "Всё под контролем" checklist were hardcoded
constants in the canvas module, completely bypassing the app's
existing editable Client Offer Settings (Настройки → Предложение →
"Дополнительно к заказу", textarea-backed extraServices/controlLines
the user can already edit for the old templates). preparedSnapshot
now attaches the resolved controlLines/extraServices/titles onto
every snapshot; the 8 signature templates read them via a local
shadowing const (falls back to the previous defaults when nothing
is configured), so editing that Settings textarea now actually
changes what shows up in these templates too, on all 8 of them from
one change since addonRow/checklistCard are shared helpers.
Verified locally: default list (6 items) lays out cleanly in one card
with 2-line wrapping where needed; a snapshot with custom
controlLines/extraServices renders those exact custom strings instead
of the defaults, confirming the wiring actually works end to end and
isn't just falling back silently.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
User pointed at https://menu.furset.ru/view/23855347 as a design they
like and asked for a couple more templates in that direction. Added:
- premium-dark: near-black background, warm gold accents, real
line-drawn icon badges (calendar/people/box -- not emoji) for the
date/guests/boxes stats, bullet-point value props under the title,
minimal flat menu list -- closely matching that reference's visual
language instead of the card-heavy style of the existing templates.
- premium-emerald: the same layout in an emerald-green palette, as
the second "couple" of designs requested, sharing the cover code
with premium-dark and only differing by PALETTES entry.
Both plug into the existing shared renderContentPages engine, so they
get proper multi-page menu + pricing pages for free, consistent with
all 6 existing signature templates. New icon-drawing helpers
(iconBadge/iconCalendar/iconPeople/iconGauge/iconBox, statBadgeRow,
bulletList) live in the shared helper section for reuse.
Verified locally: both render in <70ms combined, produce 3 pages each
for a normal order, and hold up under the same adversarial-data pass
used for the other 6 (long names, 350 guests, seven-figure price,
item with no photo/price/category) -- no overlap, no crashes, generous
ellipsis truncation throughout. Fixed one real bug found while
testing: the date value in the stat badge ("8 августа 2026 г.") was
getting ellipsis-truncated at font-size 20px; reduced to 16px so it
fits cleanly.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Same issue as the cards just removed from cream-elegance: a hardcoded
['РЫБНЫЕ','МЯСНЫЕ','ВЕГЕТАРИАНСКИЕ','САЛАТЫ','ДЕСЕРТЫ'] tab row that
had nothing to do with what was actually in the order (an order with
zero vegetarian items still showed a "ВЕГЕТАРИАНСКИЕ" tab as if
active/relevant). Now derives the chip row from the distinct
categoryName values actually present on the order's items, and
renders nothing if that data isn't available.
Verified this pass end-to-end rather than just by inspection:
- Real "Скачать PDF" pipeline (sunClientOfferDebugPdf) produces a
valid application/pdf blob with a correct %PDF-1.4 header, not just
canvases.
- Confirmed Canvas text genuinely falls back through the font stack
(measured identical metrics for an unregistered font vs. Georgia
directly, and document.fonts.load() resolves instantly for a
never-registered family) -- so a blocked/unreachable Google Fonts
degrades silently to Georgia/Arial instead of hanging or erroring,
which matters given this app already had to work around Russia
connectivity issues elsewhere.
- Re-rendered all 6 templates against deliberately adversarial data
(150+ word event name, 44-char client name, 350 guests, a
million-ruble price, an item with no photo/price/category, a long
promo code) -- no crashes, no overlap, graceful ellipsis truncation
throughout.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
They were hardcoded labels (Рыбные закуски / Мясные закуски / Для
вегетарианцев / Мини-салаты) with a generic "Собрано под ваше
событие" caption that didn't reflect the actual order — confusing and
requested to be removed. The freed space now goes to a bigger 3x2
photo grid of the order's real items instead of leaving a gap.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Fix the price band on cream-elegance: it was a solid muddy olive
fill that didn't read as "this is your total" (reported directly by
the user). It's now a white card with a gold border, matching the
rest of that template's light card language; the dark templates'
solid-fill price band is unchanged since that already reads well
against a dark background.
- Give emerald-circles, midnight-checklist, gourmet-hero and
diamond-gold the same treatment as the first 2: real fonts
(Playfair Display/Montserrat or Unbounded/Manrope depending on
aesthetic) instead of Arial/Georgia, the same center-alignment fixes
applied everywhere text used align:'center', and their own
fully-styled inner pages (menu + pricing) via the shared
renderContentPages engine instead of the old generic base renderer.
- Fix two real regressions the font swap introduced and caught by
rendering each template locally: gourmet-hero's headline was
overflowing width and getting ellipsis-truncated ("КЕЙТЕРИНГ..."),
and diamond-gold's heading was wrapping to a second line that
collided with the subtitle below it. Both fixed with size/width
adjustments verified by direct canvas measurement.
- All 6 signature templates are selectable in Settings again.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The cover for these 2 templates was fully custom, but the item-list
and pricing pages behind it still came from the old generic base
renderer (Arial, different card style, different page chrome) — so a
downloaded offer read as a nice cover stapled to a plain, differently
styled document ("два предложения").
Both templates now render their own item-list pages (photo, name,
category/weight, qty and line total, paginated ~10 rows/page) and a
matching pricing-breakdown page (base cost, discount, promo, delivery,
total, per-guest), using the same fonts, palette and card language as
the cover, with a shared footer/page-number treatment across every
page. The other 4 signature templates and the 13 classic ones are
untouched and keep using the original base renderer.
Verified locally: 8 items -> 3 pages (cover + 1 menu + pricing),
15 items -> 4 pages (cover + 2 menu + pricing), 0 items -> 3 pages,
no errors, base renderer confirmed never invoked for these 2 ids.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>