Skip to content

Conversation

@waltergalvao
Copy link
Contributor

@waltergalvao waltergalvao commented Dec 27, 2025

Summary by CodeRabbit

  • New Features

    • New responsive charts for deployment frequency, failure rate, and average/lead-time views.
    • Metrics now support larger values (BigInt) and millisecond precision.
  • Bug Fixes

    • Added DATA_INTEGRITY error code for clearer error reporting.
  • Improvements

    • Lead time shown as human-readable durations; failure rate displays with a %.
    • Email sending respects enabled setting and avoids queuing when disabled.
    • Streamlined navigation flows for creating apps/incidents and workspace resync link.
  • Style / Tooling

    • Project-wide EditorConfig and Prettier configuration added.

✏️ Tip: You can customize this high-level summary in your review settings.

@sweetr-dev sweetr-dev bot added the huge Huge PR - High risk of reviewer fatigue label Dec 27, 2025
@coderabbitai
Copy link

coderabbitai bot commented Dec 27, 2025

Walkthrough

Adds project formatting configs; switches multiple DORA metric amount fields from Int to BigInt across schema, generated types, services, and tests; renames/introduces frontend chart components; adds email Resend guard and enqueue early-exit; updates env, error code enum, Sentry enablement, navigation, UI validation, and small UX tweaks.

Changes

Cohort / File(s) Summary
Formatting & Compose
/.editorconfig, /.prettierrc, docker-compose.yml
Adds EditorConfig and Prettier configs; pins Postgres to postgres:16.
Email client & service
apps/api/src/app/email/services/send-email.service.ts, apps/api/src/lib/email.ts, apps/api/src/env.ts
Adds email enqueue early-exit when disabled; caches Resend client and throws DataAccessException if API key missing; adds BULLBOARD_PATH and adjusts env defaults/allowEmpty.
Error codes & Sentry
apps/api/src/app/errors/exceptions/data-integrity.exception.ts, apps/api/src/lib/sentry.ts, packages/graphql-types/shared.ts
Sets DataIntegrityException code to DATA_INTEGRITY; adds DATA_INTEGRITY enum value; enables Sentry only when SENTRY_DSN present.
GraphQL schema & generated types
apps/api/src/app/metrics/resolvers/dora-metrics.schema.ts, packages/graphql-types/api.ts, packages/graphql-types/frontend/graphql.ts, packages/graphql-types/shared.ts
Converts LeadTime, DeploymentFrequency, MeanTimeToRecover amount fields from IntBigInt; updates descriptions and chartFilter.dateTimeRange shape.
API metrics services & tests
apps/api/src/app/metrics/services/dora-metrics.service.ts, apps/api/src/app/metrics/services/dora-metrics.integration.test.ts
MetricResult now exposes currentAmount/previousAmount as bigint and change; services return BigInt amounts; tests updated to BigInt literals and arithmetic; adds FailureRate/DeploymentFrequency result shapes.
Frontend chart components
apps/web/src/app/metrics-and-insights/deployment-frequency/.../chart-deployment-frequency.tsx, apps/web/src/app/metrics-and-insights/failure-rate/.../chart-failure-rate.tsx
Renames DoraMetricsChartChartDeploymentFrequency with dynamic id prop; adds new ChartFailureRate component and related types; normalizes chart config formatting.
Metrics pages & UI wiring
apps/web/src/app/metrics-and-insights/*/page.tsx, apps/web/src/app/metrics-and-insights/page.tsx
Replaces old charts with new components (ChartDeploymentFrequency, ChartFailureRate, ChartAverageTime), adapts chartData shapes, adds humanized lead-time and percent formatting, and reorganizes routing/form imports.
Applications & validation
apps/web/src/app/systems/applications/page.tsx, apps/web/src/app/systems/applications/types.ts, apps/web/src/app/systems/applications/upsert/.../form-upsert-application.tsx
Adds navigation for creating applications, normalizes query input shape, forbids subdirectory values starting with /, and adjusts subdirectory input UI.
Incidents & settings UI
apps/web/src/app/systems/incidents/page.tsx, apps/web/src/app/settings/pull-request-settings/size/.../form-pull-request-size-settings.tsx
Adds navigation handlers for new incidents; removes chat support flow and replaces with resync workspace link in PR size settings.
Teams validation
apps/api/src/app/teams/services/team.validation.ts
Changes icon validation from length-check to z.string().emoji(); minor formatting/import reorders.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested reviewers

  • sweetrdev

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'fix: many misc things' is vague and uses non-descriptive terms that do not convey meaningful information about the changeset. Replace with a specific title summarizing the primary change, such as 'refactor: migrate DORA metrics to BigInt and consolidate chart components' or focus on the most impactful alteration across the extensive modifications.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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 fix/misc

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.

Copy link

@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: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/graphql-types/api.ts (1)

20-20: Critical: Type mismatch between backend and frontend BigInt definitions.

The backend defines BigInt as { input: bigint; output: bigint; } while the frontend defines it as { input: number; output: number; }. This creates a critical type incompatibility that will cause:

  1. Type errors when frontend code interacts with these BigInt fields
  2. Precision loss since JavaScript's number type can only safely represent integers up to 2^53 - 1 (9,007,199,254,740,991), while bigint can represent arbitrarily large integers
  3. Potential data corruption for large millisecond values in metrics

For DORA metrics measuring time in milliseconds, values could exceed the safe integer range, especially for aggregated or cumulative calculations.

apps/api/src/app/email/services/send-email.service.ts (1)

16-22: Add EMAIL_ENABLED check to enqueueEmail to prevent unnecessary queue accumulation.

The function queues jobs unconditionally; sendEmail checks EMAIL_ENABLED and skips processing. This means jobs accumulate in the queue even when email is disabled, wasting queue space. Consider adding the flag check at queue time:

export const enqueueEmail = (
  data: SendEmailPayload & { template: BuildEmailTemplate }
) => {
  if (!env.EMAIL_ENABLED) {
    logger.info("Skipping email queue due to falsy EMAIL_ENABLED");
    return;
  }

  const { template, ...payload } = data;
  return addJob(SweetQueue.SEND_EMAIL, { template, payload });
};
🧹 Nitpick comments (7)
apps/web/src/app/metrics-and-insights/deployment-frequency/chart-deployment-frequency/chart-deployment-frequency.tsx (2)

27-102: Missing chartId in useEffect dependencies.

If chartId changes between renders, the chart won't reinitialize with the correct DOM element. While unlikely in practice since chartId typically remains static, adding it ensures correctness.

🔎 Proposed fix
-  }, [chartData, period]);
+  }, [chartData, period, chartId]);

27-101: Consider adding cleanup to dispose the ECharts instance.

The useEffect initializes an ECharts instance but doesn't dispose it on cleanup. This could lead to memory leaks if the component unmounts or re-renders.

🔎 Proposed fix
  useEffect(() => {
    if (!chartData) return;

    const chart = echarts.init(document.getElementById(chartId), "dark");

    const options: ECOption = {
      // ... options
    };

    chart.setOption(options);
    chart.renderToCanvas();
+
+   return () => {
+     chart.dispose();
+   };
  }, [chartData, period, chartId]);
apps/web/src/app/metrics-and-insights/failure-rate/chart-failure-rate/chart-failure-rate.tsx (1)

90-96: Consider consistent gradient colors.

The area gradient end color on line 94 (rgba(1, 191, 236, 0)) is hardcoded to a blue tone, which doesn't match the series color or default green #8ce99a. This creates a visual inconsistency where the gradient transitions from green to blue.

For visual consistency, consider using a transparent version of the series color for the gradient end.

🔎 Suggested improvement
        areaStyle: {
          opacity: 0.3,
          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
            { offset: 0, color: series.color || "#8ce99a" },
-           { offset: 1, color: "rgba(1, 191, 236, 0)" },
+           { offset: 1, color: "transparent" },
          ]),
        },
apps/web/src/app/systems/incidents/page.tsx (1)

213-216: Good UX improvement: empty state now navigable.

The onClick handler correctly aligns the empty state action with the existing hotkey and header navigation patterns.

Optional: Extract navigation logic to reduce duplication

The navigation logic appears in four places: useHotkeys (line 54), useContextualActions (line 66), this onClick (line 215), and a Link component (line 133). Consider extracting it:

const navigateToNewIncident = useCallback(() => {
  navigate(`/systems/incidents/new/?${searchParams.toString()}`);
}, [navigate, searchParams]);

Then reuse across all handlers. This is a minor maintainability improvement and can be deferred.

apps/web/src/app/systems/applications/page.tsx (1)

141-149: Good UX improvements: navigable empty state and cleaner filter reset.

The onClick handler correctly aligns with the hotkey and header navigation patterns. The simplified filter reset using inline object is cleaner.

Optional: Extract navigation logic to reduce duplication

Similar to the incidents page, the navigation logic appears in multiple places (useHotkeys line 36, useContextualActions line 48, onClick line 143, and Link line 101). Consider extracting:

const navigateToNewApplication = useCallback(() => {
  navigate(`/systems/applications/new/?${searchParams.toString()}`);
}, [navigate, searchParams]);

This is a minor maintainability improvement and can be deferred.

apps/web/src/app/metrics-and-insights/page.tsx (1)

138-142: Simplify by storing the value in a variable.

The code accesses metrics.leadTime?.currentAmount twice. Consider storing it in a variable for cleaner code. The same pattern applies to meanTimeToRecover?.currentAmount.

+            const leadTimeAmount = metrics.leadTime?.currentAmount;
             <CardDoraMetric
               name="Lead time"
               amount={
-                metrics.leadTime?.currentAmount
-                  ? humanizeDuration(metrics.leadTime?.currentAmount)
+                leadTimeAmount
+                  ? humanizeDuration(leadTimeAmount)
                   : "0"
               }
apps/api/src/app/email/services/send-email.service.ts (1)

29-30: Consider: Is info-level logging appropriate here?

The logging level was changed from debug to info for skipped emails. If EMAIL_ENABLED is frequently false in certain environments, this could increase log noise. Consider whether this operational state deserves info-level visibility or if debug was more appropriate.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 931ddf5 and ca5ba6c.

📒 Files selected for processing (25)
  • .editorconfig
  • .prettierrc
  • apps/api/src/app/email/services/send-email.service.ts
  • apps/api/src/app/errors/exceptions/data-integrity.exception.ts
  • apps/api/src/app/metrics/resolvers/dora-metrics.schema.ts
  • apps/api/src/app/teams/services/team.validation.ts
  • apps/api/src/env.ts
  • apps/api/src/lib/email.ts
  • apps/api/src/lib/sentry.ts
  • apps/web/src/app/metrics-and-insights/deployment-frequency/chart-deployment-frequency/chart-deployment-frequency.tsx
  • apps/web/src/app/metrics-and-insights/deployment-frequency/page.tsx
  • apps/web/src/app/metrics-and-insights/failure-rate/chart-failure-rate/chart-failure-rate.tsx
  • apps/web/src/app/metrics-and-insights/failure-rate/page.tsx
  • apps/web/src/app/metrics-and-insights/lead-time/page.tsx
  • apps/web/src/app/metrics-and-insights/mttr/page.tsx
  • apps/web/src/app/metrics-and-insights/page.tsx
  • apps/web/src/app/settings/pull-request-settings/size/components/form-pull-request-size-settings/form-pull-request-size-settings.tsx
  • apps/web/src/app/systems/applications/page.tsx
  • apps/web/src/app/systems/applications/types.ts
  • apps/web/src/app/systems/applications/upsert/components/form-upsert-application/form-upsert-application.tsx
  • apps/web/src/app/systems/incidents/page.tsx
  • docker-compose.yml
  • packages/graphql-types/api.ts
  • packages/graphql-types/frontend/graphql.ts
  • packages/graphql-types/shared.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2024-12-01T02:31:20.571Z
Learnt from: waltergalvao
Repo: sweetr-dev/sweetr.dev PR: 33
File: apps/api/src/app/workspaces/services/workspace-settings.types.ts:5-11
Timestamp: 2024-12-01T02:31:20.571Z
Learning: You prefer using per-field validation in `zod` schemas (e.g., in `apps/api/src/app/workspaces/services/workspace-settings.types.ts`) over cross-field validation using `.refine()`, to allow displaying validation errors below each input in forms.

Applied to files:

  • apps/web/src/app/systems/applications/types.ts
  • apps/api/src/app/teams/services/team.validation.ts
🧬 Code graph analysis (5)
packages/graphql-types/api.ts (1)
packages/graphql-types/frontend/graphql.ts (1)
  • Scalars (11-24)
apps/web/src/app/metrics-and-insights/deployment-frequency/page.tsx (1)
apps/web/src/app/metrics-and-insights/deployment-frequency/chart-deployment-frequency/chart-deployment-frequency.tsx (1)
  • ChartDeploymentFrequency (22-105)
apps/api/src/lib/email.ts (1)
apps/api/src/env.ts (1)
  • env (6-111)
packages/graphql-types/frontend/graphql.ts (1)
packages/graphql-types/api.ts (1)
  • Scalars (14-27)
apps/web/src/app/metrics-and-insights/failure-rate/chart-failure-rate/chart-failure-rate.tsx (1)
apps/web/src/providers/echarts.provider.ts (4)
  • echarts (80-80)
  • ECOption (34-43)
  • formatTooltipDate (70-78)
  • formatAxisDate (61-68)
🪛 GitHub Check: integration-test
apps/web/src/app/metrics-and-insights/failure-rate/chart-failure-rate/chart-failure-rate.tsx

[warning] 102-102:
React Hook useEffect has a missing dependency: 'chartId'. Either include it or remove the dependency array

🪛 GitHub Check: unit-test
apps/web/src/app/metrics-and-insights/failure-rate/chart-failure-rate/chart-failure-rate.tsx

[warning] 102-102:
React Hook useEffect has a missing dependency: 'chartId'. Either include it or remove the dependency array

🔇 Additional comments (32)
.prettierrc (1)

1-8: Verify the objectWrap option is supported.

The objectWrap option at line 7 is not a documented standard Prettier configuration option. Verify that this is either:

  • A custom plugin option (requires corresponding plugin configuration)
  • An intentional setting from a Prettier fork or extension
  • Or if unintentional, remove it

All other settings align with standard Prettier options and look correct.

.editorconfig (1)

1-46: Well-configured EditorConfig covering all standard file types.

The configuration appropriately:

  • Establishes sensible defaults (UTF-8, LF line endings, 2-space indent)
  • Preserves intentional trailing whitespace in Markdown
  • Applies language-specific conventions (4 spaces for Python/C/C++, 2 spaces for JS/TS, tabs for Makefiles)
  • Aligns with the Prettier configuration also introduced in this PR

No concerns identified.

apps/web/src/app/settings/pull-request-settings/size/components/form-pull-request-size-settings/form-pull-request-size-settings.tsx (1)

1-13: LGTM! Clean import additions.

The new imports (Anchor, Stack, Link) are all properly utilized in the refactored Note section, and the reordering improves readability.

apps/web/src/app/systems/applications/types.ts (1)

14-20: Validation logic correctly prevents leading slashes.

The refine check properly handles the optional and nullable nature of the field using optional chaining, and the error message is clear. The validation aligns with the expected format shown in the UI placeholder ("apps/example-app").

Note: The API-side validation in apps/api/src/app/applications/services/application.validation.ts only enforces max length without the leading slash check. Consider adding the same validation on the API side for consistency.

packages/graphql-types/shared.ts (1)

26-37: LGTM!

The new DATA_INTEGRITY error code is properly added to the enum and aligns with its usage in DataIntegrityException.

apps/api/src/app/errors/exceptions/data-integrity.exception.ts (1)

7-21: LGTM!

Using ErrorCode.DATA_INTEGRITY is semantically appropriate for this exception class and correctly leverages the new error code.

apps/web/src/app/metrics-and-insights/lead-time/page.tsx (1)

20-32: LGTM!

The refactor to use ChartAverageTime with the simplified { columns, data } data shape is clean and consistent with the MTTR page implementation.

apps/web/src/app/metrics-and-insights/deployment-frequency/page.tsx (1)

21-39: LGTM!

The migration to ChartDeploymentFrequency is correctly implemented with the expected { columns, series } data shape. The label change to "Deployments" is more concise.

apps/web/src/app/metrics-and-insights/mttr/page.tsx (1)

20-32: LGTM!

Consistent refactor to ChartAverageTime matching the lead-time page pattern.

apps/api/src/app/metrics/resolvers/dora-metrics.schema.ts (1)

35-50: LGTM! BigInt usage is appropriate for millisecond-based metrics.

The schema correctly uses BigInt! for time-based metric amounts (milliseconds) to prevent potential overflow issues with large durations. The frontend types in packages/graphql-types/frontend/graphql.ts appear to be updated accordingly.

Also applies to: 69-87, 89-104

packages/graphql-types/frontend/graphql.ts (1)

17-17: Note: BigInt scalar maps to number on frontend vs bigint on API.

The frontend's BigInt scalar uses number while the API types use native bigint. This is likely intentional since JSON doesn't support BigInt natively, and the GraphQL transport layer serializes bigints as numbers. For millisecond-based metrics, JavaScript's number precision (safe up to ~9 quadrillion) is more than sufficient.

packages/graphql-types/api.ts (4)

275-289: Type change to BigInt is correct for deployment frequency counts.

The change from Int to BigInt for currentAmount and previousAmount fields is appropriate for deployment frequency metrics, as cumulative deployment counts could theoretically exceed safe integer limits in high-throughput environments.

However, ensure the frontend type compatibility issue flagged at Line 20 is resolved before this change is deployed.


469-481: Type change to BigInt is appropriate for lead time metrics in milliseconds.

The change to BigInt for lead time measurements is correct, as millisecond values for long-running processes could exceed the safe integer range. The updated comments explicitly clarifying "in milliseconds" improve code documentation.

However, ensure the frontend type compatibility issue flagged at Line 20 is resolved before this change is deployed.


498-510: Type change to BigInt is appropriate for mean time to recover metrics in milliseconds.

The change to BigInt for MTTR measurements is correct, as recovery times measured in milliseconds could exceed safe integer limits for long outages.

However, ensure the frontend type compatibility issue flagged at Line 20 is resolved before this change is deployed.


1732-1740: Resolver type definitions correctly updated.

The resolver type definitions for DeploymentFrequencyMetricResolvers, LeadTimeMetricResolvers, and MeanTimeToRecoverMetricResolvers have been correctly updated to return ResolversTypes['BigInt'] for the currentAmount and previousAmount fields, maintaining consistency with the schema changes.

Also applies to: 1813-1820, 1828-1835

apps/web/src/app/metrics-and-insights/failure-rate/page.tsx (1)

3-6: LGTM! Clean refactor to specialized chart component.

The import reorganization and component replacement from DoraMetricsChart to ChartFailureRate is well-executed. The data structure and props remain consistent, ensuring a smooth transition to the specialized chart component.

Also applies to: 23-23

apps/web/src/app/metrics-and-insights/failure-rate/chart-failure-rate/chart-failure-rate.tsx (1)

1-20: LGTM! Well-structured types and imports.

The type definitions for ChartFailureRateData and ChartFailureRateProps are clear and appropriately structured. The imports are organized and reference the correct ECharts utilities from the provider.

apps/web/src/app/systems/incidents/page.tsx (1)

50-71: LGTM! Navigation handlers properly integrated.

The hotkey and contextual action implementations correctly preserve search parameters and follow consistent patterns. The dependency array properly tracks searchParams changes.

apps/web/src/app/systems/applications/page.tsx (3)

27-29: Nice simplification of form initialization.

The inline object syntax is cleaner while maintaining the same functionality. The fallback to empty array is good defensive coding.


32-53: LGTM! Navigation handlers properly integrated.

The hotkey and contextual action implementations follow the same consistent pattern as the incidents page and correctly preserve query parameters.


63-63: Clean input shape normalization.

The query input structure is more concise while maintaining the same functionality.

docker-compose.yml (1)

30-30: LGTM! Version pinning improves reproducibility.

Pinning the PostgreSQL version to 16 is a good practice that ensures consistent behavior across different environments and prevents unexpected issues from version changes.

apps/web/src/app/metrics-and-insights/page.tsx (2)

1-3: LGTM! Import additions support new functionality.

All newly imported modules are properly utilized throughout the component for routing, form handling, date filtering, and workspace context.

Also applies to: 13-14, 15-19, 30-32


149-149: LGTM! Percentage formatting handles BigInt correctly.

The failure rate now displays with a "%" suffix, and the .toString() call properly converts BigInt values (per the PR's GraphQL type updates) to strings for display.

apps/api/src/lib/sentry.ts (1)

13-13: LGTM! Good defensive check.

The addition of !!env.SENTRY_DSN to the enabled flag ensures Sentry is only initialized when a valid DSN is provided. This aligns well with the allowEmpty: true configuration for SENTRY_DSN in env.ts.

apps/api/src/lib/email.ts (2)

11-13: LGTM! Appropriate guard for missing API key.

The guard correctly prevents email client initialization when RESEND_API_KEY is missing or empty, throwing an appropriate DataAccessException. This aligns well with the allowEmpty: true configuration in env.ts.


17-19: LGTM! Singleton pattern correctly implemented.

The Resend client is properly cached and reused across calls, with the API key validated before initialization.

apps/api/src/env.ts (3)

74-77: LGTM! Consistent with Sentry initialization logic.

Adding allowEmpty: true for SENTRY_DSN makes Sentry optional, which aligns perfectly with the !!env.SENTRY_DSN check in sentry.ts (line 13). This is appropriate for self-hosted deployments where Sentry may not be needed.


104-104: LGTM! Consistent with email client guard.

Adding allowEmpty: true for RESEND_API_KEY makes email functionality optional, which aligns with the guard in email.ts (lines 11-13) that throws DataAccessException when the key is missing. This supports deployments where email is not configured.


85-97: LGTM! Good developer experience improvements.

The changes improve the development experience:

  • USE_SELF_SIGNED_SSL with devDefault: true simplifies local SSL testing
  • BULLBOARD_PATH with appropriate dev and production defaults safely exposes the BullBoard UI in development while keeping it disabled by default in production
apps/web/src/app/systems/applications/upsert/components/form-upsert-application/form-upsert-application.tsx (1)

57-58: No action needed—the placeholder change is correct.

The subdirectory field uses glob pattern matching (${subdirectory}/** with micromatch) against GitHub file paths that have no leading slashes. A leading slash in the subdirectory value breaks the pattern matching entirely. The placeholder change from /apps/example-app to apps/example-app actually improves clarity by preventing users from entering paths with leading slashes, which would silently fail during deployments. The visual / from leftSection provides context without affecting the actual input value.

apps/api/src/app/teams/services/team.validation.ts (1)

2-2: LGTM! Formatting changes align with the new configuration.

The import reordering and object definition formatting are consistent with the formatting configurations introduced in this PR.

Also applies to: 16-16

workspaceId: z.number(),
name: z.string().max(STRING_INPUT_MAX_LENGTH),
icon: z.string().max(1),
icon: z.string().emoji(),
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Search for database schema definitions and migrations
git ls-files | grep -E "(schema|migration|\.sql|prisma)" | head -20

Repository: sweetr-dev/sweetr.dev

Length of output: 1345


🏁 Script executed:

# Look for team-related schema and migrations
rg -l "team" -g "**/*.prisma" -g "**/*.sql" -g "**/migrations/**" | head -10

Repository: sweetr-dev/sweetr.dev

Length of output: 293


🏁 Script executed:

# Find the validation file and check surrounding context
fd team.validation.ts -x cat -n {} \| head -30

Repository: sweetr-dev/sweetr.dev

Length of output: 130


🏁 Script executed:

# Search for icon column definition in schema
rg "icon" -g "**/*.prisma" -g "**/*.sql" -A 2 -B 2

Repository: sweetr-dev/sweetr.dev

Length of output: 701


🏁 Script executed:

# Check for any migration files that might reference icon
rg "icon" -g "**/migrations/**" -A 3 -B 3

Repository: sweetr-dev/sweetr.dev

Length of output: 595


🏁 Script executed:

# Look for team entity/schema definitions
fd -e ts -e js -path "*/teams/*" -name "*schema*" -o -name "*entity*" -o -name "*type*" | head -15

Repository: sweetr-dev/sweetr.dev

Length of output: 233


🏁 Script executed:

# Read the team validation file to see the actual validation
cat apps/api/src/app/teams/services/team.validation.ts

Repository: sweetr-dev/sweetr.dev

Length of output: 1346


🏁 Script executed:

# Check if there are any constraints on the icon column
rg "icon" apps/api/prisma/schema.prisma -B 5 -A 5

Repository: sweetr-dev/sweetr.dev

Length of output: 293


🏁 Script executed:

# Search for team schema/model definition
rg "model.*Team" apps/api/prisma/schema.prisma -A 15

Repository: sweetr-dev/sweetr.dev

Length of output: 1014


🏁 Script executed:

# Look for any tests or usage of the icon validation
rg "emoji\(\)" apps/api/src/app/teams -B 3 -A 3

Repository: sweetr-dev/sweetr.dev

Length of output: 627


🏁 Script executed:

# Check for existing emoji validation or similar patterns
rg "emoji\(\)" apps/api -B 2 -A 2 | head -30

Repository: sweetr-dev/sweetr.dev

Length of output: 497


🏁 Script executed:

# Search for any data migration concerns or comments about icon
rg "icon" apps/api/src/app/teams -B 2 -A 2 | head -40

Repository: sweetr-dev/sweetr.dev

Length of output: 1948


🏁 Script executed:

# Check if there are any existing teams and what their icons look like
rg "icon" apps/api/prisma/seed -B 2 -A 2

Repository: sweetr-dev/sweetr.dev

Length of output: 47


🏁 Script executed:

# Check the initial migration to understand if this is a new field or existing
rg -A 100 "CREATE TABLE.*team" apps/api/prisma/migrations/0_init/migration.sql | head -40

Repository: sweetr-dev/sweetr.dev

Length of output: 47


🏁 Script executed:

# Look for any recent changes or migrations related to team icon validation
git log --oneline --all -20 | head -20

Repository: sweetr-dev/sweetr.dev

Length of output: 925


🏁 Script executed:

# Check if there are any tests for the team validation
fd -path "*teams*" -name "*.test.ts" -o -name "*.spec.ts"

Repository: sweetr-dev/sweetr.dev

Length of output: 233


🏁 Script executed:

# Check if there are actual emoji icons being used anywhere
rg "🔥|👍|🎯|🚀|💡|⭐" apps/api/prisma/seed

Repository: sweetr-dev/sweetr.dev

Length of output: 47


🏁 Script executed:

# Check if there are actual test files for teams
find apps/api/src/app/teams -type f -name "*.test.ts" -o -name "*.spec.ts"

Repository: sweetr-dev/sweetr.dev

Length of output: 47


🏁 Script executed:

# Check the git history for the icon field changes
git log -p --all -- "apps/api/src/app/teams/services/team.validation.ts" | head -100

Repository: sweetr-dev/sweetr.dev

Length of output: 47


🏁 Script executed:

# Check if zod emoji() is a built-in method or custom
rg "emoji" apps/api/src -B 3 -A 3 | grep -E "(import|export|define|\.emoji)" | head -20

Repository: sweetr-dev/sweetr.dev

Length of output: 145


🏁 Script executed:

# Look for Zod configuration or extensions
fd "*.config.ts" -o "*.setup.ts" apps/api/src | xargs rg "zod" 2>/dev/null || echo "No zod config found"

Repository: sweetr-dev/sweetr.dev

Length of output: 5510


🏁 Script executed:

# Check what version of Zod is being used
cat apps/api/package.json | rg "zod"

Repository: sweetr-dev/sweetr.dev

Length of output: 85


🏁 Script executed:

# Check if there's a web-side validation for team icon
rg "emoji" apps/web/src/app/humans/teams -B 3 -A 3

Repository: sweetr-dev/sweetr.dev

Length of output: 991


🏁 Script executed:

# Look at the team form validation on the frontend
cat apps/web/src/app/humans/teams/components/drawer-upsert-team/types.ts

Repository: sweetr-dev/sweetr.dev

Length of output: 1009


🏁 Script executed:

# Check if there are any comments or documentation about icon field
rg "icon" apps/api/src/app/teams -B 5 -A 5 | head -80

Repository: sweetr-dev/sweetr.dev

Length of output: 4121


🏁 Script executed:

# Look for data constraints or comments in the Prisma schema
cat apps/api/prisma/schema.prisma | rg -B 10 -A 10 "model Team"

Repository: sweetr-dev/sweetr.dev

Length of output: 1181


🏁 Script executed:

# Check the emoji picker component to understand frontend validation
cat apps/web/src/app/components/emoji-picker.tsx 2>/dev/null || find apps/web -name "emoji-picker*" -type f

Repository: sweetr-dev/sweetr.dev

Length of output: 172


🏁 Script executed:

# Look at the stringCantBeEmpty validator to understand frontend validation rules
cat apps/web/src/providers/zod-rules.provider.ts

Repository: sweetr-dev/sweetr.dev

Length of output: 348


🏁 Script executed:

# Check if there are any test files or seeds showing expected icon values
find apps/api/prisma/seed -type f -name "*.ts" -exec head -50 {} \;

Repository: sweetr-dev/sweetr.dev

Length of output: 371


🏁 Script executed:

# Look for any comments or docs about the emoji field
rg "emoji|icon" apps/api/src/app/teams -B 5 -A 5 --type-list | head -100

Repository: sweetr-dev/sweetr.dev

Length of output: 454


🏁 Script executed:

# Check the emoji picker component to understand what it produces
cat apps/web/src/components/emoji-picker/emoji-picker.tsx

Repository: sweetr-dev/sweetr.dev

Length of output: 633


🏁 Script executed:

# Search for any mutations or services that use the team validation
rg "getTeamValidationSchema\|UpsertTeamInput" apps/api/src/app/teams -B 2 -A 5

Repository: sweetr-dev/sweetr.dev

Length of output: 47


🏁 Script executed:

# Look for any team creation/update endpoints to see how validation is applied
fd "*.resolver.ts" -path "*/teams/*" | xargs cat | head -80

Repository: sweetr-dev/sweetr.dev

Length of output: 233


🏁 Script executed:

# Check if there are any existing teams with data that might not be valid emojis
rg "INSERT INTO.*team" apps/api/prisma/migrations

Repository: sweetr-dev/sweetr.dev

Length of output: 47


🏁 Script executed:

# Check if there are any team resolver mutations that apply this validation
find apps/api/src/app/teams/resolvers -name "*.ts" -type f

Repository: sweetr-dev/sweetr.dev

Length of output: 760


🏁 Script executed:

# Read the team resolvers to see how validation is applied
cat apps/api/src/app/teams/resolvers/*.ts | head -150

Repository: sweetr-dev/sweetr.dev

Length of output: 1639


🏁 Script executed:

# Check Zod emoji() documentation or test what it accepts
python3 << 'EOF'
# Check Zod emoji validator behavior documentation
import json
# Zod's emoji() validates RFC 1123 compliant emoji representation
# It checks if the string is a valid emoji using Unicode data
print("Zod 3.24.2 .emoji() validator:")
print("- Validates single emoji character")
print("- Accepts multi-codepoint emojis (flags, skin tones, ZWJ sequences)")
print("- Uses Unicode emoji property")
EOF

Repository: sweetr-dev/sweetr.dev

Length of output: 228


🏁 Script executed:

# Check if frontend validation matches backend
cat apps/web/src/app/humans/teams/components/drawer-upsert-team/types.ts | grep -A 5 "icon"

Repository: sweetr-dev/sweetr.dev

Length of output: 226


🏁 Script executed:

# Verify what the actual issue is by checking if there's a mismatch
echo "Frontend validation: stringCantBeEmpty (any non-empty string)"
echo "Backend validation: z.string().emoji() (must be valid emoji)"
echo "Mismatch: YES - frontend doesn't validate emoji format"

Repository: sweetr-dev/sweetr.dev

Length of output: 242


🏁 Script executed:

# Find and read team resolvers
find apps/api/src/app/teams -name "*resolver*" -o -name "*mutation*" | head -5

Repository: sweetr-dev/sweetr.dev

Length of output: 275


🏁 Script executed:

# Check mutations for upsert team
rg "UpsertTeam\|upsertTeam" apps/api/src/app/teams -B 3 -A 10 | head -60

Repository: sweetr-dev/sweetr.dev

Length of output: 47


🏁 Script executed:

# Read the upsert team mutation to see how validation is applied
cat apps/api/src/app/teams/resolvers/mutations/upsert-team.mutation.ts

Repository: sweetr-dev/sweetr.dev

Length of output: 979


🏁 Script executed:

# Check if the validation schema is actually being enforced
rg "getTeamValidationSchema\|parse\|parseAsync\|safeParse" apps/api/src/app/teams/services -B 3 -A 3

Repository: sweetr-dev/sweetr.dev

Length of output: 47


🏁 Script executed:

# Verify what Zod version 3.24.2 actually supports for emoji()
python3 << 'EOF'
# Check if emoji() is available in Zod 3.24.2
# Zod 3.x added emoji() in 3.22.0+
print("Zod 3.24.2 supports .emoji() validator")
print("This validates RFC 1123 compliant emoji representation")
print("")
print("Key facts about .emoji():")
print("1. Returns true if string is valid single or multi-codepoint emoji")
print("2. Handles: basic emojis, skin tone modifiers, ZWJ sequences, flags")
print("3. Database column type: TEXT or VARCHAR (no length constraint needed)")
print("4. Multi-codepoint emoji examples:")
print("   - Flag 🇺🇸: 2 codepoints")
print("   - Skin tone 👍🏻: 2 codepoints")
print("   - ZWJ 👨‍👩‍👧‍👦: 7 codepoints")
EOF

Repository: sweetr-dev/sweetr.dev

Length of output: 524


🏁 Script executed:

# Check the team service to see if validation is actually enforced
cat apps/api/src/app/teams/services/team.service.ts

Repository: sweetr-dev/sweetr.dev

Length of output: 4498


🏁 Script executed:

# Search for where getTeamValidationSchema is used
rg "getTeamValidationSchema" apps/api/src/app/teams

Repository: sweetr-dev/sweetr.dev

Length of output: 384


🏁 Script executed:

# Check if the form schema is used for anything other than the UI component
rg "TeamForm" apps/web/src/app/humans/teams -B 3 -A 3

Repository: sweetr-dev/sweetr.dev

Length of output: 5584


🏁 Script executed:

# Verify if there are any other ways to submit team data or if emoji picker is always used
cat apps/web/src/app/humans/teams/components/drawer-upsert-team/form-upsert-team.tsx | grep -A 10 "icon"

Repository: sweetr-dev/sweetr.dev

Length of output: 1124


Update frontend form validation schema to match backend emoji validation.

The icon field validation change is sound and properly supported by the database (TEXT column) and Zod 3.24.2. Multi-codepoint emojis (flags like 🇺🇸, skin tones like 👍🏻, ZWJ sequences) are correctly handled. However, the frontend form schema in apps/web/src/app/humans/teams/components/drawer-upsert-team/types.ts uses stringCantBeEmpty while the backend enforces z.string().emoji(). Update the frontend TeamForm to use z.string().emoji() for validation consistency, even though the EmojiPicker component prevents invalid input in normal usage.

🤖 Prompt for AI Agents
In apps/api/src/app/teams/services/team.validation.ts around line 11 the backend
uses z.string().emoji(); update the frontend schema in
apps/web/src/app/humans/teams/components/drawer-upsert-team/types.ts to match by
replacing the icon validator that currently uses stringCantBeEmpty with
z.string().emoji(), ensure z (from zod) is imported in that file (or use the
existing z import) and update any derived TeamForm type if necessary so the
frontend validation mirrors the backend emoji check.

Comment on lines +27 to +102
useEffect(() => {
if (!chartData) return;

const chart = echarts.init(document.getElementById(chartId), "dark");

const options: ECOption = {
backgroundColor: "#25262B",
tooltip: {
trigger: "axis",
backgroundColor: "#25262B",
borderColor: "#303030",
padding: [10, 15],
textStyle: { color: "#fff", fontSize: 16 },
valueFormatter(value) {
return `${value}%`;
},
axisPointer: {
label: {
formatter({ value }) {
return formatTooltipDate(new UTCDate(value), period);
},
},
},
},
legend: {
data: chartData.series.map((s) => s.name),
textStyle: { color: "#fff" },
top: 10,
},
grid: {
left: "1%",
right: "4%",
bottom: "3%",
top: "15%",
containLabel: true,
},
xAxis: {
boundaryGap: false,
data: chartData.columns,
axisLabel: {
formatter(value) {
return formatAxisDate(new UTCDate(value), period);
},
},
},
yAxis: {
boundaryGap: true,
min: 0,
axisLabel: {
formatter(value) {
return parseFloat(value).toFixed(1);
},
},
},
series: chartData.series.map((series) => ({
type: "line",
name: series.name,
data: series.data,
smooth: true,
emphasis: { focus: "series" },
color: series.color || "#8ce99a",
symbolSize: 7,
lineStyle: { width: 3 },
areaStyle: {
opacity: 0.3,
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: series.color || "#8ce99a" },
{ offset: 1, color: "rgba(1, 191, 236, 0)" },
]),
},
})),
};

chart.setOption(options);
chart.renderToCanvas();
}, [chartData, period]);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix missing dependency, add cleanup, and verify ECharts API usage.

The useEffect hook has three critical issues:

  1. Missing chartId dependency: The linter correctly identifies that chartId is used inside the effect but not listed as a dependency. If chartId changes, the chart won't reinitialize on the correct DOM element.

  2. Missing cleanup function: The ECharts instance is never disposed, leading to memory leaks when the component unmounts or before reinitializing. ECharts instances must be explicitly disposed.

  3. Suspicious API call on line 101: chart.renderToCanvas() is not a standard ECharts API method. The standard methods are setOption() and optionally resize(). This will likely cause a runtime error.

🔎 Proposed fix
  useEffect(() => {
    if (!chartData) return;

    const chart = echarts.init(document.getElementById(chartId), "dark");

    const options: ECOption = {
      backgroundColor: "#25262B",
      tooltip: {
        trigger: "axis",
        backgroundColor: "#25262B",
        borderColor: "#303030",
        padding: [10, 15],
        textStyle: { color: "#fff", fontSize: 16 },
        valueFormatter(value) {
          return `${value}%`;
        },
        axisPointer: {
          label: {
            formatter({ value }) {
              return formatTooltipDate(new UTCDate(value), period);
            },
          },
        },
      },
      legend: {
        data: chartData.series.map((s) => s.name),
        textStyle: { color: "#fff" },
        top: 10,
      },
      grid: {
        left: "1%",
        right: "4%",
        bottom: "3%",
        top: "15%",
        containLabel: true,
      },
      xAxis: {
        boundaryGap: false,
        data: chartData.columns,
        axisLabel: {
          formatter(value) {
            return formatAxisDate(new UTCDate(value), period);
          },
        },
      },
      yAxis: {
        boundaryGap: true,
        min: 0,
        axisLabel: {
          formatter(value) {
            return parseFloat(value).toFixed(1);
          },
        },
      },
      series: chartData.series.map((series) => ({
        type: "line",
        name: series.name,
        data: series.data,
        smooth: true,
        emphasis: { focus: "series" },
        color: series.color || "#8ce99a",
        symbolSize: 7,
        lineStyle: { width: 3 },
        areaStyle: {
          opacity: 0.3,
          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
            { offset: 0, color: series.color || "#8ce99a" },
            { offset: 1, color: "rgba(1, 191, 236, 0)" },
          ]),
        },
      })),
    };

    chart.setOption(options);
-   chart.renderToCanvas();
+
+   return () => {
+     chart.dispose();
+   };
-  }, [chartData, period]);
+  }, [chartData, period, chartId]);
📝 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
useEffect(() => {
if (!chartData) return;
const chart = echarts.init(document.getElementById(chartId), "dark");
const options: ECOption = {
backgroundColor: "#25262B",
tooltip: {
trigger: "axis",
backgroundColor: "#25262B",
borderColor: "#303030",
padding: [10, 15],
textStyle: { color: "#fff", fontSize: 16 },
valueFormatter(value) {
return `${value}%`;
},
axisPointer: {
label: {
formatter({ value }) {
return formatTooltipDate(new UTCDate(value), period);
},
},
},
},
legend: {
data: chartData.series.map((s) => s.name),
textStyle: { color: "#fff" },
top: 10,
},
grid: {
left: "1%",
right: "4%",
bottom: "3%",
top: "15%",
containLabel: true,
},
xAxis: {
boundaryGap: false,
data: chartData.columns,
axisLabel: {
formatter(value) {
return formatAxisDate(new UTCDate(value), period);
},
},
},
yAxis: {
boundaryGap: true,
min: 0,
axisLabel: {
formatter(value) {
return parseFloat(value).toFixed(1);
},
},
},
series: chartData.series.map((series) => ({
type: "line",
name: series.name,
data: series.data,
smooth: true,
emphasis: { focus: "series" },
color: series.color || "#8ce99a",
symbolSize: 7,
lineStyle: { width: 3 },
areaStyle: {
opacity: 0.3,
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: series.color || "#8ce99a" },
{ offset: 1, color: "rgba(1, 191, 236, 0)" },
]),
},
})),
};
chart.setOption(options);
chart.renderToCanvas();
}, [chartData, period]);
useEffect(() => {
if (!chartData) return;
const chart = echarts.init(document.getElementById(chartId), "dark");
const options: ECOption = {
backgroundColor: "#25262B",
tooltip: {
trigger: "axis",
backgroundColor: "#25262B",
borderColor: "#303030",
padding: [10, 15],
textStyle: { color: "#fff", fontSize: 16 },
valueFormatter(value) {
return `${value}%`;
},
axisPointer: {
label: {
formatter({ value }) {
return formatTooltipDate(new UTCDate(value), period);
},
},
},
},
legend: {
data: chartData.series.map((s) => s.name),
textStyle: { color: "#fff" },
top: 10,
},
grid: {
left: "1%",
right: "4%",
bottom: "3%",
top: "15%",
containLabel: true,
},
xAxis: {
boundaryGap: false,
data: chartData.columns,
axisLabel: {
formatter(value) {
return formatAxisDate(new UTCDate(value), period);
},
},
},
yAxis: {
boundaryGap: true,
min: 0,
axisLabel: {
formatter(value) {
return parseFloat(value).toFixed(1);
},
},
},
series: chartData.series.map((series) => ({
type: "line",
name: series.name,
data: series.data,
smooth: true,
emphasis: { focus: "series" },
color: series.color || "#8ce99a",
symbolSize: 7,
lineStyle: { width: 3 },
areaStyle: {
opacity: 0.3,
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: series.color || "#8ce99a" },
{ offset: 1, color: "rgba(1, 191, 236, 0)" },
]),
},
})),
};
chart.setOption(options);
return () => {
chart.dispose();
};
}, [chartData, period, chartId]);
🧰 Tools
🪛 GitHub Check: integration-test

[warning] 102-102:
React Hook useEffect has a missing dependency: 'chartId'. Either include it or remove the dependency array

🪛 GitHub Check: unit-test

[warning] 102-102:
React Hook useEffect has a missing dependency: 'chartId'. Either include it or remove the dependency array

Copy link

@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: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/api/src/app/metrics/services/dora-metrics.integration.test.ts (1)

1541-1546: Update test assertions to match BigInt return type.

The service correctly returns currentAmount as BigInt, and lines 1541–1545 appropriately use BigInt division and comparison operators (which handle type coercion). However, test assertions at lines 1583, 1646, 1716, 1791, 1843, 1904, 1957, 2022, 2074, and 2128 compare result.currentAmount with plain numbers using .toBe(), which performs strict equality and will fail since BigInt(7200000) !== 7200000. These assertions should wrap the expected values: .toBe(BigInt(7200000)) or use .toEqual() for BigInt comparison.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ca5ba6c and f26f25e.

📒 Files selected for processing (2)
  • apps/api/src/app/metrics/services/dora-metrics.integration.test.ts
  • apps/api/src/app/metrics/services/dora-metrics.service.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). (1)
  • GitHub Check: integration-test
🔇 Additional comments (9)
apps/api/src/app/metrics/services/dora-metrics.service.ts (5)

6-22: LGTM on interface definitions.

The type structure is well-designed: MetricResult uses bigint for amount fields, while FailureRateResult intentionally uses number for percentage-based failure rate values. The use of Pick with explicit field redefinitions keeps the API clear.


269-276: LGTM on BigInt conversion for lead time metric.

The conversion from number (after Math.floor) to BigInt is correct and aligns with the MetricResult interface requirements.


324-414: LGTM on failure rate metric implementation.

The FailureRateResult return type correctly uses number for currentAmount/previousAmount since these represent percentage values (0-100), unlike the other metrics which use bigint for millisecond or count values.


530-553: LGTM on deployment frequency BigInt handling.

The conversion pattern (bigint → Number → BigInt) is necessary because JavaScript doesn't allow mixed arithmetic between bigint and number types. This is acceptable given deployment counts are unlikely to exceed Number.MAX_SAFE_INTEGER.


774-781: LGTM on MTTR BigInt conversion.

The conversion pattern is consistent with getLeadTimeMetric and correctly returns bigint values as required by the MetricResult interface.

apps/api/src/app/metrics/services/dora-metrics.integration.test.ts (4)

2-25: Import reorganization looks good.

The import statements have been reorganized for better readability, and new timeline helper functions have been added. The changes are clean and maintain proper grouping.


81-81: DateRange formatting improvement approved.

The reformatting of dateRange objects to single-line structures improves readability without changing functionality.

Also applies to: 154-154, 232-232, 295-295


69-69: Consistent addition of authorId parameter approved.

The authorId parameter has been consistently added to seedDeployment calls throughout the test file, which aligns with schema changes requiring deployment authors.

Also applies to: 143-143, 210-210, 254-254, 276-276


918-930: Excellent use of timeline helper functions.

The introduction of generateDeploymentTimeline and generateIncidentResolutionTimeline helpers significantly improves test readability and reduces code duplication. The test scenarios remain comprehensive while being more maintainable.

Also applies to: 963-974, 996-1023, 1504-1516

sweetrdev
sweetrdev previously approved these changes Dec 27, 2025
Copy link
Contributor

@sweetrdev sweetrdev left a comment

Choose a reason for hiding this comment

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

Sweet

Copy link

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/src/app/metrics-and-insights/page.tsx (1)

170-177: Apply responsive wrapping to loading skeleton for consistency.

The loading skeleton still uses wrap="nowrap", which creates an inconsistency with the main metrics group that now uses responsive wrapping. This can cause horizontal overflow during loading on mobile devices.

🔎 Proposed fix for consistent responsive behavior
         {isLoading && (
-          <Group wrap="nowrap">
+          <Group wrap={isSmallScreen ? "wrap" : "nowrap"}>
             <Skeleton h={168} />
             <Skeleton h={168} />
             <Skeleton h={168} />
             <Skeleton h={168} />
           </Group>
         )}

Based on past review comments, this was previously identified as needing the same responsive treatment as the main metrics group.

🧹 Nitpick comments (1)
apps/api/src/app/email/services/send-email.service.ts (1)

11-13: Optional: Consider removing unused emailTemplates constant.

The local emailTemplates constant is only used to derive the EmailType type, but EmailType itself doesn't appear to be used anywhere in this file. The enqueueEmail function uses BuildEmailTemplate from email-template.service instead. Consider removing this if it's not needed, or add a comment explaining its future purpose.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f26f25e and 9a11a22.

📒 Files selected for processing (4)
  • apps/api/src/app/email/services/send-email.service.ts
  • apps/api/src/app/metrics/services/dora-metrics.integration.test.ts
  • apps/web/src/app/metrics-and-insights/page.tsx
  • docker-compose.yml
🚧 Files skipped from review as they are similar to previous changes (1)
  • docker-compose.yml
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: waltergalvao
Repo: sweetr-dev/sweetr.dev PR: 35
File: packages/graphql-types/api.ts:180-180
Timestamp: 2024-12-05T04:58:17.468Z
Learning: In this project, updates to the Prisma schema that require a migration are handled in separate PRs.
🧬 Code graph analysis (2)
apps/web/src/app/metrics-and-insights/page.tsx (3)
apps/web/src/providers/screen.provider.ts (1)
  • useScreenSize (4-10)
apps/web/src/providers/date.provider.ts (1)
  • humanizeDuration (19-21)
apps/web/src/app/metrics-and-insights/components/card-dora-metric/dora-card-stat.tsx (1)
  • CardDoraMetric (18-86)
apps/api/src/app/email/services/send-email.service.ts (4)
apps/api/src/app/email/services/email-template.service.ts (2)
  • emailTemplates (3-5)
  • BuildEmailTemplate (13-16)
apps/api/src/lib/email.ts (1)
  • EmailPayload (7-7)
apps/api/src/env.ts (1)
  • env (6-111)
apps/api/src/lib/logger.ts (1)
  • logger (21-76)
🔇 Additional comments (14)
apps/web/src/app/metrics-and-insights/page.tsx (6)

1-36: LGTM! Import organization looks good.

The updated imports properly support the new routing, responsive behavior, and date handling features. All additions appear to be utilized in the component.


42-42: Good addition of responsive screen detection.

The isSmallScreen hook properly enables responsive layout behavior for the DORA metrics cards.


127-127: Excellent responsive fix for the metrics group.

The responsive wrapping properly addresses the horizontal overflow concern raised in previous reviews. Cards will now stack on mobile devices while remaining in a row on larger screens.


140-144: Proper human-readable duration formatting.

The lead time amount correctly uses humanizeDuration for a more user-friendly display (e.g., "3 hours ago" format), with an appropriate fallback to "0" when no data is available.


151-151: Appropriate percentage formatting for failure rate.

The "%" suffix improves clarity and aligns with user expectations for a rate metric. The .toString() method properly handles both Number and BigInt types.


157-166: Consistent duration formatting for MTTR.

The MTTR formatting follows the same pattern as lead time, using humanizeDuration for a user-friendly display. This consistency across similar metrics is good.

apps/api/src/app/email/services/send-email.service.ts (1)

16-27: Good defensive structure with early return guard.

The enqueueEmail function is well-structured with appropriate early-exit logic and clear separation of concerns. Once the env import issue is resolved, this implementation will work correctly.

apps/api/src/app/metrics/services/dora-metrics.integration.test.ts (7)

2-25: LGTM: Import reorganization supports timeline helpers.

The addition of generateDeploymentTimeline and generateIncidentResolutionTimeline helpers improves test maintainability by reducing code duplication across tests that require timeline data.


69-69: LGTM: Consistent seedDeployment API usage.

The migration to passing deployedAt and authorId as a composite object improves the API clarity. The pattern is used consistently throughout all test cases.

Also applies to: 143-143, 210-210


81-81: LGTM: DateRange formatting simplification.

The single-line dateRange format is more compact and improves readability without changing functionality.

Also applies to: 154-154, 232-232


1541-1545: LGTM: BigInt division correctly implements hours conversion.

The BigInt division truncates rather than rounds, but the test appropriately uses range assertions (toBeGreaterThan and toBeLessThan) to accommodate this behavior. The arithmetic correctly converts milliseconds to hours.


918-930: LGTM: Timeline helpers improve test maintainability.

The introduction of generateDeploymentTimeline and generateIncidentResolutionTimeline helpers reduces code duplication and makes test scenarios more readable through configuration-based setup.

Also applies to: 1504-1516


87-100: LGTM: Comprehensive chart data validation.

The test appropriately validates:

  • Column/data alignment
  • Date column validity
  • Data value finiteness and non-negativity
  • Presence of actual data points

The use of Number(d) for validation handles potential BigInt chart values correctly.


86-86: No action needed. The currentAmount and previousAmount fields are computed values returned by the metrics service functions—not database fields. The BigInt conversion in test assertions (lines 86, 160, 238, 300–303, 838, 1541, 1583–1585) aligns with the service layer's return types and requires no Prisma schema migration.

Copy link

@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

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9a11a22 and 29441eb.

📒 Files selected for processing (2)
  • apps/api/src/app/email/services/send-email.service.ts
  • apps/web/src/app/metrics-and-insights/page.tsx
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: waltergalvao
Repo: sweetr-dev/sweetr.dev PR: 35
File: packages/graphql-types/api.ts:180-180
Timestamp: 2024-12-05T04:58:17.468Z
Learning: In this project, updates to the Prisma schema that require a migration are handled in separate PRs.
🧬 Code graph analysis (1)
apps/api/src/app/email/services/send-email.service.ts (4)
apps/api/src/app/email/services/email-template.service.ts (2)
  • emailTemplates (3-5)
  • BuildEmailTemplate (13-16)
apps/api/src/lib/email.ts (1)
  • EmailPayload (7-7)
apps/api/src/env.ts (1)
  • env (6-111)
apps/api/src/lib/logger.ts (1)
  • logger (21-76)
🔇 Additional comments (6)
apps/api/src/app/email/services/send-email.service.ts (4)

18-21: Good defensive guard.

The early return when EMAIL_ENABLED is falsy prevents unnecessary queue operations and clearly logs the skip reason. This is a clean implementation of feature-flag gating.


32-35: Consistent guard implementation.

The early return guard matches the pattern in enqueueEmail and appropriately uses logger.info to signal configuration-based skipping.


37-53: LGTM!

The email sending logic is well-structured:

  • Obtains client via getEmailClient()
  • Routes non-production emails to Resend's test address
  • Includes proper error handling with Sentry integration
  • Returns the response data

28-30: All call sites properly updated for the breaking signature change.

The sendEmail function signature correctly requires payload: SendEmailPayload as the first parameter. The single call site in send-email.worker.ts:24 properly invokes the function by passing the payload as the first argument: await sendEmail({ ...template, ...job.data.payload, }). No issues found.

apps/web/src/app/metrics-and-insights/page.tsx (2)

127-127: Responsive wrapping implemented correctly.

The conditional wrapping logic wrap={isSmallScreen ? "wrap" : "nowrap"} properly addresses the previous review concern about horizontal overflow on smaller screens. Both the loaded metric cards (line 127) and the loading skeleton state (line 171) now adapt responsively, ensuring a good user experience across all viewport sizes.

Also applies to: 171-171


151-151: Good UX improvement with percentage suffix.

Appending the "%" suffix to the failure rate clearly communicates the metric's unit, improving readability. The null-safe implementation with optional chaining and fallback to "0" is appropriate.

@@ -1,23 +1,25 @@
import { InitialSyncCompleteEmail } from "@sweetr/email-templates";
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's read the full send-email.service.ts file to understand its current state
cat -n apps/api/src/app/email/services/send-email.service.ts | head -70

Repository: sweetr-dev/sweetr.dev

Length of output: 2274


🏁 Script executed:

# Check for exports from send-email.service.ts
rg -n "export" apps/api/src/app/email/services/send-email.service.ts --type=ts

Repository: sweetr-dev/sweetr.dev

Length of output: 379


🏁 Script executed:

# Search for imports of EmailType from send-email.service.ts
rg -n "import.*EmailType.*from.*send-email" --type=ts

Repository: sweetr-dev/sweetr.dev

Length of output: 47


🏁 Script executed:

# Search for imports of emailTemplates from send-email.service.ts
rg -n "import.*emailTemplates.*from.*send-email" --type=ts

Repository: sweetr-dev/sweetr.dev

Length of output: 47


🏁 Script executed:

# Check email-template.service.ts to see the duplicate mapping
cat -n apps/api/src/app/email/services/email-template.service.ts | head -20

Repository: sweetr-dev/sweetr.dev

Length of output: 587


🏁 Script executed:

# Double-check if EmailType is used anywhere in the codebase outside of send-email.service.ts
rg "EmailType" --type=ts --type=tsx -g "!send-email.service.ts"

Repository: sweetr-dev/sweetr.dev

Length of output: 92


🏁 Script executed:

# Check if the emailTemplates constant from send-email.service.ts is used anywhere
rg "emailTemplates.*initialSyncComplete|initialSyncComplete.*emailTemplates" --type=ts --type=tsx

Repository: sweetr-dev/sweetr.dev

Length of output: 92


🏁 Script executed:

# Check if EmailType is used anywhere in the codebase outside of send-email.service.ts
rg "EmailType" --type=ts -g "!send-email.service.ts"

Repository: sweetr-dev/sweetr.dev

Length of output: 47


🏁 Script executed:

# Check if the emailTemplates constant from send-email.service.ts is used anywhere
rg "emailTemplates.*initialSyncComplete|initialSyncComplete.*emailTemplates" --type=ts

Repository: sweetr-dev/sweetr.dev

Length of output: 192


Remove unused imports and dead code.

The InitialSyncCompleteEmail import (line 1), emailTemplates constant (line 10), and EmailType type (line 12) are not used anywhere in this file. email-template.service.ts already provides the authoritative emailTemplates mapping and EmailTemplate type. Remove these unused declarations to eliminate code duplication and dead code.

🤖 Prompt for AI Agents
In apps/api/src/app/email/services/send-email.service.ts around lines 1 to 12,
remove the unused import "InitialSyncCompleteEmail" (line 1) and delete the
duplicated/dead declarations "emailTemplates" (line ~10) and the "EmailType"
type (line ~12); instead rely on the authoritative emailTemplates mapping and
EmailTemplate type exported from email-template.service.ts (import them if not
already used) so the file has no unused imports or duplicate definitions.

@waltergalvao waltergalvao merged commit ebb272a into main Dec 27, 2025
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

huge Huge PR - High risk of reviewer fatigue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants