Auto-commit local changes before build (2026-03-20 12:13:27)

This commit is contained in:
Ivo Oskamp 2026-03-20 12:13:27 +01:00
parent fb841fb4e6
commit 0cbd3e59b1
3 changed files with 174 additions and 108 deletions

View File

@ -92,6 +92,7 @@ def cove_accounts():
)
customers = Customer.query.filter_by(active=True).order_by(Customer.name.asc()).all()
customer_rows = [{"id": c.id, "name": c.name} for c in customers]
jobs = Job.query.filter_by(archived=False).order_by(Job.job_name.asc()).all()
for acc in unmatched + matched:
@ -104,7 +105,7 @@ def cove_accounts():
"main/cove_accounts.html",
unmatched=unmatched,
matched=matched,
customers=customers,
customers=customer_rows,
jobs=jobs,
settings=settings,
STATUS_LABELS={
@ -150,9 +151,8 @@ def cove_account_link(cove_account_db_id: int):
flash("Customer not found.", "danger")
return redirect(url_for("main.cove_accounts"))
default_job_name = (cove_acc.account_name or cove_acc.computer_name or f"Cove account {cove_acc.account_id}").strip()
job_name = (request.form.get("job_name") or default_job_name).strip()
backup_type = (request.form.get("backup_type") or _derive_backup_type_for_account(cove_acc)).strip()
job_name = (cove_acc.account_name or cove_acc.computer_name or f"Cove account {cove_acc.account_id}").strip()
backup_type = _derive_backup_type_for_account(cove_acc)
job = Job(
customer_id=customer.id,

View File

@ -21,7 +21,7 @@
{# ── Unmatched accounts (need a job) ─────────────────────────────────────── #}
{% if unmatched %}
<h4 class="mb-2">Unmatched <span class="badge bg-warning text-dark">{{ unmatched|length }}</span></h4>
<p class="text-muted small mb-3">These accounts have no linked job yet. Create a new job or link to an existing one.</p>
<p class="text-muted small mb-3">Click a row to create a new job or link to an existing one.</p>
<div class="table-responsive mb-4">
<table class="table table-sm table-hover align-middle">
@ -36,12 +36,17 @@
<th>Last status</th>
<th>Last run</th>
<th>First seen</th>
<th></th>
</tr>
</thead>
<tbody>
{% for acc in unmatched %}
<tr>
<tr class="cove-unmatched-row"
style="cursor: pointer;"
data-id="{{ acc.id }}"
data-job-name="{{ acc.derived_job_name | e }}"
data-backup-software="{{ acc.derived_backup_software | e }}"
data-backup-type="{{ acc.derived_backup_type | e }}"
data-cove-customer="{{ acc.customer_name | e if acc.customer_name else '' }}">
<td>{{ acc.derived_backup_software }}</td>
<td>{{ acc.derived_backup_type }}</td>
<td>{{ acc.derived_job_name }}</td>
@ -57,108 +62,7 @@
</td>
<td class="text-muted small">{{ acc.last_run_at|local_datetime if acc.last_run_at else '—' }}</td>
<td class="text-muted small">{{ acc.first_seen_at|local_datetime }}</td>
<td>
<button class="btn btn-sm btn-primary"
data-bs-toggle="modal"
data-bs-target="#link-modal-{{ acc.id }}">
Link / Create job
</button>
</td>
</tr>
{# Link modal #}
<div class="modal fade" id="link-modal-{{ acc.id }}" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Link: {{ acc.account_name or acc.account_id }}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<p class="text-muted small mb-3">
Cove account <strong>{{ acc.account_id }}</strong>
customer: <strong>{{ acc.customer_name or '?' }}</strong>
</p>
<ul class="nav nav-tabs mb-3" id="tab-{{ acc.id }}" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" data-bs-toggle="tab"
data-bs-target="#create-{{ acc.id }}" type="button">
Create new job
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" data-bs-toggle="tab"
data-bs-target="#existing-{{ acc.id }}" type="button">
Link to existing job
</button>
</li>
</ul>
<div class="tab-content">
{# Tab 1: Create new job #}
<div class="tab-pane fade show active" id="create-{{ acc.id }}">
<form method="post" action="{{ url_for('main.cove_account_link', cove_account_db_id=acc.id) }}">
<input type="hidden" name="action" value="create" />
<div class="mb-3">
<label class="form-label">Customer <span class="text-danger">*</span></label>
<select class="form-select" name="customer_id" required>
<option value="">Select customer…</option>
{% for c in customers %}
<option value="{{ c.id }}"
{% if acc.customer_name and acc.customer_name.lower() == c.name.lower() %}selected{% endif %}>
{{ c.name }}
</option>
{% endfor %}
</select>
</div>
<div class="mb-3">
<label class="form-label">Job name</label>
<input type="text" class="form-control" name="job_name"
value="{{ acc.derived_job_name }}" />
<div class="form-text">Defaults to the Cove account name.</div>
</div>
<div class="mb-3">
<label class="form-label">Backup type</label>
<input type="text" class="form-control" name="backup_type"
value="{{ acc.derived_backup_type }}" />
<div class="form-text">Derived from Cove datasource profile.</div>
</div>
<div class="d-flex justify-content-end gap-2">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Create job &amp; link</button>
</div>
</form>
</div>
{# Tab 2: Link to existing job #}
<div class="tab-pane fade" id="existing-{{ acc.id }}">
<form method="post" action="{{ url_for('main.cove_account_link', cove_account_db_id=acc.id) }}">
<input type="hidden" name="action" value="link" />
<div class="mb-3">
<label class="form-label">Job <span class="text-danger">*</span></label>
<select class="form-select" name="job_id" required>
<option value="">Select job…</option>
{% for j in jobs %}
<option value="{{ j.id }}">
{{ j.customer.name ~ ' ' if j.customer else '' }}{{ j.backup_software }} / {{ j.job_name }}
</option>
{% endfor %}
</select>
</div>
<div class="d-flex justify-content-end gap-2">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Link to job</button>
</div>
</form>
</div>
</div>{# /tab-content #}
</div>
</div>
</div>
</div>{# /modal #}
{% endfor %}
</tbody>
</table>
@ -238,4 +142,158 @@
</div>
{% endif %}
{# ── Shared link/create modal ─────────────────────────────────────────────── #}
<div class="modal fade" id="coveLinkModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="coveLinkModalTitle">Link Cove account</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<ul class="nav nav-tabs mb-3" id="coveLinkTabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" data-bs-toggle="tab"
data-bs-target="#coveCreateTab" type="button">Create new job</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" data-bs-toggle="tab"
data-bs-target="#coveExistingTab" type="button">Link to existing</button>
</li>
</ul>
<div class="tab-content">
{# Tab 1: Create new job #}
<div class="tab-pane fade show active" id="coveCreateTab">
<form method="post" id="coveCreateForm">
<input type="hidden" name="action" value="create" />
<input type="hidden" name="customer_id" id="coveCustomerId" />
<div class="mb-3">
<label class="form-label">Customer <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="coveCustomerInput"
list="coveCustomerList" placeholder="Type customer name…" autocomplete="off" />
<datalist id="coveCustomerList">
{% for c in customers %}
<option value="{{ c.name | e }}"></option>
{% endfor %}
</datalist>
</div>
<dl class="row mb-3 dl-compact">
<dt class="col-5">Job name</dt>
<dd class="col-7" id="coveDisplayJobName"></dd>
<dt class="col-5">Backup software</dt>
<dd class="col-7" id="coveDisplayBackupSoftware"></dd>
<dt class="col-5">Backup type</dt>
<dd class="col-7" id="coveDisplayBackupType"></dd>
</dl>
<div class="d-flex justify-content-end gap-2">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Create job &amp; link</button>
</div>
</form>
</div>
{# Tab 2: Link to existing job #}
<div class="tab-pane fade" id="coveExistingTab">
<form method="post" id="coveLinkExistingForm">
<input type="hidden" name="action" value="link" />
<div class="mb-3">
<label class="form-label">Job <span class="text-danger">*</span></label>
<select class="form-select" name="job_id" required>
<option value="">Select job…</option>
{% for j in jobs %}
<option value="{{ j.id }}">
{{ j.customer.name ~ ' ' if j.customer else '' }}{{ j.backup_software }} / {{ j.job_name }}
</option>
{% endfor %}
</select>
</div>
<div class="d-flex justify-content-end gap-2">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Link to job</button>
</div>
</form>
</div>
</div>{# /tab-content #}
</div>
</div>
</div>
</div>
<script>
(function () {
var modalEl = document.getElementById('coveLinkModal');
if (!modalEl) return;
var modal = new bootstrap.Modal(modalEl);
var customers = {{ customers | tojson | safe }};
function findCustomerIdByName(name) {
var n = (name || '').trim().toLowerCase();
for (var i = 0; i < customers.length; i++) {
if (customers[i].name.toLowerCase() === n) return customers[i].id;
}
return null;
}
var linkUrlTpl = "{{ url_for('main.cove_account_link', cove_account_db_id=0) }}";
document.querySelectorAll('tr.cove-unmatched-row').forEach(function (row) {
row.addEventListener('click', function () {
var id = row.getAttribute('data-id');
var jobName = row.getAttribute('data-job-name') || '';
var backupSoftware = row.getAttribute('data-backup-software') || '';
var backupType = row.getAttribute('data-backup-type') || '';
var coveCustomer = row.getAttribute('data-cove-customer') || '';
var linkUrl = linkUrlTpl.replace('0', id);
document.getElementById('coveLinkModalTitle').textContent = jobName;
document.getElementById('coveDisplayJobName').textContent = jobName;
document.getElementById('coveDisplayBackupSoftware').textContent = backupSoftware;
document.getElementById('coveDisplayBackupType').textContent = backupType;
// Pre-fill customer if Cove's customer name matches an existing customer
var customerInput = document.getElementById('coveCustomerInput');
var customerIdField = document.getElementById('coveCustomerId');
if (customerInput) customerInput.value = '';
if (customerIdField) customerIdField.value = '';
if (coveCustomer && customerInput) {
var matchedId = findCustomerIdByName(coveCustomer);
if (matchedId) {
customerInput.value = coveCustomer;
customerIdField.value = String(matchedId);
} else {
customerInput.value = coveCustomer;
}
}
var createForm = document.getElementById('coveCreateForm');
var linkForm = document.getElementById('coveLinkExistingForm');
if (createForm) {
createForm.action = linkUrl;
createForm.onsubmit = function (ev) {
var cid = findCustomerIdByName(customerInput ? customerInput.value : '');
if (!cid) {
ev.preventDefault();
alert('Please select an existing customer name from the list.');
return false;
}
if (customerIdField) customerIdField.value = String(cid);
};
}
if (linkForm) linkForm.action = linkUrl;
modal.show();
});
});
})();
</script>
{% endblock %}

View File

@ -2,6 +2,14 @@
This file documents all changes made to this project via Claude Code.
## [2026-03-20] (2)
### Changed
- Cove Accounts page: same clickable-row UX as Cloud Connect — removed per-row "Link / Create Job" button and inline modals (N modals → 1 shared modal):
- Unmatched rows are clickable; modal pre-fills job name, backup software, backup type as read-only
- Customer via datalist auto-complete; Cove's own customer name is used as pre-fill suggestion when it matches an existing customer
- `routes_cove.py`: `customers` passed as dicts for `tojson`; `job_name` and `backup_type` now derived server-side (no longer read from form fields)
## [2026-03-20]
### Fixed