refactor: extract monitor form into shared partial for create and edit
This commit is contained in:
parent
5b7a211c21
commit
f013890c40
|
|
@ -120,90 +120,7 @@
|
|||
<!-- Edit form -->
|
||||
<div class="bg-gray-900 border border-gray-800 rounded-xl p-6">
|
||||
<h3 class="text-sm text-gray-400 mb-4">Edit Monitor</h3>
|
||||
<form id="edit-form" class="space-y-5">
|
||||
|
||||
<div>
|
||||
<label class="block text-sm text-gray-400 mb-1.5">Name</label>
|
||||
<input id="edit-name" type="text" value="<%= m.name %>"
|
||||
class="w-full bg-gray-800 border border-gray-700 rounded-lg px-4 py-2.5 text-gray-100 focus:outline-none focus:border-blue-500">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm text-gray-400 mb-1.5">URL</label>
|
||||
<div class="flex gap-2">
|
||||
<select id="edit-method"
|
||||
class="bg-gray-800 border border-gray-700 rounded-lg px-3 py-2.5 text-gray-100 focus:outline-none focus:border-blue-500 font-mono text-sm">
|
||||
<% ['GET','POST','PUT','PATCH','DELETE','HEAD','OPTIONS'].forEach(function(method) { %>
|
||||
<option <%= (m.method || 'GET') === method ? 'selected' : '' %>><%= method %></option>
|
||||
<% }) %>
|
||||
</select>
|
||||
<input id="edit-url" type="url" value="<%= m.url %>"
|
||||
class="flex-1 bg-gray-800 border border-gray-700 rounded-lg px-4 py-2.5 text-gray-100 focus:outline-none focus:border-blue-500">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="flex items-center justify-between mb-1.5">
|
||||
<label class="text-sm text-gray-400">Headers <span class="text-gray-600">(optional)</span></label>
|
||||
<button type="button" id="edit-add-header" class="text-xs text-blue-400 hover:text-blue-300 transition-colors">+ Add header</button>
|
||||
</div>
|
||||
<div id="edit-headers-list" class="space-y-2">
|
||||
<% if (m.request_headers && typeof m.request_headers === 'object') {
|
||||
Object.entries(m.request_headers).forEach(function([k, v]) { %>
|
||||
<div class="header-row flex gap-2">
|
||||
<input type="text" value="<%= k %>" placeholder="Header name" class="hk flex-1 bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-gray-100 placeholder-gray-600 focus:outline-none focus:border-blue-500 text-sm">
|
||||
<input type="text" value="<%= v %>" placeholder="Value" class="hv flex-1 bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-gray-100 placeholder-gray-600 focus:outline-none focus:border-blue-500 text-sm">
|
||||
<button type="button" onclick="this.parentElement.remove()" class="px-2 text-gray-600 hover:text-red-400 transition-colors text-sm">✕</button>
|
||||
</div>
|
||||
<% }) } %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="edit-body-section" class="<%= ['GET','HEAD','OPTIONS'].includes(m.method || 'GET') ? 'hidden' : '' %>">
|
||||
<label class="block text-sm text-gray-400 mb-1.5">Request Body <span class="text-gray-600">(optional)</span></label>
|
||||
<textarea id="edit-request-body" rows="4" placeholder='{"key": "value"}'
|
||||
class="w-full bg-gray-800 border border-gray-700 rounded-lg px-4 py-2.5 text-gray-100 placeholder-gray-600 focus:outline-none focus:border-blue-500 font-mono text-sm resize-y"><%= m.request_body || '' %></textarea>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-4">
|
||||
<div class="flex-1">
|
||||
<label class="block text-sm text-gray-400 mb-1.5">Interval</label>
|
||||
<select id="edit-interval" class="w-full bg-gray-800 border border-gray-700 rounded-lg px-4 py-2.5 text-gray-100 focus:outline-none focus:border-blue-500">
|
||||
<% [['1','1 second'],['2','2 seconds'],['5','5 seconds'],['10','10 seconds'],['20','20 seconds'],['30','30 seconds'],['60','1 minute'],['300','5 minutes'],['600','10 minutes'],['1800','30 minutes'],['3600','1 hour']].forEach(function([val, label]) { %>
|
||||
<option value="<%= val %>" <%= String(m.interval_s) === val ? 'selected' : '' %>><%= label %></option>
|
||||
<% }) %>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<label class="block text-sm text-gray-400 mb-1.5">Timeout</label>
|
||||
<select id="edit-timeout" class="w-full bg-gray-800 border border-gray-700 rounded-lg px-4 py-2.5 text-gray-100 focus:outline-none focus:border-blue-500">
|
||||
<% [['5000','5 seconds'],['10000','10 seconds'],['30000','30 seconds'],['60000','60 seconds']].forEach(function([val, label]) { %>
|
||||
<option value="<%= val %>" <%= String(m.timeout_ms || 30000) === val ? 'selected' : '' %>><%= label %></option>
|
||||
<% }) %>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm text-gray-400 mb-1.5">Regions <span class="text-gray-600">(leave all unselected to use all regions)</span></label>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<% [['eu-central','🇩🇪 EU Central'],['us-east','🇺🇸 US East'],['us-west','🇺🇸 US West'],['ap-southeast','🇸🇬 AP Southeast']].forEach(function([val, label]) { %>
|
||||
<label class="flex items-center gap-2 bg-gray-800 border border-gray-700 hover:border-gray-500 rounded-lg px-3 py-2 cursor-pointer transition-colors">
|
||||
<input type="checkbox" value="<%= val %>" class="edit-region-check accent-blue-500" <%= (m.regions && m.regions.includes(val)) ? 'checked' : '' %>> <span class="text-sm text-gray-300"><%- label %></span>
|
||||
</label>
|
||||
<% }) %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm text-gray-400 mb-1.5">Conditions <span class="text-gray-600">(optional)</span></label>
|
||||
<p class="text-xs text-gray-600 mb-3">Define up/down conditions. Defaults to status < 400.</p>
|
||||
<div id="edit-query-builder"></div>
|
||||
</div>
|
||||
|
||||
<div id="edit-error" class="text-red-400 text-sm hidden"></div>
|
||||
<button type="submit" class="bg-blue-600 hover:bg-blue-500 text-white text-sm font-medium px-6 py-2.5 rounded-lg transition-colors">Save Changes</button>
|
||||
</form>
|
||||
<%~ include('./partials/monitor-form', { _form: { monitor: m, isEdit: true, prefix: 'edit-', bg: 'bg-gray-800', border: 'border-gray-700' } }) %>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
|
@ -211,36 +128,9 @@
|
|||
<script>
|
||||
|
||||
const monitorId = '<%= m.id %>';
|
||||
let editQuery = <%~ JSON.stringify(m.query || null) %>;
|
||||
|
||||
const editQb = new QueryBuilder(document.getElementById('edit-query-builder'), (q) => {
|
||||
editQuery = q;
|
||||
});
|
||||
editQb.setQuery(editQuery);
|
||||
|
||||
// Method/body visibility
|
||||
const editMethod = document.getElementById('edit-method');
|
||||
const editBodySection = document.getElementById('edit-body-section');
|
||||
function updateEditBodyVisibility() {
|
||||
editBodySection.classList.toggle('hidden', ['GET','HEAD','OPTIONS'].includes(editMethod.value));
|
||||
}
|
||||
editMethod.addEventListener('change', updateEditBodyVisibility);
|
||||
|
||||
// Dynamic headers
|
||||
document.getElementById('edit-add-header').addEventListener('click', () => {
|
||||
addHeaderRow(document.getElementById('edit-headers-list'));
|
||||
});
|
||||
|
||||
function addHeaderRow(container, key='', value='') {
|
||||
const row = document.createElement('div');
|
||||
row.className = 'header-row flex gap-2';
|
||||
row.innerHTML = `
|
||||
<input type="text" value="${escapeHtml(key)}" placeholder="Header name" class="hk flex-1 bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-gray-100 placeholder-gray-600 focus:outline-none focus:border-blue-500 text-sm">
|
||||
<input type="text" value="${escapeHtml(value)}" placeholder="Value" class="hv flex-1 bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-gray-100 placeholder-gray-600 focus:outline-none focus:border-blue-500 text-sm">
|
||||
<button type="button" onclick="this.parentElement.remove()" class="px-2 text-gray-600 hover:text-red-400 transition-colors text-sm">✕</button>
|
||||
`;
|
||||
container.appendChild(row);
|
||||
}
|
||||
const _prefix = 'edit-';
|
||||
const _initialQuery = <%~ JSON.stringify(m.query || null) %>;
|
||||
<%~ include('./partials/monitor-form-js') %>
|
||||
|
||||
// Toggle button
|
||||
document.getElementById('toggle-btn').onclick = async () => {
|
||||
|
|
@ -261,26 +151,7 @@
|
|||
const errEl = document.getElementById('edit-error');
|
||||
errEl.classList.add('hidden');
|
||||
try {
|
||||
const headers = {};
|
||||
document.querySelectorAll('#edit-headers-list .header-row').forEach(row => {
|
||||
const k = row.querySelector('.hk').value.trim();
|
||||
const v = row.querySelector('.hv').value.trim();
|
||||
if (k) headers[k] = v;
|
||||
});
|
||||
|
||||
const body = {
|
||||
name: document.getElementById('edit-name').value.trim(),
|
||||
url: document.getElementById('edit-url').value.trim(),
|
||||
method: document.getElementById('edit-method').value,
|
||||
interval_s: Number(document.getElementById('edit-interval').value),
|
||||
timeout_ms: Number(document.getElementById('edit-timeout').value),
|
||||
};
|
||||
if (Object.keys(headers).length) body.request_headers = headers;
|
||||
else body.request_headers = null;
|
||||
const rb = document.getElementById('edit-request-body').value.trim();
|
||||
body.request_body = rb || null;
|
||||
if (editQuery) body.query = editQuery;
|
||||
body.regions = [...document.querySelectorAll('.edit-region-check:checked')].map(el => el.value);
|
||||
const body = collectFormData(_prefix);
|
||||
await api(`/monitors/${monitorId}`, { method: 'PATCH', body });
|
||||
location.reload();
|
||||
} catch (err) {
|
||||
|
|
|
|||
|
|
@ -7,168 +7,24 @@
|
|||
<h2 class="text-lg font-semibold text-gray-200 mt-2">Create Monitor</h2>
|
||||
</div>
|
||||
|
||||
<form id="create-form" class="space-y-6">
|
||||
<div>
|
||||
<label class="block text-sm text-gray-400 mb-1.5">Name</label>
|
||||
<input id="name" type="text" required placeholder="Production API"
|
||||
class="w-full bg-gray-900 border border-gray-800 rounded-lg px-4 py-2.5 text-gray-100 placeholder-gray-600 focus:outline-none focus:border-blue-500">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm text-gray-400 mb-1.5">URL</label>
|
||||
<div class="flex gap-2">
|
||||
<select id="method"
|
||||
class="bg-gray-900 border border-gray-800 rounded-lg px-3 py-2.5 text-gray-100 focus:outline-none focus:border-blue-500 font-mono text-sm">
|
||||
<option>GET</option>
|
||||
<option>POST</option>
|
||||
<option>PUT</option>
|
||||
<option>PATCH</option>
|
||||
<option>DELETE</option>
|
||||
<option>HEAD</option>
|
||||
<option>OPTIONS</option>
|
||||
</select>
|
||||
<input id="url" type="url" required placeholder="https://api.example.com/health"
|
||||
class="flex-1 bg-gray-900 border border-gray-800 rounded-lg px-4 py-2.5 text-gray-100 placeholder-gray-600 focus:outline-none focus:border-blue-500">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Request Headers -->
|
||||
<div>
|
||||
<div class="flex items-center justify-between mb-1.5">
|
||||
<label class="text-sm text-gray-400">Headers <span class="text-gray-600">(optional)</span></label>
|
||||
<button type="button" id="add-header" class="text-xs text-blue-400 hover:text-blue-300 transition-colors">+ Add header</button>
|
||||
</div>
|
||||
<div id="headers-list" class="space-y-2"></div>
|
||||
</div>
|
||||
|
||||
<!-- Request Body -->
|
||||
<div id="body-section" class="hidden">
|
||||
<label class="block text-sm text-gray-400 mb-1.5">Request Body <span class="text-gray-600">(optional)</span></label>
|
||||
<textarea id="request-body" rows="4" placeholder='{"key": "value"}'
|
||||
class="w-full bg-gray-900 border border-gray-800 rounded-lg px-4 py-2.5 text-gray-100 placeholder-gray-600 focus:outline-none focus:border-blue-500 font-mono text-sm resize-y"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-4">
|
||||
<div class="flex-1">
|
||||
<label class="block text-sm text-gray-400 mb-1.5">Ping Interval</label>
|
||||
<select id="interval"
|
||||
class="w-full bg-gray-900 border border-gray-800 rounded-lg px-4 py-2.5 text-gray-100 focus:outline-none focus:border-blue-500">
|
||||
<option value="1">1 second</option>
|
||||
<option value="2">2 seconds</option>
|
||||
<option value="5">5 seconds</option>
|
||||
<option value="10">10 seconds</option>
|
||||
<option value="20">20 seconds</option>
|
||||
<option value="30">30 seconds</option>
|
||||
<option value="60" selected>1 minute</option>
|
||||
<option value="300">5 minutes</option>
|
||||
<option value="600">10 minutes</option>
|
||||
<option value="1800">30 minutes</option>
|
||||
<option value="3600">1 hour</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<label class="block text-sm text-gray-400 mb-1.5">Timeout</label>
|
||||
<select id="timeout"
|
||||
class="w-full bg-gray-900 border border-gray-800 rounded-lg px-4 py-2.5 text-gray-100 focus:outline-none focus:border-blue-500">
|
||||
<option value="5000">5 seconds</option>
|
||||
<option value="10000">10 seconds</option>
|
||||
<option value="30000" selected>30 seconds</option>
|
||||
<option value="60000">60 seconds</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm text-gray-400 mb-1.5">Regions <span class="text-gray-600">(optional — leave all unselected to use all regions)</span></label>
|
||||
<div class="flex flex-wrap gap-2" id="region-list">
|
||||
<label class="region-option flex items-center gap-2 bg-gray-900 border border-gray-800 hover:border-gray-600 rounded-lg px-3 py-2 cursor-pointer transition-colors">
|
||||
<input type="checkbox" value="eu-central" class="region-check accent-blue-500"> <span class="text-sm text-gray-300">🇩🇪 EU Central</span>
|
||||
</label>
|
||||
<label class="region-option flex items-center gap-2 bg-gray-900 border border-gray-800 hover:border-gray-600 rounded-lg px-3 py-2 cursor-pointer transition-colors">
|
||||
<input type="checkbox" value="us-east" class="region-check accent-blue-500"> <span class="text-sm text-gray-300">🇺🇸 US East</span>
|
||||
</label>
|
||||
<label class="region-option flex items-center gap-2 bg-gray-900 border border-gray-800 hover:border-gray-600 rounded-lg px-3 py-2 cursor-pointer transition-colors">
|
||||
<input type="checkbox" value="us-west" class="region-check accent-blue-500"> <span class="text-sm text-gray-300">🇺🇸 US West</span>
|
||||
</label>
|
||||
<label class="region-option flex items-center gap-2 bg-gray-900 border border-gray-800 hover:border-gray-600 rounded-lg px-3 py-2 cursor-pointer transition-colors">
|
||||
<input type="checkbox" value="ap-southeast" class="region-check accent-blue-500"> <span class="text-sm text-gray-300">🇸🇬 AP Southeast</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm text-gray-400 mb-1.5">Query Conditions <span class="text-gray-600">(optional)</span></label>
|
||||
<p class="text-xs text-gray-600 mb-3">Define when this monitor should be considered "up". Defaults to status < 400.</p>
|
||||
<div id="query-builder"></div>
|
||||
</div>
|
||||
|
||||
<div id="form-error" class="text-red-400 text-sm hidden"></div>
|
||||
|
||||
<button type="submit" id="submit-btn"
|
||||
class="w-full bg-blue-600 hover:bg-blue-500 text-white font-medium py-3 rounded-lg transition-colors">
|
||||
Create Monitor
|
||||
</button>
|
||||
</form>
|
||||
<%~ include('./partials/monitor-form', { _form: { monitor: {}, isEdit: false, prefix: '', bg: 'bg-gray-900', border: 'border-gray-800' } }) %>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
const _prefix = '';
|
||||
const _initialQuery = null;
|
||||
<%~ include('./partials/monitor-form-js') %>
|
||||
|
||||
let currentQuery = null;
|
||||
// Show body section for non-GET methods
|
||||
const methodSel = document.getElementById('method');
|
||||
const bodySection = document.getElementById('body-section');
|
||||
function updateBodyVisibility() {
|
||||
const m = methodSel.value;
|
||||
bodySection.classList.toggle('hidden', ['GET','HEAD','OPTIONS'].includes(m));
|
||||
}
|
||||
methodSel.addEventListener('change', updateBodyVisibility);
|
||||
|
||||
// Dynamic headers
|
||||
document.getElementById('add-header').addEventListener('click', () => {
|
||||
const row = document.createElement('div');
|
||||
row.className = 'header-row flex gap-2';
|
||||
row.innerHTML = `
|
||||
<input type="text" placeholder="Header name" class="hk flex-1 bg-gray-900 border border-gray-800 rounded-lg px-3 py-2 text-gray-100 placeholder-gray-600 focus:outline-none focus:border-blue-500 text-sm">
|
||||
<input type="text" placeholder="Value" class="hv flex-1 bg-gray-900 border border-gray-800 rounded-lg px-3 py-2 text-gray-100 placeholder-gray-600 focus:outline-none focus:border-blue-500 text-sm">
|
||||
<button type="button" onclick="this.parentElement.remove()" class="px-2 text-gray-600 hover:text-red-400 transition-colors text-sm">✕</button>
|
||||
`;
|
||||
document.getElementById('headers-list').appendChild(row);
|
||||
});
|
||||
|
||||
const qb = new QueryBuilder(document.getElementById('query-builder'), (q) => {
|
||||
currentQuery = q;
|
||||
});
|
||||
|
||||
document.getElementById('create-form').addEventListener('submit', async (e) => {
|
||||
document.getElementById('form').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const btn = document.getElementById('submit-btn');
|
||||
const errEl = document.getElementById('form-error');
|
||||
const errEl = document.getElementById('error');
|
||||
errEl.classList.add('hidden');
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Creating...';
|
||||
|
||||
try {
|
||||
const headers = {};
|
||||
document.querySelectorAll('.header-row').forEach(row => {
|
||||
const k = row.querySelector('.hk').value.trim();
|
||||
const v = row.querySelector('.hv').value.trim();
|
||||
if (k) headers[k] = v;
|
||||
});
|
||||
|
||||
const body = {
|
||||
name: document.getElementById('name').value.trim(),
|
||||
url: document.getElementById('url').value.trim(),
|
||||
method: document.getElementById('method').value,
|
||||
interval_s: Number(document.getElementById('interval').value),
|
||||
timeout_ms: Number(document.getElementById('timeout').value),
|
||||
};
|
||||
if (Object.keys(headers).length) body.request_headers = headers;
|
||||
const regions = [...document.querySelectorAll('.region-check:checked')].map(el => el.value);
|
||||
if (regions.length) body.regions = regions;
|
||||
const rb = document.getElementById('request-body').value.trim();
|
||||
if (rb) body.request_body = rb;
|
||||
if (currentQuery) body.query = currentQuery;
|
||||
|
||||
const body = collectFormData(_prefix);
|
||||
await api('/monitors/', { method: 'POST', body });
|
||||
window.location.href = '/dashboard/home';
|
||||
} catch (err) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
let _currentQuery = _initialQuery;
|
||||
|
||||
// Query builder
|
||||
const _qb = new QueryBuilder(document.getElementById(_prefix + 'query-builder'), (q) => {
|
||||
_currentQuery = q;
|
||||
});
|
||||
if (_initialQuery) _qb.setQuery(_initialQuery);
|
||||
|
||||
// Method/body visibility
|
||||
const _methodSel = document.getElementById(_prefix + 'method');
|
||||
const _bodySection = document.getElementById(_prefix + 'body-section');
|
||||
_methodSel.addEventListener('change', () => {
|
||||
_bodySection.classList.toggle('hidden', ['GET','HEAD','OPTIONS'].includes(_methodSel.value));
|
||||
});
|
||||
|
||||
// Dynamic headers
|
||||
document.querySelector('.' + (_prefix || '') + 'add-header').addEventListener('click', () => {
|
||||
const container = document.getElementById(_prefix + 'headers-list');
|
||||
const row = document.createElement('div');
|
||||
row.className = 'header-row flex gap-2';
|
||||
const bg = container.closest('form').querySelector('input')?.className.match(/bg-gray-\d+/)?.[0] || 'bg-gray-900';
|
||||
const border = container.closest('form').querySelector('input')?.className.match(/border-gray-\d+/)?.[0] || 'border-gray-800';
|
||||
row.innerHTML = `
|
||||
<input type="text" placeholder="Header name" class="hk flex-1 ${bg} border ${border} rounded-lg px-3 py-2 text-gray-100 placeholder-gray-600 focus:outline-none focus:border-blue-500 text-sm">
|
||||
<input type="text" placeholder="Value" class="hv flex-1 ${bg} border ${border} rounded-lg px-3 py-2 text-gray-100 placeholder-gray-600 focus:outline-none focus:border-blue-500 text-sm">
|
||||
<button type="button" onclick="this.parentElement.remove()" class="px-2 text-gray-600 hover:text-red-400 transition-colors text-sm">✕</button>
|
||||
`;
|
||||
container.appendChild(row);
|
||||
});
|
||||
|
||||
// Collect form data into API body
|
||||
function collectFormData(prefix) {
|
||||
const headers = {};
|
||||
document.querySelectorAll('#' + prefix + 'headers-list .header-row').forEach(row => {
|
||||
const k = row.querySelector('.hk').value.trim();
|
||||
const v = row.querySelector('.hv').value.trim();
|
||||
if (k) headers[k] = v;
|
||||
});
|
||||
|
||||
const body = {
|
||||
name: document.getElementById(prefix + 'name').value.trim(),
|
||||
url: document.getElementById(prefix + 'url').value.trim(),
|
||||
method: document.getElementById(prefix + 'method').value,
|
||||
interval_s: Number(document.getElementById(prefix + 'interval').value),
|
||||
timeout_ms: Number(document.getElementById(prefix + 'timeout').value),
|
||||
};
|
||||
if (Object.keys(headers).length) body.request_headers = headers;
|
||||
else body.request_headers = null;
|
||||
const rb = document.getElementById(prefix + 'request-body').value.trim();
|
||||
body.request_body = rb || null;
|
||||
body.regions = [...document.querySelectorAll('.' + prefix + 'region-check:checked')].map(el => el.value);
|
||||
if (_currentQuery) body.query = _currentQuery;
|
||||
return body;
|
||||
}
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
<%
|
||||
const monitor = it._form?.monitor || {};
|
||||
const isEdit = !!it._form?.isEdit;
|
||||
const prefix = it._form?.prefix || '';
|
||||
const bg = it._form?.bg || 'bg-gray-900';
|
||||
const border = it._form?.border || 'border-gray-800';
|
||||
const btnText = isEdit ? 'Save Changes' : 'Create Monitor';
|
||||
const formId = prefix + 'form';
|
||||
const methods = ['GET','POST','PUT','PATCH','DELETE','HEAD','OPTIONS'];
|
||||
const intervals = [['1','1 second'],['2','2 seconds'],['5','5 seconds'],['10','10 seconds'],['20','20 seconds'],['30','30 seconds'],['60','1 minute'],['300','5 minutes'],['600','10 minutes'],['1800','30 minutes'],['3600','1 hour']];
|
||||
const timeouts = [['5000','5 seconds'],['10000','10 seconds'],['30000','30 seconds'],['60000','60 seconds']];
|
||||
const regions = [['eu-central','🇩🇪 EU Central'],['us-east','🇺🇸 US East'],['us-west','🇺🇸 US West'],['ap-southeast','🇸🇬 AP Southeast']];
|
||||
const curMethod = monitor.method || 'GET';
|
||||
const bodyHidden = ['GET','HEAD','OPTIONS'].includes(curMethod);
|
||||
%>
|
||||
|
||||
<form id="<%= formId %>" class="space-y-<%= isEdit ? '5' : '6' %>">
|
||||
<div>
|
||||
<label class="block text-sm text-gray-400 mb-1.5">Name</label>
|
||||
<input id="<%= prefix %>name" type="text" required value="<%= monitor.name || '' %>" placeholder="Production API"
|
||||
class="w-full <%= bg %> border <%= border %> rounded-lg px-4 py-2.5 text-gray-100 placeholder-gray-600 focus:outline-none focus:border-blue-500">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm text-gray-400 mb-1.5">URL</label>
|
||||
<div class="flex gap-2">
|
||||
<select id="<%= prefix %>method"
|
||||
class="<%= bg %> border <%= border %> rounded-lg px-3 py-2.5 text-gray-100 focus:outline-none focus:border-blue-500 font-mono text-sm">
|
||||
<% methods.forEach(function(method) { %>
|
||||
<option <%= curMethod === method ? 'selected' : '' %>><%= method %></option>
|
||||
<% }) %>
|
||||
</select>
|
||||
<input id="<%= prefix %>url" type="url" required value="<%= monitor.url || '' %>" placeholder="https://api.example.com/health"
|
||||
class="flex-1 <%= bg %> border <%= border %> rounded-lg px-4 py-2.5 text-gray-100 placeholder-gray-600 focus:outline-none focus:border-blue-500">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Request Headers -->
|
||||
<div>
|
||||
<div class="flex items-center justify-between mb-1.5">
|
||||
<label class="text-sm text-gray-400">Headers <span class="text-gray-600">(optional)</span></label>
|
||||
<button type="button" class="<%= prefix %>add-header text-xs text-blue-400 hover:text-blue-300 transition-colors">+ Add header</button>
|
||||
</div>
|
||||
<div id="<%= prefix %>headers-list" class="space-y-2">
|
||||
<% if (monitor.request_headers && typeof monitor.request_headers === 'object') {
|
||||
Object.entries(monitor.request_headers).forEach(function([k, v]) { %>
|
||||
<div class="header-row flex gap-2">
|
||||
<input type="text" value="<%= k %>" placeholder="Header name" class="hk flex-1 <%= bg %> border <%= border %> rounded-lg px-3 py-2 text-gray-100 placeholder-gray-600 focus:outline-none focus:border-blue-500 text-sm">
|
||||
<input type="text" value="<%= v %>" placeholder="Value" class="hv flex-1 <%= bg %> border <%= border %> rounded-lg px-3 py-2 text-gray-100 placeholder-gray-600 focus:outline-none focus:border-blue-500 text-sm">
|
||||
<button type="button" onclick="this.parentElement.remove()" class="px-2 text-gray-600 hover:text-red-400 transition-colors text-sm">✕</button>
|
||||
</div>
|
||||
<% }) } %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Request Body -->
|
||||
<div id="<%= prefix %>body-section" class="<%= bodyHidden ? 'hidden' : '' %>">
|
||||
<label class="block text-sm text-gray-400 mb-1.5">Request Body <span class="text-gray-600">(optional)</span></label>
|
||||
<textarea id="<%= prefix %>request-body" rows="4" placeholder='{"key": "value"}'
|
||||
class="w-full <%= bg %> border <%= border %> rounded-lg px-4 py-2.5 text-gray-100 placeholder-gray-600 focus:outline-none focus:border-blue-500 font-mono text-sm resize-y"><%= monitor.request_body || '' %></textarea>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-4">
|
||||
<div class="flex-1">
|
||||
<label class="block text-sm text-gray-400 mb-1.5"><%= isEdit ? 'Interval' : 'Ping Interval' %></label>
|
||||
<select id="<%= prefix %>interval"
|
||||
class="w-full <%= bg %> border <%= border %> rounded-lg px-4 py-2.5 text-gray-100 focus:outline-none focus:border-blue-500">
|
||||
<% intervals.forEach(function([val, label]) { %>
|
||||
<option value="<%= val %>" <%= String(monitor.interval_s || '60') === val ? 'selected' : '' %>><%= label %></option>
|
||||
<% }) %>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<label class="block text-sm text-gray-400 mb-1.5">Timeout</label>
|
||||
<select id="<%= prefix %>timeout"
|
||||
class="w-full <%= bg %> border <%= border %> rounded-lg px-4 py-2.5 text-gray-100 focus:outline-none focus:border-blue-500">
|
||||
<% timeouts.forEach(function([val, label]) { %>
|
||||
<option value="<%= val %>" <%= String(monitor.timeout_ms || '30000') === val ? 'selected' : '' %>><%= label %></option>
|
||||
<% }) %>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm text-gray-400 mb-1.5">Regions <span class="text-gray-600">(leave all unselected to use all regions)</span></label>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<% regions.forEach(function([val, label]) { %>
|
||||
<label class="flex items-center gap-2 <%= bg %> border <%= border %> hover:border-gray-600 rounded-lg px-3 py-2 cursor-pointer transition-colors">
|
||||
<input type="checkbox" value="<%= val %>" class="<%= prefix %>region-check accent-blue-500" <%= (monitor.regions && monitor.regions.includes(val)) ? 'checked' : '' %>> <span class="text-sm text-gray-300"><%- label %></span>
|
||||
</label>
|
||||
<% }) %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm text-gray-400 mb-1.5"><%= isEdit ? 'Conditions' : 'Query Conditions' %> <span class="text-gray-600">(optional)</span></label>
|
||||
<p class="text-xs text-gray-600 mb-3">Define <% if (isEdit) { %>up/down conditions<% } else { %>when this monitor should be considered "up"<% } %>. Defaults to status < 400.</p>
|
||||
<div id="<%= prefix %>query-builder"></div>
|
||||
</div>
|
||||
|
||||
<div id="<%= prefix %>error" class="text-red-400 text-sm hidden"></div>
|
||||
|
||||
<button type="submit" id="<%= prefix %>submit-btn"
|
||||
class="<%= isEdit ? '' : 'w-full ' %>bg-blue-600 hover:bg-blue-500 text-white<%= isEdit ? ' text-sm' : '' %> font-medium px-6 py-<%= isEdit ? '2.5' : '3' %> rounded-lg transition-colors">
|
||||
<%= btnText %>
|
||||
</button>
|
||||
</form>
|
||||
Loading…
Reference in New Issue