admin-magic-links.js (5471B)
1 let magicLinkRules = []; 2 3 // Init on page load 4 document.addEventListener('DOMContentLoaded', function() { 5 magicLinkRules = window.initialMagicLinks || []; 6 renderMagicLinkRules(); 7 setupMagicLinksForm(); 8 }); 9 10 function renderMagicLinkRules() { 11 const container = document.getElementById('magic-link-rules'); 12 if (!container) return; 13 14 container.innerHTML = ''; 15 16 magicLinkRules.forEach((rule, index) => { 17 const ruleDiv = document.createElement('div'); 18 ruleDiv.style.cssText = 'border: 1px solid #ddd; padding: 15px; margin-bottom: 15px; border-radius: 5px;'; 19 20 ruleDiv.innerHTML = ` 21 <div style="display: flex; justify-content: space-between; align-items: start; margin-bottom: 10px;"> 22 <h4 style="margin: 0;">Rule ${index + 1}</h4> 23 <button onclick="removeMagicLinkRule(${index})" style="background-color: #dc3545; color: white; padding: 5px 10px; border: none; border-radius: 3px; font-size: 12px; cursor: pointer;"> 24 Remove 25 </button> 26 </div> 27 28 <div style="margin-bottom: 10px;"> 29 <label style="display: block; font-weight: bold; margin-bottom: 5px; font-size: 13px;">Name:</label> 30 <input type="text" value="${escapeHtml(rule.name)}" 31 onchange="updateMagicLinkRule(${index}, 'name', this.value)" 32 placeholder="e.g., Open on website.tld" 33 style="width: 100%; padding: 6px; font-size: 13px; border: 1px solid #ccc; border-radius: 3px;"> 34 <small>Shown as the label next to the link in the file viewer</small> 35 </div> 36 37 <div style="display: flex; gap: 10px; margin-bottom: 10px;"> 38 <div style="flex: 1;"> 39 <label style="display: block; font-weight: bold; margin-bottom: 5px; font-size: 13px;">If tag category is:</label> 40 <input type="text" value="${escapeHtml(rule.category)}" 41 onchange="updateMagicLinkRule(${index}, 'category', this.value)" 42 placeholder="e.g., source" 43 style="width: 100%; padding: 6px; font-size: 13px; border: 1px solid #ccc; border-radius: 3px;"> 44 </div> 45 <div style="flex: 1;"> 46 <label style="display: block; font-weight: bold; margin-bottom: 5px; font-size: 13px;">equal to:</label> 47 <input type="text" value="${escapeHtml(rule.value)}" 48 onchange="updateMagicLinkRule(${index}, 'value', this.value)" 49 placeholder="e.g., example" 50 style="width: 100%; padding: 6px; font-size: 13px; border: 1px solid #ccc; border-radius: 3px;"> 51 </div> 52 </div> 53 54 <div style="margin-bottom: 0;"> 55 <label style="display: block; font-weight: bold; margin-bottom: 5px; font-size: 13px;">URL Template:</label> 56 <input type="text" value="${escapeHtml(rule.template)}" 57 onchange="updateMagicLinkRule(${index}, 'template', this.value)" 58 placeholder="e.g., https://website.tld/$name" 59 style="width: 100%; padding: 6px; font-size: 13px; font-family: monospace; border: 1px solid #ccc; border-radius: 3px;"> 60 <small>Must start with http:// or https://. Use $category or \${category} to insert the file's tag value.</small> 61 </div> 62 `; 63 64 container.appendChild(ruleDiv); 65 }); 66 } 67 68 function addMagicLinkRule() { 69 magicLinkRules.push({ 70 name: '', 71 category: '', 72 value: '', 73 template: '' 74 }); 75 renderMagicLinkRules(); 76 } 77 78 function removeMagicLinkRule(index) { 79 if (confirm('Remove this magic link rule?')) { 80 magicLinkRules.splice(index, 1); 81 renderMagicLinkRules(); 82 } 83 } 84 85 function updateMagicLinkRule(index, field, value) { 86 if (magicLinkRules[index]) { 87 magicLinkRules[index][field] = value; 88 } 89 } 90 91 function setupMagicLinksForm() { 92 const form = document.getElementById('magiclinks-form'); 93 if (!form) return; 94 95 form.addEventListener('submit', function(e) { 96 // Validate rules 97 for (let i = 0; i < magicLinkRules.length; i++) { 98 const rule = magicLinkRules[i]; 99 if (!rule.name || !rule.category || !rule.value || !rule.template) { 100 e.preventDefault(); 101 alert(`Rule ${i + 1} is incomplete. Please fill in Name, tag category, tag value and URL Template.`); 102 return; 103 } 104 if (!/^https?:\/\//i.test(rule.template.trim())) { 105 e.preventDefault(); 106 alert(`Rule ${i + 1}: URL Template must start with http:// or https://`); 107 return; 108 } 109 } 110 111 // Remove any previously-injected hidden fields from a prior submit 112 this.querySelectorAll('input[data-generated]').forEach(el => el.remove()); 113 114 // Append one hidden field per value 115 magicLinkRules.forEach((rule, i) => { 116 appendHidden(this, `magic_links[${i}][name]`, rule.name); 117 appendHidden(this, `magic_links[${i}][category]`, rule.category); 118 appendHidden(this, `magic_links[${i}][value]`, rule.value); 119 appendHidden(this, `magic_links[${i}][template]`, rule.template); 120 }); 121 }); 122 }