Add Archive and Delete buttons to document details along with button titles to state what the button does, also added a from var to fix redirect behavior if deleting from document details go back to documents listing

This commit is contained in:
johnnyq
2025-10-24 14:46:09 -04:00
parent 559506fc90
commit 04bae8dc37
3 changed files with 402 additions and 369 deletions

View File

@@ -163,17 +163,19 @@ $page_title = $row['document_name'];
<div class="col-md-3 d-print-none"> <div class="col-md-3 d-print-none">
<div class="row"> <div class="row">
<div class="col-12 mb-3"> <div class="col-12 mb-3">
<button type="button" class="btn btn-primary ajax-modal mr-2" <button type="button" class="btn btn-primary ajax-modal mr-1"
data-modal-size="lg" data-modal-size="lg"
data-modal-url="modals/document/document_edit.php?id=<?= $document_id ?>"> data-modal-url="modals/document/document_edit.php?id=<?= $document_id ?>">
<i class="fas fa-fw fa-edit"></i> <i class="fas fa-fw fa-edit" title="Edit"></i>
</button> </button>
<button type="button" class="btn btn-secondary mr-2" data-toggle="modal" data-target="#shareModal" <button type="button" class="btn btn-secondary mr-1" data-toggle="modal" data-target="#shareModal"
onclick="populateShareModal(<?php echo "$client_id, 'Document', $document_id"; ?>)"> onclick="populateShareModal(<?php echo "$client_id, 'Document', $document_id"; ?>)">
<i class="fas fa-fw fa-share"></i> <i class="fas fa-fw fa-share" title="Share"></i>
</button> </button>
<a class="btn btn-success mr-2" href="post.php?export_document=<?php echo $document_id; ?>"><i class='fas fa-fw fa-file-pdf'></i></a> <a class="btn btn-success mr-1" href="post.php?export_document=<?php echo $document_id; ?>"><i class='fas fa-fw fa-file-pdf' title="PDF Export"></i></a>
<button type="button" class="btn btn-secondary" onclick="window.print();"><i class="fas fa-fw fa-print"></i></button> <button type="button" class="btn btn-secondary mr-4" onclick="window.print();"><i class="fas fa-fw fa-print" title="Print"></i></button>
<a class="btn btn-warning mr-1 confirm-link" href="post.php?archive_document=<?= $document_id ?>" title="Archive"><i class='fas fa-fw fa-archive'></i></a>
<a class="btn btn-danger confirm-link" href="post.php?delete_document=<?= $document_id ?>&from=document_details" title="Delete"><i class='fas fa-fw fa-trash-alt'></i></a>
</div> </div>
</div> </div>
<div class="card card-body bg-light"> <div class="card card-body bg-light">

View File

@@ -633,7 +633,15 @@ if (isset($_GET['delete_document'])) {
flash_alert("Document <strong>$document_name</strong> deleted and all versions", 'error'); flash_alert("Document <strong>$document_name</strong> deleted and all versions", 'error');
// Determine redirect behavior
// If there's a "from" parameter, we can use it to decide where to go
if (isset($_GET['from']) && $_GET['from'] === 'document_details') {
// User deleted from document_details.php
redirect("documents.php?client_id=$client_id");
} else {
// Default behavior — redirect back to previous page
redirect(); redirect();
}
} }

209
js/app.js
View File

@@ -1,11 +1,11 @@
$(document).ready(function() { $(document).ready(function() {
//Prevents resubmit on forms // Prevents resubmit on forms
if(window.history.replaceState){ if (window.history.replaceState) {
window.history.replaceState(null, null, window.location.href); window.history.replaceState(null, null, window.location.href);
} }
// Slide alert up after 4 secs // Slide alert up after 4 secs
$("#alert").fadeTo(5000, 500).slideUp(500, function(){ $("#alert").fadeTo(5000, 500).slideUp(500, function() {
$("#alert").slideUp(500); $("#alert").slideUp(500);
}); });
@@ -27,14 +27,14 @@ $(document).ready(function() {
menubar: false, menubar: false,
statusbar: false, statusbar: false,
toolbar: [ toolbar: [
{ name: 'styles', items: [ 'styles' ] }, { name: 'styles', items: ['styles'] },
{ name: 'formatting', items: [ 'bold', 'italic', 'forecolor' ] }, { name: 'formatting', items: ['bold', 'italic', 'forecolor'] },
{ name: 'link', items: [ 'link'] }, { name: 'link', items: ['link'] },
{ name: 'lists', items: [ 'bullist', 'numlist' ] }, { name: 'lists', items: ['bullist', 'numlist'] },
{ name: 'alignment', items: [ 'alignleft', 'aligncenter', 'alignright', 'alignjustify' ] }, { name: 'alignment', items: ['alignleft', 'aligncenter', 'alignright', 'alignjustify'] },
{ name: 'indentation', items: [ 'outdent', 'indent' ] }, { name: 'indentation', items: ['outdent', 'indent'] },
{ name: 'table', items: [ 'table' ] }, { name: 'table', items: ['table'] },
{ name: 'extra', items: [ 'code', 'fullscreen' ] } { name: 'extra', items: ['code', 'fullscreen'] }
], ],
mobile: { mobile: {
menubar: false, menubar: false,
@@ -43,6 +43,26 @@ $(document).ready(function() {
}, },
convert_urls: false, convert_urls: false,
plugins: 'link image lists table code codesample fullscreen autoresize', plugins: 'link image lists table code codesample fullscreen autoresize',
setup: function (editor) {
editor.on('init', function() {
window.onbeforeunload = function() {
// If editor is dirty AND not inside a visible modal → warn
const inVisibleModal = editor.getContainer()?.closest('.modal.show');
if (!inVisibleModal && editor.isDirty()) {
return "You have unsaved changes. Are you sure you want to leave?";
}
};
// When the modal closes, mark editor clean
const modal = editor.getContainer()?.closest('.modal');
if (modal) {
modal.addEventListener('hidden.bs.modal', () => {
editor.undoManager.clear();
editor.setDirty(false);
});
}
});
},
license_key: 'gpl' license_key: 'gpl'
}); });
@@ -59,15 +79,15 @@ $(document).ready(function() {
menubar: false, menubar: false,
statusbar: false, statusbar: false,
toolbar: [ toolbar: [
{ name: 'styles', items: [ 'styles' ] }, { name: 'styles', items: ['styles'] },
{ name: 'formatting', items: [ 'bold', 'italic', 'forecolor' ] }, { name: 'formatting', items: ['bold', 'italic', 'forecolor'] },
{ name: 'link', items: [ 'link'] }, { name: 'link', items: ['link'] },
{ name: 'lists', items: [ 'bullist', 'numlist' ] }, { name: 'lists', items: ['bullist', 'numlist'] },
{ name: 'alignment', items: [ 'alignleft', 'aligncenter', 'alignright', 'alignjustify' ] }, { name: 'alignment', items: ['alignleft', 'aligncenter', 'alignright', 'alignjustify'] },
{ name: 'indentation', items: [ 'outdent', 'indent' ] }, { name: 'indentation', items: ['outdent', 'indent'] },
{ name: 'table', items: [ 'table' ] }, { name: 'table', items: ['table'] },
{ name: 'extra', items: [ 'code', 'fullscreen' ] }, { name: 'extra', items: ['code', 'fullscreen'] },
{ name: 'ai', items: [ 'reword', 'undo', 'redo' ] } { name: 'ai', items: ['reword', 'undo', 'redo'] }
], ],
mobile: { mobile: {
menubar: false, menubar: false,
@@ -78,6 +98,25 @@ $(document).ready(function() {
plugins: 'link image lists table code codesample fullscreen autoresize', plugins: 'link image lists table code codesample fullscreen autoresize',
license_key: 'gpl', license_key: 'gpl',
setup: function(editor) { setup: function(editor) {
editor.on('init', function() {
window.onbeforeunload = function() {
// If editor is dirty AND not inside a visible modal → warn
const inVisibleModal = editor.getContainer()?.closest('.modal.show');
if (!inVisibleModal && editor.isDirty()) {
return "You have unsaved changes. Are you sure you want to leave?";
}
};
// When the modal closes, mark editor clean
const modal = editor.getContainer()?.closest('.modal');
if (modal) {
modal.addEventListener('hidden.bs.modal', () => {
editor.undoManager.clear();
editor.setDirty(false);
});
}
});
var rewordButtonApi; var rewordButtonApi;
editor.ui.registry.addButton('reword', { editor.ui.registry.addButton('reword', {
@@ -110,13 +149,9 @@ $(document).ready(function() {
editor.setContent(data.rewordedText || 'Error: Could not reword the text.'); editor.setContent(data.rewordedText || 'Error: Could not reword the text.');
}); });
// Hide the progress indicator
editor.setProgressState(false); editor.setProgressState(false);
// Re-enable the Reword button
rewordButtonApi.setEnabled(true); rewordButtonApi.setEnabled(true);
// Optional: Show a success notification
editor.notificationManager.open({ editor.notificationManager.open({
text: 'Text reworded successfully!', text: 'Text reworded successfully!',
type: 'success', type: 'success',
@@ -125,14 +160,8 @@ $(document).ready(function() {
}) })
.catch(error => { .catch(error => {
console.error('Error:', error); console.error('Error:', error);
// Hide the progress indicator
editor.setProgressState(false); editor.setProgressState(false);
// Re-enable the Reword button
rewordButtonApi.setEnabled(true); rewordButtonApi.setEnabled(true);
// Show an error notification
editor.notificationManager.open({ editor.notificationManager.open({
text: 'An error occurred while rewording the text.', text: 'An error occurred while rewording the text.',
type: 'error', type: 'error',
@@ -142,15 +171,13 @@ $(document).ready(function() {
}, },
onSetup: function(buttonApi) { onSetup: function(buttonApi) {
rewordButtonApi = buttonApi; rewordButtonApi = buttonApi;
return function() { return function() {};
// Cleanup when the editor is destroyed (if necessary)
};
} }
}); });
} }
}); });
// Initialize TinyMCE AI // Initialize TinyMCE AI for Tickets
tinymce.init({ tinymce.init({
selector: '.tinymceTicket', selector: '.tinymceTicket',
browser_spellcheck: true, browser_spellcheck: true,
@@ -169,7 +196,7 @@ $(document).ready(function() {
{ name: 'lists', items: ['bullist', 'numlist'] }, { name: 'lists', items: ['bullist', 'numlist'] },
{ name: 'indentation', items: ['outdent', 'indent'] }, { name: 'indentation', items: ['outdent', 'indent'] },
{ name: 'ai', items: ['reword', 'undo', 'redo'] }, { name: 'ai', items: ['reword', 'undo', 'redo'] },
{ name: 'custom', items: ['redactButton'] }, // Add custom redact button to toolbar { name: 'custom', items: ['redactButton'] },
{ name: 'code', items: ['code'] }, { name: 'code', items: ['code'] },
], ],
mobile: { mobile: {
@@ -180,46 +207,50 @@ $(document).ready(function() {
plugins: 'link image lists table code codesample fullscreen autoresize code', plugins: 'link image lists table code codesample fullscreen autoresize code',
license_key: 'gpl', license_key: 'gpl',
setup: function(editor) { setup: function(editor) {
editor.on('init', function() {
window.onbeforeunload = function() {
// If editor is dirty AND not inside a visible modal → warn
const inVisibleModal = editor.getContainer()?.closest('.modal.show');
if (!inVisibleModal && editor.isDirty()) {
return "You have unsaved changes. Are you sure you want to leave?";
}
};
// When the modal closes, mark editor clean
const modal = editor.getContainer()?.closest('.modal');
if (modal) {
modal.addEventListener('hidden.bs.modal', () => {
editor.undoManager.clear();
editor.setDirty(false);
});
}
});
var rewordButtonApi; var rewordButtonApi;
// Define the Reword button (AI-related button)
editor.ui.registry.addButton('reword', { editor.ui.registry.addButton('reword', {
icon: 'ai', // Example icon for AI rewording icon: 'ai',
tooltip: 'Reword Text', tooltip: 'Reword Text',
onAction: function() { onAction: function() {
var content = editor.getContent(); var content = editor.getContent();
// Disable the Reword button
rewordButtonApi.setEnabled(false); rewordButtonApi.setEnabled(false);
// Show the progress indicator
editor.setProgressState(true); editor.setProgressState(true);
fetch('post.php?ai_reword', { fetch('post.php?ai_reword', {
method: 'POST', method: 'POST',
headers: { headers: { 'Content-Type': 'application/json' },
'Content-Type': 'application/json',
},
body: JSON.stringify({ text: content }), body: JSON.stringify({ text: content }),
}) })
.then(response => { .then(response => {
if (!response.ok) { if (!response.ok) throw new Error('Network response was not ok');
throw new Error('Network response was not ok');
}
return response.json(); return response.json();
}) })
.then(data => { .then(data => {
editor.undoManager.transact(function() { editor.undoManager.transact(function() {
editor.setContent(data.rewordedText || 'Error: Could not reword the text.'); editor.setContent(data.rewordedText || 'Error: Could not reword the text.');
}); });
// Hide the progress indicator
editor.setProgressState(false); editor.setProgressState(false);
// Re-enable the Reword button
rewordButtonApi.setEnabled(true); rewordButtonApi.setEnabled(true);
// Optional: Show a success notification
editor.notificationManager.open({ editor.notificationManager.open({
text: 'Text reworded successfully!', text: 'Text reworded successfully!',
type: 'success', type: 'success',
@@ -228,14 +259,8 @@ $(document).ready(function() {
}) })
.catch(error => { .catch(error => {
console.error('Error:', error); console.error('Error:', error);
// Hide the progress indicator
editor.setProgressState(false); editor.setProgressState(false);
// Re-enable the Reword button
rewordButtonApi.setEnabled(true); rewordButtonApi.setEnabled(true);
// Show an error notification
editor.notificationManager.open({ editor.notificationManager.open({
text: 'An error occurred while rewording the text.', text: 'An error occurred while rewording the text.',
type: 'error', type: 'error',
@@ -245,24 +270,17 @@ $(document).ready(function() {
}, },
onSetup: function(buttonApi) { onSetup: function(buttonApi) {
rewordButtonApi = buttonApi; rewordButtonApi = buttonApi;
return function() { return function() {};
// Cleanup when the editor is destroyed (if necessary)
};
} }
}); });
// Add the Redact button
editor.ui.registry.addButton('redactButton', { editor.ui.registry.addButton('redactButton', {
icon: 'permanent-pen', icon: 'permanent-pen',
tooltip: 'Redact Text', // Tooltip text for the button tooltip: 'Redact Text',
onAction: function() { onAction: function() {
var selectedText = editor.selection.getContent({ format: 'text' }); var selectedText = editor.selection.getContent({ format: 'text' });
if (selectedText) { if (selectedText) {
// Replace the selected text with [REDACTED] in bold red
var newContent = '<span style="font-weight: bold; color: red;">[REDACTED]</span>'; var newContent = '<span style="font-weight: bold; color: red;">[REDACTED]</span>';
// Replace selected content with the new content
editor.selection.setContent(newContent); editor.selection.setContent(newContent);
} else { } else {
alert('Please select a word to redact'); alert('Please select a word to redact');
@@ -272,8 +290,9 @@ $(document).ready(function() {
} }
}); });
// Initialize TinyMCE Redact-only
tinymce.init({ tinymce.init({
selector: '.tinymceRedact', // Your selector selector: '.tinymceRedact',
browser_spellcheck: true, browser_spellcheck: true,
contextmenu: false, contextmenu: false,
resize: true, resize: true,
@@ -283,35 +302,48 @@ $(document).ready(function() {
branding: false, branding: false,
menubar: false, menubar: false,
statusbar: false, statusbar: false,
toolbar: 'redactButton', // Only the redact button in the toolbar toolbar: 'redactButton',
mobile: { mobile: {
menubar: false, menubar: false,
plugins: 'autosave lists autolink', plugins: 'autosave lists autolink',
toolbar: 'redactButton' // Only the redact button on mobile toolbar: 'redactButton'
}, },
convert_urls: false, convert_urls: false,
plugins: 'link image lists table code fullscreen autoresize', plugins: 'link image lists table code fullscreen autoresize',
license_key: 'gpl', license_key: 'gpl',
setup: function(editor) { setup: function(editor) {
// Disable all text input and backspace/delete actions
editor.on('init', function() {
window.onbeforeunload = function() {
// If editor is dirty AND not inside a visible modal → warn
const inVisibleModal = editor.getContainer()?.closest('.modal.show');
if (!inVisibleModal && editor.isDirty()) {
return "You have unsaved changes. Are you sure you want to leave?";
}
};
// When the modal closes, mark editor clean
const modal = editor.getContainer()?.closest('.modal');
if (modal) {
modal.addEventListener('hidden.bs.modal', () => {
editor.undoManager.clear();
editor.setDirty(false);
});
}
});
editor.on('keydown', function(e) { editor.on('keydown', function(e) {
// Prevent all key events (including backspace and delete)
e.preventDefault(); e.preventDefault();
}); });
// Add custom toolbar button with Font Awesome icon and label
editor.ui.registry.addButton('redactButton', { editor.ui.registry.addButton('redactButton', {
icon: 'permanent-pen', icon: 'permanent-pen',
tooltip: 'Redact', // Tooltip text for the button tooltip: 'Redact',
text: 'REDACT', // Add the text next to the icon text: 'REDACT',
onAction: function() { onAction: function() {
var selectedText = editor.selection.getContent({ format: 'text' }); var selectedText = editor.selection.getContent({ format: 'text' });
if (selectedText) { if (selectedText) {
// Replace the selected text with [REDACTED] in bold red
var newContent = '<span style="font-weight: bold; color: red;">[REDACTED]</span>'; var newContent = '<span style="font-weight: bold; color: red;">[REDACTED]</span>';
// Replace selected content with the new content
editor.selection.setContent(newContent); editor.selection.setContent(newContent);
} else { } else {
alert('Please select a word to redact'); alert('Please select a word to redact');
@@ -321,23 +353,16 @@ $(document).ready(function() {
} }
}); });
// DateTime // DateTime
$('.datetimepicker').datetimepicker({ $('.datetimepicker').datetimepicker();
});
// Data Input Mask // Data Input Mask
$('[data-mask]').inputmask(); $('[data-mask]').inputmask();
// ClipboardJS // ClipboardJS fix for Bootstrap modals
//Fix to allow Clipboard Copying within Bootstrap Modals
//For use in Bootstrap Modals or with any other library that changes the focus you'll want to set the focused element as the container value.
$.fn.modal.Constructor.prototype._enforceFocus = function() {}; $.fn.modal.Constructor.prototype._enforceFocus = function() {};
// Tooltip // Tooltip
$('button').tooltip({ $('button').tooltip({
trigger: 'click', trigger: 'click',
placement: 'bottom' placement: 'bottom'
@@ -356,7 +381,6 @@ $(document).ready(function() {
} }
// Clipboard // Clipboard
var clipboard = new ClipboardJS('.clipboardjs'); var clipboard = new ClipboardJS('.clipboardjs');
clipboard.on('success', function(e) { clipboard.on('success', function(e) {
@@ -370,11 +394,10 @@ $(document).ready(function() {
}); });
// Enable Popovers // Enable Popovers
$(function () { $(function() {
$('[data-toggle="popover"]').popover() $('[data-toggle="popover"]').popover();
}); });
// Data Tables // Data Tables
new DataTable('.dataTables'); new DataTable('.dataTables');
}); });