From 6fa261b68fb4d6794d17e1deeb819f2dab63be84 Mon Sep 17 00:00:00 2001 From: sravan Date: Wed, 15 Jul 2026 00:42:53 +0530 Subject: [PATCH] feat(meetings): call log, past pagination + date filter; last-seen exact time (batch83) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #7 The past list was hard-capped at 12 (a .slice(0,12) in the client) and there was NO record of how many people were ever in a finished call — so the "only calls with >2 people" rule was impossible to apply. Added a call_history table: signaling tracks the HIGH-WATER participant count per room and logs the call when the room tears down (scheduled meetings are skipped — they already have their own row). Past meetings now follow the rules asked for: • a plain 1:1 direct call is NOT listed — unless it produced a recording/transcript (those already surface as recording entries); • a call that ever held MORE than 2 people IS listed (e.g. a 1:1 a third person joined), showing its participant count and duration; • entries are visible only to people who were actually in the call (or the group). Server-side pagination (10/page) + a from/to date filter; nothing is double-listed. #2 Last seen now shows the exact time/date, WhatsApp-style — "last seen today at 1:36 PM", "last seen yesterday at 10:15 AM", "last seen 14/07/2026 at 9:00 AM" — instead of "10 minutes ago". Co-Authored-By: Claude Opus 4.8 --- server/db.js | 18 ++++++++++ server/public/home.html | 78 +++++++++++++++++++++++++++++++---------- server/repos.js | 11 +++++- server/routes.js | 42 +++++++++++++++++++++- server/signaling.js | 35 +++++++++++++++++- 5 files changed, 163 insertions(+), 21 deletions(-) diff --git a/server/db.js b/server/db.js index b44229f..84c7414 100644 --- a/server/db.js +++ b/server/db.js @@ -238,6 +238,24 @@ try { db.exec('CREATE INDEX IF NOT EXISTS idx_users_bizgaze_user_id ON users(biz // When two accounts merge (#2), the merged-away row is deleted. This records old_id -> survivor so // any lingering reference to the old id (a cached contact, an in-flight DM) resolves to the survivor // instead of hitting a deleted user (which made messages to merged contacts silently vanish). +// #7: a log of finished CALLS (ad-hoc / group / 1:1). Scheduled meetings already have their own row, so +// they're not duplicated here. `peak` is the most people who were in the room at once — that's what lets +// "Past meetings" show a call that grew past 2 people while hiding plain 1:1s. +db.exec(` +CREATE TABLE IF NOT EXISTS call_history ( + id TEXT PRIMARY KEY, + team_id TEXT NOT NULL, + room TEXT NOT NULL, + group_id TEXT, + kind TEXT, + title TEXT, + peak INTEGER NOT NULL DEFAULT 0, + participants TEXT, + uids TEXT, + started_at INTEGER NOT NULL, + ended_at INTEGER NOT NULL +)`); +try { db.exec('CREATE INDEX IF NOT EXISTS idx_call_history_team ON call_history(team_id, ended_at)'); } catch (e) {} db.exec(` CREATE TABLE IF NOT EXISTS user_aliases ( old_id TEXT PRIMARY KEY, diff --git a/server/public/home.html b/server/public/home.html index f3f3c2a..f3a2ab7 100644 --- a/server/public/home.html +++ b/server/public/home.html @@ -820,7 +820,18 @@ .gi-open{cursor:pointer;} .sched-wrap{width:100%;margin:1.2rem 0 0;text-align:left;} .sched-sec{margin-bottom:1.4rem;} - .sched-h{font-size:.74rem;text-transform:uppercase;letter-spacing:.05em;color:var(--muted);margin:0 0 .55rem;font-weight:700;} + .sched-h{display:flex;align-items:center;gap:.6rem;font-size:.74rem;text-transform:uppercase;letter-spacing:.05em;color:var(--muted);margin:0 0 .55rem;font-weight:700;} + /* #7: past-meetings date filter + pager */ + .mtg-filter{display:inline-flex;align-items:center;gap:.35rem;margin-left:auto;text-transform:none;letter-spacing:0;} + .mtg-filter svg{color:var(--blue);flex:0 0 auto;} + .mtg-filter input[type=date]{border:1px solid var(--line);border-radius:8px;padding:.25rem .4rem;font:inherit;font-size:.72rem;font-weight:600;background:#fbfcfe;color:var(--ink);} + .mtg-filter input[type=date]:focus{outline:none;border-color:var(--blue);} + .mtg-clear{border:none;background:transparent;color:var(--muted);cursor:pointer;display:grid;place-items:center;padding:.15rem;border-radius:50%;} + .mtg-clear:hover{background:#fee2e2;color:var(--red);} + .mtg-pager{display:flex;align-items:center;justify-content:center;gap:.7rem;margin-top:.7rem;font-size:.76rem;color:var(--muted);} + .mtg-pg{border:1px solid var(--line);background:var(--card);color:var(--blue);border-radius:8px;width:30px;height:28px;display:grid;place-items:center;cursor:pointer;} + .mtg-pg:hover:not(:disabled){background:var(--blue-soft);} + .mtg-pg:disabled{opacity:.4;cursor:default;} .sched-list{display:grid;grid-template-columns:repeat(auto-fill,minmax(330px,1fr));gap:.7rem;} .sched-item{display:flex;align-items:flex-start;gap:.6rem;background:var(--card);border:1px solid var(--line);border-radius:12px;padding:.8rem .9rem;} .sched-item.live{border-color:#34d399;background:#f0fdf4;} @@ -1037,7 +1048,7 @@ -