added sorting functionality

This commit is contained in:
Felix Ivo 2025-06-16 10:56:45 +02:00
parent fd08f04600
commit e62a0ec2af
3 changed files with 280 additions and 26 deletions

278
JavaScript/script.js Normal file
View File

@ -0,0 +1,278 @@
// public/script.js
document.addEventListener('DOMContentLoaded', () => {
// --- Password Strength Checker for Registration ---
const passwordInput = document.getElementById('password');
const passwordStrengthDiv = document.getElementById('password-strength');
const passwordCriteriaUl = passwordStrengthDiv ? passwordStrengthDiv.querySelector('ul') : null;
if (passwordInput && passwordStrengthDiv && passwordCriteriaUl) {
const criteria = [
{ regex: /.{8,}/, message: "At least 8 characters", id: "len" },
{ regex: /[A-Z]/, message: "An uppercase letter", id: "upper" },
{ regex: /[a-z]/, message: "A lowercase letter", id: "lower" },
{ regex: /[0-9]/, message: "A number", id: "num" },
{ regex: /[^A-Za-z0-9]/, message: "A special character", id: "special" }
];
// Populate criteria list
criteria.forEach(c => {
const li = document.createElement('li');
li.id = `crit-${c.id}`;
li.textContent = `${c.message}`;
li.classList.add('invalid');
passwordCriteriaUl.appendChild(li);
});
passwordInput.addEventListener('input', () => {
const password = passwordInput.value;
let strengthScore = 0;
criteria.forEach(c => {
const li = document.getElementById(`crit-${c.id}`);
if (c.regex.test(password)) {
li.textContent = `${c.message}`;
li.classList.remove('invalid');
li.classList.add('valid');
strengthScore++;
} else {
li.textContent = `${c.message}`;
li.classList.remove('valid');
li.classList.add('invalid');
}
});
const strengthBar = document.querySelector('.password-strength-meter div');
if (strengthBar) {
strengthBar.style.width = (strengthScore / criteria.length * 100) + '%';
if (strengthScore < 2) strengthBar.style.backgroundColor = 'red';
else if (strengthScore < 4) strengthBar.style.backgroundColor = 'orange';
else strengthBar.style.backgroundColor = 'green';
}
if (password.length === 0) {
if (strengthBar) strengthBar.style.width = '0%';
}
});
// Trigger initial check if there's a value (e.g. browser autofill)
if(passwordInput.value) passwordInput.dispatchEvent(new Event('input'));
}
// --- AJAX Form Submissions ---
handleAjaxForm('login-form');
handleAjaxForm('register-form');
handleAjaxForm('note-form');
handleAjaxForm('logout-form'); // Added for logout
// --- Delete Note Confirmation and AJAX ---
document.querySelectorAll('.delete-note-btn').forEach(button => {
button.addEventListener('click', function(e) {
e.preventDefault();
if (confirm('Are you sure you want to delete this note?')) {
const noteId = this.dataset.noteId;
// Create a temporary form to submit for delete
const tempForm = document.createElement('form');
tempForm.method = 'POST';
tempForm.style.display = 'none';
const actionInput = document.createElement('input');
actionInput.type = 'hidden';
actionInput.name = 'action';
actionInput.value = 'delete_note';
tempForm.appendChild(actionInput);
const noteIdInput = document.createElement('input');
noteIdInput.type = 'hidden';
noteIdInput.name = 'note_id';
noteIdInput.value = noteId;
tempForm.appendChild(noteIdInput);
document.body.appendChild(tempForm); // Form must be in DOM to submit
// Use handleAjaxForm for consistency
const formData = new FormData(tempForm);
fetch('index.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
displayMessage(data.message, data.success ? 'success' : 'danger');
if (data.success) {
// Refresh page or remove row dynamically for better UX
// For simplicity, reload. A more advanced way is to find and remove table row.
setTimeout(() => { window.location.reload(); }, 1000);
}
})
.catch(error => {
console.error('Error:', error);
displayMessage('An error occurred during deletion.', 'danger');
})
.finally(() => {
document.body.removeChild(tempForm); // Clean up
});
}
});
});
// --- Drag and Drop File Upload ---
// ... (no changes from previous, ensure it's present)
const dropZone = document.getElementById('drop-zone');
const noteTitleInput = document.getElementById('title');
const noteContentInput = document.getElementById('content');
const markdownPreview = document.getElementById('markdown-preview');
if (dropZone && noteTitleInput && noteContentInput) {
dropZone.addEventListener('dragover', (event) => {
event.preventDefault();
dropZone.classList.add('drag-over');
});
dropZone.addEventListener('dragleave', () => {
dropZone.classList.remove('drag-over');
});
dropZone.addEventListener('drop', (event) => {
event.preventDefault();
dropZone.classList.remove('drag-over');
const files = event.dataTransfer.files;
if (files.length > 0) {
const file = files[0];
if (file.type === 'text/plain' || file.type === 'text/markdown' || file.name.endsWith('.md') || file.name.endsWith('.txt')) {
const reader = new FileReader();
reader.onload = (e) => {
noteTitleInput.value = file.name.replace(/\.(md|txt)$/i, '');
noteContentInput.value = e.target.result;
if (window.updateMarkdownPreview) updateMarkdownPreview();
};
reader.readAsText(file);
} else {
displayMessage('Please drop a .txt or .markdown file.', 'info');
}
}
});
}
if (noteContentInput && markdownPreview && typeof showdown !== 'undefined') {
const converter = new showdown.Converter({sanitize: true});
window.updateMarkdownPreview = function() {
markdownPreview.innerHTML = converter.makeHtml(noteContentInput.value);
}
noteContentInput.addEventListener('input', window.updateMarkdownPreview);
if(noteContentInput.value) window.updateMarkdownPreview();
} else if (noteContentInput && markdownPreview) {
noteContentInput.addEventListener('input', () => {
markdownPreview.textContent = "Preview updates on save (or include Showdown.js library for live preview).";
});
if(noteContentInput.value) markdownPreview.textContent = "Preview updates on save (or include Showdown.js library for live preview).";
}
// --- Table Sorting ---
document.querySelectorAll('.notes-table th[data-sort]').forEach(headerCell => {
headerCell.addEventListener('click', () => {
const sortBy = headerCell.dataset.sort;
const currentUrl = new URL(window.location.href);
const currentSortBy = currentUrl.searchParams.get('sort_by');
const currentSortOrder = currentUrl.searchParams.get('sort_order') || 'asc'; // Default to asc if not set
let newSortOrder = 'asc';
if (sortBy === currentSortBy && currentSortOrder.toLowerCase() === 'asc') {
newSortOrder = 'desc';
}
// If different column, or current is desc, start with asc for the new column
currentUrl.searchParams.set('sort_by', sortBy);
currentUrl.searchParams.set('sort_order', newSortOrder);
window.location.href = currentUrl.toString();
});
});
}); // End DOMContentLoaded
function handleAjaxForm(formId) { // Removed successRedirectPage, rely on JSON
const form = document.getElementById(formId);
if (form) {
form.addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(form);
const action = formData.get('action'); // Get action from FormData
if (action === 'register') {
const password = formData.get('password');
const confirmPassword = formData.get('confirm_password');
if (password !== confirmPassword) {
displayMessage('Passwords do not match.', 'danger');
return;
}
let criteriaMet = 0;
const criteriaElements = document.querySelectorAll('#password-strength ul li');
criteriaElements.forEach(li => {
if (li.classList.contains('valid')) {
criteriaMet++;
}
});
if (criteriaMet < criteriaElements.length) { // Check if all criteria are met
displayMessage('Password does not meet all requirements.', 'danger');
return;
}
}
fetch('index.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.message) { // Display message if provided
displayMessage(data.message, data.success ? 'success' : 'danger');
}
if (data.success && data.redirect) {
setTimeout(() => { window.location.href = data.redirect; }, data.message ? 1000 : 0); // Delay if message shown
} else if (data.success && (action === 'create_note' || action === 'update_note')) {
// Specific redirect for note actions if not overridden by data.redirect
setTimeout(() => { window.location.href = 'index.php?page=dashboard'; }, data.message ? 1000 : 0);
}
})
.catch(error => {
console.error('Error:', error);
displayMessage('A network error occurred. Please try again.', 'danger');
});
});
}
}
function displayMessage(message, type = 'info') {
const existingAlert = document.querySelector('#alert-container .alert');
if (existingAlert) {
existingAlert.remove();
}
const alertDiv = document.createElement('div');
alertDiv.className = `alert alert-${type}`;
alertDiv.innerHTML = message; // Use innerHTML if message contains HTML (e.g. list from server)
let alertContainer = document.getElementById('alert-container');
if (!alertContainer) {
alertContainer = document.createElement('div');
alertContainer.id = 'alert-container';
alertContainer.style.position = 'fixed';
alertContainer.style.top = '70px';
alertContainer.style.left = '50%';
alertContainer.style.transform = 'translateX(-50%)';
alertContainer.style.zIndex = '1000';
alertContainer.style.width = 'auto'; // Fit content
alertContainer.style.minWidth = '300px';
alertContainer.style.maxWidth = '90%';
document.body.prepend(alertContainer);
}
alertContainer.appendChild(alertDiv);
setTimeout(() => {
alertDiv.style.opacity = '0';
alertDiv.style.transition = 'opacity 0.5s ease-out';
setTimeout(() => {
if (alertDiv.parentNode) alertDiv.remove();
}, 500);
}, 5000);
}

View File

@ -32,10 +32,6 @@ class NotesModel extends Database
$erg=$stmt->fetchAll(\PDO::FETCH_ASSOC);
foreach($erg as $key=>$row) {
$erg[$key]['id']+=0;
}
return $erg;
}
}

View File

@ -1,25 +1,5 @@
<?php include dirname(__DIR__).'/header.phtml'; ?>
<script>
document.querySelectorAll('.notes-table th[data-sort]').forEach(headerCell => {
headerCell.addEventListener('click', () => {
const sortBy = headerCell.dataset.sort;
const currentUrl = new URL(window.location.href);
const currentSortBy = currentUrl.searchParams.get('sort_by');
const currentSortOrder = currentUrl.searchParams.get('sort_order') || 'asc'; // Default to asc if not set
let newSortOrder = 'asc';
if (sortBy === currentSortBy && currentSortOrder.toLowerCase() === 'asc') {
newSortOrder = 'desc';
}
// If different column, or current is desc, start with asc for the new column
currentUrl.searchParams.set('sort_by', sortBy);
currentUrl.searchParams.set('sort_order', newSortOrder);
window.location.href = currentUrl.toString();
});
});
</script>
<?php include dirname(__DIR__).'/header.phtml'; ?>
<script src="JavaScript/script.js"></script>
<h2>Notes</h2>