diff --git a/frontend/src/i18n/locales/de.json b/frontend/src/i18n/locales/de.json
index cff4c6c8..551fc184 100644
--- a/frontend/src/i18n/locales/de.json
+++ b/frontend/src/i18n/locales/de.json
@@ -5043,6 +5043,21 @@
"saveFailed": "Einige Änderungen konnten nicht gespeichert werden — Sie können sie unter Einstellungen → Veranstaltungsarten abschließen.",
"deleteFailed": "Eine Löschung ist fehlgeschlagen — die Liste wurde neu geladen. Mitgelieferte Arten können nur hier gelöscht werden; versuchen Sie es erneut oder fahren Sie mit ihnen fort."
},
+ "usageReporting": {
+ "subtitle": "Noch eine Sache",
+ "intro": "Möchten Sie helfen, die Weiterentwicklung von PicPeak mitzugestalten? Die anonyme Nutzungsstatistik ist freiwillig — und funktioniert anders als das Tracking, das Sie sonst gewohnt sind abzulehnen.",
+ "oneWayTitle": "Anders als Google Analytics & Co.",
+ "oneWayDesc": "Ein reiner Einwegkanal: PicPeak sendet signierte Berichte, kann aber niemals Befehle zurückempfangen. Keine Fotos, Namen, E-Mails oder Klickverfolgung — niemals.",
+ "mutualTitle": "Sehen, was alle anderen nutzen",
+ "mutualDesc": "Als Teilnehmer sehen Sie denselben geteilten Datensatz — welche Funktionen andere Installationen wirklich verwenden, nicht nur das, was Sie selbst senden.",
+ "feedbackTitle": "Feedback direkt teilen",
+ "feedbackDesc": "Senden Sie kurzes Feedback oder Funktionswünsche direkt an den Entwickler — ganz einfach aus den Einstellungen.",
+ "consentCheck": "Ich bin einverstanden — jederzeit wieder abschaltbar unter Einstellungen → Produktnutzung.",
+ "enable": "Ja, ich möchte teilnehmen",
+ "skip": "Vielleicht später",
+ "enabled": "Danke für Ihre Teilnahme!",
+ "enableFailed": "Konnte gerade nicht aktiviert werden — Sie können es jederzeit unter Einstellungen → Produktnutzung erneut versuchen."
+ },
"community": {
"subtitle": "Alles bereit",
"mission": "PicPeak gibt es, damit Fotografinnen und Fotografen ihre Galerien und Kundendaten selbst besitzen — auf dem eigenen Server, ohne monatliche SaaS-Gebühren. Danke, dass Sie es ausprobieren.",
diff --git a/frontend/src/i18n/locales/en.json b/frontend/src/i18n/locales/en.json
index aaa4ddc4..8f4b087d 100644
--- a/frontend/src/i18n/locales/en.json
+++ b/frontend/src/i18n/locales/en.json
@@ -4925,6 +4925,21 @@
"saveFailed": "Some event type changes could not be saved — you can finish them in Settings → Event Types.",
"deleteFailed": "A deletion failed — the list has been reloaded. Built-in types can only be deleted here, so retry or continue with them kept."
},
+ "usageReporting": {
+ "subtitle": "One more thing",
+ "intro": "Want to help shape what PicPeak builds next? Anonymous usage reporting is optional, and it works differently from the analytics you're used to declining.",
+ "oneWayTitle": "Not like Google Analytics",
+ "oneWayDesc": "A one-way channel only: PicPeak sends signed reports but can never receive commands back. No photos, names, emails or click tracking — ever.",
+ "mutualTitle": "See what everyone else uses",
+ "mutualDesc": "As a participant, you can browse the same shared dataset — which features other installations actually use, not just what you send.",
+ "feedbackTitle": "Share feedback directly",
+ "feedbackDesc": "Send quick feedback or feature requests straight to the maintainer, right from Settings.",
+ "consentCheck": "I agree to participate — I can turn this off anytime in Settings → Product usage.",
+ "enable": "Yes, I'll participate",
+ "skip": "Maybe later",
+ "enabled": "Thanks for participating!",
+ "enableFailed": "Couldn't enable it just now — you can try again anytime in Settings → Product usage."
+ },
"community": {
"subtitle": "You're all set",
"mission": "PicPeak exists so photographers can own their galleries and client data — on their own server, without monthly SaaS fees. Thanks for giving it a try.",
diff --git a/frontend/src/pages/SetupPage.tsx b/frontend/src/pages/SetupPage.tsx
index c5c35eea..8d186d00 100644
--- a/frontend/src/pages/SetupPage.tsx
+++ b/frontend/src/pages/SetupPage.tsx
@@ -1,7 +1,7 @@
import React, { useState } from 'react';
import { Navigate, useNavigate } from 'react-router-dom';
import { useQuery } from '@tanstack/react-query';
-import { Key, Mail, Lock, Eye, EyeOff, AlertCircle, ArrowLeft, ArrowRight, Copy, Check, ExternalLink, Bug, Lightbulb, Star, Coffee } from 'lucide-react';
+import { Key, Mail, Lock, Eye, EyeOff, AlertCircle, ArrowLeft, ArrowRight, Copy, Check, ExternalLink, Bug, Lightbulb, Star, Coffee, ShieldOff, Users, MessageSquare } from 'lucide-react';
import type { LucideIcon } from 'lucide-react';
import { toast } from 'react-toastify';
import { useTranslation } from 'react-i18next';
@@ -11,6 +11,7 @@ import { useAdminAuth } from '../contexts';
import { setupService } from '../services/setup.service';
import { settingsService } from '../services/settings.service';
import { featureFlagsService, type FeatureFlags, type FeatureKey } from '../services/featureFlags.service';
+import { productUsageService } from '../services/productUsage.service';
import { PicpeakRestoreCard } from '../components/admin/PicpeakBackupCard';
import { SetupConfigStep } from '../components/admin/SetupConfigStep';
import { SetupEventTypesStep } from '../components/admin/SetupEventTypesStep';
@@ -36,6 +37,17 @@ const COMMUNITY_LINKS: {
{ key: 'support', href: 'https://www.buymeacoffee.com/theluap', icon: Coffee },
];
+// Anonymous usage-reporting opt-in, one step before the final thank-you
+// screen. Kept deliberately short — most people reflexively decline "send us
+// data" prompts, so this leads with what makes PicPeak's reporting different
+// from typical analytics rather than repeating the full disclosure the
+// Settings → Product usage tab already shows in detail.
+const USAGE_REPORTING_POINTS: { key: string; icon: LucideIcon }[] = [
+ { key: 'oneWay', icon: ShieldOff },
+ { key: 'mutual', icon: Users },
+ { key: 'feedback', icon: MessageSquare },
+];
+
// "How will you use PicPeak?" — the opt-in feature groups shown after the admin
// account is created. galleries/analytics/userManagement are always on and not
// listed. Labels/descriptions reuse the existing Settings→Features i18n keys
@@ -70,7 +82,7 @@ export const SetupPage: React.FC = () => {
staleTime: Infinity,
});
- const [step, setStep] = useState<'token' | 'account' | 'usage' | 'eventTypes' | 'restore' | 'config' | 'community'>('token');
+ const [step, setStep] = useState<'token' | 'account' | 'usage' | 'eventTypes' | 'restore' | 'config' | 'usageReporting' | 'community'>('token');
const [form, setForm] = useState({ token: '', email: '', password: '', confirm: '' });
const [showPassword, setShowPassword] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
@@ -79,6 +91,8 @@ export const SetupPage: React.FC = () => {
const [errors, setErrors] = useState
@@ -537,8 +569,62 @@ export const SetupPage: React.FC = () => {
) : step === 'config' ? (
{t('setup.usageReporting.intro')} {t('setup.community.mission')}