Dashboard Fach hinzufügen funktioniert in swe-b1-a-dev
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"displayName": "Test Subject",
|
||||
"description": "This is a test subject",
|
||||
"color": "#af128d",
|
||||
"icon": "fa-book"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"displayName": "Test Subject-1",
|
||||
"description": "This is a test subject",
|
||||
"color": "#a63636",
|
||||
"icon": "fa-flask"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"displayName": "Test Subject",
|
||||
"description": "This is a test subject",
|
||||
"color": "#000000",
|
||||
"icon": "fa-flask"
|
||||
}
|
||||
42
swe-b1-a-dev/webseite/dashboard/api/create-subject.php
Normal file
42
swe-b1-a-dev/webseite/dashboard/api/create-subject.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
require_once("../../classes/SubjectData.php");
|
||||
require_once("../../classes/Config.php");
|
||||
|
||||
// Debug logging
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 1);
|
||||
|
||||
// Log all POST data
|
||||
error_log("POST data: " . print_r($_POST, true));
|
||||
|
||||
// Validate and sanitize input
|
||||
$displayName = htmlspecialchars($_POST['displayName']);
|
||||
$description = $_POST['description'];
|
||||
$color = preg_replace('/[^#A-Fa-f0-9]/', '', $_POST['color']);
|
||||
$icon = $_POST['icon']; // Get raw icon value
|
||||
|
||||
// Log processed values
|
||||
error_log("Processing subject creation:");
|
||||
error_log("Display Name: " . $displayName);
|
||||
error_log("Color: " . $color);
|
||||
// error_log("Icon: " . $icon);
|
||||
|
||||
// Generate ID from displayName
|
||||
$id = strtolower(preg_replace('/[^A-Za-z0-9]/', '', $displayName));
|
||||
|
||||
// Set default icon if none is provided
|
||||
if (empty($icon)) {
|
||||
$icon = 'fa-book';
|
||||
}
|
||||
|
||||
// Create and save subject
|
||||
$subject = SubjectData::createNew($id, $displayName, $description, $color, $icon, []);
|
||||
|
||||
if (!$subject || !$subject->save()) {
|
||||
error_log("Failed to create/save subject");
|
||||
http_response_code(400);
|
||||
echo json_encode(['success' => false, 'message' => 'Failed to create or save subject']);
|
||||
exit();
|
||||
}
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
35
swe-b1-a-dev/webseite/dashboard/api/create-topic.php
Normal file
35
swe-b1-a-dev/webseite/dashboard/api/create-topic.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
require_once("../../classes/TopicData.php");
|
||||
require_once("../../classes/Config.php");
|
||||
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 1);
|
||||
|
||||
// Validate and sanitize input
|
||||
$displayName = htmlspecialchars($_POST['displayName']);
|
||||
$subjectId = $_POST['subjectId'];
|
||||
$description = $_POST['description'];
|
||||
$icon = $_POST['icon'];
|
||||
$article = $_POST['article'];
|
||||
|
||||
// Generate ID from displayName
|
||||
$id = strtolower(preg_replace('/[^A-Za-z0-9]/', '', $displayName));
|
||||
|
||||
// Create topic
|
||||
$topic = TopicData::createNew(
|
||||
$id,
|
||||
$subjectId,
|
||||
$displayName,
|
||||
$icon,
|
||||
$description,
|
||||
[], // empty related topics array
|
||||
$article
|
||||
);
|
||||
|
||||
if (!$topic || !$topic->save()) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['success' => false, 'message' => 'Failed to create topic']);
|
||||
exit();
|
||||
}
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
@@ -0,0 +1,198 @@
|
||||
<div class="modal-overlay fixed inset-0 bg-black bg-opacity-50 z-50 hidden">
|
||||
<div class="modal fixed top-1/2 left-1/2 bg-white rounded-xl shadow-lg p-6 w-[800px] max-h-[90vh] overflow-y-auto">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h3 class="text-xl font-semibold">Neues Fach erstellen</h3>
|
||||
<button onclick="closeModal()" class="text-gray-500 hover:text-gray-700">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<form id="subjectForm" onsubmit="handleSubjectSubmit(event)">
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">Name des Fachs</label>
|
||||
<input type="text"
|
||||
name="displayName"
|
||||
required
|
||||
class="w-full px-4 py-3 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 text-lg enabled:hover:border-gray-400"
|
||||
placeholder="z.B. Mathematik">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">Beschreibung</label>
|
||||
<textarea name="description"
|
||||
required
|
||||
class="w-full px-4 py-3 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 text-lg enabled:hover:border-gray-400"
|
||||
rows="4"
|
||||
placeholder="Kurze Beschreibung des Fachs"></textarea>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">Farbe</label>
|
||||
<input type="color" name="color" required
|
||||
value="#3b82f6"
|
||||
class="w-full h-14 px-1 py-1 rounded-lg">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">Icon auswählen</label>
|
||||
<div class="grid grid-cols-6 gap-4 p-4 border rounded-lg bg-gray-50">
|
||||
<?php
|
||||
$icons = [
|
||||
['fa-book', 'Buch'],
|
||||
['fa-square-root-alt', 'Mathematik'],
|
||||
['fa-flask', 'Naturwissenschaften'],
|
||||
['fa-language', 'Sprachen'],
|
||||
['fa-music', 'Musik'],
|
||||
['fa-palette', 'Kunst'],
|
||||
['fa-dumbbell', 'Sport'],
|
||||
['fa-globe', 'Erdkunde'],
|
||||
['fa-clock', 'Geschichte'],
|
||||
['fa-microscope', 'Biologie'],
|
||||
['fa-atom', 'Physik'],
|
||||
['fa-vial', 'Chemie'],
|
||||
['fa-computer', 'Informatik'],
|
||||
['fa-calculator', 'Rechnen'],
|
||||
['fa-pen', 'Schreiben'],
|
||||
['fa-theater-masks', 'Theater'],
|
||||
['fa-draw-polygon', 'Geometrie'],
|
||||
['fa-tablets', 'Medizin']
|
||||
];
|
||||
|
||||
foreach ($icons as $index => [$icon, $label]) {
|
||||
echo "<div class='icon-option cursor-pointer p-4 rounded-lg hover:bg-white hover:shadow transition-all text-center'
|
||||
onclick='selectIcon(this, \"$icon\")' data-icon='$icon'>
|
||||
<i class='fas $icon text-2xl mb-2'></i>
|
||||
<div class='text-xs text-gray-600'>$label</div>
|
||||
</div>";
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<input type="hidden" name="icon" id="selectedIcon">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 flex justify-end gap-3">
|
||||
<button type="button" onclick="closeModal()"
|
||||
class="px-6 py-3 text-gray-600 hover:text-gray-800 text-lg">
|
||||
Abbrechen
|
||||
</button>
|
||||
<button type="submit"
|
||||
class="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 text-lg">
|
||||
Fach erstellen
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function closeModal() {
|
||||
const modalOverlay = document.querySelector('.modal-overlay');
|
||||
modalOverlay.classList.add('hidden');
|
||||
}
|
||||
|
||||
window.selectIcon = function(element, iconName) {
|
||||
// Remove active class from all icons
|
||||
document.querySelectorAll('.icon-option').forEach(el => {
|
||||
el.classList.remove('bg-blue-50', 'ring-2', 'ring-blue-500');
|
||||
});
|
||||
|
||||
// Add active class to selected icon
|
||||
element.classList.add('bg-blue-50', 'ring-2', 'ring-blue-500');
|
||||
|
||||
// Set the hidden input value and log it
|
||||
const iconInput = document.getElementById('selectedIcon');
|
||||
iconInput.value = iconName;
|
||||
console.log('Icon selected:', iconInput.value);
|
||||
|
||||
// Visual feedback
|
||||
element.classList.add('scale-105');
|
||||
setTimeout(() => {
|
||||
element.classList.remove('scale-105');
|
||||
}, 200);
|
||||
}
|
||||
|
||||
function handleSubjectSubmit(event) {
|
||||
event.preventDefault();
|
||||
|
||||
const form = event.target;
|
||||
const formData = new FormData(form);
|
||||
|
||||
// Get values and validate only required fields
|
||||
const displayName = formData.get('displayName').trim();
|
||||
const description = formData.get('description').trim();
|
||||
|
||||
// Validate only name and description
|
||||
if (!displayName) {
|
||||
alert('Bitte geben Sie einen Namen für das Fach ein');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!description) {
|
||||
alert('Bitte geben Sie eine Beschreibung ein');
|
||||
return;
|
||||
}
|
||||
|
||||
// Always set a default icon if none is selected
|
||||
if (!formData.get('icon')) {
|
||||
formData.set('icon', 'fa-book'); // Default icon
|
||||
}
|
||||
|
||||
// Send AJAX request
|
||||
fetch('../api/create-subject.php', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
closeModal();
|
||||
window.location.reload();
|
||||
} else {
|
||||
alert('Fehler beim Erstellen des Fachs: ' + (data.message || 'Unbekannter Fehler'));
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
alert('Ein Fehler ist aufgetreten');
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize first icon selection on page load
|
||||
/*
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const firstIcon = document.querySelector('.icon-option');
|
||||
if (firstIcon) {
|
||||
const iconName = firstIcon.getAttribute('data-icon');
|
||||
selectIcon(firstIcon, iconName);
|
||||
}
|
||||
});
|
||||
*/
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.icon-option {
|
||||
transition: all 0.2s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.icon-option:hover {
|
||||
transform: translateY(-2px);
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.icon-option.selected {
|
||||
background-color: #EBF5FF;
|
||||
border-color: #3B82F6;
|
||||
}
|
||||
|
||||
.icon-option i {
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.icon-option:hover i {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,53 @@
|
||||
<div id="topicEditorModal" class="modal-overlay fixed inset-0 bg-black/30 flex items-center justify-center hidden">
|
||||
<div class="bg-white rounded-2xl p-8 w-full max-w-[90vw] h-[95vh] shadow-2xl border-2 border-gray-100 overflow-y-auto">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h3 class="text-xl font-bold">Thema bearbeiten</h3>
|
||||
<button onclick="closeModal()" class="text-gray-500 hover:text-gray-700">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<form id="topicEditorForm" class="space-y-8">
|
||||
<!-- Subject and Topic Selection -->
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700">Fach</label>
|
||||
<select id="editSubjectSelect" name="subject" required
|
||||
class="mt-1 block w-full rounded-lg border-gray-300 shadow-sm">
|
||||
<option value="">Fach auswählen...</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700">Thema</label>
|
||||
<select id="editTopicSelect" name="topic" required
|
||||
class="mt-1 block w-full rounded-lg border-gray-300 shadow-sm">
|
||||
<option value="">Thema auswählen...</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Topic Content Editor (initially hidden) -->
|
||||
<div id="topicContentEditor" class="hidden space-y-6">
|
||||
<!-- ... rest of your form fields ... -->
|
||||
<div id="topicEditorQuill" class="h-[400px] border rounded-lg"></div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end gap-4">
|
||||
<button type="button" onclick="closeModal()"
|
||||
class="px-4 py-2 text-gray-700 bg-gray-100 rounded-lg hover:bg-gray-200">
|
||||
Abbrechen
|
||||
</button>
|
||||
<button type="submit"
|
||||
class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">
|
||||
Änderungen speichern
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="module">
|
||||
import { TopicEditor } from '/dashboard/js/modules/TopicEditor.js';
|
||||
const topicEditor = new TopicEditor();
|
||||
window.topicEditor = topicEditor;
|
||||
</script>
|
||||
@@ -0,0 +1,111 @@
|
||||
<link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet">
|
||||
<script src="https://cdn.quilljs.com/1.3.6/quill.min.js"></script>
|
||||
|
||||
<div id="topicModal" class="modal-overlay fixed inset-0 bg-black/30 flex items-center justify-center hidden">
|
||||
<div class="bg-white rounded-2xl p-8 w-full max-w-[90vw] h-[95vh] shadow-2xl border-2 border-gray-100 overflow-y-auto">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h3 class="text-xl font-bold">Neues Thema erstellen</h3>
|
||||
<button onclick="closeModal()" class="text-gray-500 hover:text-gray-700">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<form id="topicForm" class="space-y-8">
|
||||
<div class="grid grid-cols-2 gap-8">
|
||||
<!-- Left column - Topic details -->
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700">Themenname</label>
|
||||
<input type="text" name="displayName" class="mt-1 block w-full rounded-lg border-gray-300 shadow-sm" required>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700">Fach</label>
|
||||
<select id="topicSubjectSelect" name="subject" class="mt-1 block w-full rounded-lg border-gray-300 shadow-sm" required>
|
||||
<option value="">Fach auswählen...</option>
|
||||
<?php
|
||||
require_once("../../classes/SubjectData.php");
|
||||
$subjects = SubjectData::getAll();
|
||||
foreach ($subjects as $subject) {
|
||||
echo "<option value='{$subject->getId()}'>{$subject->getDisplayName()}</option>";
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700">Kurzbeschreibung</label>
|
||||
<textarea name="description" rows="2" class="mt-1 block w-full rounded-lg border-gray-300 shadow-sm" required></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right column - Content and preview -->
|
||||
<div>
|
||||
<div class="mb-6">
|
||||
<div class="flex justify-between items-center mb-2">
|
||||
<label class="block text-sm font-medium text-gray-700">Bildungsinhalt</label>
|
||||
<div class="flex gap-2">
|
||||
<button type="button" onclick="editor.undo()"
|
||||
class="text-gray-500 hover:text-gray-700 p-1">
|
||||
<i class="fas fa-undo"></i>
|
||||
</button>
|
||||
<button type="button" onclick="editor.redo()"
|
||||
class="text-gray-500 hover:text-gray-700 p-1">
|
||||
<i class="fas fa-redo"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- The editor container -->
|
||||
<div id="quillEditor" style="height: 400px;" class="bg-white border rounded-lg"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Preview section -->
|
||||
<div class="border rounded-lg p-8 mt-8 bg-gray-50">
|
||||
<h4 class="text-lg font-medium text-gray-700 mb-4">Vorschau</h4>
|
||||
<div id="contentPreview" class="prose prose-lg max-w-none bg-white p-6 rounded-lg shadow-sm"></div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end gap-4">
|
||||
<button type="button" onclick="closeModal()"
|
||||
class="px-4 py-2 text-gray-700 bg-gray-100 rounded-lg hover:bg-gray-200">
|
||||
Abbrechen
|
||||
</button>
|
||||
<button type="submit"
|
||||
class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">
|
||||
Thema erstellen
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="module">
|
||||
import { QuillEditor } from '/dashboard/js/modules/QuillEditor.js';
|
||||
const editor = new QuillEditor('quillEditor', 'contentPreview');
|
||||
window.editor = editor;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* Quill Editor Styles */
|
||||
.ql-editor {
|
||||
min-height: 350px;
|
||||
font-size: 16px;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.ql-toolbar {
|
||||
background: white;
|
||||
border-top-left-radius: 0.5rem;
|
||||
border-top-right-radius: 0.5rem;
|
||||
border: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
.ql-container {
|
||||
border-bottom-left-radius: 0.5rem;
|
||||
border-bottom-right-radius: 0.5rem;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-top: none;
|
||||
}
|
||||
</style>
|
||||
116
swe-b1-a-dev/webseite/dashboard/index.php
Normal file
116
swe-b1-a-dev/webseite/dashboard/index.php
Normal file
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
// Temporarily remove validation
|
||||
// session_start();
|
||||
// require_once("../classes/User.php");
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Lehrer Dashboard</title>
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link rel="stylesheet" href="styles/main.css">
|
||||
<link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet">
|
||||
<script src="https://cdn.quilljs.com/1.3.6/quill.min.js"></script>
|
||||
<script type="module" src="js/main.js"></script>
|
||||
</head>
|
||||
<body class="min-h-screen bg-gray-50">
|
||||
<!-- Left Sidebar -->
|
||||
<nav class="sidebar fixed w-[280px] h-full bg-[var(--primary-color)] p-8">
|
||||
<!-- Logo and Brand -->
|
||||
<div class="mb-8">
|
||||
<h2 class="text-2xl font-bold text-white">TeacherDash</h2>
|
||||
</div>
|
||||
|
||||
<!-- User Profile -->
|
||||
<div class="flex items-center gap-4 mb-8 p-3 rounded-xl bg-white/10">
|
||||
<div class="w-12 h-12 rounded-full bg-white/20 flex items-center justify-center">
|
||||
<i class="fas fa-user text-white"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-white font-medium">Max Mustermann</h3>
|
||||
<p class="text-white/70 text-sm">Mathematiklehrer</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Navigation Links -->
|
||||
<nav class="space-y-2">
|
||||
<a href="#" class="flex items-center gap-3 text-white/90 hover:bg-white/10 p-3 rounded-xl transition-all">
|
||||
<i class="fas fa-home w-6"></i>
|
||||
<span>Dashboard</span>
|
||||
</a>
|
||||
<a href="#" class="flex items-center gap-3 text-white/90 hover:bg-white/10 p-3 rounded-xl transition-all">
|
||||
<i class="fas fa-book w-6"></i>
|
||||
<span>Fächer</span>
|
||||
</a>
|
||||
<a href="#" class="flex items-center gap-3 text-white/90 hover:bg-white/10 p-3 rounded-xl transition-all">
|
||||
<i class="fas fa-tasks w-6"></i>
|
||||
<span>Aufgaben</span>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<!-- Bottom Section -->
|
||||
<div class="absolute bottom-8 left-8 right-8">
|
||||
<a href="#" class="flex items-center gap-3 text-white/90 hover:bg-white/10 p-3 rounded-xl transition-all">
|
||||
<i class="fas fa-cog w-6"></i>
|
||||
<span>Einstellungen</span>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="ml-[280px] p-8">
|
||||
<h1 class="text-2xl font-bold mb-6">Dashboard</h1>
|
||||
|
||||
<!-- Action Cards -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-8">
|
||||
<!-- Subject Card -->
|
||||
<div class="bg-white p-6 rounded-xl shadow-lg border border-gray-100">
|
||||
<h3 class="text-lg font-semibold mb-4">Fach erstellen</h3>
|
||||
<p class="text-gray-600 mb-4">Erstellen Sie ein neues Fach für Ihren Unterricht.</p>
|
||||
<button onclick="openSubjectModal()"
|
||||
class="px-4 py-2 bg-[var(--primary-color)] text-white rounded-lg hover:bg-[var(--secondary-color)]">
|
||||
Neues Fach
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Topic Card -->
|
||||
<div class="bg-white p-6 rounded-xl shadow-lg border border-gray-100">
|
||||
<h3 class="text-lg font-semibold mb-4">Thema hinzufügen</h3>
|
||||
<p class="text-gray-600 mb-4">Fügen Sie ein neues Thema zu einem Fach hinzu.</p>
|
||||
<button onclick="openTopicModal()"
|
||||
class="px-4 py-2 bg-[var(--primary-color)] text-white rounded-lg hover:bg-[var(--secondary-color)]">
|
||||
Neues Thema
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Topic Editor Card -->
|
||||
<div class="bg-white p-6 rounded-xl shadow-lg border border-gray-100">
|
||||
<h3 class="text-lg font-semibold mb-4">Thema bearbeiten</h3>
|
||||
<p class="text-gray-600 mb-4">Bearbeiten Sie existierende Themen.</p>
|
||||
<button onclick="openModal('topicEditorModal')"
|
||||
class="px-4 py-2 bg-[var(--primary-color)] text-white rounded-lg hover:bg-[var(--secondary-color)]">
|
||||
Thema bearbeiten
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recent Activity Section -->
|
||||
<div class="bg-white p-6 rounded-xl shadow-lg border border-gray-100">
|
||||
<h2 class="text-xl font-semibold mb-4">Letzte Aktivitäten</h2>
|
||||
<div id="recentActivity" class="space-y-4">
|
||||
<!-- Activity items will be loaded dynamically -->
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Include modal components -->
|
||||
<div id="modalContainer"></div>
|
||||
|
||||
<!-- Scripts -->
|
||||
<script type="module" src="js/main.js"></script>
|
||||
<input type="hidden" name="icon" id="selectedIcon"> <!-- removed required attribute -->
|
||||
</body>
|
||||
</html>
|
||||
116
swe-b1-a-dev/webseite/dashboard/js/main.js
Normal file
116
swe-b1-a-dev/webseite/dashboard/js/main.js
Normal file
@@ -0,0 +1,116 @@
|
||||
// Modal management
|
||||
export function openSubjectModal() {
|
||||
fetch('components/modals/subject-modal.php')
|
||||
.then(response => response.text())
|
||||
.then(html => {
|
||||
document.getElementById('modalContainer').innerHTML = html;
|
||||
document.querySelector('.modal-overlay').classList.remove('hidden');
|
||||
document.querySelector('.modal').classList.add('active');
|
||||
});
|
||||
}
|
||||
|
||||
export function openTopicModal() {
|
||||
console.log('Opening topic modal...'); // Debug log
|
||||
fetch('components/modals/topic-modal.php')
|
||||
.then(response => response.text())
|
||||
.then(html => {
|
||||
document.getElementById('modalContainer').innerHTML = html;
|
||||
document.querySelector('.modal-overlay').classList.remove('hidden');
|
||||
document.querySelector('.modal').classList.add('active');
|
||||
// Initialize topic manager after modal is loaded
|
||||
const event = new CustomEvent('openTopicModal');
|
||||
document.dispatchEvent(event);
|
||||
})
|
||||
.catch(error => console.error('Error loading topic modal:', error));
|
||||
}
|
||||
|
||||
export function openTopicEditorModal() {
|
||||
fetch('components/modals/topic-editor-modal.php')
|
||||
.then(response => response.text())
|
||||
.then(html => {
|
||||
document.getElementById('modalContainer').innerHTML = html;
|
||||
document.querySelector('.modal-overlay').classList.remove('hidden');
|
||||
document.querySelector('.modal').classList.add('active');
|
||||
// Initialize topic editor
|
||||
const event = new CustomEvent('openTopicEditorModal');
|
||||
document.dispatchEvent(event);
|
||||
});
|
||||
}
|
||||
|
||||
export function closeModal() {
|
||||
document.querySelector('.modal-overlay').classList.add('hidden');
|
||||
document.querySelector('.modal').classList.remove('active');
|
||||
}
|
||||
|
||||
// Event listeners
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
loadRecentActivity();
|
||||
});
|
||||
|
||||
async function loadRecentActivity() {
|
||||
try {
|
||||
const response = await fetch('api/get-recent-activity.php');
|
||||
const activities = await response.json();
|
||||
displayActivities(activities);
|
||||
} catch (error) {
|
||||
console.error('Error loading recent activities:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function displayActivities(activities) {
|
||||
const container = document.getElementById('recentActivity');
|
||||
container.innerHTML = activities.map(activity => `
|
||||
<div class="flex items-center gap-4 p-4 bg-gray-50 rounded-lg">
|
||||
<i class="fas ${activity.icon} text-blue-500"></i>
|
||||
<div>
|
||||
<p class="font-medium">${activity.title}</p>
|
||||
<p class="text-sm text-gray-600">${activity.timestamp}</p>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
async function handleSubjectSubmit(event) {
|
||||
event.preventDefault();
|
||||
const form = event.target;
|
||||
const formData = new FormData(form);
|
||||
|
||||
// Ensure icon is included
|
||||
/*
|
||||
const selectedIcon = document.getElementById('selectedIcon').value;
|
||||
if (!selectedIcon) {
|
||||
alert('Bitte wählen Sie ein Icon aus');
|
||||
return;
|
||||
}
|
||||
formData.set('icon', selectedIcon);
|
||||
*/
|
||||
|
||||
try {
|
||||
console.log('Form data before submit:', Object.fromEntries(formData));
|
||||
|
||||
const response = await fetch('api/create-subject.php', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
console.log('Server response:', result);
|
||||
|
||||
if (result.success) {
|
||||
closeModal();
|
||||
window.location.reload();
|
||||
} else {
|
||||
alert('Fehler beim Erstellen des Fachs: ' + result.message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error creating subject:', error);
|
||||
alert('Ein Fehler ist aufgetreten: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Make functions available globally
|
||||
window.openSubjectModal = openSubjectModal;
|
||||
window.openTopicModal = openTopicModal;
|
||||
window.closeModal = closeModal;
|
||||
window.handleSubjectSubmit = handleSubjectSubmit;
|
||||
window.openTopicEditorModal = openTopicEditorModal;
|
||||
80
swe-b1-a-dev/webseite/dashboard/js/modules/QuillEditor.js
Normal file
80
swe-b1-a-dev/webseite/dashboard/js/modules/QuillEditor.js
Normal file
@@ -0,0 +1,80 @@
|
||||
export class QuillEditor {
|
||||
constructor(editorId = 'quillEditor', previewId = 'contentPreview') {
|
||||
this.quill = null;
|
||||
this.editorId = editorId;
|
||||
this.previewId = previewId;
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
initialize() {
|
||||
console.log('Initializing Quill editor...'); // Debug log
|
||||
|
||||
const editorContainer = document.getElementById(this.editorId);
|
||||
if (!editorContainer) {
|
||||
console.error(`Editor container #${this.editorId} not found`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.quill = new Quill(`#${this.editorId}`, {
|
||||
theme: 'snow',
|
||||
modules: {
|
||||
toolbar: [
|
||||
[{ 'header': [1, 2, false] }],
|
||||
['bold', 'italic', 'underline'],
|
||||
['blockquote', 'code-block'],
|
||||
[{ 'list': 'ordered'}, { 'list': 'bullet' }],
|
||||
[{ 'script': 'sub'}, { 'script': 'super' }],
|
||||
['link', 'image', 'formula'],
|
||||
['clean']
|
||||
]
|
||||
},
|
||||
placeholder: 'Fügen Sie hier Ihren Inhalt ein...'
|
||||
});
|
||||
|
||||
this.quill.on('text-change', () => this.updatePreview());
|
||||
console.log('Quill editor initialized');
|
||||
}
|
||||
|
||||
updatePreview() {
|
||||
const content = this.quill.root.innerHTML;
|
||||
const preview = document.getElementById(this.previewId);
|
||||
|
||||
if (preview) {
|
||||
preview.innerHTML = content;
|
||||
|
||||
// Re-render math if present
|
||||
if (window.MathJax) {
|
||||
MathJax.typeset([preview]);
|
||||
}
|
||||
|
||||
// Highlight code blocks if any
|
||||
if (window.Prism) {
|
||||
Prism.highlightAllUnder(preview);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getContent() {
|
||||
return this.quill.root.innerHTML;
|
||||
}
|
||||
|
||||
setContent(content) {
|
||||
if (this.quill) {
|
||||
this.quill.root.innerHTML = content;
|
||||
this.updatePreview();
|
||||
}
|
||||
}
|
||||
|
||||
undo() {
|
||||
this.quill?.history.undo();
|
||||
}
|
||||
|
||||
redo() {
|
||||
this.quill?.history.redo();
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.quill?.setContents([]);
|
||||
this.updatePreview();
|
||||
}
|
||||
}
|
||||
61
swe-b1-a-dev/webseite/dashboard/js/modules/TopicEditor.js
Normal file
61
swe-b1-a-dev/webseite/dashboard/js/modules/TopicEditor.js
Normal file
@@ -0,0 +1,61 @@
|
||||
export class TopicEditor {
|
||||
constructor() {
|
||||
this.quill = null;
|
||||
this.initializeEventListeners();
|
||||
}
|
||||
|
||||
initializeEventListeners() {
|
||||
document.addEventListener('openTopicEditorModal', () => {
|
||||
this.loadSubjects();
|
||||
if (!this.quill) {
|
||||
this.initializeQuill();
|
||||
}
|
||||
});
|
||||
|
||||
// Handle subject selection change
|
||||
document.addEventListener('change', (e) => {
|
||||
if (e.target.id === 'editSubjectSelect') {
|
||||
this.loadTopicsForSubject(e.target.value);
|
||||
}
|
||||
if (e.target.id === 'editTopicSelect') {
|
||||
this.loadTopicContent(e.target.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
initializeQuill() {
|
||||
const toolbarOptions = [
|
||||
['bold', 'italic', 'underline'],
|
||||
['blockquote', 'code-block'],
|
||||
[{ 'list': 'ordered'}, { 'list': 'bullet' }],
|
||||
['link', 'image', 'formula'],
|
||||
['clean']
|
||||
];
|
||||
|
||||
this.quill = new Quill('#topicEditorQuill', {
|
||||
modules: {
|
||||
toolbar: toolbarOptions
|
||||
},
|
||||
theme: 'snow'
|
||||
});
|
||||
}
|
||||
|
||||
async loadSubjects() {
|
||||
const select = document.getElementById('editSubjectSelect');
|
||||
try {
|
||||
const subjects = SubjectData.getAll();
|
||||
select.innerHTML = '<option value="">Fach auswählen...</option>';
|
||||
Object.values(subjects).forEach(subject => {
|
||||
const option = document.createElement('option');
|
||||
option.value = subject.id;
|
||||
option.textContent = subject.displayName;
|
||||
select.appendChild(option);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error loading subjects:', error);
|
||||
alert('Fehler beim Laden der Fächer');
|
||||
}
|
||||
}
|
||||
|
||||
// ... rest of your provided methods ...
|
||||
}
|
||||
150
swe-b1-a-dev/webseite/dashboard/js/modules/TopicManager.js
Normal file
150
swe-b1-a-dev/webseite/dashboard/js/modules/TopicManager.js
Normal file
@@ -0,0 +1,150 @@
|
||||
export class TopicManager {
|
||||
constructor() {
|
||||
this.quill = null;
|
||||
this.initializeEventListeners();
|
||||
}
|
||||
|
||||
initializeEditor() {
|
||||
if (this.quill) return; // Prevent multiple initializations
|
||||
|
||||
console.log('Initializing Quill editor...'); // Debug log
|
||||
|
||||
const editorContainer = document.getElementById('quillEditor');
|
||||
if (!editorContainer) {
|
||||
console.error('Editor container not found');
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize Quill
|
||||
this.quill = new Quill('#quillEditor', {
|
||||
theme: 'snow',
|
||||
modules: {
|
||||
toolbar: [
|
||||
[{ 'header': [1, 2, false] }],
|
||||
['bold', 'italic', 'underline'],
|
||||
['blockquote', 'code-block'],
|
||||
[{ 'list': 'ordered'}, { 'list': 'bullet' }],
|
||||
['link', 'image', 'formula']
|
||||
]
|
||||
},
|
||||
placeholder: 'Fügen Sie hier Ihren Inhalt ein...'
|
||||
});
|
||||
|
||||
// Set up change handler
|
||||
this.quill.on('text-change', () => this.updatePreview());
|
||||
|
||||
console.log('Quill editor initialized'); // Debug log
|
||||
}
|
||||
|
||||
// ... rest of the methods from your code ...
|
||||
|
||||
undo() {
|
||||
if (this.quill) {
|
||||
this.quill.history.undo();
|
||||
}
|
||||
}
|
||||
|
||||
redo() {
|
||||
if (this.quill) {
|
||||
this.quill.history.redo();
|
||||
}
|
||||
}
|
||||
|
||||
deleteSelected() {
|
||||
if (this.quill) {
|
||||
const range = this.quill.getSelection();
|
||||
if (range) {
|
||||
if (range.length > 0) {
|
||||
// Delete selected text/content
|
||||
this.quill.deleteText(range.index, range.length);
|
||||
} else {
|
||||
// Delete current line if no selection
|
||||
const [line] = this.quill.getLine(range.index);
|
||||
const lineLength = line.length();
|
||||
this.quill.deleteText(range.index - lineLength, lineLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async loadSubjectsIntoSelect() {
|
||||
const select = document.getElementById('topicSubjectSelect');
|
||||
if (!select) return;
|
||||
|
||||
// Clear existing options except the first placeholder
|
||||
while (select.options.length > 1) {
|
||||
select.remove(1);
|
||||
}
|
||||
|
||||
try {
|
||||
const subjects = await this.subjectStorage.getAllSubjects();
|
||||
subjects.forEach(subject => {
|
||||
const option = document.createElement('option');
|
||||
option.value = subject.id;
|
||||
option.textContent = subject.name;
|
||||
select.appendChild(option);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error loading subjects:', error);
|
||||
}
|
||||
}
|
||||
|
||||
initializeEventListeners() {
|
||||
const form = document.getElementById('topicForm');
|
||||
if (form) {
|
||||
form.addEventListener('submit', (e) => this.handleTopicSubmit(e));
|
||||
}
|
||||
document.addEventListener('openTopicModal', () => {
|
||||
this.loadSubjectsIntoSelect();
|
||||
if (!this.quill) {
|
||||
this.initializeEditor();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updatePreview() {
|
||||
const content = this.quill.root.innerHTML;
|
||||
const preview = document.getElementById('contentPreview');
|
||||
const quillContent = document.getElementById('quillContent');
|
||||
|
||||
if (preview) {
|
||||
// Preserve classes for styling while updating content
|
||||
preview.innerHTML = content;
|
||||
// Update hidden input with content
|
||||
if (quillContent) {
|
||||
quillContent.value = content;
|
||||
}
|
||||
// Re-render math if present
|
||||
if (window.MathJax) {
|
||||
MathJax.typeset([preview]);
|
||||
}
|
||||
// Highlight code blocks if any
|
||||
if (window.Prism) {
|
||||
Prism.highlightAllUnder(preview);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleTopicSubmit(e) {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(e.target);
|
||||
const content = this.quill.root.innerHTML;
|
||||
const subjectId = formData.get('subject');
|
||||
|
||||
if (!subjectId) {
|
||||
throw new Error('Bitte wählen Sie ein Fach aus');
|
||||
}
|
||||
|
||||
// Add your topic saving logic here
|
||||
|
||||
closeModal('topicModal');
|
||||
}
|
||||
|
||||
clearEditor() {
|
||||
if (this.quill) {
|
||||
this.quill.setContents([]);
|
||||
this.updatePreview();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
22
swe-b1-a-dev/webseite/dashboard/styles/main.css
Normal file
22
swe-b1-a-dev/webseite/dashboard/styles/main.css
Normal file
@@ -0,0 +1,22 @@
|
||||
:root {
|
||||
--primary-color: #2563eb;
|
||||
--secondary-color: #1d4ed8;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
|
||||
}
|
||||
|
||||
.modal-overlay {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
.modal {
|
||||
transform: translate(-50%, -50%) scale(0.95);
|
||||
transition: transform 0.2s ease-out;
|
||||
}
|
||||
|
||||
.modal.active {
|
||||
transform: translate(-50%, -50%) scale(1);
|
||||
}
|
||||
Reference in New Issue
Block a user