[PULSE-58] Data retention settings in Site Settings #33

Merged
uz1mani merged 6 commits from staging into main 2026-02-21 19:03:25 +00:00
uz1mani commented 2026-02-21 18:40:25 +00:00 (Migrated from github.com)

Work Item

PULSE-58

Summary

  • Add Data Retention section to Site Settings → Data & Privacy
  • Dropdown to set how long raw event data is kept, options limited by plan (free: 6mo, solo: 12mo, team: 24mo, business: 36mo)
  • Privacy snippet updated to include retention period

Changes

  • lib/plans.ts: Added getMaxRetentionMonthsForPlan, getRetentionOptionsForPlan, formatRetentionMonths
  • lib/api/sites.ts: Added data_retention_months to Site and UpdateSiteRequest
  • app/sites/[id]/settings/page.tsx: Data Retention section with plan-capped Select; loads subscription to determine options
  • lib/utils/privacySnippet.ts: Include retention period in generated snippet

Test Plan

  • Open Site Settings → Data & Privacy; verify "Keep raw event data for" dropdown appears
  • As free-tier org: dropdown shows 1, 3, 6 months
  • As paid org: dropdown shows plan-appropriate options (solo + 1yr, team + 2yr, business + 3yr)
  • Save retention value; reload page; verify value persists
  • Privacy snippet includes retention sentence when retention > 0
## Work Item PULSE-58 ## Summary - Add Data Retention section to Site Settings → Data & Privacy - Dropdown to set how long raw event data is kept, options limited by plan (free: 6mo, solo: 12mo, team: 24mo, business: 36mo) - Privacy snippet updated to include retention period ## Changes - lib/plans.ts: Added getMaxRetentionMonthsForPlan, getRetentionOptionsForPlan, formatRetentionMonths - lib/api/sites.ts: Added data_retention_months to Site and UpdateSiteRequest - app/sites/[id]/settings/page.tsx: Data Retention section with plan-capped Select; loads subscription to determine options - lib/utils/privacySnippet.ts: Include retention period in generated snippet ## Test Plan - [x] Open Site Settings → Data & Privacy; verify "Keep raw event data for" dropdown appears - [x] As free-tier org: dropdown shows 1, 3, 6 months - [x] As paid org: dropdown shows plan-appropriate options (solo + 1yr, team + 2yr, business + 3yr) - [x] Save retention value; reload page; verify value persists - [x] Privacy snippet includes retention sentence when retention > 0
greptile-apps[bot] commented 2026-02-21 18:43:38 +00:00 (Migrated from github.com)

Greptile Summary

This PR implements plan-based data retention settings for sites. Users can now select retention periods from 1 month to 3 years depending on their subscription tier (free: 6mo, solo: 12mo, team: 24mo, business: 36mo). The implementation includes robust error handling for subscription loading failures, automatic clamping to valid plan options when subscriptions load, and updates to the privacy snippet generation. All previously raised issues regarding default values, error handling, and value clamping have been addressed in subsequent commits.

Confidence Score: 4/5

  • This PR is safe to merge with minimal risk
  • All previously identified issues have been addressed through iterative fixes. The implementation includes proper error handling, consistent defaults across components, and automatic value clamping. Backend validation is confirmed to exist per developer response. The only minor concern is the complexity of the subscription loading and clamping logic, but it's well-handled with clear UX feedback.
  • No files require special attention

Important Files Changed

Filename Overview
app/sites/[id]/settings/page.tsx Added data retention UI with subscription-based options, error handling for subscription load failures, and automatic clamping to valid plan options
lib/plans.ts Added retention helper functions: getMaxRetentionMonthsForPlan, getRetentionOptionsForPlan, and formatRetentionMonths with clear plan-based limits
lib/utils/privacySnippet.ts Updated privacy snippet to include retention period using formatRetentionMonths with 6-month default matching the UI

Last reviewed commit: d1d82f5

<h3>Greptile Summary</h3> This PR implements plan-based data retention settings for sites. Users can now select retention periods from 1 month to 3 years depending on their subscription tier (free: 6mo, solo: 12mo, team: 24mo, business: 36mo). The implementation includes robust error handling for subscription loading failures, automatic clamping to valid plan options when subscriptions load, and updates to the privacy snippet generation. All previously raised issues regarding default values, error handling, and value clamping have been addressed in subsequent commits. <h3>Confidence Score: 4/5</h3> - This PR is safe to merge with minimal risk - All previously identified issues have been addressed through iterative fixes. The implementation includes proper error handling, consistent defaults across components, and automatic value clamping. Backend validation is confirmed to exist per developer response. The only minor concern is the complexity of the subscription loading and clamping logic, but it's well-handled with clear UX feedback. - No files require special attention <details><summary><h3>Important Files Changed</h3></summary> | Filename | Overview | |----------|----------| | app/sites/[id]/settings/page.tsx | Added data retention UI with subscription-based options, error handling for subscription load failures, and automatic clamping to valid plan options | | lib/plans.ts | Added retention helper functions: getMaxRetentionMonthsForPlan, getRetentionOptionsForPlan, and formatRetentionMonths with clear plan-based limits | | lib/utils/privacySnippet.ts | Updated privacy snippet to include retention period using formatRetentionMonths with 6-month default matching the UI | </details> </details> <sub>Last reviewed commit: d1d82f5</sub>
greptile-apps[bot] (Migrated from github.com) reviewed 2026-02-21 18:43:44 +00:00
greptile-apps[bot] (Migrated from github.com) left a comment

6 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

<sub>6 files reviewed, 2 comments</sub> <sub>[Edit Code Review Agent Settings](https://app.greptile.com/review/github) | [Greptile](https://greptile.com?utm_source=greptile_expert&utm_medium=github&utm_campaign=code_reviews)</sub>
greptile-apps[bot] (Migrated from github.com) commented 2026-02-21 18:43:43 +00:00

Silent error swallowing - if getSubscription() fails, user gets free tier options even if they're actually on a paid plan

This could prevent paid users from accessing their full retention options if the billing API is temporarily unavailable

Prompt To Fix With AI
This is a comment left during a code review.
Path: app/sites/[id]/settings/page.tsx
Line: 100-107

Comment:
Silent error swallowing - if `getSubscription()` fails, user gets free tier options even if they're actually on a paid plan

This could prevent paid users from accessing their full retention options if the billing API is temporarily unavailable

How can I resolve this? If you propose a fix, please make it concise.
Silent error swallowing - if `getSubscription()` fails, user gets free tier options even if they're actually on a paid plan This could prevent paid users from accessing their full retention options if the billing API is temporarily unavailable <details><summary>Prompt To Fix With AI</summary> `````markdown This is a comment left during a code review. Path: app/sites/[id]/settings/page.tsx Line: 100-107 Comment: Silent error swallowing - if `getSubscription()` fails, user gets free tier options even if they're actually on a paid plan This could prevent paid users from accessing their full retention options if the billing API is temporarily unavailable How can I resolve this? If you propose a fix, please make it concise. ````` </details>
greptile-apps[bot] (Migrated from github.com) commented 2026-02-21 18:43:42 +00:00

Race condition: if subscription loads after user interacts with Select, changing from null to loaded state will re-render with new options, but formData.data_retention_months might be outside the new plan's allowed values

If user has 24 months set but subscription loads showing they're on free tier (max 6), the Select will show "24 months" in UI but that option won't exist in the dropdown

Prompt To Fix With AI
This is a comment left during a code review.
Path: app/sites/[id]/settings/page.tsx
Line: 854-864

Comment:
Race condition: if `subscription` loads after user interacts with Select, changing from null to loaded state will re-render with new options, but `formData.data_retention_months` might be outside the new plan's allowed values

If user has 24 months set but subscription loads showing they're on free tier (max 6), the Select will show "24 months" in UI but that option won't exist in the dropdown

How can I resolve this? If you propose a fix, please make it concise.
Race condition: if `subscription` loads after user interacts with Select, changing from null to loaded state will re-render with new options, but `formData.data_retention_months` might be outside the new plan's allowed values If user has 24 months set but subscription loads showing they're on free tier (max 6), the Select will show "24 months" in UI but that option won't exist in the dropdown <details><summary>Prompt To Fix With AI</summary> `````markdown This is a comment left during a code review. Path: app/sites/[id]/settings/page.tsx Line: 854-864 Comment: Race condition: if `subscription` loads after user interacts with Select, changing from null to loaded state will re-render with new options, but `formData.data_retention_months` might be outside the new plan's allowed values If user has 24 months set but subscription loads showing they're on free tier (max 6), the Select will show "24 months" in UI but that option won't exist in the dropdown How can I resolve this? If you propose a fix, please make it concise. ````` </details>
uz1mani (Migrated from github.com) reviewed 2026-02-21 18:45:44 +00:00
uz1mani (Migrated from github.com) commented 2026-02-21 18:45:44 +00:00

Issue: If subscription loads after user interaction, formData.data_retention_months can exceed the plan’s allowed values, so the Select shows a value that isn’t in the options.
Fix: Added a useEffect that runs when subscription loads. If data_retention_months is not in the plan’s options, it clamps the value to the plan max via setFormData.
Why: Avoids an invalid Select state where the displayed value doesn’t match any option.

Issue: If subscription loads after user interaction, formData.data_retention_months can exceed the plan’s allowed values, so the Select shows a value that isn’t in the options. Fix: Added a useEffect that runs when subscription loads. If data_retention_months is not in the plan’s options, it clamps the value to the plan max via setFormData. Why: Avoids an invalid Select state where the displayed value doesn’t match any option.
uz1mani (Migrated from github.com) reviewed 2026-02-21 18:45:54 +00:00
uz1mani (Migrated from github.com) commented 2026-02-21 18:45:54 +00:00

Issue: When getSubscription() fails, we fall back to free-tier options without surfacing the error, so paid users may incorrectly see limited options.
Fix: Call toast.error with the auth error message when getSubscription fails, while still using free-tier options as a fallback so the UI remains usable.
Why: Paid users are aware when billing info fails to load and can retry, without blocking them from setting retention.

Issue: When getSubscription() fails, we fall back to free-tier options without surfacing the error, so paid users may incorrectly see limited options. Fix: Call toast.error with the auth error message when getSubscription fails, while still using free-tier options as a fallback so the UI remains usable. Why: Paid users are aware when billing info fails to load and can retry, without blocking them from setting retention.
greptile-apps[bot] (Migrated from github.com) reviewed 2026-02-21 18:49:01 +00:00
greptile-apps[bot] (Migrated from github.com) left a comment

6 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

<sub>6 files reviewed, 3 comments</sub> <sub>[Edit Code Review Agent Settings](https://app.greptile.com/review/github) | [Greptile](https://greptile.com?utm_source=greptile_expert&utm_medium=github&utm_campaign=code_reviews)</sub>
greptile-apps[bot] (Migrated from github.com) commented 2026-02-21 18:48:59 +00:00

Clamping only enforced client-side, no backend validation visible

data_retention_months is clamped in the UI when subscription loads, but there's no indication of backend validation. If a user saves before subscription loads, or if they manipulate the request, they could set retention beyond their plan limits. The backend should enforce plan-based retention caps.

Prompt To Fix With AI
This is a comment left during a code review.
Path: app/sites/[id]/settings/page.tsx
Line: 115-117

Comment:
Clamping only enforced client-side, no backend validation visible

`data_retention_months` is clamped in the UI when subscription loads, but there's no indication of backend validation. If a user saves before subscription loads, or if they manipulate the request, they could set retention beyond their plan limits. The backend should enforce plan-based retention caps.

How can I resolve this? If you propose a fix, please make it concise.
Clamping only enforced client-side, no backend validation visible `data_retention_months` is clamped in the UI when subscription loads, but there's no indication of backend validation. If a user saves before subscription loads, or if they manipulate the request, they could set retention beyond their plan limits. The backend should enforce plan-based retention caps. <details><summary>Prompt To Fix With AI</summary> `````markdown This is a comment left during a code review. Path: app/sites/[id]/settings/page.tsx Line: 115-117 Comment: Clamping only enforced client-side, no backend validation visible `data_retention_months` is clamped in the UI when subscription loads, but there's no indication of backend validation. If a user saves before subscription loads, or if they manipulate the request, they could set retention beyond their plan limits. The backend should enforce plan-based retention caps. How can I resolve this? If you propose a fix, please make it concise. ````` </details>
greptile-apps[bot] (Migrated from github.com) commented 2026-02-21 18:49:00 +00:00

Default of 12 months exceeds free plan's 6-month limit

When data.data_retention_months is null/undefined (e.g., existing sites before this feature), formData defaults to 12 months. Free tier users are capped at 6 months, so the clamping effect on line 115-117 will immediately change this to 6 when subscription loads. Default should align with the minimum common denominator (6 months) or the backend should set appropriate defaults.

Prompt To Fix With AI
This is a comment left during a code review.
Path: app/sites/[id]/settings/page.tsx
Line: 143

Comment:
Default of 12 months exceeds free plan's 6-month limit

When `data.data_retention_months` is null/undefined (e.g., existing sites before this feature), formData defaults to 12 months. Free tier users are capped at 6 months, so the clamping effect on line 115-117 will immediately change this to 6 when subscription loads. Default should align with the minimum common denominator (6 months) or the backend should set appropriate defaults.

How can I resolve this? If you propose a fix, please make it concise.
Default of 12 months exceeds free plan's 6-month limit When `data.data_retention_months` is null/undefined (e.g., existing sites before this feature), formData defaults to 12 months. Free tier users are capped at 6 months, so the clamping effect on line 115-117 will immediately change this to 6 when subscription loads. Default should align with the minimum common denominator (6 months) or the backend should set appropriate defaults. <details><summary>Prompt To Fix With AI</summary> `````markdown This is a comment left during a code review. Path: app/sites/[id]/settings/page.tsx Line: 143 Comment: Default of 12 months exceeds free plan's 6-month limit When `data.data_retention_months` is null/undefined (e.g., existing sites before this feature), formData defaults to 12 months. Free tier users are capped at 6 months, so the clamping effect on line 115-117 will immediately change this to 6 when subscription loads. Default should align with the minimum common denominator (6 months) or the backend should set appropriate defaults. How can I resolve this? If you propose a fix, please make it concise. ````` </details>
greptile-apps[bot] (Migrated from github.com) commented 2026-02-21 18:48:58 +00:00

Error toast shown but still silently defaults to free tier options

When getSubscription() fails, the error toast appears but subscription stays null, so line 869 calls getRetentionOptionsForPlan(null) which returns free tier (6mo max). A paid user experiencing a temporary API issue would see only free options without realizing their actual plan tier is unavailable.

Prompt To Fix With AI
This is a comment left during a code review.
Path: app/sites/[id]/settings/page.tsx
Line: 104-106

Comment:
Error toast shown but still silently defaults to free tier options

When `getSubscription()` fails, the error toast appears but `subscription` stays null, so line 869 calls `getRetentionOptionsForPlan(null)` which returns free tier (6mo max). A paid user experiencing a temporary API issue would see only free options without realizing their actual plan tier is unavailable.

How can I resolve this? If you propose a fix, please make it concise.
Error toast shown but still silently defaults to free tier options When `getSubscription()` fails, the error toast appears but `subscription` stays null, so line 869 calls `getRetentionOptionsForPlan(null)` which returns free tier (6mo max). A paid user experiencing a temporary API issue would see only free options without realizing their actual plan tier is unavailable. <details><summary>Prompt To Fix With AI</summary> `````markdown This is a comment left during a code review. Path: app/sites/[id]/settings/page.tsx Line: 104-106 Comment: Error toast shown but still silently defaults to free tier options When `getSubscription()` fails, the error toast appears but `subscription` stays null, so line 869 calls `getRetentionOptionsForPlan(null)` which returns free tier (6mo max). A paid user experiencing a temporary API issue would see only free options without realizing their actual plan tier is unavailable. How can I resolve this? If you propose a fix, please make it concise. ````` </details>
uz1mani (Migrated from github.com) reviewed 2026-02-21 18:50:35 +00:00
uz1mani (Migrated from github.com) commented 2026-02-21 18:50:34 +00:00

Issue: When getSubscription() fails, the toast appears but we still default to free tier options, so paid users may think the limited options are correct.
Fix: Added subscriptionLoadFailed state. On failure, show a persistent inline banner: "Plan limits could not be loaded. Options shown may be limited." plus a Retry button.
Why: Makes it clear that plan info failed to load and gives users a way to retry instead of relying only on a transient toast.

Issue: When getSubscription() fails, the toast appears but we still default to free tier options, so paid users may think the limited options are correct. Fix: Added subscriptionLoadFailed state. On failure, show a persistent inline banner: "Plan limits could not be loaded. Options shown may be limited." plus a Retry button. Why: Makes it clear that plan info failed to load and gives users a way to retry instead of relying only on a transient toast.
uz1mani (Migrated from github.com) reviewed 2026-02-21 18:50:44 +00:00
uz1mani (Migrated from github.com) commented 2026-02-21 18:50:44 +00:00

Issue: Reviewer questioned whether the backend enforces plan-based retention caps.
Fix: No code change. The backend already validates in UpdateSiteHandler (internal/api/sites.go): it fetches org billing, derives MaxRetentionMonthsForPlan, and returns 400 when data_retention_months exceeds that value. Client-side clamping is only for UX; the server enforces the limit.
Why: The backend was implemented with validation from the start; this is a clarification rather than a missing check.

Issue: Reviewer questioned whether the backend enforces plan-based retention caps. Fix: No code change. The backend already validates in UpdateSiteHandler (internal/api/sites.go): it fetches org billing, derives MaxRetentionMonthsForPlan, and returns 400 when data_retention_months exceeds that value. Client-side clamping is only for UX; the server enforces the limit. Why: The backend was implemented with validation from the start; this is a clarification rather than a missing check.
uz1mani (Migrated from github.com) reviewed 2026-02-21 18:50:55 +00:00
uz1mani (Migrated from github.com) commented 2026-02-21 18:50:55 +00:00

Issue: Default of 12 months exceeds the free plan limit of 6 months, causing a visible clamp when subscription loads.
Fix: Changed the default from 12 to 6 when data.data_retention_months is null/undefined, and in the initial formData state.
Why: Uses a safe minimum across all plans so free users avoid the 12→6 jump and the default matches the lowest plan cap.

Issue: Default of 12 months exceeds the free plan limit of 6 months, causing a visible clamp when subscription loads. Fix: Changed the default from 12 to 6 when data.data_retention_months is null/undefined, and in the initial formData state. Why: Uses a safe minimum across all plans so free users avoid the 12→6 jump and the default matches the lowest plan cap.
greptile-apps[bot] (Migrated from github.com) reviewed 2026-02-21 18:58:11 +00:00
greptile-apps[bot] (Migrated from github.com) left a comment

6 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

<sub>6 files reviewed, 2 comments</sub> <sub>[Edit Code Review Agent Settings](https://app.greptile.com/review/github) | [Greptile](https://greptile.com?utm_source=greptile_expert&utm_medium=github&utm_campaign=code_reviews)</sub>
greptile-apps[bot] (Migrated from github.com) commented 2026-02-21 18:58:10 +00:00

Clamping can produce invalid Select values

If a site's saved data_retention_months is a value not in the plan's option set (e.g., 2, which isn't in [1, 3, 6, ...]), this code sets Math.min(2, 6) = 2 — still not a valid option. The Select component would then have a value with no matching dropdown entry, which may render incorrectly or show a blank selection.

Consider snapping to the nearest valid option instead of just clamping to the max:

    const opts = getRetentionOptionsForPlan(subscription.plan_id)
    const values = opts.map(o => o.value)
    const maxVal = Math.max(...values)
    setFormData(prev => {
      if (values.includes(prev.data_retention_months)) return prev
      // Find largest valid option that doesn't exceed the current value, or fall back to max
      const bestFit = values.filter(v => v <= prev.data_retention_months).pop() ?? maxVal
      return { ...prev, data_retention_months: Math.min(bestFit, maxVal) }
    })
Prompt To Fix With AI
This is a comment left during a code review.
Path: app/sites/[id]/settings/page.tsx
Line: 115-121

Comment:
**Clamping can produce invalid Select values**

If a site's saved `data_retention_months` is a value not in the plan's option set (e.g., `2`, which isn't in [1, 3, 6, ...]), this code sets `Math.min(2, 6) = 2` — still not a valid option. The `Select` component would then have a value with no matching dropdown entry, which may render incorrectly or show a blank selection.

Consider snapping to the nearest valid option instead of just clamping to the max:

```suggestion
    const opts = getRetentionOptionsForPlan(subscription.plan_id)
    const values = opts.map(o => o.value)
    const maxVal = Math.max(...values)
    setFormData(prev => {
      if (values.includes(prev.data_retention_months)) return prev
      // Find largest valid option that doesn't exceed the current value, or fall back to max
      const bestFit = values.filter(v => v <= prev.data_retention_months).pop() ?? maxVal
      return { ...prev, data_retention_months: Math.min(bestFit, maxVal) }
    })
```

How can I resolve this? If you propose a fix, please make it concise.
**Clamping can produce invalid Select values** If a site's saved `data_retention_months` is a value not in the plan's option set (e.g., `2`, which isn't in [1, 3, 6, ...]), this code sets `Math.min(2, 6) = 2` — still not a valid option. The `Select` component would then have a value with no matching dropdown entry, which may render incorrectly or show a blank selection. Consider snapping to the nearest valid option instead of just clamping to the max: ```suggestion const opts = getRetentionOptionsForPlan(subscription.plan_id) const values = opts.map(o => o.value) const maxVal = Math.max(...values) setFormData(prev => { if (values.includes(prev.data_retention_months)) return prev // Find largest valid option that doesn't exceed the current value, or fall back to max const bestFit = values.filter(v => v <= prev.data_retention_months).pop() ?? maxVal return { ...prev, data_retention_months: Math.min(bestFit, maxVal) } }) ``` <details><summary>Prompt To Fix With AI</summary> `````markdown This is a comment left during a code review. Path: app/sites/[id]/settings/page.tsx Line: 115-121 Comment: **Clamping can produce invalid Select values** If a site's saved `data_retention_months` is a value not in the plan's option set (e.g., `2`, which isn't in [1, 3, 6, ...]), this code sets `Math.min(2, 6) = 2` — still not a valid option. The `Select` component would then have a value with no matching dropdown entry, which may render incorrectly or show a blank selection. Consider snapping to the nearest valid option instead of just clamping to the max: ```suggestion const opts = getRetentionOptionsForPlan(subscription.plan_id) const values = opts.map(o => o.value) const maxVal = Math.max(...values) setFormData(prev => { if (values.includes(prev.data_retention_months)) return prev // Find largest valid option that doesn't exceed the current value, or fall back to max const bestFit = values.filter(v => v <= prev.data_retention_months).pop() ?? maxVal return { ...prev, data_retention_months: Math.min(bestFit, maxVal) } }) ``` How can I resolve this? If you propose a fix, please make it concise. ````` </details>
@@ -22,6 +23,7 @@ export function generatePrivacySnippet(site: Site): string {
const screen = site.collect_screen_resolution ?? true
greptile-apps[bot] (Migrated from github.com) commented 2026-02-21 18:58:09 +00:00

Mismatched default retention value

The fallback here is ?? 12 (12 months), but the settings page (page.tsx:146) defaults to ?? 6 (6 months). For existing sites that haven't saved a data_retention_months value yet, the privacy snippet will claim "Raw event data is automatically deleted after 1 year" while the settings page displays and saves "6 months."

These should use the same default to avoid misleading the user's privacy policy text.

  const retentionMonths = site.data_retention_months ?? 6
Prompt To Fix With AI
This is a comment left during a code review.
Path: lib/utils/privacySnippet.ts
Line: 26

Comment:
**Mismatched default retention value**

The fallback here is `?? 12` (12 months), but the settings page (`page.tsx:146`) defaults to `?? 6` (6 months). For existing sites that haven't saved a `data_retention_months` value yet, the privacy snippet will claim "Raw event data is automatically deleted after 1 year" while the settings page displays and saves "6 months."

These should use the same default to avoid misleading the user's privacy policy text.

```suggestion
  const retentionMonths = site.data_retention_months ?? 6
```

How can I resolve this? If you propose a fix, please make it concise.
**Mismatched default retention value** The fallback here is `?? 12` (12 months), but the settings page (`page.tsx:146`) defaults to `?? 6` (6 months). For existing sites that haven't saved a `data_retention_months` value yet, the privacy snippet will claim "Raw event data is automatically deleted after 1 year" while the settings page displays and saves "6 months." These should use the same default to avoid misleading the user's privacy policy text. ```suggestion const retentionMonths = site.data_retention_months ?? 6 ``` <details><summary>Prompt To Fix With AI</summary> `````markdown This is a comment left during a code review. Path: lib/utils/privacySnippet.ts Line: 26 Comment: **Mismatched default retention value** The fallback here is `?? 12` (12 months), but the settings page (`page.tsx:146`) defaults to `?? 6` (6 months). For existing sites that haven't saved a `data_retention_months` value yet, the privacy snippet will claim "Raw event data is automatically deleted after 1 year" while the settings page displays and saves "6 months." These should use the same default to avoid misleading the user's privacy policy text. ```suggestion const retentionMonths = site.data_retention_months ?? 6 ``` How can I resolve this? If you propose a fix, please make it concise. ````` </details>
uz1mani (Migrated from github.com) reviewed 2026-02-21 18:58:55 +00:00
uz1mani (Migrated from github.com) commented 2026-02-21 18:58:55 +00:00

Issue: Clamping with Math.min(current, max) can yield values not in the options (e.g. 2 with [1,3,6]), so the Select has no matching option and may render incorrectly.
Fix: Choose the largest valid option ≤ current value; if none, use max. bestFit = values.filter(v => v <= prev.data_retention_months).pop() ?? maxVal.
Why: Ensures the Select always receives a value that exists in its options.

Issue: Clamping with Math.min(current, max) can yield values not in the options (e.g. 2 with [1,3,6]), so the Select has no matching option and may render incorrectly. Fix: Choose the largest valid option ≤ current value; if none, use max. `bestFit = values.filter(v => v <= prev.data_retention_months).pop() ?? maxVal`. Why: Ensures the Select always receives a value that exists in its options.
uz1mani (Migrated from github.com) reviewed 2026-02-21 18:59:05 +00:00
@@ -22,6 +23,7 @@ export function generatePrivacySnippet(site: Site): string {
const screen = site.collect_screen_resolution ?? true
uz1mani (Migrated from github.com) commented 2026-02-21 18:59:05 +00:00

Issue: The privacy snippet defaulted to 12 months while the settings page defaults to 6, so unsaved sites could show "1 year" in the snippet but "6 months" in the UI.
Fix: Use ?? 6 to match the settings page default.
Why: Keeps defaults aligned so the generated privacy text matches what the user sees in settings.

Issue: The privacy snippet defaulted to 12 months while the settings page defaults to 6, so unsaved sites could show "1 year" in the snippet but "6 months" in the UI. Fix: Use `?? 6` to match the settings page default. Why: Keeps defaults aligned so the generated privacy text matches what the user sees in settings.
Sign in to join this conversation.
No description provided.