feat: improve db status table browsing
This commit is contained in:
@@ -110,6 +110,14 @@
|
||||
.panel-body {
|
||||
padding: 16px 18px 20px;
|
||||
}
|
||||
.panel-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 14px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.panel-body.tight {
|
||||
padding-top: 0;
|
||||
}
|
||||
@@ -211,6 +219,44 @@
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.toolbar-input {
|
||||
width: min(360px, 100%);
|
||||
padding: 11px 14px;
|
||||
border-radius: 14px;
|
||||
border: 1px solid rgba(132, 102, 54, 0.16);
|
||||
background: rgba(255, 251, 244, 0.96);
|
||||
color: #2f2419;
|
||||
font: inherit;
|
||||
}
|
||||
.toolbar-input:focus {
|
||||
outline: none;
|
||||
border-color: rgba(129, 88, 31, 0.34);
|
||||
box-shadow: 0 0 0 4px rgba(208, 176, 116, 0.16);
|
||||
}
|
||||
.toolbar-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.action-button {
|
||||
border: 1px solid rgba(128, 98, 48, 0.14);
|
||||
background: rgba(250, 240, 213, 0.62);
|
||||
color: #6c4a1a;
|
||||
border-radius: 999px;
|
||||
padding: 9px 14px;
|
||||
font: inherit;
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
cursor: pointer;
|
||||
}
|
||||
.action-button:hover {
|
||||
background: rgba(244, 228, 186, 0.8);
|
||||
}
|
||||
.action-button:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.45;
|
||||
}
|
||||
.notes {
|
||||
margin: 0;
|
||||
padding-left: 18px;
|
||||
@@ -389,6 +435,12 @@
|
||||
<span id="generated-at" class="meta-chip">로딩 중</span>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="panel-toolbar">
|
||||
<input id="table-search" class="toolbar-input" type="search" placeholder="테이블명, 설명, 화면명으로 검색" />
|
||||
<div class="toolbar-actions">
|
||||
<span id="table-count" class="meta-chip">0 / 0</span>
|
||||
</div>
|
||||
</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -499,8 +551,11 @@
|
||||
<h2 id="preview-title">테이블 내용 미리보기</h2>
|
||||
<p id="preview-subtitle" class="muted">선택한 테이블의 컬럼과 최대 50개 row를 표시합니다.</p>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<button id="preview-download" class="action-button" type="button" disabled>CSV 다운로드</button>
|
||||
<button id="preview-close" class="modal-close" type="button" aria-label="닫기">×</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="preview-meta" class="preview-meta"></div>
|
||||
<div class="panel-body tight">
|
||||
@@ -518,6 +573,9 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let allTables = [];
|
||||
let currentPreview = null;
|
||||
|
||||
function escapeHtml(value) {
|
||||
return String(value ?? "")
|
||||
.replaceAll("&", "&")
|
||||
@@ -572,6 +630,10 @@
|
||||
|
||||
function renderTables(items) {
|
||||
const target = document.getElementById("table-body");
|
||||
const countTarget = document.getElementById("table-count");
|
||||
if (countTarget) {
|
||||
countTarget.textContent = `${formatNumber(items.length)} / ${formatNumber(allTables.length)}`;
|
||||
}
|
||||
if (!items.length) {
|
||||
target.innerHTML = '<tr><td colspan="5" class="empty">표시할 테이블이 없습니다.</td></tr>';
|
||||
return;
|
||||
@@ -699,7 +761,9 @@
|
||||
}
|
||||
|
||||
function renderTablePreview(payload) {
|
||||
currentPreview = payload;
|
||||
const previewModal = document.getElementById("preview-modal");
|
||||
const downloadButton = document.getElementById("preview-download");
|
||||
const previewMeta = document.getElementById("preview-meta");
|
||||
const previewTitle = document.getElementById("preview-title");
|
||||
const previewSubtitle = document.getElementById("preview-subtitle");
|
||||
@@ -708,6 +772,9 @@
|
||||
|
||||
previewTitle.textContent = `${payload.label} · ${payload.table_ref}`;
|
||||
previewSubtitle.textContent = `${formatNumber(payload.row_count)} rows / 최대 ${formatNumber(payload.limit)}개 표시`;
|
||||
if (downloadButton) {
|
||||
downloadButton.disabled = !(payload.rows && payload.rows.length);
|
||||
}
|
||||
previewMeta.innerHTML = `
|
||||
<div>
|
||||
<div class="table-title">${escapeHtml(payload.label)}</div>
|
||||
@@ -765,11 +832,12 @@
|
||||
throw new Error(`DB 상태를 불러오지 못했습니다. (${response.status})`);
|
||||
}
|
||||
const payload = await response.json();
|
||||
allTables = payload.tables || [];
|
||||
document.getElementById("generated-at").textContent = payload.generated_at
|
||||
? `갱신 ${formatDateTime(payload.generated_at)}`
|
||||
: "갱신 시각 없음";
|
||||
renderOverview(payload.overview || {});
|
||||
renderTables(payload.tables || []);
|
||||
renderTables(allTables);
|
||||
renderBatches(payload.import_batches || []);
|
||||
renderBinarySources(payload.binary_sources || []);
|
||||
renderNotes(payload.notes || []);
|
||||
@@ -778,12 +846,61 @@
|
||||
renderScreenMap(payload.screen_map || []);
|
||||
}
|
||||
|
||||
function toCsvValue(value) {
|
||||
const text = String(value ?? "");
|
||||
if (!/[",\n]/.test(text)) return text;
|
||||
return `"${text.replaceAll('"', '""')}"`;
|
||||
}
|
||||
|
||||
function downloadPreviewCsv() {
|
||||
if (!currentPreview || !currentPreview.columns || !currentPreview.rows || !currentPreview.rows.length) return;
|
||||
const headers = currentPreview.columns.map((column) => column.name);
|
||||
const lines = [
|
||||
headers.map(toCsvValue).join(","),
|
||||
...currentPreview.rows.map((row) => headers.map((header) => toCsvValue(row[header] ?? "")).join(",")),
|
||||
];
|
||||
const blob = new Blob(["\ufeff" + lines.join("\n")], { type: "text/csv;charset=utf-8;" });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement("a");
|
||||
link.href = url;
|
||||
link.download = `${currentPreview.table_name || "table-preview"}.csv`;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
link.remove();
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
function applyTableSearch(query) {
|
||||
const normalized = String(query || "").trim().toLowerCase();
|
||||
if (!normalized) {
|
||||
renderTables(allTables);
|
||||
return;
|
||||
}
|
||||
const filtered = allTables.filter((item) => {
|
||||
const haystack = [
|
||||
item.label,
|
||||
item.table_ref,
|
||||
item.description,
|
||||
...(item.related_views || []),
|
||||
item.domain,
|
||||
item.group,
|
||||
].join(" ").toLowerCase();
|
||||
return haystack.includes(normalized);
|
||||
});
|
||||
renderTables(filtered);
|
||||
}
|
||||
|
||||
document.getElementById("preview-close").addEventListener("click", () => {
|
||||
const modal = document.getElementById("preview-modal");
|
||||
modal.classList.remove("open");
|
||||
modal.setAttribute("aria-hidden", "true");
|
||||
});
|
||||
|
||||
document.getElementById("preview-download").addEventListener("click", downloadPreviewCsv);
|
||||
document.getElementById("table-search").addEventListener("input", (event) => {
|
||||
applyTableSearch(event.target.value);
|
||||
});
|
||||
|
||||
document.getElementById("preview-modal").addEventListener("click", (event) => {
|
||||
if (event.target.id !== "preview-modal") return;
|
||||
const modal = document.getElementById("preview-modal");
|
||||
|
||||
@@ -110,6 +110,14 @@
|
||||
.panel-body {
|
||||
padding: 16px 18px 20px;
|
||||
}
|
||||
.panel-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 14px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.panel-body.tight {
|
||||
padding-top: 0;
|
||||
}
|
||||
@@ -211,6 +219,44 @@
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.toolbar-input {
|
||||
width: min(360px, 100%);
|
||||
padding: 11px 14px;
|
||||
border-radius: 14px;
|
||||
border: 1px solid rgba(132, 102, 54, 0.16);
|
||||
background: rgba(255, 251, 244, 0.96);
|
||||
color: #2f2419;
|
||||
font: inherit;
|
||||
}
|
||||
.toolbar-input:focus {
|
||||
outline: none;
|
||||
border-color: rgba(129, 88, 31, 0.34);
|
||||
box-shadow: 0 0 0 4px rgba(208, 176, 116, 0.16);
|
||||
}
|
||||
.toolbar-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.action-button {
|
||||
border: 1px solid rgba(128, 98, 48, 0.14);
|
||||
background: rgba(250, 240, 213, 0.62);
|
||||
color: #6c4a1a;
|
||||
border-radius: 999px;
|
||||
padding: 9px 14px;
|
||||
font: inherit;
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
cursor: pointer;
|
||||
}
|
||||
.action-button:hover {
|
||||
background: rgba(244, 228, 186, 0.8);
|
||||
}
|
||||
.action-button:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.45;
|
||||
}
|
||||
.notes {
|
||||
margin: 0;
|
||||
padding-left: 18px;
|
||||
@@ -389,6 +435,12 @@
|
||||
<span id="generated-at" class="meta-chip">로딩 중</span>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="panel-toolbar">
|
||||
<input id="table-search" class="toolbar-input" type="search" placeholder="테이블명, 설명, 화면명으로 검색" />
|
||||
<div class="toolbar-actions">
|
||||
<span id="table-count" class="meta-chip">0 / 0</span>
|
||||
</div>
|
||||
</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -499,8 +551,11 @@
|
||||
<h2 id="preview-title">테이블 내용 미리보기</h2>
|
||||
<p id="preview-subtitle" class="muted">선택한 테이블의 컬럼과 최대 50개 row를 표시합니다.</p>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<button id="preview-download" class="action-button" type="button" disabled>CSV 다운로드</button>
|
||||
<button id="preview-close" class="modal-close" type="button" aria-label="닫기">×</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="preview-meta" class="preview-meta"></div>
|
||||
<div class="panel-body tight">
|
||||
@@ -518,6 +573,9 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let allTables = [];
|
||||
let currentPreview = null;
|
||||
|
||||
function escapeHtml(value) {
|
||||
return String(value ?? "")
|
||||
.replaceAll("&", "&")
|
||||
@@ -572,6 +630,10 @@
|
||||
|
||||
function renderTables(items) {
|
||||
const target = document.getElementById("table-body");
|
||||
const countTarget = document.getElementById("table-count");
|
||||
if (countTarget) {
|
||||
countTarget.textContent = `${formatNumber(items.length)} / ${formatNumber(allTables.length)}`;
|
||||
}
|
||||
if (!items.length) {
|
||||
target.innerHTML = '<tr><td colspan="5" class="empty">표시할 테이블이 없습니다.</td></tr>';
|
||||
return;
|
||||
@@ -699,7 +761,9 @@
|
||||
}
|
||||
|
||||
function renderTablePreview(payload) {
|
||||
currentPreview = payload;
|
||||
const previewModal = document.getElementById("preview-modal");
|
||||
const downloadButton = document.getElementById("preview-download");
|
||||
const previewMeta = document.getElementById("preview-meta");
|
||||
const previewTitle = document.getElementById("preview-title");
|
||||
const previewSubtitle = document.getElementById("preview-subtitle");
|
||||
@@ -708,6 +772,9 @@
|
||||
|
||||
previewTitle.textContent = `${payload.label} · ${payload.table_ref}`;
|
||||
previewSubtitle.textContent = `${formatNumber(payload.row_count)} rows / 최대 ${formatNumber(payload.limit)}개 표시`;
|
||||
if (downloadButton) {
|
||||
downloadButton.disabled = !(payload.rows && payload.rows.length);
|
||||
}
|
||||
previewMeta.innerHTML = `
|
||||
<div>
|
||||
<div class="table-title">${escapeHtml(payload.label)}</div>
|
||||
@@ -765,11 +832,12 @@
|
||||
throw new Error(`DB 상태를 불러오지 못했습니다. (${response.status})`);
|
||||
}
|
||||
const payload = await response.json();
|
||||
allTables = payload.tables || [];
|
||||
document.getElementById("generated-at").textContent = payload.generated_at
|
||||
? `갱신 ${formatDateTime(payload.generated_at)}`
|
||||
: "갱신 시각 없음";
|
||||
renderOverview(payload.overview || {});
|
||||
renderTables(payload.tables || []);
|
||||
renderTables(allTables);
|
||||
renderBatches(payload.import_batches || []);
|
||||
renderBinarySources(payload.binary_sources || []);
|
||||
renderNotes(payload.notes || []);
|
||||
@@ -778,12 +846,61 @@
|
||||
renderScreenMap(payload.screen_map || []);
|
||||
}
|
||||
|
||||
function toCsvValue(value) {
|
||||
const text = String(value ?? "");
|
||||
if (!/[",\n]/.test(text)) return text;
|
||||
return `"${text.replaceAll('"', '""')}"`;
|
||||
}
|
||||
|
||||
function downloadPreviewCsv() {
|
||||
if (!currentPreview || !currentPreview.columns || !currentPreview.rows || !currentPreview.rows.length) return;
|
||||
const headers = currentPreview.columns.map((column) => column.name);
|
||||
const lines = [
|
||||
headers.map(toCsvValue).join(","),
|
||||
...currentPreview.rows.map((row) => headers.map((header) => toCsvValue(row[header] ?? "")).join(",")),
|
||||
];
|
||||
const blob = new Blob(["\ufeff" + lines.join("\n")], { type: "text/csv;charset=utf-8;" });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement("a");
|
||||
link.href = url;
|
||||
link.download = `${currentPreview.table_name || "table-preview"}.csv`;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
link.remove();
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
function applyTableSearch(query) {
|
||||
const normalized = String(query || "").trim().toLowerCase();
|
||||
if (!normalized) {
|
||||
renderTables(allTables);
|
||||
return;
|
||||
}
|
||||
const filtered = allTables.filter((item) => {
|
||||
const haystack = [
|
||||
item.label,
|
||||
item.table_ref,
|
||||
item.description,
|
||||
...(item.related_views || []),
|
||||
item.domain,
|
||||
item.group,
|
||||
].join(" ").toLowerCase();
|
||||
return haystack.includes(normalized);
|
||||
});
|
||||
renderTables(filtered);
|
||||
}
|
||||
|
||||
document.getElementById("preview-close").addEventListener("click", () => {
|
||||
const modal = document.getElementById("preview-modal");
|
||||
modal.classList.remove("open");
|
||||
modal.setAttribute("aria-hidden", "true");
|
||||
});
|
||||
|
||||
document.getElementById("preview-download").addEventListener("click", downloadPreviewCsv);
|
||||
document.getElementById("table-search").addEventListener("input", (event) => {
|
||||
applyTableSearch(event.target.value);
|
||||
});
|
||||
|
||||
document.getElementById("preview-modal").addEventListener("click", (event) => {
|
||||
if (event.target.id !== "preview-modal") return;
|
||||
const modal = document.getElementById("preview-modal");
|
||||
|
||||
@@ -110,6 +110,14 @@
|
||||
.panel-body {
|
||||
padding: 16px 18px 20px;
|
||||
}
|
||||
.panel-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 14px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.panel-body.tight {
|
||||
padding-top: 0;
|
||||
}
|
||||
@@ -211,6 +219,44 @@
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.toolbar-input {
|
||||
width: min(360px, 100%);
|
||||
padding: 11px 14px;
|
||||
border-radius: 14px;
|
||||
border: 1px solid rgba(132, 102, 54, 0.16);
|
||||
background: rgba(255, 251, 244, 0.96);
|
||||
color: #2f2419;
|
||||
font: inherit;
|
||||
}
|
||||
.toolbar-input:focus {
|
||||
outline: none;
|
||||
border-color: rgba(129, 88, 31, 0.34);
|
||||
box-shadow: 0 0 0 4px rgba(208, 176, 116, 0.16);
|
||||
}
|
||||
.toolbar-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.action-button {
|
||||
border: 1px solid rgba(128, 98, 48, 0.14);
|
||||
background: rgba(250, 240, 213, 0.62);
|
||||
color: #6c4a1a;
|
||||
border-radius: 999px;
|
||||
padding: 9px 14px;
|
||||
font: inherit;
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
cursor: pointer;
|
||||
}
|
||||
.action-button:hover {
|
||||
background: rgba(244, 228, 186, 0.8);
|
||||
}
|
||||
.action-button:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.45;
|
||||
}
|
||||
.notes {
|
||||
margin: 0;
|
||||
padding-left: 18px;
|
||||
@@ -389,6 +435,12 @@
|
||||
<span id="generated-at" class="meta-chip">로딩 중</span>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="panel-toolbar">
|
||||
<input id="table-search" class="toolbar-input" type="search" placeholder="테이블명, 설명, 화면명으로 검색" />
|
||||
<div class="toolbar-actions">
|
||||
<span id="table-count" class="meta-chip">0 / 0</span>
|
||||
</div>
|
||||
</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -499,8 +551,11 @@
|
||||
<h2 id="preview-title">테이블 내용 미리보기</h2>
|
||||
<p id="preview-subtitle" class="muted">선택한 테이블의 컬럼과 최대 50개 row를 표시합니다.</p>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<button id="preview-download" class="action-button" type="button" disabled>CSV 다운로드</button>
|
||||
<button id="preview-close" class="modal-close" type="button" aria-label="닫기">×</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="preview-meta" class="preview-meta"></div>
|
||||
<div class="panel-body tight">
|
||||
@@ -518,6 +573,9 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let allTables = [];
|
||||
let currentPreview = null;
|
||||
|
||||
function escapeHtml(value) {
|
||||
return String(value ?? "")
|
||||
.replaceAll("&", "&")
|
||||
@@ -572,6 +630,10 @@
|
||||
|
||||
function renderTables(items) {
|
||||
const target = document.getElementById("table-body");
|
||||
const countTarget = document.getElementById("table-count");
|
||||
if (countTarget) {
|
||||
countTarget.textContent = `${formatNumber(items.length)} / ${formatNumber(allTables.length)}`;
|
||||
}
|
||||
if (!items.length) {
|
||||
target.innerHTML = '<tr><td colspan="5" class="empty">표시할 테이블이 없습니다.</td></tr>';
|
||||
return;
|
||||
@@ -699,7 +761,9 @@
|
||||
}
|
||||
|
||||
function renderTablePreview(payload) {
|
||||
currentPreview = payload;
|
||||
const previewModal = document.getElementById("preview-modal");
|
||||
const downloadButton = document.getElementById("preview-download");
|
||||
const previewMeta = document.getElementById("preview-meta");
|
||||
const previewTitle = document.getElementById("preview-title");
|
||||
const previewSubtitle = document.getElementById("preview-subtitle");
|
||||
@@ -708,6 +772,9 @@
|
||||
|
||||
previewTitle.textContent = `${payload.label} · ${payload.table_ref}`;
|
||||
previewSubtitle.textContent = `${formatNumber(payload.row_count)} rows / 최대 ${formatNumber(payload.limit)}개 표시`;
|
||||
if (downloadButton) {
|
||||
downloadButton.disabled = !(payload.rows && payload.rows.length);
|
||||
}
|
||||
previewMeta.innerHTML = `
|
||||
<div>
|
||||
<div class="table-title">${escapeHtml(payload.label)}</div>
|
||||
@@ -765,11 +832,12 @@
|
||||
throw new Error(`DB 상태를 불러오지 못했습니다. (${response.status})`);
|
||||
}
|
||||
const payload = await response.json();
|
||||
allTables = payload.tables || [];
|
||||
document.getElementById("generated-at").textContent = payload.generated_at
|
||||
? `갱신 ${formatDateTime(payload.generated_at)}`
|
||||
: "갱신 시각 없음";
|
||||
renderOverview(payload.overview || {});
|
||||
renderTables(payload.tables || []);
|
||||
renderTables(allTables);
|
||||
renderBatches(payload.import_batches || []);
|
||||
renderBinarySources(payload.binary_sources || []);
|
||||
renderNotes(payload.notes || []);
|
||||
@@ -778,12 +846,61 @@
|
||||
renderScreenMap(payload.screen_map || []);
|
||||
}
|
||||
|
||||
function toCsvValue(value) {
|
||||
const text = String(value ?? "");
|
||||
if (!/[",\n]/.test(text)) return text;
|
||||
return `"${text.replaceAll('"', '""')}"`;
|
||||
}
|
||||
|
||||
function downloadPreviewCsv() {
|
||||
if (!currentPreview || !currentPreview.columns || !currentPreview.rows || !currentPreview.rows.length) return;
|
||||
const headers = currentPreview.columns.map((column) => column.name);
|
||||
const lines = [
|
||||
headers.map(toCsvValue).join(","),
|
||||
...currentPreview.rows.map((row) => headers.map((header) => toCsvValue(row[header] ?? "")).join(",")),
|
||||
];
|
||||
const blob = new Blob(["\ufeff" + lines.join("\n")], { type: "text/csv;charset=utf-8;" });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement("a");
|
||||
link.href = url;
|
||||
link.download = `${currentPreview.table_name || "table-preview"}.csv`;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
link.remove();
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
function applyTableSearch(query) {
|
||||
const normalized = String(query || "").trim().toLowerCase();
|
||||
if (!normalized) {
|
||||
renderTables(allTables);
|
||||
return;
|
||||
}
|
||||
const filtered = allTables.filter((item) => {
|
||||
const haystack = [
|
||||
item.label,
|
||||
item.table_ref,
|
||||
item.description,
|
||||
...(item.related_views || []),
|
||||
item.domain,
|
||||
item.group,
|
||||
].join(" ").toLowerCase();
|
||||
return haystack.includes(normalized);
|
||||
});
|
||||
renderTables(filtered);
|
||||
}
|
||||
|
||||
document.getElementById("preview-close").addEventListener("click", () => {
|
||||
const modal = document.getElementById("preview-modal");
|
||||
modal.classList.remove("open");
|
||||
modal.setAttribute("aria-hidden", "true");
|
||||
});
|
||||
|
||||
document.getElementById("preview-download").addEventListener("click", downloadPreviewCsv);
|
||||
document.getElementById("table-search").addEventListener("input", (event) => {
|
||||
applyTableSearch(event.target.value);
|
||||
});
|
||||
|
||||
document.getElementById("preview-modal").addEventListener("click", (event) => {
|
||||
if (event.target.id !== "preview-modal") return;
|
||||
const modal = document.getElementById("preview-modal");
|
||||
|
||||
Reference in New Issue
Block a user