added sorting functionality
This commit is contained in:
parent
fd08f04600
commit
e62a0ec2af
278
JavaScript/script.js
Normal file
278
JavaScript/script.js
Normal 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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user