Mobile UX: Schema sayfasında parça listesine ulaşılamıyor — 1-parmak touch sayfa scroll'unu çalıyor (P2) #76

Closed
opened 2026-06-01 23:22:34 +03:00 by root · 1 comment
Owner

TL;DR

Mobil (iOS Safari) kullanıcı kategori detay (schema) sayfasında parça listesine ulaşamıyor. Şema viewport'unun 1-parmak touchmove handler'ı dikey scroll gesture'ını yakalayıp resim pan'ı olarak yorumluyor → sayfa kaymıyor → kullanıcı "schema üstte kalıyor, kodları göremiyorum" deneyimi yaşıyor. Üstüne hotspot'a dokunulduğunda satır seçimi yalnızca parts-panel'in inner scroll container'ını kaydırıyor; parts panel zaten fold altında olduğu için seçim kullanıcıya görünmüyor.

Sonuç: gerçek bir trial kullanıcı (Celik, celikccccc@hotmail.com) 5 farklı leaf kategoride 4 dk 44 sn dolaştı, 16 rage click yaptı, tek bir OEM kodu kopyalayamadı, ödemedi. Aynı pattern başka bir mevcut insight'ta da var (cmpclg7b9000dflajkg27hxvt, 2026-05-19).

Kanıt — gerçek session

Alan Değer
Session ID 019e720e-eaf3-72aa-b5a9-0000b8b84189
Kullanıcı Celik (celikccccc@hotmail.com), authenticated, trial
Cihaz / viewport iPhone Safari, 390×777 (iPhone 14/15 Pro sınıfı)
Tarih 2026-05-29 04:47:21 → 04:52:06 UTC (4 dk 44 sn)
Tıklama 36
Rage click 16
oem_code_copied 0 (kullanıcı tek bir kod bile kopyalayamadı)
Tags ux_friction, frustrated_session
Promotion reasons auth_user, event:trial_no_pay
Score / Severity 55 / P2
Panel session linki https://sp.semih.ai/insights/sessions/019e720e-eaf3-72aa-b5a9-0000b8b84189
PostHog replay https://eu.posthog.com/project/127747/replay/home?sessionRecordingId=019e720e-eaf3-72aa-b5a9-0000b8b84189
İlgili mevcut insight cmpclg7b9000dflajkg27hxvt (P2, ux_friction, conf 0.85, 2026-05-19)

Sayfa zaman çizelgesi

t (UTC) event URL
04:47:33 vin_decode_succeeded WVWZZZ6RZDY231016 /dashboard/search
04:48:11 $pageview /categories/16768065… (leaf 1)
04:48:21 $pageview araç detayına geri
04:48:38 $pageview /categories/f8029c9b… (leaf 2)
04:48:58 $pageview /categories/97232320… (leaf 3)
04:49:04 $pageview /categories/55491841… (parent gibi)
04:49:11 $pageview /categories/77c9ec73… (leaf 4)
04:49:13 parts_panel_viewed leaf 4: 18 parça, 16 hotspot grubu
04:49:51 $pageview /categories/29d41a1b… (leaf 5)
04:49:52 parts_panel_viewed leaf 5: 20 parça, 14 grup
04:50:33 $pageview /categories/224cd399… (leaf 6)
04:50:34 parts_panel_viewed leaf 6: 21 parça, 14 grup
04:51:30 $pageview 224cd399… tekrar (geri dönüş)
04:51:30 parts_panel_viewed aynı leaf, aynı sayılar
04:51:36 $pageview /categories/246ef48e… (leaf 7)
04:51:38 parts_panel_viewed leaf 7: 17 parça, 14 grup
04:52:06 $pageview aynı leaf'te kaldı → ayrıldı

Yorum: kullanıcı 5 dakikada 7 leaf kategoride kayboldu, aynı kategoriye (224cd399…) iki kez geri döndü. Parçalar zaten yüklüydü ama görünmüyordu. oem_code_copied = 0 → temel kullanıcı eylemi (kod kopyalama) bir kez bile gerçekleşmedi.

Kök sebep — kod analizi

Mobil viewport (<md) için schema-viewer.tsx layout şu:

[breadcrumb + back + title]              ~150px
[Schema viewport]   h-[400px]            ←  KRİTİK: tüm fold'u yiyor
  [zoomable image + hotspots]            
  [absolute toolbar at top-left]
[Parts panel]       max-h-[500px]        ← fold ALTINDA
  [sticky thead]
  [parts rows: #, name, OEM, qty, position]

390×777 iPhone viewport'ta safe area + URL bar sonrası ~657px usable. 150 + 400 = 550px üstte → parts panel'in tamamı görünmez.

Problem #1 (KRİTİK) — schema 1-parmak touch'u page scroll'u çalıyor

apps/web/src/hooks/use-schema-interaction.ts:82-90:

} else if (e.touches.length === 1 && isDragging.current) {
  const dx = e.touches[0].clientX - lastMousePos.current.x;
  const dy = e.touches[0].clientY - lastMousePos.current.y;
  lastMousePos.current = { x: e.touches[0].clientX, y: e.touches[0].clientY };
  setPan(panX + dx, panY + dy);
}
  • 1 parmak touchmove → her zaman setPan(...) çağrılıyor.
  • Zoom seviyesi kontrolü yok — zoom=1 (default) iken bile pan tetikleniyor, yapacak pan olmadan touch sequence yine de "consume" ediliyor.
  • e.preventDefault() çağrılmasa bile iOS Safari touch handler'ın "anlamlı iş yaptığını" görüp page scroll'u baskılıyor.
  • Viewport div'inde touch-action: none / touch-action: pan-y CSS'i yok — explicit scroll affordance da yok.

Kullanıcının "schema görseliyle problem yaşıyorum, parça kodları üstte kalıyor" cümlesi tam olarak bu davranışı tarif ediyor: parmak schema üzerindeyken yukarı kaydırma yapıyor, resim hafifçe oynuyor veya hiç oynamıyor, sayfa kaymıyor → parça listesine ulaşamıyor → 16 rage click.

Problem #2 — hotspot tap → row seçilse bile görünmez

apps/web/src/components/schema/parts-panel.tsx:82-89:

useEffect(() => {
  if (selectedGroup != null) {
    const row = rowRefs.current.get(selectedGroup);
    if (row) {
      row.scrollIntoView({ behavior: "smooth", block: "center" });
    }
  }
}, [selectedGroup]);
  • scrollIntoView en yakın scroll'lanabilir parent'ı kaydırır → parts panel'in inner overflow-y-auto container'ı (max-h-[500px]).
  • Document'ı kaydırmaz. Parts panel fold altındaysa, kullanıcı seçimin gerçekleştiğini göremez.
  • Yani: kullanıcı hotspot'a dokunuyor → state doğru güncelleniyor → satır doğru seçiliyor → ama ekranda hiçbir şey değişmiyor → kullanıcı tekrar dokunuyor → rage cluster.

Problem #3 — üçlü scroll trap

Tek bir mobil sayfada 3 ayrı scroll yüzeyi üst üste:

  1. Document body
  2. Schema viewport (overflow-hidden + pan-zoom touch handler'lar)
  3. Parts panel (overflow-y-auto, max-h-[500px])

iOS scroll chaining bazen yardımcı oluyor, ama touch handler'lı parent ortadayken çuvallıyor. Gesture'ın hangi yüzeye gideceği belirsiz → kullanıcı için kestirilemez davranış.

Problem #4 — mobile'de schema yüksekliği sabit ve büyük

apps/web/src/components/schema/schema-viewer.tsx:119:

<div className="relative flex h-[400px] flex-col border-b border-border md:h-auto md:w-[60%] md:border-b-0 md:border-r">

400px sabit yükseklik. Hiçbir collapsible/peek state yok. Parts panel'in ilk satırlarının fold içine sızması (peek) olası değil → kullanıcıya "aşağıda devamı var" sinyali verilmiyor.

Problem #5 — kullanıcı categoryViewMode = grid/tree/columns ile geliyor olabilir ama leaf'e ulaştığında schema'da kaybediyor

Bu, doğrudan bu issue'nun konusu değil ama issue #73 ile birleşik bir hikaye: önce kategori navigasyonunda kayboluyor, sonra schema sayfasında kaybediyor → toplam 2× friction önce parça kodunu görüyor.

Quick fix (~1-2 saat)

1. Schema pan'ı zoom>1 ile gate'le (KRİTİK)

apps/web/src/hooks/use-schema-interaction.ts:

 } else if (e.touches.length === 1 && isDragging.current) {
+  // Only consume the touch when actually zoomed — otherwise the user is
+  // trying to scroll the page down to reach the parts list, and we must
+  // not steal the gesture.
+  if (zoom <= 1) return;
   const dx = e.touches[0].clientX - lastMousePos.current.x;
   const dy = e.touches[0].clientY - lastMousePos.current.y;
   lastMousePos.current = { x: e.touches[0].clientX, y: e.touches[0].clientY };
   setPan(panX + dx, panY + dy);
 }

Ek CSS — viewport div'ine zoom seviyesine göre touch-action:

// schema-viewer.tsx
<div
  ref={viewportRef}
  style={{ touchAction: zoom > 1 ? "none" : "pan-y pinch-zoom" }}
  className="relative flex-1 cursor-grab overflow-hidden bg-muted/30 active:cursor-grabbing"
  ...
>

2. Hotspot tap sonrası mobil page scroll

apps/web/src/components/schema/parts-panel.tsx:

 useEffect(() => {
   if (selectedGroup != null) {
     const row = rowRefs.current.get(selectedGroup);
     if (row) {
-      row.scrollIntoView({ behavior: "smooth", block: "center" });
+      // On mobile, the parts panel itself is below the fold — pull it into
+      // the viewport (block: "start") so the user actually sees that their
+      // hotspot tap landed on a row.
+      const isMobile = typeof window !== "undefined" && window.innerWidth < 768;
+      row.scrollIntoView({
+        behavior: "smooth",
+        block: isMobile ? "start" : "center",
+      });
     }
   }
 }, [selectedGroup]);

3. Mobile schema yüksekliğini küçült (peek affordance)

apps/web/src/components/schema/schema-viewer.tsx:

-<div className="relative flex h-[400px] flex-col border-b border-border md:h-auto md:w-[60%] md:border-b-0 md:border-r">
+<div className="relative flex h-[280px] flex-col border-b border-border md:h-auto md:w-[60%] md:border-b-0 md:border-r">

280px → parts panel'in ilk 2-3 satırı fold içine sızar; kullanıcı "aşağıda devamı var" sinyalini görür. Schema zaten fullscreen butonu var (SchemaToolbar).

4. aria-busy, scroll prompt CTA, ya da inline "Parçalar (n)" badge'i

Schema toolbar'ın yanına küçük bir "📋 18 parça aşağıda" pill ekle, tıklayınca parts panel'e smooth scroll yapsın. Discoverability sıçraması.

Uzun vadeli fix

A. Hotspot tap → inline OEM kodu popover (killer feature)

Hotspot'a dokunulduğunda schema'nın üzerinde küçük bir kart popover olarak açılsın: parça adı + OEM kodu + büyük Copy butonu. Kullanıcı scroll bile etmesin — kodu doğrudan oradan kopyalasın. Bu, çoğu mobil kullanım için (tek parça arama) optimum UX.

// inside HotspotOverlay
<Popover open={selectedGroup === hotspot.group}>
  <PopoverContent>
    <div className="font-medium">{part.name}</div>
    <div className="font-mono text-sm">{part.oemCode}</div>
    <Button size="sm" onClick={() => navigator.clipboard.writeText(part.oemCode)}>
      <Copy /> Kopyala
    </Button>
  </PopoverContent>
</Popover>

B. Mobile'de parts panel'i schema'nın ÜSTÜNE veya tab toggle

Çoğu mobil kullanıcı kod arıyor, schema teknik resim okumak için değil. Layout'u flip et: parts list mobil default, schema "Şemayı göster" CTA'sı ile collapsible.

Alternatif: shadcn Tabs ile Parçalar / Şema toggle — kullanıcı modunu seçer.

C. Schema viewport için ayrı bir route veya sheet

Tap-to-open: hotspot'a tap → tam ekran sheet açılsın (image + part info + copy). Halihazırdaki fullscreen butonu zaten benzer iş yapıyor, ama tap ile değil button ile tetikleniyor.

D. parts_panel_viewed event'ini "panel actually visible" detection ile birleştir

Şu an parts_panel_viewed, PartsPanel component'i mount olduğunda fırlatılıyor — kullanıcının panel'i gerçekten görüp görmediğine bakılmıyor. IntersectionObserver ile sadece >50% görünür olduğunda emit et. Aksi halde "panel görüntülendi" event'i aldatıcı (bu session'da 5x emit edildi, kullanıcı muhtemelen hiçbirini görmedi).

E. Touch-action CSS audit

Tüm pan-zoom yüzeylerine explicit touch-action set et:

  • Default zoom = 1: touch-action: pan-y pinch-zoom (scroll OK, pinch OK)
  • Zoomed in (>1): touch-action: none (pan consume)
  • Schema toolbar / butonlar: touch-action: manipulation

Acceptance criteria

  • Mobile'de (390px viewport), parts panel'in ilk 2 satırı schema viewport altında peek olarak görünüyor.
  • Zoom=1 iken schema yüzeyine 1-parmak dikey swipe → sayfa kayıyor, schema oynamiyor.
  • Zoom>1 iken schema yüzeyine 1-parmak swipe → schema pan ediyor, sayfa kaymıyor.
  • Hotspot'a dokunduğunda parts panel viewport'a kayıyor ve seçilen satır görünür alanın başında.
  • Yeni Playwright e2e: mobile viewport (390×777), schema sayfası, simulated 1-finger swipe-up → window.scrollY artmış olmalı.
  • Manuel test: aynı VIN ile 5 leaf kategori dolaş, en az 1 OEM kodu kopyala (rage click'siz).

Validation plan

Mevcut insight cmpclg7b9000dflajkg27hxvt panel'de 14 gün validation window'una sokulabilir. Acceptance:

  • sessions_meta WHERE projectKey='sase' AND tags @> '{frustrated_session}' AND startUrl ILIKE '%/categories/%' AND viewport_width<500 filtresinde rage-click oranı %50+ düşmeli.
  • oem_code_copied per session (mobile, leaf category page) baseline ölçülecek; fix sonrası en az %2× artış hedefi.
  • parts_panel_viewed event'i IntersectionObserver ile gate'lendiğinde toplam emit sayısının düşmesi normal — gerçek görünme yüzdesi metriği olarak kullanılacak.

Etkilenen segment

  • iOS Safari mobile users — birincil hedef (bu session, viewport 390)
  • Android Chrome mobile — muhtemelen aynı pattern (touch handler aynı, viewport benzer)
  • Desktop (md+) etkilenmiyor: yatay 60/40 split, parts panel her zaman görünür.

Bağlantılı

  • İlgili insight: cmpclg7b9000dflajkg27hxvt (2026-05-19 üretilmiş, P2 ux_friction, conf 0.85, "VIN sorgulama sonrası parça panelinde sürekli tıklama ve rage click") — aynı kök sebep, daha eski. Bu issue ile birleştirilebilir.
  • İlgili issue: #73 (kategori navigasyon affordance) — kullanıcı yolculuğunun bir önceki aşaması. İkisi birlikte mobil kullanıcının "parça kodunu bulamadım" pattern'ini oluşturuyor.
  • Önceki tartışılan insight: cmpv9q3ms004dfcphluw0z8bh (parts panel affordance).

Dosyalar

  • apps/web/src/hooks/use-schema-interaction.ts (L82-90 — touch pan guard)
  • apps/web/src/components/schema/parts-panel.tsx (L82-89 — scrollIntoView block)
  • apps/web/src/components/schema/schema-viewer.tsx (L119 — schema height, L127-136 — touch handler attachment + touch-action CSS)
  • apps/web/src/components/schema/hotspot-overlay.tsx (uzun vade — inline popover için)

🤖 Generated from behavioral insight investigation of session 019e720e-eaf3-72aa-b5a9-0000b8b84189 + code analysis of apps/web/src/components/schema/*.

## TL;DR Mobil (iOS Safari) kullanıcı kategori detay (schema) sayfasında **parça listesine ulaşamıyor**. Şema viewport'unun 1-parmak `touchmove` handler'ı dikey scroll gesture'ını yakalayıp resim pan'ı olarak yorumluyor → sayfa kaymıyor → kullanıcı "schema üstte kalıyor, kodları göremiyorum" deneyimi yaşıyor. Üstüne hotspot'a dokunulduğunda satır seçimi yalnızca parts-panel'in **inner scroll container'ını** kaydırıyor; parts panel zaten fold altında olduğu için seçim kullanıcıya görünmüyor. Sonuç: gerçek bir trial kullanıcı (Celik, `celikccccc@hotmail.com`) **5 farklı leaf kategoride 4 dk 44 sn dolaştı, 16 rage click yaptı, tek bir OEM kodu kopyalayamadı, ödemedi.** Aynı pattern başka bir mevcut insight'ta da var (`cmpclg7b9000dflajkg27hxvt`, 2026-05-19). ## Kanıt — gerçek session | Alan | Değer | |---|---| | Session ID | `019e720e-eaf3-72aa-b5a9-0000b8b84189` | | Kullanıcı | Celik (`celikccccc@hotmail.com`), authenticated, **trial** | | Cihaz / viewport | **iPhone Safari, 390×777** (iPhone 14/15 Pro sınıfı) | | Tarih | 2026-05-29 04:47:21 → 04:52:06 UTC (4 dk 44 sn) | | Tıklama | 36 | | **Rage click** | **16** | | `oem_code_copied` | **0** (kullanıcı tek bir kod bile kopyalayamadı) | | Tags | `ux_friction`, `frustrated_session` | | Promotion reasons | `auth_user`, `event:trial_no_pay` | | Score / Severity | 55 / P2 | | Panel session linki | https://sp.semih.ai/insights/sessions/019e720e-eaf3-72aa-b5a9-0000b8b84189 | | PostHog replay | https://eu.posthog.com/project/127747/replay/home?sessionRecordingId=019e720e-eaf3-72aa-b5a9-0000b8b84189 | | İlgili mevcut insight | `cmpclg7b9000dflajkg27hxvt` (P2, ux_friction, conf 0.85, 2026-05-19) | ### Sayfa zaman çizelgesi | t (UTC) | event | URL | |---|---|---| | 04:47:33 | `vin_decode_succeeded` `WVWZZZ6RZDY231016` | /dashboard/search | | 04:48:11 | $pageview | `/categories/16768065…` (leaf 1) | | 04:48:21 | $pageview | araç detayına geri | | 04:48:38 | $pageview | `/categories/f8029c9b…` (leaf 2) | | 04:48:58 | $pageview | `/categories/97232320…` (leaf 3) | | 04:49:04 | $pageview | `/categories/55491841…` (parent gibi) | | 04:49:11 | $pageview | `/categories/77c9ec73…` (leaf 4) | | 04:49:13 | `parts_panel_viewed` | leaf 4: 18 parça, 16 hotspot grubu | | 04:49:51 | $pageview | `/categories/29d41a1b…` (leaf 5) | | 04:49:52 | `parts_panel_viewed` | leaf 5: 20 parça, 14 grup | | 04:50:33 | $pageview | `/categories/224cd399…` (leaf 6) | | 04:50:34 | `parts_panel_viewed` | leaf 6: 21 parça, 14 grup | | 04:51:30 | $pageview | **`224cd399…` tekrar** (geri dönüş) | | 04:51:30 | `parts_panel_viewed` | aynı leaf, aynı sayılar | | 04:51:36 | $pageview | `/categories/246ef48e…` (leaf 7) | | 04:51:38 | `parts_panel_viewed` | leaf 7: 17 parça, 14 grup | | 04:52:06 | $pageview | aynı leaf'te kaldı → ayrıldı | Yorum: kullanıcı **5 dakikada 7 leaf kategoride kayboldu**, aynı kategoriye (`224cd399…`) iki kez geri döndü. Parçalar zaten yüklüydü ama görünmüyordu. `oem_code_copied` = 0 → temel kullanıcı eylemi (kod kopyalama) **bir kez bile gerçekleşmedi**. ## Kök sebep — kod analizi Mobil viewport (`<md`) için `schema-viewer.tsx` layout şu: ``` [breadcrumb + back + title] ~150px [Schema viewport] h-[400px] ← KRİTİK: tüm fold'u yiyor [zoomable image + hotspots] [absolute toolbar at top-left] [Parts panel] max-h-[500px] ← fold ALTINDA [sticky thead] [parts rows: #, name, OEM, qty, position] ``` 390×777 iPhone viewport'ta safe area + URL bar sonrası ~657px usable. 150 + 400 = **550px** üstte → parts panel'in tamamı görünmez. ### Problem #1 (KRİTİK) — schema 1-parmak touch'u page scroll'u çalıyor `apps/web/src/hooks/use-schema-interaction.ts:82-90`: ```ts } else if (e.touches.length === 1 && isDragging.current) { const dx = e.touches[0].clientX - lastMousePos.current.x; const dy = e.touches[0].clientY - lastMousePos.current.y; lastMousePos.current = { x: e.touches[0].clientX, y: e.touches[0].clientY }; setPan(panX + dx, panY + dy); } ``` - 1 parmak touchmove → her zaman `setPan(...)` çağrılıyor. - **Zoom seviyesi kontrolü yok** — zoom=1 (default) iken bile pan tetikleniyor, yapacak pan olmadan touch sequence yine de "consume" ediliyor. - `e.preventDefault()` çağrılmasa bile iOS Safari touch handler'ın "anlamlı iş yaptığını" görüp page scroll'u baskılıyor. - Viewport div'inde `touch-action: none` / `touch-action: pan-y` CSS'i yok — explicit scroll affordance da yok. **Kullanıcının "schema görseliyle problem yaşıyorum, parça kodları üstte kalıyor" cümlesi tam olarak bu davranışı tarif ediyor:** parmak schema üzerindeyken yukarı kaydırma yapıyor, **resim hafifçe oynuyor veya hiç oynamıyor**, sayfa kaymıyor → parça listesine ulaşamıyor → 16 rage click. ### Problem #2 — hotspot tap → row seçilse bile görünmez `apps/web/src/components/schema/parts-panel.tsx:82-89`: ```ts useEffect(() => { if (selectedGroup != null) { const row = rowRefs.current.get(selectedGroup); if (row) { row.scrollIntoView({ behavior: "smooth", block: "center" }); } } }, [selectedGroup]); ``` - `scrollIntoView` **en yakın scroll'lanabilir parent**'ı kaydırır → parts panel'in **inner `overflow-y-auto`** container'ı (max-h-[500px]). - **Document'ı kaydırmaz.** Parts panel fold altındaysa, kullanıcı seçimin gerçekleştiğini göremez. - Yani: kullanıcı hotspot'a dokunuyor → state doğru güncelleniyor → satır doğru seçiliyor → ama ekranda hiçbir şey değişmiyor → kullanıcı tekrar dokunuyor → rage cluster. ### Problem #3 — üçlü scroll trap Tek bir mobil sayfada **3 ayrı scroll yüzeyi** üst üste: 1. Document body 2. Schema viewport (`overflow-hidden` + pan-zoom touch handler'lar) 3. Parts panel (`overflow-y-auto`, max-h-[500px]) iOS scroll chaining bazen yardımcı oluyor, ama touch handler'lı parent ortadayken çuvallıyor. Gesture'ın hangi yüzeye gideceği belirsiz → kullanıcı için kestirilemez davranış. ### Problem #4 — mobile'de schema yüksekliği sabit ve büyük `apps/web/src/components/schema/schema-viewer.tsx:119`: ```tsx <div className="relative flex h-[400px] flex-col border-b border-border md:h-auto md:w-[60%] md:border-b-0 md:border-r"> ``` 400px sabit yükseklik. Hiçbir collapsible/peek state yok. Parts panel'in ilk satırlarının fold içine sızması (peek) olası değil → kullanıcıya "aşağıda devamı var" sinyali verilmiyor. ### Problem #5 — kullanıcı `categoryViewMode = grid/tree/columns` ile geliyor olabilir ama leaf'e ulaştığında schema'da kaybediyor Bu, doğrudan bu issue'nun konusu değil ama issue #73 ile birleşik bir hikaye: önce kategori navigasyonunda kayboluyor, sonra schema sayfasında kaybediyor → toplam **2× friction** önce parça kodunu görüyor. ## Quick fix (~1-2 saat) ### 1. Schema pan'ı zoom>1 ile gate'le (KRİTİK) `apps/web/src/hooks/use-schema-interaction.ts`: ```diff } else if (e.touches.length === 1 && isDragging.current) { + // Only consume the touch when actually zoomed — otherwise the user is + // trying to scroll the page down to reach the parts list, and we must + // not steal the gesture. + if (zoom <= 1) return; const dx = e.touches[0].clientX - lastMousePos.current.x; const dy = e.touches[0].clientY - lastMousePos.current.y; lastMousePos.current = { x: e.touches[0].clientX, y: e.touches[0].clientY }; setPan(panX + dx, panY + dy); } ``` Ek CSS — viewport div'ine zoom seviyesine göre touch-action: ```tsx // schema-viewer.tsx <div ref={viewportRef} style={{ touchAction: zoom > 1 ? "none" : "pan-y pinch-zoom" }} className="relative flex-1 cursor-grab overflow-hidden bg-muted/30 active:cursor-grabbing" ... > ``` ### 2. Hotspot tap sonrası mobil page scroll `apps/web/src/components/schema/parts-panel.tsx`: ```diff useEffect(() => { if (selectedGroup != null) { const row = rowRefs.current.get(selectedGroup); if (row) { - row.scrollIntoView({ behavior: "smooth", block: "center" }); + // On mobile, the parts panel itself is below the fold — pull it into + // the viewport (block: "start") so the user actually sees that their + // hotspot tap landed on a row. + const isMobile = typeof window !== "undefined" && window.innerWidth < 768; + row.scrollIntoView({ + behavior: "smooth", + block: isMobile ? "start" : "center", + }); } } }, [selectedGroup]); ``` ### 3. Mobile schema yüksekliğini küçült (peek affordance) `apps/web/src/components/schema/schema-viewer.tsx`: ```diff -<div className="relative flex h-[400px] flex-col border-b border-border md:h-auto md:w-[60%] md:border-b-0 md:border-r"> +<div className="relative flex h-[280px] flex-col border-b border-border md:h-auto md:w-[60%] md:border-b-0 md:border-r"> ``` 280px → parts panel'in ilk 2-3 satırı fold içine sızar; kullanıcı "aşağıda devamı var" sinyalini görür. Schema zaten fullscreen butonu var (`SchemaToolbar`). ### 4. `aria-busy`, scroll prompt CTA, ya da inline "Parçalar (n)" badge'i Schema toolbar'ın yanına küçük bir "📋 18 parça aşağıda" pill ekle, tıklayınca parts panel'e smooth scroll yapsın. Discoverability sıçraması. ## Uzun vadeli fix ### A. Hotspot tap → inline OEM kodu popover (killer feature) Hotspot'a dokunulduğunda schema'nın üzerinde küçük bir kart popover olarak açılsın: parça adı + OEM kodu + büyük Copy butonu. Kullanıcı **scroll bile etmesin** — kodu doğrudan oradan kopyalasın. Bu, çoğu mobil kullanım için (tek parça arama) optimum UX. ```tsx // inside HotspotOverlay <Popover open={selectedGroup === hotspot.group}> <PopoverContent> <div className="font-medium">{part.name}</div> <div className="font-mono text-sm">{part.oemCode}</div> <Button size="sm" onClick={() => navigator.clipboard.writeText(part.oemCode)}> <Copy /> Kopyala </Button> </PopoverContent> </Popover> ``` ### B. Mobile'de parts panel'i schema'nın ÜSTÜNE veya tab toggle Çoğu mobil kullanıcı kod arıyor, schema teknik resim okumak için değil. Layout'u flip et: parts list mobil default, schema "Şemayı göster" CTA'sı ile collapsible. Alternatif: shadcn `Tabs` ile **Parçalar / Şema** toggle — kullanıcı modunu seçer. ### C. Schema viewport için ayrı bir route veya sheet Tap-to-open: hotspot'a tap → tam ekran sheet açılsın (image + part info + copy). Halihazırdaki fullscreen butonu zaten benzer iş yapıyor, ama tap ile değil button ile tetikleniyor. ### D. `parts_panel_viewed` event'ini "panel actually visible" detection ile birleştir Şu an `parts_panel_viewed`, `PartsPanel` component'i mount olduğunda fırlatılıyor — kullanıcının panel'i gerçekten görüp görmediğine bakılmıyor. `IntersectionObserver` ile sadece >50% görünür olduğunda emit et. Aksi halde "panel görüntülendi" event'i aldatıcı (bu session'da 5x emit edildi, kullanıcı muhtemelen hiçbirini görmedi). ### E. Touch-action CSS audit Tüm pan-zoom yüzeylerine explicit `touch-action` set et: - Default zoom = 1: `touch-action: pan-y pinch-zoom` (scroll OK, pinch OK) - Zoomed in (>1): `touch-action: none` (pan consume) - Schema toolbar / butonlar: `touch-action: manipulation` ## Acceptance criteria - [ ] Mobile'de (390px viewport), parts panel'in ilk 2 satırı schema viewport altında peek olarak görünüyor. - [ ] Zoom=1 iken schema yüzeyine 1-parmak dikey swipe → **sayfa kayıyor**, schema oynamiyor. - [ ] Zoom>1 iken schema yüzeyine 1-parmak swipe → **schema pan ediyor**, sayfa kaymıyor. - [ ] Hotspot'a dokunduğunda parts panel viewport'a kayıyor ve seçilen satır görünür alanın başında. - [ ] Yeni Playwright e2e: mobile viewport (390×777), schema sayfası, simulated 1-finger swipe-up → `window.scrollY` artmış olmalı. - [ ] Manuel test: aynı VIN ile 5 leaf kategori dolaş, en az 1 OEM kodu kopyala (rage click'siz). ## Validation plan Mevcut insight `cmpclg7b9000dflajkg27hxvt` panel'de 14 gün validation window'una sokulabilir. Acceptance: - `sessions_meta WHERE projectKey='sase' AND tags @> '{frustrated_session}' AND startUrl ILIKE '%/categories/%' AND viewport_width<500` filtresinde rage-click oranı **%50+ düşmeli**. - `oem_code_copied` per session (mobile, leaf category page) baseline ölçülecek; fix sonrası en az **%2× artış** hedefi. - `parts_panel_viewed` event'i `IntersectionObserver` ile gate'lendiğinde toplam emit sayısının düşmesi normal — gerçek görünme yüzdesi metriği olarak kullanılacak. ## Etkilenen segment - **iOS Safari mobile users** — birincil hedef (bu session, viewport 390) - **Android Chrome mobile** — muhtemelen aynı pattern (touch handler aynı, viewport benzer) - Desktop (md+) etkilenmiyor: yatay 60/40 split, parts panel her zaman görünür. ## Bağlantılı - İlgili insight: **`cmpclg7b9000dflajkg27hxvt`** (2026-05-19 üretilmiş, P2 ux_friction, conf 0.85, "VIN sorgulama sonrası parça panelinde sürekli tıklama ve rage click") — aynı kök sebep, daha eski. Bu issue ile birleştirilebilir. - İlgili issue: **#73** (kategori navigasyon affordance) — kullanıcı yolculuğunun bir önceki aşaması. İkisi birlikte mobil kullanıcının "parça kodunu bulamadım" pattern'ini oluşturuyor. - Önceki tartışılan insight: `cmpv9q3ms004dfcphluw0z8bh` (parts panel affordance). ## Dosyalar - `apps/web/src/hooks/use-schema-interaction.ts` (L82-90 — touch pan guard) - `apps/web/src/components/schema/parts-panel.tsx` (L82-89 — scrollIntoView block) - `apps/web/src/components/schema/schema-viewer.tsx` (L119 — schema height, L127-136 — touch handler attachment + touch-action CSS) - `apps/web/src/components/schema/hotspot-overlay.tsx` (uzun vade — inline popover için) --- 🤖 Generated from behavioral insight investigation of session `019e720e-eaf3-72aa-b5a9-0000b8b84189` + code analysis of `apps/web/src/components/schema/*`.
Author
Owner

🤖 Fusion task opened: FN-422

Triage queue: https://fusion.semih.ai/tasks/FN-422

This comment was posted automatically by the fusion-plugin-gitea-issues bridge. A Fusion agent will update this issue when the task moves to in-progress, in-review, or done.

🤖 Fusion task opened: `FN-422` Triage queue: https://fusion.semih.ai/tasks/FN-422 _This comment was posted automatically by the `fusion-plugin-gitea-issues` bridge. A Fusion agent will update this issue when the task moves to in-progress, in-review, or done._
root added the insight-drivensase-pilotseverity-P2type-ux_friction labels 2026-06-01 23:22:46 +03:00
root closed this issue 2026-06-03 15:49:27 +03:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: root/sase.tr#76