the build
LiveKendro
A virtual co-working space — take a desk, say what you're working on, and work the block alongside your team, your friends, or whoever else is in the room.
- My role
- Solo — product, client, server, schema
- Timeline
- 2025 — ongoing
- Core stack
- React 18 · Vite · Tailwind v4

what's in it
Features
- Sessions are the unit, rooms are optional
- A
Sessioncan haveroom: null. That one nullable field is the difference between 'sign up and start working' and 'sign up, find a room, request to join, wait for a stranger to approve you' — and the grid, stats and presence all read the same collection either way. - A floor of desks, not a call
- Every person is a nameplate carrying their name, their work topic and a clock since they sat down; video fills the desk only when the camera is on. Camera-off is the common case in co-working, so it gets a real state instead of a black rectangle.
- Presence is one query
GET /sessions/liveresolves everyone you share a team, room or friendship with and returns their open sessions with the connection labelled, so the dashboard can't drift from what the team page shows.- Chat channels are reconciled, not hooked
POST /chat/syncdiffs your rooms and teams against your Stream channels and fixes the difference. Hooking eight mutation sites instead means any one failing quietly strands you in a channel for a room you left; the reconcile is idempotent and heals rooms that predate chat.- Durations are never trusted from the wire
actualDurationis derived server-side, has break time subtracted and is capped at twice the session's own target. A throttled sweep closes anything that stops sending a heartbeat, flags itabandoned, and abandoned rows are excluded from every aggregate.- The paywall is one line, in one place
createTeamchecksuser.plan. Nothing else does — so an invited member gets the whole team experience without an upgrade, and a pricing change touches a single function.- Team rooms are private by construction
getAllRoomsonly returns rooms whoseteamis null or one of yours, andrequestToJoinRoomre-checks membership before admitting anyone. Visibility and access are enforced separately, so neither is the only thing between a stranger and a company room.- One hook owns "in session"
- The dashboard, sidebar and session page all read
useActiveSession, so they can't disagree — the dashboard used to offer 'take a desk' to someone already mid-session. - A record that buckets by your calendar
- Every finished session fills a square on a contribution grid, bucketed by the user's local date, so a session closed at 11pm lands on the day it felt like.
- Invites that degrade instead of failing
- Team invites go out over Gmail SMTP; with the credentials unset — or the send failing — the invite is still created and the toast offers a shareable link, so a mail outage can't stop a team being built.
a look around
What it looks like in use
Two people at their desks with a task list and clock beside them. Desks lay out with auto-fill rather than viewport breakpoints, because the session panel takes 384px out of the column those breakpoints assume.
presence is the point
Hours and a year of squares first, then who else is at a desk right now — one /sessions/live call across teams, rooms and friends.
Breaks come off the clock, so "49 minutes worked" means work, not the timer being open.
A channel per room and per team, created from the database rather than by hand — joining a room adds its conversation, leaving takes it away.
Who's at a desk, hours logged this week per person, and the rooms only members can open. Creating a team is the paid part; joining one never is.
The stat row and the grid fold to one column; nothing is hidden behind a 'view on desktop' notice.
same room, one hand
The floor keeps its desk nameplates on a phone, with the call controls docked where a thumb already is.
what it's made of
The stack
- Client
- React 18
- Vite
- Tailwind v4
- DaisyUI 5
- TanStack Query
- Zustand
- React Router 7
- Server
- Node
- Express 4
- Mongoose
- JWT
- bcrypt
- Data
- MongoDB (Atlas)
- Services
- Stream (chat + video)
- Nodemailer over Gmail
- ImgBB