mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 11:11:10 -05:00
good stuff
This commit is contained in:
187
static/css/inline-edit.css
Normal file
187
static/css/inline-edit.css
Normal file
@@ -0,0 +1,187 @@
|
||||
/* Inline editing styles */
|
||||
.editable-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
[data-editable] {
|
||||
position: relative;
|
||||
padding: 0.25rem;
|
||||
border-radius: 0.25rem;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
[data-editable]:hover {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.dark [data-editable]:hover {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
[data-edit-button] {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
right: 0.5rem;
|
||||
top: 0.5rem;
|
||||
transition: opacity 0.2s;
|
||||
padding: 0.5rem;
|
||||
border-radius: 0.375rem;
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.dark [data-edit-button] {
|
||||
background-color: rgba(31, 41, 55, 0.9);
|
||||
}
|
||||
|
||||
.editable-container:hover [data-edit-button] {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.form-input, .form-textarea, .form-select {
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 0.375rem;
|
||||
background-color: white;
|
||||
transition: border-color 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
|
||||
.dark .form-input, .dark .form-textarea, .dark .form-select {
|
||||
background-color: #1f2937;
|
||||
border-color: #374151;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.form-input:focus, .form-textarea:focus, .form-select:focus {
|
||||
outline: none;
|
||||
border-color: #4f46e5;
|
||||
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
|
||||
}
|
||||
|
||||
.dark .form-input:focus, .dark .form-textarea:focus, .dark .form-select:focus {
|
||||
border-color: #6366f1;
|
||||
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
|
||||
}
|
||||
|
||||
/* Notifications */
|
||||
.notification {
|
||||
position: fixed;
|
||||
bottom: 1rem;
|
||||
right: 1rem;
|
||||
padding: 1rem;
|
||||
border-radius: 0.5rem;
|
||||
color: white;
|
||||
max-width: 24rem;
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||||
z-index: 50;
|
||||
animation: slide-in 0.3s ease-out;
|
||||
}
|
||||
|
||||
.notification-success {
|
||||
background-color: #059669;
|
||||
}
|
||||
|
||||
.dark .notification-success {
|
||||
background-color: #047857;
|
||||
}
|
||||
|
||||
.notification-error {
|
||||
background-color: #dc2626;
|
||||
}
|
||||
|
||||
.dark .notification-error {
|
||||
background-color: #b91c1c;
|
||||
}
|
||||
|
||||
.notification-info {
|
||||
background-color: #3b82f6;
|
||||
}
|
||||
|
||||
.dark .notification-info {
|
||||
background-color: #2563eb;
|
||||
}
|
||||
|
||||
@keyframes slide-in {
|
||||
from {
|
||||
transform: translateX(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add/Edit Form Styles */
|
||||
.form-section {
|
||||
@apply space-y-6;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
@apply space-y-2;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
@apply block text-sm font-medium text-gray-700 dark:text-gray-300;
|
||||
}
|
||||
|
||||
.form-error {
|
||||
@apply mt-1 text-sm text-red-600 dark:text-red-400;
|
||||
}
|
||||
|
||||
.form-help {
|
||||
@apply mt-1 text-sm text-gray-500 dark:text-gray-400;
|
||||
}
|
||||
|
||||
/* Button Styles */
|
||||
.btn {
|
||||
@apply inline-flex items-center justify-center px-4 py-2 font-medium transition-colors rounded-lg;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
@apply text-white bg-blue-600 hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
@apply text-gray-700 bg-gray-200 hover:bg-gray-300 dark:bg-gray-600 dark:text-gray-200 dark:hover:bg-gray-500;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
@apply text-white bg-red-600 hover:bg-red-700 dark:bg-red-500 dark:hover:bg-red-600;
|
||||
}
|
||||
|
||||
/* Status Badges */
|
||||
.status-badge {
|
||||
@apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium;
|
||||
}
|
||||
|
||||
.status-operating {
|
||||
@apply text-green-800 bg-green-100 dark:bg-green-900 dark:text-green-200;
|
||||
}
|
||||
|
||||
.status-closed {
|
||||
@apply text-red-800 bg-red-100 dark:bg-red-900 dark:text-red-200;
|
||||
}
|
||||
|
||||
.status-construction {
|
||||
@apply text-yellow-800 bg-yellow-100 dark:bg-yellow-900 dark:text-yellow-200;
|
||||
}
|
||||
|
||||
/* Navigation Links */
|
||||
.nav-link {
|
||||
@apply flex items-center px-3 py-2 text-gray-700 transition-colors rounded-lg dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700;
|
||||
}
|
||||
|
||||
.nav-link i {
|
||||
@apply mr-2;
|
||||
}
|
||||
|
||||
/* Menu Items */
|
||||
.menu-item {
|
||||
@apply flex items-center w-full px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700;
|
||||
}
|
||||
|
||||
.menu-item i {
|
||||
@apply mr-3;
|
||||
}
|
||||
@@ -2391,6 +2391,10 @@ select {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.right-0 {
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
.top-1\/2 {
|
||||
top: 50%;
|
||||
}
|
||||
@@ -2399,6 +2403,10 @@ select {
|
||||
grid-column: span 2 / span 2;
|
||||
}
|
||||
|
||||
.col-span-3 {
|
||||
grid-column: span 3 / span 3;
|
||||
}
|
||||
|
||||
.col-span-full {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
@@ -2430,6 +2438,10 @@ select {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.mb-3 {
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.mb-4 {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
@@ -2514,6 +2526,10 @@ select {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.h-16 {
|
||||
height: 4rem;
|
||||
}
|
||||
|
||||
.h-24 {
|
||||
height: 6rem;
|
||||
}
|
||||
@@ -2558,6 +2574,10 @@ select {
|
||||
width: 1rem;
|
||||
}
|
||||
|
||||
.w-48 {
|
||||
width: 12rem;
|
||||
}
|
||||
|
||||
.w-5 {
|
||||
width: 1.25rem;
|
||||
}
|
||||
@@ -2630,6 +2650,10 @@ select {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.justify-end {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.justify-center {
|
||||
justify-content: center;
|
||||
}
|
||||
@@ -2726,6 +2750,16 @@ select {
|
||||
border-radius: 0.75rem;
|
||||
}
|
||||
|
||||
.rounded-l-lg {
|
||||
border-top-left-radius: 0.5rem;
|
||||
border-bottom-left-radius: 0.5rem;
|
||||
}
|
||||
|
||||
.rounded-r-lg {
|
||||
border-top-right-radius: 0.5rem;
|
||||
border-bottom-right-radius: 0.5rem;
|
||||
}
|
||||
|
||||
.border {
|
||||
border-width: 1px;
|
||||
}
|
||||
@@ -2805,6 +2839,11 @@ select {
|
||||
background-color: rgb(219 234 254 / 0.9);
|
||||
}
|
||||
|
||||
.bg-blue-50 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(239 246 255 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-blue-600 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(37 99 235 / var(--tw-bg-opacity));
|
||||
@@ -2834,6 +2873,11 @@ select {
|
||||
background-color: rgb(220 252 231 / 0.9);
|
||||
}
|
||||
|
||||
.bg-green-600 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(22 163 74 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-primary\/10 {
|
||||
background-color: rgb(79 70 229 / 0.1);
|
||||
}
|
||||
@@ -2847,6 +2891,11 @@ select {
|
||||
background-color: rgb(254 226 226 / 0.9);
|
||||
}
|
||||
|
||||
.bg-red-600 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(220 38 38 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-white {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
|
||||
@@ -2894,14 +2943,14 @@ select {
|
||||
--tw-gradient-stops: var(--tw-gradient-from), #eff6ff var(--tw-gradient-via-position), var(--tw-gradient-to);
|
||||
}
|
||||
|
||||
.to-indigo-50 {
|
||||
--tw-gradient-to: #eef2ff var(--tw-gradient-to-position);
|
||||
}
|
||||
|
||||
.to-secondary {
|
||||
--tw-gradient-to: #e11d48 var(--tw-gradient-to-position);
|
||||
}
|
||||
|
||||
.to-indigo-50 {
|
||||
--tw-gradient-to: #eef2ff var(--tw-gradient-to-position);
|
||||
}
|
||||
|
||||
.bg-clip-text {
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
@@ -3073,6 +3122,11 @@ select {
|
||||
color: rgb(37 99 235 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-blue-700 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(29 78 216 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-blue-800 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(30 64 175 / var(--tw-text-opacity));
|
||||
@@ -3329,15 +3383,30 @@ select {
|
||||
background-color: rgb(229 231 235 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.hover\:bg-gray-300:hover {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(209 213 219 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.hover\:bg-gray-50:hover {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(249 250 251 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.hover\:bg-green-700:hover {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(21 128 61 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.hover\:bg-primary\/10:hover {
|
||||
background-color: rgb(79 70 229 / 0.1);
|
||||
}
|
||||
|
||||
.hover\:bg-red-700:hover {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(185 28 28 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.hover\:from-primary\/90:hover {
|
||||
--tw-gradient-from: rgb(79 70 229 / 0.9) var(--tw-gradient-from-position);
|
||||
--tw-gradient-to: rgb(79 70 229 / 0) var(--tw-gradient-to-position);
|
||||
@@ -3427,6 +3496,11 @@ select {
|
||||
background-color: rgb(96 165 250 / 0.3);
|
||||
}
|
||||
|
||||
.dark\:bg-blue-500:is(.dark *) {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(59 130 246 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.dark\:bg-blue-700:is(.dark *) {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(29 78 216 / var(--tw-bg-opacity));
|
||||
@@ -3436,6 +3510,15 @@ select {
|
||||
background-color: rgb(30 64 175 / 0.3);
|
||||
}
|
||||
|
||||
.dark\:bg-blue-900:is(.dark *) {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(30 58 138 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.dark\:bg-blue-900\/50:is(.dark *) {
|
||||
background-color: rgb(30 58 138 / 0.5);
|
||||
}
|
||||
|
||||
.dark\:bg-gray-600:is(.dark *) {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(75 85 99 / var(--tw-bg-opacity));
|
||||
@@ -3463,6 +3546,11 @@ select {
|
||||
background-color: rgb(31 41 55 / 0.9);
|
||||
}
|
||||
|
||||
.dark\:bg-green-500:is(.dark *) {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(34 197 94 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.dark\:bg-green-700:is(.dark *) {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(21 128 61 / var(--tw-bg-opacity));
|
||||
@@ -3472,6 +3560,16 @@ select {
|
||||
background-color: rgb(22 101 52 / 0.3);
|
||||
}
|
||||
|
||||
.dark\:bg-green-900:is(.dark *) {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(20 83 45 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.dark\:bg-red-500:is(.dark *) {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.dark\:bg-red-700:is(.dark *) {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(185 28 28 / var(--tw-bg-opacity));
|
||||
@@ -3481,6 +3579,11 @@ select {
|
||||
background-color: rgb(153 27 27 / 0.3);
|
||||
}
|
||||
|
||||
.dark\:bg-red-900:is(.dark *) {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(127 29 29 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.dark\:bg-yellow-400\/30:is(.dark *) {
|
||||
background-color: rgb(250 204 21 / 0.3);
|
||||
}
|
||||
@@ -3494,6 +3597,11 @@ select {
|
||||
background-color: rgb(133 77 14 / 0.3);
|
||||
}
|
||||
|
||||
.dark\:bg-yellow-900:is(.dark *) {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(113 63 18 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.dark\:from-gray-950:is(.dark *) {
|
||||
--tw-gradient-from: #030712 var(--tw-gradient-from-position);
|
||||
--tw-gradient-to: rgb(3 7 18 / 0) var(--tw-gradient-to-position);
|
||||
@@ -3549,6 +3657,11 @@ select {
|
||||
color: rgb(220 252 231 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.dark\:text-green-200:is(.dark *) {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(187 247 208 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.dark\:text-primary:is(.dark *) {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(79 70 229 / var(--tw-text-opacity));
|
||||
@@ -3559,6 +3672,16 @@ select {
|
||||
color: rgb(254 226 226 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.dark\:text-red-200:is(.dark *) {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(254 202 202 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.dark\:text-red-400:is(.dark *) {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(248 113 113 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.dark\:text-white:is(.dark *) {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(255 255 255 / var(--tw-text-opacity));
|
||||
@@ -3598,20 +3721,45 @@ select {
|
||||
--tw-ring-color: rgb(250 204 21 / 0.3);
|
||||
}
|
||||
|
||||
.dark\:hover\:bg-blue-600:hover:is(.dark *) {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(37 99 235 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.dark\:hover\:bg-gray-500:hover:is(.dark *) {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(107 114 128 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.dark\:hover\:bg-gray-700:hover:is(.dark *) {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(55 65 81 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.dark\:hover\:bg-green-600:hover:is(.dark *) {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(22 163 74 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.dark\:hover\:bg-primary\/20:hover:is(.dark *) {
|
||||
background-color: rgb(79 70 229 / 0.2);
|
||||
}
|
||||
|
||||
.dark\:hover\:bg-red-600:hover:is(.dark *) {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(220 38 38 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.dark\:hover\:text-blue-300:hover:is(.dark *) {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(147 197 253 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.dark\:hover\:text-blue-400:hover:is(.dark *) {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(96 165 250 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.dark\:hover\:text-primary:hover:is(.dark *) {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(79 70 229 / var(--tw-text-opacity));
|
||||
|
||||
262
static/js/inline-edit.js
Normal file
262
static/js/inline-edit.js
Normal file
@@ -0,0 +1,262 @@
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Handle edit button clicks
|
||||
document.querySelectorAll('[data-edit-button]').forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
const contentId = this.dataset.contentId;
|
||||
const contentType = this.dataset.contentType;
|
||||
const editableFields = document.querySelectorAll(`[data-editable][data-content-id="${contentId}"]`);
|
||||
|
||||
// Toggle edit mode
|
||||
editableFields.forEach(field => {
|
||||
const currentValue = field.textContent.trim();
|
||||
const fieldName = field.dataset.fieldName;
|
||||
const fieldType = field.dataset.fieldType || 'text';
|
||||
|
||||
// Create input field
|
||||
let input;
|
||||
if (fieldType === 'textarea') {
|
||||
input = document.createElement('textarea');
|
||||
input.value = currentValue;
|
||||
input.rows = 4;
|
||||
} else if (fieldType === 'select') {
|
||||
input = document.createElement('select');
|
||||
// Get options from data attribute
|
||||
const options = JSON.parse(field.dataset.options || '[]');
|
||||
options.forEach(option => {
|
||||
const optionEl = document.createElement('option');
|
||||
optionEl.value = option.value;
|
||||
optionEl.textContent = option.label;
|
||||
optionEl.selected = option.value === currentValue;
|
||||
input.appendChild(optionEl);
|
||||
});
|
||||
} else if (fieldType === 'date') {
|
||||
input = document.createElement('input');
|
||||
input.type = 'date';
|
||||
input.value = currentValue;
|
||||
} else if (fieldType === 'number') {
|
||||
input = document.createElement('input');
|
||||
input.type = 'number';
|
||||
input.value = currentValue;
|
||||
if (field.dataset.min) input.min = field.dataset.min;
|
||||
if (field.dataset.max) input.max = field.dataset.max;
|
||||
if (field.dataset.step) input.step = field.dataset.step;
|
||||
} else {
|
||||
input = document.createElement('input');
|
||||
input.type = fieldType;
|
||||
input.value = currentValue;
|
||||
}
|
||||
|
||||
input.className = 'w-full border-gray-300 rounded-lg form-input dark:border-gray-600 dark:bg-gray-700 dark:text-white';
|
||||
input.dataset.originalValue = currentValue;
|
||||
input.dataset.fieldName = fieldName;
|
||||
|
||||
// Replace content with input
|
||||
field.textContent = '';
|
||||
field.appendChild(input);
|
||||
});
|
||||
|
||||
// Show save/cancel buttons
|
||||
const actionButtons = document.createElement('div');
|
||||
actionButtons.className = 'flex gap-2 mt-2';
|
||||
actionButtons.innerHTML = `
|
||||
<button class="px-4 py-2 text-white bg-blue-600 rounded-lg hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600" data-save-button>
|
||||
<i class="mr-2 fas fa-save"></i>Save Changes
|
||||
</button>
|
||||
<button class="px-4 py-2 text-gray-700 bg-gray-200 rounded-lg hover:bg-gray-300 dark:bg-gray-600 dark:text-gray-200 dark:hover:bg-gray-500" data-cancel-button>
|
||||
<i class="mr-2 fas fa-times"></i>Cancel
|
||||
</button>
|
||||
${this.dataset.requireReason ? `
|
||||
<div class="flex-grow">
|
||||
<input type="text" class="w-full border-gray-300 rounded-lg form-input dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
||||
placeholder="Reason for changes (required)"
|
||||
data-reason-input>
|
||||
<input type="text" class="w-full mt-1 border-gray-300 rounded-lg form-input dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
||||
placeholder="Source (optional)"
|
||||
data-source-input>
|
||||
</div>
|
||||
` : ''}
|
||||
`;
|
||||
|
||||
const container = editableFields[0].closest('.editable-container');
|
||||
container.appendChild(actionButtons);
|
||||
|
||||
// Hide edit button while editing
|
||||
this.style.display = 'none';
|
||||
});
|
||||
});
|
||||
|
||||
// Handle form submissions
|
||||
document.querySelectorAll('form[data-submit-type]').forEach(form => {
|
||||
form.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const submitType = this.dataset.submitType;
|
||||
const formData = new FormData(this);
|
||||
const data = {};
|
||||
|
||||
formData.forEach((value, key) => {
|
||||
data[key] = value;
|
||||
});
|
||||
|
||||
// Get CSRF token from meta tag
|
||||
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
|
||||
|
||||
// Submit form
|
||||
fetch(this.action, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': csrfToken
|
||||
},
|
||||
body: JSON.stringify({
|
||||
submission_type: submitType,
|
||||
...data
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.status === 'success') {
|
||||
showNotification(data.message, 'success');
|
||||
if (data.redirect_url) {
|
||||
window.location.href = data.redirect_url;
|
||||
}
|
||||
} else {
|
||||
showNotification(data.message, 'error');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showNotification('An error occurred while submitting the form.', 'error');
|
||||
console.error('Error:', error);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Handle save button clicks using event delegation
|
||||
document.addEventListener('click', function(e) {
|
||||
if (e.target.matches('[data-save-button]')) {
|
||||
const container = e.target.closest('.editable-container');
|
||||
const contentId = container.querySelector('[data-editable]').dataset.contentId;
|
||||
const contentType = container.querySelector('[data-edit-button]').dataset.contentType;
|
||||
const editableFields = container.querySelectorAll('[data-editable]');
|
||||
|
||||
// Collect changes
|
||||
const changes = {};
|
||||
editableFields.forEach(field => {
|
||||
const input = field.querySelector('input, textarea, select');
|
||||
if (input && input.value !== input.dataset.originalValue) {
|
||||
changes[input.dataset.fieldName] = input.value;
|
||||
}
|
||||
});
|
||||
|
||||
// If no changes, just cancel
|
||||
if (Object.keys(changes).length === 0) {
|
||||
cancelEdit(container);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get reason and source if required
|
||||
const reasonInput = container.querySelector('[data-reason-input]');
|
||||
const sourceInput = container.querySelector('[data-source-input]');
|
||||
const reason = reasonInput ? reasonInput.value : '';
|
||||
const source = sourceInput ? sourceInput.value : '';
|
||||
|
||||
// Validate reason if required
|
||||
if (reasonInput && !reason) {
|
||||
alert('Please provide a reason for your changes.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Get CSRF token from meta tag
|
||||
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
|
||||
|
||||
// Submit changes
|
||||
fetch(window.location.pathname, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': csrfToken
|
||||
},
|
||||
body: JSON.stringify({
|
||||
content_type: contentType,
|
||||
content_id: contentId,
|
||||
changes,
|
||||
reason,
|
||||
source
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.status === 'success') {
|
||||
if (data.auto_approved) {
|
||||
// Update the display immediately
|
||||
Object.entries(changes).forEach(([field, value]) => {
|
||||
const element = container.querySelector(`[data-editable][data-field-name="${field}"]`);
|
||||
if (element) {
|
||||
element.textContent = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
showNotification(data.message, 'success');
|
||||
if (data.redirect_url) {
|
||||
window.location.href = data.redirect_url;
|
||||
}
|
||||
} else {
|
||||
showNotification(data.message, 'error');
|
||||
}
|
||||
cancelEdit(container);
|
||||
})
|
||||
.catch(error => {
|
||||
showNotification('An error occurred while saving changes.', 'error');
|
||||
console.error('Error:', error);
|
||||
cancelEdit(container);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Handle cancel button clicks using event delegation
|
||||
document.addEventListener('click', function(e) {
|
||||
if (e.target.matches('[data-cancel-button]')) {
|
||||
const container = e.target.closest('.editable-container');
|
||||
cancelEdit(container);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function cancelEdit(container) {
|
||||
// Restore original content
|
||||
container.querySelectorAll('[data-editable]').forEach(field => {
|
||||
const input = field.querySelector('input, textarea, select');
|
||||
if (input) {
|
||||
field.textContent = input.dataset.originalValue;
|
||||
}
|
||||
});
|
||||
|
||||
// Remove action buttons
|
||||
const actionButtons = container.querySelector('.flex.gap-2');
|
||||
if (actionButtons) {
|
||||
actionButtons.remove();
|
||||
}
|
||||
|
||||
// Show edit button
|
||||
const editButton = container.querySelector('[data-edit-button]');
|
||||
if (editButton) {
|
||||
editButton.style.display = '';
|
||||
}
|
||||
}
|
||||
|
||||
function showNotification(message, type = 'info') {
|
||||
const notification = document.createElement('div');
|
||||
notification.className = `fixed bottom-4 right-4 p-4 rounded-lg shadow-lg text-white ${
|
||||
type === 'success' ? 'bg-green-600 dark:bg-green-500' :
|
||||
type === 'error' ? 'bg-red-600 dark:bg-red-500' :
|
||||
'bg-blue-600 dark:bg-blue-500'
|
||||
}`;
|
||||
notification.textContent = message;
|
||||
|
||||
document.body.appendChild(notification);
|
||||
|
||||
// Remove after 5 seconds
|
||||
setTimeout(() => {
|
||||
notification.remove();
|
||||
}, 5000);
|
||||
}
|
||||
Reference in New Issue
Block a user