Files
travelplanning/ui_implementation_plan.md
T

9.0 KiB
Raw Blame History

Kế hoạch: Tái thiết kế giao diện xem ảnh và bình luận trên Mobile (PublicPhotoModal)

Thiết kế lại giao diện mobile của modal xem ảnh công khai để trải nghiệm cuộn trang tự nhiên hơn, tương tự như Instagram/Twitter — ảnh chiếm toàn bộ chiều rộng màn hình từ trên xuống, thông tin người đăng và các nút được bố cục rõ ràng, sau đó cuộn xuống xem bình luận một cách liền mạch.


Phân tích hiện trạng

Cấu trúc hiện tại của modal trên mobile (flex flex-col):

  1. Container (90vh, overflow-hidden) — toàn bộ nội dung bị "kẹt" trong 90vh
  2. Phần ảnh (h-[45vh]) — cố định, không thể co giãn
  3. Phần "Mobile Uploader" strip — cố định (shrink-0)
  4. Phần comment panel (flex-1 min-h-0) — chỉ phần danh sách comment cuộn bên trong

Vấn đề: Ảnh bị giới hạn ở h-[45vh], có padding p-4 từ container ngoài nên không chạm mép màn hình. Nút like nằm ở góc trên trái (gần nút X), người đăng nằm ở vùng riêng giữa ảnh và comment, không cuộn cùng content.


Thay đổi đề xuất

[MODIFY] PublicPhotoModal.tsx

1. Outer wrapper — Bỏ padding và căn giữa

Hiện tại (line 358):

<div className="fixed inset-0 z-[5000] flex items-center justify-center p-4">

Thay thành — trên mobile modal full screen, không padding, không căn giữa. Desktop giữ nguyên layout cũ:

<div className="fixed inset-0 z-[5000] md:flex md:items-center md:justify-center md:p-4">

2. Container modal — Mobile: full screen cuộn tự nhiên; Desktop: giữ nguyên

Hiện tại (line 366):

<div className="relative w-full max-w-5xl h-[90vh] md:h-[85vh] bg-slate-900 ... overflow-hidden flex flex-col md:flex-row ...">

Thay thành:

  • Mobile: fixed inset-0 overflow-y-auto flex flex-col bg-slate-900 — toàn màn hình, cuộn tự nhiên theo trang
  • Desktop: relative w-full max-w-5xl h-[85vh] overflow-hidden flex flex-row rounded-[32px]
<div className="
  fixed inset-0 overflow-y-auto flex flex-col bg-slate-900 text-slate-100
  md:relative md:inset-auto md:overflow-hidden md:rounded-[32px] md:shadow-2xl md:border md:border-slate-800
  md:w-full md:max-w-5xl md:h-[85vh] md:flex-row
  animate-in zoom-in-95 duration-300
">

3. Nút X — Fixed ở góc trên phải (hiện tại đúng, cần giữ nguyên)

Nút X hiện đang dùng fixedgiữ nguyên, chỉ điều chỉnh top để tính safe-area:

className="fixed top-[calc(1rem+env(safe-area-inset-top,0px))] right-4 z-50 ..."

4. Phần ảnh (Left Side Photo) — Mobile: full width, không giới hạn chiều cao, aspect ratio tự nhiên

Hiện tại (line 377):

<div className="relative w-full md:w-3/5 h-[45vh] md:h-full ... shrink-0">

Thay thành — Trên mobile: ảnh full width, chiều cao theo tỷ lệ ảnh (aspect-ratio), không giới hạn, chạm mép trên màn hình (không có padding trên):

<div className="relative w-full md:w-3/5 md:h-full bg-slate-950 flex items-center justify-center overflow-hidden group shrink-0">

Với ảnh bên trong:

<img className="w-full h-auto max-h-[85vh] object-contain md:h-full md:max-h-none cursor-zoom-in" />

5. Nút Like — Di chuyển từ góc trên trái sang góc dưới phải của vùng ảnh

Hiện tại: absolute top-[...] left-4 — góc trên bên trái.

Thay thành: absolute bottom-4 right-4 — góc dưới bên phải của ảnh, không bị che bởi thanh địa chỉ và không xung đột với nút X:

className="absolute bottom-4 right-4 z-40 ..."

6. Thông tin người đăng — Di chuyển thành overlay góc trên trái trên ảnh

Hiện tại "Mobile Uploader strip" là một block riêng biệt giữa ảnh và comment (block md:hidden p-6 ...).

Thay thành: Overlay trực tiếp trên ảnh ở góc trên bên trái, nằm trong vùng ảnh, có safe-area padding:

<!-- Bên trong div ảnh, sau img tag -->
<div className="absolute top-[calc(0.75rem+env(safe-area-inset-top,0px))] left-4 z-40 md:hidden
  flex items-center gap-2 bg-slate-950/70 backdrop-blur-md px-2.5 py-1.5 rounded-full border border-slate-700/50">
  <div className="w-6 h-6 rounded-full bg-emerald-500/20 flex items-center justify-center">
    <User className="w-3.5 h-3.5 text-emerald-400" />
  </div>
  <span className="text-xs text-slate-200 max-w-[120px] truncate">{photo.uploader?.name || 'Ẩn danh'}</span>
</div>

7. "Mobile Uploader strip" block — Xóa block cũ

Block {/* Mobile Only: Uploader details & divider */} ở lines 597623 sẽ được xóa vì thông tin người đăng đã được hiển thị trực tiếp trên ảnh ở bước 6.

Nếu isAuthorized && photo.originalUrl (nút Tải ảnh gốc): sẽ được giữ lại nhưng chuyển vào trong phần metadata/overlay của ảnh hoặc đặt trước phần comments.

8. Phần Comments — Cuộn cùng với nội dung (không bị kẹt trong flex)

Trên mobile, toàn bộ trang cuộn — phần comment không cần overflow-y-auto riêng nữa (vì parent đã cuộn):

Hiện tại (line 626):

<div className="w-full md:w-2/5 flex-1 min-h-0 flex flex-col ...">

Thay thành:

<div className="w-full md:w-2/5 md:flex-1 md:min-h-0 flex flex-col ...">

Danh sách bình luận (line 640):

<div className="flex-1 overflow-y-auto p-6 space-y-4 ...">

→ Trên mobile không giới hạn overflow-y-auto:

<div className="md:flex-1 md:overflow-y-auto p-6 space-y-4 ...">

9. Comment Input — Sticky ở cuối trang trên mobile

Thay vì nằm cố định cuối flex column, ô nhập bình luận sẽ sticky ở bottom:

<div className="sticky bottom-0 md:static p-4 bg-slate-950/95 md:bg-slate-950/40 border-t border-slate-800/80 pb-[calc(1rem+env(safe-area-inset-bottom,0px))] md:pb-4">

Tóm tắt layout cuối cùng trên mobile

┌─────────────────────────────────────────────┐  ← màn hình (fixed inset-0)
│ [X nút đóng]                    (góc trên phải, fixed/z-50)
│─────────────────────────────────────────────│
│                                             │
│  [Người đăng]              (overlay TL)     │
│                                             │  ← Vùng ảnh (full width,
│           ẢNH                               │    height theo tỷ lệ)
│                                             │
│                         [❤ Like + số]       │
│─────────────────────────────────────────────│
│  📷 Timeline ảnh (nếu có nhiều ảnh)         │  ← Cuộn cùng trang
│─────────────────────────────────────────────│
│  Tiêu đề, mô tả, ngày chụp, địa điểm       │  ← Info overlay panel
│─────────────────────────────────────────────│
│  [Tải ảnh gốc] (nếu là chủ sở hữu)         │
│─────────────────────────────────────────────│
│  💬 Comment 1                               │  ← Cuộn cùng trang
│  💬 Comment 2                               │
│  💬 Comment 3                               │
│  ...                                        │
│─────────────────────────────────────────────│
│  [Viết bình luận...] [▶ Gửi]   (sticky bottom)│
└─────────────────────────────────────────────┘

Verification Plan

Automated Tests

  • npm run build -w frontend — không có lỗi compilation.

Manual Verification

  1. Mở app trên Chrome mobile (Android) → modal mở full screen, ảnh chạm top, không có padding.
  2. Nút X hiển thị góc trên phải, luôn visible.
  3. Tên người đăng hiển thị overlay góc trên trái, không nhầm với người bình luận.
  4. Nút like hiển thị góc dưới phải của ảnh.
  5. Vuốt lên → ảnh và bình luận cuộn cùng nhau một cách liền mạch.
  6. Ô bình luận sticky ở dưới cùng.
  7. Trên desktop (≥768px) layout 2 cột giữ nguyên hoàn toàn.
  8. Safe area insets hoạt động đúng trên iPhone (notch/Dynamic Island) dùng Safari.