The big "Учебный каталог включён" card with its buttons sat on top of the
order screen. While training mode is on, the order screen now shows only
"Включён учебный режим · отключить в настройках"; the link opens Settings
at the training switch. The guide (trial order, stock, purchasing, TTK)
moves into the "Обучение и знакомство" settings card, so nothing is lost.
The invitation card for companies with an empty catalog is unchanged.
Specs updated for the new placement, plus a check that the notice link
lands on (and focuses) the switch.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Clients: every page load re-sent all clients (72 requests for 18 clients)
because pushAll ran from several startup events at once, never skipped
unchanged clients, and overlapping saves of one client read the same stale
version, so ~40% ended in 409 conflicts. The payload also carried a fresh
updatedAt, so even identical re-sends bumped the server version and wrote a
change event, which made other devices' next save conflict too.
- Remember what the server holds per client (content fingerprint, scoped to
the workspace) and skip unchanged clients; seed it from the server
snapshot so a device that is already in sync sends nothing.
- Serialise saves per client and make pushAll single-flight.
- Load the server snapshot before the startup push instead of racing it.
- Drop the volatile updatedAt from the payload (server keeps updated_at).
Error log: a record the server refuses (Access denied for a workspace the
user is not in) stayed in the IndexedDB queue forever, was re-sent on every
flush and could block newer records behind it. Records from another
workspace are now dropped, others after 3 attempts.
Adds tests/client-sync-v1780.mjs (fake server enforcing the SQL conflict
rule; fails on the old module) to test:static, and bumps the cache-busting
versions of performance.js / app-runtime.js.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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.
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.
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 new "Банкетное меню" catalog tab (category 6) alongside Боксы/
Премиум/Посуда for composing wedding/banquet/anniversary menus. Unlike
the other tabs, dishes here are priced and weighed per guest, grouped
by menu section (catalogSection), and clicking a checkbox doesn't add
to the order directly -- it toggles inclusion in a live summary panel
showing a running per-guest price table for the whole composed menu.
The item editor gets two new fields (menu section, weight per guest)
shown only for this category, reusing the existing generic item CRUD
(editBox/saveBox) rather than building a parallel admin UI.
Also re-bumped index.html's script cache-busting query string, which
the previous whitelist-fix commit changed the content of app-runtime.js
without updating -- the same stale-cache bug fixed earlier in the
session, now closed for directly-tagged scripts too.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The signature-offer-pdf-v18.js script tag's ?v= query string was never
updated across ~9 content commits since the file was created, so browsers
kept serving a stale cached copy indefinitely. All other script/style tags
shared an equally stale v17.7.3 tag. Bumped every tag in index.html to a
single fresh version string so all recent fixes (template selection,
merged addon card, controlLines/extraServices wiring) actually reach users
without a manual hard refresh.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
New "signature" template pack (cream-elegance, neon-menu,
emerald-circles, midnight-checklist, gourmet-hero, diamond-gold)
replaces the visible template gallery in Settings. The previous 13
classic/archive templates keep rendering correctly for any existing
order that already used one, but are no longer selectable for new
orders — nothing was deleted.
Also adds a Settings option to upload a custom logo and company name
for PDF documents, replacing the hardcoded "Солнце Кейтеринг" branding
wherever a logo fails to load.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@media(max-width:720px) forced .sun-catalog-top-toolbar .cats into
flex-wrap:nowrap + overflow:auto, turning the colored category pills
(Боксы/Премиум/Напитки/Дополнения/Посуда...) into a horizontally
scrolling strip that runs off the right edge of the screen instead of
wrapping.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The previous mobile fix put the title on its own row but left the
search input as a regular flex item (flex:1 1 140px) among PDF/
"Вкладки"/"Меню". Since it greedily grows to fill whatever line it
lands on, it still forced PDF onto its own line, then itself onto the
next, then the two remaining buttons onto a third - three staggered
rows instead of the intended toolbar.
Gives the search input its own full-width row (flex-basis:100%,
order:0) and puts all catalog-head buttons (PDF, "Вкладки", "Меню") on
the next row together, sharing it evenly (flex:1 1 0) so they land on
one level instead of scattering.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The static :root fallback for --sun-ui-*/--sun-sidebar-* hardcodes the
"Standard Purple" preset (SIDEBAR_STANDARD's exact values). Any
workspace that has actually saved a different theme only gets it
applied once app-runtime.js's brand-theme module loads and runs
applyActual(), which reads the same localStorage cache
(sunBrandThemeV1) it always did - but by then the purple/mixed
defaults have already painted, so every load flashes the wrong colors
before snapping to the real ones.
Reads the same localStorage key synchronously in the
sun-startup-stability-guard script (already first in <head>, before
any stylesheet or the app-runtime.js bundle loads) and sets the same
CSS custom properties as inline styles on <html>. Inline element
styles always win over a stylesheet's :root selector regardless of
load order, so the correct theme is already active for the very first
paint. Workspaces with no saved theme yet are unaffected - the
existing static fallback still shows, same as today.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The sidebar's search and notification button icons are drawn entirely
by mask-image rules scoped to @media(min-width:901px) (the "enterprise
sidebar" redesign styling). The existing mobile block
(@media(max-width:900px)) predates that redesign and only hides the
button labels via font-size:0 - it never defined a mobile equivalent
of the icon rules, so on phones the buttons rendered with literally
nothing visible inside them: no text (hidden), no icon (never defined
for this breakpoint).
Adds a second @media(max-width:900px) block with the same mask-image
icons for #sunGlobalSearchBtn/#sunNotificationsBtn.
Also fixes the catalog toolbar (PDF / search / "Вкладки" / "Меню"):
on narrow screens flex-wrap put the title, buttons, and a
min-width:180px search input on uneven wrapped lines with no
consistent baseline. The title now takes its own full-width line
(order:-1, flex-basis:100%), the search input can shrink instead of
forcing overflow, and the toolbar buttons get an explicit
align-self:center so they land on one level together.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
.sun-doc-grid (client/phone/date/address) fell back to whatever the
last matching max-width media query set, and print rendering commonly
evaluates those against the page's content width - narrow enough to
trip the existing max-width:700px breakpoint and stack every field
onto its own line instead of the intended 2-column grid. Combined with
generous screen-sized padding/font-sizes carried into print, a normal
order routinely spilled onto a second page.
Adds an explicit @media print block that puts client/phone/date/address
in a single row regardless of viewport width (grid-auto-flow:column),
sets an explicit @page size/margin instead of relying on the browser
default, and shrinks fonts/padding throughout (header, field grid,
table rows, summary, footer) for both the order blank (.sun-doc) and
the receipt (.sun-receipt, which reuses the same grid). Verified with a
standalone reproduction of the markup/CSS - a 2-line order now renders
as one compact page instead of stacking every field before ever
reaching the table.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* chore: add one-time index html repair script
* chore: run one-time html repair on fix branch
* fix: close mobile style block [html-repair]
* chore: remove one-time html repair workflow
* chore: remove one-time html repair script
* test: add html style integrity checks
* test: enforce html integrity in deploy checks
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>