payfrit-portal/messaging.html
Zara a8a9861d90 Add messaging UI mockups — inbox feed and conversation views
Slack-inspired mobile-first messaging interface with:
- Main inbox with tab navigation (All, @Mentions, DMs, Channels)
- Slide-in sidebar menu with channels and portal links
- Full conversation view with message bubbles, reactions, typing indicators
- Thread/channel drill-down with compose bar
- Touch gestures (swipe to close sidebar/threads)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 23:27:30 +00:00

1182 lines
37 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Payfrit — Messages</title>
<style>
/* ========== RESET & BASE ========== */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--primary: #2D7FF9;
--primary-dark: #1A5FD1;
--bg: #FFFFFF;
--bg-secondary: #F5F7FA;
--bg-tertiary: #EDF0F5;
--text: #1A1D26;
--text-secondary: #6B7280;
--text-muted: #9CA3AF;
--border: #E5E7EB;
--green: #10B981;
--red: #EF4444;
--orange: #F59E0B;
--sidebar-bg: #1A1D26;
--sidebar-text: #D1D5DB;
--sidebar-active: #2D7FF9;
--mention-bg: #FEF3C7;
--mention-border: #F59E0B;
--unread-badge: #EF4444;
--shadow-sm: 0 1px 2px rgba(0,0,0,0.06);
--shadow-md: 0 4px 12px rgba(0,0,0,0.1);
--shadow-lg: 0 8px 24px rgba(0,0,0,0.15);
--radius: 12px;
--radius-sm: 8px;
--safe-top: env(safe-area-inset-top, 0px);
--safe-bottom: env(safe-area-inset-bottom, 0px);
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: var(--bg);
color: var(--text);
line-height: 1.5;
overflow: hidden;
height: 100dvh;
width: 100vw;
position: relative;
}
/* ========== APP SHELL ========== */
.app-shell {
display: flex;
flex-direction: column;
height: 100dvh;
width: 100%;
position: relative;
}
/* ========== TOP BAR ========== */
.top-bar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
padding-top: calc(12px + var(--safe-top));
background: var(--bg);
border-bottom: 1px solid var(--border);
z-index: 100;
min-height: 56px;
}
.top-bar-left {
display: flex;
align-items: center;
gap: 12px;
}
.menu-btn {
width: 36px;
height: 36px;
border: none;
background: none;
cursor: pointer;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 5px;
padding: 6px;
border-radius: var(--radius-sm);
transition: background 0.15s;
}
.menu-btn:active { background: var(--bg-secondary); }
.menu-btn span {
display: block;
width: 20px;
height: 2px;
background: var(--text);
border-radius: 2px;
transition: transform 0.2s, opacity 0.2s;
}
.menu-btn.active span:nth-child(1) { transform: rotate(45deg) translate(5px, 5px); }
.menu-btn.active span:nth-child(2) { opacity: 0; }
.menu-btn.active span:nth-child(3) { transform: rotate(-45deg) translate(5px, -5px); }
.top-bar-title {
font-size: 17px;
font-weight: 700;
color: var(--text);
}
.top-bar-subtitle {
font-size: 12px;
color: var(--text-muted);
font-weight: 400;
}
.top-bar-right {
display: flex;
align-items: center;
gap: 8px;
}
.icon-btn {
width: 36px;
height: 36px;
border: none;
background: none;
cursor: pointer;
border-radius: var(--radius-sm);
display: flex;
align-items: center;
justify-content: center;
position: relative;
transition: background 0.15s;
}
.icon-btn:active { background: var(--bg-secondary); }
.icon-btn svg {
width: 22px;
height: 22px;
color: var(--text-secondary);
}
.badge {
position: absolute;
top: 2px;
right: 2px;
width: 18px;
height: 18px;
background: var(--unread-badge);
color: #fff;
border-radius: 50%;
font-size: 10px;
font-weight: 700;
display: flex;
align-items: center;
justify-content: center;
border: 2px solid var(--bg);
}
/* ========== TAB NAV ========== */
.tab-nav {
display: flex;
background: var(--bg);
border-bottom: 1px solid var(--border);
padding: 0 16px;
gap: 4px;
}
.tab {
flex: 1;
padding: 10px 8px;
background: none;
border: none;
border-bottom: 2px solid transparent;
font-size: 13px;
font-weight: 600;
color: var(--text-muted);
cursor: pointer;
text-align: center;
transition: color 0.15s, border-color 0.15s;
position: relative;
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
}
.tab.active {
color: var(--primary);
border-bottom-color: var(--primary);
}
.tab:active { color: var(--text-secondary); }
.tab-badge {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 18px;
height: 18px;
padding: 0 5px;
background: var(--unread-badge);
color: #fff;
border-radius: 9px;
font-size: 10px;
font-weight: 700;
}
/* ========== CONTENT AREA ========== */
.content-area {
flex: 1;
overflow-y: auto;
overflow-x: hidden;
-webkit-overflow-scrolling: touch;
background: var(--bg-secondary);
}
/* ========== MESSAGE LIST ========== */
.message-list {
padding: 8px 0;
}
.message-item {
display: flex;
gap: 10px;
padding: 10px 16px;
background: var(--bg);
border-bottom: 1px solid var(--bg-tertiary);
cursor: pointer;
transition: background 0.1s;
}
.message-item:active { background: var(--bg-secondary); }
.message-item.unread { background: #F0F7FF; }
.message-item.unread .msg-sender { font-weight: 700; }
.message-item.unread .msg-preview { color: var(--text); font-weight: 500; }
.message-item.mention {
border-left: 3px solid var(--mention-border);
background: var(--mention-bg);
}
.msg-avatar {
width: 44px;
height: 44px;
border-radius: 10px;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
font-weight: 700;
color: #fff;
}
.msg-body {
flex: 1;
min-width: 0;
}
.msg-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 2px;
}
.msg-sender {
font-size: 15px;
font-weight: 600;
color: var(--text);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.msg-channel {
font-size: 12px;
color: var(--text-muted);
font-weight: 400;
}
.msg-time {
font-size: 12px;
color: var(--text-muted);
white-space: nowrap;
flex-shrink: 0;
}
.msg-preview {
font-size: 14px;
color: var(--text-secondary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
line-height: 1.4;
}
.msg-preview .mention-tag {
color: var(--primary);
font-weight: 600;
}
.unread-dot {
width: 8px;
height: 8px;
background: var(--primary);
border-radius: 50%;
flex-shrink: 0;
margin-top: 4px;
}
/* ========== SECTION HEADERS ========== */
.section-header {
padding: 10px 16px 6px;
font-size: 12px;
font-weight: 700;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.5px;
background: var(--bg-secondary);
}
/* ========== SLIDE-IN MENU (Sidebar) ========== */
.overlay {
position: fixed;
inset: 0;
background: rgba(0,0,0,0.4);
z-index: 200;
opacity: 0;
pointer-events: none;
transition: opacity 0.25s;
}
.overlay.active {
opacity: 1;
pointer-events: auto;
}
.sidebar {
position: fixed;
top: 0;
left: 0;
bottom: 0;
width: 300px;
max-width: 85vw;
background: var(--sidebar-bg);
z-index: 300;
transform: translateX(-100%);
transition: transform 0.25s ease;
display: flex;
flex-direction: column;
padding-top: var(--safe-top);
}
.sidebar.active { transform: translateX(0); }
.sidebar-header {
padding: 20px 16px 12px;
border-bottom: 1px solid rgba(255,255,255,0.08);
}
.sidebar-logo {
font-size: 20px;
font-weight: 800;
color: #fff;
letter-spacing: -0.5px;
}
.sidebar-logo span { color: var(--primary); }
.sidebar-user {
display: flex;
align-items: center;
gap: 10px;
margin-top: 14px;
}
.sidebar-user-avatar {
width: 36px;
height: 36px;
border-radius: 8px;
background: var(--primary);
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-weight: 700;
font-size: 14px;
}
.sidebar-user-info {
flex: 1;
}
.sidebar-user-name {
font-size: 14px;
font-weight: 600;
color: #fff;
}
.sidebar-user-status {
font-size: 12px;
color: var(--green);
display: flex;
align-items: center;
gap: 4px;
}
.sidebar-user-status::before {
content: '';
width: 6px;
height: 6px;
background: var(--green);
border-radius: 50%;
}
.sidebar-nav {
flex: 1;
overflow-y: auto;
padding: 12px 0;
}
.sidebar-section {
padding: 0 12px;
margin-bottom: 16px;
}
.sidebar-section-title {
font-size: 11px;
font-weight: 700;
color: rgba(255,255,255,0.4);
text-transform: uppercase;
letter-spacing: 0.8px;
padding: 0 8px 8px;
}
.sidebar-link {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 10px;
border-radius: var(--radius-sm);
color: var(--sidebar-text);
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: background 0.1s;
text-decoration: none;
}
.sidebar-link:active, .sidebar-link.active { background: rgba(255,255,255,0.08); }
.sidebar-link.active { color: #fff; font-weight: 600; }
.sidebar-link svg {
width: 18px;
height: 18px;
flex-shrink: 0;
opacity: 0.6;
}
.sidebar-link.active svg { opacity: 1; }
.sidebar-link .link-badge {
margin-left: auto;
min-width: 20px;
height: 20px;
padding: 0 6px;
background: var(--unread-badge);
color: #fff;
border-radius: 10px;
font-size: 11px;
font-weight: 700;
display: flex;
align-items: center;
justify-content: center;
}
.channel-icon {
font-size: 16px;
width: 18px;
text-align: center;
opacity: 0.6;
}
/* ========== COMPOSE BAR ========== */
.compose-bar {
display: none;
align-items: center;
gap: 8px;
padding: 10px 16px;
padding-bottom: calc(10px + var(--safe-bottom));
background: var(--bg);
border-top: 1px solid var(--border);
}
.compose-bar.visible { display: flex; }
.compose-input {
flex: 1;
padding: 10px 14px;
border: 1px solid var(--border);
border-radius: 20px;
font-size: 15px;
background: var(--bg-secondary);
color: var(--text);
outline: none;
transition: border-color 0.15s;
}
.compose-input:focus { border-color: var(--primary); }
.compose-input::placeholder { color: var(--text-muted); }
.send-btn {
width: 40px;
height: 40px;
border: none;
background: var(--primary);
color: #fff;
border-radius: 50%;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
transition: background 0.15s, transform 0.1s;
}
.send-btn:active { background: var(--primary-dark); transform: scale(0.95); }
.send-btn svg { width: 20px; height: 20px; }
/* ========== THREAD VIEW (shown when message is tapped) ========== */
.thread-view {
position: fixed;
inset: 0;
background: var(--bg);
z-index: 150;
display: flex;
flex-direction: column;
transform: translateX(100%);
transition: transform 0.25s ease;
}
.thread-view.active { transform: translateX(0); }
.thread-header {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 16px;
padding-top: calc(12px + var(--safe-top));
border-bottom: 1px solid var(--border);
min-height: 56px;
}
.back-btn {
width: 36px;
height: 36px;
border: none;
background: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
border-radius: var(--radius-sm);
}
.back-btn:active { background: var(--bg-secondary); }
.back-btn svg { width: 22px; height: 22px; color: var(--text); }
.thread-title {
font-size: 17px;
font-weight: 700;
}
.thread-messages {
flex: 1;
overflow-y: auto;
padding: 16px;
}
.thread-msg {
display: flex;
gap: 10px;
margin-bottom: 16px;
}
.thread-msg-avatar {
width: 36px;
height: 36px;
border-radius: 8px;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 13px;
font-weight: 700;
color: #fff;
}
.thread-msg-content { flex: 1; }
.thread-msg-header {
display: flex;
align-items: baseline;
gap: 8px;
margin-bottom: 4px;
}
.thread-msg-name { font-size: 14px; font-weight: 700; }
.thread-msg-time { font-size: 12px; color: var(--text-muted); }
.thread-msg-text {
font-size: 15px;
line-height: 1.5;
color: var(--text);
}
.thread-msg-text .mention-inline {
background: #E0EDFF;
color: var(--primary);
padding: 1px 4px;
border-radius: 4px;
font-weight: 600;
}
/* ========== EMPTY STATE ========== */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60px 32px;
text-align: center;
}
.empty-state-icon {
width: 64px;
height: 64px;
background: var(--bg-tertiary);
border-radius: 16px;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 16px;
font-size: 28px;
}
.empty-state-title {
font-size: 17px;
font-weight: 700;
margin-bottom: 6px;
}
.empty-state-text {
font-size: 14px;
color: var(--text-secondary);
max-width: 260px;
}
/* ========== ANIMATIONS ========== */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
.message-item { animation: fadeIn 0.2s ease; }
/* ========== RESPONSIVE ========== */
@media (min-width: 768px) {
.sidebar { width: 320px; }
.message-item { padding: 12px 20px; }
.top-bar { padding: 12px 20px; }
.tab-nav { padding: 0 20px; }
}
</style>
</head>
<body>
<!-- OVERLAY for sidebar -->
<div class="overlay" id="overlay" onclick="toggleSidebar()"></div>
<!-- SLIDE-IN SIDEBAR -->
<nav class="sidebar" id="sidebar">
<div class="sidebar-header">
<div class="sidebar-logo">Pay<span>frit</span></div>
<div class="sidebar-user">
<div class="sidebar-user-avatar">Z</div>
<div class="sidebar-user-info">
<div class="sidebar-user-name">Zara</div>
<div class="sidebar-user-status">Active</div>
</div>
</div>
</div>
<div class="sidebar-nav">
<!-- Main Nav -->
<div class="sidebar-section">
<a class="sidebar-link active" onclick="switchTab('all')">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
All Messages
<span class="link-badge">5</span>
</a>
<a class="sidebar-link" onclick="switchTab('mentions')">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="4"/><path d="M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-3.92 7.94"/></svg>
Mentions
<span class="link-badge">3</span>
</a>
<a class="sidebar-link" onclick="switchTab('dms')">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
Direct Messages
</a>
</div>
<!-- Channels -->
<div class="sidebar-section">
<div class="sidebar-section-title">Channels</div>
<a class="sidebar-link">
<span class="channel-icon">#</span>
general
<span class="link-badge">2</span>
</a>
<a class="sidebar-link">
<span class="channel-icon">#</span>
product-health
</a>
<a class="sidebar-link">
<span class="channel-icon">#</span>
api-updates
<span class="link-badge">1</span>
</a>
<a class="sidebar-link">
<span class="channel-icon">#</span>
portal-feedback
</a>
<a class="sidebar-link">
<span class="channel-icon">#</span>
bugs
</a>
</div>
<!-- Portal Links -->
<div class="sidebar-section">
<div class="sidebar-section-title">Portal</div>
<a class="sidebar-link" href="scan.html">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/></svg>
Scan Product
</a>
<a class="sidebar-link" href="dashboard.html">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18M9 21V9"/></svg>
Dashboard
</a>
</div>
</div>
</nav>
<!-- APP SHELL -->
<div class="app-shell">
<!-- TOP BAR -->
<header class="top-bar">
<div class="top-bar-left">
<button class="menu-btn" id="menuBtn" onclick="toggleSidebar()" aria-label="Menu">
<span></span><span></span><span></span>
</button>
<div>
<div class="top-bar-title">Messages</div>
<div class="top-bar-subtitle" id="connectionStatus">Connected</div>
</div>
</div>
<div class="top-bar-right">
<button class="icon-btn" aria-label="Search">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>
</button>
<button class="icon-btn" aria-label="Notifications">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg>
<span class="badge">3</span>
</button>
</div>
</header>
<!-- TAB NAV -->
<div class="tab-nav">
<button class="tab active" data-tab="all" onclick="switchTab('all')">All</button>
<button class="tab" data-tab="mentions" onclick="switchTab('mentions')">
@ Mentions <span class="tab-badge">3</span>
</button>
<button class="tab" data-tab="dms" onclick="switchTab('dms')">DMs</button>
<button class="tab" data-tab="channels" onclick="switchTab('channels')">Channels</button>
</div>
<!-- CONTENT -->
<div class="content-area" id="contentArea">
<!-- ALL MESSAGES TAB -->
<div class="tab-content" id="tab-all">
<div class="message-list">
<div class="section-header">Today</div>
<!-- Unread mention -->
<div class="message-item unread mention" onclick="openThread('thread-1')">
<div class="msg-avatar" style="background: #7C3AED;">K</div>
<div class="msg-body">
<div class="msg-header">
<div class="msg-sender">Koda <span class="msg-channel">in #general</span></div>
<span class="msg-time">2m ago</span>
</div>
<div class="msg-preview"><span class="mention-tag">@zara</span> can you check the health score display on the scan page? Something looks off with the color mapping</div>
</div>
<div class="unread-dot"></div>
</div>
<!-- Unread -->
<div class="message-item unread" onclick="openThread('thread-2')">
<div class="msg-avatar" style="background: #059669;">S</div>
<div class="msg-body">
<div class="msg-header">
<div class="msg-sender">Sarah <span class="msg-channel">in #product-health</span></div>
<span class="msg-time">18m ago</span>
</div>
<div class="msg-preview">New batch of nutritional data just landed — 450 products updated. API should reflect changes within the hour 🎉</div>
</div>
<div class="unread-dot"></div>
</div>
<!-- Unread mention -->
<div class="message-item unread mention" onclick="openThread('thread-3')">
<div class="msg-avatar" style="background: #2D7FF9;">J</div>
<div class="msg-body">
<div class="msg-header">
<div class="msg-sender">John <span class="msg-channel">in #general</span></div>
<span class="msg-time">34m ago</span>
</div>
<div class="msg-preview"><span class="mention-tag">@zara</span> get those mockups created, i want to see some designs</div>
</div>
<div class="unread-dot"></div>
</div>
<!-- Read -->
<div class="message-item" onclick="openThread('thread-4')">
<div class="msg-avatar" style="background: #DC2626;">C</div>
<div class="msg-body">
<div class="msg-header">
<div class="msg-sender">Claude <span class="msg-channel">in #api-updates</span></div>
<span class="msg-time">1h ago</span>
</div>
<div class="msg-preview">API v2 endpoint for alternatives is live. Response times averaging 120ms — well within our target</div>
</div>
</div>
<!-- Mention -->
<div class="message-item mention" onclick="openThread('thread-5')">
<div class="msg-avatar" style="background: #F59E0B;">A</div>
<div class="msg-body">
<div class="msg-header">
<div class="msg-sender">Ava <span class="msg-channel">in #portal-feedback</span></div>
<span class="msg-time">2h ago</span>
</div>
<div class="msg-preview"><span class="mention-tag">@zara</span> I'm working on some visual concepts for the comparison page — will drop them later today</div>
</div>
</div>
<div class="section-header">Yesterday</div>
<div class="message-item" onclick="openThread('thread-6')">
<div class="msg-avatar" style="background: #7C3AED;">K</div>
<div class="msg-body">
<div class="msg-header">
<div class="msg-sender">Koda <span class="msg-channel">in #general</span></div>
<span class="msg-time">Yesterday</span>
</div>
<div class="msg-preview">Pushed 50+ files today. Android app is taking shape. Let me know if you need anything from the mobile side</div>
</div>
</div>
<div class="message-item" onclick="openThread('thread-7')">
<div class="msg-avatar" style="background: #059669;">S</div>
<div class="msg-body">
<div class="msg-header">
<div class="msg-sender">Sarah <span class="msg-channel">in #product-health</span></div>
<span class="msg-time">Yesterday</span>
</div>
<div class="msg-preview">Health score algorithm v3 is looking solid. Weighted nutrient scoring is giving much better results for edge cases</div>
</div>
</div>
<div class="message-item" onclick="openThread('thread-8')">
<div class="msg-avatar" style="background: #2D7FF9;">J</div>
<div class="msg-body">
<div class="msg-header">
<div class="msg-sender">John <span class="msg-channel">in #general</span></div>
<span class="msg-time">Yesterday</span>
</div>
<div class="msg-preview">Great progress everyone! Keep it up — we're building something people actually need 🚀</div>
</div>
</div>
</div>
</div>
<!-- MENTIONS TAB -->
<div class="tab-content" id="tab-mentions" style="display:none;">
<div class="message-list">
<div class="section-header">Today</div>
<div class="message-item unread mention" onclick="openThread('thread-1')">
<div class="msg-avatar" style="background: #7C3AED;">K</div>
<div class="msg-body">
<div class="msg-header">
<div class="msg-sender">Koda <span class="msg-channel">in #general</span></div>
<span class="msg-time">2m ago</span>
</div>
<div class="msg-preview"><span class="mention-tag">@zara</span> can you check the health score display on the scan page?</div>
</div>
</div>
<div class="message-item unread mention" onclick="openThread('thread-3')">
<div class="msg-avatar" style="background: #2D7FF9;">J</div>
<div class="msg-body">
<div class="msg-header">
<div class="msg-sender">John <span class="msg-channel">in #general</span></div>
<span class="msg-time">34m ago</span>
</div>
<div class="msg-preview"><span class="mention-tag">@zara</span> get those mockups created, i want to see some designs</div>
</div>
</div>
<div class="message-item mention" onclick="openThread('thread-5')">
<div class="msg-avatar" style="background: #F59E0B;">A</div>
<div class="msg-body">
<div class="msg-header">
<div class="msg-sender">Ava <span class="msg-channel">in #portal-feedback</span></div>
<span class="msg-time">2h ago</span>
</div>
<div class="msg-preview"><span class="mention-tag">@zara</span> I'm working on some visual concepts for the comparison page</div>
</div>
</div>
</div>
</div>
<!-- DMs TAB -->
<div class="tab-content" id="tab-dms" style="display:none;">
<div class="message-list">
<div class="message-item unread" onclick="openThread('dm-1')">
<div class="msg-avatar" style="background: #7C3AED;">K</div>
<div class="msg-body">
<div class="msg-header">
<div class="msg-sender">Koda</div>
<span class="msg-time">45m ago</span>
</div>
<div class="msg-preview">Hey, quick question — what format do you want the barcode data in from the Android scanner?</div>
</div>
<div class="unread-dot"></div>
</div>
<div class="message-item" onclick="openThread('dm-2')">
<div class="msg-avatar" style="background: #2D7FF9;">J</div>
<div class="msg-body">
<div class="msg-header">
<div class="msg-sender">John</div>
<span class="msg-time">3h ago</span>
</div>
<div class="msg-preview">Looking good so far, keep it up 👍</div>
</div>
</div>
<div class="message-item" onclick="openThread('dm-3')">
<div class="msg-avatar" style="background: #F59E0B;">A</div>
<div class="msg-body">
<div class="msg-header">
<div class="msg-sender">Ava</div>
<span class="msg-time">Yesterday</span>
</div>
<div class="msg-preview">Sent you the color palette for the health scores — check your email!</div>
</div>
</div>
</div>
</div>
<!-- CHANNELS TAB -->
<div class="tab-content" id="tab-channels" style="display:none;">
<div class="message-list">
<div class="message-item" onclick="openThread('ch-general')" style="border-left: 3px solid var(--primary);">
<div class="msg-avatar" style="background: var(--primary); font-size: 20px;">#</div>
<div class="msg-body">
<div class="msg-header">
<div class="msg-sender">general</div>
<span class="msg-time">2m ago</span>
</div>
<div class="msg-preview">Koda: @zara can you check the health score display...</div>
</div>
<div class="unread-dot"></div>
</div>
<div class="message-item" onclick="openThread('ch-product')">
<div class="msg-avatar" style="background: var(--green); font-size: 20px;">#</div>
<div class="msg-body">
<div class="msg-header">
<div class="msg-sender">product-health</div>
<span class="msg-time">18m ago</span>
</div>
<div class="msg-preview">Sarah: New batch of nutritional data just landed...</div>
</div>
<div class="unread-dot"></div>
</div>
<div class="message-item" onclick="openThread('ch-api')">
<div class="msg-avatar" style="background: var(--orange); font-size: 20px;">#</div>
<div class="msg-body">
<div class="msg-header">
<div class="msg-sender">api-updates</div>
<span class="msg-time">1h ago</span>
</div>
<div class="msg-preview">Claude: API v2 endpoint for alternatives is live...</div>
</div>
</div>
<div class="message-item" onclick="openThread('ch-portal')">
<div class="msg-avatar" style="background: #7C3AED; font-size: 20px;">#</div>
<div class="msg-body">
<div class="msg-header">
<div class="msg-sender">portal-feedback</div>
<span class="msg-time">2h ago</span>
</div>
<div class="msg-preview">Ava: @zara I'm working on some visual concepts...</div>
</div>
</div>
<div class="message-item" onclick="openThread('ch-bugs')">
<div class="msg-avatar" style="background: var(--red); font-size: 20px;">#</div>
<div class="msg-body">
<div class="msg-header">
<div class="msg-sender">bugs</div>
<span class="msg-time">2d ago</span>
</div>
<div class="msg-preview">No new messages</div>
</div>
</div>
</div>
</div>
</div>
<!-- COMPOSE BAR (hidden on main view, visible in threads) -->
<div class="compose-bar" id="composeBar">
<button class="icon-btn" aria-label="Attach">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.49"/></svg>
</button>
<input class="compose-input" type="text" placeholder="Message #general..." id="composeInput">
<button class="send-btn" aria-label="Send">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 2L11 13"/><path d="M22 2l-7 20-4-9-9-4 20-7z"/></svg>
</button>
</div>
</div>
<!-- THREAD VIEW -->
<div class="thread-view" id="threadView">
<div class="thread-header">
<button class="back-btn" onclick="closeThread()" aria-label="Back">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 12H5"/><path d="M12 19l-7-7 7-7"/></svg>
</button>
<div>
<div class="thread-title" id="threadTitle">#general</div>
<div class="top-bar-subtitle">3 members</div>
</div>
</div>
<div class="thread-messages" id="threadMessages">
<div class="thread-msg">
<div class="thread-msg-avatar" style="background: #2D7FF9;">J</div>
<div class="thread-msg-content">
<div class="thread-msg-header">
<span class="thread-msg-name">John</span>
<span class="thread-msg-time">10:15 AM</span>
</div>
<div class="thread-msg-text">Great progress everyone! Keep pushing — we're building something people actually need 🚀</div>
</div>
</div>
<div class="thread-msg">
<div class="thread-msg-avatar" style="background: #7C3AED;">K</div>
<div class="thread-msg-content">
<div class="thread-msg-header">
<span class="thread-msg-name">Koda</span>
<span class="thread-msg-time">10:32 AM</span>
</div>
<div class="thread-msg-text"><span class="mention-inline">@zara</span> can you check the health score display on the scan page? Something looks off with the color mapping — scores in the 60-70 range are showing green instead of yellow</div>
</div>
</div>
<div class="thread-msg">
<div class="thread-msg-avatar" style="background: #059669;">S</div>
<div class="thread-msg-content">
<div class="thread-msg-header">
<span class="thread-msg-name">Sarah</span>
<span class="thread-msg-time">10:45 AM</span>
</div>
<div class="thread-msg-text">That might be related to the v3 scoring changes — thresholds shifted a bit. Zara might need to update the color breakpoints on the frontend</div>
</div>
</div>
</div>
<div class="compose-bar visible">
<button class="icon-btn" aria-label="Attach">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.49"/></svg>
</button>
<input class="compose-input" type="text" placeholder="Reply in thread...">
<button class="send-btn" aria-label="Send">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 2L11 13"/><path d="M22 2l-7 20-4-9-9-4 20-7z"/></svg>
</button>
</div>
</div>
<script>
// ========== SIDEBAR ==========
function toggleSidebar() {
const sidebar = document.getElementById('sidebar');
const overlay = document.getElementById('overlay');
const menuBtn = document.getElementById('menuBtn');
const isActive = sidebar.classList.contains('active');
sidebar.classList.toggle('active');
overlay.classList.toggle('active');
menuBtn.classList.toggle('active');
}
// Close sidebar on swipe left
let touchStartX = 0;
document.getElementById('sidebar').addEventListener('touchstart', (e) => {
touchStartX = e.touches[0].clientX;
});
document.getElementById('sidebar').addEventListener('touchmove', (e) => {
const diff = touchStartX - e.touches[0].clientX;
if (diff > 80) toggleSidebar();
});
// ========== TABS ==========
function switchTab(tabName) {
// Close sidebar if open
const sidebar = document.getElementById('sidebar');
if (sidebar.classList.contains('active')) toggleSidebar();
// Update tab buttons
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
const tabBtn = document.querySelector(`.tab[data-tab="${tabName}"]`);
if (tabBtn) tabBtn.classList.add('active');
// Update sidebar links
document.querySelectorAll('.sidebar-link').forEach(l => l.classList.remove('active'));
// Show content
document.querySelectorAll('.tab-content').forEach(c => c.style.display = 'none');
const content = document.getElementById(`tab-${tabName}`);
if (content) content.style.display = 'block';
// Update title
const titles = { all: 'Messages', mentions: '@ Mentions', dms: 'Direct Messages', channels: 'Channels' };
document.querySelector('.top-bar-title').textContent = titles[tabName] || 'Messages';
}
// ========== THREAD VIEW ==========
function openThread(threadId) {
document.getElementById('threadView').classList.add('active');
document.body.style.overflow = 'hidden';
}
function closeThread() {
document.getElementById('threadView').classList.remove('active');
document.body.style.overflow = '';
}
// Swipe right to close thread
let threadTouchStartX = 0;
document.getElementById('threadView').addEventListener('touchstart', (e) => {
threadTouchStartX = e.touches[0].clientX;
});
document.getElementById('threadView').addEventListener('touchmove', (e) => {
const diff = e.touches[0].clientX - threadTouchStartX;
if (diff > 100 && threadTouchStartX < 50) closeThread();
});
// ========== CONNECTION STATUS ANIMATION ==========
const statusEl = document.getElementById('connectionStatus');
setInterval(() => {
statusEl.style.opacity = '0.5';
setTimeout(() => { statusEl.style.opacity = '1'; }, 500);
}, 3000);
</script>
</body>
</html>