From fad8a52da45da738e44f0ff2b46ea71f5e9981f4 Mon Sep 17 00:00:00 2001 From: sravan Date: Wed, 15 Jul 2026 01:02:59 +0530 Subject: [PATCH] fix: last-seen backfill, report-style date filter, hover overflow (batch84) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1 Last seen still read "Offline": last_seen is a NEW column, so every existing user was NULL until they happened to reconnect. Backfill it from the newest message each user sent (best evidence we already have), and also stamp it on message send — not just on connect/disconnect — so it stays fresh if a socket never closes cleanly. #3 Past-meetings filter is a report filter now: presets (Today, Yesterday, Last 7/30 days, This month, Year to date, All time) plus a Custom range that uses the SAME branded calendar popup as the scheduler. The raw looked foreign and behaved differently per browser. #4 Hover action row overflowed off-panel on SHORT received messages: it's wider than the bubble and was anchored to the bubble's right edge, so it ran off the left. Received bubbles now anchor it from the left (growing into the empty space); own messages keep the right anchor. Co-Authored-By: Claude Opus 4.8 --- server/db.js | 8 ++++ server/public/home.html | 95 +++++++++++++++++++++++++++++++++++------ server/routes.js | 1 + 3 files changed, 91 insertions(+), 13 deletions(-) diff --git a/server/db.js b/server/db.js index 84c7414..3ee6c67 100644 --- a/server/db.js +++ b/server/db.js @@ -228,6 +228,14 @@ try { db.exec('ALTER TABLE users ADD COLUMN bizgaze_user_id TEXT'); } catch (e) // #2: when this user was last connected (stamped on connect + on their last socket closing), so contacts // can show "last seen 10 minutes ago" instead of a bare "Offline". try { db.exec('ALTER TABLE users ADD COLUMN last_seen INTEGER'); } catch (e) { /* exists */ } +// Backfill: the column is new, so every existing user was NULL and therefore still read as a bare +// "Offline" until they happened to reconnect. Seed it from the last message they sent — the best +// evidence we already have of when they were last around. Only fills rows that are still NULL. +try { + db.exec(`UPDATE users SET last_seen = ( + SELECT MAX(created_at) FROM messages WHERE messages.sender_id = users.id + ) WHERE last_seen IS NULL AND EXISTS (SELECT 1 FROM messages WHERE messages.sender_id = users.id)`); +} catch (e) { /* messages table may not exist yet on a fresh db */ } // External (non-Connect) invitee emails on a scheduled meeting (#4) — JSON array. They get an emailed // guest join link instead of an in-app invite. try { db.exec('ALTER TABLE scheduled_meetings ADD COLUMN guest_emails TEXT'); } catch (e) { /* exists */ } diff --git a/server/public/home.html b/server/public/home.html index f3a2ab7..a403648 100644 --- a/server/public/home.html +++ b/server/public/home.html @@ -535,7 +535,11 @@ overlaps the bubble slightly so there's no dead gap — on a short message the icons can't float off into empty space where the hover (and the buttons) would vanish before you can click. */ /* #4: sit FULLY above the bubble (was top:-14px, which overlapped the first line of text). */ - .msg-actions{position:absolute;bottom:calc(100% - 2px);right:6px;z-index:6;display:flex;align-items:center;gap:1px;background:var(--card);border:1px solid var(--line);border-radius:999px;padding:2px 3px;box-shadow:0 4px 12px rgba(20,30,60,.2);opacity:0;pointer-events:none;transition:opacity .12s;} + .msg-actions{position:absolute;bottom:calc(100% - 2px);right:6px;z-index:6;display:flex;align-items:center;gap:1px;white-space:nowrap;background:var(--card);border:1px solid var(--line);border-radius:999px;padding:2px 3px;box-shadow:0 4px 12px rgba(20,30,60,.2);opacity:0;pointer-events:none;transition:opacity .12s;} + /* The row is wider than a SHORT bubble. Anchored right, a received ("them") bubble pushed it off the + left edge of the panel. Anchor received messages from the LEFT so it grows into the empty space. */ + .bubble.them .msg-actions{left:0;right:auto;} + .bubble.mine .msg-actions{right:0;left:auto;} .bubble:hover .msg-actions,.bubble.show-actions .msg-actions{opacity:1;pointer-events:auto;} .msg-actions button{position:static;width:26px;height:26px;border:none;background:none;border-radius:50%;display:grid;place-items:center;cursor:pointer;color:var(--blue);opacity:1;pointer-events:auto;box-shadow:none;padding:0;transition:background .12s;} .msg-actions button:hover{background:var(--blue-soft);} @@ -824,8 +828,11 @@ /* #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-filter select,.mtg-date{border:1px solid var(--line);border-radius:8px;padding:.28rem .45rem;font:inherit;font-size:.72rem;font-weight:600;background:#fbfcfe;color:var(--ink);cursor:pointer;} + .mtg-filter select:focus,.mtg-date:focus{outline:none;border-color:var(--blue);} + .mtg-date:hover{border-color:var(--blue);color:var(--blue);} + .mtg-dash{color:var(--muted);} + .date-pop{position:fixed;z-index:9760;display:block;} /* branded calendar popup (same look as the scheduler) */ .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);} @@ -1048,7 +1055,7 @@ -