+
+ {[...mainLinks, ...dropdownLinks].map((link) => (
setIsMobileMenuOpen(false)}
className={cn(
- "text-lg font-bold uppercase flex items-center gap-3 p-3 rounded-lg transition-colors",
+ "text-sm font-black uppercase tracking-widest flex items-center gap-4 p-4 rounded-2xl transition-all",
location.pathname === link.href
- ? "text-primary bg-primary/10"
- : "text-foreground/80 hover:text-white hover:bg-white/5"
+ ? "text-primary bg-primary/10 border border-primary/20"
+ : "text-foreground/80 hover:text-white hover:bg-white/5 border border-transparent"
)}
>
- {link.icon &&
}
+
{link.label}
-
- ))}
-
+
+
-
+
Bot einladen
-
+
)}
);
-}
\ No newline at end of file
+}
diff --git a/src/web/components/SEO.tsx b/src/web/components/SEO.tsx
new file mode 100644
index 0000000..51419a9
--- /dev/null
+++ b/src/web/components/SEO.tsx
@@ -0,0 +1,41 @@
+import { Helmet } from 'react-helmet-async';
+
+interface SEOProps {
+ title?: string;
+ description?: string;
+ image?: string;
+ url?: string;
+ type?: string;
+}
+
+export function SEO({
+ title = "ManagerX - Discord Bot",
+ description = "ManagerX - Moderation, Levelsystem, Globalchat und mehr für deinen Discord Server",
+ image = "https://managerx.bot/logo.png", // Ensure absolute URL for social images
+ url = "https://managerx.bot",
+ type = "website"
+}: SEOProps) {
+ const fullTitle = title === "ManagerX - Discord Bot" ? title : `${title} | ManagerX`;
+
+ return (
+
+ {/* Search Engine */}
+ {fullTitle}
+
+
+
+ {/* Open Graph / Facebook */}
+
+
+
+
+
+
+ {/* Twitter */}
+
+
+
+
+
+ );
+}
diff --git a/src/web/components/Testimonials.tsx b/src/web/components/Testimonials.tsx
index 301be46..99d242b 100644
--- a/src/web/components/Testimonials.tsx
+++ b/src/web/components/Testimonials.tsx
@@ -1,6 +1,7 @@
import { memo } from "react";
import { motion } from "framer-motion";
-import { Star, MessageSquare, Users, TrendingUp } from "lucide-react";
+import { Star, Users, TrendingUp } from "lucide-react";
+import { useStats } from "../hooks/useStats";
const testimonials = [
{
@@ -52,7 +53,7 @@ const TestimonialCard = memo(({ testimonial, index }: { testimonial: typeof test
-
@@ -75,6 +76,8 @@ const TestimonialCard = memo(({ testimonial, index }: { testimonial: typeof test
));
export const Testimonials = memo(function Testimonials() {
+ const { data: stats } = useStats();
+
return (
@@ -89,7 +92,7 @@ export const Testimonials = memo(function Testimonials() {
transition={{ duration: 0.7, ease: "easeInOut" }}
className="text-center mb-20"
>
- Early Access Feedback
-
Stimmen der Ersten Stunde
-
-
16
+
+ {stats.guilds > 0 ? stats.guilds : "16"}
+
Aktive Server
-
~300
+
+ {stats.users > 0 ? `~${stats.users}` : "~300"}
+
Nutzer
diff --git a/src/web/hooks/useStats.ts b/src/web/hooks/useStats.ts
new file mode 100644
index 0000000..f6186d0
--- /dev/null
+++ b/src/web/hooks/useStats.ts
@@ -0,0 +1,59 @@
+import { useState, useEffect } from "react";
+
+interface StatsData {
+ uptime: string;
+ latency: string;
+ guilds: number;
+ users: number;
+ bot_name: string;
+ bot_id: number | null;
+ status: string;
+ database: string;
+}
+
+export const useStats = () => {
+ const [data, setData] = useState
({
+ uptime: "--",
+ latency: "--",
+ guilds: 0,
+ users: 0,
+ bot_name: "ManagerX",
+ bot_id: null,
+ status: "loading",
+ database: "disconnected"
+ });
+
+ const [isLoading, setIsLoading] = useState(true);
+
+ useEffect(() => {
+ const fetchStats = async () => {
+ try {
+ const response = await fetch("https://api.managerx-bot.de/v1/managerx/stats");
+ if (!response.ok) throw new Error("Offline");
+
+ const result = await response.json();
+ setData({
+ uptime: result.uptime || "--",
+ latency: result.latency || "--",
+ guilds: result.guilds || 0,
+ users: result.users || 0,
+ bot_name: result.bot_name || "ManagerX",
+ bot_id: result.bot_id || null,
+ status: result.status || "online",
+ database: result.database || "disconnected"
+ });
+ setIsLoading(false);
+ } catch (error) {
+ setData((prev) => ({ ...prev, status: "offline", latency: "--", database: "disconnected" }));
+ setIsLoading(false);
+ }
+ };
+
+ fetchStats();
+ // Optional: Alle 30 Sekunden aktualisieren
+ const interval = setInterval(fetchStats, 30000);
+ return () => clearInterval(interval);
+ }, []);
+
+ return { data, isLoading };
+};
diff --git a/src/web/index.html b/src/web/index.html
index d39b8bd..40e0061 100644
--- a/src/web/index.html
+++ b/src/web/index.html
@@ -4,6 +4,19 @@
ManagerX - Discord Bot
+
diff --git a/src/web/main.tsx b/src/web/main.tsx
index 1353fb7..1ac9578 100644
--- a/src/web/main.tsx
+++ b/src/web/main.tsx
@@ -1,10 +1,13 @@
import React from "react";
import { createRoot } from "react-dom/client";
+import { HelmetProvider } from "react-helmet-async";
import App from "./App.tsx";
import "./index.css";
createRoot(document.getElementById("root")!).render(
-
+
+
+
);
diff --git a/src/web/pages/CommandsPage.tsx b/src/web/pages/CommandsPage.tsx
new file mode 100644
index 0000000..b78ffc6
--- /dev/null
+++ b/src/web/pages/CommandsPage.tsx
@@ -0,0 +1,244 @@
+import { memo, useState, useMemo } from "react";
+import {
+ Search, ChevronRight, Terminal, Info, Filter, Sparkles
+} from "lucide-react";
+import { Navbar } from "../components/Navbar";
+import { Footer } from "../components/Footer";
+import { motion, AnimatePresence } from "framer-motion";
+import { Input } from "../components/ui/input";
+import { Badge } from "../components/ui/badge";
+import { cn } from "../lib/utils";
+import { CATEGORIES, COMMANDS, Command } from "../data/commands";
+import { SEO } from "../components/SEO";
+
+import { useRef } from "react";
+import { useInView } from "framer-motion";
+
+const CommandCard = memo(({ command }: { command: Command }) => {
+ const ref = useRef(null);
+ const isInView = useInView(ref, { once: true, margin: "200px" });
+
+ return (
+
+ {isInView && (
+
+
+
+
+
+
+ {command.badges.map((badge, i) => (
+
+ {badge}
+
+ ))}
+
+
+
+ /
+ {command.name}
+
+
+
+ {command.description}
+
+
+
+
Usage
+
+ {command.usage}
+
+
+
+ )}
+
+ );
+});
+
+export const CommandsPage = memo(function CommandsPage() {
+ const [search, setSearch] = useState("");
+ const [activeCategory, setActiveCategory] = useState("all");
+
+ const filteredCommands = useMemo(() => {
+ return COMMANDS.filter(cmd => {
+ const matchesSearch = cmd.name.toLowerCase().includes(search.toLowerCase()) ||
+ cmd.description.toLowerCase().includes(search.toLowerCase());
+ const matchesCategory = activeCategory === "all" || cmd.category === activeCategory;
+ return matchesSearch && matchesCategory;
+ });
+ }, [search, activeCategory]);
+
+ const groupedCommands = useMemo(() => {
+ const groups: { [key: string]: Command[] } = {};
+ filteredCommands.forEach(cmd => {
+ if (!groups[cmd.category]) groups[cmd.category] = [];
+ groups[cmd.category].push(cmd);
+ });
+ return groups;
+ }, [filteredCommands]);
+
+ const availableCategories = useMemo(() => {
+ const usedIds = new Set(COMMANDS.map(cmd => cmd.category));
+ return CATEGORIES.filter(cat => cat.id === "all" || usedIds.has(cat.id));
+ }, []);
+
+ const visibleCategories = useMemo(() => {
+ return availableCategories.filter(cat => cat.id !== "all" && groupedCommands[cat.id]);
+ }, [availableCategories, groupedCommands]);
+
+ return (
+
+
+
+
+
+ {/* Header Section */}
+
+
+
+ Command Reference
+
+
+ Master the Commands
+
+
+ Entdecke alle verfügbaren Slash-Commands für ManagerX. Von Moderation bis hin zu AI-Games – alles auf einen Blick.
+
+
+ {/* Search Bar */}
+
+
+
+
+
+
+ setSearch(e.target.value)}
+ className="w-full h-16 pl-16 pr-6 bg-[#111318] border-white/10 rounded-[1.5rem] focus:ring-primary focus:border-primary text-white text-lg font-bold placeholder:text-slate-600 transition-all shadow-2xl"
+ />
+
+
+
+
+ {/* Category Filters */}
+
+ {availableCategories.map((cat) => {
+ const Icon = cat.icon;
+ return (
+ setActiveCategory(cat.id)}
+ className={cn(
+ "flex items-center gap-3 px-6 py-3 rounded-2xl transition-all duration-300 font-bold uppercase tracking-widest text-[11px] border shadow-lg hover:scale-105",
+ activeCategory === cat.id
+ ? "bg-primary/20 text-primary border-primary/30 shadow-primary/10"
+ : "bg-white/5 text-slate-400 border-white/5 hover:border-white/10 hover:text-white"
+ )}
+ >
+
+ {cat.title}
+
+ );
+ })}
+
+
+ {/* Grouped Commands Content */}
+
+
+ {visibleCategories.length > 0 ? (
+ visibleCategories.map((cat) => {
+ const Icon = cat.icon;
+ return (
+
+ {/* Category Title Header */}
+
+
+ {/* Commands Grid and Category */}
+
+ {groupedCommands[cat.id].map((command) => (
+
+ ))}
+
+
+ );
+ })
+ ) : (
+
+
+ Keine Befehle gefunden
+ Versuch es mit einem anderen Suchbegriff oder einer anderen Kategorie.
+
+ )}
+
+
+
+ {/* Help Banner */}
+
+
+
+ Brauchst du mehr Hilfe?
+
+ Nutze /help direkt in Discord für detaillierte Anleitungen zu jedem einzelnen Befehl.
+
+
+
+
+
+
+
+ );
+});
+
+export default CommandsPage;
diff --git a/src/web/pages/Datenschutz.tsx b/src/web/pages/Datenschutz.tsx
index 45b1436..7cd3b0a 100644
--- a/src/web/pages/Datenschutz.tsx
+++ b/src/web/pages/Datenschutz.tsx
@@ -197,7 +197,7 @@ export const Datenschutz = memo(function Datenschutz() {
- Berechnung von Level-Fortschritten.
+ Berechnung von Level-Fortschritten (XP) und damit verbundene automatisierte Zuweisungen (z.B. Level-Rollen).
@@ -261,7 +261,8 @@ export const Datenschutz = memo(function Datenschutz() {
- ManagerX trifft keine vollautomatisierten Entscheidungen nach Art. 22 DSGVO.
+ ManagerX trifft keine vollautomatisierten Entscheidungen im Sinne des Art. 22 DSGVO, die Ihnen gegenüber rechtliche Wirkung entfalten oder Sie in ähnlicher Weise erheblich beeinträchtigen.
+ Der Bot führt jedoch funktionale Automatisierungen durch, wie etwa die automatische Zuweisung von Community-Rollen basierend auf Ihrer Aktivität (Level-System) oder das automatische Löschen von Nachrichten gemäß den Server-Einstellungen.
diff --git a/src/web/pages/Impressum.tsx b/src/web/pages/Impressum.tsx
index 5cae37c..db7e639 100644
--- a/src/web/pages/Impressum.tsx
+++ b/src/web/pages/Impressum.tsx
@@ -6,6 +6,7 @@ import {
} from "lucide-react";
import { Navbar } from "../components/Navbar";
import { Footer } from "../components/Footer";
+import { SEO } from "../components/SEO";
import { motion } from "framer-motion";
const SECTIONS = [
diff --git a/src/web/pages/Index.tsx b/src/web/pages/Index.tsx
index f24db04..ae54198 100644
--- a/src/web/pages/Index.tsx
+++ b/src/web/pages/Index.tsx
@@ -1,14 +1,19 @@
-import { Navbar } from "@/components/Navbar";
-import { Hero } from "@/components/Hero";
-import { Features } from "@/components/Features";
-import { Testimonials } from "@/components/Testimonials";
-import { FAQ } from "@/components/FAQ";
-import { CTA } from "@/components/CTA";
-import { Footer } from "@/components/Footer";
+import { Navbar } from "../components/Navbar";
+import { Hero } from "../components/Hero";
+import { Features } from "../components/Features";
+import { Testimonials } from "../components/Testimonials";
+import { FAQ } from "../components/FAQ";
+import { CTA } from "../components/CTA";
+import { Footer } from "../components/Footer";
+import { SEO } from "../components/SEO";
const Index = () => {
return (
+
diff --git a/src/web/pages/PluginsPage.tsx b/src/web/pages/PluginsPage.tsx
index ea4b917..4da6372 100644
--- a/src/web/pages/PluginsPage.tsx
+++ b/src/web/pages/PluginsPage.tsx
@@ -8,6 +8,7 @@ import {
} from "lucide-react";
import { Navbar } from "../components/Navbar";
import { Footer } from "../components/Footer";
+import { SEO } from "../components/SEO";
import { motion } from "framer-motion";
const SECTIONS = [
@@ -98,8 +99,8 @@ export const PluginsPage = memo(function PluginsPage() {
key={section.id}
onClick={() => scrollToSection(section.id)}
className={`w-full flex items-center gap-3 px-4 py-3 rounded-xl transition-all duration-300 group text-sm font-semibold border ${activeSection === section.id
- ? "bg-primary/10 text-primary border-primary/20 shadow-lg shadow-primary/5"
- : "text-slate-400 hover:text-slate-200 hover:bg-white/5 border-transparent"
+ ? "bg-primary/10 text-primary border-primary/20 shadow-lg shadow-primary/5"
+ : "text-slate-400 hover:text-slate-200 hover:bg-white/5 border-transparent"
}`}
>
{
+ const configs: { [key: string]: { label: string, color: string, icon: any } } = {
+ completed: { label: "Abgeschlossen", color: "text-emerald-500 bg-emerald-500/10 border-emerald-500/20", icon: CheckCircle2 },
+ "in-progress": { label: "In Arbeit", color: "text-primary bg-primary/10 border-primary/20", icon: CircleDashed },
+ planned: { label: "Geplant", color: "text-slate-500 bg-white/5 border-white/10", icon: Clock }
+ };
+ const config = configs[status] || configs.planned;
+ const Icon = config.icon;
+
+ return (
+
+
+ {config.label}
+
+ );
+};
+
+export const RoadmapPage = memo(function RoadmapPage() {
+ return (
+
+
+
+
+
+
+
+
+ {/* Timeline Connector */}
+
+
+
+ {ROADMAP_ITEMS.map((item, idx) => {
+ const Icon = item.icon;
+ const isEven = idx % 2 === 0;
+
+ return (
+
+ {/* Icon Node */}
+
+
+ {/* Content Card */}
+
+
+
+
+ {item.date}
+
+
{item.title}
+
+ {item.description}
+
+
+
+
+ {/* Spacer for MD screens to keep alignment */}
+
+
+ );
+ })}
+
+
+
+
+
+
+ Hast du eigene Ideen?
+
+ ManagerX wird für euch entwickelt. Schlag uns neue Features auf unserem Support-Server vor!
+
+
+ Feedback geben
+
+
+
+
+
+
+ );
+});
+
+export default RoadmapPage;
diff --git a/src/web/pages/Status.tsx b/src/web/pages/Status.tsx
index 1eec302..0b3e457 100644
--- a/src/web/pages/Status.tsx
+++ b/src/web/pages/Status.tsx
@@ -6,6 +6,7 @@ import {
} from "lucide-react";
import { Navbar } from "../components/Navbar";
import { Footer } from "../components/Footer";
+import { SEO } from "../components/SEO";
import { motion } from "framer-motion";
const Status = memo(function Status() {
// State für die Live-Daten vom Bot
diff --git a/src/web/pages/TeamPage.tsx b/src/web/pages/TeamPage.tsx
new file mode 100644
index 0000000..03ebff6
--- /dev/null
+++ b/src/web/pages/TeamPage.tsx
@@ -0,0 +1,132 @@
+import { memo } from "react";
+import { motion } from "framer-motion";
+import { Navbar } from "../components/Navbar";
+import { Footer } from "../components/Footer";
+import { Github, Twitter, Globe, Heart, Shield, Code2, Sparkles, Coffee, Youtube, Instagram } from "lucide-react";
+import { TEAM_MEMBERS } from "../data/team";
+import { SEO } from "../components/SEO";
+
+export const TeamPage = memo(function TeamPage() {
+ return (
+
+
+
+
+
+
+
+
+ {TEAM_MEMBERS.map((member, idx) => (
+
+
+
+
+ {member.avatar}
+
+
+
{member.name}
+
{member.role}
+
+ {member.bio}
+
+
+
+
+
+ {member.youtube && (
+
+
+
+ )}
+ {member.instagram && (
+
+
+
+ )}
+ {member.website && (
+
+
+
+ )}
+
+
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+
Sicherheit
+
Datenschutz steht bei uns an erster Stelle. Jede Zeile Code wird auf Sicherheit geprüft.
+
+
+
+
+
+
Open Source
+
Wir glauben an Transparenz. Fast unser gesamtes Ökosystem ist für jeden einsehbar.
+
+
+
+
+
+
Innovation
+
Ständig am Puls der Zeit mit neuen Features wie AI-gestützten Moderations-Tools.
+
+
+
+
+
+
+
+ Willst du beitragen?
+ Wir freuen uns immer über Hilfe von GitHub-Entwicklern oder kreatives Feedback.
+
+ GitHub Repository
+
+
+
+
+
+
+
+ );
+});
+
+export default TeamPage;
diff --git a/vite.config.ts b/vite.config.ts
index 704e517..7c10f20 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -21,8 +21,7 @@ export default defineConfig(({ mode }) => ({
publicDir: "../../public",
resolve: {
alias: {
- // Dein Alias zeigt auf . (jetzt src/web)
- "@": path.resolve(__dirname, "src/web"),
+ "@": path.resolve(__dirname, "./src/web"),
},
},
build: {