Commit Graph

251 Commits

Author SHA1 Message Date
cfe090b45c fix(search): left-aligned header, drop the hero card, debounce auto-fix toast
Two unrelated polish items in the VIN search page bundled into one commit
because they touch the same file.

1. Hero card removed. The entire form lived inside a generic
   `rounded-2xl border bg-background p-6 sm:p-8` container on a page that had
   no other content competing for attention — the card added no hierarchy,
   only a frame. The header is also no longer centered: icon sits to the left
   of a left-aligned h2 + subtitle, breaking the AI-default centered hero.

2. I/O/Q auto-correction toast is now debounced. Each keystroke that produced
   a correction fired its own toast, so a user holding the I key or pasting
   "IIO" stacked three toasts on top of each other. Corrections now collect
   in a ref-backed Set and a single consolidated toast fires 400ms after the
   last edit, with cleanup on unmount.

Phase 5/8 of the UX audit follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 20:51:19 +03:00
25300a339a refactor(category-view): extract shared toggle component with a11y
Both the vehicle detail page and the category detail page shipped their own
copy of a three-button view-mode toggle (Grid/Tree/Columns). They diverged
subtly and shared the same accessibility gaps: 28px tap targets, only `title`
attributes for screen readers, no `aria-pressed`, no focus ring.

- New `CategoryViewToggle` (apps/web/src/components/categories/) renders a
  proper `role="group"` segmented control with `aria-pressed`, `aria-label`
  per option, 36px tap targets, and a focus-visible ring.
- Active state is conveyed by an elevated background pill rather than just a
  hover-grey, so the selected mode is legible without color contrast guessing.
- Both pages collapse to a single line: `<CategoryViewToggle … />`.

Phase 4/8 of the UX audit follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 20:49:38 +03:00
34ad77b2a1 feat(category-page): add breadcrumb trail + safer back navigation
Addresses long-term fix from insight cmpifhxfs000lvze9cy6ne0tx: users in
deep category trees had no orientation cue beyond a single back button.

- Breadcrumb derived from the cached /categories/tree/<vehicleId> response.
  When the cache is warm (user came from the vehicle page), the full ancestry
  renders; otherwise it falls back gracefully to "Vehicle → Current".
- Vehicle label fetched via the same query key already used by the vehicle
  page, so the request is deduped.
- Back button now prefers the breadcrumb parent (resolved synchronously from
  cache) over `data.parentId` (which is async). Clicking back before the
  category payload loads no longer dumps the user to the vehicle root.
- H1 placeholder during load swapped from a stale "Kategori Detayı" string
  to an inline pulse — prevents the title from briefly displaying wrong text
  before the real name resolves. Uses a span-based pulse (Skeleton is a div
  and would be invalid HTML inside an h1).

Phase 3/8 of the UX audit follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 20:48:15 +03:00
ae20ca9676 fix(category-tree): meet 44px touch target + single hit area per row
Addresses insight cmpifhxfs000lvze9cy6ne0tx (P1 ux_friction, rage click on
/dashboard/vehicles/<id>/categories/<id>).

Before: each row had two competing click targets — a 20px chevron button and a
separate text button — both calling the same expand handler. Row height was
~28px (py-1.5), well below the 44px mobile guideline. No active/press feedback
made it hard to tell whether a tap registered, triggering rage clicks.

After:
- Whole row is a single button (non-leaf) or Link (leaf). One hit area, no
  ambiguity about what gets the click.
- min-h-[44px], gap-3, px-3 py-2 — meets mobile guideline with breathing room.
- transition-colors + hover + active:bg-accent/80 give immediate tap feedback.
- focus-visible ring for keyboard nav, aria-expanded for screen readers.
- Chevron becomes a decorative span that rotates 90deg on expand instead of
  swapping icons (no extra button), preserving the loading spinner in place.

Phase 2/8 of the UX audit follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 20:46:22 +03:00
3478144c87 fix(dashboard): restore Turkish diacritics in vehicle + category pages
Strings displayed to users were missing Turkish characters (ö, ç, ğ, ş, ı, ü).
Affects vehicle detail header/info card, category detail page, view-mode tooltips,
and the "category not found" empty state in the tree component.

Phase 1/8 of the UX audit follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 20:44:28 +03:00
b4f1b2bd10 feat(ads): wire Meta Pixel for ad attribution
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Pixel ID 1904240520247944 (sase.tr) created on ad account
act_1227112768351770. Lazy-injected like PostHog so it stays off
when VITE_META_PIXEL_ID is unset. Tracks PageView on every route
change, CompleteRegistration on email/Google signup, and
InitiateCheckout on Stripe button click (value in TRY).

Coolify env: set VITE_META_PIXEL_ID=1904240520247944 before next
deploy so it gets baked into the Vite build.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 23:28:40 +03:00
Fusion
5118c8e5e9 feat(FN-415): add locator-stability + hit-target verification tests
Commits merged:
- feat(FN-415): add locator-stability + hit-target verification tests

Files changed:
apps/api/src/telemetry/__tests__/telemetry.spec.ts |  8 +++
 .../src/routes/__tests__/dashboard-search.test.tsx | 67 ++++++++++++++++++++++
 2 files changed, 75 insertions(+)

Fusion-Task-Id: FN-415

Fusion-Task-Lineage: 01e6f3d2-cd2e-4023-b538-ea54926b4f87
2026-05-17 06:17:51 +00:00
Fusion
65daf99b2e chore(payments): remove EFT/bank-transfer method, keep Stripe only
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Removes the EFT (havale) payment surface across API, web, i18n, and docs.
Card payment via Stripe is now the only checkout path.

API (apps/api/src):
- Delete payments/bank-accounts/ module (controller + service + module)
- payments.service: drop EFT methods (createEftPayment, uploadEftReceipt,
  approveEft, rejectEft, getActiveBankInfo, getPendingEftPayments) and the
  BankAccountsService dependency
- payments.controller: drop /payments/eft, /payments/eft/:id/{receipt,approve,reject},
  /payments/bank-info, /payments/pending; keep /payments/me
- payments.module: drop BankAccountsModule import
- admin.service: drop pending EFT counter from getDashboardStats; drop
  getPendingPayments (admin EFT approval list)
- admin.controller: drop /admin/payments/pending endpoint
- admin.service.spec: drop the getPendingPayments + pendingPayments assertions

Web (apps/web/src):
- Delete components/payment/bank-transfer-card.tsx
- Delete routes/dashboard/admin/payments.tsx (EFT approval page); regenerate
  routeTree.gen.ts
- subscription/index.tsx: drop EFT tab from PaymentMethodSection, drop the
  paymentMethod state + handleEftCompleted, simplify ConfirmationCard to the
  Stripe-only path, drop unused imports (Tabs, BankTransferCard, Building2)
- billing.tsx: drop "eft" from method filter chips; keep methodLabels.eft and
  the legacy receipt download so historical EFT records still display
- dashboard.tsx + admin/index.tsx: drop /dashboard/admin/payments from the
  sidebar + admin quick-link grid; trim unused icon imports; KEYS_6 → KEYS_5
  for the now-5-card admin stats skeleton
- messages/{tr,en}.json: strip every EFT-only key under payment.* (eftTransfer,
  eftConfirmationDescription, eftPaid, eftStatus, bank.*, uploadReceipt* etc.)

Schema (apps/api/src/database/schema):
- bankAccounts table + payments.bankAccountId column are kept as-is: legacy
  EFT payments remain visible on the billing page and the schema preserves
  historical records.

Docs:
- README.md: "Stripe (kart) + EFT" → "Stripe (kart)"
- CLAUDE.md: stack table + PaymentsModule row + payments table note + route
  list updated; admin EFT approval route removed

Verification:
- pnpm typecheck: green (api + web + shared + config + ui)
- pnpm lint: green
- pnpm test: 20 web tests pass, 173 api tests pass
2026-05-16 11:03:30 +03:00
Semih
de84d8d267 fix(schema-viewer): drop empty schema panel for image-less BOM overviews
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
PL24's bomOverviewlist widget (PR-code-filtered overview tables like
"Alternatöre genel bakış · Start-Stop modu bulunan araçlar için
[PR:7L6,7L8]") returns parts but no illustration — upstream genuinely
has no schema for these. Previously the 60% left panel rendered the
text "Sema goruntusu bulunamadi", which reads like a failure and
wastes most of the layout for what is actually a complete result.

When schemaPic is null, skip the schema panel entirely and let the
parts list take the full container width. The parts panel already
handles its own header/empty state, so no other adjustment needed.
2026-05-14 20:47:43 +00:00
Semih
47f76e47d2 fix(FN-373): stop stack reset on every render in CategoryGrid
useTranslation returns a fresh `t` each render, so including it in the
useEffect deps caused the effect to re-fire continuously, resetting the
drill-down stack immediately after handleSelect pushed a child level.
Result: clicking a parent category in grid view did nothing — no API
call, no UI change, no navigation. Reproduced via Playwright on
dev.sase.tr.

Drop `t` from the dep array (key is static; locale changes mid-session
are rare and acceptable to render stale until the next prop change).
2026-05-14 20:02:14 +00:00
Fusion
6c28688283 feat(FN-373): grid view drill-down for parent categories (+1 more)
Commits merged:
- chore(FN-373): verify grid drill-down lint + typecheck clean
- fix(FN-373): grid view drill-down for parent categories

Files changed:
.../src/components/categories/category-grid.tsx    | 175 ++++++++++++++++-----
 apps/web/src/messages/en.json                      |   3 +
 apps/web/src/messages/tr.json                      |   3 +
 3 files changed, 138 insertions(+), 43 deletions(-)

Fusion-Task-Id: FN-373
2026-05-14 18:56:08 +00:00
Fusion
0ce68a7b99 feat(FN-368): parts panel loading state, inert unavailable rows, escape hatch (gitea #10)
Commits merged:
- fix(FN-368): parts panel loading state, inert unavailable rows, escape hatch (gitea #10)

Files changed:
.../schema/__tests__/parts-panel.test.tsx          | 151 ++++++++++++++
 apps/web/src/components/schema/parts-panel.tsx     | 230 ++++++++++++---------
 apps/web/src/components/schema/schema-viewer.tsx   |   7 +-
 3 files changed, 291 insertions(+), 97 deletions(-)

Fusion-Task-Id: FN-368
2026-05-14 18:43:20 +00:00
Fusion
e274e02c23 feat(FN-367): add inline retry affordance after VIN decode failure (FN-367, gitea #11)
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Commits merged:
- fix(web): add inline retry affordance after VIN decode failure (FN-367, gitea #11)

Files changed:
apps/api/src/telemetry/__tests__/telemetry.spec.ts |   2 +-
 .../src/routes/__tests__/dashboard-search.test.tsx | 172 +++++++++++++++++++++
 apps/web/src/routes/dashboard/search.tsx           | 145 ++++++++++-------
 3 files changed, 266 insertions(+), 53 deletions(-)

Fusion-Task-Id: FN-367

Fusion-Task-Lineage: 5dccf49f-5c77-4fe4-ae2d-0273016d517d
2026-05-14 13:55:36 +00:00
Fusion
a0deefd4b2 chore(remotion): acknowledge Remotion license on Player instances
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Suppresses the 'license required' console warning on the homepage.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 10:40:57 +00:00
Fusion
5bd5325367 feat(FN-356): add checkout_started PostHog event to subscription checkout flow (+1 more)
Commits merged:
- chore(FN-356): verify checkout_started event is in place
- feat(FN-356): add checkout_started PostHog event to subscription checkout flow

Files changed:
apps/web/src/routes/dashboard/subscription/index.tsx | 4 ++++
 1 file changed, 4 insertions(+)

Fusion-Task-Id: FN-356
2026-05-14 10:00:40 +00:00
Fusion
906eef148d feat(FN-346): add CLS regression test for skeleton plan grid (+1 more)
Commits merged:
- feat(FN-346): add grid-parity integration test for skeleton CLS regression
- feat(FN-346): add CLS regression test for skeleton plan grid

Files changed:
.../subscription/__tests__/cls-regression.test.tsx | 128 +++++++++++++++++++
 .../subscription/__tests__/grid-parity.test.tsx    | 141 +++++++++++++++++++++
 .../src/routes/dashboard/subscription/index.tsx    |   4 +-
 3 files changed, 271 insertions(+), 2 deletions(-)

Fusion-Task-Id: FN-346
2026-05-14 05:09:47 +00:00
Fusion
aaf2e763cb feat(FN-345): fix skeleton layout shift, dialog overflow, and safe-area padding (+1 more)
Commits merged:
- fix(FN-345): add mobile-safe max-width to cancel confirm dialog
- fix(FN-345): fix skeleton layout shift, dialog overflow, and safe-area padding

Files changed:
apps/web/src/routes/dashboard/subscription/index.tsx | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

Fusion-Task-Id: FN-345
2026-05-14 03:05:45 +00:00
Fusion
561b02b3d2 feat(FN-343): remove lingering iyzico references after Stripe migration (+1 more)
Commits merged:
- chore(FN-343): remove lingering iyzico references from docs, config, and scripts
- feat(FN-343): remove lingering iyzico references after Stripe migration

Files changed:
CLAUDE.md                                    | 10 +++++-----
 README.md                                    |  2 +-
 apps/api/src/database/schema/core.ts         |  1 +
 apps/web/src/messages/en.json                |  1 -
 apps/web/src/messages/tr.json                |  1 -
 apps/web/src/routes/dashboard/billing.tsx    |  6 +++---
 docker-compose.coolify.yml                   |  5 ++---
 docs/INDEX.md                                | 27 ++++++++++++-------------
 knowledge.md                                 | 30 ++++++++++++++--------------
 packages/shared/src/constants/error-codes.ts |  1 -
 packages/shared/src/index.ts                 |  1 -
 packages/shared/src/types/payment.ts         | 12 +----------
 scripts/fn342-pw-verify.mjs                  |  2 +-
 scripts/validate-env.sh                      |  3 +--
 14 files changed, 43 insertions(+), 59 deletions(-)

Fusion-Task-Id: FN-343
2026-05-14 02:31:42 +00:00
Sase Dev
02bf7342a1 fix(subscription): recover stepper when a pending checkout is abandoned
Some checks failed
Deploy / Deploy to Production (push) Has been cancelled
A user who picked a plan + brands, clicked the checkout button to
create the pending subscription, then closed the tab without paying
came back to a frozen "Mevcut Plan · Bekliyor" card with no payment
button and no way to edit. Status was "pending", which my conditional
treated as an active subscription and routed away from the stepper.

Frontend
- hasActiveSub narrowed to active|cancelled only. Pending users now
  see the stepper again.
- New effect pre-fills selectedPlanKey, selectedBrandIds,
  billingPeriod from the pending row on first render and jumps
  straight to the payment step.
- New PendingPaymentBanner at the top of the stepper with
  "Odemeye devam et" (scrolls + sets step=payment) and "Vazgec"
  (calls cancel-pending then resets selection).

Backend
- subscriptions.service.create() auto-expires existing pending
  rows for the user before inserting a new one — abandoned checkouts
  no longer accumulate and a fresh attempt with a different plan
  doesn't 409.
- New PATCH /subscriptions/cancel-pending + service method for
  the banner's explicit cancel action.

i18n: subscription.pendingBanner.* (tr/en).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 22:57:13 +00:00
Sase Dev
6a0b36a7a0 feat: replace Iyzico with Stripe + single-page subscription stepper
Subscription/checkout flow rewritten end-to-end. The plan-card "Devam Et"
button silently wiped brand selection on re-click and the actual proceed
button lived offscreen — fixed by collapsing plan/brands/payment/confirm
into a single vertical stepper with one sticky CTA.

Backend
- Stripe Hosted Checkout (`/payments/stripe/checkout`) + webhook
  (`/payments/stripe/webhook`, raw body) replacing the stubbed Iyzico
  module. Webhook activates subscription on `checkout.session.completed`,
  expires the pending subscription on cancel/expire so users can retry.
- New `bank_accounts` table — multiple rows, single `is_active` enforced
  by a partial unique index. Admin CRUD under `/admin/bank-accounts`
  with multipart QR upload to MinIO; atomic `/activate` swap in a
  transaction; `GET /payments/bank-info` returns the active row.
- `payments` gains `stripe_session_id`, `stripe_payment_intent_id`,
  `bank_account_id`. EFT flow now reads the active bank account at
  payment time and stores the FK for reconciliation.
- Env: `IYZICO_*` removed, `STRIPE_*` added (validated by zod schema).
- `main.ts` `rawBody: true` for Stripe signature verification.
- Drizzle 0003 snapshot id collision fixed (VIEW-only migration shared
  prevId with 0002, blocking new generates).

Frontend
- `/dashboard/subscription` rewritten as a 4-step vertical stepper with
  step-aware sticky bottom CTA; plan re-selection is idempotent and
  preserves brand state. `/dashboard/subscription/pay` deleted; Stripe
  returns to the same page via `?stripe=success|cancelled` and the UI
  polls `/subscriptions/me` until the webhook activates the row.
- New components: `bank-transfer-card.tsx` (DB-driven IBAN + Kolay Adres
  + uploaded QR image + receipt upload) and `stripe-checkout-button.tsx`.
- Active subscription view, trial onboarding/urgency banner, downgrade
  and cancel dialogs preserved.
- TR/EN i18n: new `subscription.steps.*`, `subscription.stickyCta.*`,
  `payment.stripe.*`, `payment.bank.*`; provider label updated.

PostHog: `method: "iyzico"` → `"stripe"`; new events `iban_copied`,
`kolay_adres_copied`, `qr_viewed`, `eft_initiated`,
`stripe_redirect_returned`.

Deploy runs `db:migrate` which applies 0004_hot_quicksilver
(additive: new table + nullable columns; safe to apply on prod).
Operator must add `STRIPE_SECRET_KEY`, `STRIPE_PUBLISHABLE_KEY`,
`STRIPE_WEBHOOK_SECRET` to env and create the first
`bank_accounts` row via the admin endpoint before the bank tab works.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 22:42:28 +00:00
Sase Dev
0b6d4bb0a7 feat(analytics): enrich PostHog VIN/parts/subscription events (SASE-PH-001 Faz 0/1 additive subset)
Per sase-posthog-events-prd.md, additive-only pass (no renames, no dead-feature events):

- search.tsx: add search_input_focused, search_input_validation_failed,
  search_paste_detected, search_history_item_selected; track query_source
  (manual|paste|history) and propagate to vin_decoded/success/error; add
  response_time_ms, source, status_code on success/error; add selected_index,
  candidates_count, time_to_select_ms to vin_decode_candidate_selected.
- parts-panel.tsx: add parts_panel_viewed with vehicle_id, category_id,
  parts_count, has_prices, available_groups_count (one-shot per mount).
- subscription/index.tsx: enrich plan_selected (period, from_plan, status),
  checkout_started (from_plan, brands_count, trial_available, status),
  subscription_cancelled (from_plan, billing_period, status_before_cancel,
  days_since_start, had_downgrade_offer).
2026-05-13 22:05:50 +00:00
Sase Dev
5b9af5a6a5 fix(web): hoist hooks above early returns in TrialUrgencyBanner
useEffect and useCallback were declared after three `return null` guards,
so when a user's subscription transitioned into the trial+≤7-days window
React saw more hooks than the previous render and crashed with #310.
Hoisted all hooks above the single guard return; render and dismiss
behavior unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 21:41:16 +00:00
Fusion
c8aac14d56 feat(FN-306): change trial banner threshold to ≤7 days and rename PostHog events
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Deploy / Deploy to Production (push) Has been cancelled
Commits merged:
- feat(FN-306): complete Step 1 — change trial banner threshold to ≤7 days and rename PostHog events

Files changed:
apps/web/src/components/trial-urgency-banner.tsx | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

Fusion-Task-Id: FN-306
2026-05-13 09:00:22 +00:00
Fusion
4cb715f1b4 feat(FN-300): add trial urgency banner to subscription page
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Commits merged:
- feat(FN-300): complete Step 1 — add trial urgency banner to subscription page

Files changed:
.../src/routes/dashboard/subscription/index.tsx    | 107 +++++++++++++++++++++
 1 file changed, 107 insertions(+)

Fusion-Task-Id: FN-300
2026-05-13 08:13:26 +00:00
Fusion
3dd4dc2f77 feat(FN-297): add social proof impression tracking to landing page testimonials (+2 more)
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Commits merged:
- docs(FN-297): update FN-275 audit with social proof event locations
- fix(FN-297): format IntersectionObserver mock in test file
- feat(FN-297): add social proof impression tracking to landing page testimonials

Files changed:
apps/web/src/routes/__tests__/index.test.tsx |   7 ++
 apps/web/src/routes/index.tsx                | 111 +++++++++++++++++++++++++--
 docs/product/funnel-audit-p1-cro-2026-05.md  |  19 ++++-
 3 files changed, 127 insertions(+), 10 deletions(-)

Fusion-Task-Id: FN-297
2026-05-13 07:28:34 +00:00
Fusion
6162a8f787 feat(FN-283): add PostHog events to TrialUrgencyBanner
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Commits merged:
- feat(FN-283): add PostHog events to TrialUrgencyBanner

Files changed:
apps/web/src/components/trial-urgency-banner.tsx | 34 ++++++++++++++++++++++--
 1 file changed, 32 insertions(+), 2 deletions(-)

Fusion-Task-Id: FN-283
2026-05-13 06:41:04 +00:00
Fusion
c0fef5921b feat(FN-282): add social proof impression tracking to testimonial and trust components
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Commits merged:
- feat(FN-282): add social proof impression tracking to testimonial and trust components

Files changed:
apps/web/src/routes/_auth.tsx                      | 42 +++++++++++-
 .../src/routes/dashboard/subscription/index.tsx    | 80 ++++++++++++++++++++--
 2 files changed, 117 insertions(+), 5 deletions(-)

Fusion-Task-Id: FN-282
2026-05-13 06:36:16 +00:00
Fusion
d855b564bf feat(FN-281): add payment_success and payment_failed PostHog events (+1 more)
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Commits merged:
- fix(FN-281): make result search param truly optional to fix typecheck
- feat(FN-281): add payment_success and payment_failed PostHog events

Files changed:
apps/api/package.json                              |    1 +
 apps/api/src/app.module.ts                         |    2 +
 apps/api/src/payments/payments.service.spec.ts     |   99 +-
 apps/api/src/payments/payments.service.ts          |   40 +
 apps/api/src/posthog/posthog.module.ts             |    9 +
 apps/api/src/posthog/posthog.service.ts            |   64 ++
 .../web/src/components/payment/payment-content.tsx |   24 +-
 apps/web/src/routeTree.gen.ts                      | 1110 ++++++++++----------
 apps/web/src/routes/dashboard/subscription/pay.tsx |   24 +-
 packages/config/src/index.ts                       |    4 +
 pnpm-lock.yaml                                     |   30 +
 11 files changed, 827 insertions(+), 580 deletions(-)

Fusion-Task-Id: FN-281
2026-05-13 06:30:58 +00:00
Fusion
7707443be6 feat(FN-278): add yearly_toggle_clicked PostHog events and 4-col skeleton grid
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
- Track yearly_toggle_clicked PostHog event on billing period toggle (monthly/yearly)
- Update skeleton grid from 2-col to 4-col layout with additional skeleton cards
- Add regression tests verifying analytics tracking and skeleton grid rendering

Fusion-Task-Id: FN-278
2026-05-13 05:55:25 +00:00
Fusion
cc2bfbe2b0 feat(FN-211): normalize typography on subscription page
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Commits merged:
- fix(FN-211): normalize typography on subscription page

Files changed:
apps/web/src/routes/dashboard/subscription/index.tsx | 20 +++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)

Fusion-Task-Id: FN-211
2026-05-13 05:30:19 +00:00
Fusion
fa49c20d09 feat(FN-263): normalize typography on subscription page (+2 more)
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Commits merged:
- feat(FN-263): normalize typography on subscription page
- chore(FN-263): apply Biome formatting fix for long className lines
- feat(FN-263): normalize typography on subscription page

Files changed:
.../src/routes/dashboard/subscription/index.tsx    | 116 +++++++++++----------
 1 file changed, 62 insertions(+), 54 deletions(-)

Fusion-Task-Id: FN-263
2026-05-13 04:51:56 +00:00
Fusion
f0bfe6ec8b feat(FN-280): add cancel-save flow PostHog events — cancel_flow_viewed and cancel_save_clicked
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Commits merged:
- feat(FN-280): add cancel-save flow PostHog events — cancel_flow_viewed and cancel_save_clicked

Files changed:
apps/web/src/routes/dashboard/subscription/index.tsx | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-280
2026-05-13 03:58:39 +00:00
Fusion
fb3fa6dff4 feat(FN-277): add @sase/shared resolve alias to vitest configs for both api and web (+1 more)
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Commits merged:
- docs(FN-277): update Pre-Merge QA Gate policy with explicit sub-bullets per spec
- fix(FN-277): add @sase/shared resolve alias to vitest configs for both api and web

Files changed:
.fusion/memory/MEMORY.md  | 14 +++++++++-----
 apps/api/vitest.config.ts |  6 ++++++
 apps/web/vitest.config.ts |  1 +
 3 files changed, 16 insertions(+), 5 deletions(-)

Fusion-Task-Id: FN-277
2026-05-13 02:07:02 +00:00
Fusion
6ae26e7a62 feat(FN-213): a11y audit — fix plan card double-focus trap, trust bar ARIA, color contrast
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Commits merged:
- feat(FN-213): a11y audit — fix plan card double-focus trap, trust bar ARIA, color contrast

Files changed:
apps/web/src/routes/dashboard/subscription/__tests__/index.test.tsx | 1 +
 1 file changed, 1 insertion(+)

Fusion-Task-Id: FN-213
2026-05-13 01:54:30 +00:00
Fusion
e94c1e4267 feat(FN-271): implement a11y fixes (plan card focus, trust bar ARIA, color contrast)
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Commits merged:
- feat(FN-271): complete Step 1 — implement a11y fixes (plan card focus, trust bar ARIA, color contrast)

Files changed:
.../subscription/__tests__/index.test.tsx          | 87 ++++++++++++--------
 .../src/routes/dashboard/subscription/index.tsx    | 94 ++++++++++++++++++----
 2 files changed, 133 insertions(+), 48 deletions(-)

Fusion-Task-Id: FN-271
2026-05-13 00:32:58 +00:00
Fusion
03e401b133 feat(FN-217): lazy-load canvas-confetti via dynamic import to avoid bloating subscription bundle (+2 more)
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Commits merged:
- fix(FN-217): fix import sort order (lint)
- fix(FN-217): convert @sase/shared to emit ESM for Vite/Rollup build compatibility
- fix(FN-217): lazy-load canvas-confetti via dynamic import to avoid bloating subscription bundle

Files changed:
apps/web/src/routes/dashboard/subscription/index.tsx | 13 +++++++------
 apps/web/src/routes/pricing.tsx                      |  2 +-
 packages/shared/package.json                         |  5 +++--
 packages/shared/tsconfig.json                        |  4 ++--
 4 files changed, 13 insertions(+), 11 deletions(-)

Fusion-Task-Id: FN-217
2026-05-12 22:57:32 +00:00
Fusion
3dfd0de0c6 feat(FN-216): simplify plan card click handler — remove duplicate Button.onClick
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Commits merged:
- fix(FN-216): simplify plan card click handler — remove duplicate Button.onClick

Files changed:
apps/web/src/routes/dashboard/subscription/index.tsx | 4 ----
 1 file changed, 4 deletions(-)

Fusion-Task-Id: FN-216
2026-05-12 22:31:53 +00:00
Fusion
479000d4a1 feat(FN-215): use shared formatTRY singleton instead of per-call Intl.NumberFormat
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Commits merged:
- perf(FN-215): use shared formatTRY singleton instead of per-call Intl.NumberFormat

Files changed:
apps/web/src/components/payment/payment-content.tsx  |  9 +--------
 apps/web/src/routes/dashboard/admin/index.tsx        | 11 ++---------
 apps/web/src/routes/dashboard/admin/payments.tsx     | 11 ++---------
 apps/web/src/routes/dashboard/admin/users.tsx        | 11 ++---------
 apps/web/src/routes/dashboard/billing.tsx            |  9 ++-------
 apps/web/src/routes/dashboard/subscription/index.tsx | 10 +---------
 apps/web/src/routes/pricing.tsx                      | 10 +---------
 7 files changed, 11 insertions(+), 60 deletions(-)

Fusion-Task-Id: FN-215
2026-05-12 22:28:23 +00:00
Fusion
e97aab6fe4 feat(FN-214): extract brandLimit 999 magic number to FULL_PLAN_BRAND_LIMIT constant in @sase/shared (+1 more)
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Commits merged:
- fix(FN-214): add missing import for FULL_PLAN_BRAND_LIMIT in subscription/index.tsx
- feat(FN-214): extract brandLimit 999 magic number to FULL_PLAN_BRAND_LIMIT constant in @sase/shared

Files changed:
apps/web/src/routes/dashboard/subscription/index.tsx | 16 +++++++++++-----
 apps/web/src/routes/pricing.tsx                      |  3 ++-
 packages/shared/src/constants/plans.ts               |  3 +++
 packages/shared/src/index.ts                         |  2 +-
 4 files changed, 17 insertions(+), 7 deletions(-)

Fusion-Task-Id: FN-214
2026-05-12 22:21:14 +00:00
Fusion
1a90641529 feat(FN-210): add compact testimonials strip below trust bar on landing page
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Commits merged:
- feat(FN-210): add compact testimonials strip below trust bar on landing page

Files changed:
apps/web/src/routes/index.tsx | 55 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 55 insertions(+)

Fusion-Task-Id: FN-210
2026-05-12 22:12:23 +00:00
Fusion
efbd5be98e feat(FN-212): tune plan card backgrounds, borders, and elevation for light/dark mode
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Commits merged:
- feat(FN-212): tune plan card backgrounds, borders, and elevation for light/dark mode

Files changed:
.../src/routes/dashboard/subscription/__tests__/index.test.tsx    | 2 +-
 apps/web/src/routes/dashboard/subscription/index.tsx              | 8 +++++---
 2 files changed, 6 insertions(+), 4 deletions(-)

Fusion-Task-Id: FN-212
2026-05-12 22:05:59 +00:00
Fusion
0b49c164dc feat(FN-209): implement trial urgency banner
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Commits merged:
- feat(FN-209): implement trial urgency banner

Files changed:
apps/web/src/components/trial-urgency-banner.tsx | 111 +++++++++++++++++++++++
 apps/web/src/messages/en.json                    |   7 +-
 apps/web/src/messages/tr.json                    |   7 +-
 apps/web/src/routes/dashboard.tsx                |   8 +-
 4 files changed, 129 insertions(+), 4 deletions(-)

Fusion-Task-Id: FN-209
2026-05-12 21:47:26 +00:00
Fusion
d14657a2c1 feat(FN-208): implement sticky checkout bar on scroll (+1 more)
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Commits merged:
- chore(FN-208): format code with biome
- feat(FN-208): implement sticky checkout bar on scroll

Files changed:
.../subscription/__tests__/index.test.tsx          | 10 ++++
 .../routes/dashboard/subscription/index.test.tsx   |  9 ++++
 .../src/routes/dashboard/subscription/index.tsx    | 54 +++++++++++++++++++++-
 3 files changed, 71 insertions(+), 2 deletions(-)

Fusion-Task-Id: FN-208
2026-05-12 21:38:48 +00:00
Fusion
405c0067dc feat(FN-207): Step 1 — add downgradeOffer i18n keys (tr + en) (+4 more)
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Commits merged:
- style(FN-207): apply Biome formatting
- feat(FN-207): Steps 4-9 — add downgrade save-flow UI (helper, dialogs, plans query, handler)
- feat(FN-207): Step 3 — add PATCH /subscriptions/downgrade and GET /subscriptions/plans endpoints
- feat(FN-207): Step 2 — add downgrade() and getPlans() service methods
- feat(FN-207): Step 1 — add downgradeOffer i18n keys (tr + en)

Files changed:
.../src/subscriptions/subscriptions.controller.ts  |  13 ++
 .../api/src/subscriptions/subscriptions.service.ts |  75 ++++++
 apps/web/src/messages/en.json                      |  10 +
 apps/web/src/messages/tr.json                      |  10 +
 apps/web/src/routes/dashboard/index.tsx            |  16 +-
 .../src/routes/dashboard/subscription/index.tsx    | 255 ++++++++++++++++++---
 6 files changed, 340 insertions(+), 39 deletions(-)

Fusion-Task-Id: FN-207
2026-05-12 21:29:12 +00:00
Fusion
396049a77a feat(FN-252): fix 2 failing Popular badge tests — FeatureMatrix uses aria-label, not visible text
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Commits merged:
- fix(FN-252): fix 2 failing Popular badge tests — FeatureMatrix uses aria-label, not visible text

Files changed:
.../subscription/__tests__/index.test.tsx          | 26 ++++++++--------------
 1 file changed, 9 insertions(+), 17 deletions(-)

Fusion-Task-Id: FN-252
2026-05-12 20:42:22 +00:00
Fusion
441be751c7 feat(FN-257): remove duplicated plan names and popular badge from FeatureMatrix table headers (+1 more)
Commits merged:
- fix(FN-257): update test assertions for non-duplicated FeatureMatrix text
- fix(FN-257): remove duplicated plan names and popular badge from FeatureMatrix table headers

Files changed:
apps/web/src/routes/dashboard/subscription/index.test.tsx | 10 ++++------
 apps/web/src/routes/dashboard/subscription/index.tsx      | 10 ++--------
 2 files changed, 6 insertions(+), 14 deletions(-)

Fusion-Task-Id: FN-257
2026-05-12 20:13:17 +00:00
Fusion
2907008c9d feat(FN-249): fix 15 failing regression tests — FeatureMatrix duplicate selectors
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Commits merged:
- fix(FN-249): fix 15 failing regression tests — FeatureMatrix duplicate selectors

Files changed:
.../subscription/__tests__/index.test.tsx          | 145 +++++++++++----------
 1 file changed, 77 insertions(+), 68 deletions(-)

Fusion-Task-Id: FN-249
2026-05-12 20:03:30 +00:00
Fusion
668ab50cf2 feat(FN-245): add 20 regression tests for subscription page remaining coverage
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Commits merged:
- test(FN-245): add 20 regression tests for subscription page remaining coverage

Files changed:
.../routes/dashboard/subscription/index.test.tsx   | 382 ++++++++++++++++++++-
 1 file changed, 379 insertions(+), 3 deletions(-)

Fusion-Task-Id: FN-245
2026-05-12 19:46:18 +00:00
Fusion
68b4abe858 feat(FN-244): fix 15 failing subscription regression tests — FeatureMatrix duplicate selectors (+1 more)
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Commits merged:
- style(FN-244): biome format fix for long lines
- fix(FN-244): fix 15 failing subscription regression tests — FeatureMatrix duplicate selectors

Files changed:
.../subscription/__tests__/index.test.tsx          | 69 +++++++++++++++-------
 1 file changed, 49 insertions(+), 20 deletions(-)

Fusion-Task-Id: FN-244
2026-05-12 19:31:12 +00:00
Fusion
1eefd931c9 feat(FN-206): add FeatureMatrix component, ALL_FEATURES constant, and i18n keys (+1 more)
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Commits merged:
- feat(FN-206): add FeatureMatrix tests and export SubscriptionPage for testing
- feat(FN-206): add FeatureMatrix component, ALL_FEATURES constant, and i18n keys

Files changed:
apps/web/src/messages/en.json                      |   1 +
 apps/web/src/messages/tr.json                      |   1 +
 .../routes/dashboard/subscription/index.test.tsx   | 177 +++++++++++++++++++++
 .../src/routes/dashboard/subscription/index.tsx    |  73 +++++++++
 package.json                                       |   1 +
 5 files changed, 253 insertions(+)

Fusion-Task-Id: FN-206
2026-05-12 18:42:41 +00:00