Source: frontend/src/pages/admin/AdminDashboard.jsx

/**
 * @file AdminDashboard.jsx - Administrator-Verwaltungsoberfläche
 * @author Miray
 * @description Diese Komponente stellt die Hauptverwaltungsoberfläche für Administratoren dar.
 * Sie ermöglicht die Verwaltung von Benutzern und das Monitoring von Datenbank-Aktivitäten.
 * 
 * @requires react
 * @requires sweetalert2
 * @requires ../../production-config
 */

import React, { useEffect, useState } from "react";
import Swal from "sweetalert2";
import prodconfig from "../../production-config";

/**
 * @component AdminDashboard
 * @description Hauptkomponente für die Administrator-Verwaltungsoberfläche
 * 
 * @example
 * return (
 *   <AdminDashboard />
 * )
 */
/**
 * @typedef {Object} User
 * @property {number} user_id - Eindeutige Benutzer-ID
 * @property {string} user_name - Benutzername
 * @property {string} email - E-Mail-Adresse des Benutzers
 * @property {boolean} is_verified - Verifizierungsstatus
 * @property {Date} registered_at - Registrierungsdatum
 */
/**
 * @typedef {Object} DBSession
 * @property {string} datname - Name der Datenbank
 * @property {string} usename - Datenbankbenutzer
 * @property {string} state - Aktueller Status
 * @property {string} query - Aktuelle Abfrage
 * @property {string} query_start - Startzeitpunkt der Abfrage
 */
/**
 * @typedef {Object} DBStats
 * @property {string} datname - Name der Datenbank
 * @property {number} active_connections - Anzahl aktiver Verbindungen
 * @property {number} committed_transactions - Anzahl erfolgreich abgeschlossener Transaktionen
 * @property {number} rolledback_transactions - Anzahl zurückgerollter Transaktionen
 * @property {number} blocks_read - Anzahl gelesener Blöcke
 * @property {number} blocks_hit - Cache-Treffer
 */
/**
 * State-Hooks der Komponente
 * @type {Object}
 * @property {User[]} users - Liste aller Benutzer
 * @property {User|null} editingUser - Aktuell bearbeiteter Benutzer
 * @property {string} newEmail - Neue E-Mail-Adresse bei Bearbeitung
 * @property {string} newPassword - Neues Passwort bei Bearbeitung
 * @property {DBSession[]} dbSessions - Aktive Datenbank-Sitzungen
 * @property {DBStats[]} dbStats - Datenbankstatistiken
 * @property {boolean} isMonitoringLoading - Ladezustand des Monitorings
 * @property {Set<number>} adminUserIds - Set der Admin-Benutzer-IDs
 */
const AdminDashboard = () => {
  const [users, setUsers] = useState([]);
  const [editingUser, setEditingUser] = useState(null);
  const [newEmail, setNewEmail] = useState("");
  const [newPassword, setNewPassword] = useState("");
  const [dbSessions, setDbSessions] = useState([]);
  const [dbStats, setDbStats] = useState([]);
  const [isMonitoringLoading, setIsMonitoringLoading] = useState(false);
  const [adminUserIds, setAdminUserIds] = useState(new Set());

  useEffect(() => {
    fetchAdminUserIds();
  }, []);
  
/**
 * @function fetchAdminUserIds
 * @async
 * @description Ruft die IDs aller Admin-Benutzer vom Server ab
 * @throws {Error} Wenn die Anfrage fehlschlägt
 */
  const fetchAdminUserIds = async () => {
    try {
      const response = await fetch(
        `${prodconfig.backendUrl}/api/admin/admin-roles`,
        { credentials: "include" }
      );
      const data = await response.json();
      setAdminUserIds(new Set(data.adminUserIds)); 
    } catch (error) {
      console.error("Fehler beim Abrufen der Admin-Benutzer:", error);
    }
  };

  useEffect(() => {
    fetch(`${prodconfig.backendUrl}/api/admin/users`, { credentials: "include" })
      .then((response) => response.json())
      .then((data) => setUsers(data))
      .catch((error) => console.error("Fehler beim Abrufen der Benutzer:", error));

    fetchDbSessions();
    fetchDbStats();
  }, []);

  /**
 * @function fetchDbSessions
 * @async
 * @description Ruft aktuelle Datenbank-Sitzungsinformationen ab
 * @throws {Error} Wenn die Abfrage fehlschlägt
 */
  const fetchDbSessions = async () => {
    setIsMonitoringLoading(true);
    try {
      const response = await fetch(`${prodconfig.backendUrl}/monitor/db-sessions`, { credentials: "include" });
      const data = await response.json();
      setDbSessions(data);
    } catch (error) {
      console.error("Fehler beim Abrufen der DB-Sitzungen:", error);
    } finally {
      setIsMonitoringLoading(false);
    }
  };

  /**
 * @function fetchDbStats
 * @async
 * @description Ruft Datenbankstatistiken ab
 * @throws {Error} Wenn die Abfrage fehlschlägt
 */
  const fetchDbStats = async () => {
    setIsMonitoringLoading(true);
    try {
      const response = await fetch(`${prodconfig.backendUrl}/monitor/db-stats`, { credentials: "include" });
      const data = await response.json();
      setDbStats(data);
    } catch (error) {
      console.error("Fehler beim Abrufen der DB-Statistiken:", error);
    } finally {
      setIsMonitoringLoading(false);
    }
  };

  /**
 * @function deleteUser
 * @async
 * @param {number} id - ID des zu löschenden Benutzers
 * @description Löscht einen Benutzer nach Bestätigung
 * @throws {Error} Wenn das Löschen fehlschlägt
 */
  const deleteUser = async (id) => {
    const confirmed = await Swal.fire({
      title: "Benutzer löschen?",
      text: "Dieser Vorgang kann nicht rückgängig gemacht werden!",
      icon: "warning",
      showCancelButton: true,
      confirmButtonText: "Ja, löschen!",
      cancelButtonText: "Abbrechen",
    });

    if (confirmed.isConfirmed) {
      try {
        await fetch(`${prodconfig.backendUrl}/api/admin/users/${id}`, { method: "DELETE", credentials: "include" });
        setUsers(users.filter((user) => user.user_id !== id));
        Swal.fire("Gelöscht!", "Der Benutzer wurde erfolgreich gelöscht.", "success");
      } catch (error) {
        Swal.fire("Fehler", "Benutzer konnte nicht gelöscht werden.", "error");
        console.error("Fehler beim Löschen des Benutzers:", error);
      }
    }
  };

  /**
 * @function assignAdmin
 * @async
 * @param {number} id - ID des Benutzers, der Admin-Rechte erhalten soll
 * @description Weist einem Benutzer Admin-Rechte zu
 * @throws {Error} Wenn die Rechtezuweisung fehlschlägt
 */
  const assignAdmin = async (id) => {
    const user = users.find(u => u.user_id === id);
  
    if (user?.roles?.some(role => role.role_name === 'admin')) {
      Swal.fire("Info", "Dieser Benutzer ist bereits Admin.", "info");
      return;
    }
  
    const confirmed = await Swal.fire({
      title: "Sind Sie sicher?",
      text: "Dieser Benutzer wird Admin-Rechte erhalten.",
      icon: "warning",
      showCancelButton: true,
      confirmButtonText: "Ja, befördern!",
      cancelButtonText: "Abbrechen",
    });
  
    if (!confirmed.isConfirmed) return;
  
    try {
      const response = await fetch(`${prodconfig.backendUrl}/api/admin/users/${id}/assign-admin`, {
        method: "POST",
        credentials: "include",
      });
  
      if (!response.ok) {
        const errorData = await response.json();
        throw new Error(errorData.message || "Fehler beim Zuweisen der Admin-Rechte.");
      }
  
      await Swal.fire("Erfolg", "Admin-Rechte erfolgreich zugewiesen.", "success");
  
      // Seite neu laden
      window.location.reload();
    } catch (error) {
      Swal.fire("Fehler", error.message, "error");
      console.error("Fehler beim Zuweisen der Admin-Rechte:", error);
    }
  };
  
/**
 * @function updateUser
 * @async
 * @description Aktualisiert die Benutzerdaten des ausgewählten Benutzers
 * @throws {Error} Wenn die Aktualisierung fehlschlägt
 */
  const updateUser = async () => {
    try {
      const response = await fetch(`${prodconfig.backendUrl}/api/admin/users/${editingUser.user_id}`, {
        method: "PUT",
        headers: { "Content-Type": "application/json" },
        credentials: "include",
        body: JSON.stringify({ email: newEmail, password: newPassword }),
      });

      if (!response.ok) {
        const errorData = await response.json();
        throw new Error(errorData.message || "Fehler beim Aktualisieren.");
      }

      setUsers((prevUsers) =>
        prevUsers.map((user) =>
          user.user_id === editingUser.user_id ? { ...user, email: newEmail } : user
        )
      );

      Swal.fire("Erfolg", "Benutzer erfolgreich aktualisiert.", "success");
      setEditingUser(null);
      setNewEmail("");
      setNewPassword("");
    } catch (error) {
      console.error("Fehler beim Aktualisieren des Benutzers:", error);
    }
  };

  return (
    <div className="dashboard">
      <div className="shadow-sm bg-white border-y border-slate-200">
        <div className="flex items-center justify-between py-2 px-3">
          <h1 className="text-lg font-semibold text-black">Admin-Dashboard</h1>
          <button
            onClick={() => { window.location.href = "/dashboard"; }}
            className="text-white bg-red-500 hover:bg-red-600 px-4 py-2 rounded-md"
          >
            Zurück zum Dashboard
          </button>
        </div>
      </div>

      <div className="mt-16 px-4">
        <h2 className="text-xl font-semibold mb-4">Benutzerverwaltung</h2>
        <table className="w-full text-black border-collapse border border-slate-300">
          <thead>
            <tr>
              <th className="border border-slate-300 px-4 py-2">ID</th>
              <th className="border border-slate-300 px-4 py-2">Benutzername</th>
              <th className="border border-slate-300 px-4 py-2">E-Mail</th>
              <th className="border border-slate-300 px-4 py-2">Verifiziert</th>
              <th className="border border-slate-300 px-4 py-2">Registriert am</th>
              <th className="border border-slate-300 px-4 py-2">Admin</th>
              <th className="border border-slate-300 px-4 py-2">Aktionen</th>
            </tr>
          </thead>
          <tbody>
            {users.map((user) => (
              <tr key={user.user_id}>
                <td className="border border-slate-300 px-4 py-2">{user.user_id}</td>
                <td className="border border-slate-300 px-4 py-2">{user.user_name}</td>
                <td className="border border-slate-300 px-4 py-2">{user.email}</td>
                <td className="border border-slate-300 px-4 py-2">
                  {user.is_verified ? "Ja" : "Nein"}
                </td>
                <td className="border border-slate-300 px-4 py-2">
                  {user.registered_at
                    ? new Date(user.registered_at).toLocaleDateString("de-DE")
                    : "Kein Datum"}
                </td>
                <td className="border border-slate-300 px-4 py-2">
                  {adminUserIds.has(user.user_id) ? "Ja" : "Nein"}
                </td>
                <td className="border border-slate-300 px-4 py-2 flex gap-2">
                  <button
                    className={`px-3 py-1 rounded ${adminUserIds.has(user.user_id)
                        ? "bg-gray-200 text-gray-600 cursor-not-allowed border border-gray-300"
                        : "bg-[#669f62] hover:bg-[#68b461] text-white"
                      }`}
                    onClick={() => !adminUserIds.has(user.user_id) && assignAdmin(user.user_id)}
                    disabled={adminUserIds.has(user.user_id)}
                    title={
                      adminUserIds.has(user.user_id)
                        ? "Benutzer ist bereits Admin"
                        : "Zum Admin befördern"
                    }
                  >
                    {adminUserIds.has(user.user_id) ? "Bereits Admin" : "Admin-Rechte zuweisen"}
                  </button>

                  <button
                    className="bg-red-500 text-white px-3 py-1 rounded hover:bg-red-600"
                    onClick={() => deleteUser(user.user_id)}
                  >
                    Löschen
                  </button>
                  <button
                    className="bg-blue-500 text-white px-3 py-1 rounded hover:bg-blue-600"
                    onClick={() => {
                      setEditingUser(user);
                      setNewEmail(user.email);
                      setNewPassword(""); 
                    }}
                  >
                    Bearbeiten
                  </button>
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>

      {editingUser && (
        <div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50 z-50">
          <div className="bg-white p-6 rounded-lg shadow-lg max-w-md w-full">
            <h2 className="text-xl font-bold mb-4">Benutzer bearbeiten</h2>
            <form
              onSubmit={(e) => {
                e.preventDefault();
                updateUser();
              }}
            >
              <div className="mb-4">
                <label className="block text-sm font-medium text-gray-700">
                  Benutzername:
                </label>
                <input
                  type="text"
                  value={editingUser.user_name}
                  disabled
                  className="w-full mt-1 p-2 border border-gray-300 rounded-md"
                />
              </div>
              <div className="mb-4">
                <label className="block text-sm font-medium text-gray-700">
                  E-Mail:
                </label>
                <input
                  type="email"
                  value={newEmail}
                  onChange={(e) => setNewEmail(e.target.value)}
                  className="w-full mt-1 p-2 border border-gray-300 rounded-md"
                />
              </div>
              <div className="mb-4">
                <label className="block text-sm font-medium text-gray-700">
                  Neues Passwort:
                </label>
                <input
                  type="password"
                  value={newPassword}
                  onChange={(e) => setNewPassword(e.target.value)}
                  className="w-full mt-1 p-2 border border-gray-300 rounded-md"
                />
              </div>
              <div className="flex justify-end gap-2">
                <button
                  type="submit"
                  className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"
                >
                  Speichern
                </button>
                <button
                  type="button"
                  onClick={() => {
                    setEditingUser(null);
                    setNewEmail("");
                    setNewPassword("");
                  }}
                  className="bg-gray-300 px-4 py-2 rounded hover:bg-gray-400"
                >
                  Abbrechen
                </button>
              </div>
            </form>
          </div>
        </div>
      )}

      <div className="monitoring-section mt-8">
        <h2 className="text-xl font-semibold mb-4">System Monitoring</h2>

        {isMonitoringLoading ? (
          <p>Lade Monitoring-Daten...</p>
        ) : (
          <>
            <div className="mb-6">
              <h3 className="text-lg font-medium">Aktive DB-Sitzungen</h3>
              <table className="w-full text-black border-collapse border border-slate-300">
                <thead>
                  <tr>
                    <th className="border border-slate-300 px-4 py-2">Datenbank</th>
                    <th className="border border-slate-300 px-4 py-2">Benutzer</th>
                    <th className="border border-slate-300 px-4 py-2">Status</th>
                    <th className="border border-slate-300 px-4 py-2">Abfrage</th>
                    <th className="border border-slate-300 px-4 py-2">Abfragebeginn</th>
                  </tr>
                </thead>
                <tbody>
                  {dbSessions.map((session, index) => (
                    <tr key={index}>
                      <td className="border border-slate-300 px-4 py-2">{session.datname}</td>
                      <td className="border border-slate-300 px-4 py-2">{session.usename}</td>
                      <td className="border border-slate-300 px-4 py-2">{session.state}</td>
                      <td className="border border-slate-300 px-4 py-2">{session.query}</td>
                      <td className="border border-slate-300 px-4 py-2">{session.query_start}</td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>

            <div>
              <h3 className="text-lg font-medium">Datenbankstatistiken</h3>
              <table className="w-full text-black border-collapse border border-slate-300">
                <thead>
                  <tr>
                    <th className="border border-slate-300 px-4 py-2">Datenbank</th>
                    <th className="border border-slate-300 px-4 py-2">Aktive Verbindungen</th>
                    <th className="border border-slate-300 px-4 py-2">Transaktionen (Commit)</th>
                    <th className="border border-slate-300 px-4 py-2">Transaktionen (Rollback)</th>
                    <th className="border border-slate-300 px-4 py-2">Gelesene Blöcke</th>
                    <th className="border border-slate-300 px-4 py-2">Treffer in Blöcken</th>
                  </tr>
                </thead>
                <tbody>
                  {dbStats.map((stat, index) => (
                    <tr key={index}>
                      <td className="border border-slate-300 px-4 py-2">{stat.datname}</td>
                      <td className="border border-slate-300 px-4 py-2">{stat.active_connections}</td>
                      <td className="border border-slate-300 px-4 py-2">{stat.committed_transactions}</td>
                      <td className="border border-slate-300 px-4 py-2">{stat.rolledback_transactions}</td>
                      <td className="border border-slate-300 px-4 py-2">{stat.blocks_read}</td>
                      <td className="border border-slate-300 px-4 py-2">{stat.blocks_hit}</td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          </>
        )}
      </div>
    </div>
  );
};

export default AdminDashboard;