User-Comfort im Zahlungsparser

This commit is contained in:
2026-09-09 17:32:50 +02:00
parent 651b6147bf
commit 025035190d
2 changed files with 352 additions and 51 deletions
@@ -4,6 +4,7 @@ import {toast} from 'vue3-toastify'
import FullScreenModal from "../../../../Views/Components/FullScreenModal.vue";
import LoadingModal from "../../../../Views/Components/LoadingModal.vue";
import Icon from "../../../../Views/Components/Icon.vue";
import RichSelectBox from "../../../../Views/Components/RichSelectBox.vue";
import {useAjax} from "../../../../../resources/js/components/ajaxHandler.js";
/**
@@ -41,6 +42,39 @@ const participantsByIdentifier = computed(
() => Object.fromEntries(participants.value.map(participant => [participant.identifier, participant])),
)
/**
* Die Auswahlliste für alle Zeilen -- einmal gebaut, nicht je Zeile.
*
* Getrennt nach an- und abgemeldet, weil die Entscheidung unterschiedlich weit trägt: Eine Buchung
* auf eine abgemeldete Anmeldung zieht eine Erstattung nach sich. Als Fließtext in einer Zeile ging
* das unter, als Gruppenüberschrift steht es da.
*/
const participantOptions = computed(() => {
const options = [{value: '', label: '— ignorieren —'}]
for (const participant of participants.value) {
const state = participant.isSettled ? 'vollständig bezahlt' : participant.amountOpen + ' offen'
options.push(participant.isSignedOff
? {
value: participant.identifier,
label: participant.name,
description: 'abgemeldet am ' + participant.signedOffAt + ' · ' + state,
group: 'Abgemeldete Teilis',
icon: 'user-slash',
muted: true,
}
: {
value: participant.identifier,
label: participant.name,
description: state,
group: 'Angemeldete Teilis',
})
}
return options
})
function isAssigned(row) {
return (assignment.value[row.rowNumber] ?? '') !== ''
}
@@ -140,16 +174,6 @@ function reset() {
if (fileInput.value) fileInput.value.value = ''
}
function optionLabel(participant) {
const state = participant.isSettled
? 'vollständig bezahlt'
: participant.amountOpen + ' offen'
return participant.isSignedOff
? participant.name + ' — abgemeldet, ' + state
: participant.name + ' — ' + state
}
function assignedParticipant(row) {
return participantsByIdentifier.value[assignment.value[row.rowNumber]] ?? null
}
@@ -257,13 +281,10 @@ function signedOffOf(row) {
<td class="purpose">{{ row.purpose }}</td>
<td>
<div class="assignment">
<select v-model="assignment[row.rowNumber]" class="assignment-select">
<option value=""> ignorieren </option>
<option v-for="participant in participants" :key="participant.identifier"
:value="participant.identifier">
{{ optionLabel(participant) }}
</option>
</select>
<RichSelectBox v-model="assignment[row.rowNumber]"
:options="participantOptions"
placeholder="— ignorieren —"
filterable/>
<span class="pill" :class="'pill-' + rowState(row).key">
<Icon :key="rowState(row).icon" :name="rowState(row).icon"/> {{ rowState(row).label }}
</span>
@@ -458,7 +479,7 @@ function signedOffOf(row) {
padding: 10px;
border-bottom: 1px solid #e5e7eb;
vertical-align: top;
font-size: 0.9rem;
font-size: 0.95rem;
}
.statement-table .right {
@@ -476,7 +497,7 @@ function signedOffOf(row) {
}
.assignment-column {
width: 320px;
width: 360px;
}
/* Zeilenfarbe sagt auf einen Blick, was passiert: grün wird gebucht, gelb will geprüft werden,
@@ -520,14 +541,6 @@ function signedOffOf(row) {
gap: 5px;
}
.assignment-select {
width: 100%;
padding: 5px 6px;
border: 1px solid #d1d5db;
border-radius: 6px;
background-color: #ffffff;
font-size: 0.85rem;
}
.pill {
align-self: flex-start;