206 lines
9.4 KiB
HTML
206 lines
9.4 KiB
HTML
{% extends "base.html" %}
|
||
{% block title %}Websites — Website Checker{% endblock %}
|
||
{% block content %}
|
||
<div class="page-header">
|
||
<h1 class="page-title">Website Link Management</h1>
|
||
<div style="display:flex;gap:.5rem;align-items:center">
|
||
<div class="search-wrap" style="max-width:220px">
|
||
<span class="search-icon">🔍</span>
|
||
<input type="text" id="site-search" placeholder="Search name or URL…" autocomplete="off">
|
||
</div>
|
||
<select id="site-type-filter" class="form-control" style="width:auto">
|
||
<option value="">All types</option>
|
||
<option value="daily">Daily</option>
|
||
<option value="weekly">Weekly</option>
|
||
</select>
|
||
<button class="btn btn-primary" onclick="openModal('modal-create-site')">+ Add Website</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<table class="table table-hover">
|
||
<thead>
|
||
<tr><th>ID</th><th>Name</th><th>Type</th><th>URL</th><th>Note</th><th>Visibility</th><th>Created By</th><th>Actions</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for w in websites %}
|
||
<tr class="{{ 'row-weekly' if w.check_type == 'weekly' else '' }} site-row"
|
||
data-name="{{ w.name | lower }}" data-url="{{ w.url | lower }}"
|
||
data-type="{{ w.check_type }}">
|
||
<td>{{ w.id }}</td>
|
||
<td><strong>{{ w.name }}</strong></td>
|
||
<td><span class="badge badge-{{ 'warn' if w.check_type == 'weekly' else 'neutral' }}">{{ w.check_type }}</span></td>
|
||
<td class="url-cell"><a href="{{ w.url }}" target="_blank" rel="noopener">{{ w.url[:60] }}{% if w.url|length > 60 %}…{% endif %}</a></td>
|
||
<td class="text-muted">{{ (w.note or '')[:40] }}{% if (w.note or '')|length > 40 %}…{% endif %}</td>
|
||
<td><span class="badge badge-{{ 'accent' if w.visibility == 'assigned' else 'neutral' }}">{{ w.visibility }}</span></td>
|
||
<td class="text-muted">{{ w.creator or '—' }}</td>
|
||
<td class="actions">
|
||
<button class="btn btn-ghost btn-sm" onclick="loadEditSite({{ w.id }})">✎ Edit</button>
|
||
<form method="post" action="{{ url_for('admin_websites.delete', website_id=w.id) }}"
|
||
style="display:inline" onsubmit="return confirm('Soft-delete {{ w.name }}?')">
|
||
<button class="btn btn-danger btn-sm" type="submit">✕</button>
|
||
</form>
|
||
</td>
|
||
</tr>
|
||
{% else %}
|
||
<tr><td colspan="8" class="text-center text-muted">No websites configured.</td></tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<!-- Create Website Modal -->
|
||
<div class="modal-overlay" id="modal-create-site">
|
||
<div class="modal-dialog modal-lg">
|
||
<div class="modal-header">
|
||
<h3>Add Website</h3>
|
||
<button class="modal-close" onclick="closeModal('modal-create-site')">✕</button>
|
||
</div>
|
||
<form method="post" action="{{ url_for('admin_websites.create') }}">
|
||
<div class="modal-body">
|
||
<div class="form-row">
|
||
<div class="form-group flex-1">
|
||
<label class="form-label">Name *</label>
|
||
<input class="form-control" name="name" required>
|
||
</div>
|
||
<div class="form-group flex-1">
|
||
<label class="form-label">Check Type</label>
|
||
<select class="form-control" name="check_type">
|
||
<option value="daily">📅 Daily</option>
|
||
<option value="weekly">🗓 Weekly</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label">URL *</label>
|
||
<input class="form-control" name="url" type="url" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label">Visibility</label>
|
||
<select class="form-control" name="visibility">
|
||
<option value="all">All users</option>
|
||
<option value="assigned">Assigned users only</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label">Note</label>
|
||
<textarea class="form-control" name="note" rows="2"></textarea>
|
||
</div>
|
||
<hr>
|
||
<h4 style="margin-bottom:.5rem">Credentials <button type="button" class="btn btn-secondary btn-sm" onclick="addCredRow(this)">+ Add</button></h4>
|
||
<div class="cred-rows">
|
||
<div class="cred-row">
|
||
<div class="form-group"><label class="form-label">Label</label><input class="form-control" name="cred_label[]" placeholder="e.g. Admin"></div>
|
||
<div class="form-group"><label class="form-label">Username</label><input class="form-control" name="cred_username[]"></div>
|
||
<div class="form-group"><label class="form-label">Password</label><input class="form-control" name="cred_password[]" type="password"></div>
|
||
<div class="form-group"><label class="form-label"> </label><button type="button" class="btn btn-danger btn-sm" onclick="removeRow(this)">✕</button></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn btn-ghost" onclick="closeModal('modal-create-site')">Cancel</button>
|
||
<button type="submit" class="btn btn-primary">Create Website</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Edit Website Modal -->
|
||
<div class="modal-overlay" id="modal-edit-site">
|
||
<div class="modal-dialog modal-lg">
|
||
<div class="modal-header">
|
||
<h3>Edit Website</h3>
|
||
<button class="modal-close" onclick="closeModal('modal-edit-site')">✕</button>
|
||
</div>
|
||
<form method="post" id="edit-site-form">
|
||
<div class="modal-body" id="edit-site-body">
|
||
<div class="text-center text-muted">Loading…</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn btn-ghost" onclick="closeModal('modal-edit-site')">Cancel</button>
|
||
<button type="submit" class="btn btn-primary">Save Changes</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<script>
|
||
function loadEditSite(id) {
|
||
fetch('/admin/websites/' + id + '/detail')
|
||
.then(r => r.json())
|
||
.then(site => {
|
||
const form = document.getElementById('edit-site-form');
|
||
form.action = '/admin/websites/' + id + '/edit';
|
||
|
||
document.getElementById('edit-site-body').innerHTML = buildSiteForm(site);
|
||
openModal('modal-edit-site');
|
||
});
|
||
}
|
||
|
||
function buildSiteForm(s) {
|
||
const creds = (s.credentials || []).map(c => `
|
||
<div class="cred-row">
|
||
<input class="form-control" name="cred_label[]" placeholder="Label" value="${esc(c.label)}">
|
||
<input class="form-control" name="cred_username[]" placeholder="Username" value="${esc(c.username)}">
|
||
<input class="form-control" name="cred_password[]" placeholder="Password" type="password" value="${esc(c.password)}">
|
||
<button type="button" class="btn btn-danger btn-sm" onclick="removeRow(this)">✕</button>
|
||
</div>`).join('') || emptyCredRow();
|
||
|
||
return `
|
||
<div class="form-group"><label class="form-label">Name *</label>
|
||
<input class="form-control" name="name" value="${esc(s.name)}" required></div>
|
||
<div class="form-row">
|
||
<div class="form-group flex-1"><label class="form-label">Check Type</label>
|
||
<select class="form-control" name="check_type">
|
||
<option value="daily" ${s.check_type==='daily'?'selected':''}>📅 Daily</option>
|
||
<option value="weekly" ${s.check_type==='weekly'?'selected':''}>🗓 Weekly</option>
|
||
</select></div>
|
||
<div class="form-group flex-1"><label class="form-label">Visibility</label>
|
||
<select class="form-control" name="visibility">
|
||
<option value="all" ${s.visibility==='all'?'selected':''}>All users</option>
|
||
<option value="assigned" ${s.visibility==='assigned'?'selected':''}>Assigned only</option>
|
||
</select></div>
|
||
</div>
|
||
<div class="form-group"><label class="form-label">URL *</label>
|
||
<input class="form-control" name="url" type="url" value="${esc(s.url)}" required></div>
|
||
<div class="form-group"><label class="form-label">Note</label>
|
||
<textarea class="form-control" name="note" rows="2">${esc(s.note)}</textarea></div>
|
||
<hr>
|
||
<h4>Credentials <button type="button" class="btn btn-ghost btn-sm" onclick="addCredRow(this)">+ Add</button></h4>
|
||
<div class="cred-rows">${creds}</div>`;
|
||
}
|
||
|
||
function emptyCredRow() {
|
||
return `<div class="cred-row">
|
||
<input class="form-control" name="cred_label[]" placeholder="Label">
|
||
<input class="form-control" name="cred_username[]" placeholder="Username">
|
||
<input class="form-control" name="cred_password[]" placeholder="Password" type="password">
|
||
<button type="button" class="btn btn-danger btn-sm" onclick="removeRow(this)">✕</button>
|
||
</div>`;
|
||
}
|
||
|
||
function addCredRow(btn) {
|
||
btn.closest('.modal-body').querySelector('.cred-rows').insertAdjacentHTML('beforeend', emptyCredRow());
|
||
}
|
||
function removeRow(btn) { btn.closest('.cred-row').remove(); }
|
||
function esc(s) { return (s||'').replace(/&/g,'&').replace(/"/g,'"').replace(/</g,'<'); }
|
||
|
||
(function() {
|
||
var searchEl = document.getElementById('site-search');
|
||
var typeEl = document.getElementById('site-type-filter');
|
||
function filterSites() {
|
||
var q = searchEl.value.trim().toLowerCase();
|
||
var type = typeEl.value;
|
||
document.querySelectorAll('.site-row').forEach(function(row) {
|
||
var nameMatch = !q || row.dataset.name.includes(q) || row.dataset.url.includes(q);
|
||
var typeMatch = !type || row.dataset.type === type;
|
||
row.style.display = (nameMatch && typeMatch) ? '' : 'none';
|
||
});
|
||
}
|
||
searchEl.addEventListener('input', filterSites);
|
||
typeEl.addEventListener('change', filterSites);
|
||
})();
|
||
</script>
|
||
{% endblock %}
|