admindb 2.3.1 → 2.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -16
- package/dist/app.js +119 -171
- package/dist/public/css/app.css +1 -1
- package/dist/public/js/browse.js +1 -1
- package/dist/public/js/databases.js +1 -1
- package/dist/public/js/designer.js +1 -1
- package/dist/public/js/forms.js +1 -1
- package/dist/public/js/home.js +1 -1
- package/dist/public/js/inspector.js +1 -1
- package/dist/public/js/query.js +1 -1
- package/dist/public/js/schema.js +1 -1
- package/dist/public/js/seed.js +1 -1
- package/dist/routes/api/erd.js +2 -2
- package/dist/routes/api/helpers.d.ts +1 -1
- package/dist/routes/api/import-export.js +8 -6
- package/dist/routes/api/index.js +16 -9
- package/dist/routes/api/query.js +8 -5
- package/dist/routes/api/rows.js +36 -21
- package/dist/routes/api/seed.js +29 -16
- package/dist/routes/api/tables.js +32 -18
- package/dist/routes/databases.js +2 -0
- package/dist/routes/pages.d.ts +1 -1
- package/dist/routes/pages.js +137 -43
- package/dist/views/layouts/main.hbs +1 -1
- package/dist/views/pages/databases.hbs +1 -1
- package/dist/views/pages/erd.hbs +1 -1
- package/dist/views/pages/form.hbs +1 -1
- package/dist/views/pages/home.hbs +1 -1
- package/dist/views/pages/info.hbs +1 -1
- package/dist/views/pages/schema.hbs +1 -1
- package/dist/views/pages/seed-select.hbs +6 -5
- package/dist/views/pages/seed.hbs +3 -3
- package/dist/views/pages/table.hbs +1 -1
- package/dist/views/partials/navbar.hbs +1 -1
- package/dist/views/partials/sidebar.hbs +1 -1
- package/docs/API.md +60 -55
- package/package.json +1 -1
package/dist/public/js/browse.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(){"use strict";const e=window.APP&&window.APP.basePath||"",t=!(!window.APP||!window.APP.readonly),n=document.getElementById("browse-config"),s=n?JSON.parse(n.textContent):{table:"",filters:{},pkCols:[],hasPk:!1,readonly:t},o=encodeURIComponent(s.table),l=window.Utils?window.Utils.escapeHtml:function(e){return String(null==e?"":e)};let a=null,i={};function r(e){return a?a.columns.find(t=>t.name===e):null}function c(e){return!!a&&-1!==a.primaryKey.indexOf(e)}function d(e){return a?a.foreignKeys.find(t=>t.from===e):null}function u(e,t){const n=e.find(e=>e.op===t);return n?n.value??null:null}function p(e,t,n){if(null==e||""===e)return"";const s=String(e);if(t){const e=s.match(/^(\d{4}-\d{2}-\d{2})/);return e?e[1]:""}if(n){const e=s.match(/(\d{2}:\d{2}(?::\d{2})?)/);return e?e[1]:""}const o=s.match(/^(\d{4}-\d{2}-\d{2})(?:[ T](\d{2}):(\d{2})(?::(\d{2}))?)?/);return o?o[1]+"T"+(o[2]?o[2]+":"+o[3]+(o[4]?":"+o[4]:""):"00:00:00"):""}function f(e,t,n){const s=document.createElement("option");return s.value=t,s.textContent=n,e.appendChild(s),s}function m(e){const t=document.createElement("label");t.className="block";const n=document.createElement("span");n.className="field-label !mb-1 font-mono",n.textContent=e.name,t.appendChild(n);const o=d(e.name),l=(e.type||"").toUpperCase(),a="BOOLEAN"===l||"BOOL"===l,r=l.startsWith("INT")||"REAL"===l||"NUMERIC"===l||l.startsWith("DECIMAL")||"FLOAT"===l||"DOUBLE"===l||l.startsWith("BIGINT")||l.startsWith("SMALLINT")||l.startsWith("SERIAL")||"MONEY"===l,c="DATE"===l,m="TIME"===l||"TIMETZ"===l||l.startsWith("TIME ")||l.startsWith("TIME("),h="DATETIME"===l||l.includes("TIMESTAMP"),g=c||m||h,b=s.filters[e.name],y=function(e){return null==e?[]:Array.isArray(e)?e.filter(e=>e&&"object"==typeof e&&"string"==typeof e.op):"object"==typeof e?[e]:[]}(b),v=function(e){return"string"==typeof e?e:null}(b);if(o){const n=document.createElement("select");n.className="filter-input field-select !py-1.5 !text-[13px]",n.dataset.column=e.name,n.dataset.kind="fk",f(n,"","(any)"),f(n,"__NULL__","(not set / NULL)"),(i[e.name]||[]).forEach(e=>f(n,String(e.value),""===e.label?String(e.value):e.label));let s=u(y,"eq");return null==s&&v&&v.startsWith("=")&&(s=v.slice(1)),"__NULL__"===s?n.value="__NULL__":null!=s&&[...n.options].some(e=>e.value===String(s))&&(n.value=String(s)),t.appendChild(n),t}if(a){const n=document.createElement("select");n.className="filter-input field-select !py-1.5 !text-[13px]",n.dataset.column=e.name,n.dataset.kind="bool",f(n,"","(any)"),f(n,"1","true"),f(n,"0","false");let s=u(y,"eq");return null==s&&v&&v.startsWith("=")&&(s=v.slice(1)),"1"!==s&&"0"!==s&&"true"!==s&&"false"!==s||(n.value="true"===s||"1"===s?"1":"0"),t.appendChild(n),t}if(g||r){const n=document.createElement("div");n.className="flex items-center gap-1.5";const s=document.createElement("input"),o=document.createElement("input");if(g)s.type=o.type=c?"date":m?"time":"datetime-local",(m||h)&&(s.step=o.step="1");else{s.type=o.type="number";const e=l.startsWith("INTEGER")||"INT"===l||l.startsWith("BIGINT")||l.startsWith("SMALLINT")||l.startsWith("SERIAL");s.step=o.step=e?"1":"any"}s.className="filter-input field-input !py-1.5 !text-[13px]",o.className="filter-input field-input !py-1.5 !text-[13px]",s.dataset.column=e.name,s.dataset.bound="min",s.placeholder=g?"from":"min",o.dataset.column=e.name,o.dataset.bound="max",o.placeholder=g?"to":"max";const a=function(e){const t=e.find(e=>"between"===e.op);return t?{min:t.value??null,max:t.max??null}:{min:null,max:null}}(y),i=u(y,"gte")??u(y,"gt")??a.min,r=u(y,"lte")??u(y,"lt")??a.max;return null!=i&&(s.value=g?p(i,c,m):String(i)),null!=r&&(o.value=g?p(r,c,m):String(r)),n.appendChild(s),n.appendChild(o),t.appendChild(n),t}const x=document.createElement("input");x.type="text",x.className="filter-input field-input !py-1.5 !text-[13px]",x.dataset.column=e.name,x.dataset.kind="text",x.placeholder="BLOB"===l?"0x… or text":"filter…",x.autocomplete="off",x.spellcheck=!1;let w=v;if(null==w){const e=u(y,"prefix"),t=u(y,"eq"),n=u(y,"like");null!=e?w=String(e)+"*":null!=t?w="="+String(t):null!=n&&(w=String(n))}return null!=w&&(x.value=w),t.appendChild(x),t}function h(t){let n="";const s=window.location.pathname.match(/\/tables\/([^/]+)(?:\/|$)/);if(s&&(n=decodeURIComponent(s[1])),!n)return;const o=document.getElementById("page-size"),l=new URLSearchParams({page:"1",size:o?o.value:"50"});Object.keys(t).length&&l.set("f",JSON.stringify(t)),window.location.href=e+"/tables/"+encodeURIComponent(n)+"?"+l.toString()}function g(){const e=document.getElementById("filter-form"),t=document.getElementById("clear-filter"),n=document.getElementById("filter-fields");e&&n&&a&&(a.columns.forEach(e=>n.appendChild(m(e))),e.addEventListener("submit",e=>{e.preventDefault(),h(function(){const e={},t={};document.querySelectorAll("#filter-fields .filter-input").forEach(e=>{const n=e.dataset.column;n&&(t[n]=t[n]||[]).push(e)});for(const n of Object.keys(t)){const s=t[n],o=s[0],l=o.dataset.kind||"";if("fk"===l){const t=o.value;if(""===t)continue;e[n]="__NULL__"===t?{op:"null"}:{op:"eq",value:t}}else if("bool"===l){const t=o.value;if(""===t)continue;e[n]={op:"eq",value:t}}else if(2===s.length&&o.dataset.bound){const t=s.find(e=>"min"===e.dataset.bound),o=s.find(e=>"max"===e.dataset.bound),l=t&&""!==t.value?t.value:null,a=o&&""!==o.value?o.value:null;null!=l&&null!=a?e[n]={op:"between",value:l,max:a}:null!=l?e[n]={op:"gte",value:l}:null!=a&&(e[n]={op:"lte",value:a})}else{const t=(o.value||"").trim();t&&(e[n]=t)}}return e}())})),t&&t.addEventListener("click",()=>h({}))}let b=null;function y(){b&&(b.classList.remove("cell-focused"),b=null)}function v(e){e&&e!==b&&!e.classList.contains("js-editing")&&(y(),b=e,e.classList.add("cell-focused"),e.scrollIntoView({block:"nearest",inline:"nearest"}))}function x(e){const t=e.closest("tr"),n=t&&t.dataset.pk,s=e.dataset.col;if(!n||!s)return;const o=e.dataset.origValue;w(e,"true"===e.dataset.origNull?null:o),e.classList.remove("cell-dirty"),delete e.dataset.origValue,delete e.dataset.origNull,k.delete(n+"::"+s),I(),UI.showToast('Reverted edit in column "'+s+'".',"info")}function w(e,t){const n=null==t,s=n?"":String(t);e.dataset.null=String(n),e.dataset.value=s,e.classList.remove("js-editing","opacity-60");const o=void 0!==e.dataset.origValue,a=e.dataset.origValue,i="true"===e.dataset.origNull,c=o?"Staged edit: "+(n?"NULL":s)+" (Saved: "+(i?"NULL":a)+")":s,d=o?'<button type="button" class="cell-revert-btn js-revert-cell" title="Revert this field to saved value">↺</button>':"";if(n)return void(e.innerHTML='<div class="flex items-center justify-between gap-1"><span class="chip chip-null js-cell-value">NULL</span>'+d+"</div>");const u=r(e.dataset.col),p=(u?.type||e.dataset.type||"").toUpperCase(),f="true"===e.dataset.blob||p.includes("BLOB")||/^0x[0-9a-f]{8,}$/i.test(s),m="true"===e.dataset.json||s.startsWith("{")&&s.endsWith("}")||s.startsWith("[")&&s.endsWith("]");if(f){const t=s.startsWith("0x")?Math.floor((s.length-2)/2):s.length;e.innerHTML='<div class="flex items-center justify-between gap-1 js-cell-value"><div class="flex items-center gap-1.5 min-w-0"><span class="inline-flex items-center gap-1 rounded-md bg-secondary/10 px-1.5 py-0.5 text-[11px] font-mono font-medium text-secondary">🗃️ BLOB · '+t+' B</span><button type="button" class="btn btn-ghost btn-xs btn-circle opacity-0 group-hover:opacity-100 transition-opacity js-inspect-btn" title="Inspect BLOB"><svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg></button></div>'+d+"</div>"}else e.innerHTML=m?'<div class="flex items-center justify-between gap-1 js-cell-value"><div class="flex items-center gap-1.5 min-w-0"><span class="inline-flex items-center gap-1 rounded-md bg-info/10 px-1.5 py-0.5 text-[11px] font-mono font-medium text-info">{ } JSON</span><span class="block max-w-[140px] truncate font-mono text-[12px] text-base-content/70" title="'+l(c)+'">'+l(s)+'</span><button type="button" class="btn btn-ghost btn-xs btn-circle opacity-0 group-hover:opacity-100 transition-opacity js-inspect-btn" title="Inspect JSON"><svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg></button></div>'+d+"</div>":'<div class="flex items-center justify-between gap-1 group/cell"><span class="js-cell-value block max-w-xs truncate font-mono text-[13px]" title="'+l(c)+'">'+l(s.length>120?s.slice(0,120)+"…":s)+'</span><div class="flex items-center gap-0.5 shrink-0">'+d+(s.length>30?'<button type="button" class="btn btn-ghost btn-xs btn-circle opacity-0 group-hover/cell:opacity-100 transition-opacity js-inspect-btn shrink-0" title="Inspect text"><svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg></button>':"")+"</div></div>"}function E(e){return"true"===e.dataset.null?null:e.dataset.value??""}const k=new Map;function L(e,t,n){const s=e.dataset.pk,o=t.dataset.col;if(!s||!o)return;const l=r(o);if(!l)return;let a,i=!1;if("checkbox"===n.type)a=n.checked?"1":"0";else{const e=void 0===n.value?"":String(n.value);""===e||"__NULL__"===e?(a="",i=!0):a=e}const u=(l.type||"").toUpperCase(),p="BOOLEAN"===u||"BOOL"===u,f=void 0!==t.dataset.origValue?"true"===t.dataset.origNull:"true"===t.dataset.null,m=void 0!==t.dataset.origValue?t.dataset.origValue??"":t.dataset.value??"";let h=!1;if(i&&f)h=!0;else if(!i&&!f)if(p){h=("1"===m||"true"===m||1===m||!0===m)===("1"===a||"true"===a)}else h=String(m)===String(a);if(h)return delete t.dataset.origValue,delete t.dataset.origNull,t.classList.remove("cell-dirty"),k.delete(s+"::"+o),w(t,i?null:a),void I();void 0===t.dataset.origValue&&(t.dataset.origValue=m,t.dataset.origNull=f?"true":"false"),w(t,i?null:a),k.set(s+"::"+o,{td:t,pk:s,col:o,rawValue:a,setNull:i,isPk:c(o),isFk:!!d(o),origValue:t.dataset.origValue,origNull:"true"===t.dataset.origNull}),t.classList.add("cell-dirty"),I()}function I(){if(!N||!j)return;const e=k.size;j.textContent=String(e),N.classList.toggle("hidden",0===e)}function S(){if(!k.size||t)return;const e=new Map;for(const t of k.values()){let n=e.get(t.pk);n||(n={id:t.pk,values:{},nulls:[]},e.set(t.pk,n)),t.setNull?n.nulls.push(t.col):n.values[t.col]=t.rawValue}const n=[...e.values()];C&&(C.disabled=!0),Api.post("/api/tables/"+o+"/rows/bulk-update",{updates:n}).then(e=>{UI.showToast(e.message||"Changes applied.","success"),window.location.reload()}).catch(e=>{UI.showError(e.message),C&&(C.disabled=!1)})}async function T(){const e=k.size;if(!e)return;if(await UI.confirm({title:"Discard Changes",message:"You have "+e+" pending inline edit(s). Are you sure you want to discard all staged changes and revert to the saved values?",confirmText:"Discard "+e+" change(s)",danger:!0})){for(const e of k.values())w(e.td,e.origNull?null:e.origValue),e.td.classList.remove("cell-dirty"),delete e.td.dataset.origValue,delete e.td.dataset.origNull;k.clear(),I(),UI.showToast("Pending edits discarded.","info")}}function A(){if(k.size){var e=document.createElement("div");e.className="fixed inset-0 z-[100] flex items-center justify-center bg-black/60 backdrop-blur-xs p-4 opacity-0 transition-opacity duration-200";var t=document.createElement("div");t.className="app-card w-full max-w-3xl overflow-hidden shadow-2xl border border-base-300 transform scale-95 transition-transform duration-200 max-h-[85vh] flex flex-col";var n="";k.forEach((e,t)=>{var s=e.origNull?'<span class="chip chip-null text-[10px]">NULL</span>':l(e.origValue),o=e.setNull?'<span class="chip chip-null text-[10px]">NULL</span>':l(e.rawValue);n+='<tr class="hover:bg-base-200/50"><td class="font-mono text-xs font-semibold">'+l(e.pk)+'</td><td class="font-mono text-xs text-primary font-medium">'+l(e.col)+'</td><td class="font-mono text-xs text-error line-through opacity-80 max-w-xs truncate">'+s+'</td><td class="font-mono text-xs text-success font-medium max-w-xs truncate">'+o+'</td><td class="text-right"><button type="button" class="btn btn-ghost btn-xs text-warning js-modal-revert-item" data-key="'+l(t)+'">Revert</button></td></tr>'}),t.innerHTML='<div class="flex items-center justify-between border-b border-base-200 px-6 py-4"> <div class="flex items-center gap-2"> <h3 class="text-base font-bold text-base-content">Staged Changes Diff</h3> <span class="chip bg-warning/15 text-warning font-mono">'+k.size+' edit(s)</span> </div> <button type="button" class="btn btn-ghost btn-xs btn-circle js-diff-close text-base-content/40 hover:text-base-content">×</button></div><div class="flex-1 overflow-y-auto p-4"> <table class="table table-sm"> <thead><tr class="text-xs uppercase text-base-content/50"><th>Row PK</th><th>Column</th><th>Original Value</th><th>Staged Value</th><th class="text-right">Action</th></tr></thead> <tbody id="diff-rows-body">'+n+'</tbody> </table></div><div class="flex items-center justify-between border-t border-base-200 bg-base-200/30 px-6 py-4"> <button type="button" id="diff-modal-discard" class="btn btn-ghost btn-sm text-error">Discard All</button> <div class="flex gap-2"> <button type="button" class="btn btn-ghost btn-sm js-diff-close">Cancel</button> <button type="button" id="diff-modal-apply" class="btn btn-primary btn-sm gap-1"> <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" /></svg> Apply '+k.size+" Change(s) </button> </div></div>",e.appendChild(t),document.body.appendChild(e),requestAnimationFrame(()=>{e.classList.remove("opacity-0"),e.classList.add("opacity-100"),t.classList.remove("scale-95"),t.classList.add("scale-100")}),t.querySelectorAll(".js-diff-close").forEach(e=>e.addEventListener("click",s)),e.addEventListener("click",t=>{t.target===e&&s()}),t.querySelectorAll(".js-modal-revert-item").forEach(e=>{e.addEventListener("click",()=>{const t=e.dataset.key,n=k.get(t);n&&(x(n.td),e.closest("tr").remove(),k.size||s())})}),t.querySelector("#diff-modal-apply").addEventListener("click",()=>{s(),S()}),t.querySelector("#diff-modal-discard").addEventListener("click",async()=>{s(),await T()})}function s(){e.classList.remove("opacity-100"),e.classList.add("opacity-0"),t.classList.remove("scale-100"),t.classList.add("scale-95"),setTimeout(()=>e.remove(),180)}}let N=null,j=null,C=null;function B(e){if(t)return;if(e.classList.contains("js-editing"))return;const n=e.closest("tr");if(!n||!n.dataset.pk||!n.dataset.table)return;if(!r(e.dataset.col))return;v(e);const s=document.querySelector(".js-editing");s&&s._finish&&s._finish(!0),e._origNull="true"===e.dataset.null,e._origValue=E(e);const o=function(e){const t=e.dataset.col,n=r(t);if(!n)return null;const s=d(t),o=(n.type||"").toUpperCase(),l="BOOLEAN"===o||"BOOL"===o,a="DATE"===o,c="TIME"===o||"TIMETZ"===o||o.startsWith("TIME ")||o.startsWith("TIME("),u="DATETIME"===o||o.includes("TIMESTAMP"),m=a||c||u,h=o.startsWith("INT")||"REAL"===o||"NUMERIC"===o||o.startsWith("DECIMAL")||"FLOAT"===o||"DOUBLE"===o||o.startsWith("BIGINT")||o.startsWith("SMALLINT")||o.startsWith("SERIAL")||"MONEY"===o,g="BLOB"===o||"BYTEA"===o||"BINARY"===o,b="UUID"===o,y=o.includes("JSON"),v=o.endsWith("[]")||o.startsWith("_")||o.includes("ARRAY"),x="INTERVAL"===o,w="POINT"===o||"GEOMETRY"===o||"BOX"===o||"CIRCLE"===o||"POLYGON"===o,k="BIT"===o||"VARBIT"===o||o.startsWith("BIT(")||o.startsWith("VARBIT("),L=E(e),I=null===L||""===L;let S;if(l)S=document.createElement("input"),S.type="checkbox",S.className="checkbox checkbox-primary checkbox-sm js-inline-ctrl",S.checked=1===L||!0===L||"1"===L||"true"===L;else if(s)S=document.createElement("select"),S.className="field-select !w-64 !py-1.5 !text-[13px] js-inline-ctrl",f(S,"","(not set / NULL)"),(i[t]||[]).forEach(e=>{const t=f(S,String(e.value),""===e.label?String(e.value):e.label);I||String(L)!==String(e.value)||(t.selected=!0)});else if(m)S=document.createElement("input"),S.type=a?"date":c?"time":"datetime-local",(c||u)&&(S.step="1"),S.className="field-input !w-64 !py-1.5 !text-[13px] js-inline-ctrl font-mono",I||(S.value=p(String(L),a,c)),setTimeout(()=>{if("function"==typeof S.showPicker)try{S.showPicker()}catch(e){}},50);else if(h){S=document.createElement("input"),S.type="number";const e=o.startsWith("INTEGER")||"INT"===o||o.startsWith("BIGINT")||o.startsWith("SMALLINT")||o.startsWith("SERIAL");S.step=e?"1":"any",S.className="field-input !w-48 !py-1.5 !text-[13px] js-inline-ctrl font-mono",I||(S.value=String(L))}else b?(S=document.createElement("input"),S.type="text",S.className="field-input font-mono !w-64 !py-1.5 !text-[12px] js-inline-ctrl",S.placeholder="00000000-0000-0000-0000-000000000000",I||(S.value=String(L))):v?(S=document.createElement("input"),S.type="text",S.className="field-input font-mono !w-64 !py-1.5 !text-[12px] js-inline-ctrl",S.placeholder="{item1, item2}",I||(S.value=String(L))):x?(S=document.createElement("input"),S.type="text",S.className="field-input font-mono !w-64 !py-1.5 !text-[12px] js-inline-ctrl",S.placeholder="e.g. '1 day'",I||(S.value=String(L))):w?(S=document.createElement("input"),S.type="text",S.className="field-input font-mono !w-64 !py-1.5 !text-[12px] js-inline-ctrl",S.placeholder="(x, y)",I||(S.value=String(L))):k?(S=document.createElement("input"),S.type="text",S.className="field-input font-mono !w-64 !py-1.5 !text-[12px] js-inline-ctrl",S.placeholder="101010",I||(S.value=String(L))):(S=document.createElement("input"),S.type="text",S.className="field-input !w-64 !py-1.5 !text-[13px] js-inline-ctrl",S.placeholder=g?"BYTEA"===o?"\\x… or 0x… hex":"0x… hex or text":y?'{"key":"value"}':"",I||(S.value=String(L)));return S}(e);if(!o)return;e.classList.add("js-editing"),e.innerHTML="",e.appendChild(o);let l=!1;const a=(t,s)=>{l||(l=!0,t?L(n,e,o):function(e){w(e,e._origNull?null:e._origValue),e.classList.remove("js-editing")}(e),"down"===s?U("down"):"right"===s?U("right"):"left"===s&&U("left"))};e._finish=a,o.addEventListener("keydown",e=>{"Enter"===e.key?(e.preventDefault(),a(!0,"down")):"Tab"===e.key?(e.preventDefault(),a(!0,e.shiftKey?"left":"right")):"Escape"===e.key&&(e.preventDefault(),a(!1))}),"SELECT"===o.tagName||"checkbox"===o.type?o.addEventListener("change",()=>a(!0)):o.addEventListener("blur",()=>a(!0)),o.focus(),"function"==typeof o.select&&o.select()}function U(e){const t=e=>Array.prototype.slice.call(e.querySelectorAll("td.cell-editable")).filter(e=>"none"!==e.style.display&&!e.classList.contains("hidden"));if(!b){const e=document.querySelector("#main-table tbody tr");if(e){const n=t(e);n.length&&v(n[0])}return}const n=b.closest("tr");if(!n)return;const s=t(n),o=s.indexOf(b);if("right"===e)if(o>=0&&o<s.length-1)v(s[o+1]);else{const e=n.nextElementSibling;if(e){const n=t(e);n.length&&v(n[0])}}else if("left"===e)if(o>0)v(s[o-1]);else{const e=n.previousElementSibling;if(e){const n=t(e);n.length&&v(n[n.length-1])}}else if("down"===e){const e=n.nextElementSibling;if(e){const n=b.dataset.col,s=n?e.querySelector('td[data-col="'+CSS.escape(n)+'"]'):null;if(s&&"none"!==s.style.display)v(s);else{const n=t(e);(n[o]||n[0])&&v(n[o]||n[0])}}}else if("up"===e){const e=n.previousElementSibling;if(e){const n=b.dataset.col,s=n?e.querySelector('td[data-col="'+CSS.escape(n)+'"]'):null;if(s&&"none"!==s.style.display)v(s);else{const n=t(e);(n[o]||n[0])&&v(n[o]||n[0])}}}}function M(){N=function(){const e=document.createElement("div");return e.id="changes-bar",e.className="fixed bottom-5 left-1/2 z-50 flex -translate-x-1/2 items-center gap-3 rounded-2xl border border-base-300 bg-base-100/95 backdrop-blur-md px-4 py-2.5 shadow-2xl transition-all duration-200 hidden",e.innerHTML='<div class="flex items-center gap-2"><span class="relative flex h-2.5 w-2.5"><span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-warning opacity-75"></span><span class="relative inline-flex rounded-full h-2.5 w-2.5 bg-warning"></span></span><span class="text-sm font-medium text-base-content"><b id="changes-count" class="tabular-nums text-warning font-bold">0</b> pending edit(s)</span></div><div class="h-4 w-px bg-base-300"></div><div class="flex items-center gap-1.5"><button type="button" id="changes-review" class="btn btn-ghost btn-sm gap-1.5 text-xs font-medium"><svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg>Review Diff</button><button type="button" id="changes-apply" class="btn btn-primary btn-sm gap-1 text-xs font-semibold"><svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" /></svg>Apply</button><button type="button" id="changes-discard" class="btn btn-ghost btn-sm text-xs text-base-content/60 hover:text-error">Discard</button></div>',document.body.appendChild(e),e}(),j=document.getElementById("changes-count"),C=document.getElementById("changes-apply");const e=document.getElementById("changes-discard"),n=document.getElementById("changes-review");C&&C.addEventListener("click",S),e&&e.addEventListener("click",T),n&&n.addEventListener("click",A),document.addEventListener("click",e=>{const t=e.target.closest(".js-revert-cell");if(t){e.stopPropagation(),e.preventDefault();const n=t.closest("td");return void(n&&x(n))}const n=e.target.closest("td.cell-editable");n&&!n.classList.contains("js-editing")&&v(n)}),t||document.querySelectorAll(".js-inline-cell").forEach(e=>{e.addEventListener("dblclick",()=>B(e))}),document.addEventListener("keydown",e=>{if(document.querySelector(".js-editing"))return;const n=document.activeElement?document.activeElement.tagName:"";if("INPUT"!==n&&"TEXTAREA"!==n&&"SELECT"!==n&&b)if("ArrowRight"===e.key)e.preventDefault(),U("right");else if("ArrowLeft"===e.key)e.preventDefault(),U("left");else if("ArrowDown"===e.key)e.preventDefault(),U("down");else if("ArrowUp"===e.key)e.preventDefault(),U("up");else if("Tab"===e.key)e.preventDefault(),U(e.shiftKey?"left":"right");else if("Enter"===e.key)e.preventDefault(),t||B(b);else if("Escape"===e.key)y();else if(" "!==e.key||t){if("i"===e.key||"I"===e.key)e.preventDefault(),V(b);else if((e.ctrlKey||e.metaKey)&&"c"===e.key.toLowerCase()){const e=E(b);null!=e&&navigator.clipboard.writeText(String(e)).then(()=>{UI.showToast("Cell value copied.","info")})}}else{const t=r(b.dataset.col);if(t&&"BOOLEAN"===(t.type||"").toUpperCase()){e.preventDefault();const t=b.closest("tr"),n=E(b),s=1===n||"1"===n||!0===n||"true"===n?"0":"1";L(t,b,{value:s,type:"checkbox",checked:"1"===s})}else e.preventDefault(),V(b)}})}function R(){return Array.prototype.slice.call(document.querySelectorAll(".row-checkbox:checked")).map(e=>e.value).filter(Boolean)}async function q(e){const t=R();if(t.length)try{const n=await Api.post("/api/tables/"+o+"/rows/bulk-export",{ids:t,format:e}),s=new Blob([n.content],{type:"json"===e?"application/json;charset=utf-8":"text/csv;charset=utf-8"}),l=document.createElement("a");l.href=URL.createObjectURL(s),l.download=n.filename,document.body.appendChild(l),l.click(),l.remove(),URL.revokeObjectURL(l.href),UI.showToast("Exported "+n.rowCount+" row(s).","success")}catch(e){UI.showError(e.message)}}async function O(){const e=R();if(!e.length||t)return;let n;try{n=await Api.post("/api/tables/"+o+"/rows/bulk-impact",{ids:e})}catch(e){return void UI.showError(e.message)}const a=n&&n.references||[],i=a.length?'<p class="mb-2 font-medium text-warning">⚠ Other tables reference these records:</p><ul class="mb-2 list-disc space-y-1 pl-5 text-xs">'+a.map(e=>'<li><code class="font-mono font-bold">'+l(e.table)+"."+l(e.from)+'</code> — <b class="tabular-nums">'+e.count+"</b> row(s) point at them</li>").join("")+'</ul><p class="text-[11px] text-base-content/55">Depending on foreign key rules, those rows may cascade-delete or block this action.</p>':'<p class="text-xs text-base-content/60">No other tables reference these rows — safe to delete.</p>';if(await UI.confirm({title:"Delete Selected Rows",message:"Delete "+e.length+' selected row(s) from table "'+s.table+'"? This action cannot be undone.',item:s.table,itemType:"Table",detailsHtml:i,confirmText:"Delete "+e.length+" row(s)",danger:!0}))try{const t=await Api.post("/api/tables/"+o+"/rows/bulk-delete",{ids:e,confirmImpact:!0});UI.showToast(t.message||"Rows deleted.","success"),window.location.reload()}catch(e){UI.showError(e.message)}}function D(e,t){const n=e.querySelector(".js-ref-chevron");n&&n.classList.toggle("rotate-180",t)}let W=null;function P(){const e=document.querySelector(".js-ref-pop");e&&(e._cleanup&&e._cleanup(),e.remove()),W&&(D(W,!1),W.setAttribute("aria-expanded","false"),W.classList.remove("opacity-50"),W=null)}function _(t,n){P();const s=document.createElement("div");s.className="js-ref-pop app-card js-ref-anim z-50 min-w-[280px] max-w-[460px] overflow-hidden shadow-lift",s.innerHTML=n.length?n.map(t=>function(e,t){const n=t+"/tables/"+encodeURIComponent(e.table),s=e.count??e.total??(e.rows?e.rows.length:0),o=e.columns||(e.rows&&e.rows.length>0?Object.keys(e.rows[0]).filter(e=>"_rowid_"!==e):[]);let a='<div class="flex items-center justify-between gap-2 border-b border-base-200 px-3 py-2"><span class="min-w-0 truncate"><a class="link link-primary font-mono text-xs font-semibold" href="'+n+'">'+l(e.table)+'</a><span class="ml-1.5 text-xs text-base-content/45">via <code class="font-mono">'+l(e.from)+"</code>"+(void 0!==e.value?' = <code class="font-mono">'+l(String(e.value))+"</code>":"")+'</span></span><span class="chip '+(s?"chip-pk":"chip-type")+'">'+l(s+" row"+(1===s?"":"s"))+"</span></div>";return e.rows&&e.rows.length&&o.length?a+'<div class="max-h-64 overflow-auto"><table class="w-full"><thead><tr class="bg-base-200/40"><tr>'+o.map(function(e){return'<th class="whitespace-nowrap px-2.5 py-1.5 text-left text-[10px] font-semibold uppercase tracking-wide text-base-content/50"><span class="font-mono normal-case">'+l(e)+"</span></th>"}).join("")+"</tr></tr></thead><tbody>"+e.rows.map(function(e){return'<tr class="border-t border-base-200/60 hover:bg-primary/[0.04]">'+o.map(function(t){const n=e[t];if(null==n)return'<td class="px-2.5 py-1.5 align-middle"><span class="text-[11px] text-base-content/40">NULL</span></td>';const s=String(n),o=s.length>120?s.slice(0,120)+"…":s;return'<td class="max-w-[180px] truncate px-2.5 py-1.5 align-middle font-mono text-[11px] text-base-content/80" title="'+l(s)+'">'+l(o)+"</td>"}).join("")+"</tr>"}).join("")+'</tbody></table></div><div class="flex justify-end border-t border-base-200 px-3 py-1.5"><a class="btn btn-ghost btn-xs text-base-content/60 hover:text-primary" href="'+n+'">Open table</a></div>':a+'<div class="px-3 py-3 text-xs text-base-content/45">No matching rows.</div>'}(t,e)).join('<div class="h-px bg-base-200"></div>'):'<div class="px-4 py-3 text-sm text-base-content/45">No related rows for this record.</div>',s.style.visibility="hidden",document.body.appendChild(s);const o=Math.min(460,Math.max(280,s.offsetWidth)),a=s.offsetHeight,i=t.getBoundingClientRect();let r=i.left;r+o>window.innerWidth-8&&(r=Math.max(8,window.innerWidth-o-8));let c=i.bottom+6;c+a>window.innerHeight-8&&(c=Math.max(8,i.top-a-6)),s.style.position="fixed",s.style.left=r+"px",s.style.top=c+"px",s.style.visibility="visible",W=t;const d=e=>{e&&e.target&&s.contains(e.target)||P()};window.addEventListener("scroll",d,!0),window.addEventListener("resize",d);const u=e=>{s.contains(e.target)||e.target===t||t.contains(e.target)||P()};document.addEventListener("click",u,!0);const p=e=>{"Escape"===e.key&&P()};document.addEventListener("keydown",p),s._cleanup=()=>{window.removeEventListener("scroll",d,!0),window.removeEventListener("resize",d),document.removeEventListener("click",u,!0),document.removeEventListener("keydown",p)}}function V(e){if(!e)return;const n=e.closest("tr");if(!n)return;const o=e.dataset.col,l="true"===e.dataset.null,a=e.dataset.value,i=n.dataset.pk,c=n.dataset.table||s.table,d="true"===e.dataset.blob,u="true"===e.dataset.json,p="true"===e.dataset.image,f="true"===e.dataset.audio,m="true"===e.dataset.video,h="true"===e.dataset.pdf,g=e.querySelector("img")&&e.querySelector("img").src||"",b=e.dataset.type||(r(o)?.type??"");window.Inspector&&window.Inspector.open&&window.Inspector.open({table:c,pk:i,col:o,colType:b,value:a,isNull:l,isBlob:d,isJson:u,isImage:p,isAudio:f,isVideo:m,isPdf:h,blobUrl:g,readonly:t,onSave:s=>{if(!t){L(n,e,{value:s,type:"text"})}}})}function z(){const e=document.getElementById("main-table");if(!e)return;const t=document.getElementById("col-manager-modal"),n=document.getElementById("btn-columns-manager"),o=document.getElementById("col-count-badge"),a=document.getElementById("col-manager-list"),i=document.getElementById("col-search"),d=document.getElementById("col-btn-show-all"),u=document.getElementById("col-btn-hide-non-pk"),p=document.getElementById("col-btn-reset"),f=document.getElementById("col-manager-summary"),m=Array.prototype.slice.call(e.querySelectorAll("thead th[data-col]"));if(!m.length)return;const h=m.map(e=>e.dataset.col),g="admindb:col_settings:"+(s.table||"default");let b=function(){try{const e=localStorage.getItem(g);if(e){const t=JSON.parse(e);if(t&&"object"==typeof t){const e=new Set(h),n=Array.isArray(t.order)?t.order.filter(t=>e.has(t)):[];h.forEach(e=>{-1===n.indexOf(e)&&n.push(e)});const s=Array.isArray(t.hidden)?t.hidden.filter(t=>e.has(t)):[],o=Array.isArray(t.pinned)?t.pinned.filter(t=>e.has(t)):[];return{order:n,hidden:s,pinned:o}}}}catch(e){}return{order:h.slice(),hidden:[],pinned:[]}}();function y(){try{localStorage.setItem(g,JSON.stringify(b))}catch(e){}v(),w()}function v(){const t=b.order,n=new Set(b.hidden),s=new Set(b.pinned),l=e.querySelector("thead tr");if(l){const e=l.querySelector("th.sticky-col-actions"),o={};m.forEach(e=>{o[e.dataset.col]=e}),t.forEach(t=>{const a=o[t];if(a){e?l.insertBefore(a,e):l.appendChild(a);const o=n.has(t);a.style.display=o?"none":"";const i=s.has(t)&&!o;a.classList.toggle("sticky-col-left",i)}})}e.querySelectorAll("tbody tr").forEach(e=>{const o=Array.prototype.slice.call(e.querySelectorAll("td[data-col]")),l={};o.forEach(e=>{l[e.dataset.col]=e});const a=e.querySelector("td.sticky-col-actions");t.forEach(t=>{const o=l[t];if(o){a?e.insertBefore(o,a):e.appendChild(o);const l=n.has(t);o.style.display=l?"none":"";const i=s.has(t)&&!l;o.classList.toggle("sticky-col-left",i)}})}),x();const a=h.length-b.hidden.length;o&&(o.textContent=a+"/"+h.length,o.classList.toggle("badge-warning",b.hidden.length>0)),f&&(f.textContent=a+" of "+h.length+" visible · "+b.pinned.length+" pinned")}function x(){const t=b.order.filter(e=>b.pinned.includes(e)&&!b.hidden.includes(e)),n=e.querySelector("thead th.th-select-all");let s=n?n.offsetWidth:40;e.querySelectorAll(".sticky-col-left-last").forEach(e=>e.classList.remove("sticky-col-left-last")),t.forEach((n,o)=>{const l=e.querySelector('thead th[data-col="'+CSS.escape(n)+'"]'),a=l?l.offsetWidth:120,i=o===t.length-1;l&&(l.style.left=s+"px",i&&l.classList.add("sticky-col-left-last")),e.querySelectorAll('tbody td[data-col="'+CSS.escape(n)+'"]').forEach(e=>{e.style.left=s+"px",i&&e.classList.add("sticky-col-left-last")}),s+=a}),b.order.forEach(n=>{if(!t.includes(n)){const t=e.querySelector('thead th[data-col="'+CSS.escape(n)+'"]');t&&(t.style.left=""),e.querySelectorAll('tbody td[data-col="'+CSS.escape(n)+'"]').forEach(e=>{e.style.left=""})}})}function w(){if(!a)return;a.innerHTML="";const e=(i?i.value:"").toLowerCase().trim();b.order.forEach((t,n)=>{if(e&&-1===t.toLowerCase().indexOf(e))return;const s=b.hidden.includes(t),o=b.pinned.includes(t),i=t.startsWith("ref:"),d=i?t.slice(4):"",u=!i&&c(t),p=i?null:r(t),f=p?p.type:"",m=i?d:t,h=document.createElement("div");h.className="col-manager-item"+(s?" is-hidden":"")+(o?" is-pinned":""),h.dataset.col=t,h.dataset.index=String(n),h.draggable=!0,h.innerHTML='<div class="flex items-center gap-2 min-w-0 flex-1"><span class="col-drag-handle" title="Drag to reorder"><svg class="h-3.5 w-3.5" viewBox="0 0 24 24" fill="currentColor"><circle cx="9" cy="7" r="1.5"/><circle cx="15" cy="7" r="1.5"/><circle cx="9" cy="12" r="1.5"/><circle cx="15" cy="12" r="1.5"/><circle cx="9" cy="17" r="1.5"/><circle cx="15" cy="17" r="1.5"/></svg></span><input type="checkbox" class="checkbox checkbox-primary checkbox-xs js-col-toggle" '+(s?"":"checked")+' title="Toggle visibility" /><span class="font-mono font-medium truncate text-base-content text-[13px]" title="'+(i?"Referencing table: "+l(d):l(t))+'">'+l(m)+"</span>"+(i?'<span class="chip chip-type text-[9px] font-mono bg-secondary/15 text-secondary">REF</span>':"")+(u?'<span class="chip chip-pk text-[9px]">PK</span>':"")+(f?'<span class="chip chip-type text-[9px] font-mono opacity-70">'+l(f)+"</span>":"")+'</div><div class="flex items-center gap-1 shrink-0"><button type="button" class="btn btn-ghost btn-xs btn-square js-col-pin '+(o?"text-primary bg-primary/10":"text-base-content/40 hover:text-base-content")+'" title="'+(o?"Unpin column":"Pin column to left (sticky)")+'"><svg class="h-3.5 w-3.5" fill="'+(o?"currentColor":"none")+'" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M19 13.5l-5-5m3-3.5a2.121 2.121 0 00-3 0L9.5 9.5 5 10l5 5 .5 4.5 4.5-4.5a2.121 2.121 0 000-3zM4 20l5-5" /></svg></button><button type="button" class="btn btn-ghost btn-xs btn-square text-base-content/40 hover:text-base-content js-col-up" '+(0===n?"disabled":"")+' title="Move Up"><svg class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 15l7-7 7 7"/></svg></button><button type="button" class="btn btn-ghost btn-xs btn-square text-base-content/40 hover:text-base-content js-col-down" '+(n===b.order.length-1?"disabled":"")+' title="Move Down"><svg class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7"/></svg></button></div>';const g=h.querySelector(".js-col-toggle");g&&g.addEventListener("change",()=>{g.checked?b.hidden=b.hidden.filter(e=>e!==t):b.hidden.includes(t)||b.hidden.push(t),y()});const v=h.querySelector(".js-col-pin");v&&v.addEventListener("click",()=>{b.pinned.includes(t)?b.pinned=b.pinned.filter(e=>e!==t):(b.pinned.push(t),b.hidden=b.hidden.filter(e=>e!==t)),y()});const x=h.querySelector(".js-col-up");x&&n>0&&x.addEventListener("click",()=>{const e=b.order[n-1];b.order[n-1]=b.order[n],b.order[n]=e,y()});const w=h.querySelector(".js-col-down");w&&n<b.order.length-1&&w.addEventListener("click",()=>{const e=b.order[n+1];b.order[n+1]=b.order[n],b.order[n]=e,y()}),function(e,t){e.addEventListener("dragstart",n=>{E=t,e.classList.add("is-dragging"),n.dataTransfer&&(n.dataTransfer.effectAllowed="move",n.dataTransfer.setData("text/plain",t))}),e.addEventListener("dragend",()=>{E=null,e.classList.remove("is-dragging"),a.querySelectorAll(".drag-over-top, .drag-over-bottom").forEach(e=>{e.classList.remove("drag-over-top","drag-over-bottom")})}),e.addEventListener("dragover",n=>{if(n.preventDefault(),n.dataTransfer&&(n.dataTransfer.dropEffect="move"),!E||E===t)return;const s=e.getBoundingClientRect(),o=s.top+s.height/2;n.clientY<o?(e.classList.add("drag-over-top"),e.classList.remove("drag-over-bottom")):(e.classList.add("drag-over-bottom"),e.classList.remove("drag-over-top"))}),e.addEventListener("dragleave",()=>{e.classList.remove("drag-over-top","drag-over-bottom")}),e.addEventListener("drop",n=>{if(n.preventDefault(),e.classList.remove("drag-over-top","drag-over-bottom"),!E||E===t)return;const s=b.order.indexOf(E);let o=b.order.indexOf(t);if(-1===s||-1===o)return;const l=e.getBoundingClientRect(),a=l.top+l.height/2;b.order.splice(s,1),o=b.order.indexOf(t),n.clientY>=a&&(o+=1),b.order.splice(o,0,E),y()})}(h,t),a.appendChild(h)})}let E=null;d&&d.addEventListener("click",()=>{b.hidden=[],y()}),u&&u.addEventListener("click",()=>{b.hidden=h.filter(e=>!c(e)),y()}),p&&p.addEventListener("click",()=>{b={order:h.slice(),hidden:[],pinned:[]};try{localStorage.removeItem(g)}catch(e){}y(),UI.showToast("Columns reset to default.","info")}),i&&i.addEventListener("input",w),n&&t&&n.addEventListener("click",()=>{w(),"function"==typeof t.showModal?t.showModal():t.classList.add("modal-open"),i&&(i.value="",setTimeout(()=>i.focus(),50))}),v(),window.addEventListener("resize",x)}!async function(){try{a=await Api.get("/api/tables/"+o+"/info")}catch(e){a=null}try{i=await Api.get("/api/tables/"+o+"/fk-options")}catch(e){i={}}z(),g(),document.querySelectorAll(".js-row-copy-json").forEach(e=>{e.addEventListener("click",t=>{t.stopPropagation();const n=e.closest("tr");if(!n)return;const s={};n.querySelectorAll("td.cell-editable").forEach(e=>{const t=e.dataset.col;t&&(s[t]="true"===e.dataset.null?null:e.dataset.value)}),navigator.clipboard.writeText(JSON.stringify(s,null,2)).then(()=>{UI.showToast("Row copied as JSON.","success")})})}),document.querySelectorAll(".js-row-copy-sql").forEach(e=>{e.addEventListener("click",t=>{t.stopPropagation();const n=e.closest("tr");if(!n)return;const o=[],l=[];n.querySelectorAll("td.cell-editable").forEach(e=>{const t=e.dataset.col;if(t)if(o.push('"'+t.replace(/"/g,'""')+'"'),"true"===e.dataset.null)l.push("NULL");else{const t=e.dataset.value;l.push("'"+String(t).replace(/'/g,"''")+"'")}});const a='INSERT INTO "'+s.table.replace(/"/g,'""')+'" ('+o.join(", ")+") VALUES ("+l.join(", ")+");";navigator.clipboard.writeText(a).then(()=>{UI.showToast("SQL INSERT copied to clipboard.","success")})})}),document.querySelectorAll(".js-row-duplicate").forEach(t=>{t.addEventListener("click",n=>{n.stopPropagation();const s=t.dataset.pk;s&&(window.location.href=e+"/tables/"+o+"/rows/new?duplicate="+encodeURIComponent(s))})}),document.querySelectorAll(".dropdown").forEach(e=>{const t=e.querySelector('label[role="button"]')||e.querySelector("button");t&&t.addEventListener("click",()=>{const n=t.getBoundingClientRect(),s=window.innerHeight-n.bottom;e.classList.toggle("dropdown-top",s<220)})}),M(),function(){let e=null;document.addEventListener("click",e=>{const t=e.target.closest(".js-inspect-btn"),n=e.target.closest("td.cell-editable");if(t){e.stopPropagation(),e.preventDefault();const n=t.closest("td");return void(n&&V(n))}n&&e.altKey&&(e.stopPropagation(),e.preventDefault(),V(n))}),document.addEventListener("mouseover",n=>{const o=n.target.closest("td.cell-editable");o&&!o.classList.contains("js-editing")&&(clearTimeout(e),e=setTimeout(()=>{const e=o.dataset.value,n="true"===o.dataset.blob,l="true"===o.dataset.json,a="true"===o.dataset.image,i="true"===o.dataset.audio,c="true"===o.dataset.video,d="true"===o.dataset.pdf,u=e&&e.length>40;if(!(n||l||a||i||c||d||u))return;const p=o.dataset.col,f=o.closest("tr")?.dataset.pk,m=o.closest("tr")?.dataset.table||s.table,h=o.querySelector("img")&&o.querySelector("img").src||"",g=o.dataset.type||(r(p)?.type??"");window.Inspector&&window.Inspector.peek&&window.Inspector.peek(o,{table:m,pk:f,col:p,colType:g,value:e,isNull:"true"===o.dataset.null,isBlob:n,isJson:l,isImage:a,isAudio:i,isVideo:c,isPdf:d,blobUrl:h,readonly:t,onSave:e=>{if(!t){const t=o.closest("tr");t&&L(t,o,{value:e,type:"text"})}}})},500))}),document.addEventListener("mouseout",t=>{t.target.closest("td.cell-editable")&&clearTimeout(e)})}(),function(){const e=document.getElementById("bulk-bar");if(!e)return;const n=document.getElementById("select-all-rows"),s=document.getElementById("bulk-count"),o=document.getElementById("bulk-delete"),l=document.getElementById("bulk-export-csv"),a=document.getElementById("bulk-export-json"),i=document.getElementById("bulk-clear"),r=Array.prototype.slice.call(document.querySelectorAll(".row-checkbox"));function c(){const l=R().length;s&&(s.textContent=String(l)),e.classList.toggle("hidden",0===l),n&&(n.checked=l>0&&l===r.length),o&&(o.disabled=t||0===l)}r.forEach(e=>e.addEventListener("change",c)),n&&n.addEventListener("change",()=>{r.forEach(e=>{e.checked=n.checked}),c()}),i&&i.addEventListener("click",()=>{r.forEach(e=>{e.checked=!1}),c()}),l&&l.addEventListener("click",()=>q("csv")),a&&a.addEventListener("click",()=>q("json")),o&&o.addEventListener("click",O)}(),document.querySelectorAll(".js-delete-row").forEach(e=>{e.addEventListener("click",async()=>{const t=e.dataset.table,n=e.dataset.pk;t&&n&&await UI.confirm({title:"Delete Row",message:"Are you sure you want to permanently delete this row? This action cannot be undone.",item:n,itemType:"Primary Key",confirmText:"Delete row",danger:!0})&&Api.del("/api/tables/"+encodeURIComponent(t)+"/row/"+encodeURIComponent(n)).then(()=>{UI.showToast("Row deleted.","success"),window.location.reload()}).catch(e=>UI.showError(e.message))})}),function(){const t=document.getElementById("page-size");t&&t.addEventListener("change",()=>{const n=t.dataset.table,s=t.value,o=t.dataset.orderby||"",l=t.dataset.orderdir||"asc",a=t.dataset.filters||"";let i=e+"/tables/"+encodeURIComponent(n)+"?page=1&size="+encodeURIComponent(s)+"&orderBy="+encodeURIComponent(o)+"&orderDir="+encodeURIComponent(l);a&&(i+="&"+a),window.location.href=i})}(),function(){const e=document.getElementById("import-csv"),t=document.getElementById("import-panel"),n=document.getElementById("import-csv-close"),s=document.getElementById("import-csv-text"),o=document.getElementById("import-csv-file"),l=document.getElementById("import-csv-submit");e&&t&&e.addEventListener("click",()=>{t.classList.toggle("hidden"),t.classList.contains("hidden")||s.focus()}),n&&n.addEventListener("click",()=>t.classList.add("hidden")),o&&o.addEventListener("change",()=>{const e=o.files&&o.files[0];if(!e)return;const t=new FileReader;t.onload=()=>{s.value=String(t.result||"")},t.readAsText(e)}),l&&s&&l.addEventListener("click",async()=>{const e=s.value.trim();if(!e)return UI.showError("Paste or choose a CSV file first.");const t=window.location.pathname.match(/\/tables\/([^/]+)(?:\/|$)/),n=t?decodeURIComponent(t[1]):"";if(!n)return UI.showError("Could not determine the table.");l.disabled=!0;try{const t=await Api.post("/api/tables/"+encodeURIComponent(n)+"/rows/import",{csv:e});UI.showToast(t.message||"Import complete.","success"),window.location.reload()}catch(e){UI.showError(e.message)}finally{l.disabled=!1}})}(),document.querySelectorAll(".js-ref-toggle").forEach(e=>{e.addEventListener("click",t=>{if(t.stopPropagation(),"true"===e.getAttribute("aria-expanded"))return void P();const n=e.dataset.table,s=e.dataset.pk;n&&s&&(e.classList.add("opacity-50"),Api.get("/api/tables/"+encodeURIComponent(n)+"/rows/"+encodeURIComponent(s)+"/references").then(t=>{e.classList.remove("opacity-50");const n=e.dataset.refTable,s=(t&&t.references||[]).filter(e=>!n||e.table===n);e.setAttribute("aria-expanded","true"),D(e,!0),_(e,s)}).catch(t=>{e.classList.remove("opacity-50"),UI.showError(t.message)}))})})}()}();
|
|
1
|
+
!function(){"use strict";const e=window.APP&&window.APP.basePath||"",t=!(!window.APP||!window.APP.readonly),n=document.getElementById("browse-config"),s=n?JSON.parse(n.textContent):{table:"",filters:{},pkCols:[],hasPk:!1,readonly:t},o=encodeURIComponent(window.APP&&window.APP.dbId||s&&s.dbId||""),l=encodeURIComponent(s.table),a=window.Utils?window.Utils.escapeHtml:function(e){return String(null==e?"":e)};let i=null,r={};function c(e){return i?i.columns.find(t=>t.name===e):null}function d(e){return!!i&&-1!==i.primaryKey.indexOf(e)}function u(e){return i?i.foreignKeys.find(t=>t.from===e):null}function p(e,t){const n=e.find(e=>e.op===t);return n?n.value??null:null}function f(e,t,n){if(null==e||""===e)return"";const s=String(e);if(t){const e=s.match(/^(\d{4}-\d{2}-\d{2})/);return e?e[1]:""}if(n){const e=s.match(/(\d{2}:\d{2}(?::\d{2})?)/);return e?e[1]:""}const o=s.match(/^(\d{4}-\d{2}-\d{2})(?:[ T](\d{2}):(\d{2})(?::(\d{2}))?)?/);return o?o[1]+"T"+(o[2]?o[2]+":"+o[3]+(o[4]?":"+o[4]:""):"00:00:00"):""}function m(e,t,n){const s=document.createElement("option");return s.value=t,s.textContent=n,e.appendChild(s),s}function h(e){const t=document.createElement("label");t.className="block";const n=document.createElement("span");n.className="field-label !mb-1 font-mono",n.textContent=e.name,t.appendChild(n);const o=u(e.name),l=(e.type||"").toUpperCase(),a="BOOLEAN"===l||"BOOL"===l,i=l.startsWith("INT")||"REAL"===l||"NUMERIC"===l||l.startsWith("DECIMAL")||"FLOAT"===l||"DOUBLE"===l||l.startsWith("BIGINT")||l.startsWith("SMALLINT")||l.startsWith("SERIAL")||"MONEY"===l,c="DATE"===l,d="TIME"===l||"TIMETZ"===l||l.startsWith("TIME ")||l.startsWith("TIME("),h="DATETIME"===l||l.includes("TIMESTAMP"),g=c||d||h,b=s.filters[e.name],y=function(e){return null==e?[]:Array.isArray(e)?e.filter(e=>e&&"object"==typeof e&&"string"==typeof e.op):"object"==typeof e?[e]:[]}(b),v=function(e){return"string"==typeof e?e:null}(b);if(o){const n=document.createElement("select");n.className="filter-input field-select !py-1.5 !text-[13px]",n.dataset.column=e.name,n.dataset.kind="fk",m(n,"","(any)"),m(n,"__NULL__","(not set / NULL)"),(r[e.name]||[]).forEach(e=>m(n,String(e.value),""===e.label?String(e.value):e.label));let s=p(y,"eq");return null==s&&v&&v.startsWith("=")&&(s=v.slice(1)),"__NULL__"===s?n.value="__NULL__":null!=s&&[...n.options].some(e=>e.value===String(s))&&(n.value=String(s)),t.appendChild(n),t}if(a){const n=document.createElement("select");n.className="filter-input field-select !py-1.5 !text-[13px]",n.dataset.column=e.name,n.dataset.kind="bool",m(n,"","(any)"),m(n,"1","true"),m(n,"0","false");let s=p(y,"eq");return null==s&&v&&v.startsWith("=")&&(s=v.slice(1)),"1"!==s&&"0"!==s&&"true"!==s&&"false"!==s||(n.value="true"===s||"1"===s?"1":"0"),t.appendChild(n),t}if(g||i){const n=document.createElement("div");n.className="flex items-center gap-1.5";const s=document.createElement("input"),o=document.createElement("input");if(g)s.type=o.type=c?"date":d?"time":"datetime-local",(d||h)&&(s.step=o.step="1");else{s.type=o.type="number";const e=l.startsWith("INTEGER")||"INT"===l||l.startsWith("BIGINT")||l.startsWith("SMALLINT")||l.startsWith("SERIAL");s.step=o.step=e?"1":"any"}s.className="filter-input field-input !py-1.5 !text-[13px]",o.className="filter-input field-input !py-1.5 !text-[13px]",s.dataset.column=e.name,s.dataset.bound="min",s.placeholder=g?"from":"min",o.dataset.column=e.name,o.dataset.bound="max",o.placeholder=g?"to":"max";const a=function(e){const t=e.find(e=>"between"===e.op);return t?{min:t.value??null,max:t.max??null}:{min:null,max:null}}(y),i=p(y,"gte")??p(y,"gt")??a.min,r=p(y,"lte")??p(y,"lt")??a.max;return null!=i&&(s.value=g?f(i,c,d):String(i)),null!=r&&(o.value=g?f(r,c,d):String(r)),n.appendChild(s),n.appendChild(o),t.appendChild(n),t}const x=document.createElement("input");x.type="text",x.className="filter-input field-input !py-1.5 !text-[13px]",x.dataset.column=e.name,x.dataset.kind="text",x.placeholder="BLOB"===l?"0x… or text":"filter…",x.autocomplete="off",x.spellcheck=!1;let w=v;if(null==w){const e=p(y,"prefix"),t=p(y,"eq"),n=p(y,"like");null!=e?w=String(e)+"*":null!=t?w="="+String(t):null!=n&&(w=String(n))}return null!=w&&(x.value=w),t.appendChild(x),t}function g(t){let n=s.table;if(!n){const e=window.location.pathname.match(/\/tables\/[^/]+\/([^/]+)(?:\/|$)/);e&&(n=decodeURIComponent(e[1]))}if(!n)return;const l=document.getElementById("page-size"),a=new URLSearchParams({page:"1",size:l?l.value:"50"});Object.keys(t).length&&a.set("f",JSON.stringify(t)),window.location.href=e+"/tables/"+o+"/"+encodeURIComponent(n)+"?"+a.toString()}function b(){const e=document.getElementById("filter-form"),t=document.getElementById("clear-filter"),n=document.getElementById("filter-fields");e&&n&&i&&(i.columns.forEach(e=>n.appendChild(h(e))),e.addEventListener("submit",e=>{e.preventDefault(),g(function(){const e={},t={};document.querySelectorAll("#filter-fields .filter-input").forEach(e=>{const n=e.dataset.column;n&&(t[n]=t[n]||[]).push(e)});for(const n of Object.keys(t)){const s=t[n],o=s[0],l=o.dataset.kind||"";if("fk"===l){const t=o.value;if(""===t)continue;e[n]="__NULL__"===t?{op:"null"}:{op:"eq",value:t}}else if("bool"===l){const t=o.value;if(""===t)continue;e[n]={op:"eq",value:t}}else if(2===s.length&&o.dataset.bound){const t=s.find(e=>"min"===e.dataset.bound),o=s.find(e=>"max"===e.dataset.bound),l=t&&""!==t.value?t.value:null,a=o&&""!==o.value?o.value:null;null!=l&&null!=a?e[n]={op:"between",value:l,max:a}:null!=l?e[n]={op:"gte",value:l}:null!=a&&(e[n]={op:"lte",value:a})}else{const t=(o.value||"").trim();t&&(e[n]=t)}}return e}())})),t&&t.addEventListener("click",()=>g({}))}let y=null;function v(){y&&(y.classList.remove("cell-focused"),y=null)}function x(e){e&&e!==y&&!e.classList.contains("js-editing")&&(v(),y=e,e.classList.add("cell-focused"),e.scrollIntoView({block:"nearest",inline:"nearest"}))}function w(e){const t=e.closest("tr"),n=t&&t.dataset.pk,s=e.dataset.col;if(!n||!s)return;const o=e.dataset.origValue;E(e,"true"===e.dataset.origNull?null:o),e.classList.remove("cell-dirty"),delete e.dataset.origValue,delete e.dataset.origNull,L.delete(n+"::"+s),S(),UI.showToast('Reverted edit in column "'+s+'".',"info")}function E(e,t){const n=null==t,s=n?"":String(t);e.dataset.null=String(n),e.dataset.value=s,e.classList.remove("js-editing","opacity-60");const o=void 0!==e.dataset.origValue,l=e.dataset.origValue,i="true"===e.dataset.origNull,r=o?"Staged edit: "+(n?"NULL":s)+" (Saved: "+(i?"NULL":l)+")":s,d=o?'<button type="button" class="cell-revert-btn js-revert-cell" title="Revert this field to saved value">↺</button>':"";if(n)return void(e.innerHTML='<div class="flex items-center justify-between gap-1"><span class="chip chip-null js-cell-value">NULL</span>'+d+"</div>");const u=c(e.dataset.col),p=(u?.type||e.dataset.type||"").toUpperCase(),f="true"===e.dataset.blob||p.includes("BLOB")||/^0x[0-9a-f]{8,}$/i.test(s),m="true"===e.dataset.json||s.startsWith("{")&&s.endsWith("}")||s.startsWith("[")&&s.endsWith("]");if(f){const t=s.startsWith("0x")?Math.floor((s.length-2)/2):s.length;e.innerHTML='<div class="flex items-center justify-between gap-1 js-cell-value"><div class="flex items-center gap-1.5 min-w-0"><span class="inline-flex items-center gap-1 rounded-md bg-secondary/10 px-1.5 py-0.5 text-[11px] font-mono font-medium text-secondary">🗃️ BLOB · '+t+' B</span><button type="button" class="btn btn-ghost btn-xs btn-circle opacity-0 group-hover:opacity-100 transition-opacity js-inspect-btn" title="Inspect BLOB"><svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg></button></div>'+d+"</div>"}else e.innerHTML=m?'<div class="flex items-center justify-between gap-1 js-cell-value"><div class="flex items-center gap-1.5 min-w-0"><span class="inline-flex items-center gap-1 rounded-md bg-info/10 px-1.5 py-0.5 text-[11px] font-mono font-medium text-info">{ } JSON</span><span class="block max-w-[140px] truncate font-mono text-[12px] text-base-content/70" title="'+a(r)+'">'+a(s)+'</span><button type="button" class="btn btn-ghost btn-xs btn-circle opacity-0 group-hover:opacity-100 transition-opacity js-inspect-btn" title="Inspect JSON"><svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg></button></div>'+d+"</div>":'<div class="flex items-center justify-between gap-1 group/cell"><span class="js-cell-value block max-w-xs truncate font-mono text-[13px]" title="'+a(r)+'">'+a(s.length>120?s.slice(0,120)+"…":s)+'</span><div class="flex items-center gap-0.5 shrink-0">'+d+(s.length>30?'<button type="button" class="btn btn-ghost btn-xs btn-circle opacity-0 group-hover/cell:opacity-100 transition-opacity js-inspect-btn shrink-0" title="Inspect text"><svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg></button>':"")+"</div></div>"}function k(e){return"true"===e.dataset.null?null:e.dataset.value??""}const L=new Map;function I(e,t,n){const s=e.dataset.pk,o=t.dataset.col;if(!s||!o)return;const l=c(o);if(!l)return;let a,i=!1;if("checkbox"===n.type)a=n.checked?"1":"0";else{const e=void 0===n.value?"":String(n.value);""===e||"__NULL__"===e?(a="",i=!0):a=e}const r=(l.type||"").toUpperCase(),p="BOOLEAN"===r||"BOOL"===r,f=void 0!==t.dataset.origValue?"true"===t.dataset.origNull:"true"===t.dataset.null,m=void 0!==t.dataset.origValue?t.dataset.origValue??"":t.dataset.value??"";let h=!1;if(i&&f)h=!0;else if(!i&&!f)if(p){h=("1"===m||"true"===m||1===m||!0===m)===("1"===a||"true"===a)}else h=String(m)===String(a);if(h)return delete t.dataset.origValue,delete t.dataset.origNull,t.classList.remove("cell-dirty"),L.delete(s+"::"+o),E(t,i?null:a),void S();void 0===t.dataset.origValue&&(t.dataset.origValue=m,t.dataset.origNull=f?"true":"false"),E(t,i?null:a),L.set(s+"::"+o,{td:t,pk:s,col:o,rawValue:a,setNull:i,isPk:d(o),isFk:!!u(o),origValue:t.dataset.origValue,origNull:"true"===t.dataset.origNull}),t.classList.add("cell-dirty"),S()}function S(){if(!j||!C)return;const e=L.size;C.textContent=String(e),j.classList.toggle("hidden",0===e)}function T(){if(!L.size||t)return;const e=new Map;for(const t of L.values()){let n=e.get(t.pk);n||(n={id:t.pk,values:{},nulls:[]},e.set(t.pk,n)),t.setNull?n.nulls.push(t.col):n.values[t.col]=t.rawValue}const n=[...e.values()];B&&(B.disabled=!0),Api.post("/api/tables/"+o+"/"+l+"/rows/bulk-update",{updates:n}).then(e=>{UI.showToast(e.message||"Changes applied.","success"),window.location.reload()}).catch(e=>{UI.showError(e.message),B&&(B.disabled=!1)})}async function A(){const e=L.size;if(!e)return;if(await UI.confirm({title:"Discard Changes",message:"You have "+e+" pending inline edit(s). Are you sure you want to discard all staged changes and revert to the saved values?",confirmText:"Discard "+e+" change(s)",danger:!0})){for(const e of L.values())E(e.td,e.origNull?null:e.origValue),e.td.classList.remove("cell-dirty"),delete e.td.dataset.origValue,delete e.td.dataset.origNull;L.clear(),S(),UI.showToast("Pending edits discarded.","info")}}function N(){if(L.size){var e=document.createElement("div");e.className="fixed inset-0 z-[100] flex items-center justify-center bg-black/60 backdrop-blur-xs p-4 opacity-0 transition-opacity duration-200";var t=document.createElement("div");t.className="app-card w-full max-w-3xl overflow-hidden shadow-2xl border border-base-300 transform scale-95 transition-transform duration-200 max-h-[85vh] flex flex-col";var n="";L.forEach((e,t)=>{var s=e.origNull?'<span class="chip chip-null text-[10px]">NULL</span>':a(e.origValue),o=e.setNull?'<span class="chip chip-null text-[10px]">NULL</span>':a(e.rawValue);n+='<tr class="hover:bg-base-200/50"><td class="font-mono text-xs font-semibold">'+a(e.pk)+'</td><td class="font-mono text-xs text-primary font-medium">'+a(e.col)+'</td><td class="font-mono text-xs text-error line-through opacity-80 max-w-xs truncate">'+s+'</td><td class="font-mono text-xs text-success font-medium max-w-xs truncate">'+o+'</td><td class="text-right"><button type="button" class="btn btn-ghost btn-xs text-warning js-modal-revert-item" data-key="'+a(t)+'">Revert</button></td></tr>'}),t.innerHTML='<div class="flex items-center justify-between border-b border-base-200 px-6 py-4"> <div class="flex items-center gap-2"> <h3 class="text-base font-bold text-base-content">Staged Changes Diff</h3> <span class="chip bg-warning/15 text-warning font-mono">'+L.size+' edit(s)</span> </div> <button type="button" class="btn btn-ghost btn-xs btn-circle js-diff-close text-base-content/40 hover:text-base-content">×</button></div><div class="flex-1 overflow-y-auto p-4"> <table class="table table-sm"> <thead><tr class="text-xs uppercase text-base-content/50"><th>Row PK</th><th>Column</th><th>Original Value</th><th>Staged Value</th><th class="text-right">Action</th></tr></thead> <tbody id="diff-rows-body">'+n+'</tbody> </table></div><div class="flex items-center justify-between border-t border-base-200 bg-base-200/30 px-6 py-4"> <button type="button" id="diff-modal-discard" class="btn btn-ghost btn-sm text-error">Discard All</button> <div class="flex gap-2"> <button type="button" class="btn btn-ghost btn-sm js-diff-close">Cancel</button> <button type="button" id="diff-modal-apply" class="btn btn-primary btn-sm gap-1"> <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" /></svg> Apply '+L.size+" Change(s) </button> </div></div>",e.appendChild(t),document.body.appendChild(e),requestAnimationFrame(()=>{e.classList.remove("opacity-0"),e.classList.add("opacity-100"),t.classList.remove("scale-95"),t.classList.add("scale-100")}),t.querySelectorAll(".js-diff-close").forEach(e=>e.addEventListener("click",s)),e.addEventListener("click",t=>{t.target===e&&s()}),t.querySelectorAll(".js-modal-revert-item").forEach(e=>{e.addEventListener("click",()=>{const t=e.dataset.key,n=L.get(t);n&&(w(n.td),e.closest("tr").remove(),L.size||s())})}),t.querySelector("#diff-modal-apply").addEventListener("click",()=>{s(),T()}),t.querySelector("#diff-modal-discard").addEventListener("click",async()=>{s(),await A()})}function s(){e.classList.remove("opacity-100"),e.classList.add("opacity-0"),t.classList.remove("scale-100"),t.classList.add("scale-95"),setTimeout(()=>e.remove(),180)}}let j=null,C=null,B=null;function U(e){if(t)return;if(e.classList.contains("js-editing"))return;const n=e.closest("tr");if(!n||!n.dataset.pk||!n.dataset.table)return;if(!c(e.dataset.col))return;x(e);const s=document.querySelector(".js-editing");s&&s._finish&&s._finish(!0),e._origNull="true"===e.dataset.null,e._origValue=k(e);const o=function(e){const t=e.dataset.col,n=c(t);if(!n)return null;const s=u(t),o=(n.type||"").toUpperCase(),l="BOOLEAN"===o||"BOOL"===o,a="DATE"===o,i="TIME"===o||"TIMETZ"===o||o.startsWith("TIME ")||o.startsWith("TIME("),d="DATETIME"===o||o.includes("TIMESTAMP"),p=a||i||d,h=o.startsWith("INT")||"REAL"===o||"NUMERIC"===o||o.startsWith("DECIMAL")||"FLOAT"===o||"DOUBLE"===o||o.startsWith("BIGINT")||o.startsWith("SMALLINT")||o.startsWith("SERIAL")||"MONEY"===o,g="BLOB"===o||"BYTEA"===o||"BINARY"===o,b="UUID"===o,y=o.includes("JSON"),v=o.endsWith("[]")||o.startsWith("_")||o.includes("ARRAY"),x="INTERVAL"===o,w="POINT"===o||"GEOMETRY"===o||"BOX"===o||"CIRCLE"===o||"POLYGON"===o,E="BIT"===o||"VARBIT"===o||o.startsWith("BIT(")||o.startsWith("VARBIT("),L=k(e),I=null===L||""===L;let S;if(l)S=document.createElement("input"),S.type="checkbox",S.className="checkbox checkbox-primary checkbox-sm js-inline-ctrl",S.checked=1===L||!0===L||"1"===L||"true"===L;else if(s)S=document.createElement("select"),S.className="field-select !w-64 !py-1.5 !text-[13px] js-inline-ctrl",m(S,"","(not set / NULL)"),(r[t]||[]).forEach(e=>{const t=m(S,String(e.value),""===e.label?String(e.value):e.label);I||String(L)!==String(e.value)||(t.selected=!0)});else if(p)S=document.createElement("input"),S.type=a?"date":i?"time":"datetime-local",(i||d)&&(S.step="1"),S.className="field-input !w-64 !py-1.5 !text-[13px] js-inline-ctrl font-mono",I||(S.value=f(String(L),a,i)),setTimeout(()=>{if("function"==typeof S.showPicker)try{S.showPicker()}catch(e){}},50);else if(h){S=document.createElement("input"),S.type="number";const e=o.startsWith("INTEGER")||"INT"===o||o.startsWith("BIGINT")||o.startsWith("SMALLINT")||o.startsWith("SERIAL");S.step=e?"1":"any",S.className="field-input !w-48 !py-1.5 !text-[13px] js-inline-ctrl font-mono",I||(S.value=String(L))}else b?(S=document.createElement("input"),S.type="text",S.className="field-input font-mono !w-64 !py-1.5 !text-[12px] js-inline-ctrl",S.placeholder="00000000-0000-0000-0000-000000000000",I||(S.value=String(L))):v?(S=document.createElement("input"),S.type="text",S.className="field-input font-mono !w-64 !py-1.5 !text-[12px] js-inline-ctrl",S.placeholder="{item1, item2}",I||(S.value=String(L))):x?(S=document.createElement("input"),S.type="text",S.className="field-input font-mono !w-64 !py-1.5 !text-[12px] js-inline-ctrl",S.placeholder="e.g. '1 day'",I||(S.value=String(L))):w?(S=document.createElement("input"),S.type="text",S.className="field-input font-mono !w-64 !py-1.5 !text-[12px] js-inline-ctrl",S.placeholder="(x, y)",I||(S.value=String(L))):E?(S=document.createElement("input"),S.type="text",S.className="field-input font-mono !w-64 !py-1.5 !text-[12px] js-inline-ctrl",S.placeholder="101010",I||(S.value=String(L))):(S=document.createElement("input"),S.type="text",S.className="field-input !w-64 !py-1.5 !text-[13px] js-inline-ctrl",S.placeholder=g?"BYTEA"===o?"\\x… or 0x… hex":"0x… hex or text":y?'{"key":"value"}':"",I||(S.value=String(L)));return S}(e);if(!o)return;e.classList.add("js-editing"),e.innerHTML="",e.appendChild(o);let l=!1;const a=(t,s)=>{l||(l=!0,t?I(n,e,o):function(e){E(e,e._origNull?null:e._origValue),e.classList.remove("js-editing")}(e),"down"===s?R("down"):"right"===s?R("right"):"left"===s&&R("left"))};e._finish=a,o.addEventListener("keydown",e=>{"Enter"===e.key?(e.preventDefault(),a(!0,"down")):"Tab"===e.key?(e.preventDefault(),a(!0,e.shiftKey?"left":"right")):"Escape"===e.key&&(e.preventDefault(),a(!1))}),"SELECT"===o.tagName||"checkbox"===o.type?o.addEventListener("change",()=>a(!0)):o.addEventListener("blur",()=>a(!0)),o.focus(),"function"==typeof o.select&&o.select()}function R(e){const t=e=>Array.prototype.slice.call(e.querySelectorAll("td.cell-editable")).filter(e=>"none"!==e.style.display&&!e.classList.contains("hidden"));if(!y){const e=document.querySelector("#main-table tbody tr");if(e){const n=t(e);n.length&&x(n[0])}return}const n=y.closest("tr");if(!n)return;const s=t(n),o=s.indexOf(y);if("right"===e)if(o>=0&&o<s.length-1)x(s[o+1]);else{const e=n.nextElementSibling;if(e){const n=t(e);n.length&&x(n[0])}}else if("left"===e)if(o>0)x(s[o-1]);else{const e=n.previousElementSibling;if(e){const n=t(e);n.length&&x(n[n.length-1])}}else if("down"===e){const e=n.nextElementSibling;if(e){const n=y.dataset.col,s=n?e.querySelector('td[data-col="'+CSS.escape(n)+'"]'):null;if(s&&"none"!==s.style.display)x(s);else{const n=t(e);(n[o]||n[0])&&x(n[o]||n[0])}}}else if("up"===e){const e=n.previousElementSibling;if(e){const n=y.dataset.col,s=n?e.querySelector('td[data-col="'+CSS.escape(n)+'"]'):null;if(s&&"none"!==s.style.display)x(s);else{const n=t(e);(n[o]||n[0])&&x(n[o]||n[0])}}}}function M(){j=function(){const e=document.createElement("div");return e.id="changes-bar",e.className="fixed bottom-5 left-1/2 z-50 flex -translate-x-1/2 items-center gap-3 rounded-2xl border border-base-300 bg-base-100/95 backdrop-blur-md px-4 py-2.5 shadow-2xl transition-all duration-200 hidden",e.innerHTML='<div class="flex items-center gap-2"><span class="relative flex h-2.5 w-2.5"><span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-warning opacity-75"></span><span class="relative inline-flex rounded-full h-2.5 w-2.5 bg-warning"></span></span><span class="text-sm font-medium text-base-content"><b id="changes-count" class="tabular-nums text-warning font-bold">0</b> pending edit(s)</span></div><div class="h-4 w-px bg-base-300"></div><div class="flex items-center gap-1.5"><button type="button" id="changes-review" class="btn btn-ghost btn-sm gap-1.5 text-xs font-medium"><svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg>Review Diff</button><button type="button" id="changes-apply" class="btn btn-primary btn-sm gap-1 text-xs font-semibold"><svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" /></svg>Apply</button><button type="button" id="changes-discard" class="btn btn-ghost btn-sm text-xs text-base-content/60 hover:text-error">Discard</button></div>',document.body.appendChild(e),e}(),C=document.getElementById("changes-count"),B=document.getElementById("changes-apply");const e=document.getElementById("changes-discard"),n=document.getElementById("changes-review");B&&B.addEventListener("click",T),e&&e.addEventListener("click",A),n&&n.addEventListener("click",N),document.addEventListener("click",e=>{const t=e.target.closest(".js-revert-cell");if(t){e.stopPropagation(),e.preventDefault();const n=t.closest("td");return void(n&&w(n))}const n=e.target.closest("td.cell-editable");n&&!n.classList.contains("js-editing")&&x(n)}),t||document.querySelectorAll(".js-inline-cell").forEach(e=>{e.addEventListener("dblclick",()=>U(e))}),document.addEventListener("keydown",e=>{if(document.querySelector(".js-editing"))return;const n=document.activeElement?document.activeElement.tagName:"";if("INPUT"!==n&&"TEXTAREA"!==n&&"SELECT"!==n&&y)if("ArrowRight"===e.key)e.preventDefault(),R("right");else if("ArrowLeft"===e.key)e.preventDefault(),R("left");else if("ArrowDown"===e.key)e.preventDefault(),R("down");else if("ArrowUp"===e.key)e.preventDefault(),R("up");else if("Tab"===e.key)e.preventDefault(),R(e.shiftKey?"left":"right");else if("Enter"===e.key)e.preventDefault(),t||U(y);else if("Escape"===e.key)v();else if(" "!==e.key||t){if("i"===e.key||"I"===e.key)e.preventDefault(),z(y);else if((e.ctrlKey||e.metaKey)&&"c"===e.key.toLowerCase()){const e=k(y);null!=e&&navigator.clipboard.writeText(String(e)).then(()=>{UI.showToast("Cell value copied.","info")})}}else{const t=c(y.dataset.col);if(t&&"BOOLEAN"===(t.type||"").toUpperCase()){e.preventDefault();const t=y.closest("tr"),n=k(y),s=1===n||"1"===n||!0===n||"true"===n?"0":"1";I(t,y,{value:s,type:"checkbox",checked:"1"===s})}else e.preventDefault(),z(y)}})}function q(){return Array.prototype.slice.call(document.querySelectorAll(".row-checkbox:checked")).map(e=>e.value).filter(Boolean)}async function O(e){const t=q();if(t.length)try{const n=await Api.post("/api/tables/"+o+"/"+l+"/rows/bulk-export",{ids:t,format:e}),s=new Blob([n.content],{type:"json"===e?"application/json;charset=utf-8":"text/csv;charset=utf-8"}),a=document.createElement("a");a.href=URL.createObjectURL(s),a.download=n.filename,document.body.appendChild(a),a.click(),a.remove(),URL.revokeObjectURL(a.href),UI.showToast("Exported "+n.rowCount+" row(s).","success")}catch(e){UI.showError(e.message)}}async function D(){const e=q();if(!e.length||t)return;let n;try{n=await Api.post("/api/tables/"+o+"/"+l+"/rows/bulk-impact",{ids:e})}catch(e){return void UI.showError(e.message)}const i=n&&n.references||[],r=i.length?'<p class="mb-2 font-medium text-warning">⚠ Other tables reference these records:</p><ul class="mb-2 list-disc space-y-1 pl-5 text-xs">'+i.map(e=>'<li><code class="font-mono font-bold">'+a(e.table)+"."+a(e.from)+'</code> — <b class="tabular-nums">'+e.count+"</b> row(s) point at them</li>").join("")+'</ul><p class="text-[11px] text-base-content/55">Depending on foreign key rules, those rows may cascade-delete or block this action.</p>':'<p class="text-xs text-base-content/60">No other tables reference these rows — safe to delete.</p>';if(await UI.confirm({title:"Delete Selected Rows",message:"Delete "+e.length+' selected row(s) from table "'+s.table+'"? This action cannot be undone.',item:s.table,itemType:"Table",detailsHtml:r,confirmText:"Delete "+e.length+" row(s)",danger:!0}))try{const t=await Api.post("/api/tables/"+o+"/"+l+"/rows/bulk-delete",{ids:e,confirmImpact:!0});UI.showToast(t.message||"Rows deleted.","success"),window.location.reload()}catch(e){UI.showError(e.message)}}function W(e,t){const n=e.querySelector(".js-ref-chevron");n&&n.classList.toggle("rotate-180",t)}let P=null;function _(){const e=document.querySelector(".js-ref-pop");e&&(e._cleanup&&e._cleanup(),e.remove()),P&&(W(P,!1),P.setAttribute("aria-expanded","false"),P.classList.remove("opacity-50"),P=null)}function V(t,n){_();const s=document.createElement("div");s.className="js-ref-pop app-card js-ref-anim z-50 min-w-[280px] max-w-[460px] overflow-hidden shadow-lift",s.innerHTML=n.length?n.map(t=>function(e,t){const n=t+"/tables/"+encodeURIComponent(e.table),s=e.count??e.total??(e.rows?e.rows.length:0),o=e.columns||(e.rows&&e.rows.length>0?Object.keys(e.rows[0]).filter(e=>"_rowid_"!==e):[]);let l='<div class="flex items-center justify-between gap-2 border-b border-base-200 px-3 py-2"><span class="min-w-0 truncate"><a class="link link-primary font-mono text-xs font-semibold" href="'+n+'">'+a(e.table)+'</a><span class="ml-1.5 text-xs text-base-content/45">via <code class="font-mono">'+a(e.from)+"</code>"+(void 0!==e.value?' = <code class="font-mono">'+a(String(e.value))+"</code>":"")+'</span></span><span class="chip '+(s?"chip-pk":"chip-type")+'">'+a(s+" row"+(1===s?"":"s"))+"</span></div>";return e.rows&&e.rows.length&&o.length?l+'<div class="max-h-64 overflow-auto"><table class="w-full"><thead><tr class="bg-base-200/40"><tr>'+o.map(function(e){return'<th class="whitespace-nowrap px-2.5 py-1.5 text-left text-[10px] font-semibold uppercase tracking-wide text-base-content/50"><span class="font-mono normal-case">'+a(e)+"</span></th>"}).join("")+"</tr></tr></thead><tbody>"+e.rows.map(function(e){return'<tr class="border-t border-base-200/60 hover:bg-primary/[0.04]">'+o.map(function(t){const n=e[t];if(null==n)return'<td class="px-2.5 py-1.5 align-middle"><span class="text-[11px] text-base-content/40">NULL</span></td>';const s=String(n),o=s.length>120?s.slice(0,120)+"…":s;return'<td class="max-w-[180px] truncate px-2.5 py-1.5 align-middle font-mono text-[11px] text-base-content/80" title="'+a(s)+'">'+a(o)+"</td>"}).join("")+"</tr>"}).join("")+'</tbody></table></div><div class="flex justify-end border-t border-base-200 px-3 py-1.5"><a class="btn btn-ghost btn-xs text-base-content/60 hover:text-primary" href="'+n+'">Open table</a></div>':l+'<div class="px-3 py-3 text-xs text-base-content/45">No matching rows.</div>'}(t,e)).join('<div class="h-px bg-base-200"></div>'):'<div class="px-4 py-3 text-sm text-base-content/45">No related rows for this record.</div>',s.style.visibility="hidden",document.body.appendChild(s);const o=Math.min(460,Math.max(280,s.offsetWidth)),l=s.offsetHeight,i=t.getBoundingClientRect();let r=i.left;r+o>window.innerWidth-8&&(r=Math.max(8,window.innerWidth-o-8));let c=i.bottom+6;c+l>window.innerHeight-8&&(c=Math.max(8,i.top-l-6)),s.style.position="fixed",s.style.left=r+"px",s.style.top=c+"px",s.style.visibility="visible",P=t;const d=e=>{e&&e.target&&s.contains(e.target)||_()};window.addEventListener("scroll",d,!0),window.addEventListener("resize",d);const u=e=>{s.contains(e.target)||e.target===t||t.contains(e.target)||_()};document.addEventListener("click",u,!0);const p=e=>{"Escape"===e.key&&_()};document.addEventListener("keydown",p),s._cleanup=()=>{window.removeEventListener("scroll",d,!0),window.removeEventListener("resize",d),document.removeEventListener("click",u,!0),document.removeEventListener("keydown",p)}}function z(e){if(!e)return;const n=e.closest("tr");if(!n)return;const l=e.dataset.col,a="true"===e.dataset.null,i=e.dataset.value,r=n.dataset.pk,d=n.dataset.table||s.table,u="true"===e.dataset.blob,p="true"===e.dataset.json,f="true"===e.dataset.image,m="true"===e.dataset.audio,h="true"===e.dataset.video,g="true"===e.dataset.pdf,b=e.querySelector("img")&&e.querySelector("img").src||"",y=e.dataset.type||(c(l)?.type??"");window.Inspector&&window.Inspector.open&&window.Inspector.open({dbId:o,table:d,pk:r,col:l,colType:y,value:i,isNull:a,isBlob:u,isJson:p,isImage:f,isAudio:m,isVideo:h,isPdf:g,blobUrl:b,readonly:t,onSave:s=>{if(!t){I(n,e,{value:s,type:"text"})}}})}function H(){const e=document.getElementById("main-table");if(!e)return;const t=document.getElementById("col-manager-modal"),n=document.getElementById("btn-columns-manager"),o=document.getElementById("col-count-badge"),l=document.getElementById("col-manager-list"),i=document.getElementById("col-search"),r=document.getElementById("col-btn-show-all"),u=document.getElementById("col-btn-hide-non-pk"),p=document.getElementById("col-btn-reset"),f=document.getElementById("col-manager-summary"),m=Array.prototype.slice.call(e.querySelectorAll("thead th[data-col]"));if(!m.length)return;const h=m.map(e=>e.dataset.col),g="admindb:col_settings:"+(s.table||"default");let b=function(){try{const e=localStorage.getItem(g);if(e){const t=JSON.parse(e);if(t&&"object"==typeof t){const e=new Set(h),n=Array.isArray(t.order)?t.order.filter(t=>e.has(t)):[];h.forEach(e=>{-1===n.indexOf(e)&&n.push(e)});const s=Array.isArray(t.hidden)?t.hidden.filter(t=>e.has(t)):[],o=Array.isArray(t.pinned)?t.pinned.filter(t=>e.has(t)):[];return{order:n,hidden:s,pinned:o}}}}catch(e){}return{order:h.slice(),hidden:[],pinned:[]}}();function y(){try{localStorage.setItem(g,JSON.stringify(b))}catch(e){}v(),w()}function v(){const t=b.order,n=new Set(b.hidden),s=new Set(b.pinned),l=e.querySelector("thead tr");if(l){const e=l.querySelector("th.sticky-col-actions"),o={};m.forEach(e=>{o[e.dataset.col]=e}),t.forEach(t=>{const a=o[t];if(a){e?l.insertBefore(a,e):l.appendChild(a);const o=n.has(t);a.style.display=o?"none":"";const i=s.has(t)&&!o;a.classList.toggle("sticky-col-left",i)}})}e.querySelectorAll("tbody tr").forEach(e=>{const o=Array.prototype.slice.call(e.querySelectorAll("td[data-col]")),l={};o.forEach(e=>{l[e.dataset.col]=e});const a=e.querySelector("td.sticky-col-actions");t.forEach(t=>{const o=l[t];if(o){a?e.insertBefore(o,a):e.appendChild(o);const l=n.has(t);o.style.display=l?"none":"";const i=s.has(t)&&!l;o.classList.toggle("sticky-col-left",i)}})}),x();const a=h.length-b.hidden.length;o&&(o.textContent=a+"/"+h.length,o.classList.toggle("badge-warning",b.hidden.length>0)),f&&(f.textContent=a+" of "+h.length+" visible · "+b.pinned.length+" pinned")}function x(){const t=b.order.filter(e=>b.pinned.includes(e)&&!b.hidden.includes(e)),n=e.querySelector("thead th.th-select-all");let s=n?n.offsetWidth:40;e.querySelectorAll(".sticky-col-left-last").forEach(e=>e.classList.remove("sticky-col-left-last")),t.forEach((n,o)=>{const l=e.querySelector('thead th[data-col="'+CSS.escape(n)+'"]'),a=l?l.offsetWidth:120,i=o===t.length-1;l&&(l.style.left=s+"px",i&&l.classList.add("sticky-col-left-last")),e.querySelectorAll('tbody td[data-col="'+CSS.escape(n)+'"]').forEach(e=>{e.style.left=s+"px",i&&e.classList.add("sticky-col-left-last")}),s+=a}),b.order.forEach(n=>{if(!t.includes(n)){const t=e.querySelector('thead th[data-col="'+CSS.escape(n)+'"]');t&&(t.style.left=""),e.querySelectorAll('tbody td[data-col="'+CSS.escape(n)+'"]').forEach(e=>{e.style.left=""})}})}function w(){if(!l)return;l.innerHTML="";const e=(i?i.value:"").toLowerCase().trim();b.order.forEach((t,n)=>{if(e&&-1===t.toLowerCase().indexOf(e))return;const s=b.hidden.includes(t),o=b.pinned.includes(t),i=t.startsWith("ref:"),r=i?t.slice(4):"",u=!i&&d(t),p=i?null:c(t),f=p?p.type:"",m=i?r:t,h=document.createElement("div");h.className="col-manager-item"+(s?" is-hidden":"")+(o?" is-pinned":""),h.dataset.col=t,h.dataset.index=String(n),h.draggable=!0,h.innerHTML='<div class="flex items-center gap-2 min-w-0 flex-1"><span class="col-drag-handle" title="Drag to reorder"><svg class="h-3.5 w-3.5" viewBox="0 0 24 24" fill="currentColor"><circle cx="9" cy="7" r="1.5"/><circle cx="15" cy="7" r="1.5"/><circle cx="9" cy="12" r="1.5"/><circle cx="15" cy="12" r="1.5"/><circle cx="9" cy="17" r="1.5"/><circle cx="15" cy="17" r="1.5"/></svg></span><input type="checkbox" class="checkbox checkbox-primary checkbox-xs js-col-toggle" '+(s?"":"checked")+' title="Toggle visibility" /><span class="font-mono font-medium truncate text-base-content text-[13px]" title="'+(i?"Referencing table: "+a(r):a(t))+'">'+a(m)+"</span>"+(i?'<span class="chip chip-type text-[9px] font-mono bg-secondary/15 text-secondary">REF</span>':"")+(u?'<span class="chip chip-pk text-[9px]">PK</span>':"")+(f?'<span class="chip chip-type text-[9px] font-mono opacity-70">'+a(f)+"</span>":"")+'</div><div class="flex items-center gap-1 shrink-0"><button type="button" class="btn btn-ghost btn-xs btn-square js-col-pin '+(o?"text-primary bg-primary/10":"text-base-content/40 hover:text-base-content")+'" title="'+(o?"Unpin column":"Pin column to left (sticky)")+'"><svg class="h-3.5 w-3.5" fill="'+(o?"currentColor":"none")+'" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M19 13.5l-5-5m3-3.5a2.121 2.121 0 00-3 0L9.5 9.5 5 10l5 5 .5 4.5 4.5-4.5a2.121 2.121 0 000-3zM4 20l5-5" /></svg></button><button type="button" class="btn btn-ghost btn-xs btn-square text-base-content/40 hover:text-base-content js-col-up" '+(0===n?"disabled":"")+' title="Move Up"><svg class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 15l7-7 7 7"/></svg></button><button type="button" class="btn btn-ghost btn-xs btn-square text-base-content/40 hover:text-base-content js-col-down" '+(n===b.order.length-1?"disabled":"")+' title="Move Down"><svg class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7"/></svg></button></div>';const g=h.querySelector(".js-col-toggle");g&&g.addEventListener("change",()=>{g.checked?b.hidden=b.hidden.filter(e=>e!==t):b.hidden.includes(t)||b.hidden.push(t),y()});const v=h.querySelector(".js-col-pin");v&&v.addEventListener("click",()=>{b.pinned.includes(t)?b.pinned=b.pinned.filter(e=>e!==t):(b.pinned.push(t),b.hidden=b.hidden.filter(e=>e!==t)),y()});const x=h.querySelector(".js-col-up");x&&n>0&&x.addEventListener("click",()=>{const e=b.order[n-1];b.order[n-1]=b.order[n],b.order[n]=e,y()});const w=h.querySelector(".js-col-down");w&&n<b.order.length-1&&w.addEventListener("click",()=>{const e=b.order[n+1];b.order[n+1]=b.order[n],b.order[n]=e,y()}),function(e,t){e.addEventListener("dragstart",n=>{E=t,e.classList.add("is-dragging"),n.dataTransfer&&(n.dataTransfer.effectAllowed="move",n.dataTransfer.setData("text/plain",t))}),e.addEventListener("dragend",()=>{E=null,e.classList.remove("is-dragging"),l.querySelectorAll(".drag-over-top, .drag-over-bottom").forEach(e=>{e.classList.remove("drag-over-top","drag-over-bottom")})}),e.addEventListener("dragover",n=>{if(n.preventDefault(),n.dataTransfer&&(n.dataTransfer.dropEffect="move"),!E||E===t)return;const s=e.getBoundingClientRect(),o=s.top+s.height/2;n.clientY<o?(e.classList.add("drag-over-top"),e.classList.remove("drag-over-bottom")):(e.classList.add("drag-over-bottom"),e.classList.remove("drag-over-top"))}),e.addEventListener("dragleave",()=>{e.classList.remove("drag-over-top","drag-over-bottom")}),e.addEventListener("drop",n=>{if(n.preventDefault(),e.classList.remove("drag-over-top","drag-over-bottom"),!E||E===t)return;const s=b.order.indexOf(E);let o=b.order.indexOf(t);if(-1===s||-1===o)return;const l=e.getBoundingClientRect(),a=l.top+l.height/2;b.order.splice(s,1),o=b.order.indexOf(t),n.clientY>=a&&(o+=1),b.order.splice(o,0,E),y()})}(h,t),l.appendChild(h)})}let E=null;r&&r.addEventListener("click",()=>{b.hidden=[],y()}),u&&u.addEventListener("click",()=>{b.hidden=h.filter(e=>!d(e)),y()}),p&&p.addEventListener("click",()=>{b={order:h.slice(),hidden:[],pinned:[]};try{localStorage.removeItem(g)}catch(e){}y(),UI.showToast("Columns reset to default.","info")}),i&&i.addEventListener("input",w),n&&t&&n.addEventListener("click",()=>{w(),"function"==typeof t.showModal?t.showModal():t.classList.add("modal-open"),i&&(i.value="",setTimeout(()=>i.focus(),50))}),v(),window.addEventListener("resize",x)}!async function(){try{i=await Api.get("/api/tables/"+o+"/"+l+"/info")}catch(e){i=null}try{r=await Api.get("/api/tables/"+o+"/"+l+"/fk-options")}catch(e){r={}}H(),b(),document.querySelectorAll(".js-row-copy-json").forEach(e=>{e.addEventListener("click",t=>{t.stopPropagation();const n=e.closest("tr");if(!n)return;const s={};n.querySelectorAll("td.cell-editable").forEach(e=>{const t=e.dataset.col;t&&(s[t]="true"===e.dataset.null?null:e.dataset.value)}),navigator.clipboard.writeText(JSON.stringify(s,null,2)).then(()=>{UI.showToast("Row copied as JSON.","success")})})}),document.querySelectorAll(".js-row-copy-sql").forEach(e=>{e.addEventListener("click",t=>{t.stopPropagation();const n=e.closest("tr");if(!n)return;const o=[],l=[];n.querySelectorAll("td.cell-editable").forEach(e=>{const t=e.dataset.col;if(t)if(o.push('"'+t.replace(/"/g,'""')+'"'),"true"===e.dataset.null)l.push("NULL");else{const t=e.dataset.value;l.push("'"+String(t).replace(/'/g,"''")+"'")}});const a='INSERT INTO "'+s.table.replace(/"/g,'""')+'" ('+o.join(", ")+") VALUES ("+l.join(", ")+");";navigator.clipboard.writeText(a).then(()=>{UI.showToast("SQL INSERT copied to clipboard.","success")})})}),document.querySelectorAll(".js-row-duplicate").forEach(t=>{t.addEventListener("click",n=>{n.stopPropagation();const s=t.dataset.pk;s&&(window.location.href=e+"/tables/"+o+"/"+l+"/rows/new?duplicate="+encodeURIComponent(s))})}),document.querySelectorAll(".dropdown").forEach(e=>{const t=e.querySelector('label[role="button"]')||e.querySelector("button");t&&t.addEventListener("click",()=>{const n=t.getBoundingClientRect(),s=window.innerHeight-n.bottom;e.classList.toggle("dropdown-top",s<220)})}),M(),function(){let e=null;document.addEventListener("click",e=>{const t=e.target.closest(".js-inspect-btn"),n=e.target.closest("td.cell-editable");if(t){e.stopPropagation(),e.preventDefault();const n=t.closest("td");return void(n&&z(n))}n&&e.altKey&&(e.stopPropagation(),e.preventDefault(),z(n))}),document.addEventListener("mouseover",n=>{const o=n.target.closest("td.cell-editable");o&&!o.classList.contains("js-editing")&&(clearTimeout(e),e=setTimeout(()=>{const e=o.dataset.value,n="true"===o.dataset.blob,l="true"===o.dataset.json,a="true"===o.dataset.image,i="true"===o.dataset.audio,r="true"===o.dataset.video,d="true"===o.dataset.pdf,u=e&&e.length>40;if(!(n||l||a||i||r||d||u))return;const p=o.dataset.col,f=o.closest("tr")?.dataset.pk,m=o.closest("tr")?.dataset.table||s.table,h=o.querySelector("img")&&o.querySelector("img").src||"",g=o.dataset.type||(c(p)?.type??"");window.Inspector&&window.Inspector.peek&&window.Inspector.peek(o,{table:m,pk:f,col:p,colType:g,value:e,isNull:"true"===o.dataset.null,isBlob:n,isJson:l,isImage:a,isAudio:i,isVideo:r,isPdf:d,blobUrl:h,readonly:t,onSave:e=>{if(!t){const t=o.closest("tr");t&&I(t,o,{value:e,type:"text"})}}})},500))}),document.addEventListener("mouseout",t=>{t.target.closest("td.cell-editable")&&clearTimeout(e)})}(),function(){const e=document.getElementById("bulk-bar");if(!e)return;const n=document.getElementById("select-all-rows"),s=document.getElementById("bulk-count"),o=document.getElementById("bulk-delete"),l=document.getElementById("bulk-export-csv"),a=document.getElementById("bulk-export-json"),i=document.getElementById("bulk-clear"),r=Array.prototype.slice.call(document.querySelectorAll(".row-checkbox"));function c(){const l=q().length;s&&(s.textContent=String(l)),e.classList.toggle("hidden",0===l),n&&(n.checked=l>0&&l===r.length),o&&(o.disabled=t||0===l)}r.forEach(e=>e.addEventListener("change",c)),n&&n.addEventListener("change",()=>{r.forEach(e=>{e.checked=n.checked}),c()}),i&&i.addEventListener("click",()=>{r.forEach(e=>{e.checked=!1}),c()}),l&&l.addEventListener("click",()=>O("csv")),a&&a.addEventListener("click",()=>O("json")),o&&o.addEventListener("click",D)}(),document.querySelectorAll(".js-delete-row").forEach(e=>{e.addEventListener("click",async()=>{const t=e.dataset.table,n=e.dataset.pk;t&&n&&await UI.confirm({title:"Delete Row",message:"Are you sure you want to permanently delete this row? This action cannot be undone.",item:n,itemType:"Primary Key",confirmText:"Delete row",danger:!0})&&Api.del("/api/tables/"+o+"/"+encodeURIComponent(t)+"/row/"+encodeURIComponent(n)).then(()=>{UI.showToast("Row deleted.","success"),window.location.reload()}).catch(e=>UI.showError(e.message))})}),function(){const t=document.getElementById("page-size");t&&t.addEventListener("change",()=>{const n=t.dataset.table,s=t.value,o=t.dataset.orderby||"",l=t.dataset.orderdir||"asc",a=t.dataset.filters||"";let i=e+"/tables/"+encodeURIComponent(n)+"?page=1&size="+encodeURIComponent(s)+"&orderBy="+encodeURIComponent(o)+"&orderDir="+encodeURIComponent(l);a&&(i+="&"+a),window.location.href=i})}(),function(){const e=document.getElementById("import-csv"),t=document.getElementById("import-panel"),n=document.getElementById("import-csv-close"),s=document.getElementById("import-csv-text"),l=document.getElementById("import-csv-file"),a=document.getElementById("import-csv-submit");e&&t&&e.addEventListener("click",()=>{t.classList.toggle("hidden"),t.classList.contains("hidden")||s.focus()}),n&&n.addEventListener("click",()=>t.classList.add("hidden")),l&&l.addEventListener("change",()=>{const e=l.files&&l.files[0];if(!e)return;const t=new FileReader;t.onload=()=>{s.value=String(t.result||"")},t.readAsText(e)}),a&&s&&a.addEventListener("click",async()=>{const e=s.value.trim();if(!e)return UI.showError("Paste or choose a CSV file first.");const t=window.location.pathname.match(/\/tables\/([^/]+)\/([^/]+)/),n=t?decodeURIComponent(t[2]):"";if(!n)return UI.showError("Could not determine the table.");a.disabled=!0;try{const t=await Api.post("/api/tables/"+o+"/"+encodeURIComponent(n)+"/rows/import",{csv:e});UI.showToast(t.message||"Import complete.","success"),window.location.reload()}catch(e){UI.showError(e.message)}finally{a.disabled=!1}})}(),document.querySelectorAll(".js-ref-toggle").forEach(e=>{e.addEventListener("click",t=>{if(t.stopPropagation(),"true"===e.getAttribute("aria-expanded"))return void _();const n=e.dataset.table,s=e.dataset.pk;n&&s&&(e.classList.add("opacity-50"),Api.get("/api/tables/"+o+"/"+encodeURIComponent(n)+"/rows/"+encodeURIComponent(s)+"/references").then(t=>{e.classList.remove("opacity-50");const n=e.dataset.refTable,s=(t&&t.references||[]).filter(e=>!n||e.table===n);e.setAttribute("aria-expanded","true"),W(e,!0),V(e,s)}).catch(t=>{e.classList.remove("opacity-50"),UI.showError(t.message)}))})})}()}();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(){"use strict";const e=document.getElementById("new-db-name"),t=document.getElementById("create-db"),n=document.getElementById("pg-conn-name"),a=document.getElementById("pg-conn-uri"),s=document.getElementById("pg-conn-readonly"),o=document.getElementById("connect-pg"),c=document.getElementById("fs-readonly"),d=window.Utils?window.Utils.escapeHtml:function(e){return String(null==e?"":e)},r=window.Utils?window.Utils.formatBytes:function(e){return(e||0)+" B"};async function i(){const t=(e.value||"").trim();if(!t)return UI.showError("Enter a database name.");try{const e=await Api.post("/api/databases",{name:t});UI.showToast(e.message||"Database created.","success"),setTimeout(()=>{window.location.href=Api.basePath+"/"+encodeURIComponent(e.id)},400)}catch(e){UI.showError(e.message)}}async function l(){const e=(n?n.value:"").trim(),t=(a?a.value:"").trim(),c=!!s&&s.checked;if(!t)return UI.showError("Enter a PostgreSQL connection URI (e.g. postgresql://user:password@localhost:5432/dbname).");o&&(o.disabled=!0,o.textContent="Connecting…");try{const n=await Api.post("/api/databases/connect-postgres",{name:e,connectionString:t,readonly:c});UI.showToast(n.message||"PostgreSQL connected.","success"),setTimeout(()=>{window.location.href=Api.basePath+"/"+encodeURIComponent(n.id)},400)}catch(e){UI.showError(e.message),o&&(o.disabled=!1,o.innerHTML=(window.AdminIcons?AdminIcons.get("plus","h-4 w-4"):"")+" Connect PostgreSQL")}}t&&t.addEventListener("click",i),e&&e.addEventListener("keydown",e=>{"Enter"===e.key&&i()}),o&&o.addEventListener("click",l),a&&a.addEventListener("keydown",e=>{"Enter"===e.key&&l()}),document.querySelectorAll(".js-toggle-db-mode").forEach(e=>{e.addEventListener("click",async()=>{const t=e.dataset.id;if(!t)return;const n=!("true"===e.dataset.readonly);try{const e=await Api.post("/api/databases/"+encodeURIComponent(t)+"/mode",{readonly:n});UI.showToast(e.message||`Mode updated to ${n?"Read-only":"Writable"}.`,"success"),window.location.reload()}catch(e){UI.showError(e.message)}})}),document.querySelectorAll(".js-delete-db").forEach(e=>{e.addEventListener("click",async()=>{const t=e.dataset.id;if(!t)return;await UI.confirm({title:"Delete Database",message:'Are you sure you want to permanently delete database "'+t+'"? This will remove the SQLite database file and all stored tables and records from the disk.',item:t,itemType:"Database",requireInputMatch:t,confirmText:"Delete database",danger:!0})&&Api.del("/api/databases/"+encodeURIComponent(t)).then(e=>{UI.showToast(e.message||"Database deleted.","success"),window.location.reload()}).catch(e=>UI.showError(e.message))})}),document.querySelectorAll(".js-disconnect-db").forEach(e=>{e.addEventListener("click",async()=>{const t=e.dataset.id,n=e.dataset.name||t;if(!t)return;await UI.confirm({title:"Disconnect PostgreSQL Database",message:'Are you sure you want to disconnect database "'+n+'" from AdminDB? This closes the connection pool in AdminDB; your remote database and data are untouched.',confirmText:"Disconnect database",danger:!1})&&Api.del("/api/databases/"+encodeURIComponent(t)).then(e=>{UI.showToast(e.message||"Database disconnected.","success"),window.location.reload()}).catch(e=>UI.showError(e.message))})});const m=document.getElementById("fs-path"),p=document.getElementById("fs-go"),u=document.getElementById("fs-up"),h=document.getElementById("fs-current"),w=document.getElementById("fs-listing");let g={path:"",parent:null};function y(e){if(g={path:e.path,parent:e.parent},h&&(h.textContent=e.path),m&&(m.value=e.path),u&&(u.disabled=!e.parent),!w)return;if(w.innerHTML="",!e.entries.length)return void(w.innerHTML='<div class="px-4 py-6 text-center text-sm text-base-content/40">No folders or database files here.</div>');const t=window.AdminIcons?AdminIcons.get("folder","h-3.5 w-3.5"):"📁",n=window.AdminIcons?AdminIcons.get("database","h-3.5 w-3.5"):"🗄️";e.entries.forEach(e=>{const a=document.createElement("button");a.type="button",a.className="flex w-full items-center gap-2.5 px-3 py-2 text-left text-sm transition hover:bg-primary/[0.06]",a.title=e.path,e.isDir?(a.innerHTML='<span class="grid h-6 w-6 shrink-0 place-items-center rounded-md bg-base-200 text-base-content/50">'+t+'</span><span class="min-w-0 flex-1 truncate font-medium text-base-content/80">'+d(e.name)+'</span><span class="text-xs text-base-content/35">folder</span>',a.addEventListener("click",()=>f(e.path))):(a.innerHTML='<span class="grid h-6 w-6 shrink-0 place-items-center rounded-md bg-primary/10 text-primary">'+n+'</span><span class="min-w-0 flex-1 truncate font-mono text-[13px] text-base-content/85">'+d(e.name)+'</span><span class="text-xs text-base-content/40">'+r(e.size)+"</span>",a.addEventListener("click",()=>async function(e){const t=!!c&&c.checked;try{const n=await Api.post("/api/databases/open",{path:e,readonly:t});UI.showToast(n.message||"Database opened.","success"),window.location.href=Api.basePath+"/"+encodeURIComponent(n.id)}catch(e){UI.showError(e.message)}}(e.path))),w.appendChild(a)})}async function f(e){try{y(await Api.get("/api/fs/list"+(e?"?path="+encodeURIComponent(e):"")))}catch(e){UI.showError(e.message)}}w&&(f(""),p&&p.addEventListener("click",()=>f((m.value||"").trim()||"")),m&&m.addEventListener("keydown",e=>{"Enter"===e.key&&f((m.value||"").trim()||"")}),u&&u.addEventListener("click",function(){g.parent&&f(g.parent)}))}();
|
|
1
|
+
!function(){"use strict";const e=document.getElementById("new-db-name"),t=document.getElementById("create-db"),n=document.getElementById("pg-conn-name"),a=document.getElementById("pg-conn-uri"),s=document.getElementById("pg-conn-readonly"),o=document.getElementById("connect-pg"),c=document.getElementById("fs-readonly"),d=window.Utils?window.Utils.escapeHtml:function(e){return String(null==e?"":e)},r=window.Utils?window.Utils.formatBytes:function(e){return(e||0)+" B"};async function i(){const t=(e.value||"").trim();if(!t)return UI.showError("Enter a database name.");try{const e=await Api.post("/api/databases",{name:t});UI.showToast(e.message||"Database created.","success"),setTimeout(()=>{window.location.href=Api.basePath+"/home/"+encodeURIComponent(e.id)},400)}catch(e){UI.showError(e.message)}}async function l(){const e=(n?n.value:"").trim(),t=(a?a.value:"").trim(),c=!!s&&s.checked;if(!t)return UI.showError("Enter a PostgreSQL connection URI (e.g. postgresql://user:password@localhost:5432/dbname).");o&&(o.disabled=!0,o.textContent="Connecting…");try{const n=await Api.post("/api/databases/connect-postgres",{name:e,connectionString:t,readonly:c});UI.showToast(n.message||"PostgreSQL connected.","success"),setTimeout(()=>{window.location.href=Api.basePath+"/home/"+encodeURIComponent(n.id)},400)}catch(e){UI.showError(e.message),o&&(o.disabled=!1,o.innerHTML=(window.AdminIcons?AdminIcons.get("plus","h-4 w-4"):"")+" Connect PostgreSQL")}}t&&t.addEventListener("click",i),e&&e.addEventListener("keydown",e=>{"Enter"===e.key&&i()}),o&&o.addEventListener("click",l),a&&a.addEventListener("keydown",e=>{"Enter"===e.key&&l()}),document.querySelectorAll(".js-toggle-db-mode").forEach(e=>{e.addEventListener("click",async()=>{const t=e.dataset.id;if(!t)return;const n=!("true"===e.dataset.readonly);try{const e=await Api.post("/api/databases/"+encodeURIComponent(t)+"/mode",{readonly:n});UI.showToast(e.message||`Mode updated to ${n?"Read-only":"Writable"}.`,"success"),window.location.reload()}catch(e){UI.showError(e.message)}})}),document.querySelectorAll(".js-delete-db").forEach(e=>{e.addEventListener("click",async()=>{const t=e.dataset.id;if(!t)return;await UI.confirm({title:"Delete Database",message:'Are you sure you want to permanently delete database "'+t+'"? This will remove the SQLite database file and all stored tables and records from the disk.',item:t,itemType:"Database",requireInputMatch:t,confirmText:"Delete database",danger:!0})&&Api.del("/api/databases/"+encodeURIComponent(t)).then(e=>{UI.showToast(e.message||"Database deleted.","success"),window.location.reload()}).catch(e=>UI.showError(e.message))})}),document.querySelectorAll(".js-disconnect-db").forEach(e=>{e.addEventListener("click",async()=>{const t=e.dataset.id,n=e.dataset.name||t;if(!t)return;await UI.confirm({title:"Disconnect PostgreSQL Database",message:'Are you sure you want to disconnect database "'+n+'" from AdminDB? This closes the connection pool in AdminDB; your remote database and data are untouched.',confirmText:"Disconnect database",danger:!1})&&Api.del("/api/databases/"+encodeURIComponent(t)).then(e=>{UI.showToast(e.message||"Database disconnected.","success"),window.location.reload()}).catch(e=>UI.showError(e.message))})});const m=document.getElementById("fs-path"),p=document.getElementById("fs-go"),u=document.getElementById("fs-up"),h=document.getElementById("fs-current"),w=document.getElementById("fs-listing");let g={path:"",parent:null};function y(e){if(g={path:e.path,parent:e.parent},h&&(h.textContent=e.path),m&&(m.value=e.path),u&&(u.disabled=!e.parent),!w)return;if(w.innerHTML="",!e.entries.length)return void(w.innerHTML='<div class="px-4 py-6 text-center text-sm text-base-content/40">No folders or database files here.</div>');const t=window.AdminIcons?AdminIcons.get("folder","h-3.5 w-3.5"):"📁",n=window.AdminIcons?AdminIcons.get("database","h-3.5 w-3.5"):"🗄️";e.entries.forEach(e=>{const a=document.createElement("button");a.type="button",a.className="flex w-full items-center gap-2.5 px-3 py-2 text-left text-sm transition hover:bg-primary/[0.06]",a.title=e.path,e.isDir?(a.innerHTML='<span class="grid h-6 w-6 shrink-0 place-items-center rounded-md bg-base-200 text-base-content/50">'+t+'</span><span class="min-w-0 flex-1 truncate font-medium text-base-content/80">'+d(e.name)+'</span><span class="text-xs text-base-content/35">folder</span>',a.addEventListener("click",()=>f(e.path))):(a.innerHTML='<span class="grid h-6 w-6 shrink-0 place-items-center rounded-md bg-primary/10 text-primary">'+n+'</span><span class="min-w-0 flex-1 truncate font-mono text-[13px] text-base-content/85">'+d(e.name)+'</span><span class="text-xs text-base-content/40">'+r(e.size)+"</span>",a.addEventListener("click",()=>async function(e){const t=!!c&&c.checked;try{const n=await Api.post("/api/databases/open",{path:e,readonly:t});UI.showToast(n.message||"Database opened.","success"),window.location.href=Api.basePath+"/home/"+encodeURIComponent(n.id)}catch(e){UI.showError(e.message)}}(e.path))),w.appendChild(a)})}async function f(e){try{y(await Api.get("/api/fs/list"+(e?"?path="+encodeURIComponent(e):"")))}catch(e){UI.showError(e.message)}}w&&(f(""),p&&p.addEventListener("click",()=>f((m.value||"").trim()||"")),m&&m.addEventListener("keydown",e=>{"Enter"===e.key&&f((m.value||"").trim()||"")}),u&&u.addEventListener("click",function(){g.parent&&f(g.parent)}))}();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(){"use strict";window.Utils?window.Utils.isPostgres():window.APP&&window.APP.dialect;const e=window.Utils?window.Utils.getDesignerTypes():["TEXT","INTEGER","REAL","BLOB","BOOLEAN"],t=window.Utils?window.Utils.NAME_REGEX:/^[A-Za-z_][A-Za-z0-9_$]*$/,l=document.getElementById("columns"),
|
|
1
|
+
!function(){"use strict";window.Utils?window.Utils.isPostgres():window.APP&&window.APP.dialect;const e=window.Utils?window.Utils.getDesignerTypes():["TEXT","INTEGER","REAL","BLOB","BOOLEAN"],t=window.Utils?window.Utils.NAME_REGEX:/^[A-Za-z_][A-Za-z0-9_$]*$/,n=encodeURIComponent(window.APP&&window.APP.dbId||""),l=document.getElementById("columns"),o=document.getElementById("designer-error"),c=document.getElementById("table-name"),s=document.getElementById("add-column"),a=document.getElementById("create-table"),i=document.getElementById("copy-sql"),r=document.getElementById("sql-preview"),d=document.getElementById("preview-code"),u=document.getElementById("preview-placeholder"),m=document.getElementById("preview-status"),p={};let f=[],b=null;const v=window.Utils?window.Utils.escapeHtml:function(e){return String(null==e?"":e)};function y(){const t=document.createElement("div");t.className="grid grid-cols-2 items-end gap-2.5 rounded-xl border border-base-300 bg-base-100 p-3 shadow-soft sm:grid-cols-12",t.innerHTML=['<div class="col-span-2 sm:col-span-2"><label class="field-label">Name</label>','<input class="field-input col-name" placeholder="column_name" autocomplete="off" spellcheck="false"></div>','<div class="col-span-1 sm:col-span-2"><label class="field-label">Type</label>','<select class="field-select col-type">'+e.map(e=>"<option>"+e+"</option>").join("")+"</select></div>",'<div class="col-span-1 sm:col-span-2"><label class="field-label">FK table</label>','<select class="field-select col-fk-table"><option value="">— none —</option>'+f.map(e=>"<option>"+v(e)+"</option>").join("")+"</select></div>",'<div class="col-span-1 sm:col-span-2"><label class="field-label">FK column</label>','<select class="field-select col-fk-col"><option value="">—</option></select></div>','<div class="col-span-1 sm:col-span-2"><label class="field-label">Default</label>','<input class="field-input col-default" placeholder="0, CURRENT_TIMESTAMP" autocomplete="off" spellcheck="false"></div>','<div class="col-span-2 flex items-end justify-between gap-1 pb-0.5 sm:col-span-2">','<label class="flex cursor-pointer flex-col items-center gap-1" title="Primary key"><input type="checkbox" class="checkbox checkbox-primary checkbox-xs col-pk"><span class="text-[10px] font-semibold uppercase text-base-content/50">PK</span></label>','<label class="flex cursor-pointer flex-col items-center gap-1" title="Not null"><input type="checkbox" class="checkbox checkbox-xs col-notnull"><span class="text-[10px] font-semibold uppercase text-base-content/50">NN</span></label>','<label class="flex cursor-pointer flex-col items-center gap-1" title="Unique"><input type="checkbox" class="checkbox checkbox-xs col-unique"><span class="text-[10px] font-semibold uppercase text-base-content/50">UQ</span></label>','<button type="button" class="btn btn-ghost btn-xs text-error remove-col" title="Remove column">×</button>',"</div>"].join(""),l.appendChild(t);const o=t.querySelector(".col-fk-table"),c=t.querySelector(".col-fk-col");o.addEventListener("change",()=>{c.innerHTML='<option value="">—</option>';const e=o.value;e&&async function(e){if(p[e])return p[e];const t=await Api.get("/api/tables/"+n+"/"+encodeURIComponent(e)+"/info");return p[e]=t.columns.map(e=>e.name),p[e]}(e).then(e=>{c.innerHTML='<option value="">—</option>'+e.map(e=>"<option>"+v(e)+"</option>").join("")}).catch(()=>{})}),t.querySelector(".remove-col").addEventListener("click",async()=>{const e=t.querySelector(".col-name").value.trim();if(e){if(!await UI.confirm({title:"Remove Column",message:'Remove column "'+e+'" from the table definition?',confirmText:"Remove",danger:!0}))return}t.remove(),x()}),t.querySelectorAll("input, select").forEach(e=>{e.addEventListener("input",x),e.addEventListener("change",x)})}function h(){const e=[];return document.querySelectorAll("#columns > div").forEach(t=>{const n=t.querySelector(".col-name").value.trim();if(!n)return;const l=t.querySelector(".col-fk-table").value,o=t.querySelector(".col-fk-col").value;e.push({name:n,type:t.querySelector(".col-type").value,primaryKey:t.querySelector(".col-pk").checked,notNull:t.querySelector(".col-notnull").checked,unique:t.querySelector(".col-unique").checked,defaultValue:t.querySelector(".col-default").value.trim()||null,foreignKey:l&&o?{table:l,column:o}:null})}),e}function g(){const e=c.value.trim(),n=h();if(!e)return"Table name is required.";if(!t.test(e))return'Invalid table name: "'+e+'". Use letters, digits and underscores.';if(!n.length)return"Add at least one column.";let l=0;const o={};for(const e of n){if(!t.test(e.name))return'Invalid column name: "'+e.name+'".';if(o[e.name])return'Duplicate column name: "'+e.name+'".';if(o[e.name]=!0,e.primaryKey&&(l+=1),e.foreignKey&&(!e.foreignKey.table||!e.foreignKey.column))return'Column "'+e.name+'" has an incomplete foreign key (choose both table and column).'}return l>1?"At most one column can be a primary key.":null}function w(e){o.classList.remove("hidden"),o.innerHTML='<div class="alert alert-error shadow-lg"><span>'+v(e)+"</span></div>"}function x(){clearTimeout(b),b=setTimeout(E,250)}async function E(){if(g())return o.classList.add("hidden"),r.classList.add("hidden"),u.classList.remove("hidden"),void(m&&(m.textContent="fix validation errors…"));o.classList.add("hidden");try{const e=await Api.post("/api/tables/"+n+"/generate",{name:c.value.trim(),columns:h()});d.textContent=e.sql,r.classList.remove("hidden"),u.classList.add("hidden"),m&&(m.textContent="live preview · not executed",m.className="text-[11px] font-medium text-accent")}catch(e){r.classList.add("hidden"),u.classList.remove("hidden"),w(e.message)}}s&&s.addEventListener("click",y),a&&a.addEventListener("click",async function(){o.classList.add("hidden");const e=g();if(e)return void w(e);const t={name:c.value.trim(),columns:h()};try{const e=await Api.post("/api/tables/"+n,t);UI.showToast(e.message||"Table created.","success"),setTimeout(()=>{window.location.href=Api.basePath+"/tables/"+n+"/"+encodeURIComponent(t.name)},500)}catch(e){w(e.message)}}),i&&i.addEventListener("click",()=>{const e=d.textContent||"";e?navigator.clipboard.writeText(e).then(()=>UI.showToast("SQL copied to clipboard.","success")).catch(()=>UI.showError("Could not copy.")):UI.showError("Nothing to copy yet.")}),async function(){try{const e=await Api.get("/api/tables/"+n);f=e.map(e=>e.name).filter(e=>"_"!==e.charAt(0))}catch(e){f=[]}y(),y(),c.addEventListener("input",x)}()}();
|
package/dist/public/js/forms.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(){"use strict";const e=document.getElementById("form-config");if(!e)return;const t=JSON.parse(e.textContent),n=document.getElementById("row-fields"),a=document.getElementById("form-error"),s=document.getElementById("generated-sql"),o=document.getElementById("generated-sql-code"),l=document.getElementById("row-submit"),r=document.getElementById("row-generate"),i=document.getElementById("copy-sql"),c=encodeURIComponent(t.table);let d=null,m=null,u={};const p=window.Utils?window.Utils.escapeHtml:function(e){return String(null==e?"":e)},x=window.Utils&&window.Utils.toLocalInput?window.Utils.toLocalInput:function(e){return null==e?"":String(e)},f=window.Utils&&window.Utils.toLocalNow?window.Utils.toLocalNow:function(){return(new Date).toISOString()};function h(){return"undefined"!=typeof crypto&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{const t=16*Math.random()|0;return("x"===e?t:3&t|8).toString(16)})}function g(e){return"edit"===t.mode&&m?m[e.name]:"insert"===t.mode?function(e,t){if(null==t)return null;let n=String(t).trim();if(""===n||/^null$/i.test(n))return null;n=n.replace(/::[a-zA-Z0-9_\s"\[\]]+$/i,"").trim();const a=(e.type||"").toUpperCase(),s=n.match(/^'(.*)'$/s);if(s)return s[1].replace(/''/g,"'");if(/^[-+]?\d+(\.\d+)?$/.test(n))return Number(n);if(/^(true|false)$/i.test(n))return/^true$/i.test(n);if("UUID"===a&&/gen_random_uuid|uuid_generate/i.test(n))return h();if("BOOLEAN"===a||"BOOL"===a){if("1"===n||/^true$/i.test(n))return 1;if("0"===n||/^false$/i.test(n))return 0}return("DATE"===a||"DATETIME"===a||a.includes("TIMESTAMP"))&&/current_timestamp|now\s*\(|datetime\s*\(|date\s*\(|clock_timestamp|statement_timestamp|transaction_timestamp/i.test(n)?f("DATE"===a,!1):"TIME"!==a&&"TIMETZ"!==a||!/current_time|now\s*\(|time\s*\(/i.test(n)?null:f(!1,!0)}(e,e.dflt_value):null}function y(e,n,a,s){const o=document.createElement("div");o.className="space-y-2";const l=document.createElement("input");l.type="hidden",l.dataset.column=e;let r=[];if(Array.isArray(a))r=a.map(String);else if("string"==typeof a&&a.trim().startsWith("{")&&a.trim().endsWith("}")){const e=a.trim().slice(1,-1);r=e?e.split(",").map(e=>e.trim().replace(/^"(.*)"$/,"$1")):[]}else if("string"==typeof a&&a.trim().startsWith("[")&&a.trim().endsWith("]"))try{const e=JSON.parse(a);Array.isArray(e)&&(r=e.map(String))}catch(e){r=[]}else null!=a&&""!==String(a).trim()&&(r=[String(a)]);function i(){l.value=r.length?"{"+r.map(e=>e.includes(",")||e.includes('"')?'"'+e.replace(/"/g,'\\"')+'"':e).join(",")+"}":"",function(){if(c.innerHTML="",!r.length)return void(c.innerHTML='<span class="text-xs text-base-content/40 italic self-center px-1.5">No array items (empty / NULL)</span>');r.forEach((e,t)=>{const n=document.createElement("span");n.className="inline-flex items-center gap-1 rounded bg-primary/10 px-2 py-1 text-xs font-mono font-medium text-primary",n.innerHTML="<span>"+p(e)+'</span><button type="button" class="text-primary hover:text-error ml-1 font-bold">×</button>',n.querySelector("button").addEventListener("click",()=>{r.splice(t,1),i()}),c.appendChild(n)})}()}const c=document.createElement("div");c.className="flex flex-wrap gap-1.5 min-h-[32px] p-1.5 rounded-lg border border-base-300 bg-base-100";const d=document.createElement("div");d.className="flex gap-1.5";const m=document.createElement("input");m.type="text",m.className="field-input flex-1 font-mono text-xs",m.placeholder="Type item and press Enter or click Add…";const u=document.createElement("button");function x(){const e=m.value.trim();e&&(e.includes(",")?e.split(",").map(e=>e.trim()).filter(Boolean).forEach(e=>r.push(e)):r.push(e),m.value="",i())}return u.type="button",u.className="btn btn-ghost btn-xs text-primary font-medium",u.textContent="+ Add Item",u.addEventListener("click",x),m.addEventListener("keydown",e=>{"Enter"===e.key&&(e.preventDefault(),x())}),d.appendChild(m),d.appendChild(u),o.appendChild(l),o.appendChild(c),o.appendChild(d),i(),s&&"edit"===t.mode&&(m.disabled=!0),o}function E(e){const n=-1!==d.primaryKey.indexOf(e.name),a=document.createElement("div");a.className="space-y-1.5";const s=document.createElement("label");s.className="field-label",s.innerHTML="<span>"+p(e.name)+"</span>"+(e.notnull?'<span class="text-error">*</span>':"")+(n?'<span class="chip chip-pk">PK</span>':"")+(e.type?'<span class="chip chip-type">'+p(e.type)+"</span>":"")+("insert"===t.mode&&e.dflt_value?'<span class="chip chip-type">default</span>':""),a.appendChild(s);const o=d.foreignKeys.find(t=>t.from===e.name),l=(e.type||"").toUpperCase(),r="BOOLEAN"===l||"BOOL"===l,i=l.startsWith("INT")||"REAL"===l||"NUMERIC"===l||l.startsWith("DECIMAL")||"FLOAT"===l||"DOUBLE"===l||l.startsWith("BIGINT")||l.startsWith("SMALLINT")||l.startsWith("SERIAL")||"MONEY"===l,c="BLOB"===l||"BYTEA"===l||"BINARY"===l,m="DATE"===l,E="TIME"===l||"TIMETZ"===l||l.startsWith("TIME ")||l.startsWith("TIME("),b="DATETIME"===l||l.includes("TIMESTAMP"),v="UUID"===l,N=l.includes("JSON")||e.name.toLowerCase().includes("json"),w=l.endsWith("[]")||l.startsWith("_")||l.includes("ARRAY"),C="INET"===l||"CIDR"===l||"MACADDR"===l||"MACADDR8"===l,T="INTERVAL"===l,I="POINT"===l||"GEOMETRY"===l||"BOX"===l||"CIRCLE"===l||"POLYGON"===l,S="BIT"===l||"VARBIT"===l||l.startsWith("BIT(")||l.startsWith("VARBIT("),L=g(e);let D;if(r){const t=document.createElement("label");t.className="flex cursor-pointer items-center gap-2 pt-1",D=document.createElement("input"),D.type="checkbox",D.className="field-check",D.dataset.column=e.name,(1===(M=L)||!0===M||"1"===M||"true"===M)&&(D.checked=!0);const n=document.createElement("span");return n.className="text-sm text-base-content/70",n.textContent="True / Enabled",t.appendChild(D),t.appendChild(n),a.appendChild(t),a}if(o){D=document.createElement("select"),D.className="field-select",D.dataset.column=e.name;const t=u[e.name]||[],n=document.createElement("option");n.value="",n.textContent="(not set)",D.appendChild(n),t.forEach(e=>{const t=document.createElement("option");t.value=String(e.value),t.textContent=""===e.label?String(e.value):e.label,null!=L&&String(L)===String(e.value)&&(t.selected=!0),D.appendChild(t)})}else{if(m||E||b){const s=function(e,n,a,s){const o=(n||"").toUpperCase(),l="DATE"===o,r="TIME"===o||"TIMETZ"===o||o.startsWith("TIME ")||o.startsWith("TIME("),i="DATETIME"===o||o.includes("TIMESTAMP"),c=document.createElement("div");c.className="space-y-1";const d=document.createElement("div");d.className="flex items-center gap-1.5";const m=document.createElement("input");m.type=l?"date":r?"time":"datetime-local",(r||i)&&(m.step="1"),m.className="field-input flex-1 font-mono text-[13px]",m.dataset.column=e,null!=a&&(m.value=x(a,o));const u=document.createElement("div");function p(){if(m.value)try{if(l){const e=m.value.split("-"),t=new Date(Number(e[0]),Number(e[1])-1,Number(e[2]));u.textContent="📅 "+t.toLocaleDateString(void 0,{weekday:"short",year:"numeric",month:"short",day:"numeric"})}else if(r)u.textContent="⏰ "+m.value;else{const e=new Date(m.value);isNaN(e.getTime())?u.textContent=m.value:u.textContent="📅 "+e.toLocaleString(void 0,{weekday:"short",year:"numeric",month:"short",day:"numeric",hour:"2-digit",minute:"2-digit",second:"2-digit"})}}catch(e){u.textContent=m.value}else u.textContent="(empty / NULL)"}u.className="text-[11px] text-base-content/50 font-mono transition-all",m.addEventListener("input",p),m.addEventListener("change",p),p();const h=document.createElement("div");h.className="dropdown dropdown-end";const g=document.createElement("button");g.type="button",g.tabIndex=0,g.className="btn btn-ghost btn-sm btn-square text-primary hover:bg-primary/10 transition",g.title="Open calendar selector & presets",g.innerHTML='<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" /></svg>';const y=document.createElement("ul");function E(e,t,n){const a=document.createElement("li"),s=document.createElement("button");s.type="button",s.className="flex items-center gap-2 py-1.5 font-medium",s.innerHTML="<span>"+t+"</span><span>"+e+"</span>",s.addEventListener("click",()=>{n(),p(),document.activeElement&&"function"==typeof document.activeElement.blur&&document.activeElement.blur()}),a.appendChild(s),y.appendChild(a)}y.tabIndex=0,y.className="dropdown-content z-50 menu p-2 shadow-2xl bg-base-100 rounded-xl w-56 border border-base-300 text-xs space-y-0.5",E("Open Picker","📅",()=>{if("function"==typeof m.showPicker)try{m.showPicker()}catch(e){m.focus()}else m.focus()}),r?(E("Now","⚡",()=>{m.value=f(!1,!0)}),E("Start of Hour","⏱️",()=>{const e=new Date;var t;m.value=(t=e.getHours(),String(t).padStart(2,"0")+":00:00")})):l?(E("Today","⚡",()=>{m.value=f(!0,!1)}),E("Yesterday","⏪",()=>{const e=new Date;e.setDate(e.getDate()-1);const t=e=>String(e).padStart(2,"0");m.value=e.getFullYear()+"-"+t(e.getMonth()+1)+"-"+t(e.getDate())}),E("Tomorrow","⏩",()=>{const e=new Date;e.setDate(e.getDate()+1);const t=e=>String(e).padStart(2,"0");m.value=e.getFullYear()+"-"+t(e.getMonth()+1)+"-"+t(e.getDate())}),E("+7 Days (1 Week)","🗓️",()=>{const e=new Date;e.setDate(e.getDate()+7);const t=e=>String(e).padStart(2,"0");m.value=e.getFullYear()+"-"+t(e.getMonth()+1)+"-"+t(e.getDate())}),E("+30 Days (1 Month)","📆",()=>{const e=new Date;e.setDate(e.getDate()+30);const t=e=>String(e).padStart(2,"0");m.value=e.getFullYear()+"-"+t(e.getMonth()+1)+"-"+t(e.getDate())})):(E("Now","⚡",()=>{m.value=f(!1,!1)}),E("Today (Start of Day)","🌅",()=>{m.value=f(!0,!1)+"T00:00:00"}),E("Today (End of Day)","🌃",()=>{m.value=f(!0,!1)+"T23:59:59"}),E("Yesterday","⏪",()=>{const e=new Date;e.setDate(e.getDate()-1);const t=e=>String(e).padStart(2,"0");m.value=e.getFullYear()+"-"+t(e.getMonth()+1)+"-"+t(e.getDate())+"T"+t(e.getHours())+":"+t(e.getMinutes())+":"+t(e.getSeconds())}),E("Tomorrow","⏩",()=>{const e=new Date;e.setDate(e.getDate()+1);const t=e=>String(e).padStart(2,"0");m.value=e.getFullYear()+"-"+t(e.getMonth()+1)+"-"+t(e.getDate())+"T"+t(e.getHours())+":"+t(e.getMinutes())+":"+t(e.getSeconds())}),E("+7 Days (1 Week)","🗓️",()=>{const e=new Date;e.setDate(e.getDate()+7);const t=e=>String(e).padStart(2,"0");m.value=e.getFullYear()+"-"+t(e.getMonth()+1)+"-"+t(e.getDate())+"T"+t(e.getHours())+":"+t(e.getMinutes())+":"+t(e.getSeconds())}));const b=document.createElement("li");return b.className="border-t border-base-200 my-1",y.appendChild(b),E("Clear / Set NULL","🧹",()=>{m.value=""}),h.appendChild(g),h.appendChild(y),d.appendChild(m),d.appendChild(h),c.appendChild(d),c.appendChild(u),s&&"edit"===t.mode&&(m.disabled=!0),c}(e.name,e.type,L,n);return a.appendChild(s),a}if(w){const t=y(e.name,e.type,L,n);return a.appendChild(t),a}if(v){const s=function(e,n,a){const s=document.createElement("div");s.className="space-y-1";const o=document.createElement("div");o.className="flex items-center gap-1.5";const l=document.createElement("input");l.type="text",l.className="field-input font-mono text-xs flex-1",l.placeholder="00000000-0000-0000-0000-000000000000",l.dataset.column=e,null!=n&&(l.value=String(n));const r=document.createElement("span");function i(){const e=l.value.trim();e?/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(e)?(r.className="text-[10px] font-mono text-success font-semibold",r.textContent="✓ Valid UUID v4"):(r.className="text-[10px] font-mono text-warning",r.textContent="⚠ Custom format"):r.textContent=""}r.className="text-[10px] font-mono transition-all text-base-content/40",l.addEventListener("input",i),i();const c=document.createElement("button");return c.type="button",c.className="btn btn-ghost btn-xs gap-1 text-primary text-xs shrink-0",c.innerHTML="✨ Gen UUID",c.addEventListener("click",()=>{l.value=h(),i()}),o.appendChild(l),o.appendChild(c),s.appendChild(o),s.appendChild(r),a&&"edit"===t.mode&&(l.disabled=!0),s}(e.name,L,n);return a.appendChild(s),a}if(N||L&&"object"==typeof L){const s=function(e,n,a,s){const o=document.createElement("div");o.className="space-y-1.5";const l=document.createElement("textarea");l.rows=4,l.className="field-input font-mono text-[13px] w-full",l.placeholder='{\n "key": "value"\n}',l.dataset.column=e,null!=a&&(l.value="object"==typeof a?JSON.stringify(a,null,2):String(a));const r=document.createElement("div");r.className="flex items-center justify-between text-xs";const i=document.createElement("span");function c(){const e=l.value.trim();if(!e)return i.textContent="(empty / NULL)",void(i.className="font-mono text-[11px] text-base-content/40");try{JSON.parse(e),i.textContent="✓ Valid JSON",i.className="font-mono text-[11px] text-success font-semibold"}catch(e){i.textContent="✗ Invalid JSON syntax",i.className="font-mono text-[11px] text-error font-medium"}}i.className="font-mono text-[11px] text-base-content/40",l.addEventListener("input",c),c();const d=document.createElement("div");d.className="flex items-center gap-1";const m=document.createElement("button");m.type="button",m.className="btn btn-ghost btn-xs text-xs",m.textContent="Beautify",m.addEventListener("click",()=>{try{const e=JSON.parse(l.value);l.value=JSON.stringify(e,null,2),c()}catch(e){UI.showError("Invalid JSON: "+e.message)}});const u=document.createElement("button");u.type="button",u.className="btn btn-ghost btn-xs text-xs",u.textContent="Minify",u.addEventListener("click",()=>{try{const e=JSON.parse(l.value);l.value=JSON.stringify(e),c()}catch(e){UI.showError("Invalid JSON: "+e.message)}});const p=document.createElement("button");return p.type="button",p.className="btn btn-ghost btn-xs gap-1 text-primary text-xs font-semibold",p.innerHTML="🧩 Inspector",p.addEventListener("click",()=>{window.Inspector&&window.Inspector.open&&window.Inspector.open({table:t.table,pk:t.pk||"",col:e,colType:n,value:l.value,isNull:!l.value,isJson:!0,readonly:!1,onSave:e=>{l.value=e,c()}})}),d.appendChild(m),d.appendChild(u),d.appendChild(p),r.appendChild(i),r.appendChild(d),o.appendChild(l),o.appendChild(r),s&&"edit"===t.mode&&(l.disabled=!0),o}(e.name,e.type,L,n);return a.appendChild(s),a}if(T){const s=document.createElement("div");s.className="space-y-1.5",D=document.createElement("input"),D.type="text",D.className="field-input font-mono text-xs",D.placeholder="e.g. '1 day', '02:30:00', '1 month 2 days'",D.dataset.column=e.name,null!=L&&(D.value=String(L));const o=document.createElement("div");return o.className="flex flex-wrap gap-1",["1 day","1 week","1 month","1 year","1 hour","30 mins"].forEach(e=>{const t=document.createElement("button");t.type="button",t.className="btn btn-ghost btn-xs text-[11px] font-mono",t.textContent=e,t.addEventListener("click",()=>{D.value=e}),o.appendChild(t)}),s.appendChild(D),s.appendChild(o),n&&"edit"===t.mode&&(D.disabled=!0),a.appendChild(s),a}if(I)D=document.createElement("input"),D.type="text",D.className="field-input font-mono text-xs",D.placeholder="(x, y) e.g. '(12.345, 67.890)'",D.dataset.column=e.name,null!=L&&(D.value=String(L));else if(S)D=document.createElement("input"),D.type="text",D.className="field-input font-mono text-xs",D.placeholder="Binary e.g. '101010'",D.dataset.column=e.name,null!=L&&(D.value=String(L));else if(C)D=document.createElement("input"),D.type="text",D.className="field-input font-mono text-xs",D.placeholder="MACADDR"===l?"08:00:2b:01:02:03":"192.168.1.1 or 2001:db8::1",D.dataset.column=e.name,null!=L&&(D.value=String(L));else if(i){D=document.createElement("input"),D.type="number";const n=l.startsWith("INTEGER")||"INT"===l||l.startsWith("BIGINT")||l.startsWith("SMALLINT")||l.startsWith("SERIAL");D.step=n?"1":"any",D.className="field-input font-mono",D.dataset.column=e.name,!l.startsWith("SERIAL")||"insert"!==t.mode||null!=L&&""!==L||(D.placeholder="(auto-increment serial)"),null!=L&&(D.value=String(L))}else{if(c){const s=document.createElement("div");s.className="space-y-2",D=document.createElement("input"),D.type="text",D.className="field-input font-mono text-xs",D.placeholder="BYTEA"===l?"\\x… or 0x… hex":"0x… hex or plain text",D.dataset.column=e.name,null!=L&&(D.value=String(L));const o=document.createElement("div");o.className="flex flex-wrap items-center gap-2";const r=document.createElement("input");r.type="file",r.className="file-input file-input-bordered file-input-xs w-full max-w-xs";const i=document.createElement("img");return i.className="h-7 max-w-12 rounded object-cover border border-base-300 bg-base-200 hidden",r.addEventListener("change",e=>{const t=e.target.files&&e.target.files[0];if(!t)return;const n=new FileReader;n.onload=()=>{const e=n.result,a=new Uint8Array(e);let s="BYTEA"===l?"\\x":"0x";for(let e=0;e<a.length;e++)s+=("00"+a[e].toString(16)).slice(-2);if(D.value=s,t.type.startsWith("image/")){const e=new FileReader;e.onload=()=>{i.src=e.result,i.classList.remove("hidden")},e.readAsDataURL(t)}else i.classList.add("hidden")},n.readAsArrayBuffer(t)}),o.appendChild(r),o.appendChild(i),s.appendChild(D),s.appendChild(o),n&&"edit"===t.mode&&(D.disabled=!0),a.appendChild(s),a}D=document.createElement("input"),D.type="text",D.className="field-input",D.dataset.column=e.name,null!=L&&(D.value=String(L))}}var M;return n&&"edit"===t.mode&&(D.disabled=!0),a.appendChild(D),a}async function b(e){a.classList.add("hidden");const l=function(){const e={};return n.querySelectorAll("[data-column]").forEach(t=>{const n=t.dataset.column;"checkbox"===t.type?e[n]=t.checked?"true":"":e[n]=t.value}),e}(),r="edit"===t.mode;let i;i=r?"/api/tables/"+c+"/row/"+encodeURIComponent(t.pk)+(e?"":"/generate"):"/api/tables/"+c+"/rows"+(e?"":"/generate");const d=r?"PUT":"POST";try{const n=await Api.request(d,i,{values:l});e?(UI.showToast(n.message||"Saved.","success"),setTimeout(()=>{window.location.href=Api.basePath+"/tables/"+encodeURIComponent(t.table)},500)):(m=n.sql,o.textContent=m,s.classList.remove("hidden"),s.scrollIntoView({behavior:"smooth",block:"nearest"}))}catch(e){a.classList.remove("hidden"),a.innerHTML='<div class="flex items-start gap-2.5 rounded-xl border border-error/40 bg-error/10 px-4 py-3 text-sm font-medium text-error"><span>'+p(e.message)+"</span></div>"}var m}l&&l.addEventListener("click",()=>b(!0)),r&&r.addEventListener("click",()=>b(!1)),i&&i.addEventListener("click",()=>{navigator.clipboard.writeText(o.textContent).then(()=>UI.showToast("Copied to clipboard.","success")).catch(()=>UI.showError("Could not copy."))}),async function(){try{d=await Api.get("/api/tables/"+c+"/info"),"edit"===t.mode&&(m=await Api.get("/api/tables/"+c+"/row/"+encodeURIComponent(t.pk)));try{u=await Api.get("/api/tables/"+c+"/fk-options")}catch(e){u={}}n.innerHTML="",d.columns.forEach(e=>n.appendChild(E(e)))}catch(e){a.classList.remove("hidden"),a.innerHTML='<div class="flex items-start gap-2.5 rounded-xl border border-error/40 bg-error/10 px-4 py-3 text-sm font-medium text-error"><span>'+p(e.message)+"</span></div>"}}()}();
|
|
1
|
+
!function(){"use strict";const e=document.getElementById("form-config");if(!e)return;const t=JSON.parse(e.textContent),n=document.getElementById("row-fields"),a=document.getElementById("form-error"),s=document.getElementById("generated-sql"),o=document.getElementById("generated-sql-code"),l=document.getElementById("row-submit"),r=document.getElementById("row-generate"),i=document.getElementById("copy-sql"),c=encodeURIComponent(window.APP&&window.APP.dbId||t&&t.dbId||""),d=encodeURIComponent(t.table);let m=null,u=null,p={};const x=window.Utils?window.Utils.escapeHtml:function(e){return String(null==e?"":e)},f=window.Utils&&window.Utils.toLocalInput?window.Utils.toLocalInput:function(e){return null==e?"":String(e)},h=window.Utils&&window.Utils.toLocalNow?window.Utils.toLocalNow:function(){return(new Date).toISOString()};function g(){return"undefined"!=typeof crypto&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{const t=16*Math.random()|0;return("x"===e?t:3&t|8).toString(16)})}function y(e){return"edit"===t.mode&&u?u[e.name]:"insert"===t.mode?function(e,t){if(null==t)return null;let n=String(t).trim();if(""===n||/^null$/i.test(n))return null;n=n.replace(/::[a-zA-Z0-9_\s"\[\]]+$/i,"").trim();const a=(e.type||"").toUpperCase(),s=n.match(/^'(.*)'$/s);if(s)return s[1].replace(/''/g,"'");if(/^[-+]?\d+(\.\d+)?$/.test(n))return Number(n);if(/^(true|false)$/i.test(n))return/^true$/i.test(n);if("UUID"===a&&/gen_random_uuid|uuid_generate/i.test(n))return g();if("BOOLEAN"===a||"BOOL"===a){if("1"===n||/^true$/i.test(n))return 1;if("0"===n||/^false$/i.test(n))return 0}return("DATE"===a||"DATETIME"===a||a.includes("TIMESTAMP"))&&/current_timestamp|now\s*\(|datetime\s*\(|date\s*\(|clock_timestamp|statement_timestamp|transaction_timestamp/i.test(n)?h("DATE"===a,!1):"TIME"!==a&&"TIMETZ"!==a||!/current_time|now\s*\(|time\s*\(/i.test(n)?null:h(!1,!0)}(e,e.dflt_value):null}function E(e,n,a,s){const o=document.createElement("div");o.className="space-y-2";const l=document.createElement("input");l.type="hidden",l.dataset.column=e;let r=[];if(Array.isArray(a))r=a.map(String);else if("string"==typeof a&&a.trim().startsWith("{")&&a.trim().endsWith("}")){const e=a.trim().slice(1,-1);r=e?e.split(",").map(e=>e.trim().replace(/^"(.*)"$/,"$1")):[]}else if("string"==typeof a&&a.trim().startsWith("[")&&a.trim().endsWith("]"))try{const e=JSON.parse(a);Array.isArray(e)&&(r=e.map(String))}catch(e){r=[]}else null!=a&&""!==String(a).trim()&&(r=[String(a)]);function i(){l.value=r.length?"{"+r.map(e=>e.includes(",")||e.includes('"')?'"'+e.replace(/"/g,'\\"')+'"':e).join(",")+"}":"",function(){if(c.innerHTML="",!r.length)return void(c.innerHTML='<span class="text-xs text-base-content/40 italic self-center px-1.5">No array items (empty / NULL)</span>');r.forEach((e,t)=>{const n=document.createElement("span");n.className="inline-flex items-center gap-1 rounded bg-primary/10 px-2 py-1 text-xs font-mono font-medium text-primary",n.innerHTML="<span>"+x(e)+'</span><button type="button" class="text-primary hover:text-error ml-1 font-bold">×</button>',n.querySelector("button").addEventListener("click",()=>{r.splice(t,1),i()}),c.appendChild(n)})}()}const c=document.createElement("div");c.className="flex flex-wrap gap-1.5 min-h-[32px] p-1.5 rounded-lg border border-base-300 bg-base-100";const d=document.createElement("div");d.className="flex gap-1.5";const m=document.createElement("input");m.type="text",m.className="field-input flex-1 font-mono text-xs",m.placeholder="Type item and press Enter or click Add…";const u=document.createElement("button");function p(){const e=m.value.trim();e&&(e.includes(",")?e.split(",").map(e=>e.trim()).filter(Boolean).forEach(e=>r.push(e)):r.push(e),m.value="",i())}return u.type="button",u.className="btn btn-ghost btn-xs text-primary font-medium",u.textContent="+ Add Item",u.addEventListener("click",p),m.addEventListener("keydown",e=>{"Enter"===e.key&&(e.preventDefault(),p())}),d.appendChild(m),d.appendChild(u),o.appendChild(l),o.appendChild(c),o.appendChild(d),i(),s&&"edit"===t.mode&&(m.disabled=!0),o}function b(e){const n=-1!==m.primaryKey.indexOf(e.name),a=document.createElement("div");a.className="space-y-1.5";const s=document.createElement("label");s.className="field-label",s.innerHTML="<span>"+x(e.name)+"</span>"+(e.notnull?'<span class="text-error">*</span>':"")+(n?'<span class="chip chip-pk">PK</span>':"")+(e.type?'<span class="chip chip-type">'+x(e.type)+"</span>":"")+("insert"===t.mode&&e.dflt_value?'<span class="chip chip-type">default</span>':""),a.appendChild(s);const o=m.foreignKeys.find(t=>t.from===e.name),l=(e.type||"").toUpperCase(),r="BOOLEAN"===l||"BOOL"===l,i=l.startsWith("INT")||"REAL"===l||"NUMERIC"===l||l.startsWith("DECIMAL")||"FLOAT"===l||"DOUBLE"===l||l.startsWith("BIGINT")||l.startsWith("SMALLINT")||l.startsWith("SERIAL")||"MONEY"===l,c="BLOB"===l||"BYTEA"===l||"BINARY"===l,d="DATE"===l,u="TIME"===l||"TIMETZ"===l||l.startsWith("TIME ")||l.startsWith("TIME("),b="DATETIME"===l||l.includes("TIMESTAMP"),v="UUID"===l,N=l.includes("JSON")||e.name.toLowerCase().includes("json"),w=l.endsWith("[]")||l.startsWith("_")||l.includes("ARRAY"),C="INET"===l||"CIDR"===l||"MACADDR"===l||"MACADDR8"===l,I="INTERVAL"===l,T="POINT"===l||"GEOMETRY"===l||"BOX"===l||"CIRCLE"===l||"POLYGON"===l,S="BIT"===l||"VARBIT"===l||l.startsWith("BIT(")||l.startsWith("VARBIT("),L=y(e);let D;if(r){const t=document.createElement("label");t.className="flex cursor-pointer items-center gap-2 pt-1",D=document.createElement("input"),D.type="checkbox",D.className="field-check",D.dataset.column=e.name,(1===(A=L)||!0===A||"1"===A||"true"===A)&&(D.checked=!0);const n=document.createElement("span");return n.className="text-sm text-base-content/70",n.textContent="True / Enabled",t.appendChild(D),t.appendChild(n),a.appendChild(t),a}if(o){D=document.createElement("select"),D.className="field-select",D.dataset.column=e.name;const t=p[e.name]||[],n=document.createElement("option");n.value="",n.textContent="(not set)",D.appendChild(n),t.forEach(e=>{const t=document.createElement("option");t.value=String(e.value),t.textContent=""===e.label?String(e.value):e.label,null!=L&&String(L)===String(e.value)&&(t.selected=!0),D.appendChild(t)})}else{if(d||u||b){const s=function(e,n,a,s){const o=(n||"").toUpperCase(),l="DATE"===o,r="TIME"===o||"TIMETZ"===o||o.startsWith("TIME ")||o.startsWith("TIME("),i="DATETIME"===o||o.includes("TIMESTAMP"),c=document.createElement("div");c.className="space-y-1";const d=document.createElement("div");d.className="flex items-center gap-1.5";const m=document.createElement("input");m.type=l?"date":r?"time":"datetime-local",(r||i)&&(m.step="1"),m.className="field-input flex-1 font-mono text-[13px]",m.dataset.column=e,null!=a&&(m.value=f(a,o));const u=document.createElement("div");function p(){if(m.value)try{if(l){const e=m.value.split("-"),t=new Date(Number(e[0]),Number(e[1])-1,Number(e[2]));u.textContent="📅 "+t.toLocaleDateString(void 0,{weekday:"short",year:"numeric",month:"short",day:"numeric"})}else if(r)u.textContent="⏰ "+m.value;else{const e=new Date(m.value);isNaN(e.getTime())?u.textContent=m.value:u.textContent="📅 "+e.toLocaleString(void 0,{weekday:"short",year:"numeric",month:"short",day:"numeric",hour:"2-digit",minute:"2-digit",second:"2-digit"})}}catch(e){u.textContent=m.value}else u.textContent="(empty / NULL)"}u.className="text-[11px] text-base-content/50 font-mono transition-all",m.addEventListener("input",p),m.addEventListener("change",p),p();const x=document.createElement("div");x.className="dropdown dropdown-end";const g=document.createElement("button");g.type="button",g.tabIndex=0,g.className="btn btn-ghost btn-sm btn-square text-primary hover:bg-primary/10 transition",g.title="Open calendar selector & presets",g.innerHTML='<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" /></svg>';const y=document.createElement("ul");function E(e,t,n){const a=document.createElement("li"),s=document.createElement("button");s.type="button",s.className="flex items-center gap-2 py-1.5 font-medium",s.innerHTML="<span>"+t+"</span><span>"+e+"</span>",s.addEventListener("click",()=>{n(),p(),document.activeElement&&"function"==typeof document.activeElement.blur&&document.activeElement.blur()}),a.appendChild(s),y.appendChild(a)}y.tabIndex=0,y.className="dropdown-content z-50 menu p-2 shadow-2xl bg-base-100 rounded-xl w-56 border border-base-300 text-xs space-y-0.5",E("Open Picker","📅",()=>{if("function"==typeof m.showPicker)try{m.showPicker()}catch(e){m.focus()}else m.focus()}),r?(E("Now","⚡",()=>{m.value=h(!1,!0)}),E("Start of Hour","⏱️",()=>{const e=new Date;var t;m.value=(t=e.getHours(),String(t).padStart(2,"0")+":00:00")})):l?(E("Today","⚡",()=>{m.value=h(!0,!1)}),E("Yesterday","⏪",()=>{const e=new Date;e.setDate(e.getDate()-1);const t=e=>String(e).padStart(2,"0");m.value=e.getFullYear()+"-"+t(e.getMonth()+1)+"-"+t(e.getDate())}),E("Tomorrow","⏩",()=>{const e=new Date;e.setDate(e.getDate()+1);const t=e=>String(e).padStart(2,"0");m.value=e.getFullYear()+"-"+t(e.getMonth()+1)+"-"+t(e.getDate())}),E("+7 Days (1 Week)","🗓️",()=>{const e=new Date;e.setDate(e.getDate()+7);const t=e=>String(e).padStart(2,"0");m.value=e.getFullYear()+"-"+t(e.getMonth()+1)+"-"+t(e.getDate())}),E("+30 Days (1 Month)","📆",()=>{const e=new Date;e.setDate(e.getDate()+30);const t=e=>String(e).padStart(2,"0");m.value=e.getFullYear()+"-"+t(e.getMonth()+1)+"-"+t(e.getDate())})):(E("Now","⚡",()=>{m.value=h(!1,!1)}),E("Today (Start of Day)","🌅",()=>{m.value=h(!0,!1)+"T00:00:00"}),E("Today (End of Day)","🌃",()=>{m.value=h(!0,!1)+"T23:59:59"}),E("Yesterday","⏪",()=>{const e=new Date;e.setDate(e.getDate()-1);const t=e=>String(e).padStart(2,"0");m.value=e.getFullYear()+"-"+t(e.getMonth()+1)+"-"+t(e.getDate())+"T"+t(e.getHours())+":"+t(e.getMinutes())+":"+t(e.getSeconds())}),E("Tomorrow","⏩",()=>{const e=new Date;e.setDate(e.getDate()+1);const t=e=>String(e).padStart(2,"0");m.value=e.getFullYear()+"-"+t(e.getMonth()+1)+"-"+t(e.getDate())+"T"+t(e.getHours())+":"+t(e.getMinutes())+":"+t(e.getSeconds())}),E("+7 Days (1 Week)","🗓️",()=>{const e=new Date;e.setDate(e.getDate()+7);const t=e=>String(e).padStart(2,"0");m.value=e.getFullYear()+"-"+t(e.getMonth()+1)+"-"+t(e.getDate())+"T"+t(e.getHours())+":"+t(e.getMinutes())+":"+t(e.getSeconds())}));const b=document.createElement("li");return b.className="border-t border-base-200 my-1",y.appendChild(b),E("Clear / Set NULL","🧹",()=>{m.value=""}),x.appendChild(g),x.appendChild(y),d.appendChild(m),d.appendChild(x),c.appendChild(d),c.appendChild(u),s&&"edit"===t.mode&&(m.disabled=!0),c}(e.name,e.type,L,n);return a.appendChild(s),a}if(w){const t=E(e.name,e.type,L,n);return a.appendChild(t),a}if(v){const s=function(e,n,a){const s=document.createElement("div");s.className="space-y-1";const o=document.createElement("div");o.className="flex items-center gap-1.5";const l=document.createElement("input");l.type="text",l.className="field-input font-mono text-xs flex-1",l.placeholder="00000000-0000-0000-0000-000000000000",l.dataset.column=e,null!=n&&(l.value=String(n));const r=document.createElement("span");function i(){const e=l.value.trim();e?/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(e)?(r.className="text-[10px] font-mono text-success font-semibold",r.textContent="✓ Valid UUID v4"):(r.className="text-[10px] font-mono text-warning",r.textContent="⚠ Custom format"):r.textContent=""}r.className="text-[10px] font-mono transition-all text-base-content/40",l.addEventListener("input",i),i();const c=document.createElement("button");return c.type="button",c.className="btn btn-ghost btn-xs gap-1 text-primary text-xs shrink-0",c.innerHTML="✨ Gen UUID",c.addEventListener("click",()=>{l.value=g(),i()}),o.appendChild(l),o.appendChild(c),s.appendChild(o),s.appendChild(r),a&&"edit"===t.mode&&(l.disabled=!0),s}(e.name,L,n);return a.appendChild(s),a}if(N||L&&"object"==typeof L){const s=function(e,n,a,s){const o=document.createElement("div");o.className="space-y-1.5";const l=document.createElement("textarea");l.rows=4,l.className="field-input font-mono text-[13px] w-full",l.placeholder='{\n "key": "value"\n}',l.dataset.column=e,null!=a&&(l.value="object"==typeof a?JSON.stringify(a,null,2):String(a));const r=document.createElement("div");r.className="flex items-center justify-between text-xs";const i=document.createElement("span");function c(){const e=l.value.trim();if(!e)return i.textContent="(empty / NULL)",void(i.className="font-mono text-[11px] text-base-content/40");try{JSON.parse(e),i.textContent="✓ Valid JSON",i.className="font-mono text-[11px] text-success font-semibold"}catch(e){i.textContent="✗ Invalid JSON syntax",i.className="font-mono text-[11px] text-error font-medium"}}i.className="font-mono text-[11px] text-base-content/40",l.addEventListener("input",c),c();const d=document.createElement("div");d.className="flex items-center gap-1";const m=document.createElement("button");m.type="button",m.className="btn btn-ghost btn-xs text-xs",m.textContent="Beautify",m.addEventListener("click",()=>{try{const e=JSON.parse(l.value);l.value=JSON.stringify(e,null,2),c()}catch(e){UI.showError("Invalid JSON: "+e.message)}});const u=document.createElement("button");u.type="button",u.className="btn btn-ghost btn-xs text-xs",u.textContent="Minify",u.addEventListener("click",()=>{try{const e=JSON.parse(l.value);l.value=JSON.stringify(e),c()}catch(e){UI.showError("Invalid JSON: "+e.message)}});const p=document.createElement("button");return p.type="button",p.className="btn btn-ghost btn-xs gap-1 text-primary text-xs font-semibold",p.innerHTML="🧩 Inspector",p.addEventListener("click",()=>{window.Inspector&&window.Inspector.open&&window.Inspector.open({table:t.table,pk:t.pk||"",col:e,colType:n,value:l.value,isNull:!l.value,isJson:!0,readonly:!1,onSave:e=>{l.value=e,c()}})}),d.appendChild(m),d.appendChild(u),d.appendChild(p),r.appendChild(i),r.appendChild(d),o.appendChild(l),o.appendChild(r),s&&"edit"===t.mode&&(l.disabled=!0),o}(e.name,e.type,L,n);return a.appendChild(s),a}if(I){const s=document.createElement("div");s.className="space-y-1.5",D=document.createElement("input"),D.type="text",D.className="field-input font-mono text-xs",D.placeholder="e.g. '1 day', '02:30:00', '1 month 2 days'",D.dataset.column=e.name,null!=L&&(D.value=String(L));const o=document.createElement("div");return o.className="flex flex-wrap gap-1",["1 day","1 week","1 month","1 year","1 hour","30 mins"].forEach(e=>{const t=document.createElement("button");t.type="button",t.className="btn btn-ghost btn-xs text-[11px] font-mono",t.textContent=e,t.addEventListener("click",()=>{D.value=e}),o.appendChild(t)}),s.appendChild(D),s.appendChild(o),n&&"edit"===t.mode&&(D.disabled=!0),a.appendChild(s),a}if(T)D=document.createElement("input"),D.type="text",D.className="field-input font-mono text-xs",D.placeholder="(x, y) e.g. '(12.345, 67.890)'",D.dataset.column=e.name,null!=L&&(D.value=String(L));else if(S)D=document.createElement("input"),D.type="text",D.className="field-input font-mono text-xs",D.placeholder="Binary e.g. '101010'",D.dataset.column=e.name,null!=L&&(D.value=String(L));else if(C)D=document.createElement("input"),D.type="text",D.className="field-input font-mono text-xs",D.placeholder="MACADDR"===l?"08:00:2b:01:02:03":"192.168.1.1 or 2001:db8::1",D.dataset.column=e.name,null!=L&&(D.value=String(L));else if(i){D=document.createElement("input"),D.type="number";const n=l.startsWith("INTEGER")||"INT"===l||l.startsWith("BIGINT")||l.startsWith("SMALLINT")||l.startsWith("SERIAL");D.step=n?"1":"any",D.className="field-input font-mono",D.dataset.column=e.name,!l.startsWith("SERIAL")||"insert"!==t.mode||null!=L&&""!==L||(D.placeholder="(auto-increment serial)"),null!=L&&(D.value=String(L))}else{if(c){const s=document.createElement("div");s.className="space-y-2",D=document.createElement("input"),D.type="text",D.className="field-input font-mono text-xs",D.placeholder="BYTEA"===l?"\\x… or 0x… hex":"0x… hex or plain text",D.dataset.column=e.name,null!=L&&(D.value=String(L));const o=document.createElement("div");o.className="flex flex-wrap items-center gap-2";const r=document.createElement("input");r.type="file",r.className="file-input file-input-bordered file-input-xs w-full max-w-xs";const i=document.createElement("img");return i.className="h-7 max-w-12 rounded object-cover border border-base-300 bg-base-200 hidden",r.addEventListener("change",e=>{const t=e.target.files&&e.target.files[0];if(!t)return;const n=new FileReader;n.onload=()=>{const e=n.result,a=new Uint8Array(e);let s="BYTEA"===l?"\\x":"0x";for(let e=0;e<a.length;e++)s+=("00"+a[e].toString(16)).slice(-2);if(D.value=s,t.type.startsWith("image/")){const e=new FileReader;e.onload=()=>{i.src=e.result,i.classList.remove("hidden")},e.readAsDataURL(t)}else i.classList.add("hidden")},n.readAsArrayBuffer(t)}),o.appendChild(r),o.appendChild(i),s.appendChild(D),s.appendChild(o),n&&"edit"===t.mode&&(D.disabled=!0),a.appendChild(s),a}D=document.createElement("input"),D.type="text",D.className="field-input",D.dataset.column=e.name,null!=L&&(D.value=String(L))}}var A;return n&&"edit"===t.mode&&(D.disabled=!0),a.appendChild(D),a}async function v(e){a.classList.add("hidden");const l=function(){const e={};return n.querySelectorAll("[data-column]").forEach(t=>{const n=t.dataset.column;"checkbox"===t.type?e[n]=t.checked?"true":"":e[n]=t.value}),e}(),r="edit"===t.mode;let i;i=r?"/api/tables/"+c+"/"+d+"/row/"+encodeURIComponent(t.pk)+(e?"":"/generate"):"/api/tables/"+c+"/"+d+"/rows"+(e?"":"/generate");const m=r?"PUT":"POST";try{const n=await Api.request(m,i,{values:l});e?(UI.showToast(n.message||"Saved.","success"),setTimeout(()=>{window.location.href=Api.basePath+"/tables/"+c+"/"+encodeURIComponent(t.table)},500)):(u=n.sql,o.textContent=u,s.classList.remove("hidden"),s.scrollIntoView({behavior:"smooth",block:"nearest"}))}catch(e){a.classList.remove("hidden"),a.innerHTML='<div class="flex items-start gap-2.5 rounded-xl border border-error/40 bg-error/10 px-4 py-3 text-sm font-medium text-error"><span>'+x(e.message)+"</span></div>"}var u}l&&l.addEventListener("click",()=>v(!0)),r&&r.addEventListener("click",()=>v(!1)),i&&i.addEventListener("click",()=>{navigator.clipboard.writeText(o.textContent).then(()=>UI.showToast("Copied to clipboard.","success")).catch(()=>UI.showError("Could not copy."))}),async function(){try{m=await Api.get("/api/tables/"+c+"/"+d+"/info"),"edit"===t.mode&&(u=await Api.get("/api/tables/"+c+"/"+d+"/row/"+encodeURIComponent(t.pk)));try{p=await Api.get("/api/tables/"+c+"/"+d+"/fk-options")}catch(e){p={}}n.innerHTML="",m.columns.forEach(e=>n.appendChild(b(e)))}catch(e){a.classList.remove("hidden"),a.innerHTML='<div class="flex items-start gap-2.5 rounded-xl border border-error/40 bg-error/10 px-4 py-3 text-sm font-medium text-error"><span>'+x(e.message)+"</span></div>"}}()}();
|
package/dist/public/js/home.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(){"use strict";var e=
|
|
1
|
+
!function(){"use strict";var e=encodeURIComponent(window.APP&&window.APP.dbId||"");var t=document.getElementById("home-table-search"),n=document.getElementById("home-table-search-clear"),l=document.getElementById("home-table-count-text"),r=document.getElementById("home-no-tables-match"),o=document.getElementById("home-search-query-text"),a=Array.prototype.slice.call(document.querySelectorAll(".home-table-row")),i=document.getElementById("home-bulk-toolbar"),c=document.getElementById("home-bulk-count-num"),d=document.getElementById("home-select-all"),s=document.getElementById("home-bulk-deselect"),u=document.getElementById("home-clear-normal"),h=document.getElementById("home-clear-force"),m=document.getElementById("home-drop-normal"),f=document.getElementById("home-drop-force"),g=Array.prototype.slice.call(document.querySelectorAll(".home-table-checkbox"));function p(e){return e&&"none"!==e.style.display}function y(){return g.filter(function(e){return p(e.closest("tr"))})}function b(){return g.filter(function(e){return e.checked}).map(function(e){return e.dataset.table})}function w(){if(i){var e=b().length;if(c&&(c.textContent=String(e)),e>0?(i.style.overflow="hidden",i.style.maxHeight=i.scrollHeight+80+"px",i.style.opacity="1",i.setAttribute("aria-hidden","false"),clearTimeout(i._overflowTimer),i._overflowTimer=setTimeout(function(){b().length>0&&(i.style.overflow="visible",i.style.maxHeight="none")},250)):(clearTimeout(i._overflowTimer),i.style.overflow="hidden",i.style.maxHeight=i.scrollHeight+"px",i.offsetHeight,i.style.maxHeight="0",i.style.opacity="0",i.setAttribute("aria-hidden","true")),d){var t=y(),n=t.length,l=t.filter(function(e){return e.checked}).length;0===n?(d.checked=!1,d.indeterminate=!1,d.disabled=!0):(d.disabled=!1,0===l?(d.checked=!1,d.indeterminate=!1):l===n?(d.checked=!0,d.indeterminate=!1):(d.checked=!1,d.indeterminate=!0))}g.forEach(function(e){var t=e.closest("tr");t&&(e.checked?t.classList.add("bg-primary/5"):t.classList.remove("bg-primary/5"))})}}function v(){if(t){var e=t.value,i=e.trim().toLowerCase();n&&(e.length>0?n.classList.remove("hidden"):n.classList.add("hidden"));var c=0;a.forEach(function(e){var t=(e.getAttribute("data-table")||"").toLowerCase(),n=!i||-1!==t.indexOf(i);e.style.display=n?"":"none",n&&c++}),r&&(0===c&&a.length>0?(r.classList.remove("hidden"),o&&(o.textContent=e.trim())):r.classList.add("hidden")),l&&(l.textContent=i?c+" of "+a.length+" table"+(1!==a.length?"s":""):a.length+" total"),w()}}if(t&&(t.addEventListener("input",v),t.addEventListener("keydown",function(e){"Escape"===e.key&&(t.value="",v())})),n&&n.addEventListener("click",function(){t&&(t.value="",t.focus(),v())}),d){function E(e){return e.map(function(e){return'<span class="inline-flex items-center gap-1 font-mono text-[12px] bg-base-200 rounded px-1.5 py-0.5 mb-1 mr-1">'+(t=e,String(null==t?"":t).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")+"</span>");var t}).join("")}function k(e,t,n,l){window.UI&&UI.showToast&&(n.length>0&&UI.showToast(t+" "+n.length+" table"+(1!==n.length?"s":"")+".","success"),l.length>0&&UI.showToast(l.length+" table"+(1!==l.length?"s":"")+" failed: "+l.map(function(e){return e.table+" — "+e.error}).join("; "),"error"))}async function I(e,t,n){try{return await Api.post(e,{tables:t,force:n})}catch(e){return window.UI&&UI.showError&&UI.showError(e.message||"Request failed."),null}}async function L(t){var n=b();if(n.length&&await UI.confirm({title:t?"Force Clear Rows":"Clear Rows",message:t?"FK checks will be DISABLED. All rows in the selected "+n.length+" table(s) will be permanently deleted regardless of any foreign key references.":"All rows in the selected "+n.length+" table(s) will be permanently deleted. This will fail if other tables reference them via FK constraints.",confirmText:t?"Force Clear":"Clear Rows",danger:!0,detailsHtml:E(n),requireInputMatch:String(n.length),requireInputPlaceholder:"Type "+n.length+" to confirm"})){var l=await I("/api/tables/"+e+"/bulk-truncate",n,t);l&&(k(0,"Cleared",l.cleared||[],l.failed||[]),(l.cleared||[]).length>0&&window.location.reload())}}async function T(t){var n=b();if(n.length&&await UI.confirm({title:t?"Force Drop Tables":"Drop Tables",message:t?"FK checks will be DISABLED. The selected "+n.length+" table(s) and ALL their data will be PERMANENTLY deleted, ignoring any foreign key constraints.":"The selected "+n.length+" table(s) and ALL their data will be PERMANENTLY deleted. This cannot be undone.",confirmText:t?"Force Drop":"Drop Tables",danger:!0,detailsHtml:E(n),requireInputMatch:String(n.length),requireInputPlaceholder:"Type "+n.length+" to confirm"})){var l=await I("/api/tables/"+e+"/bulk-drop",n,t);l&&(k(0,"Dropped",l.dropped||[],l.failed||[]),(l.dropped||[]).length>0&&window.location.reload())}}g.forEach(function(e){e.addEventListener("change",w)}),d.addEventListener("change",function(){var e=d.checked,n=y();e&&t&&t.value.trim().length>0&&g.forEach(function(e){p(e.closest("tr"))||(e.checked=!1)}),n.forEach(function(t){t.checked=e}),w()}),s&&s.addEventListener("click",function(){g.forEach(function(e){e.checked=!1}),w()}),document.addEventListener("click",function(e){document.querySelectorAll("#home-dd-clear, #home-dd-drop").forEach(function(t){if(!t.contains(e.target)){var n=t.querySelector("ul");n&&n.removeAttribute("tabindex")}})}),u&&u.addEventListener("click",function(){L(!1)}),h&&h.addEventListener("click",function(){L(!0)}),m&&m.addEventListener("click",function(){T(!1)}),f&&f.addEventListener("click",function(){T(!0)}),w()}}();
|