Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions src/components/provider-card.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest"
import { openUrl } from "@tauri-apps/plugin-opener"
import { ProviderCard } from "@/components/provider-card"
import { groupLinesByType } from "@/lib/group-lines-by-type"
import { formatResetTooltipText } from "@/lib/reset-tooltip"
import { REFRESH_COOLDOWN_MS } from "@/lib/settings"
import { formatFixedPrecisionNumber } from "@/lib/utils"

Expand Down Expand Up @@ -260,9 +261,32 @@ describe("ProviderCard", () => {
/>
)
expect(screen.getByText("Resets in 1h 5m")).toBeInTheDocument()
expect(screen.getByText(formatResetTooltipText("2026-02-02T01:05:00.000Z")!)).toBeInTheDocument()
vi.useRealTimers()
})

it("does not render reset tooltip for invalid reset timestamps", () => {
render(
<ProviderCard
name="Resets"
displayMode="used"
lines={[
{
type: "progress",
label: "Invalid",
used: 10,
limit: 100,
format: { kind: "percent" },
resetsAt: "not-a-date",
},
]}
/>
)

expect(screen.getByText("100% cap")).toBeInTheDocument()
expect(screen.queryByText(/^Next reset:/)).not.toBeInTheDocument()
})

it("shows 'Resets soon' when reset is under 5 minutes away", () => {
vi.useFakeTimers()
const now = new Date("2026-02-02T00:00:00.000Z")
Expand Down Expand Up @@ -386,6 +410,7 @@ describe("ProviderCard", () => {
)
const resetButton = screen.getByRole("button", { name: /^Resets today at / })
expect(resetButton).toBeInTheDocument()
expect(screen.getByText(formatResetTooltipText(resetsAt)!)).toBeInTheDocument()
fireEvent.click(resetButton)
expect(onToggle).toHaveBeenCalledTimes(1)
vi.useRealTimers()
Expand Down
26 changes: 25 additions & 1 deletion src/components/provider-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { groupLinesByType } from "@/lib/group-lines-by-type"
import { clamp01, formatCountNumber, formatFixedPrecisionNumber } from "@/lib/utils"
import { calculateDeficit, calculatePaceStatus, type PaceStatus } from "@/lib/pace-status"
import { buildPaceDetailText, formatCompactDuration, formatDeficitText, formatRunsOutText, getPaceStatusText } from "@/lib/pace-tooltip"
import { formatResetTooltipText } from "@/lib/reset-tooltip"

interface ProviderCardProps {
name: string
Expand Down Expand Up @@ -423,6 +424,7 @@ function MetricLineRenderer({
? formatResetAt(now, line.resetsAt)
: formatResetIn(now, line.resetsAt)
: null
const resetTooltipText = line.resetsAt ? formatResetTooltipText(line.resetsAt) : null

const secondaryText =
resetLabel ??
Expand Down Expand Up @@ -494,7 +496,29 @@ function MetricLineRenderer({
{primaryText}
</span>
{secondaryText && (
resetLabel && onResetTimerDisplayModeToggle ? (
resetTooltipText ? (
<Tooltip>
<TooltipTrigger
render={(props) =>
resetLabel && onResetTimerDisplayModeToggle ? (
<button
{...props}
type="button"
onClick={onResetTimerDisplayModeToggle}
className="text-xs text-muted-foreground tabular-nums hover:text-foreground transition-colors"
>
{secondaryText}
</button>
) : (
<span {...props} className="text-xs text-muted-foreground tabular-nums">
{secondaryText}
</span>
)
}
/>
<TooltipContent side="top">{resetTooltipText}</TooltipContent>
</Tooltip>
) : resetLabel && onResetTimerDisplayModeToggle ? (
<button
type="button"
onClick={onResetTimerDisplayModeToggle}
Expand Down
18 changes: 18 additions & 0 deletions src/lib/reset-tooltip.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { describe, expect, it } from "vitest"
import { formatResetTooltipText } from "@/lib/reset-tooltip"

describe("reset-tooltip", () => {
it("returns null for invalid reset timestamp", () => {
expect(formatResetTooltipText("not-a-date")).toBeNull()
})

it("formats reset tooltip content with date and time", () => {
const resetsAt = "2026-02-03T12:34:00.000Z"
const expected = new Intl.DateTimeFormat(undefined, {
dateStyle: "medium",
timeStyle: "short",
}).format(Date.parse(resetsAt))

expect(formatResetTooltipText(resetsAt)).toBe(`Next reset: ${expected}`)
})
})
10 changes: 10 additions & 0 deletions src/lib/reset-tooltip.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const RESET_TOOLTIP_FORMATTER = new Intl.DateTimeFormat(undefined, {
dateStyle: "medium",
timeStyle: "short",
})

export function formatResetTooltipText(resetsAtIso: string): string | null {
const resetsAtMs = Date.parse(resetsAtIso)
if (!Number.isFinite(resetsAtMs)) return null
return `Next reset: ${RESET_TOOLTIP_FORMATTER.format(resetsAtMs)}`
}