Added anonymous stats

This commit is contained in:
maziggy
2025-12-11 13:35:59 +01:00
parent 77a66ea867
commit 7048bafff3
12 changed files with 664 additions and 392 deletions
+2
View File
@@ -438,6 +438,8 @@ export interface AppSettings {
time_format: 'system' | '12h' | '24h';
// Default printer
default_printer_id: number | null;
// Telemetry
telemetry_enabled: boolean;
}
export type AppSettingsUpdate = Partial<AppSettings>;
+15
View File
@@ -243,6 +243,21 @@ export default {
latestVersion: 'Neueste Version',
upToDate: 'Sie sind auf dem neuesten Stand',
updateAvailable: 'Update verfügbar',
telemetry: 'Anonyme Telemetrie',
telemetryDescription: 'Helfen Sie BamBuddy zu verbessern, indem Sie anonyme Nutzungsdaten senden',
telemetryLearnMore: 'Mehr erfahren',
telemetryInfoTitle: 'Welche Daten werden gesammelt?',
telemetryInfoIntro: 'BamBuddy sammelt minimale anonyme Daten, um zu verstehen, wie viele Personen die App nutzen und welche Versionen verwendet werden. Dies hilft bei der Priorisierung von Fehlerbehebungen und neuen Funktionen.',
telemetryInfoCollected: 'Was wir sammeln:',
telemetryInfoItem1: 'Eine zufällige Installations-ID (nicht mit Ihnen oder Ihrer Hardware verknüpft)',
telemetryInfoItem2: 'Die App-Version, die Sie verwenden',
telemetryInfoItem3: 'Ein Zeitstempel (um tägliche/wöchentliche aktive Nutzer zu zählen)',
telemetryInfoNotCollected: 'Was wir NICHT sammeln:',
telemetryInfoNotItem1: 'IP-Adressen oder Standortdaten',
telemetryInfoNotItem2: 'Druckernamen, Seriennummern oder Druckerdaten',
telemetryInfoNotItem3: 'Druckverlauf, Dateinamen oder persönliche Inhalte',
telemetryInfoNotItem4: 'Informationen, die Sie identifizieren könnten',
telemetryInfoFooter: 'Sie können die Telemetrie jederzeit deaktivieren. Die Installations-ID wird zufällig generiert und kann nicht zu Ihnen zurückverfolgt werden.',
// Notifications
notificationLanguage: 'Benachrichtigungssprache',
notificationLanguageDescription: 'Sprache für Push-Benachrichtigungen',
+15
View File
@@ -243,6 +243,21 @@ export default {
latestVersion: 'Latest Version',
upToDate: 'You are up to date',
updateAvailable: 'Update available',
telemetry: 'Anonymous telemetry',
telemetryDescription: 'Help improve BamBuddy by sending anonymous usage data',
telemetryLearnMore: 'Learn more',
telemetryInfoTitle: 'What data is collected?',
telemetryInfoIntro: 'BamBuddy collects minimal anonymous data to help understand how many people use the app and which versions are in use. This helps prioritize bug fixes and new features.',
telemetryInfoCollected: 'What we collect:',
telemetryInfoItem1: 'A random installation ID (not linked to you or your hardware)',
telemetryInfoItem2: 'The app version you\'re running',
telemetryInfoItem3: 'A timestamp (to count daily/weekly active users)',
telemetryInfoNotCollected: 'What we do NOT collect:',
telemetryInfoNotItem1: 'IP addresses or location data',
telemetryInfoNotItem2: 'Printer names, serial numbers, or any printer data',
telemetryInfoNotItem3: 'Print history, filenames, or any personal content',
telemetryInfoNotItem4: 'Any information that could identify you',
telemetryInfoFooter: 'You can disable telemetry at any time. The installation ID is randomly generated and cannot be traced back to you.',
// Notifications
notificationLanguage: 'Notification Language',
notificationLanguageDescription: 'Language for push notifications',
+108 -1
View File
@@ -1,5 +1,5 @@
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { Loader2, Plus, Plug, AlertTriangle, RotateCcw, Bell, Download, RefreshCw, ExternalLink, Globe, Droplets, Thermometer, FileText, Edit2, Send, CheckCircle, XCircle, History, Trash2, Upload, Zap, TrendingUp, Calendar, DollarSign, Power, PowerOff, Key, Copy, Database } from 'lucide-react';
import { Loader2, Plus, Plug, AlertTriangle, RotateCcw, Bell, Download, RefreshCw, ExternalLink, Globe, Droplets, Thermometer, FileText, Edit2, Send, CheckCircle, XCircle, History, Trash2, Upload, Zap, TrendingUp, Calendar, DollarSign, Power, PowerOff, Key, Copy, Database, Info, X, Shield } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { api } from '../api/client';
import type { AppSettings, SmartPlug, SmartPlugStatus, NotificationProvider, NotificationTemplate, UpdateStatus } from '../api/client';
@@ -50,6 +50,7 @@ export function SettingsPage() {
const [showBulkPlugConfirm, setShowBulkPlugConfirm] = useState<'on' | 'off' | null>(null);
const [showBackupModal, setShowBackupModal] = useState(false);
const [showRestoreModal, setShowRestoreModal] = useState(false);
const [showTelemetryInfo, setShowTelemetryInfo] = useState(false);
const handleDefaultViewChange = (path: string) => {
setDefaultViewState(path);
@@ -310,6 +311,7 @@ export function SettingsPage() {
settings.energy_tracking_mode !== localSettings.energy_tracking_mode ||
settings.check_updates !== localSettings.check_updates ||
settings.notification_language !== localSettings.notification_language ||
settings.telemetry_enabled !== localSettings.telemetry_enabled ||
settings.ams_humidity_good !== localSettings.ams_humidity_good ||
settings.ams_humidity_fair !== localSettings.ams_humidity_fair ||
settings.ams_temp_good !== localSettings.ams_temp_good ||
@@ -848,6 +850,32 @@ export function SettingsPage() {
<div className="w-11 h-6 bg-bambu-dark-tertiary peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-bambu-green"></div>
</label>
</div>
<div className="flex items-center justify-between">
<div>
<div className="flex items-center gap-2">
<p className="text-white">{t('settings.telemetry')}</p>
<button
onClick={() => setShowTelemetryInfo(true)}
className="inline-flex items-center gap-1 px-2 py-0.5 text-xs bg-bambu-dark rounded-full text-bambu-gray hover:text-white hover:bg-bambu-dark-tertiary transition-colors"
>
<Info className="w-3 h-3" />
{t('settings.telemetryLearnMore')}
</button>
</div>
<p className="text-sm text-bambu-gray">
{t('settings.telemetryDescription')}
</p>
</div>
<label className="relative inline-flex items-center cursor-pointer">
<input
type="checkbox"
checked={localSettings.telemetry_enabled}
onChange={(e) => updateSetting('telemetry_enabled', e.target.checked)}
className="sr-only peer"
/>
<div className="w-11 h-6 bg-bambu-dark-tertiary peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-bambu-green"></div>
</label>
</div>
<div className="border-t border-bambu-dark-tertiary pt-4">
<div className="flex items-center justify-between mb-2">
@@ -1831,6 +1859,85 @@ export function SettingsPage() {
}}
/>
)}
{/* Telemetry Info Modal */}
{showTelemetryInfo && (
<div
className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4"
onClick={() => setShowTelemetryInfo(false)}
>
<Card className="w-full max-w-lg" onClick={(e: React.MouseEvent) => e.stopPropagation()}>
<CardHeader className="flex flex-row items-center justify-between">
<div className="flex items-center gap-2">
<Shield className="w-5 h-5 text-bambu-green" />
<h2 className="text-lg font-semibold text-white">{t('settings.telemetryInfoTitle')}</h2>
</div>
<button
onClick={() => setShowTelemetryInfo(false)}
className="p-1 rounded hover:bg-bambu-dark-tertiary text-bambu-gray hover:text-white"
>
<X className="w-5 h-5" />
</button>
</CardHeader>
<CardContent className="space-y-4">
<p className="text-bambu-gray text-sm">
{t('settings.telemetryInfoIntro')}
</p>
<div className="space-y-3">
<h3 className="text-white font-medium">{t('settings.telemetryInfoCollected')}</h3>
<ul className="space-y-2 text-sm">
<li className="flex items-start gap-2 text-bambu-gray">
<CheckCircle className="w-4 h-4 text-bambu-green mt-0.5 shrink-0" />
<span>{t('settings.telemetryInfoItem1')}</span>
</li>
<li className="flex items-start gap-2 text-bambu-gray">
<CheckCircle className="w-4 h-4 text-bambu-green mt-0.5 shrink-0" />
<span>{t('settings.telemetryInfoItem2')}</span>
</li>
<li className="flex items-start gap-2 text-bambu-gray">
<CheckCircle className="w-4 h-4 text-bambu-green mt-0.5 shrink-0" />
<span>{t('settings.telemetryInfoItem3')}</span>
</li>
</ul>
</div>
<div className="space-y-3">
<h3 className="text-white font-medium">{t('settings.telemetryInfoNotCollected')}</h3>
<ul className="space-y-2 text-sm">
<li className="flex items-start gap-2 text-bambu-gray">
<XCircle className="w-4 h-4 text-red-400 mt-0.5 shrink-0" />
<span>{t('settings.telemetryInfoNotItem1')}</span>
</li>
<li className="flex items-start gap-2 text-bambu-gray">
<XCircle className="w-4 h-4 text-red-400 mt-0.5 shrink-0" />
<span>{t('settings.telemetryInfoNotItem2')}</span>
</li>
<li className="flex items-start gap-2 text-bambu-gray">
<XCircle className="w-4 h-4 text-red-400 mt-0.5 shrink-0" />
<span>{t('settings.telemetryInfoNotItem3')}</span>
</li>
<li className="flex items-start gap-2 text-bambu-gray">
<XCircle className="w-4 h-4 text-red-400 mt-0.5 shrink-0" />
<span>{t('settings.telemetryInfoNotItem4')}</span>
</li>
</ul>
</div>
<p className="text-bambu-gray text-xs border-t border-bambu-dark-tertiary pt-4">
{t('settings.telemetryInfoFooter')}
</p>
<Button
onClick={() => setShowTelemetryInfo(false)}
className="w-full"
>
{t('common.close')}
</Button>
</CardContent>
</Card>
</div>
)}
</div>
);
}