Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@steven-tey
Copy link
Collaborator

@steven-tey steven-tey commented Aug 8, 2025

Screenshot 2025-10-17 at 4 43 06 PM Screenshot 2025-10-17 at 4 43 25 PM Screenshot 2025-10-17 at 4 53 20 PM

Summary by CodeRabbit

  • New Features
    • TimestampTooltip rolled out across tables, lists, and cards — hover to see local/UTC/UNIX times and a live “ago” string; optional interactive rows allow click-to-copy with feedback.
  • Chores
    • TimestampTooltip added to the shared UI package and exported for reuse.

@vercel
Copy link
Contributor

vercel bot commented Aug 8, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Updated (UTC)
dub Ready Ready Preview Oct 17, 2025 10:00pm

💡 Enable Vercel Agent with $100 free credit for automated AI reviews

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 8, 2025

Walkthrough

Adds a new TimestampTooltip component to the UI package and replaces many inline/Tooltip-wrapped timestamp renderings across the web app with this component to provide structured timestamp rows, live relative “ago” text, and optional click-to-copy behavior.

Changes

Cohort / File(s) Summary
New UI export & component
packages/ui/src/timestamp-tooltip.tsx, packages/ui/src/index.tsx
Adds and exports TimestampTooltip and TimestampTooltipProps; implements tooltip content with local/UTC/UNIX rows, live "ago" text, per-row click-to-copy, and toast feedback.
Partner & Program dashboard tables
apps/web/app/(ee)/partners.dub.co/(dashboard)/programs/[programSlug]/(enrolled)/earnings/earnings-table.tsx, apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/bounties/[bountyId]/bounty-submissions-table.tsx, apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/campaigns/[campaignId]/campaign-events-columns.tsx, apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/campaigns/campaigns-table.tsx, apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/commission-table.tsx, apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/partners-table.tsx
Replace previous date accessors or Tooltip usage with TimestampTooltip wrappers for createdAt/updatedAt cells; add TimestampTooltip imports and remove some formatDateTime usages.
Links & analytics UI
apps/web/ui/analytics/events/events-table.tsx, apps/web/ui/links/link-builder/link-creator-info.tsx, apps/web/ui/links/link-title-column.tsx, apps/web/app/app.dub.co/(dashboard)/[slug]/links/utm/template-card.tsx
Swap tooltip/formatting logic for TimestampTooltip, preserving visible formatted/time-ago text while delegating full timestamp display and interactivity to the component.
Customer components & tables
apps/web/ui/customers/customer-activity-list.tsx, apps/web/ui/customers/customer-details-column.tsx, apps/web/ui/customers/customer-sales-table.tsx, apps/web/ui/customers/customer-table/customer-table.tsx
Wrap customer-related created/seen/updated date displays with TimestampTooltip, configuring rows/side/interactive props and updating imports.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Cell as Table Cell / UI
  participant TT as TimestampTooltip (component)
  participant Tooltip as Internal Tooltip
  participant Clipboard as Clipboard API
  participant Toast as Toasts

  Cell->>TT: render(timestamp, rows?, interactive?, children=visibleDate)
  TT->>Tooltip: provide content(ago + rows table)
  Tooltip-->>Cell: show on hover/focus (delay/side)

  alt user clicks a row value (interactive)
    TT->>Clipboard: writeText(rowValue)
    Clipboard-->>TT: success / failure
    TT-->>Toast: show success/error
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰
I hop through cells where timestamps glow,
I tuck their moments in a tooltip’s bow,
Local, UTC, UNIX — neat and spry,
Click to copy, watch the toast fly,
A rabbit’s whisker nudges time nearby.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "Add timestamp tooltip" accurately captures the main objective of the pull request, which is to introduce a new TimestampTooltip component and systematically apply it across the codebase to enhance date/timestamp displays with tooltip functionality. The title is concise (3 words), clear, and specific enough that a teammate scanning the history would immediately understand the primary change. It avoids vague language and directly reflects the core work performed across the changeset.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch timestamp-tooltip

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@TWilson023 TWilson023 marked this pull request as ready for review October 17, 2025 20:01
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (6)
packages/ui/src/timestamp-tooltip.tsx (4)

102-106: “UNIX Timestamp” unit ambiguity (ms vs s).

date.getTime() returns milliseconds. Most users expect UNIX timestamps in seconds. Either divide by 1000 or rename the label.

Choose one:

  • Prefer seconds:
- label: "UNIX Timestamp",
- value: date.getTime().toString(),
+ label: "UNIX (s)",
+ value: Math.floor(date.getTime() / 1000).toString(),
  • Or keep ms but clarify:
- label: "UNIX Timestamp",
+ label: "UNIX (ms)",

15-17: className prop is unused.

className is accepted but never applied.

Pass it to a trigger wrapper without altering layout:

-    >
-      {children}
+    >
+      <span className={className}>{children}</span>
     </Tooltip>

If your Tooltip supports a trigger class prop, prefer that to avoid extra span.

Also applies to: 33-35, 115-171


120-140: Accessibility: clickable rows are not keyboard‑operable.

<tr> has onClick but no role/tabindex/keyboard handling. Screen‑reader and keyboard users can’t activate copy.

Make the row operable when interactive:

-              <tr
+              <tr
+                role={interactive ? "button" : undefined}
+                tabIndex={interactive ? 0 : -1}
                 key={idx}
                 className={cn(
                   interactive &&
                     "before:bg-bg-emphasis relative select-none before:absolute before:-inset-x-1 before:inset-y-0 before:rounded before:opacity-0 before:content-[''] hover:cursor-pointer hover:before:opacity-60 active:before:opacity-100",
                 )}
                 onClick={interactive ? /* ... */ : undefined}
+                onKeyDown={
+                  interactive
+                    ? (e) => {
+                        if (e.key === "Enter" || e.key === " ") {
+                          e.preventDefault();
+                          (e.currentTarget as HTMLElement).click();
+                        }
+                      }
+                    : undefined
+                }
               >

Optionally add focus styles (e.g., focus:outline focus:outline-2).

Also applies to: 141-160


52-74: Future timestamps show “… ago”.

If date > now, UI still appends “ago” (and diff logic includes seconds). Consider “in …” or “Just now” thresholds.

Example:

-  const relative =
-    formatDuration(relativeDuration, { ... }) + " ago";
+  const isFuture = date > new Date();
+  const relative =
+    formatDuration(relativeDuration, { ... }) + (isFuture ? "" : " ago");
+  if (isFuture) relative = `in ${relative}`;
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/commission-table.tsx (1)

93-101: LGTM! Good interactive timestamp tooltip usage.

The TimestampTooltip integration works well for the Date column. The interactive mode with all timestamp formats is appropriate for a financial data table where precise timestamps matter.

Minor suggestion: Consider adding select-none to the <p> tag for consistency with events-table.tsx (Line 194), which prevents text selection conflicts when users click to copy timestamps in interactive mode.

-              <p>{formatDateTimeSmart(row.original.createdAt)}</p>
+              <p className="select-none">{formatDateTimeSmart(row.original.createdAt)}</p>
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/bounties/[bountyId]/bounty-submissions-table.tsx (1)

189-203: Consider standardizing TimestampTooltip props for consistency.

The implementation is correct, but there's an inconsistency with the earnings-table.tsx usage:

  • This file uses default props: rows defaults to ["local", "utc"] and interactive defaults to false
  • earnings-table.tsx explicitly sets rows={["local"]} and interactive={true}

This creates an inconsistent user experience where some timestamps show both local and UTC times with click-to-copy, while others show both timezones without the ability to copy.

Consider standardizing the props across all TimestampTooltip instances. For example:

 <TimestampTooltip
   timestamp={row.original.createdAt}
   side="left"
+  rows={["local"]}
+  interactive
   delayDuration={150}
 >

Or alternatively, establish a project-wide convention for which props should be used in table timestamp columns and apply it consistently.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c28c9a8 and f267680.

📒 Files selected for processing (15)
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/programs/[programSlug]/(enrolled)/earnings/earnings-table.tsx (2 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/bounties/[bountyId]/bounty-submissions-table.tsx (2 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/campaigns/[campaignId]/campaign-events-columns.tsx (2 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/campaigns/campaigns-table.tsx (2 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/commission-table.tsx (2 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/partners-table.tsx (2 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/links/utm/template-card.tsx (2 hunks)
  • apps/web/ui/analytics/events/events-table.tsx (2 hunks)
  • apps/web/ui/customers/customer-activity-list.tsx (2 hunks)
  • apps/web/ui/customers/customer-details-column.tsx (2 hunks)
  • apps/web/ui/customers/customer-sales-table.tsx (2 hunks)
  • apps/web/ui/links/link-builder/link-creator-info.tsx (2 hunks)
  • apps/web/ui/links/link-title-column.tsx (2 hunks)
  • packages/ui/src/index.tsx (1 hunks)
  • packages/ui/src/timestamp-tooltip.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (13)
apps/web/ui/analytics/events/events-table.tsx (1)
packages/ui/src/timestamp-tooltip.tsx (1)
  • TimestampTooltip (28-172)
apps/web/app/app.dub.co/(dashboard)/[slug]/links/utm/template-card.tsx (1)
packages/ui/src/timestamp-tooltip.tsx (1)
  • TimestampTooltip (28-172)
apps/web/ui/links/link-title-column.tsx (2)
packages/ui/src/timestamp-tooltip.tsx (1)
  • TimestampTooltip (28-172)
packages/utils/src/functions/time-ago.ts (1)
  • timeAgo (3-32)
apps/web/app/(ee)/partners.dub.co/(dashboard)/programs/[programSlug]/(enrolled)/earnings/earnings-table.tsx (1)
packages/ui/src/timestamp-tooltip.tsx (1)
  • TimestampTooltip (28-172)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/campaigns/[campaignId]/campaign-events-columns.tsx (2)
packages/ui/src/timestamp-tooltip.tsx (1)
  • TimestampTooltip (28-172)
packages/utils/src/functions/time-ago.ts (1)
  • timeAgo (3-32)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/partners-table.tsx (1)
packages/ui/src/timestamp-tooltip.tsx (1)
  • TimestampTooltip (28-172)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/bounties/[bountyId]/bounty-submissions-table.tsx (1)
packages/ui/src/timestamp-tooltip.tsx (1)
  • TimestampTooltip (28-172)
apps/web/ui/customers/customer-activity-list.tsx (1)
packages/ui/src/timestamp-tooltip.tsx (1)
  • TimestampTooltip (28-172)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/commission-table.tsx (1)
packages/ui/src/timestamp-tooltip.tsx (1)
  • TimestampTooltip (28-172)
apps/web/ui/customers/customer-sales-table.tsx (1)
packages/ui/src/timestamp-tooltip.tsx (1)
  • TimestampTooltip (28-172)
apps/web/ui/links/link-builder/link-creator-info.tsx (2)
packages/ui/src/timestamp-tooltip.tsx (1)
  • TimestampTooltip (28-172)
packages/utils/src/functions/time-ago.ts (1)
  • timeAgo (3-32)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/campaigns/campaigns-table.tsx (1)
packages/ui/src/timestamp-tooltip.tsx (1)
  • TimestampTooltip (28-172)
apps/web/ui/customers/customer-details-column.tsx (1)
packages/ui/src/timestamp-tooltip.tsx (1)
  • TimestampTooltip (28-172)
🪛 Biome (2.1.2)
packages/ui/src/timestamp-tooltip.tsx

[error] 80-80: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)

🔇 Additional comments (13)
packages/ui/src/index.tsx (1)

43-43: Public export looks good.

Re‑exporting ./timestamp-tooltip from the UI index cleanly exposes the component.

apps/web/ui/customers/customer-activity-list.tsx (1)

2-2: Nice integration of TimestampTooltip.

  • Correct import path and placement.
  • Good use of side="right", interactive, and multi‑row formats.
  • Keeping formatDateTimeSmart as the trigger text preserves existing table styling.

Also applies to: 139-148

apps/web/ui/customers/customer-sales-table.tsx (1)

2-2: Good table cell migration to TimestampTooltip.

  • Accessor returns a Date, passed as timestamp prop—solid.
  • Keeping formatDateTimeSmart for the trigger text maintains consistency.
  • side="right" and interactive are appropriate for dense tables.

Also applies to: 40-49

apps/web/ui/links/link-builder/link-creator-info.tsx (1)

3-4: Adoption looks good; verify createdAt type with timeAgo.

  • Tooltip config (rows, delay, interactive) is consistent with the PR.
  • timeAgo(link.createdAt) expects a Date | null per utils. Ensure link.createdAt is a Date (or convert).

Run a quick check to confirm createdAt’s type:

Also applies to: 31-38

apps/web/app/app.dub.co/(dashboard)/[slug]/links/utm/template-card.tsx (1)

112-119: LGTM! Clean timestamp tooltip integration.

The TimestampTooltip wrapper is correctly implemented with appropriate props for the template card context. The side="left" positioning works well for this right-aligned element, and showing only local time keeps the tooltip concise.

apps/web/ui/customers/customer-details-column.tsx (1)

126-138: LGTM! Consistent timestamp tooltip usage.

The TimestampTooltip integration maintains the existing date display while adding helpful tooltip context. The implementation aligns well with the broader PR pattern of standardizing timestamp displays.

apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/campaigns/[campaignId]/campaign-events-columns.tsx (1)

48-60: LGTM! Well-configured interactive timestamp tooltip.

The migration to TimestampTooltip is well-executed with appropriate props for a technical events table. The interactive mode with all three timestamp representations (local, UTC, UNIX) provides excellent utility for debugging and data analysis.

apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/partners-table.tsx (1)

195-206: LGTM! Proper timestamp tooltip integration in table cell.

The TimestampTooltip wrapping is correctly implemented for the Enrolled column. The side="right" positioning and delayDuration={150} provide good UX for hovering over table cells.

apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/campaigns/campaigns-table.tsx (1)

119-127: LGTM! Clean timestamp tooltip integration.

The TimestampTooltip is correctly implemented for the Created column. The configuration is appropriate for the campaigns table context, with local time being sufficient for this use case.

apps/web/ui/links/link-title-column.tsx (1)

384-386: LGTM! Minimal and effective timestamp tooltip usage.

The TimestampTooltip integration is clean and unobtrusive for the link title column. Using default props where appropriate keeps the code simple while still providing helpful timestamp context on hover.

apps/web/ui/analytics/events/events-table.tsx (1)

188-197: LGTM! Excellent interactive timestamp tooltip implementation.

The TimestampTooltip integration is well-executed with the select-none class on the child div (Line 194) preventing text selection conflicts when users click to copy timestamps in interactive mode. This is a good pattern for interactive tooltips.

apps/web/app/(ee)/partners.dub.co/(dashboard)/programs/[programSlug]/(enrolled)/earnings/earnings-table.tsx (1)

17-17: LGTM!

The TimestampTooltip import is correctly added to the existing @dub/ui imports.

apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/bounties/[bountyId]/bounty-submissions-table.tsx (1)

19-19: LGTM!

The TimestampTooltip import is correctly added to the existing @dub/ui imports.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
packages/ui/src/timestamp-tooltip.tsx (1)

96-98: DST bug: timezone abbreviation derived from current time, not the row's timestamp.

Using new Date() here yields the current timezone abbreviation (e.g., "PDT" for Pacific Daylight Time) even if the date being displayed falls in a different DST period (e.g., "PST" for Pacific Standard Time). This causes the short label to mismatch the actual timestamp's timezone state.

Apply this fix to derive the abbreviation from the row's date:

- shortLabel: new Date()
-   .toLocaleTimeString("en-US", { timeZoneName: "short" })
-   .split(" ")[2],
+ shortLabel: Intl.DateTimeFormat("en-US", {
+   hour: "2-digit",
+   timeZoneName: "short",
+ })
+   .formatToParts(date)
+   .find((p) => p.type === "timeZoneName")?.value,
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f267680 and a5bcdd4.

📒 Files selected for processing (2)
  • apps/web/ui/links/link-title-column.tsx (2 hunks)
  • packages/ui/src/timestamp-tooltip.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
apps/web/ui/links/link-title-column.tsx (2)
packages/ui/src/timestamp-tooltip.tsx (1)
  • TimestampTooltip (28-36)
packages/utils/src/functions/time-ago.ts (1)
  • timeAgo (3-32)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (3)
apps/web/ui/links/link-title-column.tsx (1)

384-390: LGTM! Clean integration of the new TimestampTooltip component.

The usage correctly wraps the compact timeAgo display with rich timestamp details in the tooltip, preserving the existing delayDuration and appropriately showing only the local timezone row for this context.

packages/ui/src/timestamp-tooltip.tsx (2)

111-115: Verify UNIX timestamp units: milliseconds vs. seconds.

date.getTime() returns milliseconds since the epoch, but UNIX timestamps are conventionally expressed in seconds. Verify whether milliseconds are intended here or if this should be divided by 1000.

If seconds are expected:

  unix: {
    label: "UNIX Timestamp",
-   value: date.getTime().toString(),
+   value: Math.floor(date.getTime() / 1000).toString(),
    valueMono: true,
  },

136-146: LGTM! Clipboard async handling is correctly implemented.

The click handler properly uses async/await and catches rejected promises, addressing the issue flagged in previous reviews. Toast feedback provides good UX for both success and error cases.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
packages/ui/src/timestamp-tooltip.tsx (2)

107-109: DST bug still present: short timezone label uses current time instead of the timestamp.

As flagged in the previous review and confirmed by TWilson023, using new Date() yields the current timezone abbreviation (e.g., PDT) even if the timestamp is in a different DST period (e.g., PST).

The fix should derive the short label from the actual date variable:

-              shortLabel: new Date()
-                .toLocaleTimeString("en-US", { timeZoneName: "short" })
-                .split(" ")[2],
+              shortLabel: Intl.DateTimeFormat("en-US", { 
+                hour: "2-digit", 
+                timeZoneName: "short" 
+              })
+                .formatToParts(date)
+                .find((p) => p.type === "timeZoneName")?.value,

Based on learnings and previous review discussion.


72-93: Future timestamps and empty duration edge cases not handled.

The relative duration logic has two unresolved issues from the previous review:

  1. Future timestamps: When date > now, diff becomes negative and intervalToDuration receives inverted start/end, producing incorrect results.
  2. Empty duration: If formatDuration returns an empty string (timestamps very close together), the output becomes just " ago".

Apply this refactor to handle both cases:

+ const now = new Date();
+ const isPast = date.getTime() <= now.getTime();
+
- const diff = new Date().getTime() - date.getTime();
+ const diff = Math.abs(now.getTime() - date.getTime());
  const relativeDuration = intervalToDuration({
-   start: date,
-   end: new Date(),
+   start: isPast ? date : now,
+   end: isPast ? now : date,
  });
- const relative =
-   formatDuration(relativeDuration, {
+ const formattedDuration = formatDuration(relativeDuration, {
      delimiter: ", ",
      format: [
        "years",
        "months",
        "days",
        ...(diff < MONTH_MS
          ? [
              "hours" as const,
              ...(diff < DAY_MS
                ? ["minutes" as const, "seconds" as const]
                : []),
            ]
          : []),
      ],
-   }) + " ago";
+   });
+ const relative = formattedDuration
+   ? `${formattedDuration} ${isPast ? "ago" : "from now"}`
+   : "just now";
🧹 Nitpick comments (2)
packages/ui/src/timestamp-tooltip.tsx (2)

57-58: Unreachable defensive throw creates confusing error contract.

The parent component (TimestampTooltip) already validates the timestamp at line 34, so this throw is unreachable in normal execution. If it somehow executes, throwing before hooks violates React's rules.

Remove the redundant check or replace it with a development-only assertion:

- if (!timestamp)
-   throw new Error("Falsy timestamp not permitted in TimestampTooltipContent");
+ if (process.env.NODE_ENV === "development" && !timestamp) {
+   console.error("TimestampTooltipContent received falsy timestamp");
+ }

134-139: Consider cleanup and performance implications of second-by-second re-renders.

Re-rendering every second is appropriate for keeping relative timestamps fresh, but consider:

  1. When the tooltip is not visible, this still runs (minor performance concern)
  2. Multiple tooltip instances on a page each have their own intervals

For optimization, consider using a global interval or visibility detection, though the current approach is acceptable for typical use cases.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a5bcdd4 and 1cb6f88.

📒 Files selected for processing (2)
  • apps/web/ui/analytics/events/events-table.tsx (2 hunks)
  • packages/ui/src/timestamp-tooltip.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/ui/analytics/events/events-table.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (2)
packages/ui/src/timestamp-tooltip.tsx (2)

156-164: Good: Async clipboard handling is now correct.

The clipboard write is properly awaited and error handling works as expected. This addresses the previous review concern.


19-26: Robust timezone detection with appropriate fallback.

The getLocalTimeZone function correctly uses the Intl API with proper error handling and fallback.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
packages/ui/src/timestamp-tooltip.tsx (2)

72-93: Handle future timestamps and empty duration edge cases.

As flagged in previous reviews, this code doesn't handle:

  1. Future timestamps: intervalToDuration expects start < end. For future dates, results are undefined or incorrect.
  2. Zero-duration edge case: If formatDuration returns empty string (timestamp ≈ now), the display shows just " ago".

Apply this fix:

+ const now = new Date();
+ const isPast = date.getTime() <= now.getTime();
+ 
- const diff = new Date().getTime() - date.getTime();
+ const diff = Math.abs(now.getTime() - date.getTime());
  const relativeDuration = intervalToDuration({
-   start: date,
-   end: new Date(),
+   start: isPast ? date : now,
+   end: isPast ? now : date,
  });
- const relative =
-   formatDuration(relativeDuration, {
+ const formattedDuration = formatDuration(relativeDuration, {
      delimiter: ", ",
      format: [
        "years",
        "months",
        "days",
        ...(diff < MONTH_MS
          ? [
              "hours" as const,
              ...(diff < DAY_MS
                ? ["minutes" as const, "seconds" as const]
                : []),
            ]
          : []),
      ],
-   }) + " ago";
+   });
+ const relative = formattedDuration
+   ? `${formattedDuration} ${isPast ? "ago" : "from now"}`
+   : "just now";

108-110: Fix DST bug: short label uses current time instead of timestamp's date.

As flagged in previous reviews, line 108 uses new Date() (current time) to derive the timezone abbreviation. This returns the wrong abbreviation when the timestamp is in a different DST period (e.g., shows "PDT" for a winter timestamp that should be "PST").

Apply this fix to use the actual timestamp's date:

              label: getLocalTimeZone(),
-             shortLabel: new Date()
-               .toLocaleTimeString("en-US", { timeZoneName: "short" })
-               .split(" ")[2],
+             shortLabel: Intl.DateTimeFormat("en-US", { 
+               hour: "2-digit", 
+               timeZoneName: "short" 
+             })
+               .formatToParts(date)
+               .find((p) => p.type === "timeZoneName")?.value,
              successMessageLabel: "local timestamp",

This also eliminates the brittle string split.

🧹 Nitpick comments (2)
apps/web/ui/customers/customer-table/customer-table.tsx (1)

175-186: Consider showing all timestamp formats for consistency.

This table uses rows={["local"]} while most other tables in this PR use rows={["local", "utc", "unix"]} (e.g., events-table.tsx line 191, link-creator-info.tsx line 33). Providing UTC and UNIX timestamps helps users working across timezones or with APIs.

Apply this diff for consistency:

           <TimestampTooltip
             timestamp={row.original.createdAt}
-            rows={["local"]}
+            rows={["local", "utc", "unix"]}
             side="left"
             delayDuration={150}
           >
apps/web/app/(ee)/partners.dub.co/(dashboard)/programs/[programSlug]/(enrolled)/earnings/earnings-table.tsx (1)

82-88: Consider including UTC and UNIX timestamps for consistency.

Like the customer table, this uses rows={["local"]} while most other tables show all three formats. Partners often work with API integrations where UTC/UNIX timestamps are valuable.

Apply this diff:

           <TimestampTooltip
             timestamp={row.original.createdAt}
             side="right"
-            rows={["local"]}
+            rows={["local", "utc", "unix"]}
           >
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1cb6f88 and c65cbbf.

📒 Files selected for processing (9)
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/programs/[programSlug]/(enrolled)/earnings/earnings-table.tsx (2 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/campaigns/[campaignId]/campaign-events-columns.tsx (2 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/commission-table.tsx (2 hunks)
  • apps/web/ui/analytics/events/events-table.tsx (2 hunks)
  • apps/web/ui/customers/customer-activity-list.tsx (2 hunks)
  • apps/web/ui/customers/customer-sales-table.tsx (2 hunks)
  • apps/web/ui/customers/customer-table/customer-table.tsx (2 hunks)
  • apps/web/ui/links/link-builder/link-creator-info.tsx (2 hunks)
  • packages/ui/src/timestamp-tooltip.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/campaigns/[campaignId]/campaign-events-columns.tsx
  • apps/web/ui/customers/customer-activity-list.tsx
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/commission-table.tsx
  • apps/web/ui/customers/customer-sales-table.tsx
🧰 Additional context used
🧬 Code graph analysis (4)
apps/web/ui/analytics/events/events-table.tsx (1)
packages/ui/src/timestamp-tooltip.tsx (1)
  • TimestampTooltip (28-50)
apps/web/ui/customers/customer-table/customer-table.tsx (1)
packages/ui/src/timestamp-tooltip.tsx (1)
  • TimestampTooltip (28-50)
apps/web/app/(ee)/partners.dub.co/(dashboard)/programs/[programSlug]/(enrolled)/earnings/earnings-table.tsx (1)
packages/ui/src/timestamp-tooltip.tsx (1)
  • TimestampTooltip (28-50)
apps/web/ui/links/link-builder/link-creator-info.tsx (2)
packages/ui/src/timestamp-tooltip.tsx (1)
  • TimestampTooltip (28-50)
packages/utils/src/functions/time-ago.ts (1)
  • timeAgo (3-32)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (2)
apps/web/ui/links/link-builder/link-creator-info.tsx (1)

31-37: LGTM!

Good use of TimestampTooltip with comprehensive timestamp formats and relative time display via timeAgo.

apps/web/ui/analytics/events/events-table.tsx (1)

188-196: LGTM!

The implementation correctly uses TimestampTooltip with full timestamp context and proper styling (select-none prevents accidental text selection during copy).

unix: {
label: "UNIX Timestamp",
successMessageLabel: "UNIX timestamp",
value: (date.getTime() / 1000).toString(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use integer seconds for UNIX timestamp.

UNIX timestamps are conventionally represented as integer seconds. The current division by 1000 can produce fractional seconds (e.g., 1234567890.123).

Apply this fix:

            unix: {
              label: "UNIX Timestamp",
              successMessageLabel: "UNIX timestamp",
-             value: (date.getTime() / 1000).toString(),
+             value: Math.floor(date.getTime() / 1000).toString(),
              valueMono: true,
            },
🤖 Prompt for AI Agents
In packages/ui/src/timestamp-tooltip.tsx around line 128, the UNIX timestamp is
set using (date.getTime() / 1000) which can produce fractional seconds; change
it to an integer by using Math.floor(date.getTime() / 1000) (or Math.trunc) so
the value is an integer number of seconds to conform to UNIX timestamp
conventions.

@steven-tey steven-tey enabled auto-merge October 17, 2025 21:59
@steven-tey steven-tey disabled auto-merge October 17, 2025 22:01
@steven-tey steven-tey merged commit 7ffc571 into main Oct 17, 2025
7 of 8 checks passed
@steven-tey steven-tey deleted the timestamp-tooltip branch October 17, 2025 22:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants