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>
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>
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>
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>
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>
Add htmlFor/id to the email, member-since, referral code, share link and
delete-confirm fields so every Label is programmatically tied to its
Input. Drop the delete-confirm placeholder that echoed the exact confirm
word (which defeated the type-to-confirm friction) and disable autocomplete.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
Restructure the 6-tab settings layout: a left vertical tab rail on
desktop (lg) with content beside it, and a horizontally scrollable row
on mobile with labels always visible (were icon-only, ambiguous). Tab
triggers are now mapped from a single TAB_ITEMS array instead of being
hand-repeated.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add a PasswordField with show/hide toggle, hint text and inline error;
the new-password hint and live "passwords do not match" message now
render under the fields (submit disabled on mismatch) instead of only
firing a toast on submit.
- Show skeletons while the connections and referral-stats queries load,
instead of flashing "Not linked"/0.
- De-nest the referral stat cards (cards-inside-cards) into plain muted
panels and use tabular-nums for the figures.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The profile phone input was dead UI — there is no phone column in the
schema, PATCH /users/me only accepts { name, image }, and it was never
loaded, so saving the profile sent an empty phone that the API silently
dropped. Remove the field and stop sending phone. Real phone capture
needs a backend migration (follow-up).
Also fix the "member since" date hard-coding tr-TR; it now follows the
active locale like the changelog already does.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
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>
LinkedIn, Instagram and Facebook icon links in the footer brand column,
opening in a new tab with rel="noopener noreferrer" and per-icon
aria-labels.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
TR/EN toggle in the footer bottom bar, wired to the i18n store's
setLocale (persists to localStorage, updates <html lang>). Buttons use
aria-pressed within a labelled group; active locale is highlighted.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
- 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>
Wrap each footer link column in a labelled <nav aria-labelledby> with an
<h3> (was an ungrouped <h4>), add a top border to delineate the footer,
and raise low-contrast text from muted-foreground/70 to full
muted-foreground (column titles to text-foreground).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move all hard-coded Turkish footer copy (column titles, links, tagline,
copyright, status) into a footer.* message block in tr/en and resolve
them via t(), so the footer follows the selected locale.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
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>
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>
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>
- 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>
- category-tree: kılavuz çizgileri + grid-rows aç/kapa animasyonu geri alındı,
orijinal smooth yapı (paddingLeft girinti) restore edildi; uzun isimlerin
kesilmemesi için truncate yerine break-words korundu
- TreeViewIcon: 80ms gecikmeli/2px küçük hareket yerine gövdeden gecikmesiz
scaleX büyüme (tree-grow keyframe, origin-left); grid/list ile aynı anında tepki
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- category-tree: kılavuz çizgileri + grid-rows aç/kapa animasyonu geri alındı,
orijinal smooth yapı (paddingLeft girinti) restore edildi
- TreeViewIcon: 80ms gecikmeli/2px küçük hareket yerine gövdeden gecikmesiz
scaleX büyüme (tree-grow keyframe, origin-left); grid/list ile aynı anında tepki
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- category-tree: isim span'inde truncate yerine break-words; derin alt
kategorilerde uzun isimler artık kesilmiyor, gerekirse alt satıra sarıyor
- TreeViewIcon: remount+keyframe (opacity 0'dan dolma) yerine grid/list ile
aynı transition mekanizması; dallar hep görünür, aktifte anında kayar
- globals.css: kullanılmayan tree-branch-in keyframe kaldırıldı
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
- 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>
- 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>
Locks in the catalog regression check after CategoryGrid switched from
in-place stack drilling to per-level route navigation: catalog-mode parent
clicks route to the catalog category page preserving variant + mgp search
params (no vehicles-mode cache seeding), and vehicles-mode parent clicks
route to the category page and seed the query cache.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
- 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>
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>
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>
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>
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>
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>
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>
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>
The checkout button read "{amount} ile öde"; per request it now reads
"Kredi Kartı ile Öde" (EN: "Pay with Credit Card"), clarifying the payment
method. The amount stays visible in the order summary directly above the
button, so no pricing context is lost.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
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>
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>
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>
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>
The brand-count plan model leaves users unsure whether they need 1/2/3
brands. Added a PlanHelperHint above the grid ("Hangi planı seçmeliyim? …
4+ marka için Full Paket marka başına daha avantajlı"). Also added an
outcome-focused hook line to the trial card ("Şase numarasını gir, doğru
parçayı saniyede bul …") above its feature list, selling the result rather
than only listing features.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three CRO additions at the point of decision:
- Outcome-focused page subtitle ("Doğru parçayı saniyede bul …") replacing
the purely procedural copy.
- Per-day price anchor on every plan card (≈ ₺X/gün), reframing the cost as
a small daily number. Computed from the displayed monthly/yearly price.
- "2 ay bedava" note under the billing toggle when yearly is selected,
making the otherwise-abstract -17% concrete (yearly = 10× monthly).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>