Skip to content

feat: add calendar cache status and actions (#22532)#11

Open
everettbu wants to merge 1 commit into
calendar-cache-foundationfrom
introduce-cache-key-overflow
Open

feat: add calendar cache status and actions (#22532)#11
everettbu wants to merge 1 commit into
calendar-cache-foundationfrom
introduce-cache-key-overflow

Conversation

@everettbu

@everettbu everettbu commented Jul 28, 2025

Copy link
Copy Markdown

Test 1

Summary by CodeRabbit

  • New Features

    • Introduced a dropdown menu for managing calendar credentials, including cache status display, cache deletion, and app removal options.
    • Added the ability to view and delete cached calendar data for Google Calendar integrations.
    • Connected calendars now display the last cache update timestamp.
  • Improvements

    • Enhanced localization with new cache management strings.
    • Expanded calendar and credential data shown to users with additional metadata.
  • Bug Fixes

    • Improved consistency and reliability in cache status reporting and credential updates.
  • Chores

    • Updated development scripts and added a utility script for testing Google Calendar webhooks.
    • Database schema updated to track cache update times.

* feat: add calendar cache status dropdown

- Add updatedAt field to CalendarCache schema with migration
- Create tRPC cacheStatus endpoint for fetching cache timestamps
- Add action dropdown to CalendarSwitch for Google Calendar entries
- Display formatted last updated timestamp in dropdown
- Add placeholder for cache deletion functionality
- Include translation strings for dropdown content

The dropdown only appears for Google Calendar integrations that have
active cache entries and provides cache management options for future
extensibility.

Co-Authored-By: zomars@cal.com <zomars@me.com>

* fix: resolve Prisma type incompatibilities in repository files

- Remove problematic satisfies clause in selectedCalendar.ts
- Add missing cacheStatus parameter to ConnectedCalendarList component
- Fixes type errors that were preventing CI from passing

Co-Authored-By: zomars@cal.com <zomars@me.com>

* refactor: integrate cache status into connectedCalendars handler

- Remove separate cacheStatus tRPC endpoint as requested
- Return cache status as separate field in connectedCalendars response
- Update UI components to use cache data from connectedCalendars
- Fix Prisma type incompatibilities in repository files

Co-Authored-By: zomars@cal.com <zomars@me.com>

* fix: resolve Prisma type incompatibilities and fix data flow for cache status

- Fix Prisma.SortOrder usage in membership.ts orderBy clauses
- Remove problematic satisfies clause in selectedCalendar.ts
- Fix TeamSelect type reference in team.ts
- Update SelectedCalendarsSettingsWebWrapper to properly pass cacheStatus data flow

Co-Authored-By: zomars@cal.com <zomars@me.com>

* Discard changes to packages/lib/server/repository/membership.ts

* Discard changes to packages/lib/server/repository/team.ts

* fix: improve calendar cache dropdown with proper formatting and subscription logic

- Fix timestamp HTML entity encoding with interpolation escapeValue: false
- Only show dropdown for subscribed Google calendars (googleChannelId exists)
- Hide delete option when no cache data exists
- Include updatedAt and googleChannelId fields upstream in user repository
- Update data flow to pass subscription status through components

Co-Authored-By: zomars@cal.com <zomars@me.com>

* feat: update SelectedCalendar.updatedAt when Google webhooks trigger cache refresh

- Add updateManyByCredentialId method to SelectedCalendarRepository
- Update fetchAvailabilityAndSetCache to refresh SelectedCalendar timestamps
- Ensure webhook flow updates both CalendarCache and SelectedCalendar records
- Maintain proper timestamp tracking for calendar cache operations

Co-Authored-By: zomars@cal.com <zomars@me.com>

* Add script to automate Tunnelmole webhook setup

Introduces test-gcal-webhooks.sh to start Tunnelmole, extract the public URL, and update GOOGLE_WEBHOOK_URL in the .env file. Handles process management, rate limits, and ensures environment configuration for Google Calendar webhooks.

* Update dev:cron script to use npx tsx

Replaces 'ts-node' with 'npx tsx' in the dev:cron script for running cron-tester.ts, likely to improve compatibility or leverage tsx features.

* Update cache status string and improve CalendarSwitch UI

Renamed 'last_updated' to 'cache_last_updated' in locale file for clarity and updated CalendarSwitch to use the new string. Also added dark mode text color support for cache status display.

* refactor: move cache management to credential-level dropdown with Remove App

- Create CredentialActionsDropdown component consolidating cache and app removal actions
- Add deleteCache tRPC mutation for credential-level cache deletion
- Update connectedCalendars handler to include cacheUpdatedAt at credential level
- Move dropdown from individual CalendarSwitch to credential level in SelectedCalendarsSettingsWebWrapper
- Remove cache-related props from CalendarSwitch component
- Add translation strings for cache management actions
- Consolidate all credential-level actions (cache management + Remove App) in one dropdown

Co-Authored-By: zomars@cal.com <zomars@me.com>

* fix: remove duplicate translation keys in common.json

- Remove duplicate cache-related keys at lines 51-56
- Keep properly positioned keys later in file
- Addresses GitHub comment from zomars about duplicate keys

Co-Authored-By: zomars@cal.com <zomars@me.com>

* fix: rename translation key to cache_last_updated

- Address GitHub comment from zomars
- Rename 'last_updated' to 'cache_last_updated' for specificity
- Update usage in CredentialActionsDropdown component

Co-Authored-By: zomars@cal.com <zomars@me.com>

* fix: remove duplicate last_updated translation key

Co-Authored-By: zomars@cal.com <zomars@me.com>

* fix: add confirmation dialog for cache deletion and use repository pattern

- Add confirmation dialog for destructive cache deletion action
- Replace direct Prisma calls with CalendarCacheRepository pattern
- Add getCacheStatusByCredentialIds method to repository interface
- Fix import paths for UI components
- Address GitHub review comments from zomars

Co-Authored-By: zomars@cal.com <zomars@me.com>

* Update CredentialActionsDropdown.tsx

* Update common.json

* Update common.json

* fix: remove nested div wrapper to resolve HTML structure error

- Remove wrapping div around DisconnectIntegration component
- Fixes nested <p> tag validation error preventing Remove App functionality
- Maintains existing confirmation dialog patterns

Co-Authored-By: zomars@cal.com <zomars@me.com>

* Fix API handler response termination logic

Removed unnecessary return values after setting status in the integrations API handler. This clarifies response handling and prevents returning the response object when not needed. Resolves "API handler should not return a value, received object".

* fix: 400 is correct error code for computing slot for past booking (#22574)

* fix

* add test

* chore: release v5.5.1

* Refactor credential disconnect to use confirmation dialog

Replaces the DisconnectIntegration component with an inline confirmation dialog for removing app credentials. Adds disconnect mutation logic and updates UI to improve user experience and consistency.

* Set default value for CalendarCache.updatedAt

Added a default value of NOW() for the updatedAt column in the CalendarCache table to ensure existing and future rows have a valid timestamp. Updated the Prisma schema to reflect this change and provide compatibility for legacy data and raw inserts.

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Benny Joo <sldisek783@gmail.com>
Co-authored-by: emrysal <me@alexvanandel.com>
@coderabbitai

coderabbitai Bot commented Jul 28, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

This set of changes introduces cache management features for calendar integrations, including new database fields, repository methods, and UI components. It adds localization strings, updates data fetching to include cache timestamps, and implements TRPC endpoints and handlers for cache deletion. The UI now provides dropdown actions for cache and credential management. Supporting scripts and type definitions are also updated.

Changes

Cohort / File(s) Change Summary
Calendar Cache Schema & Migration
packages/prisma/schema.prisma, packages/prisma/migrations/20250715160635_add_calendar_cache_updated_at/migration.sql
Added updatedAt field to CalendarCache model and database table, with default and auto-update behavior.
Calendar Cache Repository Enhancements
packages/features/calendar-cache/calendar-cache.repository.interface.ts, packages/features/calendar-cache/calendar-cache.repository.ts, packages/features/calendar-cache/calendar-cache.repository.mock.ts
Added getCacheStatusByCredentialIds method to the interface, implementation, and mock for retrieving cache update timestamps by credential ID.
Selected Calendar Repository Update
packages/lib/server/repository/selectedCalendar.ts
Added updateManyByCredentialId static method for bulk updating by credential ID; simplified findMany.
Google Calendar Service Update
packages/app-store/googlecalendar/lib/CalendarService.ts
After fetching and caching calendar availability, now triggers a bulk update on related SelectedCalendar records.
Connected Calendars Data Enrichment
packages/trpc/server/routers/viewer/calendars/connectedCalendars.handler.ts, packages/lib/getConnectedDestinationCalendars.ts, packages/lib/server/repository/user.ts
Connected calendars now include cacheUpdatedAt, updatedAt, and googleChannelId fields in data retrieval and type definitions.
TRPC Cache Management Endpoint
packages/trpc/server/routers/viewer/calendars/_router.tsx, packages/trpc/server/routers/viewer/calendars/deleteCache.handler.ts
Added deleteCache mutation and handler for deleting cached calendar data by credential.
Credential Actions Dropdown UI
packages/features/apps/components/CredentialActionsDropdown.tsx, packages/platform/atoms/selected-calendars/wrappers/SelectedCalendarsSettingsWebWrapper.tsx
Introduced CredentialActionsDropdown component for cache and credential actions; integrated into calendar settings UI, replacing previous disconnect logic.
Localization
apps/web/public/static/locales/en/common.json
Added new English localization strings for cache management UI.
Dev & Utility Scripts
apps/web/package.json, scripts/test-gcal-webhooks.sh
Changed dev script to use npx tsx for cron testing; added a shell script for managing Google webhook tunnels.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant UI (CredentialActionsDropdown)
  participant TRPC
  participant deleteCacheHandler
  participant DB (CalendarCache)
  participant DB (Credential)

  User->>UI (CredentialActionsDropdown): Click "Delete Cache"
  UI (CredentialActionsDropdown)->>TRPC: Call deleteCache mutation with credentialId
  TRPC->>deleteCacheHandler: Invoke handler with user context, credentialId
  deleteCacheHandler->>DB (Credential): Verify credential ownership
  alt Credential valid
    deleteCacheHandler->>DB (CalendarCache): Delete cache entries for credentialId
    deleteCacheHandler-->>TRPC: Return { success: true }
    TRPC-->>UI (CredentialActionsDropdown): Return success
    UI (CredentialActionsDropdown)-->>User: Show success toast
  else Invalid credential
    deleteCacheHandler-->>TRPC: Throw access denied error
    TRPC-->>UI (CredentialActionsDropdown): Return error
    UI (CredentialActionsDropdown)-->>User: Show error toast
  end
Loading
sequenceDiagram
  participant UI (Settings Wrapper)
  participant TRPC
  participant connectedCalendarsHandler
  participant CalendarCacheRepository
  participant DB

  UI (Settings Wrapper)->>TRPC: Fetch connected calendars
  TRPC->>connectedCalendarsHandler: Handle request
  connectedCalendarsHandler->>DB: Fetch connected calendars
  connectedCalendarsHandler->>CalendarCacheRepository: getCacheStatusByCredentialIds(credentialIds)
  CalendarCacheRepository->>DB: Query cache groupBy credentialId, max(updatedAt)
  CalendarCacheRepository-->>connectedCalendarsHandler: Return cache status map
  connectedCalendarsHandler-->>TRPC: Return enriched calendars with cacheUpdatedAt
  TRPC-->>UI (Settings Wrapper): Return enriched data
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~18 minutes

Poem

🐇
In the warren of code, a cache now appears,
With timestamps and dropdowns, it quietly cheers.
Calendars update, and tunnels connect,
Localization strings for each new effect.
Reviewers, take heed—hop through with delight,
For cache-clearing bunnies keep calendars light!
🗓️✨

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch introduce-cache-key-overflow

🪧 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 generate unit tests to generate unit tests for 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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 (5)
scripts/test-gcal-webhooks.sh (2)

41-52: Fix unused loop variable.

The static analysis tool correctly identified that the loop variable i is unused. Consider using an underscore to indicate an intentionally unused variable:

-  for i in {1..20}; do
+  for _ in {1..20}; do

Otherwise, the retry logic and rate limit handling are well-implemented.


67-71: Consider portability for sed command.

The sed -i '' syntax is macOS-specific. For better portability across Linux and macOS, consider using a more portable approach:

-if grep -q '^GOOGLE_WEBHOOK_URL=' "$ENV_FILE"; then
-  sed -i '' -E "s|^GOOGLE_WEBHOOK_URL=.*|GOOGLE_WEBHOOK_URL=$TUNNEL_URL|" "$ENV_FILE"
-else
+if grep -q '^GOOGLE_WEBHOOK_URL=' "$ENV_FILE"; then
+  if [[ "$OSTYPE" == "darwin"* ]]; then
+    sed -i '' -E "s|^GOOGLE_WEBHOOK_URL=.*|GOOGLE_WEBHOOK_URL=$TUNNEL_URL|" "$ENV_FILE"
+  else
+    sed -i -E "s|^GOOGLE_WEBHOOK_URL=.*|GOOGLE_WEBHOOK_URL=$TUNNEL_URL|" "$ENV_FILE"
+  fi
+else

Alternatively, use a temporary file approach that works consistently across platforms.

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

3382-3388: Maintain placeholder & punctuation consistency across new i18n keys

  1. cache_last_updated currently uses {{timestamp}} whereas existing keys (e.g. refund_failed_subject, event_type_seats) rely on more descriptive placeholders like {{date}} or {{count}}. Consider renaming to something less implementation-specific such as {{date}} or {{value}} so translators have clearer context and you avoid leaking internal variable names into translation workflows.

  2. For user-facing strings, keep terminal punctuation consistent.

    • cache_deleted_successfully ends without a period while similar success toasts earlier (team_upgraded_successfully, profile_updated_successfully, etc.) omit punctuation, so this is fine.
    • error_deleting_cache follows the same pattern as other _error keys—good.
  3. No pluralisation or html tags are required here, but remember to escape colons in translation files (Last updated:) for certain i18n tools if they treat : specially.

Nothing blocking, just a light clean-up to stay aligned with existing conventions.

packages/app-store/googlecalendar/lib/CalendarService.ts (1)

1023-1024: Good integration of timestamp tracking with cache operations.

The call to SelectedCalendarRepository.updateManyByCredentialId after cache operations is well-positioned and follows the logical flow. The empty object parameter suggests it's leveraging automatic updatedAt field updates.

Consider adding error handling for the repository call to prevent cache operations from failing if the timestamp update encounters issues:

+    try {
       // Update SelectedCalendar.updatedAt for all calendars under this credential
       await SelectedCalendarRepository.updateManyByCredentialId(this.credential.id, {});
+    } catch (error) {
+      this.log.error("Failed to update selected calendar timestamps", safeStringify(error));
+      // Continue execution as this is not critical for cache functionality
+    }
packages/features/apps/components/CredentialActionsDropdown.tsx (1)

75-128: Well-structured dropdown UI with good UX patterns.

The implementation demonstrates solid UX practices:

  • Clear cache status display with formatted timestamps
  • Logical separation between cache and credential actions
  • Appropriate destructive styling for delete operations
  • Proper state management for modal interactions

Consider using a more accessible date formatting approach:

-                    {t("cache_last_updated", {
-                      timestamp: new Intl.DateTimeFormat("en-US", {
-                        dateStyle: "short",
-                        timeStyle: "short",
-                      }).format(new Date(cacheUpdatedAt)),
-                      interpolation: { escapeValue: false },
-                    })}
+                    {t("cache_last_updated", {
+                      timestamp: new Intl.DateTimeFormat(undefined, {
+                        dateStyle: "short",
+                        timeStyle: "short",
+                      }).format(new Date(cacheUpdatedAt)),
+                      interpolation: { escapeValue: false },
+                    })}

Using undefined instead of "en-US" respects the user's locale preferences.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 3784421 and cb78692.

📒 Files selected for processing (17)
  • apps/web/package.json (1 hunks)
  • apps/web/public/static/locales/en/common.json (1 hunks)
  • packages/app-store/googlecalendar/lib/CalendarService.ts (1 hunks)
  • packages/features/apps/components/CredentialActionsDropdown.tsx (1 hunks)
  • packages/features/calendar-cache/calendar-cache.repository.interface.ts (1 hunks)
  • packages/features/calendar-cache/calendar-cache.repository.mock.ts (1 hunks)
  • packages/features/calendar-cache/calendar-cache.repository.ts (1 hunks)
  • packages/lib/getConnectedDestinationCalendars.ts (1 hunks)
  • packages/lib/server/repository/selectedCalendar.ts (2 hunks)
  • packages/lib/server/repository/user.ts (1 hunks)
  • packages/platform/atoms/selected-calendars/wrappers/SelectedCalendarsSettingsWebWrapper.tsx (5 hunks)
  • packages/prisma/migrations/20250715160635_add_calendar_cache_updated_at/migration.sql (1 hunks)
  • packages/prisma/schema.prisma (1 hunks)
  • packages/trpc/server/routers/viewer/calendars/_router.tsx (2 hunks)
  • packages/trpc/server/routers/viewer/calendars/connectedCalendars.handler.ts (2 hunks)
  • packages/trpc/server/routers/viewer/calendars/deleteCache.handler.ts (1 hunks)
  • scripts/test-gcal-webhooks.sh (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (4)
packages/trpc/server/routers/viewer/calendars/_router.tsx (1)
packages/trpc/server/routers/viewer/calendars/deleteCache.handler.ts (1)
  • deleteCacheHandler (13-33)
packages/app-store/googlecalendar/lib/CalendarService.ts (2)
packages/lib/server/repository/selectedCalendar.ts (1)
  • SelectedCalendarRepository (42-438)
packages/platform/libraries/index.ts (1)
  • SelectedCalendarRepository (112-112)
packages/features/apps/components/CredentialActionsDropdown.tsx (1)
packages/ui/components/dropdown/Dropdown.tsx (5)
  • Dropdown (12-12)
  • DropdownMenuTrigger (15-26)
  • DropdownMenuContent (34-51)
  • DropdownMenuItem (63-71)
  • DropdownItem (161-181)
packages/lib/getConnectedDestinationCalendars.ts (1)
packages/types/Calendar.d.ts (1)
  • SelectedCalendar (318-321)
🪛 Shellcheck (0.10.0)
scripts/test-gcal-webhooks.sh

[warning] 41-41: i appears unused. Verify use (or export if used externally).

(SC2034)

🔇 Additional comments (28)
packages/lib/server/repository/selectedCalendar.ts (2)

260-260: LGTM! Good refactoring.

The simplification by removing the intermediate variable and directly passing parameters makes the code more concise and readable while maintaining the same functionality.


400-405: LGTM! Well-implemented bulk update method.

The new updateManyByCredentialId method provides a clean abstraction for bulk updating selected calendars by credential ID. The implementation correctly uses updateMany for efficient bulk operations and follows the repository pattern consistently.

scripts/test-gcal-webhooks.sh (3)

1-20: LGTM! Well-structured initialization.

The script has proper configuration setup with clear variable names, appropriate signal trapping for cleanup, and good process management using pgrep to check for existing Tunnelmole instances.


11-18: LGTM! Proper resource cleanup.

The cleanup function correctly handles process termination by only killing processes owned by the script and uses proper signal handling with kill and wait.


76-79: LGTM! Proper process lifecycle management.

The conditional wait logic correctly handles different scenarios - only waiting for processes the script started while allowing immediate exit when reusing existing tunnels.

packages/prisma/schema.prisma (1)

718-719: LGTM! Well-designed timestamp field.

The addition of the updatedAt field with @default(now()) and @updatedAt is properly implemented. The default value ensures existing records receive appropriate timestamps during migration, while the @updatedAt attribute provides automatic timestamp management for future updates.

packages/features/calendar-cache/calendar-cache.repository.mock.ts (1)

27-30: LGTM! Consistent mock implementation.

The new getCacheStatusByCredentialIds method follows the established mock pattern with appropriate logging and returns a sensible empty array result for the disabled cache scenario.

packages/lib/server/repository/user.ts (1)

899-900: LGTM! Appropriate addition of cache metadata fields.

The addition of updatedAt and googleChannelId to the selectedCalendars selection is consistent with the calendar cache status feature. These fields will enable the UI to display cache freshness information and manage Google Calendar integrations effectively.

packages/prisma/migrations/20250715160635_add_calendar_cache_updated_at/migration.sql (1)

1-9: LGTM! Well-structured migration with safe default handling.

The migration correctly adds the updatedAt column with DEFAULT NOW() to handle existing rows safely. The TIMESTAMP(3) type is appropriate for millisecond precision tracking.

Note: The warning comment at the top mentions "without a default value" but the actual SQL includes DEFAULT NOW(), which resolves the issue safely.

packages/features/calendar-cache/calendar-cache.repository.interface.ts (1)

30-32: LGTM! Clean interface addition.

The new method signature is well-designed with clear parameter and return types. The nullable updatedAt field appropriately handles cases where cache entries may not have update timestamps.

packages/trpc/server/routers/viewer/calendars/_router.tsx (1)

28-33: LGTM! Follows established patterns.

The new deleteCache mutation properly uses authedProcedure for authentication, includes appropriate input validation with zod, and follows the consistent dynamic import pattern used elsewhere in the router.

packages/features/calendar-cache/calendar-cache.repository.ts (1)

173-188: LGTM! Efficient implementation.

The method correctly uses Prisma's groupBy with _max aggregation to efficiently retrieve the latest cache update timestamp for each credential. The in operator appropriately handles the array of credential IDs, and the result mapping matches the interface contract.

packages/trpc/server/routers/viewer/calendars/deleteCache.handler.ts (2)

17-26: Security validation implemented correctly.

The credential ownership verification is essential and properly implemented. The handler correctly ensures that only the authenticated user can delete their own credential's cache data.


28-30: Cache deletion is appropriate.

The deleteMany operation correctly removes all cache entries associated with the credential. This is the expected behavior when a user wants to clear their calendar cache.

packages/lib/getConnectedDestinationCalendars.ts (1)

21-28: LGTM! Consistent type updates.

The addition of "updatedAt" and "googleChannelId" to both selected calendar arrays is consistent and supports the new cache status tracking functionality. The type changes align well with the broader calendar cache management features being introduced.

packages/trpc/server/routers/viewer/calendars/connectedCalendars.handler.ts (2)

1-1: LGTM!

The import of CalendarCacheRepository is correctly structured and follows standard module import conventions.


39-39: LGTM!

The return statement correctly uses the enriched connected calendars data, ensuring cache timestamps are available to the frontend.

packages/platform/atoms/selected-calendars/wrappers/SelectedCalendarsSettingsWebWrapper.tsx (4)

5-5: LGTM!

The import change from DisconnectIntegration to CredentialActionsDropdown correctly reflects the component upgrade.


70-79: Excellent implementation of the new dropdown component.

The CredentialActionsDropdown integration is well-executed:

  • Properly wrapped in a flex container for consistent styling
  • All necessary props are provided including cache timestamp
  • Maintains existing functionality while adding cache management capabilities
  • Clean and readable implementation

98-98: Good defensive programming practice.

Explicitly providing || null ensures a clear API contract and prevents undefined values from being passed to the component.


123-132: Consistent dropdown implementation in error scenarios.

The CredentialActionsDropdown usage in the alert matches the main implementation perfectly, providing users with consistent management options regardless of calendar status.

packages/features/apps/components/CredentialActionsDropdown.tsx (7)

1-18: Well-organized imports.

The imports are comprehensive and appropriately sourced. Good use of destructuring for dropdown components and clear separation of concerns.


20-27: Well-designed props interface.

The interface clearly distinguishes required from optional props and uses appropriate TypeScript types. The naming is descriptive and covers all necessary use cases.


29-40: Clean component setup.

The function signature with proper destructuring and state management using hooks follows React best practices. State variables are appropriately named and scoped.


42-50: Well-implemented cache deletion mutation.

The TRPC mutation properly handles both success and error cases with appropriate user feedback via toast notifications. The integration with the success callback is clean.


52-65: Excellent disconnect mutation with proper cache management.

The mutation implementation includes crucial cache invalidation in the onSettled hook, ensuring UI consistency after credential removal. The invalidation of both connectedCalendars and apps.integrations queries is thorough.


67-73: Sound conditional rendering logic.

The conditions appropriately check integration type, permissions, and cache availability. The early return pattern prevents unnecessary rendering when no actions are available.


130-155: Excellent implementation of confirmation dialogs.

Both dialogs follow consistent patterns with:

  • Appropriate danger styling for destructive actions
  • Clear confirmation messaging
  • Proper mutation parameter passing
  • Good modal state management

This provides essential user protection against accidental data loss.

Comment thread apps/web/package.json
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next",
"dev": "yarn copy-static && next dev --turbopack",
"dev:cron": "ts-node cron-tester.ts",
"dev:cron": "npx tsx cron-tester.ts",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Include tsx as a pinned devDependency and clean up ts-node

npx tsx cron-tester.ts implicitly downloads the latest tsx on demand.
Pinning tsx in devDependencies makes CI runs deterministic, speeds up local
invocations, and allows lock-file auditing. If no other script still requires
ts-node, you can drop that package to reduce install size.

Example patch:

@@
-    "ts-node": "^10.9.1",
+    "tsx": "^4.7.0"

Verify whether any remaining scripts call ts-node; if none do, remove it.


🏁 Script executed:

#!/bin/bash
set -e

echo "=== apps/web/package.json: devDependencies ==="
jq '{devDependencies}' apps/web/package.json || true
echo

echo "=== apps/web/package.json: scripts ==="
jq '{scripts}' apps/web/package.json || true
echo

echo "=== Searching entire repo for ts-node usage ==="
rg --no-heading --line-number --color never 'ts-node' --glob '!node_modules/*' .
echo

echo "=== Searching entire repo for tsx usage ==="
rg --no-heading --line-number --color never 'tsx' --glob '!node_modules/*' .

Length of output: 294982


Include and Pin tsx, Don’t Remove ts-node Yet

  • Add tsx to apps/web/package.json devDependencies (pin to a specific version for stability):
     "devDependencies": {
  • "ts-node": "^10.9.1",
  • "ts-node": "^10.9.1",
  • "tsx": "^4.7.0"
    }
- Update the cron script to use the installed binary instead of `npx`:
```diff
 "scripts": {
-    "dev:cron": "npx tsx cron-tester.ts",
+    "dev:cron": "tsx cron-tester.ts",
   …
 }
  • Leave ts-node in place for now—it's still used by:
    • yarn check-changed-files (ts-node scripts/ts-check-changed-files.ts)
    • yarn translate-locales (ts-node scripts/check-missing-translations.ts)
      Migrate these to tsx when you’re ready, then remove ts-node.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"dev:cron": "npx tsx cron-tester.ts",
"devDependencies": {
"ts-node": "^10.9.1",
"tsx": "^4.7.0"
},
"scripts": {
"dev:cron": "tsx cron-tester.ts",
}
🤖 Prompt for AI Agents
In apps/web/package.json at line 11, add tsx as a pinned devDependency with a
specific version for stability. Update the "dev:cron" script to use the locally
installed tsx binary instead of npx. Do not remove ts-node yet, as it is still
required by other scripts; keep it until those scripts are migrated to tsx.

Comment on lines +27 to +36
const credentialIds = connectedCalendars.map((cal) => cal.credentialId);
const cacheRepository = new CalendarCacheRepository();
const cacheStatuses = await cacheRepository.getCacheStatusByCredentialIds(credentialIds);

const cacheStatusMap = new Map(cacheStatuses.map((cache) => [cache.credentialId, cache.updatedAt]));

const enrichedConnectedCalendars = connectedCalendars.map((calendar) => ({
...calendar,
cacheUpdatedAt: cacheStatusMap.get(calendar.credentialId) || null,
}));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Well-structured cache enrichment logic.

The implementation correctly:

  • Extracts credential IDs from connected calendars
  • Uses repository pattern for data access
  • Creates an efficient lookup map for cache statuses
  • Enriches data without mutating original objects
  • Handles missing cache entries gracefully with || null

Consider adding error handling for the cache repository call to ensure robustness:


🏁 Script executed:

#!/bin/bash
# Verify error handling patterns in similar repository calls
ast-grep --pattern 'await $_.getCacheStatusByCredentialIds($_)'

Length of output: 236


Add robust error handling around cache status fetching

While the cache enrichment logic is solid, it’s safer to guard against failures and edge cases in getCacheStatusByCredentialIds. Specifically:

  • Early-exit when there are no credential IDs:
    const credentialIds = connectedCalendars.map(c => c.credentialId);
    if (credentialIds.length === 0) {
      return connectedCalendars.map(c => ({ ...c, cacheUpdatedAt: null }));
    }
  • Wrap the repository call in a try/catch to prevent unhandled rejections:
    let cacheStatuses;
    try {
      cacheStatuses = await cacheRepository.getCacheStatusByCredentialIds(credentialIds);
    } catch (error) {
      // Replace with your logging mechanism
      console.error('Failed to fetch cache statuses', error);
      cacheStatuses = [];
    }

Then build the lookup map and enrich as before.

File to update:

  • packages/trpc/server/routers/viewer/calendars/connectedCalendars.handler.ts (lines 27–36)
🤖 Prompt for AI Agents
In packages/trpc/server/routers/viewer/calendars/connectedCalendars.handler.ts
around lines 27 to 36, add an early return that maps connectedCalendars to
include cacheUpdatedAt: null if credentialIds is empty. Then wrap the call to
getCacheStatusByCredentialIds in a try/catch block to handle potential errors
gracefully by logging the error and setting cacheStatuses to an empty array.
Keep the rest of the enrichment logic unchanged after these additions.

@github-actions

Copy link
Copy Markdown
Contributor

This PR is being marked as stale due to inactivity.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants