From 2369f7158d860de8ba772ef243a891bb96d7c189 Mon Sep 17 00:00:00 2001 From: Kelvi Yawo Jules Agbessi Awuklu Date: Fri, 13 Dec 2024 13:43:58 +0100 Subject: [PATCH] Update 13 files - /TeacherDashboard/index.html - /TeacherDashboard/TeacherDashboard.code-workspace - /TeacherDashboard/components/resourceModal.html - /TeacherDashboard/components/subjectModal.html - /TeacherDashboard/components/topicModal.html - /TeacherDashboard/js/main.js - /TeacherDashboard/js/subjects/colors.js - /TeacherDashboard/js/subjects/subjectManager.js - /TeacherDashboard/js/subjects/SubjectModel.js - /TeacherDashboard/js/subjects/SubjectStorage.js - /TeacherDashboard/js/topics/TopicModel.js - /TeacherDashboard/js/topics/topicManager.js - /TeacherDashboard/styles/main.css --- .../TeacherDashboard.code-workspace | 8 + .../components/resourceModal.html | 33 ++++ TeacherDashboard/components/subjectModal.html | 67 +++++++ TeacherDashboard/components/topicModal.html | 74 +++++++ TeacherDashboard/index.html | 152 +++++++++++++++ TeacherDashboard/js/main.js | 113 +++++++++++ TeacherDashboard/js/subjects/SubjectModel.js | 47 +++++ .../js/subjects/SubjectStorage.js | 39 ++++ TeacherDashboard/js/subjects/colors.js | 21 ++ .../js/subjects/subjectManager.js | 139 +++++++++++++ TeacherDashboard/js/topics/TopicModel.js | 0 TeacherDashboard/js/topics/topicManager.js | 178 +++++++++++++++++ TeacherDashboard/styles/main.css | 184 ++++++++++++++++++ 13 files changed, 1055 insertions(+) create mode 100644 TeacherDashboard/TeacherDashboard.code-workspace create mode 100644 TeacherDashboard/components/resourceModal.html create mode 100644 TeacherDashboard/components/subjectModal.html create mode 100644 TeacherDashboard/components/topicModal.html create mode 100644 TeacherDashboard/index.html create mode 100644 TeacherDashboard/js/main.js create mode 100644 TeacherDashboard/js/subjects/SubjectModel.js create mode 100644 TeacherDashboard/js/subjects/SubjectStorage.js create mode 100644 TeacherDashboard/js/subjects/colors.js create mode 100644 TeacherDashboard/js/subjects/subjectManager.js create mode 100644 TeacherDashboard/js/topics/TopicModel.js create mode 100644 TeacherDashboard/js/topics/topicManager.js create mode 100644 TeacherDashboard/styles/main.css diff --git a/TeacherDashboard/TeacherDashboard.code-workspace b/TeacherDashboard/TeacherDashboard.code-workspace new file mode 100644 index 0000000..876a149 --- /dev/null +++ b/TeacherDashboard/TeacherDashboard.code-workspace @@ -0,0 +1,8 @@ +{ + "folders": [ + { + "path": "." + } + ], + "settings": {} +} \ No newline at end of file diff --git a/TeacherDashboard/components/resourceModal.html b/TeacherDashboard/components/resourceModal.html new file mode 100644 index 0000000..7139630 --- /dev/null +++ b/TeacherDashboard/components/resourceModal.html @@ -0,0 +1,33 @@ + + \ No newline at end of file diff --git a/TeacherDashboard/components/subjectModal.html b/TeacherDashboard/components/subjectModal.html new file mode 100644 index 0000000..546d7b2 --- /dev/null +++ b/TeacherDashboard/components/subjectModal.html @@ -0,0 +1,67 @@ + + diff --git a/TeacherDashboard/components/topicModal.html b/TeacherDashboard/components/topicModal.html new file mode 100644 index 0000000..a9f248b --- /dev/null +++ b/TeacherDashboard/components/topicModal.html @@ -0,0 +1,74 @@ + + diff --git a/TeacherDashboard/index.html b/TeacherDashboard/index.html new file mode 100644 index 0000000..d564d06 --- /dev/null +++ b/TeacherDashboard/index.html @@ -0,0 +1,152 @@ + + + + + + Lehrer Dashboard + + + + + + + + + + + + + + + + + + + + +
+

Dashboard

+ + +
+ +
+

Fach erstellen

+

Erstellen Sie ein neues Fach für Ihren Unterricht.

+ +
+ + +
+

Thema hinzufügen

+

Fügen Sie ein neues Thema zu einem Fach hinzu.

+ +
+ + +
+

Materialien hochladen

+

Laden Sie Unterrichtsmaterialien hoch.

+ +
+
+ + +
+

Letzte Aktivitäten

+
+ +
+
+
+ + +
+ + + + + + + diff --git a/TeacherDashboard/js/main.js b/TeacherDashboard/js/main.js new file mode 100644 index 0000000..ba52edc --- /dev/null +++ b/TeacherDashboard/js/main.js @@ -0,0 +1,113 @@ +import { SubjectManager } from './subjects/subjectManager.js'; +import { TopicManager } from './topics/topicManager.js'; + +// Make modal functions globally available +window.openModal = (modalId) => { + const modal = document.getElementById(modalId); + modal.classList.remove('hidden'); + + if (modalId === 'topicModal') { + document.dispatchEvent(new Event('openTopicModal')); + } + + gsap.fromTo(modal.children[0], + { y: -50, opacity: 0 }, + { y: 0, opacity: 1, duration: 0.3 } + ); +}; + +window.closeModal = (modalId) => { + const modal = document.getElementById(modalId); + gsap.to(modal.children[0], { + y: -50, + opacity: 0, + duration: 0.3, + onComplete: () => { + modal.classList.add('hidden'); + } + }); +}; + +// Make this function globally available +window.openSubjectModal = () => { + openModal('subjectModal'); +}; + +async function loadModals() { + const modalContainer = document.getElementById('modalContainer'); + const modals = ['subjectModal', 'topicModal', 'resourceModal']; + + for (const modal of modals) { + try { + const response = await fetch(`components/${modal}.html`); + const html = await response.text(); + modalContainer.innerHTML += html; + } catch (error) { + console.error(`Error loading ${modal}:`, error); + } + } + + // Initialize color picker functionality for subject modal + initializeColorPicker(); +} + +function initializeColorPicker() { + const colorOptions = document.querySelectorAll('.color-option'); + colorOptions.forEach(option => { + option.addEventListener('click', function() { + document.querySelectorAll('.color-option').forEach(opt => + opt.classList.remove('ring-2', 'ring-blue-500')); + this.classList.add('ring-2', 'ring-blue-500'); + document.getElementById('selectedColor').value = this.dataset.color; + }); + }); +} + +// Initialize everything after DOM content is loaded +document.addEventListener('DOMContentLoaded', async () => { + await loadModals(); + const subjectManager = new SubjectManager(); + // Make topicManager globally available + window.topicManager = new TopicManager(); + + // Initialize event listeners for the subject form + const form = document.getElementById('subjectForm'); + if (form) { + form.addEventListener('submit', async (e) => { + e.preventDefault(); + const submitButton = form.querySelector('button[type="submit"]'); + submitButton.disabled = true; + submitButton.innerHTML = ' Speichern...'; + + try { + await subjectManager.handleSubjectSubmit(e); + showNotification('Fach erfolgreich erstellt!', 'success'); + } catch (error) { + showNotification(error.message || 'Fehler beim Erstellen des Fachs', 'error'); + } finally { + submitButton.disabled = false; + submitButton.innerHTML = 'Speichern'; + } + }); + } +}); + +function showNotification(message, type = 'success') { + const notification = document.createElement('div'); + notification.className = `fixed bottom-4 right-4 p-4 rounded-lg text-white ${ + type === 'success' ? 'bg-green-500' : 'bg-red-500' + }`; + notification.textContent = message; + document.body.appendChild(notification); + + gsap.to(notification, { + opacity: 0, + y: 20, + delay: 3, + duration: 0.5, + onComplete: () => notification.remove() + }); +} + +// Form submissions and other JavaScript functions +// ... rest of your JavaScript code ... diff --git a/TeacherDashboard/js/subjects/SubjectModel.js b/TeacherDashboard/js/subjects/SubjectModel.js new file mode 100644 index 0000000..e0edaad --- /dev/null +++ b/TeacherDashboard/js/subjects/SubjectModel.js @@ -0,0 +1,47 @@ +export class SubjectModel { + constructor(data) { + this.id = data.id || crypto.randomUUID(); + this.name = data.name; + this.description = data.description || ''; + this.color = data.color; + this.icon = data.icon; + this.createdAt = data.createdAt || new Date().toISOString(); + this.updatedAt = new Date().toISOString(); + this.topics = data.topics || []; + this.resources = data.resources || []; + this.metadata = { + lastAccessed: new Date().toISOString(), + topicCount: 0, + resourceCount: 0 + }; + this.content = data.content || ''; + this.materials = data.materials || []; + } + + validate() { + const required = ['name', 'color', 'icon']; + const missing = required.filter(field => !this[field]); + + if (missing.length > 0) { + throw new Error(`Missing required fields: ${missing.join(', ')}`); + } + return true; + } + + toJSON() { + return { + id: this.id, + name: this.name, + description: this.description, + color: this.color, + icon: this.icon, + createdAt: this.createdAt, + updatedAt: this.updatedAt, + topics: this.topics, + resources: this.resources, + metadata: this.metadata, + content: this.content, + materials: this.materials + }; + } +} diff --git a/TeacherDashboard/js/subjects/SubjectStorage.js b/TeacherDashboard/js/subjects/SubjectStorage.js new file mode 100644 index 0000000..1446f39 --- /dev/null +++ b/TeacherDashboard/js/subjects/SubjectStorage.js @@ -0,0 +1,39 @@ +export class SubjectStorage { + constructor() { + this.storageKey = 'teacherDash_subjects'; + } + + async getAllSubjects() { + try { + const data = localStorage.getItem(this.storageKey); + return data ? JSON.parse(data) : []; + } catch (error) { + console.error('Error loading subjects:', error); + return []; + } + } + + async saveSubject(subject) { + try { + const subjects = await this.getAllSubjects(); + const existingIndex = subjects.findIndex(s => s.id === subject.id); + + if (existingIndex >= 0) { + subjects[existingIndex] = subject; + } else { + subjects.push(subject); + } + + localStorage.setItem(this.storageKey, JSON.stringify(subjects)); + return subject; + } catch (error) { + throw new Error('Failed to save subject: ' + error.message); + } + } + + async deleteSubject(subjectId) { + const subjects = await this.getAllSubjects(); + const filtered = subjects.filter(s => s.id !== subjectId); + localStorage.setItem(this.storageKey, JSON.stringify(filtered)); + } +} diff --git a/TeacherDashboard/js/subjects/colors.js b/TeacherDashboard/js/subjects/colors.js new file mode 100644 index 0000000..81eb13b --- /dev/null +++ b/TeacherDashboard/js/subjects/colors.js @@ -0,0 +1,21 @@ +export const subjectColors = { + red: { hex: '#FF6B6B', name: 'Rot' }, + teal: { hex: '#4ECDC4', name: 'Türkis' }, + blue: { hex: '#45B7D1', name: 'Blau' }, + green: { hex: '#96CEB4', name: 'Grün' }, + brown: { hex: '#D4A373', name: 'Braun' }, + purple: { hex: '#9B5DE5', name: 'Lila' }, + pink: { hex: '#F15BB5', name: 'Pink' }, + yellow: { hex: '#FEE440', name: 'Gelb' }, + lightBlue: { hex: '#00BBF9', name: 'Hellblau' }, + mint: { hex: '#00F5D4', name: 'Mint' } +}; + +export function generateColorPalette() { + return Object.entries(subjectColors) + .map(([key, color]) => ({ + id: key, + hex: color.hex, + name: color.name + })); +} diff --git a/TeacherDashboard/js/subjects/subjectManager.js b/TeacherDashboard/js/subjects/subjectManager.js new file mode 100644 index 0000000..f0723bf --- /dev/null +++ b/TeacherDashboard/js/subjects/subjectManager.js @@ -0,0 +1,139 @@ +import { generateColorPalette } from './colors.js'; +import { SubjectModel } from './SubjectModel.js'; +import { SubjectStorage } from './SubjectStorage.js'; + +export class SubjectManager { + constructor() { + this.storage = new SubjectStorage(); + this.subjects = []; + this.initializeEventListeners(); + } + + initializeEventListeners() { + const form = document.getElementById('subjectForm'); + if (form) { + form.addEventListener('submit', (e) => this.handleSubjectSubmit(e)); + } + + this.initializeColorPicker(); + this.initializeIconPicker(); + } + + initializeColorPicker() { + const palette = generateColorPalette(); + const container = document.getElementById('colorPalette'); + + if (container) { + container.innerHTML = palette.map(color => ` +
+
+ `).join(''); + + this.addColorPickerListeners(); + } + } + + initializeIconPicker() { + const iconOptions = document.querySelectorAll('.icon-option'); + iconOptions.forEach(option => { + option.addEventListener('click', () => { + iconOptions.forEach(opt => opt.classList.remove('selected')); + option.classList.add('selected'); + document.getElementById('selectedIcon').value = option.dataset.icon; + }); + }); + } + + addColorPickerListeners() { + const colorOptions = document.querySelectorAll('.color-option'); + colorOptions.forEach(option => { + option.addEventListener('click', () => { + colorOptions.forEach(opt => opt.classList.remove('selected')); + option.classList.add('selected'); + document.getElementById('selectedColor').value = option.dataset.color; + }); + }); + } + + async handleSubjectSubmit(e) { + e.preventDefault(); + const formData = new FormData(e.target); + + try { + const subjectData = { + name: formData.get('displayName'), + description: formData.get('description'), + color: formData.get('color'), + icon: formData.get('icon'), + materials: await this.processFiles(formData.getAll('materials[]')) + }; + + const subject = new SubjectModel(subjectData); + subject.validate(); + + await this.storage.saveSubject(subject.toJSON()); + this.updateSubjectsList(subject.toJSON()); + closeModal('subjectModal'); + e.target.reset(); + + return subject; + } catch (error) { + throw new Error(`Failed to create subject: ${error.message}`); + } + } + + async processFiles(files) { + return Array.from(files).map(file => ({ + name: file.name, + size: file.size, + type: file.type, + lastModified: file.lastModified + })); + } + + async loadSubjects() { + const subjects = await this.storage.getAllSubjects(); + subjects.forEach(subject => this.updateSubjectsList(subject)); + } + + validateSubjectForm(formData) { + const requiredFields = ['displayName', 'color', 'icon']; + return requiredFields.every(field => formData.get(field)?.trim()); + } + + updateSubjectsList(subject) { + const container = document.getElementById('subjectsList'); + if (!container) { + const mainContent = document.querySelector('main'); + const subjectsSection = document.createElement('div'); + subjectsSection.className = 'mt-8'; + subjectsSection.innerHTML = ` +

Meine Fächer

+
+
+ `; + mainContent.appendChild(subjectsSection); + } + + const subjectElement = this.createSubjectElement(subject); + document.getElementById('subjectsList').appendChild(subjectElement); + } + + createSubjectElement(subject) { + const div = document.createElement('div'); + div.className = 'bg-white p-4 rounded-xl shadow-sm border border-gray-100 flex items-center gap-4'; + div.innerHTML = ` +
+ +
+
+

${subject.name}

+

${subject.description || ''}

+
+ `; + return div; + } +} diff --git a/TeacherDashboard/js/topics/TopicModel.js b/TeacherDashboard/js/topics/TopicModel.js new file mode 100644 index 0000000..e69de29 diff --git a/TeacherDashboard/js/topics/topicManager.js b/TeacherDashboard/js/topics/topicManager.js new file mode 100644 index 0000000..901e783 --- /dev/null +++ b/TeacherDashboard/js/topics/topicManager.js @@ -0,0 +1,178 @@ +import { SubjectStorage } from '../subjects/SubjectStorage.js'; + +export class TopicManager { + constructor() { + this.subjectStorage = new SubjectStorage(); + this.quill = null; + this.initializeEventListeners(); + } + + initializeEditor() { + const toolbarOptions = [ + ['undo', 'redo'], + [{ 'header': [1, 2, false] }], + ['bold', 'italic', 'underline', 'strike'], + ['blockquote', 'code-block'], + [{ 'list': 'ordered'}, { 'list': 'bullet' }], + [{ 'script': 'sub'}, { 'script': 'super' }], + [{ 'indent': '-1'}, { 'indent': '+1' }], + ['link', 'image', 'formula'], + ['clean'] + ]; + + const editorOptions = { + modules: { + toolbar: toolbarOptions, + keyboard: { + bindings: { + custom1: { + key: 'Z', + shortKey: true, + handler: () => this.undo() + }, + custom2: { + key: 'Z', + shortKey: true, + shiftKey: true, + handler: () => this.redo() + }, + custom3: { + key: 'Delete', + handler: () => this.deleteSelected() + } + } + }, + history: { + delay: 1000, + maxStack: 500, + userOnly: true + } + }, + theme: 'snow', + formats: [ + 'header', + 'bold', 'italic', 'underline', 'strike', + 'list', 'bullet', + 'indent', + 'link', 'image', + 'code-block', + 'blockquote', + 'clean' + ] + }; + + this.quill = new Quill('#quillEditor', editorOptions); + this.quill.on('text-change', () => { + this.updatePreview(); + }); + } + + 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(); + } + } +} diff --git a/TeacherDashboard/styles/main.css b/TeacherDashboard/styles/main.css new file mode 100644 index 0000000..ef53bce --- /dev/null +++ b/TeacherDashboard/styles/main.css @@ -0,0 +1,184 @@ +:root { + --primary-color: #4f46e5; + --secondary-color: #3730a3; + --background-color: #f8fafc; + --text-color: #1e293b; + --accent-color: #818cf8; +} + +.color-option { + width: 40px; + height: 40px; + border-radius: 12px; + cursor: pointer; + transition: all 0.3s ease; + border: 2px solid #e2e8f0; +} + +.color-option:hover { + transform: scale(1.1); +} + +.color-option.selected { + border: 2px solid var(--primary-color); + box-shadow: 0 0 0 2px rgba(79, 70, 229, 0.3); +} + +.modal { + opacity: 0; + visibility: hidden; + transition: all 0.3s ease; +} + +.modal:not(.hidden) { + opacity: 1; + visibility: visible; +} + +.modal > div { + transform: translateY(0); + transition: transform 0.3s ease, opacity 0.3s ease; +} + +.modal.hidden > div { + transform: translateY(-20px); +} + +/* Action Cards */ +.bg-white { + background-color: white; + transition: transform 0.2s ease, box-shadow 0.2s ease; +} + +.bg-white:hover { + transform: translateY(-2px); + box-shadow: 0 4px 25px rgba(0, 0, 0, 0.1); +} + +button { + transition: all 0.2s ease; +} + +button:disabled { + opacity: 0.7; + cursor: not-allowed; +} + +/* Sidebar Styles */ +.sidebar { + background: linear-gradient(to bottom, var(--primary-color), var(--secondary-color)); +} + +.sidebar a { + display: flex; + align-items: center; + text-decoration: none; +} + +.sidebar a:hover { + background-color: rgba(255, 255, 255, 0.1); +} + +/* Form Styles */ +input[type="text"], +textarea, +select { + width: 100%; + padding: 0.75rem; + border-radius: 0.5rem; + border: 1px solid #e2e8f0; + transition: all 0.2s ease; +} + +input[type="text"]:focus, +textarea:focus, +select:focus { + outline: none; + border-color: var(--primary-color); + box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1); +} + +/* Icon Selector Styles */ +.icon-option { + transition: all 0.2s ease; + color: var(--text-color); +} + +.icon-option:hover { + transform: scale(1.05); + border-color: var(--primary-color); + color: var(--primary-color); +} + +.icon-option.selected { + background-color: var(--primary-color); + color: white; + border-color: var(--primary-color); +} + +/* Enhanced Quill content styling */ +.quill-content { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; + line-height: 1.6; +} + +.quill-content h1 { + font-size: 2em; + font-weight: 700; + margin: 1em 0 0.5em; + color: #111; + line-height: 1.2; +} + +.quill-content h2 { + font-size: 1.5em; + font-weight: 600; + margin: 1em 0 0.5em; + color: #222; + line-height: 1.3; +} + +.quill-content p { + margin: 0 0 1em; + color: #333; +} + +.quill-content ul, .quill-content ol { + margin: 0 0 1em 1.5em; + padding: 0; +} + +.quill-content ul { + list-style-type: disc; +} + +.quill-content ol { + list-style-type: decimal; +} + +.quill-content pre { + background: #f8f9fa; + padding: 1em; + border-radius: 6px; + margin: 0 0 1em; + overflow-x: auto; +} + +.quill-content blockquote { + border-left: 4px solid #e5e7eb; + padding: 0.5em 0 0.5em 1em; + margin: 0 0 1em; + color: #4b5563; + font-style: italic; +} + +.quill-content code { + background: #f3f4f6; + padding: 0.2em 0.4em; + border-radius: 4px; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + font-size: 0.9em; + color: #e11d48; +} + +/* Rest of your CSS styles */