takeone-youtube-clone/public/coach-review-mockups.html
ghassan 716693a74f Coach reviews: in-player capture strip, draggable overlay, timeline UI
Capture strip
- Twin of the point-capture strip, embedded inside #ytpWrap. Two time slots
  (Start + optional End) with the scrubber driving whichever is "active".
  Editable mm:ss inputs, zoom (1x/5x/20x), inline note + coach name + emoji
  chip picker. Cancel / Save on the right; Delete in edit mode.
- ▶ preview button plays Start → End and auto-pauses at End; click again to
  stop early. Playback uses the live overlay so previewing shows what the
  viewer will see.
- Removed the #reviewModal popup; openAddReviewModal(), setReviewEmoji(),
  saveReview() and the reviewForm submit binding are gone.

Live-preview overlay + drag-to-position
- As the user types, the coach-note overlay updates on the video in real
  time. In capture mode the overlay is grab-able and draggable to any spot
  over the video (mouse + touch), with a red dashed outline + "↕ drag me"
  hint.
- Overlay position is stored as normalized 0..1 CENTER coords of the video
  wrap and applied via `left: X%; top: Y%; transform: translate(-50%,-50%)`
  so the overlay stays at the same relative spot on any resize / fullscreen.
- Font-size, padding and border-radius scale with the player width via CSS
  container queries (`container-type: inline-size` on #ytpWrap + `clamp(...cqi)`),
  so the note grows/shrinks proportionally.
- Draggable overlay lives above the strip (z-index 70 vs 60) so it can be
  dropped anywhere including over the strip.

Persistence (schema change)
- Migration 2026_08_08_000001_add_position_to_coach_reviews: adds nullable
  decimal(6,4) position_x / position_y to coach_reviews.
- CoachReview fillable + casts updated. store/updateReview validate
  position_x / position_y as numeric between 0 and 1 and persist them.
- showCoachNoteOverlay(text, pos) applies the saved position on playback.

Cards: timeline design (Option B from the mockup)
- Vertical spine with red dots on the left; each review is a chapter-style
  time label above a subtle card. Card body: emoji as the leading indicator,
  note text, then coach name + tools (▶ / ✏️ / 🗑).
- ▶ = playReviewSlowmo(id): plays Start → End once at 1×, then loops back
  and plays again at 0.5×, then pauses and hides the overlay. Only rendered
  when the review has an end_time > start_time.
- Card body still clickable for the classic "jump-and-play-once" behaviour.
- Fixes a latent bug where the primary Save button stayed disabled after a
  successful save, silently swallowing the next click. Both the review and
  point capture strips now re-enable the button in their cancel handlers.

Also
- Design mockup file at public/coach-review-mockups.html shows the four
  candidate layouts side by side (kept for future design iterations).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-08-08 07:07:03 +03:00

419 lines
17 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Coach Review — Design Mockups</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<style>
:root {
--bg: #0f0f0f;
--bg-panel: #181818;
--text-primary: #f1f1f1;
--text-secondary: #aaa;
--brand-red: #e61e1e;
--border: #262626;
}
* { box-sizing: border-box; }
body {
margin: 0;
background: var(--bg);
color: var(--text-primary);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
padding: 32px 16px;
}
.page {
max-width: 1400px;
margin: 0 auto;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 28px;
}
@media (max-width: 900px) { .page { grid-template-columns: 1fr; } }
.mockup {
background: var(--bg-panel);
border: 1px solid var(--border);
border-radius: 14px;
padding: 24px;
position: relative;
}
.mockup h2 {
margin: 0 0 4px;
font-size: 15px;
text-transform: uppercase;
letter-spacing: .1em;
color: var(--text-secondary);
font-weight: 700;
}
.mockup h2 .letter {
display: inline-block;
background: var(--brand-red);
color: #fff;
width: 24px; height: 24px;
border-radius: 6px;
text-align: center;
line-height: 24px;
font-size: 13px;
margin-right: 8px;
letter-spacing: 0;
}
.mockup .vibe { font-size: 12px; color: var(--text-secondary); margin: 0 0 18px; }
.mockup .stage {
background: #0f0f0f;
border: 1px solid #202020;
border-radius: 10px;
padding: 14px;
min-height: 260px;
}
.mockup .stage > * + * { margin-top: 8px; }
.pill { font-size: 10px; padding: 2px 6px; border-radius: 4px; font-weight: 600; text-transform: uppercase; }
/* ─── Option A: Sticky-note ─────────────────────────── */
.a-card {
display: flex; gap: 14px;
background: rgba(234, 179, 8, .05);
border: 1px solid rgba(234, 179, 8, .18);
border-left: 4px solid rgba(234, 179, 8, .7);
border-radius: 8px;
padding: 14px 16px;
}
.a-card .emoji { font-size: 26px; line-height: 1; flex-shrink: 0; }
.a-card .body { flex: 1; min-width: 0; }
.a-card .note { font-size: 15px; font-weight: 600; color: #f1f1f1; margin: 0 0 6px; }
.a-card .divider { height: 1px; background: rgba(255,255,255,.08); margin: 8px 0; }
.a-card .meta { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.a-card .meta-left { font-size: 12px; color: var(--text-secondary); }
.a-card .meta-left b { color: #f1f1f1; font-weight: 500; }
.a-card .meta-left .dot { margin: 0 6px; opacity: .5; }
.a-card .tools { display: inline-flex; gap: 4px; }
/* ─── Option B: Timeline dot ────────────────────────── */
.b-timeline {
position: relative;
padding-left: 32px;
}
.b-timeline::before {
content: '';
position: absolute;
left: 12px; top: 0; bottom: 0;
width: 2px; background: rgba(255,255,255,.08);
}
.b-item { position: relative; margin-bottom: 22px; }
.b-item:last-child { margin-bottom: 0; }
.b-dot {
position: absolute; left: -26px; top: 2px;
width: 12px; height: 12px; border-radius: 50%;
background: var(--brand-red);
box-shadow: 0 0 0 3px rgba(230,30,30,.2), 0 0 0 6px var(--bg-panel);
}
.b-time {
font-size: 11px; font-weight: 700; letter-spacing: .06em;
color: var(--brand-red); text-transform: uppercase;
margin-bottom: 6px;
}
.b-card {
background: rgba(255,255,255,.03);
border: 1px solid rgba(255,255,255,.06);
border-radius: 8px;
padding: 10px 12px;
display: flex; align-items: flex-start; gap: 10px;
}
.b-card .emoji { font-size: 18px; line-height: 1.2; flex-shrink: 0; }
.b-card .body { flex: 1; min-width: 0; }
.b-card .note { font-size: 14px; font-weight: 500; color: #f1f1f1; margin: 0 0 4px; }
.b-card .author { font-size: 11.5px; color: var(--text-secondary); }
.b-card .tools { display: inline-flex; gap: 4px; align-self: center; }
/* ─── Option C: Media card ──────────────────────────── */
.c-card {
display: flex; gap: 14px;
background: #141414;
border: 1px solid rgba(255,255,255,.08);
border-radius: 10px;
padding: 12px;
}
.c-thumb {
width: 72px; height: 72px; flex-shrink: 0;
border-radius: 8px;
background: linear-gradient(135deg, rgba(230,30,30,.28), rgba(230,30,30,.08));
border: 1px solid rgba(230,30,30,.35);
display: flex; align-items: center; justify-content: center;
font-size: 30px;
}
.c-body { flex: 1; min-width: 0; display: flex; flex-direction: column; justify-content: space-between; }
.c-head { display: flex; align-items: center; gap: 8px; }
.c-time {
font-size: 11px; font-weight: 700; letter-spacing: .04em;
color: #3ea6ff;
background: rgba(62, 166, 255, .12);
padding: 2px 8px; border-radius: 4px;
font-variant-numeric: tabular-nums;
}
.c-note { font-size: 14px; font-weight: 600; color: #f1f1f1; margin: 4px 0; line-height: 1.3; }
.c-foot { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.c-author { font-size: 11.5px; color: var(--text-secondary); }
.c-tools { display: inline-flex; gap: 4px; }
/* ─── Option D: Chat-bubble ─────────────────────────── */
.d-row { display: flex; gap: 10px; align-items: flex-end; }
.d-avatar {
width: 40px; height: 40px; border-radius: 50%; flex-shrink: 0;
background: linear-gradient(135deg, #1f2937, #111827);
border: 2px solid var(--brand-red);
display: flex; align-items: center; justify-content: center;
font-size: 20px; line-height: 1;
box-shadow: 0 4px 12px rgba(0,0,0,.4);
}
.d-bubble {
background: rgba(255,255,255,.05);
border: 1px solid rgba(255,255,255,.08);
border-radius: 14px 14px 14px 4px;
padding: 10px 14px;
flex: 1; min-width: 0;
}
.d-bubble .note { font-size: 14px; font-weight: 500; color: #f1f1f1; margin: 0 0 6px; line-height: 1.35; }
.d-bubble .meta {
display: flex; align-items: center; justify-content: space-between; gap: 8px;
padding-top: 6px; border-top: 1px solid rgba(255,255,255,.06);
}
.d-bubble .meta-left { font-size: 11px; color: var(--text-secondary); }
.d-bubble .meta-left b { color: #f1f1f1; font-weight: 500; }
.d-bubble .meta-left .dot { margin: 0 6px; opacity: .5; }
.d-tools { display: inline-flex; gap: 4px; }
/* ─── Shared button styling for mockups ─────────────── */
.btn-icon {
width: 26px; height: 26px; padding: 0;
display: inline-flex; align-items: center; justify-content: center;
border-radius: 5px; border: none;
background: rgba(255,255,255,.06);
color: var(--text-primary); cursor: pointer;
font-size: 12px; line-height: 1;
transition: background .12s;
}
.btn-icon:hover { background: rgba(255,255,255,.14); }
.btn-slowmo {
width: auto; padding: 0 10px; gap: 4px;
background: rgba(230,30,30,.16); color: var(--brand-red);
font-size: 11px;
}
.btn-slowmo:hover { background: rgba(230,30,30,.28); color: #fff; }
/* header */
.header {
text-align: center; margin-bottom: 32px;
}
.header h1 {
margin: 0 0 6px;
font-size: 26px;
color: #fff;
}
.header p {
margin: 0;
color: var(--text-secondary);
font-size: 14px;
}
</style>
</head>
<body>
<div class="header">
<h1>Coach Review Card — 4 design directions</h1>
<p>Same content, four different visual approaches. Pick one and I'll build it.</p>
</div>
<div class="page">
<!-- ═══ Option A: Sticky-note ═══════════════════════════ -->
<div class="mockup">
<h2><span class="letter">A</span> Sticky-note</h2>
<p class="vibe">Warm amber tint, chunky emoji, feels like a coach's Post-it on a locker.</p>
<div class="stage">
<div class="a-card">
<span class="emoji">👀</span>
<div class="body">
<p class="note">Keep Your Hand Up</p>
<div class="divider"></div>
<div class="meta">
<div class="meta-left">
<b>Ghassan</b><span class="dot">·</span>00:48 00:52
</div>
<div class="tools">
<button class="btn-icon btn-slowmo"><i class="bi bi-play-fill"></i><i class="bi bi-hourglass-split"></i></button>
<button class="btn-icon">✏️</button>
<button class="btn-icon">🗑️</button>
</div>
</div>
</div>
</div>
<div class="a-card">
<span class="emoji">🤔</span>
<div class="body">
<p class="note">Missed the counter opportunity — great angle but no follow-up</p>
<div class="divider"></div>
<div class="meta">
<div class="meta-left">
<b>Coach Sara</b><span class="dot">·</span>02:05 02:20
</div>
<div class="tools">
<button class="btn-icon btn-slowmo"><i class="bi bi-play-fill"></i><i class="bi bi-hourglass-split"></i></button>
<button class="btn-icon">✏️</button>
<button class="btn-icon">🗑️</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- ═══ Option B: Timeline dot ═══════════════════════════ -->
<div class="mockup">
<h2><span class="letter">B</span> Timeline dot</h2>
<p class="vibe">Vertical spine with red dots. Reads like a session log — nice for many notes.</p>
<div class="stage">
<div class="b-timeline">
<div class="b-item">
<span class="b-dot"></span>
<div class="b-time">00:48 — 00:52</div>
<div class="b-card">
<span class="emoji">👀</span>
<div class="body">
<p class="note">Keep Your Hand Up</p>
<div class="author">Ghassan</div>
</div>
<div class="tools">
<button class="btn-icon btn-slowmo"><i class="bi bi-play-fill"></i><i class="bi bi-hourglass-split"></i></button>
<button class="btn-icon">✏️</button>
<button class="btn-icon">🗑️</button>
</div>
</div>
</div>
<div class="b-item">
<span class="b-dot"></span>
<div class="b-time">02:05 — 02:20</div>
<div class="b-card">
<span class="emoji">🤔</span>
<div class="body">
<p class="note">Missed the counter — no follow-up after the angle change</p>
<div class="author">Coach Sara</div>
</div>
<div class="tools">
<button class="btn-icon btn-slowmo"><i class="bi bi-play-fill"></i><i class="bi bi-hourglass-split"></i></button>
<button class="btn-icon">✏️</button>
<button class="btn-icon">🗑️</button>
</div>
</div>
</div>
<div class="b-item">
<span class="b-dot"></span>
<div class="b-time">03:25</div>
<div class="b-card">
<span class="emoji">😄</span>
<div class="body">
<p class="note">Excellent exit and re-entry — perfect execution</p>
<div class="author">Ghassan</div>
</div>
<div class="tools">
<button class="btn-icon">✏️</button>
<button class="btn-icon">🗑️</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- ═══ Option C: Media card ═══════════════════════════ -->
<div class="mockup">
<h2><span class="letter">C</span> Media card</h2>
<p class="vibe">Big emoji tile on the left, blue time badge — feels like a video-platform "content card".</p>
<div class="stage">
<div class="c-card">
<div class="c-thumb">👀</div>
<div class="c-body">
<div class="c-head"><span class="c-time">00:48 → 00:52</span></div>
<p class="c-note">Keep Your Hand Up</p>
<div class="c-foot">
<span class="c-author">Ghassan</span>
<div class="c-tools">
<button class="btn-icon btn-slowmo"><i class="bi bi-play-fill"></i><i class="bi bi-hourglass-split"></i></button>
<button class="btn-icon">✏️</button>
<button class="btn-icon">🗑️</button>
</div>
</div>
</div>
</div>
<div class="c-card">
<div class="c-thumb">🤔</div>
<div class="c-body">
<div class="c-head"><span class="c-time">02:05 → 02:20</span></div>
<p class="c-note">Missed the counter — no follow-up after the angle change</p>
<div class="c-foot">
<span class="c-author">Coach Sara</span>
<div class="c-tools">
<button class="btn-icon btn-slowmo"><i class="bi bi-play-fill"></i><i class="bi bi-hourglass-split"></i></button>
<button class="btn-icon">✏️</button>
<button class="btn-icon">🗑️</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- ═══ Option D: Chat-bubble ═══════════════════════════ -->
<div class="mockup">
<h2><span class="letter">D</span> Chat-bubble</h2>
<p class="vibe">Emoji as coach's avatar + message bubble. Feels like WhatsApp — casual + dense.</p>
<div class="stage">
<div class="d-row">
<div class="d-avatar">👀</div>
<div class="d-bubble">
<p class="note">Keep Your Hand Up</p>
<div class="meta">
<div class="meta-left"><b>Ghassan</b><span class="dot">·</span>00:4800:52</div>
<div class="d-tools">
<button class="btn-icon btn-slowmo"><i class="bi bi-play-fill"></i><i class="bi bi-hourglass-split"></i></button>
<button class="btn-icon">✏️</button>
<button class="btn-icon">🗑️</button>
</div>
</div>
</div>
</div>
<div class="d-row" style="margin-top: 14px;">
<div class="d-avatar">🤔</div>
<div class="d-bubble">
<p class="note">Missed the counter — no follow-up after the angle change</p>
<div class="meta">
<div class="meta-left"><b>Coach Sara</b><span class="dot">·</span>02:0502:20</div>
<div class="d-tools">
<button class="btn-icon btn-slowmo"><i class="bi bi-play-fill"></i><i class="bi bi-hourglass-split"></i></button>
<button class="btn-icon">✏️</button>
<button class="btn-icon">🗑️</button>
</div>
</div>
</div>
</div>
<div class="d-row" style="margin-top: 14px;">
<div class="d-avatar">😄</div>
<div class="d-bubble">
<p class="note">Excellent exit and re-entry — perfect execution</p>
<div class="meta">
<div class="meta-left"><b>Ghassan</b><span class="dot">·</span>03:25</div>
<div class="d-tools">
<button class="btn-icon">✏️</button>
<button class="btn-icon">🗑️</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>