Commit Graph

159 Commits

Author SHA1 Message Date
f7d5f03572 feat(catalog): hide per-level category filter on vehicle page
The new cross-tree catalog search sits directly above the top-level grid on
the vehicle page, so the grid's own "Kategori ara…" filter was a redundant
second search box. Add a hideFilter prop to CategoryGrid and set it there;
catalog-browse and deep category levels (no global search) keep the filter.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 13:35:50 +03:00
8718b08415 feat(catalog): full-catalog search on vehicle page (leaf categories + OEM parts)
The vehicle page search previously only filtered category names at the
currently rendered level. Add a server-side cross-tree search over what's
already drilled into the DB.

New GET /categories/search/:vehicleId?q= returns two sections:
- categories: name-matched leaves UNION the leaf categories that contain a
  matching part (with hit count). "fren balatası" matches no leaf by name —
  the pads are parts under leaves like "Disk freni" — so the union surfaces
  the right leaves.
- parts: parts matching every token on name, or the raw query on oem_code,
  with OEM + leaf + breadcrumb.

Pure DB read (no upstream drill); a treeIncomplete hint is returned when the
vehicle's tree looks barely drilled. Frontend adds a debounced search box on
the vehicle page that hides the normal browse while active.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 13:30:21 +03:00
9753d00015 fix(web): link "Bize Ulaşın" custom-needs button to the contact page
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
The custom-integration / bulk-query CTA was a dead button. Wrap it in a
Link to /contact.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 12:49:16 +03:00
efd42ade4a fix(web): align remaining "50+ marka" claims to the real count (27)
Follow-up to the landing fix: the credibility strip (subscription/checkout),
meta description, auth-layout badge and blog copy still claimed "50+
marka" while the catalog supports 27. Align all to 27 for consistency.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 11:59:46 +03:00
c76ab7a280 feat(web): plan preselection from landing pricing (CRO #7)
Carry the plan a visitor picks on the landing pricing through to checkout:
- Pricing CTAs now pass ?plan=<key>; logged-in users go straight to
  /dashboard/subscription, others to /register.
- Subscription page accepts ?plan= (and a localStorage carrier that
  survives the value-first register→search onboarding detour) and
  preselects the plan — advancing to brand selection (or payment for
  Full) — unless the sub is active/pending or onboarding is in progress.
- Register validates ?plan and persists it on signup so the choice isn't
  lost through the trial onboarding.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 03:01:26 +03:00
0dc298631d fix(web): landing — align pricing tier cards for easy comparison
Drop the staggered sm:translate-y offsets on the three brand tiers so
their titles, prices and feature lists share a baseline, making the
plans scannable side by side instead of cascading down.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 02:53:33 +03:00
e21683bce6 feat(web): landing — add FAQ section and trust signals near final CTA
Add an objection-handling FAQ (brands, KVKK/security, no card, cancel
anytime, integration, no-VIN path) before the closing CTA, and a
KVKK/SSL trust line under the final CTA button.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 02:52:51 +03:00
076c61d599 feat(web): landing — stronger final CTA, move referral after the close
The closing CTA was an outline (ghost) button — the weakest style at the
most important conversion point. Make it a filled brand button, size lg,
with clearer copy ("30 Gün Ücretsiz Başla — Kart Gerekmez"). Move the
referral banner (a retention loop) to after the final CTA so it no longer
sits between the pricing and the primary close.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 02:51:11 +03:00
eaf6d47f05 fix(web): landing — consistent brand count (27) and wrong-part cost figure
The page claimed "50+ marka" in the hero, carousel, plan and meta while
the stats block and the actual brand carousel show 27 — a credibility
contradiction. Align every brand-count mention to 27. Also align the
wrong-part cost: hero said "450 TL", final CTA "200-500 TL" — use the
200-500 TL range in both.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 02:50:12 +03:00
18576a2818 chore(web): contact — E-posta kartını kaldır, form placeholder + meta güncelle
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
- info@sase.tr E-posta bilgi kartı kaldırıldı (Destek + Adres kaldı)
- mesaj placeholder "Bize..." → "Size nasıl yardımcı olabiliriz?"
- meta description info@sase.tr referansından arındırıldı

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 02:37:21 +03:00
7eb2e85bbc feat(auth): "beni 30 gün hatırla" + login Turnstile koruması
- session.expiresIn = 30 gün; login'de rememberMe checkbox signIn.email'e bağlandı
  (işaretli=30 gün kalıcı çerez, değilse oturum çerezi)
- better-auth captcha plugin'i (cloudflare-turnstile, TURNSTILE_SECRET_KEY varsa)
  sign-in/sign-up'ı korur; login formuna Turnstile widget'ı + x-captcha-response

Not: auth.ts ve login.tsx her iki özelliği birlikte içerdiğinden tek commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 02:14:18 +03:00
a09182fb98 feat: Cloudflare Turnstile (register + contact) + captcha altyapısı
- Turnstile widget bileşeni (public site key gömülü, VITE_TURNSTILE_SITE_KEY ile override)
- register: signUp.email'e x-captcha-response header'ı
- contact: token body'de; ContactService Cloudflare siteverify ile doğrular
  (TURNSTILE_SECRET_KEY yoksa atlanır), contact.dto'ya turnstileToken
- @sase/config: TURNSTILE_SECRET_KEY env (opsiyonel)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 02:14:06 +03:00
cf1fee68a9 fix(web): responsive billing table + filters on mobile
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
The payments table forced min-w-[640px] inside overflow-x-auto, so mobile
got an awkward horizontal scroll, and the 5-option status filter could
overflow the viewport. Make the table a responsive layout: on mobile each
payment renders as a labelled stacked card (column label via ::before
from data-label, header row hidden), and on sm+ it stays a normal table.
Let the segmented filters wrap instead of overflowing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 00:36:03 +03:00
c633e846e2 fix(payments): curate /payments/me response — stop leaking internal columns, join plan name
getMyPayments returned the raw payment row, exposing internal fields
(adminNote, iyzicoPaymentId, bankAccountId, session/intent ids) to the
end user. Replace with an explicit projection that returns only what the
billing UI needs, joins planName from the subscription's plan (was always
"-"), and surfaces Stripe receipt availability as a hasStripeReceipt
boolean instead of the raw payment intent id. Frontend reads the boolean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 00:31:33 +03:00
d77face831 fix(web): billing — correct cancelled status label and gate receipt button
- Subscription status is "cancelled" (and "pending") in the API; the i18n
  keys/variant map used "canceled", so the badge showed the raw
  billing.subStatus.cancelled key. Fix the spelling and add pending.
- Only show "View receipt" for Stripe payments that actually have a
  stripePaymentIntentId. Seeded/legacy completed payments have none, so
  the button used to appear and then toast "no receipt available".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 00:28:39 +03:00
7a8fe5e98e feat(payments): Stripe receipt link on billing rows
Add GET /payments/:id/receipt — resolves the Stripe-hosted receipt URL
from the payment intent's latest charge (ownership-scoped; returns EFT
receipt directly when present, null otherwise). Wire a "View receipt"
action on completed Stripe rows that fetches the URL on demand and opens
it, with a toast when none is available.

Closes the last billing-audit item (#8).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 00:14:28 +03:00
0e61a2fcc7 feat(web): billing loading skeleton shape + show-more pagination
Make the payments loading state mirror the table card (header + rows)
instead of generic bars, and paginate the list client-side (10 at a time
with a "Show more" button) so long payment histories don't render all at
once.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 00:07:50 +03:00
94d04d5339 refactor(web): semantic billing table + segmented filters
Replace the div-grid "table" with a real <table> (thead/th scope=col/
tbody/td) inside an overflow-x-auto wrapper, so screen readers announce
each cell under its column and every amount/status is labelled (drops the
hidden-header mobile hack). Convert the filter button rows into compact
segmented controls (fieldset/legend) and only show filters once payments
exist.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 00:06:36 +03:00
36bd4b500b feat(web): add subscription summary card to billing page
Billing was payment history only. Add a summary card at the top showing
the current plan, status badge, and next renewal / trial-end / end date
(from /subscriptions/me), with a "Manage subscription" link — or a
"Choose a plan" CTA when there's no active subscription.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 00:04:58 +03:00
acada22e71 fix(web): billing — distinguish empty vs filtered, localize date, tabular nums
Show "no payments match this filter" with a clear-filters button when
filters hide everything (was the misleading "no payments yet"). Follow
the active locale for the payment date instead of hard-coding tr-TR, and
use tabular figures for date and amount columns.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 00:03:26 +03:00
0749a55b41 fix(web): repair EFT receipt download and add EFT payment filter
The receipt link read payment.receiptUrl, but the API returns the column
as eftReceiptUrl — so the download button never rendered (dead feature).
Use eftReceiptUrl. Also add the missing "EFT" option to the method
filter; EFT is a live method with rows and receipts but couldn't be
filtered.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 00:02:23 +03:00
10cd67911d refactor(web): move changelog out of settings into its own route
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Product changelog isn't an account setting. Give it a dedicated
/dashboard/changelog route and a "What's New" sidebar link (Support
section), and drop the changelog tab from settings. The header page
title now resolves support-section routes too.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 23:37:28 +03:00
239791290e feat(web): add Preferences tab (theme + language) to settings
New "Preferences" settings tab giving the theme a real home in settings
(light/dark/system selector wired to userSettings + applyTheme) alongside
the language switcher, instead of theme living only in the header toggle.
Theme group uses a fieldset/legend for accessible grouping.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 23:32:15 +03:00
d679907e2c feat(web): make settings tabs URL-addressable via ?tab=
Add an optional `tab` search param (validated against the known tab set)
to /dashboard/settings and drive the Tabs component from it, so each tab
is shareable/bookmarkable (e.g. /dashboard/settings?tab=referral). Tab
clicks update the URL with replace to avoid history spam; existing links
need no search param since `tab` is optional.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 23:23:44 +03:00
681a25ec2e feat(web): language switcher in dashboard header + shared component
Extract the footer's TR/EN toggle into a reusable LanguageSwitcher with
two variants: `inline` (segmented row, used by the footer) and
`dropdown` (round Globe icon + menu with active check). Add the dropdown
variant to the dashboard header next to the theme toggle so app users
can switch language without the marketing footer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 23:08:12 +03:00
e90f825ed7 refactor(web): unify dashboard footer via SiteFooter compact variant
Add a `compact` variant to SiteFooter (single-line copyright + legal
links) and use it in the dashboard layout, replacing the divergent
hard-coded inline footer. The dashboard footer is now i18n-aware,
contrast-corrected, and adds the KVKK link for parity with the site
footer. Both layouts now render from one component.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 20:56:50 +03:00
d482d5db6c fix(web): footer content — real anchors, single email, drop fake status
- Point the three product links to distinct, real feature anchors
  (#feature-vin / #feature-oem / #feature-schema); add matching ids and
  scroll-mt to the feature cards in index.tsx (were all -> #features).
- De-duplicate the email: brand-column address is now the single
  mailto link; removed the redundant bottom-bar copy.
- Remove the static "all services operational" indicator that never
  reflected real status (misleading trust signal).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 20:54:39 +03:00
b71b5d1de8 refactor(web): extract landing footer into shared SiteFooter component
Behavior-preserving move of the inline footer markup out of index.tsx
into components/site-footer.tsx, mirroring the site-header convention.
No visual or content change — sets up the footer for i18n, a11y and
content fixes in follow-up commits.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 20:50:17 +03:00
fa4e08a0a7 feat(web): add "upgrade account" CTA above sidebar profile for free users
Shows a brand-tinted "Hesabını Yükselt" badge above the username in the
sidebar footer, linking to the subscription page. Hidden for users with
an active paid plan. Renders in desktop (expanded + collapsed icon
variant), and the mobile drawer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 20:09:12 +03:00
e1d929d3eb feat(web): replace register subtext with terms-acceptance notice
Swap the "start searching / no spam" line under the register button for a
terms-of-use acceptance notice linking to /terms.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 19:54:37 +03:00
71242a72be feat(web): dashboard sidebar UX/UI + a11y overhaul
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Replace the click-to-logout profile trap with a proper DropdownMenu
(settings / subscription / logout), swap the hand-rolled mobile overlay
for a Radix-Dialog Sheet (focus-trap, Esc, scroll-lock), and fix
accessibility gaps: aria-labels on icon buttons, aria-current + labelled
nav landmarks, and effect-based auth redirect instead of navigating
during render.

Also: reorder nav (Main → Account → Support → Admin), full i18n of
labels/sections, conditional "upgrade plan" button, header page title,
collapsed-mode active indicator + tooltips, contrast/touch-target
tweaks, and a typed NavItem to drop the `as any` exact cast.

Adds DropdownMenu, Tooltip and Sheet primitives to @sase/ui.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 13:54:53 +03:00
57b3cb7ebe feat: contact sayfasına iletişim formu + POST /contact endpoint'i
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
- Yeni contact modülü: @Public POST /contact, zod validation, @Throttle 5/10dk
  spam koruması; EmailService ile admin@sase.tr'ye mail (reply-to = gönderen),
  kullanıcı girdileri HTML-escape
- EmailService: replyTo desteği eklendi
- contact.tsx: useState + zod + @sase/ui ile iletişim formu (mevcut form
  pattern'iyle tutarlı; yeni form kütüphanesi yok), toast + alan validasyonu

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 01:27:56 +03:00
1db9a632cb feat(web): görünüm toggle için animasyonlu CSS ikonları (grid/tree/columns)
unlumen cards-view/list-view motion gerektirdiği için saf CSS eşdeğerleri:
- CardsViewIcon (grid): 2×2 kareler yer değiştirir
- TreeViewIcon (tree): bespoke dallanan ağaç, kademeli çizilir (tree-branch-in)
- ListViewIcon (columns): 3 çizgi birer slot kayar
isActive ile sürülür, motion-reduce saygılı. CategoryViewToggle + 4 inline
katalog toggle'ı bu ikonlara çevrildi; kullanılmayan lucide import'ları temizlendi.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 00:44:03 +03:00
d72e7bb758 fix(web): VIN ikonundaki marka logolarını tutarlı 28px kareye sabitle
Tailwind Preflight img'lere height:auto uyguladığından statik-SVG logolar
en-boy oranına göre ölçekleniyordu (Opel ~17px küçük, Renault ~29px taşma).
VinBrandIcon artık sabit 28px kare + object-contain ile hem img hem svg
logolarını aynı boyutta gösteriyor; büyüteç optik denge için biraz küçük.
Playwright ile landing VIN textbox üzerinde doğrulandı.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 00:00:22 +03:00
77de860211 feat: VIN textbox WMI marka ikonu + WMI haritasını shared'e taşı/genişlet
- WMI_BRAND_MAP + getBrandFromWmi packages/shared'e taşındı (tek kaynak);
  corgi.service artık buradan import ediyor (davranış aynı, testler geçiyor)
- VinBrandIcon: VIN'in WMI'ı bilinen markaya denk gelince büyüteç yerine
  marka logosu pop animasyonuyla görünür (landing + search VIN textbox)
- WMI haritasına prod DB'de decode edilmiş VIN'lerden 23 eksik WMI eklendi
  (Türkiye fabrikaları NM4/NMT/NLA/NLH/NMB dahil; tüm DB WMI'ları artık tanınıyor)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 23:51:40 +03:00
96171c0726 feat(web): landing marka karuselinde logolar + tıklayınca animasyonlu marka adı
- Kayan şeritte metin yerine CarBrandLogo (cardog-icons + statik SVG'ler)
- Logoya tıklayınca seçilen marka adı SmartAnimateText ile karakter karakter yazılır
- globals.css: char-in keyframe + animate-char-in utility

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 23:37:24 +03:00
001ad1986c feat(web): unify vehicle drill-down breadcrumb & back navigation
The vehicle category flow had two competing navigation models: the grid
drilled in place (no URL change, own breadcrumb) while the category route
had a separate breadcrumb reconstructed from the cached tree plus a back
button with different semantics. The route breadcrumb collapsed on
refresh/deep-link and for lazily-fetched deep nodes, and browser-back from
an in-grid drill ejected the user out of the whole flow.

Make the URL + a server-provided ancestor trail the single source of truth:

- API: getCategoryWithParts now returns `ancestors[]` (root→parent) via a
  recursive CTE, so breadcrumbs are complete regardless of client cache.
- New shared <CategoryBreadcrumb> (Arama → vehicle → ancestors → current)
  used by both the vehicle and category pages.
- Category page back button derives the parent from `ancestors` (no more
  cache-path race); cache reconstruction (findCategoryPath) removed.
- CategoryGrid drills via route navigation per level (each level a URL +
  history entry), seeding the query cache for instant render. Browser-back
  now goes up one level and deep levels are shareable/refreshable.
- Tree & Columns intentionally keep their in-place paradigm.
- a11y: aria-labels on back buttons and the breadcrumb nav.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 22:55:01 +03:00
9fc222d525 feat(web): register form UX — password toggle, live rules, ref-code collapse
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
- Password field gets a show/hide eye toggle (reduces typos on a hidden,
  rule-constrained field).
- Live requirement checklist (8+ karakter, 1 büyük harf, 1 rakam) turns
  green as the user types, replacing the rule that only lived in the
  placeholder and vanished on input.
- Optional referral code is collapsed behind a "Referans kodun var mı?"
  link, shortening the form for the ~all users without one (auto-expands
  when a ?ref= code is present).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 22:13:19 +03:00
a01605eb90 fix(web): handle real signup errors on register (no more false success)
better-auth's signUp.email returns { error } instead of throwing (same as
login.tsx), so the old try/catch never caught auth failures: a failed signup
still showed "Hesap oluşturuldu!", fired user_signed_up / CompleteRegistration,
and redirected to the dashboard. Now we check the returned error, distinguish
"e-posta zaten kayıtlı" (via /users/check-email) from other failures (showing
the actual reason, e.g. weak password), keep the user on the form, and only
fire success analytics on real success. Network failures get a distinct
"Bağlantı hatası" toast.

Also: CTA "Kayıt Ol" -> "Ücretsiz Başla" (matches pricing) and added
"Spam göndermeyiz" trust microcopy.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 22:11:41 +03:00
6e5c4b262d fix(web): align catalog figures site-wide to 50+ marka / 1M+ parça
The pricing/subscription credibility strip stated 50+ marka / 1M+ parça
(the current real figures) while the rest of the site still showed the
outdated 27+ marka / 243K parça — so the same visitor saw "1M+ parts" on
pricing and "243K parts" on register. Updated every catalog-scale claim
(SEO/OG/Twitter meta, page-meta default, homepage stat cards + feature
cards + CTA list, auth promo panel, blog prose) to the consistent
50+ marka / 1M+ parça. Also sorted blog imports for biome.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 22:07:55 +03:00
1685ee4b44 feat(web): add "Nasıl çalışır" 3-step band to pricing page
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Cold visitors landing straight on /pricing had no product context. Added a
compact 3-step band between the credibility strip and FAQ: enter the chassis
number -> see the parts (OEM codes, diagrams, compatible parts) -> pick the
right part by comparing prices. Grounded in the actual product, no ordering
claims.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 16:16:46 +03:00
b8c2e7568a feat(web): add FAQ section to pricing page
The pricing page had no objection handling and a half-empty page below the
cards. Added a PricingFaq disclosure section (native <details>) covering the
six recurring questions: how the free trial works, whether a card is needed,
cancellation, refunds, changing plan/brands, and payment security — all
grounded in actual product behaviour (30-day Full trial, no card, period-end
access on cancel, downgrade, Stripe PCI-DSS / KVKK).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 16:15:03 +03:00
d895c9dd28 refactor(web): share CredibilityStrip across pricing + subscription
Extracted CredibilityStrip out of the subscription route module into
@/components/credibility-strip so the public pricing page can reuse it
without pulling in the heavy dashboard/Stripe/Remotion bundle. Subscription
now imports it (dropping the now-unused Boxes/Hash/Layers/Zap icons); pricing
renders it below the plan cards to justify price with real product facts
(50+ marka, 1M+ parça, OEM, saniyede sonuç, KVKK·SSL). Test import updated.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 16:12:55 +03:00
34bbb01fed feat(web): align pricing cards with subscription page
Bring the public pricing cards in line with the in-app subscription page and
strengthen the CTA:
- Move the "Popüler" badge from 2 Marka to Full Paket (one consistent story
  across both surfaces).
- CTA "Başla" -> "Ücretsiz Başla" (leans on the free trial the flow grants).
- Yearly line now uses a consistent ₺ format ("veya ₺2.000,00/yıl") plus a
  "· 2 ay bedava" savings cue instead of the bare, differently-formatted
  "veya 2.000,00 TL/yıl".
- Plan-decision helper above the grid ("Hangi planı seçmeliyim? …").

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 16:10:43 +03:00
d49964aac1 feat(web): value-focused pricing hero with trial + risk-reversal trust line
The pricing hero was purely procedural ("Fiyatlandırma" / "İhtiyacınıza
uygun planı seçin") with no value prop and no mention of the free trial —
the strongest conversion lever, present in the meta description and header
CTA but absent from the page body. Reworked into an eyebrow + outcome
headline ("Doğru parçayı saniyede bul") + supporting line, plus a trust
row: 30 gün ücretsiz · kredi kartı gerekmez · istediğin zaman iptal ·
7 gün iade garantisi (reusing existing subscription trust keys).

Also sorted pricing.tsx imports to satisfy biome.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 16:08:35 +03:00
85f4252f7e feat(web): default to yearly billing, make Full Paket the popular plan
Per request: shift the "Popüler" emphasis from the monthly-anchored 2 Marka
to Full Paket, default the billing toggle to yearly, and put a "+2 ay hediye"
gift badge on the yearly tab (replacing the abstract -17%). Removed the
now-redundant "2 ay bedava" note under the toggle since the badge carries
that message.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 15:49:56 +03:00
a631380e74 fix(web): stop mobile layout viewport expanding past device width
On mobile the dashboard opened "zoomed in" — the layout viewport reported
~548px instead of the device's 390px, so users had to pinch-zoom out to
fit. Root cause: the dashboard main column (flex flex-1 flex-col) is a flex
item with the default min-width:auto, so it refused to shrink below its
content's intrinsic width. The subscription feature-comparison table
(~514px: a w-48 label column + 4 plan columns) therefore dragged the whole
column — and the mobile layout viewport — wider than the screen, despite
the table sitting in an overflow-x-auto wrapper.

Adding min-w-0 lets the column shrink to the viewport; the wide table now
scrolls inside its own container. Verified with iPhone-class emulation
(390px): innerWidth/scrollWidth = 390 with no horizontal overflow across
/dashboard, /dashboard/search and /dashboard/subscription. Fix lives in the
shared dashboard layout, so it repairs every dashboard page.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 15:43:33 +03:00
4ba06615a1 style(web): satisfy biome on subscription page (format + a11y)
Run biome --write formatting on the subscription page + new tests, and
swap the trial-progress banner's role="status" for aria-live="polite"
(keeps the polite live-region announcement without tripping
lint/a11y/useSemanticElements).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 15:31:08 +03:00
0bae337c69 feat(web): show persistent trial-progress banner for days 8-30
Trial users previously got no reminder of remaining days until the urgent
(<=7d) amber banner appeared, leaving most of the 30-day window with no
conversion nudge. Added a calm, brand-toned TrialProgressBanner shown when
more than 7 days remain: days left, a progress bar (elapsed share of the
30-day trial), and a "Planını seç" CTA that scrolls to checkout and fires
trial_progress_cta_clicked. The urgent banner still owns the final 7 days.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 15:29:42 +03:00
2912dc6eb2 feat(web): add credibility strip to subscription page
The page had no credibility signal at the point of conversion. Added a
CredibilityStrip under the header (shown only to converting users:
no-sub / trial / expired / pending) surfacing real, verifiable product
facts — 50+ marka, 1M+ parça, OEM + alternatif kodlar, saniyede sonuç,
KVKK uyumlu · SSL. Deliberately no fabricated customer counts or
testimonials.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 15:27:43 +03:00