A ticket records a known problem: title, area, hosts, subject, description, links to alert rules and anomalies, and a log of comments, edits, resolving and reopening. Opening, resolving (with the resolution as the note) and reopening go on the timeline; every step is audited, and the audit rows stay off the timeline. An alert or anomaly an open ticket covers (by link, by subject, or a host-wide ticket on the host itself) shows a Known issue badge, and offers "Open a ticket" prefilled from its rule or anomaly when none does. Hosts, monitors and containers get a Known issues card. MCP: list_tickets, get_ticket (viewer); open_ticket, update_ticket, comment_ticket, resolve_ticket, reopen_ticket (operator). list_alerts and list_anomalies carry known_issue; get_overview, get_host, get_monitor and get_container carry open_tickets. Also update_change and delete_change for changes recorded by hand (author or admin). Tickets are left out of backups, like the timeline (2026-10-15 ruling). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
82 lines
3.9 KiB
HTML
82 lines
3.9 KiB
HTML
{# tickets/form.html — open a ticket or edit one (tickets.service): its title,
|
|
area, description, hosts and what it is about, and what it links to. A new
|
|
ticket opened from an alert or anomaly arrives filled in and linked. #}
|
|
{% extends "base.html" %}
|
|
{% from "_macros.html" import crumbs, page_header %}
|
|
{% set title = ("Edit #" ~ ticket.id) if ticket else "Open a ticket" %}
|
|
{% block title %}{{ title }} — Tickets — Steward{% endblock %}
|
|
{% block breadcrumb %}{{ crumbs([("Tickets", "/tickets/")] + ([("#" ~ ticket.id, "/tickets/" ~ ticket.id)] if ticket else []) + [(title if not ticket else "Edit", "")]) }}{% endblock %}
|
|
{% block content %}
|
|
{{ page_header(title, "A known problem, open until someone resolves it.") }}
|
|
|
|
{% if error %}<div class="alert alert-error" role="alert">{{ error }}</div>{% endif %}
|
|
|
|
<form method="post" action="{{ '/tickets/' ~ ticket.id if ticket else '/tickets/' }}" class="form-page">{{ csrf_field() }}
|
|
<input type="hidden" name="back" value="{{ back }}">
|
|
<section class="card form-section" aria-labelledby="tk-what">
|
|
<h2 id="tk-what" class="section-title">The problem</h2>
|
|
<div class="field">
|
|
<label class="field-label" for="tk-title">Title</label>
|
|
<input class="input" id="tk-title" type="text" name="title" value="{{ values.title }}" maxlength="200"
|
|
required autofocus placeholder="e.g. Backups to the NAS time out at night">
|
|
</div>
|
|
<div class="field">
|
|
<label class="field-label" for="tk-area">Area</label>
|
|
<select class="input" id="tk-area" name="area" required>
|
|
<option value="">— pick one —</option>
|
|
{% for key, label in areas %}<option value="{{ key }}"{% if values.area == key %} selected{% endif %}>{{ label }}</option>{% endfor %}
|
|
</select>
|
|
<span class="field-hint">What part of the infrastructure it is about.</span>
|
|
</div>
|
|
<div class="field">
|
|
<label class="field-label" for="tk-subject">What it is about <span class="tone-dim">(optional)</span></label>
|
|
<input class="input" id="tk-subject" type="text" name="subject" value="{{ values.subject }}" maxlength="255"
|
|
placeholder="e.g. web-1/nginx, a monitor's name; empty for the hosts as a whole">
|
|
<span class="field-hint">An alert or anomaly on this resource reads as a known issue while the ticket is open.</span>
|
|
</div>
|
|
<div class="field">
|
|
<label class="field-label" for="tk-body">Description <span class="tone-dim">(optional)</span></label>
|
|
<textarea class="input" id="tk-body" name="body" rows="5" maxlength="8000"
|
|
placeholder="What is seen, since when, and what has been tried.">{{ values.body }}</textarea>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="card form-section" aria-labelledby="tk-hosts">
|
|
<h2 id="tk-hosts" class="section-title">Hosts</h2>
|
|
<p class="field-hint">The ticket shows on each ticked host's page.</p>
|
|
{% if hosts %}
|
|
<div class="check-list">
|
|
{% for h in hosts %}
|
|
<label class="field-check">
|
|
<input type="checkbox" name="hosts" value="{{ h }}"{% if h in values.hosts %} checked{% endif %}>
|
|
<span>{{ h }}</span>
|
|
</label>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
<p class="tone-muted">No hosts yet.</p>
|
|
{% endif %}
|
|
</section>
|
|
|
|
{% if links %}
|
|
<section class="card form-section" aria-labelledby="tk-links">
|
|
<h2 id="tk-links" class="section-title">Linked</h2>
|
|
<div class="check-list">
|
|
{% for l in links %}
|
|
<label class="field-check">
|
|
<input type="checkbox" name="link" value="{{ l.type }}:{{ l.id }}" checked>
|
|
<span>{{ l.label }}</span>
|
|
</label>
|
|
{% endfor %}
|
|
</div>
|
|
{% if ticket %}<p class="field-hint">Untick one to unlink it.</p>{% endif %}
|
|
</section>
|
|
{% endif %}
|
|
|
|
<div class="form-actions">
|
|
<button type="submit" class="btn">{{ "Save" if ticket else "Open the ticket" }}</button>
|
|
<a href="{{ back }}" class="btn btn-ghost">Cancel</a>
|
|
</div>
|
|
</form>
|
|
{% endblock %}
|