-
+
+
setTitle(e.target.value)}
- placeholder="What are you working on?"
- className="h-12 border-0 bg-transparent px-0 text-lg font-medium shadow-none focus-visible:ring-0"
- />
-
-
-
-
-
- {visibleClients.map((client) => (
- handleClientChange(client.id)}
- />
- ))}
- {!showAllClients && hiddenClientCount > 0 ? (
-
- ) : null}
-
- {(showAllClients || (clients?.length ?? 0) > FEATURED_CLIENT_COUNT) && (
-
- )}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {clientId && rate === 0 && selectedClient?.defaultHourlyRate ? (
-
- Client default: ${selectedClient.defaultHourlyRate}/hr (used when left at zero).
-
- ) : null}
-
-
-
-
-
- {(
- [
- ["now", "Now"],
- ["pick", "Pick time"],
- ["ago", "Time ago"],
- ] as const
- ).map(([mode, label]) => (
-
- ))}
-
- {startMode === "pick" ? (
-
setPickedStart(e.target.value)}
- className="mt-2"
- />
- ) : null}
- {startMode === "ago" ? (
-
- setMinutesAgo(e.target.value)}
- className="w-24"
- />
- minutes ago
-
- ) : null}
-
-
-
- >
- ) : (
- <>
-
-
-
-
-
- {visibleClients.map((client) => (
- handleClientChange(client.id)}
- />
- ))}
- {!showAllClients && hiddenClientCount > 0 ? (
-
- ) : null}
-
- {(showAllClients || (clients?.length ?? 0) > FEATURED_CLIENT_COUNT) && (
-
- )}
-
-
-
-
-
-
-
-
-
- setStopNote(e.target.value)}
- placeholder={
- running?.description?.trim()
- ? running.description
- : "Update description when you stop"
- }
+ placeholder="e.g. Client kickoff…"
+ className="h-11"
/>
>
- )}
+ ) : null}
{running ? (
- ) : (
+ ) : null}
+
+
+
+
+
+
+
+
+
+
+
+ {activeClientId
+ ? "Linking an invoice adds the completed time as a billable line item."
+ : "Choose a client to see billable invoices."}
+
+
+
+
+
+
+
+
+
+
+ {running ? (
+
+ ) : (
+ <>
+
+
+
+ {clientId && rate === 0 && selectedClient?.defaultHourlyRate ? (
+
+ {`Uses ${selectedClient.defaultHourlyRate}/hr from ${selectedClient.name}.`}
+
+ ) : null}
+
+
+
+
+ {(
+ [
+ ["now", "Now"],
+ ["pick", "Pick time"],
+ ["ago", "Time ago"],
+ ] as const
+ ).map(([mode, label]) => (
+
+ ))}
+
+ {startMode === "pick" ? (
+
setPickedStart(event.target.value)}
+ />
+ ) : null}
+ {startMode === "ago" ? (
+
+ setMinutesAgo(event.target.value)}
+ className="w-24"
+ />
+ minutes ago
+
+ ) : null}
+
+ >
+ )}
+
+
+ {running ? (
+
+
+ setStopNote(event.target.value)}
+ placeholder="Add a final note…"
+ />
+
+ ) : null}
+
+
+
+
+ {!running ? (
+
- )}
-
+
+ ) : null}
{!compact ? (
-
-
- Today's entries
-
+
+
+
+
+ Today
+
+ {completedToday.length === 1
+ ? "1 completed entry"
+ : `${completedToday.length} completed entries`}
+
+
+
+ {todayHours.toFixed(2)}h
+
+
- {todayEntries?.some((e) => e.endedAt) ? (
- setEditEntryId(entry.id)}
- />
+ {completedToday.length > 0 ? (
+ setEditEntryId(entry.id)} />
) : (
-
- No entries today.{" "}
-
- View history
-
-
+
+
No time logged yet
+
+ Start your first timer or open history to add an entry manually.
+
+
)}
+
+
+
) : null}
diff --git a/apps/web/src/lib/draft-invoice.ts b/apps/web/src/lib/draft-invoice.ts
index db83980..e720734 100644
--- a/apps/web/src/lib/draft-invoice.ts
+++ b/apps/web/src/lib/draft-invoice.ts
@@ -1,6 +1,11 @@
/** Default invoice number format (matches web/mobile create forms). */
export function generateInvoiceNumber(now = new Date()): string {
- const date = now.toISOString().slice(0, 10).replace(/-/g, "");
+ const date = [
+ now.getFullYear(),
+ String(now.getMonth() + 1).padStart(2, "0"),
+ String(now.getDate()).padStart(2, "0"),
+ ].join("");
+
return `INV-${date}-${String(now.getTime()).slice(-6)}`;
}