diff options
| author | Paul-Christian Volkmer | 2026-03-06 16:48:26 +0100 |
|---|---|---|
| committer | Paul-Christian Volkmer | 2026-03-06 16:48:26 +0100 |
| commit | b7b7fa3da199f2f910c27084a075f91063e46381 (patch) | |
| tree | 4d63eb7eb830b1aea5e9b90f2a5c76d90d75a74d | |
| parent | bf6bfa904e127f51b79cfafb96e1280b50e9615a (diff) | |
feat: improve request list
| -rw-r--r-- | src/main/kotlin/dev/dnpm/etl/processor/web/HomeController.kt | 6 | ||||
| -rw-r--r-- | src/main/kotlin/dev/dnpm/etl/processor/web/StatisticsRestController.kt | 3 | ||||
| -rw-r--r-- | src/main/resources/templates/fragments.html | 4 | ||||
| -rw-r--r-- | src/main/resources/templates/index.html | 93 | ||||
| -rw-r--r-- | src/web/charts.js | 2 | ||||
| -rw-r--r-- | src/web/style.css | 89 |
6 files changed, 146 insertions, 51 deletions
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/web/HomeController.kt b/src/main/kotlin/dev/dnpm/etl/processor/web/HomeController.kt index ed0f264..9262c29 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/web/HomeController.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/web/HomeController.kt @@ -45,7 +45,7 @@ class HomeController( ) { @GetMapping fun index( - @PageableDefault(page = 0, size = 20, sort = ["processedAt"], direction = Sort.Direction.DESC) + @PageableDefault(page = 0, size = 10, sort = ["processedAt"], direction = Sort.Direction.DESC) pageable: Pageable, model: Model, ): String { @@ -58,14 +58,14 @@ class HomeController( @GetMapping(path = ["patient/{patientPseudonym}"]) fun byPatient( @PathVariable patientPseudonym: PatientPseudonym, - @PageableDefault(page = 0, size = 20, sort = ["processedAt"], direction = Sort.Direction.DESC) + @PageableDefault(page = 0, size = 10, sort = ["processedAt"], direction = Sort.Direction.DESC) pageable: Pageable, model: Model, ): String { val requests = requestService.findRequestByPatientId(patientPseudonym, pageable) model.addAttribute("patientPseudonym", patientPseudonym.value) model.addAttribute("requests", requests) - + model.addAttribute("postInitialSubmissionBlock", appConfigProperties.postInitialSubmissionBlock) return "index" } diff --git a/src/main/kotlin/dev/dnpm/etl/processor/web/StatisticsRestController.kt b/src/main/kotlin/dev/dnpm/etl/processor/web/StatisticsRestController.kt index 27cc3dc..9d3f824 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/web/StatisticsRestController.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/web/StatisticsRestController.kt @@ -51,7 +51,8 @@ class StatisticsRestController( RequestStatus.ERROR -> "#FF0000" RequestStatus.WARNING -> "#FF8C00" RequestStatus.SUCCESS -> "#008000" - RequestStatus.NO_CONSENT -> "#004A9D" + RequestStatus.NO_CONSENT, + RequestStatus.BLOCKED_INITIAL -> "#004A9D" else -> "#708090" } diff --git a/src/main/resources/templates/fragments.html b/src/main/resources/templates/fragments.html index 7a8469b..403ad09 100644 --- a/src/main/resources/templates/fragments.html +++ b/src/main/resources/templates/fragments.html @@ -37,10 +37,10 @@ </div> <th:block th:fragment="accept-initial" sec:authorize="hasRole('USER') or hasRole('ADMIN')"> <button class="btn btn-green" hx-swap="outerHTML" th:hx-delete="@{/submission/{requestId}/accepted(requestId=${request.uuid})}" th:if="${ request.submissionType.value == 'initial' and (request.status.value == 'success' or request.status.value == 'warning') and request.submissionAccepted == true}" title="Doch keine Meldebestätigung - jetzt blockieren"> - 🔓 + 🔓 Initialmeldung </button> <button class="btn btn-yellow" hx-swap="outerHTML" th:hx-put="@{/submission/{requestId}/accepted(requestId=${request.uuid})}" th:if="${ request.submissionType.value == 'initial' and (request.status.value == 'success' or request.status.value == 'warning') and request.submissionAccepted == false}" title="Meldebestätigung vorhanden - jetzt nicht weiter blockieren"> - 🔒 + 🔒 Initialmeldung </button> </th:block> <footer th:fragment="footer"> diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index 16354c6..db85196 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -28,6 +28,55 @@ </div> <div class="border" th:if="${requests.totalElements > 0}"> + <div class="paged"> + <div class="card" th:each="request : ${requests}"> + <div th:if="${request.status.value.contains('success')}" class="card-header bg-green">Erfolgreiche Übertragung</div> + <div th:if="${request.status.value.contains('warning')}" class="card-header bg-yellow">Übertragung mit Warnungen</div> + <div th:if="${request.status.value.contains('error')}" class="card-header bg-red">Übertragung mit Fehlern</div> + <div th:if="${request.status.value == 'unknown' and not request.isPendingUnknown()}" class="card-header bg-gray">Unbekannter Status</div> + <div th:if="${request.status.value == 'unknown' and request.isPendingUnknown()}" class="card-header bg-gray">⏰ Unbekannter Status ⏰</div> + <div th:if="${request.status.value == 'duplication'}" class="card-header bg-gray">Gestoppt: Duplikation</div> + <div th:if="${request.status.value == 'no-consent'}" class="card-header bg-blue">Gestoppt: Kein Consent</div> + <div th:if="${request.status.value == 'blocked-initial'}" class="card-header bg-blue">Gestoppt: Noch keine Meldebestätigung für vorhergehende Meldung</div> + <div class="card-sub-header" th:classappend="${request.type.value == 'delete' ? 'delete' : ''}"> + <div th:if="${request.type.value != 'delete'}"> + <span th:if="${request.submissionType.value == 'initial'}"><span>📨 Übertragung vom </span><time th:datetime="${request.processedAt}">[[ ${request.processedAt} ]]</time></span> + <span th:if="${request.submissionType.value == 'addition'}"><span>🔄 Übertragung vom </span><time th:datetime="${request.processedAt}">[[ ${request.processedAt} ]]</time></span> + <span th:if="${request.submissionType.value == 'unknown'}"><span>❓ Übertragung vom </span><time th:datetime="${request.processedAt}">[[ ${request.processedAt} ]]</time></span> + </div> + <div th:if="${request.type.value == 'delete'}"> + <span>🗑 Löschanfrage vom </span><time th:datetime="${request.processedAt}">[[ ${request.processedAt} ]]</time> + </div> + <div th:insert="~{fragments :: accept-initial}" sec:authorize="hasRole('USER') or hasRole('ADMIN')" th:if="${postInitialSubmissionBlock}"></div> + </div> + <div class="card-content"> + <div>Request-ID</div> + <div th:if="not ${request.report}"><span>[[ ${request.uuid} ]]</span></div> + <div th:if="${request.report}"> + <a th:href="@{/report/{id}(id=${request.uuid})}" sec:authorize="hasRole('USER') or hasRole('ADMIN')">[[ ${request.uuid} ]]</a> + <th:block sec:authorize="not (hasRole('USER') or hasRole('ADMIN'))">[[ ${request.uuid} ]]</th:block> + </div> + <div>Typ</div> + <div th:style="${request.type.value == 'delete'} ? 'color: red;'"> + <span> + [[ ${request.type} ]] + <th:block th:if="${request.submissionType.value != 'unknown'}">([[ ${request.submissionType} ]])</th:block> + </span> + </div> + <div sec:authorize="hasRole('USER') or hasRole('ADMIN')">Patienten-Pseudonym</div> + <div class="patient-id" th:if="${patientPseudonym != null}" sec:authorize="hasRole('USER') or hasRole('ADMIN')"> + [[ ${request.patientPseudonym} ]] + </div> + <div class="patient-id" th:if="${patientPseudonym == null}" sec:authorize="hasRole('USER') or hasRole('ADMIN')"> + <a th:href="@{/patient/{pid}(pid=${request.patientPseudonym})}">[[ ${request.patientPseudonym} ]]</a> + </div> + <div sec:authorize="hasRole('USER') or hasRole('ADMIN')">TAN</div> + <div class="patient-id" sec:authorize="hasRole('USER') or hasRole('ADMIN')"> + [[ ${request.tan} ]] + </div> + </div> + </div> + </div> <div th:if="${patientPseudonym == null}" class="page-control"> <a id="first-page-link" th:href="@{/(page=${0})}" title="Zum Anfang: Taste W" th:if="${not requests.isFirst()}">⇤</a><a th:if="${requests.isFirst()}">⇤</a> <a id="prev-page-link" th:href="@{/(page=${requests.getNumber() - 1})}" title="Seite zurück: Taste A" th:if="${not requests.isFirst()}">←</a><a th:if="${requests.isFirst()}">←</a> @@ -42,50 +91,6 @@ <a id="next-page-link" th:href="@{/patient/{patientPseudonym}(patientPseudonym=${patientPseudonym},page=${requests.getNumber() + 1})}" title="Seite vor: Taste D" th:if="${not requests.isLast()}">→</a><a th:if="${requests.isLast()}">→</a> <a id="last-page-link" th:href="@{/patient/{patientPseudonym}(patientPseudonym=${patientPseudonym},page=${requests.getTotalPages() - 1})}" title="Zum Ende: Taste S" th:if="${not requests.isLast()}">⇥</a><a th:if="${requests.isLast()}">⇥</a> </div> - <table class="paged"> - <thead> - <tr> - <th>Status</th> - <th>Typ</th> - <th sec:authorize="hasRole('USER') or hasRole('ADMIN')" th:if="${postInitialSubmissionBlock}">Aktion</th> - <th>ID</th> - <th>Datum</th> - <th>Patienten-ID</th> - </tr> - </thead> - <tbody> - <tr th:each="request : ${requests}"> - <td th:if="${request.status.value.contains('success')}" class="bg-green"><small>[[ ${request.status} ]]</small></td> - <td th:if="${request.status.value.contains('warning')}" class="bg-yellow"><small>[[ ${request.status} ]]</small></td> - <td th:if="${request.status.value.contains('error')}" class="bg-red"><small>[[ ${request.status} ]]</small></td> - <td th:if="${request.status.value == 'unknown' and not request.isPendingUnknown()}" class="bg-gray"><small>[[ ${request.status} ]]</small></td> - <td th:if="${request.status.value == 'unknown' and request.isPendingUnknown()}" class="bg-yellow"><small>⏰ [[ ${request.status} ]] ⏰</small></td> - <td th:if="${request.status.value == 'duplication'}" class="bg-gray"><small>[[ ${request.status} ]]</small></td> - <td th:if="${request.status.value == 'no-consent'}" class="bg-blue"><small>[[ ${request.status} ]]</small></td> - <td th:if="${request.status.value == 'blocked-initial'}" class="bg-gray"><small>[[ ${request.status} ]]</small></td> - <td th:style="${request.type.value == 'delete'} ? 'color: red;'"> - <small> - [[ ${request.type} ]] - <th:block th:if="${request.submissionType.value != 'unknown'}">([[ ${request.submissionType} ]])</th:block> - </small> - </td> - <td th:insert="~{fragments :: accept-initial}" sec:authorize="hasRole('USER') or hasRole('ADMIN')" th:if="${postInitialSubmissionBlock}"></td> - <td th:if="not ${request.report}">[[ ${request.uuid} ]]</td> - <td th:if="${request.report}"> - <a th:href="@{/report/{id}(id=${request.uuid})}" sec:authorize="hasRole('USER') or hasRole('ADMIN')">[[ ${request.uuid} ]]</a> - <th:block sec:authorize="not (hasRole('USER') or hasRole('ADMIN'))">[[ ${request.uuid} ]]</th:block> - </td> - <td><time th:datetime="${request.processedAt}">[[ ${request.processedAt} ]]</time></td> - <td class="patient-id" th:if="${patientPseudonym != null}" sec:authorize="hasRole('USER') or hasRole('ADMIN')"> - [[ ${request.patientPseudonym} ]] - </td> - <td class="patient-id" th:if="${patientPseudonym == null}" sec:authorize="hasRole('USER') or hasRole('ADMIN')"> - <a th:href="@{/patient/{pid}(pid=${request.patientPseudonym})}">[[ ${request.patientPseudonym} ]]</a> - </td> - <td class="patient-id" sec:authorize="not (hasRole('USER') or hasRole('ADMIN'))">***</td> - </tr> - </tbody> - </table> </div> </main> diff --git a/src/web/charts.js b/src/web/charts.js index 866e60d..fe9dda7 100644 --- a/src/web/charts.js +++ b/src/web/charts.js @@ -203,7 +203,7 @@ export function drawBarChart(url, elemId, title, data) { '#008000', '#004A9D', '#708090', - '#708090', + '#004A9D', '#708090' ], animationDuration: 250, diff --git a/src/web/style.css b/src/web/style.css index 3631750..f5396be 100644 --- a/src/web/style.css +++ b/src/web/style.css @@ -430,6 +430,95 @@ table.config-table td > button:last-of-type { min-height: 100vh; } +.card { + margin: .5rem 0; + min-height: 5rem; + border-left: 0.5rem solid var(--bg-gray); + border-radius: .2rem; + background: white; + overflow: hidden; + box-shadow: 0 .1rem .2rem var(--table-border); +} + +.card > .card-header { + content: ""; + padding: 0.2rem; + color: white; + background: var(--bg-gray); + font-family: monospace; + font-size: 0.8rem; +} + +.card > .card-header.bg-red { + background: var(--bg-red); +} + +.card:has(> .card-header.bg-red) { + border-left: 0.5rem solid var(--bg-red); +} + +.card > .card-header.bg-yellow { + background: var(--bg-yellow); +} + +.card:has(> .card-header.bg-yellow) { + border-left: 0.5rem solid var(--bg-yellow); +} + +.card > .card-header.bg-green { + background: var(--bg-green); +} + +.card:has(> .card-header.bg-green) { + border-left: 0.5rem solid var(--bg-green); +} + +.card > .card-header.bg-blue { + background: var(--bg-blue); +} + +.card:has(> .card-header.bg-blue) { + border-left: 0.5rem solid var(--bg-blue); +} + +.card > .card-sub-header { + display: flex; + padding: 0 .5rem; + border-bottom: 1px solid var(--table-border); + font-size: 1rem; + font-weight: bold; + background: #eeeeee; + align-items: center; + height: 3rem; +} + +.card > .card-sub-header.delete { + background: #ffdddd; +} + +.card > .card-sub-header > *:nth-child(2) { + margin: 0 0 0 auto; +} + +.card > .card-content { + padding: .5rem; + display: grid; + grid-template-columns: 1fr 5fr; +} + +.card > .card-content > div:nth-child(even), +.card > .card-content > div:nth-child(even) * { + font-family: monospace; +} + +.card > .card-content > *:nth-child(odd) { + font-weight: bold; +} + +.card > .card-content > div { + padding: 0.1rem 0; +} + table.samples { max-width: 100%; overflow-x: scroll; |
