mirror of
https://github.com/Theodor-Springmann-Stiftung/musenalm.git
synced 2026-02-04 10:35:30 +00:00
289 lines
10 KiB
Plaintext
289 lines
10 KiB
Plaintext
{{ $model := . }}
|
|
|
|
<edit-page>
|
|
<div class="flex container-normal bg-slate-100 mx-auto px-8">
|
|
<div class="flex flex-row w-full justify-between">
|
|
<div class="flex flex-col justify-end-safe flex-2/5">
|
|
<div class="mb-1">
|
|
<i class="ri-text"></i> Verwaltung
|
|
</div>
|
|
<h1 class="text-2xl w-full font-bold text-slate-900 mb-1">
|
|
Abkürzungen
|
|
</h1>
|
|
<div class="flex flex-row gap-x-3">
|
|
<div>
|
|
<a href="/abkuerzungen/" class="text-gray-700 hover:text-slate-950 block no-underline">
|
|
<i class="ri-eye-line"></i> Anschauen
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="container-normal mx-auto mt-4 !px-0">
|
|
{{/* usermessage moved into action bar */}}
|
|
|
|
{{- if (IsAdminOrEditor $model.request.user) -}}
|
|
{{/* Editable form for admin/editor */}}
|
|
<form
|
|
autocomplete="off"
|
|
class="w-full dbform"
|
|
id="abkform"
|
|
method="POST"
|
|
action="/abkuerzungen/">
|
|
<input type="hidden" name="csrf_token" value="{{ $model.csrf_token }}" />
|
|
|
|
<div class="mb-4 flex items-center gap-3">
|
|
<button type="button" id="add-row-btn" class="resetbutton w-auto px-3 py-1.5 flex items-center gap-2">
|
|
<i class="ri-add-line"></i>
|
|
<span>Zeile hinzufügen</span>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="overflow-x-auto bg-white border border-gray-200 rounded">
|
|
<table class="w-full table-fixed" style="table-layout: fixed;">
|
|
<colgroup>
|
|
<col style="width: 12rem;" />
|
|
<col />
|
|
<col style="width: 8rem;" />
|
|
</colgroup>
|
|
<thead>
|
|
<tr class="border-b-2 border-gray-300 bg-gray-50">
|
|
<th class="text-left px-4 py-3 font-bold text-sm text-gray-700">Kürzel</th>
|
|
<th class="text-left px-4 py-3 font-bold text-sm text-gray-700">Bedeutung</th>
|
|
<th class="px-4 py-3"></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="abk-tbody">
|
|
{{- range $index, $entry := $model.result.Entries -}}
|
|
<tr class="border-b border-gray-200 hover:bg-gray-50 odd:bg-stone-100" data-rel-row data-row="{{ $index }}">
|
|
<td class="px-4 py-2.5" style="width: 12rem;">
|
|
<input type="text"
|
|
name="abkuerzungen[{{ $index }}][key]"
|
|
value="{{ $entry.Key }}"
|
|
class="inputinput font-mono text-base w-full border border-gray-300 px-2 py-1 rounded"
|
|
autocomplete="off"
|
|
required>
|
|
</td>
|
|
<td class="px-4 py-2.5">
|
|
<input type="text"
|
|
name="abkuerzungen[{{ $index }}][value]"
|
|
value="{{ $entry.Value }}"
|
|
class="inputinput text-base w-full border border-gray-300 px-2 py-1 rounded"
|
|
autocomplete="off"
|
|
required>
|
|
</td>
|
|
<td class="px-2 py-2.5 pr-4 text-center" style="width: 8rem;">
|
|
<input type="hidden" name="abkuerzungen[{{ $index }}][_delete]" id="abkuerzungen_delete_{{ $index }}" value="false" class="delete-flag">
|
|
<button type="button" class="delete-btn inline-flex items-center gap-1 whitespace-nowrap text-sm px-1.5" data-delete-toggle="abkuerzungen_delete_{{ $index }}">
|
|
<i class="ri-delete-bin-line"></i>
|
|
<span class="no-underline" data-delete-label data-delete-default="Entfernen" data-delete-active="Wird entfernt" data-delete-hover="Rückgängig">Entfernen</span>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
{{- end -}}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="w-full flex items-end justify-between gap-4 mt-6 flex-wrap">
|
|
<p
|
|
id="abk-save-feedback"
|
|
class="form-action-bar-message save-feedback {{ if $model.error }}save-feedback-error text-red-700{{ else if $model.success }}save-feedback-success text-green-700{{ else }}hidden{{ end }}"
|
|
{{ if $model.success }}data-autohide="true"{{ end }}
|
|
aria-live="polite">
|
|
{{- if $model.error -}}
|
|
{{ $model.error }}
|
|
{{- else if $model.success -}}
|
|
{{ $model.success }}
|
|
{{- end -}}
|
|
</p>
|
|
<div class="flex items-center gap-3 self-end flex-wrap">
|
|
<a href="/abkuerzungen/" class="resetbutton w-40 flex items-center gap-2 justify-center">
|
|
<i class="ri-close-line"></i>
|
|
<span>Abbrechen</span>
|
|
</a>
|
|
<button type="submit" class="submitbutton w-48 flex items-center gap-2 justify-center">
|
|
<i class="ri-save-line"></i>
|
|
<span>Alle speichern</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
|
|
<script>
|
|
(function() {
|
|
const tbody = document.getElementById('abk-tbody');
|
|
const addBtn = document.getElementById('add-row-btn');
|
|
let rowIndex = {{ len $model.result.Entries }};
|
|
|
|
const updateDeleteLabel = (button, deleted, hovered) => {
|
|
const label = button.querySelector('[data-delete-label]');
|
|
if (label) {
|
|
let nextLabel;
|
|
if (deleted && hovered) {
|
|
nextLabel = label.getAttribute('data-delete-hover') || 'Rückgängig';
|
|
} else if (deleted) {
|
|
nextLabel = label.getAttribute('data-delete-active') || 'Wird entfernt';
|
|
} else {
|
|
nextLabel = label.getAttribute('data-delete-default') || 'Entfernen';
|
|
}
|
|
label.textContent = nextLabel;
|
|
}
|
|
|
|
const icon = button.querySelector('i');
|
|
if (!icon) {
|
|
return;
|
|
}
|
|
if (deleted) {
|
|
if (hovered) {
|
|
icon.classList.remove('hidden');
|
|
icon.classList.add('ri-arrow-go-back-line');
|
|
icon.classList.remove('ri-delete-bin-line');
|
|
} else {
|
|
icon.classList.add('hidden');
|
|
icon.classList.remove('ri-delete-bin-line', 'ri-arrow-go-back-line');
|
|
}
|
|
} else {
|
|
icon.classList.remove('hidden');
|
|
icon.classList.add('ri-delete-bin-line');
|
|
icon.classList.remove('ri-arrow-go-back-line');
|
|
}
|
|
};
|
|
|
|
const setRowDeleted = (row, button, deleted) => {
|
|
const deleteFlag = row.querySelector('.delete-flag');
|
|
if (deleteFlag) {
|
|
deleteFlag.value = deleted ? 'true' : 'false';
|
|
}
|
|
row.classList.toggle('bg-red-50', deleted);
|
|
row.querySelectorAll('input, select, textarea').forEach((control) => {
|
|
if (control === deleteFlag || control.type === 'hidden') {
|
|
return;
|
|
}
|
|
if ('readOnly' in control) {
|
|
control.readOnly = deleted;
|
|
}
|
|
control.classList.toggle('opacity-50', deleted);
|
|
control.classList.toggle('cursor-not-allowed', deleted);
|
|
});
|
|
updateDeleteLabel(button, deleted, button.matches(':hover'));
|
|
};
|
|
|
|
const wireDeleteButton = (button) => {
|
|
if (!button || button.dataset.abkBound === 'true') {
|
|
return;
|
|
}
|
|
button.dataset.abkBound = 'true';
|
|
|
|
button.addEventListener('click', (event) => {
|
|
event.preventDefault();
|
|
const row = button.closest('tr');
|
|
if (!row) {
|
|
return;
|
|
}
|
|
const deleteFlag = row.querySelector('.delete-flag');
|
|
const isDeleted = deleteFlag && deleteFlag.value === 'true';
|
|
setRowDeleted(row, button, !isDeleted);
|
|
});
|
|
|
|
button.addEventListener('mouseenter', () => {
|
|
const row = button.closest('tr');
|
|
const deleteFlag = row ? row.querySelector('.delete-flag') : null;
|
|
if (!deleteFlag || deleteFlag.value !== 'true') {
|
|
return;
|
|
}
|
|
updateDeleteLabel(button, true, true);
|
|
});
|
|
|
|
button.addEventListener('mouseleave', () => {
|
|
const row = button.closest('tr');
|
|
const deleteFlag = row ? row.querySelector('.delete-flag') : null;
|
|
updateDeleteLabel(button, deleteFlag && deleteFlag.value === 'true', false);
|
|
});
|
|
|
|
const row = button.closest('tr');
|
|
const deleteFlag = row ? row.querySelector('.delete-flag') : null;
|
|
if (row && deleteFlag && deleteFlag.value === 'true') {
|
|
setRowDeleted(row, button, true);
|
|
} else {
|
|
updateDeleteLabel(button, false, false);
|
|
}
|
|
};
|
|
|
|
// Add new row
|
|
addBtn.addEventListener('click', function() {
|
|
const tr = document.createElement('tr');
|
|
tr.className = 'border-b border-gray-200 hover:bg-gray-50 odd:bg-stone-100';
|
|
tr.dataset.row = rowIndex;
|
|
tr.setAttribute('data-rel-row', '');
|
|
tr.innerHTML = `
|
|
<td class="px-4 py-2.5" style="width: 12rem;">
|
|
<input type="text"
|
|
name="abkuerzungen[${rowIndex}][key]"
|
|
value=""
|
|
class="inputinput font-mono text-base w-full border border-gray-300 px-2 py-1 rounded"
|
|
autocomplete="off"
|
|
required>
|
|
</td>
|
|
<td class="px-4 py-2.5">
|
|
<input type="text"
|
|
name="abkuerzungen[${rowIndex}][value]"
|
|
value=""
|
|
class="inputinput text-base w-full border border-gray-300 px-2 py-1 rounded"
|
|
autocomplete="off"
|
|
required>
|
|
</td>
|
|
<td class="px-2 py-2.5 pr-4 text-center" style="width: 8rem;">
|
|
<input type="hidden" name="abkuerzungen[${rowIndex}][_delete]" id="abkuerzungen_delete_${rowIndex}" value="false" class="delete-flag">
|
|
<button type="button" class="delete-btn inline-flex items-center gap-1 whitespace-nowrap text-sm px-1.5" data-delete-toggle="abkuerzungen_delete_${rowIndex}">
|
|
<i class="ri-delete-bin-line"></i>
|
|
<span class="no-underline" data-delete-label data-delete-default="Entfernen" data-delete-active="Wird entfernt" data-delete-hover="Rückgängig">Entfernen</span>
|
|
</button>
|
|
</td>
|
|
`;
|
|
tbody.appendChild(tr);
|
|
wireDeleteButton(tr.querySelector('.delete-btn'));
|
|
rowIndex++;
|
|
|
|
// Focus the new key input
|
|
const newKeyInput = tr.querySelector('input[type="text"]');
|
|
if (newKeyInput) {
|
|
newKeyInput.focus();
|
|
}
|
|
});
|
|
|
|
const deleteButtons = tbody.querySelectorAll('.delete-btn');
|
|
deleteButtons.forEach((button) => wireDeleteButton(button));
|
|
})();
|
|
</script>
|
|
|
|
{{- else -}}
|
|
{{/* Read-only view for public */}}
|
|
<div class="bg-white border border-gray-200 rounded overflow-hidden">
|
|
{{- if $model.result.Entries -}}
|
|
<table class="w-full">
|
|
<thead>
|
|
<tr class="border-b-2 border-gray-300 bg-gray-50">
|
|
<th class="text-left px-4 py-3 font-bold text-sm text-gray-700 w-48">Kürzel</th>
|
|
<th class="text-left px-4 py-3 font-bold text-sm text-gray-700">Bedeutung</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{{- range $entry := $model.result.Entries -}}
|
|
<tr class="border-b border-gray-200 hover:bg-gray-50 odd:bg-stone-100">
|
|
<td class="px-4 py-2.5 font-bold font-mono text-base">{{ $entry.Key }}</td>
|
|
<td class="px-4 py-2.5 text-base">{{ $entry.Value }}</td>
|
|
</tr>
|
|
{{- end -}}
|
|
</tbody>
|
|
</table>
|
|
{{- else -}}
|
|
<div class="px-4 py-8 text-center text-gray-500">Keine Abkürzungen gefunden.</div>
|
|
{{- end -}}
|
|
</div>
|
|
{{- end -}}
|
|
</div>
|
|
</edit-page>
|