Commit Graph

204 Commits

Author SHA1 Message Date
pavlov346346-source
3610927a33 fix: restore write permissions in local-only mode
RBAC permission checks (has()) always deferred to the cloud
workspace's role, which is null when using "work locally without
internet" — so every guarded action (save/delete order, edit catalog,
etc.) was blocked with "no permission" even though there is no
workspace to restrict against. Local-only mode now grants full access,
matching how nav permissions already behave with no cloud session.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-15 17:49:05 +03:00
pavlov346346-source
26681195b0 fix: accept emergency export format in Settings backup restore
The Settings "load from file" button only understood its own
sun-catering-backup format. Extend it to also read the
caterium-full-export format produced by the browser-console
emergency export (localStorage + IndexedDB), so an already-downloaded
export file can be restored directly through the UI.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-15 17:30:31 +03:00
pavlov346346-source
345ee8e052 feat: add JSON file backup export/import to Settings
Lets users download the local database as a JSON file and restore it
from a file, independent of cloud sync — needed while Supabase is
unreachable to move data between computers or recover after an outage.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-15 17:29:32 +03:00
pavlov346346-source
98ba823f16 merge: reconcile production promo-code work with main
Bring the 13 commits pushed directly to production (trial promo
codes, owner onboarding, service worker/PWA cache fixes, auth
proxy fallback) back into main, so the two branches share one
history again. The direct-to-production pushes had diverged from
main, which silently broke the auto-promote pipeline (plain
non-force push rejected as non-fast-forward).
2026-09-15 17:07:07 +03:00
pavlov346346-source
caa13eca59 feat: add "work locally without internet" option to login gate
When Supabase is unreachable, employees were stuck on the login
screen with no way in, even though the app is fully usable offline
(orders, catalog, stock, etc. all live in localStorage already).

Adds a low-key link on the login screen that sets a local-only flag
and skips the auth gate entirely until a real cloud sign-in succeeds
(which clears the flag). Deliberately does not touch any cloud/session
state, so it can't trigger an automatic cloud pull that would overwrite
data created while working offline.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-15 16:57:56 +03:00
pavlov346346-source
79b470d03f fix: fall back to direct Supabase auth on proxy 500 2026-09-14 17:26:32 +03:00
pavlov346346-source
708b90e87d fix: align safe service worker with release cache guard 2026-09-14 17:10:16 +03:00
pavlov346346-source
30a534b67f fix: keep safe navigation compatible with release guard 2026-09-14 17:01:37 +03:00
pavlov346346-source
5b827b4e09 fix: reopen app after persisted login session 2026-09-14 16:57:45 +03:00
pavlov346346-source
0249734252 fix: stop service worker from rewriting app HTML 2026-09-14 16:43:56 +03:00
pavlov346346-source
74a6261d5b test: syntax-check trial promo module 2026-09-14 16:39:41 +03:00
pavlov346346-source
77485cafca fix: keep promo onboarding server-authoritative 2026-09-14 12:23:00 +03:00
pavlov346346-source
e6fff9e58d fix: restore validated PWA cache key for onboarding release 2026-09-14 12:07:10 +03:00
pavlov346346-source
b09276f2fc Add v17.8.2 owner workspace onboarding migration 2026-09-14 10:57:45 +03:00
pavlov346346-source
9d38b5550b Fix company owner and employee onboarding 2026-09-14 10:57:28 +03:00
pavlov346346-source
f514b22798 feat: load trial promo developer UI in production PWA 2026-09-14 02:49:05 +03:00
pavlov346346-source
afb502c345 feat: add trial promo management to developer console 2026-09-13 18:44:18 +03:00
pavlov346346-source
170c078998 feat: require trial promo code during Caterium signup 2026-09-13 18:43:54 +03:00
pavlov346346-source
834557e865 fix: show real error text instead of "[object Object]" in auth gate
String(err) on a plain object (a PostgrestError-shaped object without a
.message, or any non-Error rejection) renders as the literal string
"[object Object]" with no useful information. Added errText() that
prefers err.message, falls back to JSON-stringifying the object, and
only then falls back to a generic message - and applied it to the three
catch blocks in the login/company-creation gate flow (auth(), the
pending-registration finisher, and the "retry workspace access" button),
which is what surfaced the raw "[object Object]" during login.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-13 17:30:17 +03:00
pavlov346346-source
fe029715eb ops: add api.caterium.ru proxy script to repo, fix auth CORS
The deployed PHP reverse proxy at api.caterium.ru only forwarded/allowed
a fixed set of CORS request headers, missing x-supabase-api-version which
supabase-js v2.112.4's auth client sends on every request. That made the
browser reject the preflight and fail the actual login call client-side
with a generic "Failed to fetch" (not a server error, so it never showed
up in server logs) - every login was broken since the proxy went live.

Fixed on the live server and committed the previously SCP-only script
here so future edits go through git instead of being SSH-only.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-13 17:08:58 +03:00
pavlov346346-source
5d73b2c37e Route Supabase REST/Auth/Storage/Functions traffic through api.caterium.ru proxy
The api.caterium.ru PHP reverse proxy has been deployed and verified end-to-end
(curl tests plus live authenticated RPC calls through a real browser session).
This switches the client's Supabase fetch calls to go through the proxy so
requests from Russia work without a VPN. Realtime's WebSocket connection is
unaffected since it doesn't go through fetch.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-13 02:50:37 +03:00
pavlov346346-source
aafad18d6a fix: remove the racy forced password-change gate for new employees
showPasswordChangeGate() replaced the auth gate's content and returned,
but the same login also fires onAuthStateChange, which kicks off
loadMemberships() independently. Once that resolved (workspace found),
ensureAuthGate()'s periodic poll (every 5s, plus assorted event-driven
calls) saw signedIn+workspace and tore the gate down immediately,
regardless of whether a password-change form was currently blocking
it - the employee would see the "create a new password" screen flash
and vanish before they could use it, dropping them straight into the
app still on their temporary password.

Removes the forced gate entirely (mustChangePassword/
showPasswordChangeGate) and adds a "Пароль" box to the account
settings card instead, so changing your password is a deliberate,
always-available action rather than a one-shot dialog racing the rest
of the login flow.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-13 01:59:36 +03:00
pavlov346346-source
4afde880d2 revert: back out the api.caterium.ru fetch rewrite for now
The previous commit accidentally carried over an uncommitted proxy
cutover (supabaseProxyFetch wired into createClient's global.fetch)
that was left sitting in the working tree from an earlier, paused
verification session - it was never meant to ship without first
completing the full checklist (auth, clients, orders, Storage,
Functions) against the live proxy. Reverting just that piece so
main goes back to calling cksuehzcimitsxmeloes.supabase.co directly,
keeping the "Загрузить из облака" button fix from the same commit.
The proxy cutover will come back once it's actually been verified
end-to-end.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 18:37:56 +03:00
pavlov346346-source
38a4ba47eb fix: restore the missing "Загрузить из облака" button in settings
bindCloudUI() already wires a click handler on #sunCloudPullV2
(confirm dialog -> pullRemote({replace:true, quiet:false}), which
backs up the local copy and force-replaces it with the server's
orders/clients/etc.), but renderCloudUI()'s template never actually
emitted a button with that id - the handler was permanently a no-op.
Adds the missing button to the "Профиль и аккаунт" settings card.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 18:35:20 +03:00
pavlov346346-source
5427b71651 fix: wrap catalog category pills on mobile instead of horizontal scroll
@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>
2026-09-12 18:20:41 +03:00
pavlov346346-source
870cc405dc fix: keep catalog toolbar buttons together on mobile
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>
2026-09-12 18:06:13 +03:00
pavlov346346-source
edfce3b7e6 fix: pre-apply cached brand/sidebar theme before first paint
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>
2026-09-12 17:56:19 +03:00
pavlov346346-source
1af9733154 fix: restore search/notification icons and catalog toolbar on mobile
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>
2026-09-12 17:45:31 +03:00
pavlov346346-source
09d54682c2 fix: add timeout and error handling to loadMemberships
Every other cloud RPC call goes through sunCloudAwait (12s timeout),
but loadMemberships() (which resolves which workspace to open right
after login, showing "Загружаю рабочую базу...") called client.rpc/
.from directly with no timeout and no catch. A single slow or dropped
request left the auth gate stuck on that message forever instead of
surfacing a retryable error - there's already a "Проверить ещё раз"
button wired to reloadMemberships(), but nothing ever told the user
they needed to press it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 15:50:04 +03:00
pavlov346346-source
571f8e199d fix: compact print layout for the order blank and receipt
.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>
2026-09-12 14:45:31 +03:00
pavlov346346-source
2e2f02efc3 fix: match the early login boot placeholder to the cream login redesign
performance.js paints a full-screen boot placeholder immediately on
page load (before app-runtime.js and the real auth gate exist), so it
never got updated when login-signature-v1776.js was redesigned from a
dark two-column layout with an offer-gallery/002.jpg table photo to
the light single-column "cream login" with the Caterium SVG mark.

Result: every signed-out visitor saw the old dark/photo screen flash
for a moment, then get replaced by the new light screen once the real
gate rendered - the flicker was two genuinely different, undeployed-
in-sync designs, not a caching artifact. Rebuilt the boot placeholder's
markup and inline critical CSS to mirror login-signature-v1776.js's
current design (same background, mark, type, and copy) so the first
paint already matches what replaces it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 14:40:33 +03:00
pavlov346346-source
195e0ba5c3 fix: reconcile version/test drift from the unreviewed production merge
The just-merged production-only commits were never run through QA
(they were pushed directly to the production branch), so several
version markers and hardcoded test strings had drifted out of sync
with each other:

- package.json was bumped to 17.8.0 but package-lock.json,
  release-manifest.json and app-runtime.js's own VERSION constant were
  never updated to match - reverted to 17.7.3 since no other release
  artifact actually changed.
- service-worker.js's cache name legitimately moved to
  v81-20260912-account-center-loader (real new modules need the cache
  bust), but release-manifest.json's pwaCache field and two
  release-check.mjs assertions still expected the old v78 name.
- edge-security-v1774.mjs and static-security.mjs asserted the old
  employee role list (with "admin") and old PWA cache name that
  production's own commits had already changed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 14:40:22 +03:00
pavlov346346-source
12ba761af9 Merge production (account center + login redesign) into main
production had diverged from main with 20 unreviewed direct-push
commits never merged back (account center feature, owner-only employee
roles, and a login-screen redesign - the exact "cream login" work that
replaced the old dark table-photo screen). Neither QA nor the
audit fixes on main had ever seen this code.

Conflict resolution:
- service-worker.js: kept production's newer cache-refresh mechanism
  (CRITICAL_FRESH, forceFresh, withAccountCenter, v81 cache name) and
  combined both sides' CORE asset lists (account-center-v1780.js +
  login-signature-v1776.js from production, auth-security-v1774.js +
  order-enhancements-v1775.js from main).
- deploy-timeweb.yml: kept main's version, which already independently
  verifies service-worker.js's sha256 alongside the login/logo files -
  strictly more thorough than production's version of the same check.

Also fixes fallout from production's commits never having been
QA-tested before landing: package.json was bumped to 17.8.0 with
nothing else in the codebase updated to match (reverted to 17.7.3,
matching package-lock.json/release-manifest.json/app-runtime.js, since
no other release artifact actually changed), and three tests
(static-security.mjs, edge-security-v1774.mjs, release-check.mjs) had
hardcoded strings (old PWA cache name, old employee role list) that no
longer matched the code they were checking.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 14:36:52 +03:00
pavlov346346-source
a281150f12 docs: record the missing sun_employee_prepare/finalize_v28 migration
sun_employee_prepare_v28 and sun_employee_finalize_v28 - the RPCs
caterium-create-employee's whole authorization model rests on - were
applied directly to the production database and were never committed,
so the actual authorization logic wasn't auditable from the repo.
Recorded verbatim via pg_get_functiondef() against the live database
on 2026-09-12 (read-only; nothing was re-applied). Confirmed both
match what the security audit inferred from the Edge Function's
error-message handling: caller identity + workspace-owner/platform-admin
check + plan/feature gates + member limits, all enforced here rather
than in the Edge Function itself.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 14:16:16 +03:00
pavlov346346-source
aeca9eae99 fix: lock down caterium-platform-auth-admin CORS to allowlisted origins
This Edge Function grants platform-admin power (list every user across
every workspace, ban/unban accounts, trigger password resets for any
user_id) but answered with Access-Control-Allow-Origin: '*', unlike the
sibling caterium-create-employee function which already uses an origin
allowlist. Authorization itself was never bypassable this way (the
function still requires the caller's own Bearer token and re-checks
sun_is_platform_admin() server-side), but a wildcard CORS response
removes a real layer of defense-in-depth if a platform-admin token were
ever exposed to another origin.

Applies the same allowedOrigin()/corsHeaders() pattern already proven in
caterium-create-employee, and extends edge-security-v1774.mjs (which
already asserted the wildcard was gone from create-employee, but never
checked this function) to cover both.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 14:09:16 +03:00
pavlov346346-source
4106452f56 fix: stop the menu-editor button-patch interval once it succeeds
setupOldButton() polled the DOM every 4s forever after the menu editor
first opened, with no way to ever stop - the interval id was not even
kept in a variable. It now returns whether the button was found, the
interval only starts when the first attempt fails, and clears itself
on the first successful attempt (also wired into the existing
disconnect() cleanup alongside the other timers).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 14:09:06 +03:00
pavlov346346-source
a08633f9d0 perf: stop recomputing the full client list per client on bulk push
pushAllClients() already has every client profile from its own
listClients() call, but looped through pushClient(key) -> getClient(key)
-> listClients() again for each one - N clients meant N+1 full
order-history recomputations instead of one. pushClient now accepts an
optional already-known profile so the bulk path skips the redundant
lookup; single-key callers (scheduleServerPush's debounce) are
unaffected.

compareClientSources() had the same shape of duplicate work: it called
buildClients()/serverListClients() directly and then again inside
mergeClientSources(). mergeClientSources() now accepts already-computed
local/server arrays instead of always recomputing both.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 14:08:57 +03:00
pavlov346346-source
60d00e9f56 fix: correct stale data-layer references in manifest and SW cache
release-manifest.json still pointed "dataLayer" at the removed
data-layer-v1771.js instead of the actually-loaded v1773.

service-worker.js's offline cache list never included
auth-security-v1774.js, order-enhancements-v1775.js or
login-signature-v1776.js despite performance.js loading all three at
runtime - PWA/offline mode could serve a stale or missing module.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 14:08:47 +03:00
pavlov346346-source
a98e636937 fix: gate production promotion on QA success; remove dead data-layer files
promote-production.yml triggered on push to main independently of
qa.yml, with no branch protection configured on the repo - a failing
QA run (npm audit, static security tests, e2e) never blocked
production. Switch it to the same workflow_run pattern deploy-timeweb.yml
already uses: only promote the exact commit QA just passed.

Also removes public/core/data-layer-v1770/1771/1772.js: only v1773 is
ever loaded (index.html, performance.js's loadDataLayer, service-worker
cache all reference v1773 only) - the older three were dead weight
shipped to every visitor.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 14:08:38 +03:00
pavlov346346-source
ce1930ef5d fix: always load account center for authenticated users 2026-09-12 10:11:09 +03:00
pavlov346346-source
bd7995d68c fix: expose manager account and password controls 2026-09-12 10:10:25 +03:00
pavlov346346-source
f0629ba1a4 test: enforce owner-only employee roles 2026-09-12 08:53:42 +03:00
pavlov346346-source
879a7e0f83 test: include account center in deploy checks 2026-09-12 08:53:36 +03:00
pavlov346346-source
10f3074319 fix: prevent employee accounts from becoming company owner 2026-09-12 08:52:57 +03:00
pavlov346346-source
3a54721594 chore: refresh account center assets 2026-09-12 08:51:55 +03:00
pavlov346346-source
5cff7135f2 feat: load Caterium account center 2026-09-12 08:48:57 +03:00
pavlov346346-source
9c868aa99b fix: lock company owner and simplify employee accounts 2026-09-12 08:48:39 +03:00
pavlov346346-source
0e1a7c98e8 feat: add owner and employee account center 2026-09-12 08:48:06 +03:00
pavlov346346-source
0417a05e9c feat: owner-only company accounts and self profile 2026-09-12 08:47:40 +03:00
pavlov346346-source
c5d68411ed fix: add one-click PWA cache refresh for login 2026-09-12 08:27:31 +03:00