Files

209 lines
9.4 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block title %}Websites — Bid 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">&nbsp;</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() {
var searchEl = document.getElementById('site-search');
var typeEl = document.getElementById('site-type-filter');
var _timer = null;
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', function() {
clearTimeout(_timer);
_timer = setTimeout(filterSites, 250);
});
typeEl.addEventListener('change', filterSites);
})();
</script>
{% endblock %}