{"ns":"calendar","title":"Tempo · Team calendar","version":"2.8","summary":"Tempo is a team calendar. It shows a person’s week in Day, Week, Month and Agenda views, lets them create, move and resize events on a 15-minute grid, reply to invitations, book rooms with capacity and double-booking rules, find a time that is free for everyone, and change recurring series for one date or for all dates. The signed-in user is Linh Pham; the primary time zone is GMT+7 and a second UTC column can be shown.","problem":"Teams need a calendar they can trust: events must not hide each other, rooms must never be double-booked, a change to one date of a series must not touch the others, and the guest list and time zones must always be correct.","personas":[{"name":"Organizer","need":"Create and change events, invite people, book a room that fits and is free, and edit one date or a whole series."},{"name":"Invitee","need":"See who is going, reply Yes, No or Maybe, and see declined events clearly without losing them."},{"name":"Remote colleague","need":"Read the same hours in UTC next to local time."}],"goals":["Overlapping events are always readable side by side.","A room can never hold more people than it has seats or be booked twice for the same time; back-to-back bookings are allowed.","A change with scope \"This event\" affects exactly one date; a change with \"All events\" affects every date.","Guest summaries always reflect the latest replies."],"features":[{"id":"VIEW","title":"Views and navigation","summary":"Week grid, overlaps, other views, calendars and keyboard.","reqs":[{"id":"FR-VIEW-01","weight":"Must","text":"The Week view opens on the current week with every marker of the day: today, the now line, off-hours shading, all-day events and the time-zone label.","accept":["Heading \"19 – 25 Oct 2026\"; Monday 19 is marked as today","Hours 07:00 to 21:00; gutter header \"GMT+7\" with tooltip \"Ho Chi Minh City\"","A red now-line on Monday at 10:40 with a \"10:40\" tag in the gutter","Rows before 09:00 and after 18:00 are shaded; events already finished today are faded","17 timed events; all-day row shows \"Team offsite\" (Thu 22) and \"Founders’ Day\" (Fri 23, dashed chip)"]},{"id":"FR-VIEW-02","weight":"Should","text":"Events that overlap in time share the day column in lanes side by side and never cover one another; an event that overlaps nothing uses the full width.","accept":["Monday: \"Design review\" (10:00-11:00) and \"1:1 Linh / Quang\" (10:30-11:00) each take half the column and both titles are readable","Wednesday 15:00-16:15: \"Interview — Backend\" on the left half, \"Code freeze sync\" and \"Interview debrief\" stacked on the right","\"Vendor call\" (Wed 09:30) uses the full width"]},{"id":"FR-VIEW-03","weight":"Could","text":"Icon-only controls and event flags are identified by tooltips and accessible names.","accept":["\"Today\" tooltip \"Today, Monday, 19 October\"; chevrons \"Previous week\" / \"Next week\"; view switch shows keys D, W, M, A","Globe \"Show second time zone (UTC)\"; circular arrow \"Reset demo data\"; avatar \"Signed in as Linh Pham\"","Sidebar \"1 invitation awaiting reply\"; Holidays has a lock \"Read-only calendar\"; room Fuji \"Fuji · 6 seats · Floor 3\"","Repeat flag \"Repeats: Weekly on Monday\" / \"Repeats: Every weekday (Mon–Fri)\"; \"Video call\"; \"Private\"; \"Awaiting your reply\"","Accessible name \"Code freeze sync, 15:15 to 15:45, declined\""]},{"id":"FR-VIEW-04","weight":"Could","text":"The Month view shows at most three chips per day and folds the rest into \"+N more\", which opens that day.","accept":["October 2026 grid starts on Monday 28 Sep","Wed 21: two chips then \"+3 more\"; the tooltip lists hidden titles separated by \" · \"","Clicking \"+3 more\" opens Day view for Wednesday, 21 October with 5 events"]},{"id":"FR-VIEW-05","weight":"Could","text":"The Agenda view lists the next 14 days, only days that have events, with the reply state of each row.","accept":["Heading \"19 Oct – 1 Nov 2026\"; first block \"19\", \"Oct · Mon\", \"Today\" pill","Tue 20: \"Sprint planning\" with an \"Awaiting reply\" pill and room \"Halong\"","\"Code freeze sync\" shows \"Declined\" with the title struck through; \"Team offsite\" shows \"All day\" and \"Going\"","Sun 25 has no block; clicking the date \"20\" opens Day view for Tuesday, 20 October"]},{"id":"FR-VIEW-06","weight":"Could","text":"Keyboard keys switch views and move the range, and the mini month mirrors the visible range.","accept":["d, w, m, a switch view; t returns to today; left and right arrows move by one day or week","Day view of Monday 19 shows 4 timed events","Mini month day 19 is named \"Monday, 19 October, has events\"","Clicking Wed 4 Nov in the mini month jumps to the week of 2 Nov"]},{"id":"FR-VIEW-07","weight":"Could","text":"Calendars can be hidden with the sidebar checkboxes, and Reset restores the seeded week.","accept":["Unchecking Holidays hides \"Founders’ Day\" only; checking it brings it back","Reset shows toast \"Calendar restored\": deleted events return, hidden calendars are checked again, 17 timed events"]}]},{"id":"POP","title":"Event popover, permissions and replies","summary":"Details, organizer-only editing and RSVP.","reqs":[{"id":"FR-POP-01","weight":"Must","text":"Clicking an event opens a popover with date and duration, calendar, room, guests and the reply summary; the organizer sees Edit, Delete and Close and no reply buttons.","accept":["\"Design review\": \"Monday, 19 October · 10:00 – 11:00 (1 h)\", room \"Fuji · 6 seats · Floor 3\"","Guests header \"3 guests · 2 yes, 1 maybe\"; rows Linh Pham (you) with an \"Organizer\" tag, Minh Ho, Thao Vu","Hovering a guest shows \"Thao Vu · Maybe\"; Escape closes the popover"]},{"id":"FR-POP-02","weight":"Should","text":"Only the organizer can change an event; for everyone else the popover shows a lock, the event cannot be dragged, and the server refuses the change.","accept":["\"Roadmap Q4\" shows a lock with \"Only Thao Vu can edit\" and a \"Going?\" row with Yes pressed; dragging it does not move it","Dragging \"Interview — Backend\" leaves it at 15:00 - 16:00; lock \"Only Khoa Ngo can edit\"","Changing \"Sprint planning\" through the API returns 403 \"Only Minh Ho can change Sprint planning\"","Read-only calendar events (Founders’ Day, \"Holidays · read-only calendar\") have no Edit or Delete"]},{"id":"FR-POP-03","weight":"Must","text":"Replying Yes, No or Maybe updates the guest summary immediately, the event styling and the sidebar count of unanswered invitations.","accept":["\"Sprint planning\" starts at \"5 guests · 2 yes, 1 no, 1 maybe, 1 awaiting\"","Yes: toast \"You’re going\", header \"5 guests · 3 yes, 1 no, 1 maybe\", solid outline, no \"?\" flag, and the sidebar line \"1 invitation awaiting reply\" disappears","Linh’s own guest row shows the new reply","An organizer replying to their own event is refused: 422 \"You organize Design review\""]},{"id":"FR-POP-04","weight":"Should","text":"A declined event stays visible, striped with a struck-through title, and changing the reply back removes the styling.","accept":["No: toast \"Invitation declined\", summary \"2 yes, 2 no, 1 maybe\"","Maybe: toast \"Replied maybe\", summary \"2 yes, 1 no, 2 maybe\"; the event is no longer striped"]}]},{"id":"CRE","title":"Creating events","summary":"Drag-create, quick-create and validation.","reqs":[{"id":"FR-CRE-01","weight":"Must","text":"Dragging on an empty part of the grid selects a time range that quick-create turns into an event on the Work, Team or Personal calendar.","accept":["Dragging Wed 11:00 to 12:00 shows \"11:00 – 12:00\" and opens a card \"Wed 21 Oct · 11:00 – 12:00 (1 h)\"","The calendar list offers Work, Team and Personal, not Holidays","Saving without a title shows \"Title is required\"","With the title \"Pairing\" the toast is \"Created Pairing\" and the event is in the Work colour"]},{"id":"FR-CRE-02","weight":"Could","text":"A plain click proposes a one-hour slot starting on the half-hour at or before the click.","accept":["Click at about 16:20 gives 16:00 - 17:00; at about 16:40 gives 16:30 - 17:30","Escape creates nothing; Enter in the title saves with \"Created Notes\""]},{"id":"FR-CRE-03","weight":"Should","text":"The editor and the create service validate each field with its own message and nothing is created on error.","accept":["Empty title: \"Title is required\" (400)","End not after start: \"End must be after start\"","Times not on a quarter hour: 422 \"Times snap to 15 minutes\"","Creating on Holidays: 403 \"Holidays is read-only\"","\"All day\" can only be set on new events; for existing events the checkbox is disabled with the tooltip \"All-day can only be set on new events\""]}]},{"id":"ROOM","title":"Guests, rooms and find-a-time","summary":"Free/busy, capacity and double-booking.","reqs":[{"id":"FR-ROOM-01","weight":"Must","text":"The editor shows free/busy rows for every guest and the chosen room and suggests start times that are free for all, never in the past part of today.","accept":["Panel \"Find a time · 21 Oct, 09:00–18:00\" with a row per guest and the room","Busy block tooltips: \"Thao Vu · 09:30–10:30 · Vendor call\" and \"Booked 15:00–16:00 · Interview — Backend\"","For Linh, Khoa and Thao in Sapa for 60 min the suggestions are 10:30, 11:00, 11:30, 12:00; clicking 12:00 sets 12:00 - 13:00","For Linh and Minh on Monday 19 Oct for 30 min: 11:00, 11:30, 13:00, 13:30 (nothing before 11:00 today)","Duration other than 15, 30, 45, 60, 90, 120 gives 400 \"Duration must be 15, 30, 45, 60, 90 or 120 minutes\""]},{"id":"FR-ROOM-02","weight":"Must","text":"A room cannot hold more invited people, organizer included, than it has seats.","accept":["Sapa (4 seats) with 5 people: \"Room Sapa holds 4 people (5 invited)\" under Room, nothing is created","Removing one guest lets Save succeed and the event shows \"Sapa · 4 seats · Floor 3\""]},{"id":"FR-ROOM-03","weight":"Must","text":"A room cannot be booked for a time that overlaps another booking, but a booking that starts exactly when another ends is allowed.","accept":["Fuji is booked 10:00 - 11:00 by Design review; 10:30 - 11:00 is refused with \"Room Fuji is booked 10:00–11:00 by Design review\" (409 by the service)","11:00 - 11:30 in Fuji is created (\"Created Clash\" / \"Sync\") and sits directly under Design review"]}]},{"id":"REC","title":"Recurring events","summary":"Scope \"This event\" versus \"All events\".","reqs":[{"id":"FR-REC-01","weight":"Must","text":"Changing a recurring event asks \"Change recurring event\" with \"This event\" (default) or \"All events\"; \"This event\" changes only that date and leaves every other date unchanged.","accept":["Moving \"1:1 Linh / Quang\" to 11:30 with \"This event\": toast \"1:1 Linh / Quang moved to Mon 19 Oct, 11:30–12:00\"; the moved item shows \"Weekly on Monday · changed for this date\"","On Monday 26 Oct the event is still 10:30 - 11:00 with \"Repeats: Weekly on Monday\""]},{"id":"FR-REC-02","weight":"Should","text":"Choosing \"All events\" applies the change to every date of the series.","accept":["Renaming to \"1:1 Linh / Quang (v2)\" with All events: toast \"Changes saved\"; Monday 19 and 26 both show the new title","Moving to 11:30 with All events puts Monday 26 at 11:30 - 12:00"]},{"id":"FR-REC-03","weight":"Should","text":"The repeat rule can only be changed for all events.","accept":["Repeat \"Does not repeat\" with \"This event\": \"Repeat can only change for all events\" under Repeat (422 by the service)","With \"All events\" the toast is \"Changes saved\" and the series becomes a single event (no Demo day on Fri 30)"]},{"id":"FR-REC-04","weight":"Should","text":"Deleting a recurring event asks for a scope; deleting a one-off event asks for inline confirmation.","accept":["\"Delete recurring event\" with This event / All events and a \"Delete\" button; This event: toast \"Deleted Demo day on 23 Oct\" and Fri 30 still has it","One-off \"Run club\": inline \"Delete Run club?\" with Cancel / Delete; toast \"Event deleted\"","The service without a scope refuses with 400 \"Choose \\\"This event\\\" or \\\"All events\\\"\"; with scope=this it returns {\"ok\":true,\"deleted\":\"occurrence\",\"date\":\"2026-10-26\"}","Unknown event: 404 \"Event e999 not found\""]}]},{"id":"MOV","title":"Moving and resizing","summary":"Drag move, undo and resize on the 15-minute grid.","reqs":[{"id":"FR-MOV-01","weight":"Could","text":"A one-off event can be dragged to a new time in 15-minute steps and the move can be undone.","accept":["Dragging \"Vendor call\" to Thu 10:00 shows a time tag and the toast \"Vendor call moved to Thu 22 Oct, 10:00–11:00\" with \"Undo\"","Undo: toast \"Move undone\" and the event is back on Wed 09:30 - 10:30"]},{"id":"FR-MOV-02","weight":"Could","text":"Dragging the bottom edge changes the end time in 15-minute steps.","accept":["\"Lunch with Ha\" 12:00 - 13:00 dragged to 13:30 shows \"12:00 – 13:30 · 1 h 30 min\"; toast \"Lunch with Ha moved to Mon 19 Oct, 12:00–13:30\"","Its popover says \"Private — others see only “Busy”\""]}]},{"id":"TZ","title":"Second time zone","summary":"The optional UTC column.","reqs":[{"id":"FR-TZ-01","weight":"Should","text":"The globe button shows a UTC label column that is seven hours behind the primary GMT+7 hours and is remembered after reload.","accept":["Tooltip \"Coordinated Universal Time (-7 h)\"","08:00 shows 01:00 and 14:00 shows 07:00","The column is still shown after a reload and disappears when the button is pressed again"]}]}],"nfr":[],"glossary":[{"term":"Lane","meaning":"One of the side-by-side columns inside a day that overlapping events share."},{"term":"Scope","meaning":"The choice between \"This event\" (one date) and \"All events\" (the whole series) when changing a recurring event."},{"term":"Find a time","meaning":"The free/busy strip in the editor that suggests start times free for all guests and the room."}],"outOfScope":["Time zone changes of the primary zone","Email invitations","Multiple users signed in at once"]}