Files
Joachim Hummel a39b53151e Apply modern SaaS design to all admin pages
Modernize all admin interface pages with consistent design language:
- Add hero sections with gradient backgrounds and blur effects
- Implement modern card designs with hover animations
- Use gradient buttons with shadow effects
- Add emoji icons in colored containers
- Apply consistent color themes per page
- Enhance user experience with smooth transitions

Pages updated:
- /admin/devices (purple theme)
- /admin/mqtt (cyan/blue theme)
- /admin/setup (emerald theme)
- /admin/users (violet theme)
- /admin/settings (indigo theme)
- /admin/emails (pink/rose theme)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-29 23:31:25 +00:00

493 lines
21 KiB
TypeScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { useState } from "react";
export default function SetupGuidePage() {
const [openSections, setOpenSections] = useState<Record<string, boolean>>({
"1": true, // Installation section open by default
});
const toggleSection = (id: string) => {
setOpenSections(prev => ({ ...prev, [id]: !prev[id] }));
};
return (
<div className="max-w-5xl mx-auto space-y-8">
{/* Hero Section with Gradient */}
<div className="relative overflow-hidden rounded-2xl bg-gradient-to-br from-emerald-600 via-teal-700 to-cyan-800 p-8 shadow-xl">
<div className="absolute top-0 right-0 -mt-4 -mr-4 h-40 w-40 rounded-full bg-white/10 blur-3xl"></div>
<div className="absolute bottom-0 left-0 -mb-4 -ml-4 h-40 w-40 rounded-full bg-white/10 blur-3xl"></div>
<div className="relative">
<h1 className="text-4xl font-bold text-white mb-3 flex items-center gap-3">
<span className="text-5xl">📱</span>
OwnTracks App Setup Anleitung
</h1>
<p className="text-emerald-100 text-lg">
Diese Anleitung erklärt Schritt-für-Schritt, wie Sie die OwnTracks App
auf Ihrem Smartphone installieren und mit dem Location Tracker System verbinden.
</p>
</div>
</div>
<div className="bg-white rounded-2xl shadow-lg p-8">
{/* Table of Contents */}
<div className="bg-gradient-to-br from-blue-50 to-indigo-50 border-2 border-blue-200 rounded-2xl p-6 mb-8 shadow-md">
<h2 className="text-xl font-bold text-gray-900 mb-4 flex items-center gap-2">
<span className="text-2xl">📋</span>
Inhaltsverzeichnis
</h2>
<ul className="grid grid-cols-1 md:grid-cols-2 gap-3">
<li><a href="#installation" className="flex items-center gap-2 text-blue-700 hover:text-blue-900 font-semibold hover:underline bg-white px-4 py-2 rounded-lg shadow-sm hover:shadow-md transition-all"> 1. Installation</a></li>
<li><a href="#credentials" className="flex items-center gap-2 text-blue-700 hover:text-blue-900 font-semibold hover:underline bg-white px-4 py-2 rounded-lg shadow-sm hover:shadow-md transition-all"> 2. MQTT Credentials erhalten</a></li>
<li><a href="#configuration" className="flex items-center gap-2 text-blue-700 hover:text-blue-900 font-semibold hover:underline bg-white px-4 py-2 rounded-lg shadow-sm hover:shadow-md transition-all"> 3. App Konfiguration</a></li>
<li><a href="#testing" className="flex items-center gap-2 text-blue-700 hover:text-blue-900 font-semibold hover:underline bg-white px-4 py-2 rounded-lg shadow-sm hover:shadow-md transition-all"> 5. Verbindung testen</a></li>
<li><a href="#ports" className="flex items-center gap-2 text-blue-700 hover:text-blue-900 font-semibold hover:underline bg-white px-4 py-2 rounded-lg shadow-sm hover:shadow-md transition-all"> 6. Port 1883 vs. 9001</a></li>
<li><a href="#troubleshooting" className="flex items-center gap-2 text-blue-700 hover:text-blue-900 font-semibold hover:underline bg-white px-4 py-2 rounded-lg shadow-sm hover:shadow-md transition-all"> 7. Troubleshooting</a></li>
</ul>
</div>
{/* Section 1: Installation */}
<Section
id="installation"
title="1. Installation"
icon="📥"
isOpen={openSections["1"]}
onToggle={() => toggleSection("1")}
>
<div className="grid md:grid-cols-2 gap-6">
<div className="bg-gradient-to-br from-blue-50 to-indigo-50 border-2 border-blue-200 rounded-xl p-5 shadow-md hover:shadow-lg transition-shadow">
<h4 className="font-bold text-lg mb-3 flex items-center gap-2">
<span className="text-2xl">🍎</span>
iOS (iPhone/iPad)
</h4>
<ol className="list-decimal list-inside space-y-2 text-sm">
<li>Öffnen Sie den <strong>App Store</strong></li>
<li>Suchen Sie nach <strong>"OwnTracks"</strong></li>
<li>Laden Sie die App herunter</li>
</ol>
<a
href="https://apps.apple.com/app/owntracks/id692424691"
target="_blank"
rel="noopener noreferrer"
className="inline-block mt-3 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 text-sm font-semibold shadow-md hover:shadow-lg transition-all"
>
App Store Link
</a>
</div>
<div className="bg-gradient-to-br from-green-50 to-emerald-50 border-2 border-green-200 rounded-xl p-5 shadow-md hover:shadow-lg transition-shadow">
<h4 className="font-bold text-lg mb-3 flex items-center gap-2">
<span className="text-2xl">🤖</span>
Android
</h4>
<ol className="list-decimal list-inside space-y-2 text-sm">
<li>Öffnen Sie den <strong>Google Play Store</strong></li>
<li>Suchen Sie nach <strong>"OwnTracks"</strong></li>
<li>Laden Sie die App herunter</li>
</ol>
<a
href="https://play.google.com/store/apps/details?id=org.owntracks.android"
target="_blank"
rel="noopener noreferrer"
className="inline-block mt-3 px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 text-sm font-semibold shadow-md hover:shadow-lg transition-all"
>
Play Store Link
</a>
</div>
</div>
</Section>
{/* Section 2: Credentials */}
<Section
id="credentials"
title="2. MQTT Credentials erhalten"
icon="🔑"
isOpen={openSections["2"]}
onToggle={() => toggleSection("2")}
>
<div className="bg-gradient-to-r from-yellow-50 to-orange-50 border-2 border-yellow-300 rounded-xl p-5 mb-6 shadow-md">
<p className="text-sm font-bold text-yellow-900 flex items-center gap-2">
<span className="text-xl"></span>
Wichtig: Bevor Sie die App konfigurieren, benötigen Sie MQTT-Zugangsdaten!
</p>
</div>
<ol className="list-decimal list-inside space-y-3 text-sm">
<li>Navigieren Sie zu <a href="/admin/mqtt" className="text-blue-600 hover:underline font-semibold">MQTT Provisioning</a></li>
<li>Klicken Sie auf <strong>"Device Provisionieren"</strong></li>
<li>Wählen Sie Ihr Device aus der Liste</li>
<li>Aktivieren Sie <strong>"Automatisch Username & Passwort generieren"</strong></li>
<li>Klicken Sie auf <strong>"Erstellen"</strong></li>
<li>
<strong className="text-red-600">Speichern Sie die Credentials sofort!</strong>
<div className="bg-gray-100 p-3 rounded mt-2 font-mono text-xs">
Username: device_10_abc123<br />
Password: ******************
</div>
</li>
</ol>
</Section>
{/* Section 3: Configuration */}
<Section
id="configuration"
title="3. OwnTracks App Konfiguration"
icon="⚙️"
isOpen={openSections["3"]}
onToggle={() => toggleSection("3")}
>
<div className="space-y-6">
<div>
<h4 className="font-bold mb-3">Schritt 1: Zu Einstellungen navigieren</h4>
<ul className="list-disc list-inside text-sm space-y-1">
<li><strong>iOS:</strong> Tippen Sie auf das Symbol (oben rechts)</li>
<li><strong>Android:</strong> Tippen Sie auf (Hamburger-Menü) Einstellungen</li>
</ul>
</div>
<div>
<h4 className="font-bold mb-3">Schritt 2: Modus auswählen</h4>
<p className="text-sm mb-2">Gehen Sie zu <strong>"Verbindung"</strong> oder <strong>"Connection"</strong></p>
<p className="text-sm">Wählen Sie <strong>"Modus"</strong> <strong className="text-green-600">MQTT</strong></p>
</div>
<div>
<h4 className="font-bold mb-3">Schritt 3: Server-Einstellungen</h4>
<ConfigTable />
</div>
<div>
<h4 className="font-bold mb-3">Schritt 4: Authentifizierung</h4>
<table className="w-full text-sm border">
<thead className="bg-gray-100">
<tr>
<th className="border p-2 text-left">Einstellung</th>
<th className="border p-2 text-left">Wert</th>
</tr>
</thead>
<tbody>
<tr>
<td className="border p-2">Benutzername</td>
<td className="border p-2 font-mono text-xs">device_XX_xxxxxxxx</td>
</tr>
<tr>
<td className="border p-2">Passwort</td>
<td className="border p-2 font-mono text-xs">Ihr generiertes Passwort</td>
</tr>
</tbody>
</table>
</div>
<div>
<h4 className="font-bold mb-3">Schritt 5: Device Identifikation</h4>
<div className="bg-red-50 border border-red-200 rounded-lg p-4">
<p className="text-sm font-semibold text-red-800 mb-2"> Wichtig!</p>
<p className="text-sm">Die Device ID und Tracker ID müssen mit der Device-ID übereinstimmen, die Sie im System konfiguriert haben (z.B. <code className="bg-gray-200 px-1 rounded">10</code>, <code className="bg-gray-200 px-1 rounded">12</code>, <code className="bg-gray-200 px-1 rounded">15</code>).</p>
</div>
<table className="w-full text-sm border mt-4">
<thead className="bg-gray-100">
<tr>
<th className="border p-2 text-left">Einstellung</th>
<th className="border p-2 text-left">Wert</th>
</tr>
</thead>
<tbody>
<tr>
<td className="border p-2">Geräte ID / Device ID</td>
<td className="border p-2 font-mono">10</td>
</tr>
<tr>
<td className="border p-2">Tracker ID</td>
<td className="border p-2 font-mono">10</td>
</tr>
</tbody>
</table>
</div>
</div>
</Section>
{/* Section 5: Testing */}
<Section
id="testing"
title="5. Verbindung testen"
icon="✅"
isOpen={openSections["5"]}
onToggle={() => toggleSection("5")}
>
<ol className="list-decimal list-inside space-y-3 text-sm">
<li>
<strong>Verbindung prüfen:</strong> Sie sollten ein <span className="text-green-600 font-semibold">grünes Symbol</span> oder "Connected" sehen
</li>
<li>
<strong>Testpunkt senden:</strong> Tippen Sie auf den Location-Button (Fadenkreuz-Symbol)
</li>
<li>
<strong>Im Location Tracker prüfen:</strong>
<a href="/map" className="text-blue-600 hover:underline ml-1 font-semibold"> Zur Live-Karte</a>
<ul className="list-disc list-inside ml-6 mt-2 space-y-1">
<li>Marker mit Ihrer Device-Farbe</li>
<li>Aktuelle Koordinaten</li>
<li>Zeitstempel der letzten Position</li>
</ul>
</li>
</ol>
</Section>
{/* Section 6: Ports */}
<Section
id="ports"
title="6. Port 1883 vs. 9001 - Was ist der Unterschied?"
icon="🔌"
isOpen={openSections["6"]}
onToggle={() => toggleSection("6")}
>
<PortComparison />
</Section>
{/* Section 7: Troubleshooting */}
<Section
id="troubleshooting"
title="7. Troubleshooting - Häufige Probleme"
icon="🔧"
isOpen={openSections["7"]}
onToggle={() => toggleSection("7")}
>
<TroubleshootingSection />
</Section>
{/* Quick Start Checklist */}
<div className="bg-gradient-to-br from-green-50 to-emerald-50 border-2 border-green-200 rounded-2xl shadow-lg p-6 mt-8">
<h3 className="text-xl font-bold text-gray-900 mb-4 flex items-center gap-2">
<span className="text-2xl"></span>
Schnellstart-Checkliste
</h3>
<ChecklistItems />
</div>
{/* Support Section */}
<div className="bg-gradient-to-br from-slate-50 to-gray-50 border-2 border-gray-200 rounded-2xl shadow-lg p-6 mt-8">
<h3 className="text-xl font-bold text-gray-900 mb-4 flex items-center gap-2">
<span className="text-2xl">📞</span>
Weiterführende Informationen
</h3>
<div className="grid md:grid-cols-2 gap-4 text-sm">
<div>
<h4 className="font-semibold mb-2">OwnTracks Dokumentation:</h4>
<ul className="space-y-1">
<li><a href="https://owntracks.org" target="_blank" rel="noopener noreferrer" className="text-blue-600 hover:underline"> Website</a></li>
<li><a href="https://owntracks.org/booklet/" target="_blank" rel="noopener noreferrer" className="text-blue-600 hover:underline"> Dokumentation</a></li>
</ul>
</div>
<div>
<h4 className="font-semibold mb-2">Location Tracker System:</h4>
<ul className="space-y-1">
<li><a href="/admin" className="text-blue-600 hover:underline"> Dashboard</a></li>
<li><a href="/map" className="text-blue-600 hover:underline"> Live-Karte</a></li>
<li><a href="/admin/mqtt" className="text-blue-600 hover:underline"> MQTT Provisioning</a></li>
</ul>
</div>
</div>
</div>
</div>
</div>
);
}
// Section Component
function Section({
id,
title,
icon,
isOpen,
onToggle,
children,
}: {
id: string;
title: string;
icon: string;
isOpen: boolean;
onToggle: () => void;
children: React.ReactNode;
}) {
return (
<div id={id} className="bg-white rounded-2xl shadow-lg overflow-hidden mb-6 border border-gray-200">
<button
onClick={onToggle}
className="flex items-center justify-between w-full text-left bg-gradient-to-r from-gray-50 to-slate-50 hover:from-blue-50 hover:to-indigo-50 px-6 py-5 transition-all"
>
<h2 className="text-2xl font-bold text-gray-900 flex items-center gap-3">
<span className="text-3xl">{icon}</span>
{title}
</h2>
<span className={`text-3xl font-bold transition-transform ${isOpen ? 'rotate-180' : ''}`}>
{isOpen ? "" : "+"}
</span>
</button>
{isOpen && <div className="p-6 bg-white border-t border-gray-200">{children}</div>}
</div>
);
}
// Config Table Component
function ConfigTable() {
return (
<table className="w-full text-sm border">
<thead className="bg-gray-100">
<tr>
<th className="border p-2 text-left">Einstellung</th>
<th className="border p-2 text-left">Wert</th>
<th className="border p-2 text-left">Hinweis</th>
</tr>
</thead>
<tbody>
<tr>
<td className="border p-2">Hostname</td>
<td className="border p-2 font-mono">192.168.10.118</td>
<td className="border p-2 text-gray-600">IP-Adresse des Servers</td>
</tr>
<tr>
<td className="border p-2">Port</td>
<td className="border p-2 font-mono">1883</td>
<td className="border p-2 text-gray-600">Standard MQTT Port</td>
</tr>
<tr className="bg-red-50">
<td className="border p-2">Websockets nutzen</td>
<td className="border p-2 font-bold text-red-600"> DEAKTIVIERT</td>
<td className="border p-2 text-gray-600">Nur bei Port 9001!</td>
</tr>
<tr className="bg-red-50">
<td className="border p-2">TLS</td>
<td className="border p-2 font-bold text-red-600"> DEAKTIVIERT</td>
<td className="border p-2 text-gray-600">Lokales Netzwerk</td>
</tr>
<tr>
<td className="border p-2">Client ID</td>
<td className="border p-2 text-gray-500">Automatisch</td>
<td className="border p-2 text-gray-600">Kann leer bleiben</td>
</tr>
</tbody>
</table>
);
}
// Port Comparison Component
function PortComparison() {
return (
<div className="grid md:grid-cols-2 gap-6">
<div className="border-2 border-green-400 rounded-xl p-6 bg-gradient-to-br from-green-50 to-emerald-50 shadow-lg hover:shadow-xl transition-shadow">
<h4 className="font-bold text-lg mb-3 text-green-800">Port 1883 (Standard MQTT)</h4>
<ul className="space-y-2 text-sm">
<li> <strong>Protokoll:</strong> Standard MQTT (TCP)</li>
<li> <strong>Verwendung:</strong> Mobile Apps, IoT-Geräte</li>
<li> <strong>Websockets:</strong> Nein</li>
<li className="mt-3 pt-3 border-t border-green-300">
<strong>Empfohlen für OwnTracks App!</strong>
</li>
</ul>
<div className="mt-4 bg-white p-2 rounded text-xs font-mono">
Port: 1883<br />
Websockets: DEAKTIVIERT
</div>
</div>
<div className="border-2 border-blue-400 rounded-xl p-6 bg-gradient-to-br from-blue-50 to-indigo-50 shadow-lg hover:shadow-xl transition-shadow">
<h4 className="font-bold text-lg mb-3 text-blue-800">Port 9001 (MQTT over WebSockets)</h4>
<ul className="space-y-2 text-sm">
<li> <strong>Protokoll:</strong> MQTT über WebSocket</li>
<li> <strong>Verwendung:</strong> Browser, Web-Apps</li>
<li> <strong>Websockets:</strong> Ja</li>
<li className="mt-3 pt-3 border-t border-blue-300">
<strong>Für Web-Anwendungen</strong>
</li>
</ul>
<div className="mt-4 bg-white p-2 rounded text-xs font-mono">
Port: 9001<br />
Websockets: AKTIVIERT
</div>
</div>
</div>
);
}
// Troubleshooting Component
function TroubleshootingSection() {
return (
<div className="space-y-4">
<TroubleshootingItem
problem="Verbindung fehlgeschlagen"
solutions={[
"Überprüfen Sie Hostname (192.168.10.118) und Port (1883)",
"Stellen Sie sicher, dass Smartphone im selben Netzwerk ist",
"Deaktivieren Sie TLS/SSL",
"Deaktivieren Sie Websockets bei Port 1883",
"Prüfen Sie Username und Passwort",
]}
/>
<TroubleshootingItem
problem="Verbunden, aber keine Daten auf der Karte"
solutions={[
"Device ID und Tracker ID müssen übereinstimmen",
"Standortberechtigungen 'Immer' erteilen",
"Akkuoptimierung deaktivieren (Android)",
]}
/>
<TroubleshootingItem
problem="Tracking stoppt im Hintergrund"
solutions={[
"iOS: Hintergrundaktualisierung aktivieren",
"iOS: Standortzugriff auf 'Immer' setzen",
"Android: Akkuoptimierung deaktivieren",
"Android: Standort 'Immer zulassen'",
]}
/>
</div>
);
}
function TroubleshootingItem({ problem, solutions }: { problem: string; solutions: string[] }) {
return (
<div className="bg-gradient-to-br from-red-50 to-orange-50 border-2 border-red-200 rounded-xl p-5 shadow-md hover:shadow-lg transition-shadow">
<h4 className="font-bold text-red-700 mb-3 text-lg flex items-center gap-2">
<span className="text-xl"></span>
{problem}
</h4>
<ul className="space-y-1 text-sm">
{solutions.map((solution, i) => (
<li key={i} className="flex items-start gap-2">
<span className="text-green-600 font-bold"></span>
<span>{solution}</span>
</li>
))}
</ul>
</div>
);
}
// Checklist Items Component
function ChecklistItems() {
const items = [
"OwnTracks App installiert",
"MQTT Credentials generiert und gespeichert",
"Modus auf MQTT gesetzt",
"Hostname: 192.168.10.118 eingetragen",
"Port: 1883 eingetragen",
"Websockets: ❌ Deaktiviert",
"TLS: ❌ Deaktiviert",
"Benutzername und Passwort eingetragen",
"Device ID und Tracker ID korrekt gesetzt",
"Standortberechtigungen 'Immer' erteilt",
"Akkuoptimierung deaktiviert (Android)",
"Verbindung erfolgreich (grünes Symbol)",
"Position auf Karte sichtbar",
];
return (
<ul className="space-y-2 text-sm">
{items.map((item, i) => (
<li key={i} className="flex items-start gap-2">
<input type="checkbox" className="mt-1" />
<span>{item}</span>
</li>
))}
</ul>
);
}