Skip to content

(SP: 2) [Frontend] Reduce Vercel variable costs via caching and analytics cleanup#367

Merged
ViktorSvertoka merged 2 commits into
developfrom
perf/reduce-vercel-runtime-costs
Feb 25, 2026
Merged

(SP: 2) [Frontend] Reduce Vercel variable costs via caching and analytics cleanup#367
ViktorSvertoka merged 2 commits into
developfrom
perf/reduce-vercel-runtime-costs

Conversation

@ViktorSvertoka
Copy link
Copy Markdown
Member

@ViktorSvertoka ViktorSvertoka commented Feb 25, 2026

Summary

This PR reduces Vercel variable costs by removing unnecessary server work and improving cache usage.

Changes

  • Removed 30s notification polling; now refreshes on open + tab visibility.
  • Removed revalidatePath('/', 'layout') from notification actions.
  • Enabled ISR for blog pages (revalidate = 3600) and removed noStore.
  • Moved blog post ISR config to route segment page (blog/[slug]/page.tsx).
  • Enabled Sanity CDN globally (useCdn: true) and removed local useCdn: false overrides in blog/layout/blog-author.
  • Added cached blog categories in locale layout via unstable_cache (revalidate: 3600, tag: blog-categories).
  • Removed Speed Insights integration and package.
  • Kept Web Analytics, but only in production (NODE_ENV === 'production').

Out of scope

  • No changes to quizzes page.
  • No changes to shop pages.

Expected impact

  • Lower Function Invocations and Fluid CPU/Memory.
  • Lower origin transfer for blog content.
  • No Speed Insights data-point charges.

Closes #366

Summary by CodeRabbit

  • Performance Improvements

    • Blog pages now use 1-hour revalidation and cached category data for faster loads
    • CDN caching enabled for content delivery
  • Behavior Changes

    • Notification polling replaced with visibility-triggered fetches; dropdown no longer forces a refresh
    • Removed some post-notification cache revalidation calls
  • Changes

    • Analytics now runs only in production
    • Speed Insights dependency removed

@ViktorSvertoka ViktorSvertoka self-assigned this Feb 25, 2026
@ViktorSvertoka ViktorSvertoka added the performance Performance and efficiency optimizations without functional changes. label Feb 25, 2026
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Feb 25, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
devlovers-net Ready Ready Preview, Comment Feb 25, 2026 11:53pm

Request Review

@netlify
Copy link
Copy Markdown

netlify Bot commented Feb 25, 2026

Deploy Preview for develop-devlovers ready!

Name Link
🔨 Latest commit 7f80727
🔍 Latest deploy log https://app.netlify.com/projects/develop-devlovers/deploys/699f8b5f2e55bb00083da7b5
😎 Deploy Preview https://deploy-preview-367--develop-devlovers.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 25, 2026

📝 Walkthrough

Walkthrough

Removed layout-wide revalidation, enabled CDN/caching for Sanity and blog data, added ISR (3600s) to blog pages, replaced notification polling with visibility-driven refresh, cached blog categories, and removed Speed Insights with Analytics limited to production. (48 words)

Changes

Cohort / File(s) Summary
Notification Mutations
frontend/actions/notifications.ts
Removed revalidatePath imports/calls from markAsRead, markAllAsRead, and createNotification (no layout revalidation).
Notification UI
frontend/components/header/NotificationBell.tsx
Replaced setInterval polling with document visibility-change listener; removed on-open refresh; UI/classname tweaks and minor type refinements.
Blog ISR & Pages
frontend/app/[locale]/blog/page.tsx, frontend/app/[locale]/blog/[slug]/page.tsx, frontend/app/[locale]/blog/category/[category]/page.tsx
Set export const revalidate = 3600 on list/category/slug pages (was 0); removed unstable_noStore usage and related gating.
Post Details & API Fetches
frontend/app/[locale]/blog/[slug]/PostDetails.tsx, frontend/app/api/blog-author/route.ts
Removed client.withConfig({ useCdn: false }) usage; switched to default client.fetch(...) (CDN-enabled behavior).
Layout & Category Caching
frontend/app/[locale]/layout.tsx
Added unstable_cache wrapper getCachedBlogCategories with TTL 3600 and blog-categories tag; replaced direct category fetch with cached call.
Sanity Client Config
frontend/client.ts
Changed useCdn from false to true in exported Sanity client (CDN caching enabled).
Analytics & Dependency Removal
frontend/app/layout.tsx, frontend/package.json
Removed @vercel/speed-insights; Analytics now rendered only when NODE_ENV === 'production'.

Sequence Diagram(s)

(Skipped — changes are configuration/optimizations and small control-flow tweaks that do not introduce a new multi-component sequential flow requiring visualization.)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • AM1007

Poem

🐰
Hopping through code with a twitchy ear,
Caches snug now, and polling less near.
ISR hums, Speed Insights gone away,
CDN carries the traffic today.
Rejoice — I nibble bugs, then hop away!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main changes: removing unnecessary polling/revalidation and cleaning up analytics, directly aligning with the core objectives to reduce Vercel costs.
Linked Issues check ✅ Passed All major objectives from #366 are implemented: notification polling removed, revalidatePath calls eliminated, blog ISR enabled, CDN enabled globally, blog categories cached, and Speed Insights removed with Web Analytics production-only.
Out of Scope Changes check ✅ Passed All changes directly address the objectives in #366; no unrelated modifications detected. Changes focus exclusively on reducing Vercel runtime costs through caching, ISR, and analytics cleanup.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch perf/reduce-vercel-runtime-costs

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (5)
frontend/components/header/NotificationBell.tsx (2)

80-91: fetchNotifications missing from useEffect dependency array — wrap in useCallback.

fetchNotifications is defined inside the component and referenced in both useEffect calls without being listed in their dependency arrays. While there's no runtime stale-closure bug here (the function only closes over stable setState refs and a static import), React's exhaustive-deps lint rule will flag both effects. Wrapping in useCallback is the correct fix.

♻️ Proposed refactor
+import { useCallback, useEffect, useRef, useState } from 'react';
-import { useEffect, useRef, useState } from 'react';

-  const fetchNotifications = async () => {
+  const fetchNotifications = useCallback(async () => {
    try {
      const data = await getNotifications();
      const parsed = data.map(n => ({
        ...n,
        createdAt: new Date(n.createdAt),
      }));
      setNotifications(parsed);
    } catch (error) {
      console.error(error);
    } finally {
      setLoading(false);
    }
-  };
+  }, []);

  useEffect(() => {
    fetchNotifications();
    const handleVisibility = () => {
      if (document.visibilityState === 'visible') {
        fetchNotifications();
      }
    };
    document.addEventListener('visibilitychange', handleVisibility);
    return () =>
      document.removeEventListener('visibilitychange', handleVisibility);
-  }, []);
+  }, [fetchNotifications]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/components/header/NotificationBell.tsx` around lines 80 - 91, The
effect hooks in NotificationBell reference the local function fetchNotifications
without listing it in dependencies, triggering exhaustive-deps warnings; wrap
fetchNotifications in useCallback (e.g., const fetchNotifications =
useCallback(..., [/* stable deps or none */])) so its identity is stable, then
include fetchNotifications in the useEffect dependency arrays (the
visibilitychange listener effect and initial fetch effect) to satisfy the linter
and avoid stale-closure issues.

145-151: Move KNOWN_TYPES and KnownType to module scope.

Both are declared inside the component body, meaning a new array and type reference are created on every render. Neither depends on component state or props.

♻️ Proposed refactor
+const KNOWN_TYPES = ['SYSTEM', 'ACHIEVEMENT', 'ARTICLE', 'SHOP'] as const;
+type KnownType = (typeof KNOWN_TYPES)[number];

 export function NotificationBell() {
   ...
-  const KNOWN_TYPES = ['SYSTEM', 'ACHIEVEMENT', 'ARTICLE', 'SHOP'] as const;
-  type KnownType = (typeof KNOWN_TYPES)[number];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/components/header/NotificationBell.tsx` around lines 145 - 151,
KNOWN_TYPES and KnownType are declared inside the component causing repeated
allocations on every render; move them to module scope so they are created once.
Extract the const KNOWN_TYPES and the type alias KnownType out of the component
body (above the component file's default export) and keep
getSafeNotificationType (which can remain in the module or component)
referencing those top-level symbols so renders no longer recreate the
array/type.
frontend/app/[locale]/blog/category/[category]/page.tsx (1)

37-55: categoriesQuery duplicates the cached query in [locale]/layout.tsx — consider reusing getCachedBlogCategories.

The GROQ query at lines 37-42 and the raw client.fetch call at line 53 are identical to the query inside getCachedBlogCategories defined in frontend/app/[locale]/layout.tsx. If getCachedBlogCategories were extracted to a shared module (e.g., lib/blog/categories.ts), this page could reuse the cached result instead of issuing a separate Sanity call.

With revalidate = 3600, this only fires at most once per hour, so the runtime impact is low — but extracting the shared function keeps the cache key and TTL consistent across call sites.

♻️ Proposed refactor — extract shared cached query
// lib/blog/categories.ts (new file)
+import { unstable_cache } from 'next/cache';
+import groq from 'groq';
+import { client } from '@/client';
+
+export type Category = { _id: string; title: string };
+
+export const getCachedBlogCategories = unstable_cache(
+  async () =>
+    client.fetch<Category[]>(groq`
+      *[_type == "category"] | order(orderRank asc) { _id, title }
+    `),
+  ['blog-categories'],
+  { revalidate: 3600, tags: ['blog-categories'] }
+);

Then in category/page.tsx:

-import { client } from '@/client';
+import { getCachedBlogCategories } from '@/lib/blog/categories';
 ...
-const categoriesQuery = groq`...`;
 ...
-  const categories: Category[] = await client.fetch(categoriesQuery);
+  const categories = await getCachedBlogCategories();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/app/`[locale]/blog/category/[category]/page.tsx around lines 37 -
55, The categories GROQ query and direct client.fetch in BlogCategoryPage
duplicate the cached logic in getCachedBlogCategories; refactor by extracting
getCachedBlogCategories into a shared module (e.g., lib/blog/categories.ts) and
replace the local categoriesQuery + client.fetch usage in BlogCategoryPage with
a call to getCachedBlogCategories(locale) (keep using Category[] and
matchedCategory lookup via slugify(item.title)), ensuring the shared function
preserves the same cache TTL (revalidate) and export name so both this page and
the existing getCachedBlogCategories caller use the same cached result.
frontend/app/[locale]/blog/page.tsx (1)

75-82: Duplicate categories fetch — same as the one cached in [locale]/layout.tsx.

Same DRY issue as in category/[category]/page.tsx: the identical GROQ query is issued via a raw client.fetch instead of reusing getCachedBlogCategories. Extracting it to a shared cached function (see comment in category/[category]/page.tsx) would apply here too.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/app/`[locale]/blog/page.tsx around lines 75 - 82, Replace the
duplicate raw client.fetch GROQ call that populates the local `categories`
variable with a call to the shared cached helper `getCachedBlogCategories` (the
same cache used by `[locale]/layout.tsx`), i.e. remove the
`client.fetch(groq`...`)` usage in `page.tsx` and call
`getCachedBlogCategories()` instead so the query is centralized and cached;
ensure the returned value is assigned to `categories` and import
`getCachedBlogCategories` where needed.
frontend/app/[locale]/blog/[slug]/page.tsx (1)

19-38: generateMetadata issues a separate Sanity fetch that duplicates data fetched by PostDetails.

Since the Sanity client sends POST requests, Next.js's native fetch deduplication won't merge these two calls within the same ISR render pass. The impact is one extra origin hit every 3600 s per slug — acceptable for now, but worth tracking if Sanity origin quotas become a concern.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/app/`[locale]/blog/[slug]/page.tsx around lines 19 - 38,
generateMetadata currently repeats the Sanity fetch done by PostDetails; extract
the Sanity query into a single shared helper (e.g., getPostBySlug or
fetchPostWithCache) and have both generateMetadata and PostDetails call that
helper; to prevent duplicate origin calls within the same render, wrap that
helper with Next/React's cache() (import cache from 'react' or 'next/cache') so
repeated calls with the same slug/locale are deduplicated, and update
generateMetadata and PostDetails to call the new helper instead of duplicating
the client.fetch logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/app/`[locale]/blog/page.tsx:
- Line 9: The page currently exports revalidate = 3600 but unconditionally reads
searchParams in the Server Component (causing full dynamic rendering), so move
the author-filter read into a new Client Component and stop accessing
searchParams in the server page; create an AuthorFilterShell client component
that uses useSearchParams() (and returns null when ?author= is present) and
renders BlogPageHeader when no author filter exists, wrap it in Suspense from
the server page, and remove the unconditional searchParams access from the page
component so the revalidate export becomes effective again.

---

Nitpick comments:
In `@frontend/app/`[locale]/blog/[slug]/page.tsx:
- Around line 19-38: generateMetadata currently repeats the Sanity fetch done by
PostDetails; extract the Sanity query into a single shared helper (e.g.,
getPostBySlug or fetchPostWithCache) and have both generateMetadata and
PostDetails call that helper; to prevent duplicate origin calls within the same
render, wrap that helper with Next/React's cache() (import cache from 'react' or
'next/cache') so repeated calls with the same slug/locale are deduplicated, and
update generateMetadata and PostDetails to call the new helper instead of
duplicating the client.fetch logic.

In `@frontend/app/`[locale]/blog/category/[category]/page.tsx:
- Around line 37-55: The categories GROQ query and direct client.fetch in
BlogCategoryPage duplicate the cached logic in getCachedBlogCategories; refactor
by extracting getCachedBlogCategories into a shared module (e.g.,
lib/blog/categories.ts) and replace the local categoriesQuery + client.fetch
usage in BlogCategoryPage with a call to getCachedBlogCategories(locale) (keep
using Category[] and matchedCategory lookup via slugify(item.title)), ensuring
the shared function preserves the same cache TTL (revalidate) and export name so
both this page and the existing getCachedBlogCategories caller use the same
cached result.

In `@frontend/app/`[locale]/blog/page.tsx:
- Around line 75-82: Replace the duplicate raw client.fetch GROQ call that
populates the local `categories` variable with a call to the shared cached
helper `getCachedBlogCategories` (the same cache used by `[locale]/layout.tsx`),
i.e. remove the `client.fetch(groq`...`)` usage in `page.tsx` and call
`getCachedBlogCategories()` instead so the query is centralized and cached;
ensure the returned value is assigned to `categories` and import
`getCachedBlogCategories` where needed.

In `@frontend/components/header/NotificationBell.tsx`:
- Around line 80-91: The effect hooks in NotificationBell reference the local
function fetchNotifications without listing it in dependencies, triggering
exhaustive-deps warnings; wrap fetchNotifications in useCallback (e.g., const
fetchNotifications = useCallback(..., [/* stable deps or none */])) so its
identity is stable, then include fetchNotifications in the useEffect dependency
arrays (the visibilitychange listener effect and initial fetch effect) to
satisfy the linter and avoid stale-closure issues.
- Around line 145-151: KNOWN_TYPES and KnownType are declared inside the
component causing repeated allocations on every render; move them to module
scope so they are created once. Extract the const KNOWN_TYPES and the type alias
KnownType out of the component body (above the component file's default export)
and keep getSafeNotificationType (which can remain in the module or component)
referencing those top-level symbols so renders no longer recreate the
array/type.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2c77e2a and 828b508.

⛔ Files ignored due to path filters (1)
  • frontend/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (11)
  • frontend/actions/notifications.ts
  • frontend/app/[locale]/blog/[slug]/PostDetails.tsx
  • frontend/app/[locale]/blog/[slug]/page.tsx
  • frontend/app/[locale]/blog/category/[category]/page.tsx
  • frontend/app/[locale]/blog/page.tsx
  • frontend/app/[locale]/layout.tsx
  • frontend/app/api/blog-author/route.ts
  • frontend/app/layout.tsx
  • frontend/client.ts
  • frontend/components/header/NotificationBell.tsx
  • frontend/package.json
💤 Files with no reviewable changes (2)
  • frontend/package.json
  • frontend/actions/notifications.ts

Comment thread frontend/app/[locale]/blog/page.tsx
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
frontend/app/[locale]/blog/page.tsx (1)

83-83: ⚠️ Potential issue | 🟠 Major

revalidate = 3600 is still defeated — BlogPageHeader calls useSearchParams() without a Suspense boundary.

The direct searchParams access in the server component was correctly removed, but BlogPageHeader (see frontend/components/blog/BlogPageHeader.tsx lines 9–24) internally calls useSearchParams(). During static rendering, the entire page is deopted into client-side rendering by useSearchParams when there is no Suspense boundary that caught it — useSearchParams() causes the tree up to the closest Suspense boundary to be client-side rendered. During production builds, a static page that calls useSearchParams from a Client Component must be wrapped in a Suspense boundary, otherwise the build fails.

The export const revalidate = 3600 on line 9 will have no practical effect until BlogPageHeader is wrapped in <Suspense>.

🛡️ Proposed fix
+import { Suspense } from 'react';
 ...
-        <BlogPageHeader title={t('title')} subtitle={t('subtitle')} />
+        <Suspense fallback={null}>
+          <BlogPageHeader title={t('title')} subtitle={t('subtitle')} />
+        </Suspense>

Using fallback={null} is acceptable here since BlogPageHeader already returns null when an author filter is active — there is no visible loading state to preserve. Alternatively, you can pass a skeleton or the header markup directly as the fallback so the static shell always shows a header.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/app/`[locale]/blog/page.tsx at line 83, BlogPageHeader internally
calls useSearchParams which forces the tree to client render unless caught by a
Suspense boundary, so the export const revalidate = 3600 is ineffective; wrap
the BlogPageHeader usage in the page component inside a React.Suspense (e.g.,
<Suspense fallback={null}> around <BlogPageHeader ... />) so the header's client
rendering is isolated and the page can remain statically revalidated, keeping
the fallback minimal (fallback={null} or a header skeleton) as appropriate.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@frontend/app/`[locale]/blog/page.tsx:
- Line 83: BlogPageHeader internally calls useSearchParams which forces the tree
to client render unless caught by a Suspense boundary, so the export const
revalidate = 3600 is ineffective; wrap the BlogPageHeader usage in the page
component inside a React.Suspense (e.g., <Suspense fallback={null}> around
<BlogPageHeader ... />) so the header's client rendering is isolated and the
page can remain statically revalidated, keeping the fallback minimal
(fallback={null} or a header skeleton) as appropriate.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 828b508 and 7f80727.

📒 Files selected for processing (1)
  • frontend/app/[locale]/blog/page.tsx

@ViktorSvertoka ViktorSvertoka merged commit 4dd23b2 into develop Feb 25, 2026
11 checks passed
@ViktorSvertoka ViktorSvertoka deleted the perf/reduce-vercel-runtime-costs branch February 25, 2026 23:57
ViktorSvertoka added a commit that referenced this pull request Feb 26, 2026
* (SP: 3) [Backend] add Nova Poshta shipping foundation + checkout persistence + async label workflow (#364)

* (SP: 2) [Frontend] Reduce Vercel variable costs via caching and analytics cleanup (#367)

* perf(vercel): cut runtime costs via notification, blog cache, and analytics changes

* perf(blog): remove server searchParams usage to preserve ISR

* fix(build): align Netlify Node version and remove SpeedInsights import

* chore(release): bump version to 1.0.4

---------

Co-authored-by: Liudmyla Sovetovs <milkaegik@gmail.com>
ViktorSvertoka added a commit that referenced this pull request Feb 26, 2026
* (SP: 3) [Backend] add Nova Poshta shipping foundation + checkout persistence + async label workflow (#364)

* (SP: 2) [Frontend] Reduce Vercel variable costs via caching and analytics cleanup (#367)

* perf(vercel): cut runtime costs via notification, blog cache, and analytics changes

* perf(blog): remove server searchParams usage to preserve ISR

* fix(build): align Netlify Node version and remove SpeedInsights import

* chore(release): bump version to 1.0.4

* (SP: 2) [Frontend] Remove [locale] layout force-dynamic and move auth to client-side (#370)

* refactor(frontend): remove locale layout dynamic auth and move header auth client-side

* fix(frontend): prevent stale auth responses in useAuth and remove redundant dashboard dynamic layout

* (SP: 2) [Frontend] Reduce auth overhead and sync auth state across tabs (#372)

* refactor(frontend): remove locale layout dynamic auth and move header auth client-side

* fix(frontend): prevent stale auth responses in useAuth and remove redundant dashboard dynamic layout

* feat(frontend): sync auth state across tabs via BroadcastChannel

* (SP: 2) [Frontend] Quizzes page ISR + client-side progress + GitHub stars cache (#371)

* perf(quiz-flow): move quiz progress to client-side fetch, enable ISR for quizzes page

- Move user progress fetch from SSR to client-side API (/api/quiz/progress)
- Remove force-dynamic and getCurrentUser() from quizzes page
- Add revalidate=300 for ISR caching
- Use window.history.replaceState for tab URL sync (avoid Next.js navigation)
- Add forceMount to TabsContent to prevent layout shift on tab switch
- Fix nested <main> — use <section> inside DynamicGridBackground
- Cache GitHub stars count in sessionStorage to avoid refetch + re-animation

* perf: replace useRef with useState lazy initializer in GitHubStarButton

Fixes React 19 react-hooks/refs ESLint error — useRef.current cannot
be read during render. Uses useState(getStoredStars) to capture the
sessionStorage value once on mount instead.

* fix: stop star icon trembling on hover in GitHubStarButton

* (SP: 1) [Frontend] Fix quiz timer flash and card layout shift on quizzes page (#373)

* perf(quiz-flow): move quiz progress to client-side fetch, enable ISR for quizzes page

- Move user progress fetch from SSR to client-side API (/api/quiz/progress)
- Remove force-dynamic and getCurrentUser() from quizzes page
- Add revalidate=300 for ISR caching
- Use window.history.replaceState for tab URL sync (avoid Next.js navigation)
- Add forceMount to TabsContent to prevent layout shift on tab switch
- Fix nested <main> — use <section> inside DynamicGridBackground
- Cache GitHub stars count in sessionStorage to avoid refetch + re-animation

* perf: replace useRef with useState lazy initializer in GitHubStarButton

Fixes React 19 react-hooks/refs ESLint error — useRef.current cannot
be read during render. Uses useState(getStoredStars) to capture the
sessionStorage value once on mount instead.

* fix: stop star icon trembling on hover in GitHubStarButton

* fix: eliminate quiz timer flash on language switch

Remove Suspense boundary (loading.tsx) that unmounted QuizContainer
during locale navigation. Synchronous session restore via useReducer
lazy initializer and correct timer initialization via useState lazy
initializer prevent any visible state reset on language switch

* fix: replace quiz card layout shift with skeleton grid during progress load

* chore(release): v1.0.5

---------

Co-authored-by: Liudmyla Sovetovs <milkaegik@gmail.com>
Co-authored-by: Lesia Soloviova <106915140+LesiaUKR@users.noreply.github.com>
ViktorSvertoka added a commit that referenced this pull request Feb 27, 2026
* (SP: 3) [Backend] add Nova Poshta shipping foundation + checkout persistence + async label workflow (#364)

* (SP: 2) [Frontend] Reduce Vercel variable costs via caching and analytics cleanup (#367)

* perf(vercel): cut runtime costs via notification, blog cache, and analytics changes

* perf(blog): remove server searchParams usage to preserve ISR

* fix(build): align Netlify Node version and remove SpeedInsights import

* chore(release): bump version to 1.0.4

* (SP: 2) [Frontend] Remove [locale] layout force-dynamic and move auth to client-side (#370)

* refactor(frontend): remove locale layout dynamic auth and move header auth client-side

* fix(frontend): prevent stale auth responses in useAuth and remove redundant dashboard dynamic layout

* (SP: 2) [Frontend] Reduce auth overhead and sync auth state across tabs (#372)

* refactor(frontend): remove locale layout dynamic auth and move header auth client-side

* fix(frontend): prevent stale auth responses in useAuth and remove redundant dashboard dynamic layout

* feat(frontend): sync auth state across tabs via BroadcastChannel

* (SP: 2) [Frontend] Quizzes page ISR + client-side progress + GitHub stars cache (#371)

* perf(quiz-flow): move quiz progress to client-side fetch, enable ISR for quizzes page

- Move user progress fetch from SSR to client-side API (/api/quiz/progress)
- Remove force-dynamic and getCurrentUser() from quizzes page
- Add revalidate=300 for ISR caching
- Use window.history.replaceState for tab URL sync (avoid Next.js navigation)
- Add forceMount to TabsContent to prevent layout shift on tab switch
- Fix nested <main> — use <section> inside DynamicGridBackground
- Cache GitHub stars count in sessionStorage to avoid refetch + re-animation

* perf: replace useRef with useState lazy initializer in GitHubStarButton

Fixes React 19 react-hooks/refs ESLint error — useRef.current cannot
be read during render. Uses useState(getStoredStars) to capture the
sessionStorage value once on mount instead.

* fix: stop star icon trembling on hover in GitHubStarButton

* (SP: 1) [Frontend] Fix quiz timer flash and card layout shift on quizzes page (#373)

* perf(quiz-flow): move quiz progress to client-side fetch, enable ISR for quizzes page

- Move user progress fetch from SSR to client-side API (/api/quiz/progress)
- Remove force-dynamic and getCurrentUser() from quizzes page
- Add revalidate=300 for ISR caching
- Use window.history.replaceState for tab URL sync (avoid Next.js navigation)
- Add forceMount to TabsContent to prevent layout shift on tab switch
- Fix nested <main> — use <section> inside DynamicGridBackground
- Cache GitHub stars count in sessionStorage to avoid refetch + re-animation

* perf: replace useRef with useState lazy initializer in GitHubStarButton

Fixes React 19 react-hooks/refs ESLint error — useRef.current cannot
be read during render. Uses useState(getStoredStars) to capture the
sessionStorage value once on mount instead.

* fix: stop star icon trembling on hover in GitHubStarButton

* fix: eliminate quiz timer flash on language switch

Remove Suspense boundary (loading.tsx) that unmounted QuizContainer
during locale navigation. Synchronous session restore via useReducer
lazy initializer and correct timer initialization via useState lazy
initializer prevent any visible state reset on language switch

* fix: replace quiz card layout shift with skeleton grid during progress load

* chore(release): v1.0.5

* (SP: 3)[Shop][DB] Reduce Neon compute: throttle janitor + relax checkout polling + add sweep indexes (#375)

* (SP: 3) [Backend] add internal janitor (jobs 1-4), claim/lease + runbook (G0-G6)

* (SP: 3) [Backend] add provider selector, fix payments gating, i18n checkout errors

* Add shop category images to public

* (SP: 3) [Shop][Monobank] I1 structured logging: codes + logging safety checks

* (SP: 3) [Shop][Monobank] Fail-closed non-browser origin posture for webhook + janitor (ORIGIN_BLOCKED)

* (SP: 3) [Shop][Monobank] [Shop][Monobank] J gate: add orders status ownership test and pass all pre-prod invariants

* (SP: 3) [Shop][Monobank]  review fixes (tests, logging, success UI)

* (SP: 1) [Shop][Monobank] Tighten webhook log-code typing; harden DB tests; minor security/log/UI cleanups

* (SP: 1) [Shop][Monobank] harden Monobank webhook (origin/PII-safe logs) and remove duplicate sha256 hashing

* (SP: 1) [Cart] adding route for user orders to cart page

* (SP: 1) [Cart] fix after review cart mpage and adding index for orders

* (SP: 1) [Cart] Fix cart orders summary auth rendering and return totalCount for orders badge

* (SP: 1) [Cart] remove console.warn from CartPageClient to satisfy monobank logging safety invariant, namespace localStorage cart by user and reset on auth change

* (SP: 1) [Cart] rehydrate per cartOwnerId (remove didHydrate coupling)

* (SP: 2)[Backend] shop/shipping schema migrations foundation

* (SP: 2)[Backend] shop/shipping public routes + np cache + sync

* (SP: 2)[Backend] shop/shipping: shipping persistence + currency policy

* (SP: 2)[Backend] shop/shipping: webhook apply + psp fields + enqueue shipping

* (SP: 2)[Backend] shop/shipping: shipments worker + internal run + np mock

* (SP: 2)[Backend] shop/shipping: admin+ui shipping actions

* (SP: 2)[Backend] shop/shipping: retention + log sanitizer + metrics

* (SP: 1)[Backend] stabilize Monobank janitor (job1/job3) and fix failing apply-outcomes tests

* (SP: 1) [db]: add shop shipping core migration

* (SP: 1) [FIX] resolve merge artifacts in order details page

* (SP: 1) [FIX] apply post-review fixes for shipping and admin flows

* (SP: 1) [FIX] align cart shipping imports (localeToCountry + availability reason code)

* (SP: 1) [FIX] hard-block checkout when shipping disabled + i18n reason mapping

* (SP: 1) [FIX] harden webhook enqueue + shipping worker + NP catalog + cart fail-closed

* (SP: 1) [FIX] Initialize shippingMethodsLoading to true to avoid premature checkout.

* (SP: 1) [FIX] migration 17

* (SP: 1) [DB] migrarion to testind DB and adjusting tests

* (SP: 1)[DB] slow down restock janitor + enforce prod interval floor

* (SP: 1) [DB] add order status lite view (opt-in) + instrumentation

* (SP: 1) [DB] replace checkout success router.refresh polling with backoff API polling

* (SP: 1) [DB] throttle sessions activity heartbeat + use count(*) (PK invariant)

* (SP: 1)[DB] enforce production min intervals for internal shipping jobs

* (SP: 1) [DB] add minimal partial indexes for orders sweeps + rollout notes

* (SP: 1) [DB] refactor sweep claim step to FOR UPDATE SKIP LOCKED batching

* (SP: 1)[DB]: slow janitor schedule to every 30 minutes

* (SP: 1)[DB] increase polling delays for MonobankRedirectStatus

* (SP: 1)[FIX] harden webhooks + fix SSR hydration + janitor/np gates + sweeps refactor

* (SP: 1)[FIX] harden shipping enqueue gating + apply NP interval floor

---------

Co-authored-by: Liudmyla Sovetovs <milkaegik@gmail.com>
Co-authored-by: Lesia Soloviova <106915140+LesiaUKR@users.noreply.github.com>
ViktorSvertoka added a commit that referenced this pull request Mar 1, 2026
* (SP: 3) [Backend] add Nova Poshta shipping foundation + checkout persistence + async label workflow (#364)

* (SP: 2) [Frontend] Reduce Vercel variable costs via caching and analytics cleanup (#367)

* perf(vercel): cut runtime costs via notification, blog cache, and analytics changes

* perf(blog): remove server searchParams usage to preserve ISR

* fix(build): align Netlify Node version and remove SpeedInsights import

* chore(release): bump version to 1.0.4

* (SP: 2) [Frontend] Remove [locale] layout force-dynamic and move auth to client-side (#370)

* refactor(frontend): remove locale layout dynamic auth and move header auth client-side

* fix(frontend): prevent stale auth responses in useAuth and remove redundant dashboard dynamic layout

* (SP: 2) [Frontend] Reduce auth overhead and sync auth state across tabs (#372)

* refactor(frontend): remove locale layout dynamic auth and move header auth client-side

* fix(frontend): prevent stale auth responses in useAuth and remove redundant dashboard dynamic layout

* feat(frontend): sync auth state across tabs via BroadcastChannel

* (SP: 2) [Frontend] Quizzes page ISR + client-side progress + GitHub stars cache (#371)

* perf(quiz-flow): move quiz progress to client-side fetch, enable ISR for quizzes page

- Move user progress fetch from SSR to client-side API (/api/quiz/progress)
- Remove force-dynamic and getCurrentUser() from quizzes page
- Add revalidate=300 for ISR caching
- Use window.history.replaceState for tab URL sync (avoid Next.js navigation)
- Add forceMount to TabsContent to prevent layout shift on tab switch
- Fix nested <main> — use <section> inside DynamicGridBackground
- Cache GitHub stars count in sessionStorage to avoid refetch + re-animation

* perf: replace useRef with useState lazy initializer in GitHubStarButton

Fixes React 19 react-hooks/refs ESLint error — useRef.current cannot
be read during render. Uses useState(getStoredStars) to capture the
sessionStorage value once on mount instead.

* fix: stop star icon trembling on hover in GitHubStarButton

* (SP: 1) [Frontend] Fix quiz timer flash and card layout shift on quizzes page (#373)

* perf(quiz-flow): move quiz progress to client-side fetch, enable ISR for quizzes page

- Move user progress fetch from SSR to client-side API (/api/quiz/progress)
- Remove force-dynamic and getCurrentUser() from quizzes page
- Add revalidate=300 for ISR caching
- Use window.history.replaceState for tab URL sync (avoid Next.js navigation)
- Add forceMount to TabsContent to prevent layout shift on tab switch
- Fix nested <main> — use <section> inside DynamicGridBackground
- Cache GitHub stars count in sessionStorage to avoid refetch + re-animation

* perf: replace useRef with useState lazy initializer in GitHubStarButton

Fixes React 19 react-hooks/refs ESLint error — useRef.current cannot
be read during render. Uses useState(getStoredStars) to capture the
sessionStorage value once on mount instead.

* fix: stop star icon trembling on hover in GitHubStarButton

* fix: eliminate quiz timer flash on language switch

Remove Suspense boundary (loading.tsx) that unmounted QuizContainer
during locale navigation. Synchronous session restore via useReducer
lazy initializer and correct timer initialization via useState lazy
initializer prevent any visible state reset on language switch

* fix: replace quiz card layout shift with skeleton grid during progress load

* chore(release): v1.0.5

* (SP: 3)[Shop][DB] Reduce Neon compute: throttle janitor + relax checkout polling + add sweep indexes (#375)

* (SP: 3) [Backend] add internal janitor (jobs 1-4), claim/lease + runbook (G0-G6)

* (SP: 3) [Backend] add provider selector, fix payments gating, i18n checkout errors

* Add shop category images to public

* (SP: 3) [Shop][Monobank] I1 structured logging: codes + logging safety checks

* (SP: 3) [Shop][Monobank] Fail-closed non-browser origin posture for webhook + janitor (ORIGIN_BLOCKED)

* (SP: 3) [Shop][Monobank] [Shop][Monobank] J gate: add orders status ownership test and pass all pre-prod invariants

* (SP: 3) [Shop][Monobank]  review fixes (tests, logging, success UI)

* (SP: 1) [Shop][Monobank] Tighten webhook log-code typing; harden DB tests; minor security/log/UI cleanups

* (SP: 1) [Shop][Monobank] harden Monobank webhook (origin/PII-safe logs) and remove duplicate sha256 hashing

* (SP: 1) [Cart] adding route for user orders to cart page

* (SP: 1) [Cart] fix after review cart mpage and adding index for orders

* (SP: 1) [Cart] Fix cart orders summary auth rendering and return totalCount for orders badge

* (SP: 1) [Cart] remove console.warn from CartPageClient to satisfy monobank logging safety invariant, namespace localStorage cart by user and reset on auth change

* (SP: 1) [Cart] rehydrate per cartOwnerId (remove didHydrate coupling)

* (SP: 2)[Backend] shop/shipping schema migrations foundation

* (SP: 2)[Backend] shop/shipping public routes + np cache + sync

* (SP: 2)[Backend] shop/shipping: shipping persistence + currency policy

* (SP: 2)[Backend] shop/shipping: webhook apply + psp fields + enqueue shipping

* (SP: 2)[Backend] shop/shipping: shipments worker + internal run + np mock

* (SP: 2)[Backend] shop/shipping: admin+ui shipping actions

* (SP: 2)[Backend] shop/shipping: retention + log sanitizer + metrics

* (SP: 1)[Backend] stabilize Monobank janitor (job1/job3) and fix failing apply-outcomes tests

* (SP: 1) [db]: add shop shipping core migration

* (SP: 1) [FIX] resolve merge artifacts in order details page

* (SP: 1) [FIX] apply post-review fixes for shipping and admin flows

* (SP: 1) [FIX] align cart shipping imports (localeToCountry + availability reason code)

* (SP: 1) [FIX] hard-block checkout when shipping disabled + i18n reason mapping

* (SP: 1) [FIX] harden webhook enqueue + shipping worker + NP catalog + cart fail-closed

* (SP: 1) [FIX] Initialize shippingMethodsLoading to true to avoid premature checkout.

* (SP: 1) [FIX] migration 17

* (SP: 1) [DB] migrarion to testind DB and adjusting tests

* (SP: 1)[DB] slow down restock janitor + enforce prod interval floor

* (SP: 1) [DB] add order status lite view (opt-in) + instrumentation

* (SP: 1) [DB] replace checkout success router.refresh polling with backoff API polling

* (SP: 1) [DB] throttle sessions activity heartbeat + use count(*) (PK invariant)

* (SP: 1)[DB] enforce production min intervals for internal shipping jobs

* (SP: 1) [DB] add minimal partial indexes for orders sweeps + rollout notes

* (SP: 1) [DB] refactor sweep claim step to FOR UPDATE SKIP LOCKED batching

* (SP: 1)[DB]: slow janitor schedule to every 30 minutes

* (SP: 1)[DB] increase polling delays for MonobankRedirectStatus

* (SP: 1)[FIX] harden webhooks + fix SSR hydration + janitor/np gates + sweeps refactor

* (SP: 1)[FIX] harden shipping enqueue gating + apply NP interval floor

* (SP: 3) [SHOP] audit-driven e2e purchase readiness hardening (events, notifications, consent, returns) (#378)

* (SP:3)[SHOP] add canonical payment/shipping/admin audit tables + dedupe helper with flagged atomic dual-write

* (SP: 2)[SHOP] add INTL quote flow (request/offer/accept/decline), payment-init gate, and quote expiry/timeout sweeps

* (SP: 3)[SHOP] introduce outbox-driven notifications with projector + worker (phase 3)

* (SP: 3)[SHOP] add minimal returns/RMA lifecycle with atomic audit + canonical events (phase 4)

* (SP: 3)[SHOP] enforce guest status-token lite-only access and audit token usage (phase 5)

* (SP: 3)[SHOP] centralize transition guards and enforce across admin/webhook/worker flows (phase 6)

:wq
n

* (SP:3)[SHOP]: enforce DATABASE_URL_LOCAL preflight + deterministic vitest config

* (SP:3)[SHOP]: require canonical events in prod (fail-fast)

* (SP:3)[SHOP]: implement notifications transport with retries + DLQ

* (SP:3)[SHOP]: persist checkout legal consent artifact

* (SP:1)[SHOP] add migration 0025 for consent + events + audit + prices

* (SP:1)[SHOP] emit shipping_events for shipment worker transitions

* (SP:2)[SHOP] audit admin product mutations (deduped)

* (SP:2)[SHOP] make Monobank webhook retryable on transient apply failures

* (SP:3) [SHOP] enforce guest status-token scopes across order actions

* (SP:1) [SHOP] make product_prices the only write authority

* (SP:1) [SHOP]  add Playwright e2e smoke suite (local DB only)

* (SP:3) [SHOP] explicitly reject exchanges (EXCHANGES_NOT_SUPPORTED)

* (SP: 3)[FIX] harden audit + workers/tests; fix transitions/restock + webhook perf

* (SP: 3)[FIX] harden local-db test safety and tighten shop reliability guards

* (SP: 3)[FIX] fail-closed admin audit, refine shipments-worker outcomes/metrics, tighten quote+tests

* (SP: 1)[FIX] harden shipments-worker claiming/leases and make audit+quote/test paths resilient

* (SP: 1)[FIX] harden quote request errors/logging and sanitize requestId; document best-effort delete audit

* feat(ui): add devops/cloud category icons and styles (#379)

* chore(release): prepare v1.0.6 changelog

* chore: bump version to 1.0.6

---------

Co-authored-by: Liudmyla Sovetovs <milkaegik@gmail.com>
Co-authored-by: Lesia Soloviova <106915140+LesiaUKR@users.noreply.github.com>
ViktorSvertoka added a commit that referenced this pull request Mar 1, 2026
* (SP: 3) [Backend] add Nova Poshta shipping foundation + checkout persistence + async label workflow (#364)

* (SP: 2) [Frontend] Reduce Vercel variable costs via caching and analytics cleanup (#367)

* perf(vercel): cut runtime costs via notification, blog cache, and analytics changes

* perf(blog): remove server searchParams usage to preserve ISR

* fix(build): align Netlify Node version and remove SpeedInsights import

* chore(release): bump version to 1.0.4

* (SP: 2) [Frontend] Remove [locale] layout force-dynamic and move auth to client-side (#370)

* refactor(frontend): remove locale layout dynamic auth and move header auth client-side

* fix(frontend): prevent stale auth responses in useAuth and remove redundant dashboard dynamic layout

* (SP: 2) [Frontend] Reduce auth overhead and sync auth state across tabs (#372)

* refactor(frontend): remove locale layout dynamic auth and move header auth client-side

* fix(frontend): prevent stale auth responses in useAuth and remove redundant dashboard dynamic layout

* feat(frontend): sync auth state across tabs via BroadcastChannel

* (SP: 2) [Frontend] Quizzes page ISR + client-side progress + GitHub stars cache (#371)

* perf(quiz-flow): move quiz progress to client-side fetch, enable ISR for quizzes page

- Move user progress fetch from SSR to client-side API (/api/quiz/progress)
- Remove force-dynamic and getCurrentUser() from quizzes page
- Add revalidate=300 for ISR caching
- Use window.history.replaceState for tab URL sync (avoid Next.js navigation)
- Add forceMount to TabsContent to prevent layout shift on tab switch
- Fix nested <main> — use <section> inside DynamicGridBackground
- Cache GitHub stars count in sessionStorage to avoid refetch + re-animation

* perf: replace useRef with useState lazy initializer in GitHubStarButton

Fixes React 19 react-hooks/refs ESLint error — useRef.current cannot
be read during render. Uses useState(getStoredStars) to capture the
sessionStorage value once on mount instead.

* fix: stop star icon trembling on hover in GitHubStarButton

* (SP: 1) [Frontend] Fix quiz timer flash and card layout shift on quizzes page (#373)

* perf(quiz-flow): move quiz progress to client-side fetch, enable ISR for quizzes page

- Move user progress fetch from SSR to client-side API (/api/quiz/progress)
- Remove force-dynamic and getCurrentUser() from quizzes page
- Add revalidate=300 for ISR caching
- Use window.history.replaceState for tab URL sync (avoid Next.js navigation)
- Add forceMount to TabsContent to prevent layout shift on tab switch
- Fix nested <main> — use <section> inside DynamicGridBackground
- Cache GitHub stars count in sessionStorage to avoid refetch + re-animation

* perf: replace useRef with useState lazy initializer in GitHubStarButton

Fixes React 19 react-hooks/refs ESLint error — useRef.current cannot
be read during render. Uses useState(getStoredStars) to capture the
sessionStorage value once on mount instead.

* fix: stop star icon trembling on hover in GitHubStarButton

* fix: eliminate quiz timer flash on language switch

Remove Suspense boundary (loading.tsx) that unmounted QuizContainer
during locale navigation. Synchronous session restore via useReducer
lazy initializer and correct timer initialization via useState lazy
initializer prevent any visible state reset on language switch

* fix: replace quiz card layout shift with skeleton grid during progress load

* chore(release): v1.0.5

* (SP: 3)[Shop][DB] Reduce Neon compute: throttle janitor + relax checkout polling + add sweep indexes (#375)

* (SP: 3) [Backend] add internal janitor (jobs 1-4), claim/lease + runbook (G0-G6)

* (SP: 3) [Backend] add provider selector, fix payments gating, i18n checkout errors

* Add shop category images to public

* (SP: 3) [Shop][Monobank] I1 structured logging: codes + logging safety checks

* (SP: 3) [Shop][Monobank] Fail-closed non-browser origin posture for webhook + janitor (ORIGIN_BLOCKED)

* (SP: 3) [Shop][Monobank] [Shop][Monobank] J gate: add orders status ownership test and pass all pre-prod invariants

* (SP: 3) [Shop][Monobank]  review fixes (tests, logging, success UI)

* (SP: 1) [Shop][Monobank] Tighten webhook log-code typing; harden DB tests; minor security/log/UI cleanups

* (SP: 1) [Shop][Monobank] harden Monobank webhook (origin/PII-safe logs) and remove duplicate sha256 hashing

* (SP: 1) [Cart] adding route for user orders to cart page

* (SP: 1) [Cart] fix after review cart mpage and adding index for orders

* (SP: 1) [Cart] Fix cart orders summary auth rendering and return totalCount for orders badge

* (SP: 1) [Cart] remove console.warn from CartPageClient to satisfy monobank logging safety invariant, namespace localStorage cart by user and reset on auth change

* (SP: 1) [Cart] rehydrate per cartOwnerId (remove didHydrate coupling)

* (SP: 2)[Backend] shop/shipping schema migrations foundation

* (SP: 2)[Backend] shop/shipping public routes + np cache + sync

* (SP: 2)[Backend] shop/shipping: shipping persistence + currency policy

* (SP: 2)[Backend] shop/shipping: webhook apply + psp fields + enqueue shipping

* (SP: 2)[Backend] shop/shipping: shipments worker + internal run + np mock

* (SP: 2)[Backend] shop/shipping: admin+ui shipping actions

* (SP: 2)[Backend] shop/shipping: retention + log sanitizer + metrics

* (SP: 1)[Backend] stabilize Monobank janitor (job1/job3) and fix failing apply-outcomes tests

* (SP: 1) [db]: add shop shipping core migration

* (SP: 1) [FIX] resolve merge artifacts in order details page

* (SP: 1) [FIX] apply post-review fixes for shipping and admin flows

* (SP: 1) [FIX] align cart shipping imports (localeToCountry + availability reason code)

* (SP: 1) [FIX] hard-block checkout when shipping disabled + i18n reason mapping

* (SP: 1) [FIX] harden webhook enqueue + shipping worker + NP catalog + cart fail-closed

* (SP: 1) [FIX] Initialize shippingMethodsLoading to true to avoid premature checkout.

* (SP: 1) [FIX] migration 17

* (SP: 1) [DB] migrarion to testind DB and adjusting tests

* (SP: 1)[DB] slow down restock janitor + enforce prod interval floor

* (SP: 1) [DB] add order status lite view (opt-in) + instrumentation

* (SP: 1) [DB] replace checkout success router.refresh polling with backoff API polling

* (SP: 1) [DB] throttle sessions activity heartbeat + use count(*) (PK invariant)

* (SP: 1)[DB] enforce production min intervals for internal shipping jobs

* (SP: 1) [DB] add minimal partial indexes for orders sweeps + rollout notes

* (SP: 1) [DB] refactor sweep claim step to FOR UPDATE SKIP LOCKED batching

* (SP: 1)[DB]: slow janitor schedule to every 30 minutes

* (SP: 1)[DB] increase polling delays for MonobankRedirectStatus

* (SP: 1)[FIX] harden webhooks + fix SSR hydration + janitor/np gates + sweeps refactor

* (SP: 1)[FIX] harden shipping enqueue gating + apply NP interval floor

* (SP: 3) [SHOP] audit-driven e2e purchase readiness hardening (events, notifications, consent, returns) (#378)

* (SP:3)[SHOP] add canonical payment/shipping/admin audit tables + dedupe helper with flagged atomic dual-write

* (SP: 2)[SHOP] add INTL quote flow (request/offer/accept/decline), payment-init gate, and quote expiry/timeout sweeps

* (SP: 3)[SHOP] introduce outbox-driven notifications with projector + worker (phase 3)

* (SP: 3)[SHOP] add minimal returns/RMA lifecycle with atomic audit + canonical events (phase 4)

* (SP: 3)[SHOP] enforce guest status-token lite-only access and audit token usage (phase 5)

* (SP: 3)[SHOP] centralize transition guards and enforce across admin/webhook/worker flows (phase 6)

:wq
n

* (SP:3)[SHOP]: enforce DATABASE_URL_LOCAL preflight + deterministic vitest config

* (SP:3)[SHOP]: require canonical events in prod (fail-fast)

* (SP:3)[SHOP]: implement notifications transport with retries + DLQ

* (SP:3)[SHOP]: persist checkout legal consent artifact

* (SP:1)[SHOP] add migration 0025 for consent + events + audit + prices

* (SP:1)[SHOP] emit shipping_events for shipment worker transitions

* (SP:2)[SHOP] audit admin product mutations (deduped)

* (SP:2)[SHOP] make Monobank webhook retryable on transient apply failures

* (SP:3) [SHOP] enforce guest status-token scopes across order actions

* (SP:1) [SHOP] make product_prices the only write authority

* (SP:1) [SHOP]  add Playwright e2e smoke suite (local DB only)

* (SP:3) [SHOP] explicitly reject exchanges (EXCHANGES_NOT_SUPPORTED)

* (SP: 3)[FIX] harden audit + workers/tests; fix transitions/restock + webhook perf

* (SP: 3)[FIX] harden local-db test safety and tighten shop reliability guards

* (SP: 3)[FIX] fail-closed admin audit, refine shipments-worker outcomes/metrics, tighten quote+tests

* (SP: 1)[FIX] harden shipments-worker claiming/leases and make audit+quote/test paths resilient

* (SP: 1)[FIX] harden quote request errors/logging and sanitize requestId; document best-effort delete audit

* feat(ui): add devops/cloud category icons and styles (#379)

* chore(release): prepare v1.0.6 changelog

* chore: bump version to 1.0.6

* fix(orders): close missing brace in checkout shipping snapshot try block

* fix(checkout): correct nested try/catch structure for shipping snapshot

* fix(order-status): remove stale responseMode lite branch

---------

Co-authored-by: Liudmyla Sovetovs <milkaegik@gmail.com>
Co-authored-by: Lesia Soloviova <106915140+LesiaUKR@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

performance Performance and efficiency optimizations without functional changes.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant