ComponentsCalendar & time

Time tracker

Track time with one running timer shared by every tab, a weekly timesheet that accepts durations the way people type them, and a day-grouped entry list. Billable time becomes invoice line drafts with decimal-string totals. Attendance sheets use the Resource calendar's codes variant.

Plain React + BroadcastChannel (timer adapter) scheduler/time-tracker.tsx, scheduler/core/duration.ts, styles/calendar.csstheme · material · accent · density · font
Top bar

Tuesday, November 101 h 40 min

  • Mobile appCheckout flow reviewBillable1:40

Monday, November 92 h

  • Website relaunchBooking widget QABillable1:15
  • Partner supportWeekly call0:45

Installation

$ pnpm add @vamos-ui/react

The whole kit as one package — get updates by bumping the version. Add the styles to globals.css: @import "@vamos-ui/react/styles.css";

Usage

import { TimerButton, Timesheet, createLocalTimerAdapter } from "@vamos-ui/react/scheduler";

const adapter = createLocalTimerAdapter({ onStop: (entry) => api.timeEntries.create(entry) });

<TimerButton adapter={adapter} projects={projects} />          // top bar

<Timesheet
  weekStart="2026-11-09"
  rows={projects}
  value={minutesByRowAndDay}                                   // { [rowId]: { "2026-11-09": 240 } }
  onCellChange={(rowId, day, minutes) => api.timesheet.set(rowId, day, minutes)}
  dailyTarget={8 * 60}
/>

The timer is a toggle button (aria-pressed) named after its project: “Start timer for Mobile app”. The counter isn't announced every second, only when the timer starts and stops.

The timesheet is a grid with one tab stop. Use the arrow keys to move, type a digit or press Enter to edit, Enter to save (then move down), Escape to cancel and Delete to clear.

Idle detection is planned for a later release.

Examples

Weekly timesheet

An agency week. Type into a cell (1.5, 1:30, 90m, 1h 30m), press Enter to save and move down, and Escape to cancel. Row, day and week totals have their own accessible names. A working day under the 8 h target turns amber.

Timesheet · Nov 9 – Nov 15, 2026

Type a duration, Enter saves, Escape cancels
Project
Mon9
Tue10
Wed11
Thu12
Fri13
Sat14
Sun15
Total
Mobile appNorthwind · billable
Mobile app, Monday, November 9, 4 h
Mobile app, Tuesday, November 10, 5 h
Mobile app, Wednesday, November 11, 2 h 30 min
Mobile app, Thursday, November 12, 0
Mobile app, Friday, November 13, 0
Mobile app, Saturday, November 14, 0
Mobile app, Sunday, November 15, 0
Mobile app, total 11 h 30 min
Website relaunchLumen Spa · billable
Website relaunch, Monday, November 9, 3 h
Website relaunch, Tuesday, November 10, 1 h 30 min
Website relaunch, Wednesday, November 11, 0
Website relaunch, Thursday, November 12, 6 h
Website relaunch, Friday, November 13, 0
Website relaunch, Saturday, November 14, 0
Website relaunch, Sunday, November 15, 0
Website relaunch, total 10 h 30 min
InternalHiring, admin
Internal, Monday, November 9, 1 h
Internal, Tuesday, November 10, 0
Internal, Wednesday, November 11, 45 min
Internal, Thursday, November 12, 0
Internal, Friday, November 13, 0
Internal, Saturday, November 14, 0
Internal, Sunday, November 15, 0
Internal, total 1 h 45 min
Total
Monday, November 9, total 8 h
Tuesday, November 10, total 6 h 30 min, under the 8 h target
Wednesday, November 11, total 3 h 15 min, under the 8 h target
Thursday, November 12, total 6 h, under the 8 h target
Friday, November 13, total 0 min
Saturday, November 14, total 0 min
Sunday, November 15, total 0 min
Week total, 23 h 45 min

Billable time → line items

toLineItems groups billable entries by project (or task) and rate. Quantities are hours as decimal strings with unit HUR, ready for an invoice.

DescriptionHoursRateAmount
Mobile app5.83€95.00€553.85
Website relaunch4€85.00€340.00
Total€893.85

Monthly attendance (tr)

An HR attendance sheet: Resource calendar variant="codes" with the Turkish code set (Ç, İ, R, HT, RT, Ü). The codes are data, not kit logic. Type a code to set a cell.

1–31 Ekim 2026

Name
Per1
Cum2
Cmt3
Paz4
Pzt5
Sal6
Çar7
Per8
Cum9
Cmt10
Paz11
Pzt12
Sal13
Çar14
Per15
Cum16
Cmt17
Paz18
Pzt19
Sal20
Çar21
Per22
Cum23
Cmt24
Paz25
Pzt26
Sal27
Çar28
Per29
Cum30
Cmt31
Zeynep Acar
21
Zeynep Acar, 1 Ekim Perşembe. Çalıştı
Zeynep Acar, 2 Ekim Cuma. Çalıştı
Zeynep Acar, 3 Ekim Cumartesi. Hafta tatili
Zeynep Acar, 4 Ekim Pazar. Hafta tatili
Zeynep Acar, 5 Ekim Pazartesi. Çalıştı
Zeynep Acar, 6 Ekim Salı. Çalıştı
Zeynep Acar, 7 Ekim Çarşamba. Çalıştı
Zeynep Acar, 8 Ekim Perşembe. Çalıştı
Zeynep Acar, 9 Ekim Cuma. Çalıştı
Zeynep Acar, 10 Ekim Cumartesi. Hafta tatili
Zeynep Acar, 11 Ekim Pazar. Hafta tatili
Zeynep Acar, 12 Ekim Pazartesi. Çalıştı
Zeynep Acar, 13 Ekim Salı. Çalıştı
Zeynep Acar, 14 Ekim Çarşamba. Çalıştı
Zeynep Acar, 15 Ekim Perşembe. Çalıştı
Zeynep Acar, 16 Ekim Cuma. Çalıştı
Zeynep Acar, 17 Ekim Cumartesi. Hafta tatili
Zeynep Acar, 18 Ekim Pazar. Hafta tatili
Zeynep Acar, 19 Ekim Pazartesi. Çalıştı
Zeynep Acar, 20 Ekim Salı. Çalıştı
Zeynep Acar, 21 Ekim Çarşamba. Çalıştı
Zeynep Acar, 22 Ekim Perşembe. Çalıştı
Zeynep Acar, 23 Ekim Cuma. Çalıştı
Zeynep Acar, 24 Ekim Cumartesi. Hafta tatili
Zeynep Acar, 25 Ekim Pazar. Hafta tatili
Zeynep Acar, 26 Ekim Pazartesi. Çalıştı
Zeynep Acar, 27 Ekim Salı. Çalıştı
Zeynep Acar, 28 Ekim Çarşamba. Republic Day Eve. Çalıştı
Zeynep Acar, 29 Ekim Perşembe. Republic Day. Resmî tatil
Zeynep Acar, 30 Ekim Cuma. Çalıştı
Zeynep Acar, 31 Ekim Cumartesi. Hafta tatili
Hakan Er
17
Hakan Er, 1 Ekim Perşembe. Çalıştı
Hakan Er, 2 Ekim Cuma. Çalıştı
Hakan Er, 3 Ekim Cumartesi. Hafta tatili
Hakan Er, 4 Ekim Pazar. Hafta tatili
Hakan Er, 5 Ekim Pazartesi. Çalıştı
Hakan Er, 6 Ekim Salı. Çalıştı
Hakan Er, 7 Ekim Çarşamba. Çalıştı
Hakan Er, 8 Ekim Perşembe. Çalıştı
Hakan Er, 9 Ekim Cuma. Çalıştı
Hakan Er, 10 Ekim Cumartesi. Hafta tatili
Hakan Er, 11 Ekim Pazar. Hafta tatili
Hakan Er, 12 Ekim Pazartesi. Çalıştı
Hakan Er, 13 Ekim Salı. Yıllık izin
Hakan Er, 14 Ekim Çarşamba. Yıllık izin
Hakan Er, 15 Ekim Perşembe. Yıllık izin
Hakan Er, 16 Ekim Cuma. Yıllık izin
Hakan Er, 17 Ekim Cumartesi. Hafta tatili
Hakan Er, 18 Ekim Pazar. Hafta tatili
Hakan Er, 19 Ekim Pazartesi. Çalıştı
Hakan Er, 20 Ekim Salı. Çalıştı
Hakan Er, 21 Ekim Çarşamba. Çalıştı
Hakan Er, 22 Ekim Perşembe. Çalıştı
Hakan Er, 23 Ekim Cuma. Çalıştı
Hakan Er, 24 Ekim Cumartesi. Hafta tatili
Hakan Er, 25 Ekim Pazar. Hafta tatili
Hakan Er, 26 Ekim Pazartesi. Çalıştı
Hakan Er, 27 Ekim Salı. Çalıştı
Hakan Er, 28 Ekim Çarşamba. Republic Day Eve. Çalıştı
Hakan Er, 29 Ekim Perşembe. Republic Day. Resmî tatil
Hakan Er, 30 Ekim Cuma. Çalıştı
Hakan Er, 31 Ekim Cumartesi. Hafta tatili
Gül Şimşek
21
Gül Şimşek, 1 Ekim Perşembe. Çalıştı
Gül Şimşek, 2 Ekim Cuma. Çalıştı
Gül Şimşek, 3 Ekim Cumartesi. Hafta tatili
Gül Şimşek, 4 Ekim Pazar. Hafta tatili
Gül Şimşek, 5 Ekim Pazartesi. Çalıştı
Gül Şimşek, 6 Ekim Salı. Çalıştı
Gül Şimşek, 7 Ekim Çarşamba. Çalıştı
Gül Şimşek, 8 Ekim Perşembe. Çalıştı
Gül Şimşek, 9 Ekim Cuma. Çalıştı
Gül Şimşek, 10 Ekim Cumartesi. Hafta tatili
Gül Şimşek, 11 Ekim Pazar. Hafta tatili
Gül Şimşek, 12 Ekim Pazartesi. Çalıştı
Gül Şimşek, 13 Ekim Salı. Çalıştı
Gül Şimşek, 14 Ekim Çarşamba. Çalıştı
Gül Şimşek, 15 Ekim Perşembe. Çalıştı
Gül Şimşek, 16 Ekim Cuma. Çalıştı
Gül Şimşek, 17 Ekim Cumartesi. Hafta tatili
Gül Şimşek, 18 Ekim Pazar. Hafta tatili
Gül Şimşek, 19 Ekim Pazartesi. Çalıştı
Gül Şimşek, 20 Ekim Salı. Çalıştı
Gül Şimşek, 21 Ekim Çarşamba. Çalıştı
Gül Şimşek, 22 Ekim Perşembe. Çalıştı
Gül Şimşek, 23 Ekim Cuma. Çalıştı
Gül Şimşek, 24 Ekim Cumartesi. Hafta tatili
Gül Şimşek, 25 Ekim Pazar. Hafta tatili
Gül Şimşek, 26 Ekim Pazartesi. Çalıştı
Gül Şimşek, 27 Ekim Salı. Çalıştı
Gül Şimşek, 28 Ekim Çarşamba. Republic Day Eve. Çalıştı
Gül Şimşek, 29 Ekim Perşembe. Republic Day. Resmî tatil
Gül Şimşek, 30 Ekim Cuma. Çalıştı
Gül Şimşek, 31 Ekim Cumartesi. Hafta tatili
Oğuz Tan
20
Oğuz Tan, 1 Ekim Perşembe. Çalıştı
Oğuz Tan, 2 Ekim Cuma. Çalıştı
Oğuz Tan, 3 Ekim Cumartesi. Hafta tatili
Oğuz Tan, 4 Ekim Pazar. Hafta tatili
Oğuz Tan, 5 Ekim Pazartesi. Çalıştı
Oğuz Tan, 6 Ekim Salı. Çalıştı
Oğuz Tan, 7 Ekim Çarşamba. Çalıştı
Oğuz Tan, 8 Ekim Perşembe. Rapor
Oğuz Tan, 9 Ekim Cuma. Çalıştı
Oğuz Tan, 10 Ekim Cumartesi. Hafta tatili
Oğuz Tan, 11 Ekim Pazar. Hafta tatili
Oğuz Tan, 12 Ekim Pazartesi. Çalıştı
Oğuz Tan, 13 Ekim Salı. Çalıştı
Oğuz Tan, 14 Ekim Çarşamba. Çalıştı
Oğuz Tan, 15 Ekim Perşembe. Çalıştı
Oğuz Tan, 16 Ekim Cuma. Çalıştı
Oğuz Tan, 17 Ekim Cumartesi. Hafta tatili
Oğuz Tan, 18 Ekim Pazar. Hafta tatili
Oğuz Tan, 19 Ekim Pazartesi. Çalıştı
Oğuz Tan, 20 Ekim Salı. Çalıştı
Oğuz Tan, 21 Ekim Çarşamba. Çalıştı
Oğuz Tan, 22 Ekim Perşembe. Çalıştı
Oğuz Tan, 23 Ekim Cuma. Çalıştı
Oğuz Tan, 24 Ekim Cumartesi. Hafta tatili
Oğuz Tan, 25 Ekim Pazar. Hafta tatili
Oğuz Tan, 26 Ekim Pazartesi. Çalıştı
Oğuz Tan, 27 Ekim Salı. Çalıştı
Oğuz Tan, 28 Ekim Çarşamba. Republic Day Eve. Çalıştı
Oğuz Tan, 29 Ekim Perşembe. Republic Day. Resmî tatil
Oğuz Tan, 30 Ekim Cuma. Çalıştı
Oğuz Tan, 31 Ekim Cumartesi. Hafta tatili
Pınar Uysal
21
Pınar Uysal, 1 Ekim Perşembe. Çalıştı
Pınar Uysal, 2 Ekim Cuma. Çalıştı
Pınar Uysal, 3 Ekim Cumartesi. Hafta tatili
Pınar Uysal, 4 Ekim Pazar. Hafta tatili
Pınar Uysal, 5 Ekim Pazartesi. Çalıştı
Pınar Uysal, 6 Ekim Salı. Çalıştı
Pınar Uysal, 7 Ekim Çarşamba. Çalıştı
Pınar Uysal, 8 Ekim Perşembe. Çalıştı
Pınar Uysal, 9 Ekim Cuma. Çalıştı
Pınar Uysal, 10 Ekim Cumartesi. Hafta tatili
Pınar Uysal, 11 Ekim Pazar. Hafta tatili
Pınar Uysal, 12 Ekim Pazartesi. Çalıştı
Pınar Uysal, 13 Ekim Salı. Çalıştı
Pınar Uysal, 14 Ekim Çarşamba. Çalıştı
Pınar Uysal, 15 Ekim Perşembe. Çalıştı
Pınar Uysal, 16 Ekim Cuma. Çalıştı
Pınar Uysal, 17 Ekim Cumartesi. Hafta tatili
Pınar Uysal, 18 Ekim Pazar. Hafta tatili
Pınar Uysal, 19 Ekim Pazartesi. Çalıştı
Pınar Uysal, 20 Ekim Salı. Çalıştı
Pınar Uysal, 21 Ekim Çarşamba. Çalıştı
Pınar Uysal, 22 Ekim Perşembe. Çalıştı
Pınar Uysal, 23 Ekim Cuma. Çalıştı
Pınar Uysal, 24 Ekim Cumartesi. Hafta tatili
Pınar Uysal, 25 Ekim Pazar. Hafta tatili
Pınar Uysal, 26 Ekim Pazartesi. Çalıştı
Pınar Uysal, 27 Ekim Salı. Çalıştı
Pınar Uysal, 28 Ekim Çarşamba. Republic Day Eve. Çalıştı
Pınar Uysal, 29 Ekim Perşembe. Republic Day. Resmî tatil
Pınar Uysal, 30 Ekim Cuma. Çalıştı
Pınar Uysal, 31 Ekim Cumartesi. Hafta tatili
Scheduled
5
5
5
5
5
4
5
5
4
4
4
4
5
5
5
5
5
5
5
5
5

Accepted input formats

parseDuration reads hours, h:mm and unit words (h/min, sa/dk), with a decimal comma where the locale uses one. formatDuration has short, clock and decimal styles.

90 min · 1 h 30 min · 1:30 · 1.5 h

1.51 h 30 min
1,51 h 30 min
1:301 h 30 min
90m1 h 30 min
1h 30m1 h 30 min
1 sa 30 dk1 h 30 min
2 hours2 h
4545 h

API

TimerButton

PropTypeDefault
adapter
{ getActive, start, stop, subscribe? }. createLocalTimerAdapter (localStorage + BroadcastChannel) or createMemoryTimerAdapter. Back it with your API for one timer per user.
TimerAdapter—
projects
Projects to choose from (full variant).
{ id, label, meta? }[]—
projectId / defaultProjectId / onProjectChange
The selected project.
string—
variant
A top-bar pill, or project, note and a large counter.
"compact" | "full""compact"
staleAfterMin
After this many minutes a running timer asks “Still on it?”.
number600
onStart / onStop
Called after the adapter confirms.
(entry: TimeEntry) => void—
onAdjust
Adds an “Adjust end time” button to a stale timer.
(entry) => void—
titleTicker
Prefixes document.title with the elapsed time while the timer runs.
booleanfalse
labels
Overrides for the timeTracker namespace strings.
Partial<TimeTrackerLabels>—

Timesheet

PropTypeDefault
weekStart
First day shown.
string (YYYY-MM-DD)—
days
Number of day columns.
number7
rows
Projects, tasks or people.
{ id, label, meta?, readOnly? }[]—
value / defaultValue
Minutes per cell.
Record<rowId, Record<YYYY-MM-DD, minutes>>—
onCellChange
Saves one cell. A rejected promise resets the cell and shows a toast.
(rowId, day, minutes) => void | Promise<void>—
dailyTarget
Day totals under target on working days (up to today) turn amber and say so.
number (minutes)—
locked
An approved week: read-only, with a lock note.
booleanfalse
workCalendar
Shades weekends and holidays. Half days halve the target.
WorkCalendarfrom locale
display
Cell format: 7:30, 7.5 or 7 h 30 min.
"clock" | "decimal" | "short""clock"

TimeEntryList

PropTypeDefault
entries
{ id, start, end?, durationMin?, projectLabel?, note?, billable?, rate? }. Running entries count up live.
TimeEntry[]—
onEntryOpen
Makes each row a button.
(entry) => void—
showAmounts
Shows rate × time for billable entries.
booleanfalse

core (React-free)

PropTypeDefault
parseDuration(input, locale)
Minutes from “1.5”, “1,5”, “1:30”, “90m”, “1h 30m” or “1 sa 30 dk”. A bare number is hours.
number | null—
formatDuration(min, locale, style?)
“3 h 20 min” / “3 sa 20 dk”, “3:20”, or decimal “3.33”.
string—
toLineItems(entries, groupBy) / lineItemsTotal
Billable entries to { description, quantity, unitCode: "HUR", unitPrice, total, currency, entryIds }.
LineItemDraft[] / string—
timesheetTotals / entryMinutes
Row, day and week sums, and the length of one entry.
functions—