feat: add data retention settings to SiteSettingsPage, including subscription-based options and UI updates for user interaction
This commit is contained in:
@@ -21,6 +21,8 @@ export interface Site {
|
||||
enable_performance_insights?: boolean
|
||||
// Bot and noise filtering
|
||||
filter_bots?: boolean
|
||||
// Data retention (months); 0 = keep forever
|
||||
data_retention_months?: number
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
@@ -47,6 +49,8 @@ export interface UpdateSiteRequest {
|
||||
enable_performance_insights?: boolean
|
||||
// Bot and noise filtering
|
||||
filter_bots?: boolean
|
||||
// Data retention (months); 0 = keep forever
|
||||
data_retention_months?: number
|
||||
}
|
||||
|
||||
export async function listSites(): Promise<Site[]> {
|
||||
|
||||
39
lib/plans.ts
39
lib/plans.ts
@@ -40,3 +40,42 @@ export function getLimitForTierIndex(index: number): number {
|
||||
if (index < 0 || index >= TRAFFIC_TIERS.length) return 100000
|
||||
return TRAFFIC_TIERS[index].value
|
||||
}
|
||||
|
||||
/** Maximum data retention (months) allowed per plan. */
|
||||
export function getMaxRetentionMonthsForPlan(planId: string | null | undefined): number {
|
||||
switch (planId) {
|
||||
case 'business': return 60
|
||||
case 'team': return 24
|
||||
case 'solo': return 12
|
||||
default: return 6
|
||||
}
|
||||
}
|
||||
|
||||
/** Selectable retention options (months) for the given plan. */
|
||||
export function getRetentionOptionsForPlan(planId: string | null | undefined): { label: string; value: number }[] {
|
||||
const base = [
|
||||
{ label: '1 month', value: 1 },
|
||||
{ label: '3 months', value: 3 },
|
||||
{ label: '6 months', value: 6 },
|
||||
]
|
||||
const solo = [...base, { label: '1 year', value: 12 }]
|
||||
const team = [...solo, { label: '2 years', value: 24 }]
|
||||
const business = [...team, { label: '3 years', value: 36 }, { label: '5 years', value: 60 }]
|
||||
|
||||
switch (planId) {
|
||||
case 'business': return business
|
||||
case 'team': return team
|
||||
case 'solo': return solo
|
||||
default: return base
|
||||
}
|
||||
}
|
||||
|
||||
/** Human-readable label for a retention value in months. */
|
||||
export function formatRetentionMonths(months: number): string {
|
||||
if (months === 0) return 'Forever'
|
||||
if (months === 1) return '1 month'
|
||||
if (months < 12) return `${months} months`
|
||||
const years = months / 12
|
||||
if (Number.isInteger(years)) return years === 1 ? '1 year' : `${years} years`
|
||||
return `${months} months`
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Site } from '@/lib/api/sites'
|
||||
import { formatRetentionMonths } from '@/lib/plans'
|
||||
|
||||
const DOCS_URL =
|
||||
(typeof process !== 'undefined' && process.env?.NEXT_PUBLIC_APP_URL)
|
||||
@@ -22,6 +23,7 @@ export function generatePrivacySnippet(site: Site): string {
|
||||
const screen = site.collect_screen_resolution ?? true
|
||||
const perf = site.enable_performance_insights ?? false
|
||||
const filterBots = site.filter_bots ?? true
|
||||
const retentionMonths = site.data_retention_months ?? 12
|
||||
|
||||
const parts: string[] = []
|
||||
if (paths) parts.push('which pages are viewed')
|
||||
@@ -44,6 +46,9 @@ export function generatePrivacySnippet(site: Site): string {
|
||||
if (filterBots) {
|
||||
p2 += 'Known bots and referrer spam are excluded from our analytics. '
|
||||
}
|
||||
if (retentionMonths > 0) {
|
||||
p2 += `Raw event data is automatically deleted after ${formatRetentionMonths(retentionMonths)}. `
|
||||
}
|
||||
p2 += `Data is processed in a privacy-preserving way and is not used to identify individuals. For more information, see Pulse's documentation: ${DOCS_URL}`
|
||||
|
||||
return `${p1}\n\n${p2}`
|
||||
|
||||
Reference in New Issue
Block a user