Dashboard Fach hinzufügen funktioniert in swe-b1-a-dev

This commit is contained in:
Kelvi Yawo Jules Agbessi Awuklu
2024-12-21 20:08:59 +01:00
parent 7fff7bdc40
commit f0b3459c01
119 changed files with 5374 additions and 0 deletions

View File

@@ -0,0 +1,6 @@
{
"displayName": "Test Subject",
"description": "This is a test subject",
"color": "#af128d",
"icon": "fa-book"
}

View File

@@ -0,0 +1,6 @@
{
"displayName": "Test Subject-1",
"description": "This is a test subject",
"color": "#a63636",
"icon": "fa-flask"
}

View File

@@ -0,0 +1,6 @@
{
"displayName": "Test Subject",
"description": "This is a test subject",
"color": "#000000",
"icon": "fa-flask"
}

View 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]);

View 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]);

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View 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>

View 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;

View 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();
}
}

View 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 ...
}

View 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();
}
}
}

View 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);
}