Skip to content

Conversation

eunjae-lee
Copy link
Contributor

@eunjae-lee eunjae-lee commented Jul 18, 2025

What does this PR do?

This PR adds hourly booking charts on the /insights page.

In order for that, I have

  • added a new method getHourlyBookingStats in InsightsBookingService to fetch the data

  • added a trpc handler to run that method

  • implemented BookingsByHourChart.tsx

  • update some styles here and there

  • Fixes CAL-6108

Visual Demo (For contributors especially)

Screenshot.2025-07-21.at.14.00.45.mp4
Screenshot.2025-07-21.at.14.00.19.mp4

Mandatory Tasks (DO NOT REMOVE)

  • I have self-reviewed the code (A decent size PR without self-review might be rejected).
  • N/A - I have updated the developer docs in /docs if this PR makes changes that would require a documentation change. If N/A, write N/A here and check the checkbox.
  • I confirm automated tests are in place that prove my fix is effective or that my feature works.

How should this be tested?

  • Are there environment variables that should be set?
  • What are the minimal test data to have?
  • What is expected (happy path) to have (input and output)?
  • Any other important info that could help to test that PR

Checklist

  • I haven't read the contributing guide
  • My code doesn't follow the style guidelines of this project
  • I haven't commented my code, particularly in hard-to-understand areas
  • I haven't checked if my changes generate no new warnings

Copy link

linear bot commented Jul 18, 2025

Copy link
Contributor

coderabbitai bot commented Jul 18, 2025

Walkthrough

This set of changes introduces a new "Bookings by Hour" analytics feature across both backend and frontend. On the backend, a new method getBookingsByHourStats is added to the InsightsBookingService to retrieve hourly booking statistics using a raw SQL query with timezone-aware grouping, supported by a new Zod schema for input validation and a corresponding TRPC query procedure. The SQL condition formatting in the insights services is updated for consistent logical grouping. On the frontend, a new BookingsByHourChart React component and supporting chart content are implemented, along with a playground/demo page and navigation link. The insights dashboard layout is updated to include the new chart. Localization strings are updated to support the new feature.

Estimated code review effort

3 (30–60 minutes)

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

packages/features/insights/components/BookingsByHourChart.tsx

Oops! Something went wrong! :(

ESLint: 8.57.1

ESLint couldn't find the plugin "eslint-plugin-playwright".

(The package "eslint-plugin-playwright" was not found when loaded as a Node module from the directory "".)

It's likely that the plugin isn't installed correctly. Try reinstalling by running the following:

npm install eslint-plugin-playwright@latest --save-dev

The plugin "eslint-plugin-playwright" was referenced from the config file in ".eslintrc.js".

If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team.

packages/lib/server/service/insightsBooking.ts

Oops! Something went wrong! :(

ESLint: 8.57.1

ESLint couldn't find the plugin "eslint-plugin-playwright".

(The package "eslint-plugin-playwright" was not found when loaded as a Node module from the directory "".)

It's likely that the plugin isn't installed correctly. Try reinstalling by running the following:

npm install eslint-plugin-playwright@latest --save-dev

The plugin "eslint-plugin-playwright" was referenced from the config file in ".eslintrc.js".

If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 09d991e and f62e301.

📒 Files selected for processing (2)
  • packages/features/insights/components/BookingsByHourChart.tsx (1 hunks)
  • packages/lib/server/service/insightsBooking.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/features/insights/components/BookingsByHourChart.tsx
  • packages/lib/server/service/insightsBooking.ts
⏰ 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). (2)
  • GitHub Check: Install dependencies / Yarn install & cache
  • GitHub Check: Security Check
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@keithwillcode keithwillcode added consumer core area: core, team members only labels Jul 18, 2025
Copy link

delve-auditor bot commented Jul 18, 2025

No security or compliance issues detected. Reviewed everything up to f62e301.

Security Overview
  • 🔎 Scanned files: 14 changed file(s)
Detected Code Changes
Change Type Relevant files
Enhancement ► page.tsx (bookings-by-hour)
    Add new bookings by hour playground page
► insights-view.tsx
    Update insights layout and add BookingsByHourChart
► BookingsByHourChart.tsx
    Add new bookings by hour chart component
► insightsBooking.ts
    Add booking by hour stats functionality
► trpc-router.ts
    Add bookingsByHourStats endpoint
Configuration changes ► common.json
    Add bookings by hour translation

Reply to this PR with @delve-auditor followed by a description of what change you want and we'll auto-submit a change to this PR to implement it.

Copy link

vercel bot commented Jul 18, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

2 Skipped Deployments
Name Status Preview Comments Updated (UTC)
cal ⬜️ Ignored (Inspect) Jul 21, 2025 1:24pm
cal-eu ⬜️ Ignored (Inspect) Jul 21, 2025 1:24pm

@eunjae-lee eunjae-lee marked this pull request as ready for review July 21, 2025 11:54
@graphite-app graphite-app bot requested a review from a team July 21, 2025 11:54
Copy link

graphite-app bot commented Jul 21, 2025

Graphite Automations

"Add consumer team as reviewer" took an action on this PR • (07/21/25)

1 reviewer was added to this PR based on Keith Williams's automation.

"Add ready-for-e2e label" took an action on this PR • (07/21/25)

1 label was added to this PR based on Keith Williams's automation.

@dosubot dosubot bot added insights area: insights, analytics ✨ feature New feature or request labels Jul 21, 2025
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: 2

🧹 Nitpick comments (3)
apps/web/app/(use-page-wrapper)/settings/(admin-layout)/admin/playground/page.tsx (1)

8-11: Add trailing comma & localise titles

  1. A trailing comma after the last object keeps the array style consistent with the previous entry.
  2. Both link titles are user-visible strings; they should flow through our i18n layer instead of being hard-coded.
   {
     title: "Bookings by Hour",
     href: "/settings/admin/playground/bookings-by-hour",
+  },

Example localisation:

import { useTranslations } from "next-intl";
// …
const t = useTranslations("admin.playground");
const LINKS = [
  { title: t("routing_funnel"), href: "/settings/admin/playground/routing-funnel" },
  { title: t("bookings_by_hour"), href: "/settings/admin/playground/bookings-by-hour" },
];

Ensure the corresponding keys exist in the locale files (admin.playground.routing_funnel, admin.playground.bookings_by_hour).

packages/features/insights/components/TotalBookingUsersTable.tsx (1)

25-26: Padding override & potentially non-unique key

ChartCardItem already sets py-3.5; appending py-3 overrides it via Tailwind’s last-class-wins rule. That works, but it leaves duplicate utility classes in the markup and may confuse future readers. Consider either:

-<ChartCardItem key={item.userId} count={item.count} className="py-3">
+<ChartCardItem key={item.userId ?? `${item.emailMd5}-${item.count}`}" count={item.count} className="py-3">

and remove the hard-coded py-3.5 inside ChartCardItem if finer control is needed across callers.

Additionally, key={item.userId} can collapse to "null" for guests, causing React key collisions. Safeguard with a fallback (e.g., email hash + count or array index).

apps/web/public/static/locales/en/common.json (1)

2170-2170: New key looks good – ensure parity across all locales

"bookings_by_hour" follows the existing snake-case convention and the value reads well.
Just make sure the key is added (even if untranslated) to every other locale file (common.json in de, fr, etc.) to avoid runtime fall-backs or missing-key warnings.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e1d7631 and ca2112b.

📒 Files selected for processing (14)
  • apps/web/app/(use-page-wrapper)/settings/(admin-layout)/admin/playground/bookings-by-hour/page.tsx (1 hunks)
  • apps/web/app/(use-page-wrapper)/settings/(admin-layout)/admin/playground/page.tsx (1 hunks)
  • apps/web/modules/insights/insights-view.tsx (2 hunks)
  • apps/web/public/static/locales/en/common.json (1 hunks)
  • packages/features/insights/components/BookingsByHourChart.tsx (1 hunks)
  • packages/features/insights/components/ChartCard.tsx (1 hunks)
  • packages/features/insights/components/LoadingInsights.tsx (1 hunks)
  • packages/features/insights/components/TotalBookingUsersTable.tsx (1 hunks)
  • packages/features/insights/components/index.ts (1 hunks)
  • packages/features/insights/server/raw-data.schema.ts (1 hunks)
  • packages/features/insights/server/trpc-router.ts (2 hunks)
  • packages/lib/server/service/__tests__/insightsRouting.integration-test.ts (1 hunks)
  • packages/lib/server/service/insightsBooking.ts (1 hunks)
  • packages/lib/server/service/insightsRouting.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (12)
📓 Common learnings
Learnt from: eunjae-lee
PR: calcom/cal.com#22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.341Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.
packages/features/insights/components/TotalBookingUsersTable.tsx (1)

Learnt from: eunjae-lee
PR: #22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.341Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.

packages/features/insights/components/index.ts (2)

Learnt from: eunjae-lee
PR: #22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.341Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.

Learnt from: eunjae-lee
PR: #22106
File: packages/features/insights/components/RoutingFunnel.tsx:15-17
Timestamp: 2025-07-15T12:58:40.497Z
Learning: In the insights routing funnel component (packages/features/insights/components/RoutingFunnel.tsx), the useColumnFilters exclusions are intentionally different from the general useInsightsParameters exclusions. RoutingFunnel specifically excludes only ["createdAt"] while useInsightsParameters excludes ["bookingUserId", "formId", "createdAt", "eventTypeId"]. This difference is by design.

apps/web/app/(use-page-wrapper)/settings/(admin-layout)/admin/playground/page.tsx (2)

Learnt from: alishaz-polymath
PR: #22304
File: packages/features/eventtypes/components/MultiplePrivateLinksController.tsx:92-94
Timestamp: 2025-07-16T06:42:27.001Z
Learning: In the MultiplePrivateLinksController component (packages/features/eventtypes/components/MultiplePrivateLinksController.tsx), the currentLink.maxUsageCount ?? 1 fallback in the openSettingsDialog function is intentional. Missing maxUsageCount values indicate old/legacy private links that existed before the expiration feature was added, and they should default to single-use behavior (1) for backward compatibility.

Learnt from: eunjae-lee
PR: #22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.341Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.

packages/lib/server/service/__tests__/insightsRouting.integration-test.ts (3)

Learnt from: eunjae-lee
PR: #22106
File: packages/lib/server/service/insightsRouting.ts:367-368
Timestamp: 2025-07-15T13:02:17.359Z
Learning: In the InsightsRoutingService (packages/lib/server/service/insightsRouting.ts), multi-select filter data is already validated by zod before reaching the buildFormFieldSqlCondition method, so null/undefined values are not present in filterValue.data arrays and don't need to be filtered out.

Learnt from: eunjae-lee
PR: #22106
File: packages/features/insights/components/RoutingFunnel.tsx:15-17
Timestamp: 2025-07-15T12:58:40.497Z
Learning: In the insights routing funnel component (packages/features/insights/components/RoutingFunnel.tsx), the useColumnFilters exclusions are intentionally different from the general useInsightsParameters exclusions. RoutingFunnel specifically excludes only ["createdAt"] while useInsightsParameters excludes ["bookingUserId", "formId", "createdAt", "eventTypeId"]. This difference is by design.

Learnt from: eunjae-lee
PR: #22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.341Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.

packages/lib/server/service/insightsRouting.ts (3)

Learnt from: eunjae-lee
PR: #22106
File: packages/lib/server/service/insightsRouting.ts:367-368
Timestamp: 2025-07-15T13:02:17.359Z
Learning: In the InsightsRoutingService (packages/lib/server/service/insightsRouting.ts), multi-select filter data is already validated by zod before reaching the buildFormFieldSqlCondition method, so null/undefined values are not present in filterValue.data arrays and don't need to be filtered out.

Learnt from: eunjae-lee
PR: #22106
File: packages/features/insights/components/RoutingFunnel.tsx:15-17
Timestamp: 2025-07-15T12:58:40.497Z
Learning: In the insights routing funnel component (packages/features/insights/components/RoutingFunnel.tsx), the useColumnFilters exclusions are intentionally different from the general useInsightsParameters exclusions. RoutingFunnel specifically excludes only ["createdAt"] while useInsightsParameters excludes ["bookingUserId", "formId", "createdAt", "eventTypeId"]. This difference is by design.

Learnt from: eunjae-lee
PR: #22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.341Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.

apps/web/app/(use-page-wrapper)/settings/(admin-layout)/admin/playground/bookings-by-hour/page.tsx (1)

Learnt from: eunjae-lee
PR: #22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.341Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.

apps/web/public/static/locales/en/common.json (1)

Learnt from: bandhan-majumder
PR: #22359
File: packages/lib/server/locales/en/common.json:1336-1339
Timestamp: 2025-07-14T16:31:45.201Z
Learning: When making localization changes for new features, it's often safer to add new strings rather than modify existing ones to avoid breaking existing functionality that depends on the original strings. This approach allows for feature-specific customization while maintaining backward compatibility.

packages/features/insights/server/trpc-router.ts (3)

Learnt from: eunjae-lee
PR: #22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.341Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.

Learnt from: eunjae-lee
PR: #22106
File: packages/features/insights/components/RoutingFunnel.tsx:15-17
Timestamp: 2025-07-15T12:58:40.497Z
Learning: In the insights routing funnel component (packages/features/insights/components/RoutingFunnel.tsx), the useColumnFilters exclusions are intentionally different from the general useInsightsParameters exclusions. RoutingFunnel specifically excludes only ["createdAt"] while useInsightsParameters excludes ["bookingUserId", "formId", "createdAt", "eventTypeId"]. This difference is by design.

Learnt from: eunjae-lee
PR: #22106
File: packages/lib/server/service/insightsRouting.ts:367-368
Timestamp: 2025-07-15T13:02:17.359Z
Learning: In the InsightsRoutingService (packages/lib/server/service/insightsRouting.ts), multi-select filter data is already validated by zod before reaching the buildFormFieldSqlCondition method, so null/undefined values are not present in filterValue.data arrays and don't need to be filtered out.

apps/web/modules/insights/insights-view.tsx (2)

Learnt from: eunjae-lee
PR: #22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.341Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.

Learnt from: eunjae-lee
PR: #22106
File: packages/features/insights/components/RoutingFunnel.tsx:15-17
Timestamp: 2025-07-15T12:58:40.497Z
Learning: In the insights routing funnel component (packages/features/insights/components/RoutingFunnel.tsx), the useColumnFilters exclusions are intentionally different from the general useInsightsParameters exclusions. RoutingFunnel specifically excludes only ["createdAt"] while useInsightsParameters excludes ["bookingUserId", "formId", "createdAt", "eventTypeId"]. This difference is by design.

packages/features/insights/components/BookingsByHourChart.tsx (2)

Learnt from: eunjae-lee
PR: #22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.341Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.

Learnt from: eunjae-lee
PR: #22106
File: packages/features/insights/components/RoutingFunnel.tsx:15-17
Timestamp: 2025-07-15T12:58:40.497Z
Learning: In the insights routing funnel component (packages/features/insights/components/RoutingFunnel.tsx), the useColumnFilters exclusions are intentionally different from the general useInsightsParameters exclusions. RoutingFunnel specifically excludes only ["createdAt"] while useInsightsParameters excludes ["bookingUserId", "formId", "createdAt", "eventTypeId"]. This difference is by design.

packages/features/insights/server/raw-data.schema.ts (2)

Learnt from: eunjae-lee
PR: #22106
File: packages/lib/server/service/insightsRouting.ts:367-368
Timestamp: 2025-07-15T13:02:17.359Z
Learning: In the InsightsRoutingService (packages/lib/server/service/insightsRouting.ts), multi-select filter data is already validated by zod before reaching the buildFormFieldSqlCondition method, so null/undefined values are not present in filterValue.data arrays and don't need to be filtered out.

Learnt from: eunjae-lee
PR: #22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.341Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.

🧬 Code Graph Analysis (3)
packages/features/insights/components/TotalBookingUsersTable.tsx (1)
packages/features/insights/components/ChartCard.tsx (1)
  • ChartCardItem (54-73)
apps/web/app/(use-page-wrapper)/settings/(admin-layout)/admin/playground/bookings-by-hour/page.tsx (2)
packages/features/insights/components/ChartCard.tsx (1)
  • ChartCard (14-52)
packages/features/insights/components/BookingsByHourChart.tsx (1)
  • BookingsByHourChartContent (28-65)
packages/features/insights/server/trpc-router.ts (3)
packages/features/insights/server/raw-data.schema.ts (1)
  • bookingRepositoryBaseInputSchema (73-81)
packages/lib/server/service/insightsBooking.ts (1)
  • InsightsBookingService (45-269)
packages/platform/libraries/index.ts (1)
  • TRPCError (56-56)
⏰ 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). (3)
  • GitHub Check: Detect changes
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Security Check
🔇 Additional comments (16)
packages/features/insights/components/LoadingInsights.tsx (1)

2-2: Import reorder looks fine

No functional impact; ordering change complies with our style-guides.

packages/features/insights/components/index.ts (1)

3-4: LGTM! Clean export addition.

The new BookingsByHourChart export is correctly positioned alphabetically and follows the consistent formatting pattern of the other exports.

packages/lib/server/service/insightsRouting.ts (1)

172-176: LGTM! Consistent SQL condition formatting.

The addition of parentheses around SQL conditions improves logical grouping and maintains consistency across the codebase. This formatting change enhances readability without altering the underlying logic.

packages/lib/server/service/__tests__/insightsRouting.integration-test.ts (1)

557-557: LGTM! Test updated to match implementation changes.

The test expectation correctly reflects the new SQL condition formatting with additional parentheses wrapping, ensuring consistency between the implementation and test coverage.

packages/features/insights/components/ChartCard.tsx (1)

54-68: LGTM! Good component enhancement for styling flexibility.

The addition of the optional className prop properly extends the component's styling capabilities while maintaining backward compatibility. The implementation correctly uses the classNames utility to merge custom styles with existing base classes.

apps/web/app/(use-page-wrapper)/settings/(admin-layout)/admin/playground/bookings-by-hour/page.tsx (1)

1-61: LGTM! Well-structured playground page.

This playground page effectively demonstrates the BookingsByHourChart component with:

  • Comprehensive sample data covering all 24 hours
  • Proper component composition using ChartCard and BookingsByHourChartContent
  • Localization support with useLocale
  • Clean layout with descriptive content and sample data display
  • Good developer experience for testing and demonstration

The implementation follows established patterns and React best practices.

packages/features/insights/server/trpc-router.ts (2)

11-11: LGTM!

The import of bookingRepositoryBaseInputSchema is correctly added to support the new hourly bookings stats procedure.


13-13: LGTM!

The import of InsightsBookingService is correctly added to support the new procedure implementation.

packages/features/insights/server/raw-data.schema.ts (1)

73-81: Well-structured schema addition

The new bookingRepositoryBaseInputSchema follows established patterns and includes appropriate field types for hourly booking statistics. The required timeZone field and optional nullable filters for eventTypeId and memberUserId provide good flexibility for the feature.

apps/web/modules/insights/insights-view.tsx (3)

16-16: LGTM!

The import of BookingsByHourChart is correctly added to support the new hourly bookings feature.


75-82: Excellent responsive layout improvement

The new grid structure effectively organizes the charts with proper responsive behavior. The BookingsByHourChart and AverageEventDurationChart are logically grouped together, each spanning 2 columns on smaller screens and sharing the 4-column grid on larger screens.


84-102: Well-organized component grouping

The reorganization of tables into separate grid containers improves visual separation and maintains consistent responsive behavior across different screen sizes. The layout structure is clean and logical.

packages/lib/server/service/insightsBooking.ts (2)

68-115: Excellent implementation of hourly booking statistics

The method is well-implemented with several strong points:

  • Proper date format validation prevents SQL injection and ensures data integrity
  • Raw SQL query with CTE provides efficient data aggregation
  • Timezone handling using PostgreSQL's AT TIME ZONE is correct
  • Filtering by 'accepted' status ensures only confirmed bookings are counted
  • Complete 24-hour data array prevents gaps in the chart visualization

The use of Map for result lookup and Array.from for generating the complete hourly dataset is an efficient approach.


122-127: Improved SQL condition formatting

The consistent wrapping of SQL conditions in parentheses ensures proper logical grouping when combining authorization and filter conditions. This prevents potential issues with operator precedence in complex WHERE clauses.

packages/features/insights/components/BookingsByHourChart.tsx (2)

28-65: Well-implemented chart content component

The BookingsByHourChartContent component is excellently structured with:

  • Proper data transformation for hour formatting
  • Appropriate empty state handling
  • Good chart configuration with disabled decimals and clean styling
  • Responsive design using ResponsiveContainer
  • Proper accessibility with custom tooltip

The bar chart styling with rounded corners and hover effects provides a polished user experience.


100-132: Excellent main chart component implementation

The main BookingsByHourChart component demonstrates excellent practices:

  • Proper use of custom hooks for parameters and data table context
  • Well-configured TRPC query with appropriate stale time and batching settings
  • Proper loading and error state handling
  • Good fallback to CURRENT_TIMEZONE when timeZone is not available
  • Clean integration with the ChartCard wrapper

The component follows established patterns in the codebase and provides a solid foundation for the hourly booking insights feature.

Copy link
Member

@sean-brydon sean-brydon left a comment

Choose a reason for hiding this comment

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

Happy with this! Great job! Thanks for fixing feedback

@eunjae-lee eunjae-lee enabled auto-merge (squash) July 21, 2025 13:31
@eunjae-lee eunjae-lee merged commit e7535b9 into main Jul 21, 2025
61 of 63 checks passed
@eunjae-lee eunjae-lee deleted the eunjae/cal-6108-add-booking-hours-charts-to-insights branch July 21, 2025 13:52
Copy link
Contributor

E2E results are ready!

zomars pushed a commit that referenced this pull request Jul 22, 2025
* feat: add hourly booking charts on /insights

* safe guard

* rename

* update styles

* fix data

* clean up

* clean up

* re-order charts

* update style

* apply feedback

* rename

* update query
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
consumer core area: core, team members only ✨ feature New feature or request insights area: insights, analytics ready-for-e2e
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants