feat: improve fonts + button positions

This commit is contained in:
Philipp Dormann 2025-03-20 21:29:10 +01:00
parent 975f145444
commit c37fb98bed
Signed by: philipp
GPG Key ID: 3BB9ADD52DCA4314
9 changed files with 1259 additions and 1387 deletions

View File

@ -25,7 +25,7 @@
<nav class="text-sm font-medium text-gray-600" aria-label="Main Navigation"> <nav class="text-sm font-medium text-gray-600" aria-label="Main Navigation">
<a <a
class:activenav={$router.path === "/"} class:activenav={$router.path === "/"}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full" class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold"
href="/" href="/"
> >
<svg <svg
@ -43,7 +43,7 @@
{#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:GET")} {#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:GET")}
<a <a
class:activenav={$router.path === "/runners/"} class:activenav={$router.path === "/runners/"}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full" class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold"
href="/runners/" href="/runners/"
> >
<svg <svg
@ -64,7 +64,7 @@
{#if store.state.jwtinfo.userdetails.permissions.includes("TEAM:GET")} {#if store.state.jwtinfo.userdetails.permissions.includes("TEAM:GET")}
<a <a
class:activenav={$router.path === "/teams/"} class:activenav={$router.path === "/teams/"}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full" class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold"
href="/teams/" href="/teams/"
> >
<svg <svg
@ -85,7 +85,7 @@
{#if store.state.jwtinfo.userdetails.permissions.includes("ORGANIZATION:GET")} {#if store.state.jwtinfo.userdetails.permissions.includes("ORGANIZATION:GET")}
<a <a
class:activenav={$router.path.includes("/orgs/")} class:activenav={$router.path.includes("/orgs/")}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full" class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold"
href="/orgs/" href="/orgs/"
> >
<svg <svg
@ -106,7 +106,7 @@
{#if store.state.jwtinfo.userdetails.permissions.includes("DONOR:GET")} {#if store.state.jwtinfo.userdetails.permissions.includes("DONOR:GET")}
<a <a
class:activenav={$router.path.includes("/donors/")} class:activenav={$router.path.includes("/donors/")}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full" class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold"
href="/donors/" href="/donors/"
> >
<svg <svg
@ -127,7 +127,7 @@
{#if store.state.jwtinfo.userdetails.permissions.includes("DONATION:GET")} {#if store.state.jwtinfo.userdetails.permissions.includes("DONATION:GET")}
<a <a
class:activenav={$router.path.includes("/donations/")} class:activenav={$router.path.includes("/donations/")}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full" class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold"
href="/donations/" href="/donations/"
> >
<svg <svg
@ -148,7 +148,7 @@
{#if store.state.jwtinfo.userdetails.permissions.includes("TRACK:GET")} {#if store.state.jwtinfo.userdetails.permissions.includes("TRACK:GET")}
<a <a
class:activenav={$router.path === "/tracks/"} class:activenav={$router.path === "/tracks/"}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full" class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold"
href="/tracks/" href="/tracks/"
> >
<svg <svg
@ -169,7 +169,7 @@
{#if store.state.jwtinfo.userdetails.permissions.includes("CARD:GET")} {#if store.state.jwtinfo.userdetails.permissions.includes("CARD:GET")}
<a <a
class:activenav={$router.path === "/cards/"} class:activenav={$router.path === "/cards/"}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full" class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold"
href="/cards/" href="/cards/"
> >
<svg <svg
@ -192,7 +192,7 @@
{#if store.state.jwtinfo.userdetails.permissions.includes("SCAN:GET")} {#if store.state.jwtinfo.userdetails.permissions.includes("SCAN:GET")}
<a <a
class:activenav={$router.path === "/scans/"} class:activenav={$router.path === "/scans/"}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full" class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold"
href="/scans/" href="/scans/"
> >
<svg <svg
@ -214,7 +214,7 @@
{#if store.state.jwtinfo.userdetails.permissions.includes("CONTACT:GET")} {#if store.state.jwtinfo.userdetails.permissions.includes("CONTACT:GET")}
<a <a
class:activenav={$router.path === "/contacts/"} class:activenav={$router.path === "/contacts/"}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full" class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold"
href="/contacts/" href="/contacts/"
> >
<svg <svg
@ -235,7 +235,7 @@
{#if store.state.jwtinfo.userdetails.permissions.includes("STATION:GET")} {#if store.state.jwtinfo.userdetails.permissions.includes("STATION:GET")}
<a <a
class:activenav={$router.path === "/scanstations/"} class:activenav={$router.path === "/scanstations/"}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full" class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold"
href="/scanstations/" href="/scanstations/"
> >
<svg <svg
@ -257,7 +257,7 @@
{#if store.state.jwtinfo.userdetails.permissions.includes("STATSCLIENT:GET")} {#if store.state.jwtinfo.userdetails.permissions.includes("STATSCLIENT:GET")}
<a <a
class:activenav={$router.path === "/statsclients/"} class:activenav={$router.path === "/statsclients/"}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full" class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold"
href="/statsclients/" href="/statsclients/"
> >
<svg <svg
@ -279,7 +279,7 @@
{#if store.state.jwtinfo.userdetails.permissions.includes("USER:GET")} {#if store.state.jwtinfo.userdetails.permissions.includes("USER:GET")}
<a <a
class:activenav={$router.path === "/users/"} class:activenav={$router.path === "/users/"}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full" class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold"
href="/users/" href="/users/"
> >
<svg <svg
@ -300,7 +300,7 @@
{#if store.state.jwtinfo.userdetails.permissions.includes("USERGROUP:GET")} {#if store.state.jwtinfo.userdetails.permissions.includes("USERGROUP:GET")}
<a <a
class:activenav={$router.path === "/groups/"} class:activenav={$router.path === "/groups/"}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full" class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold"
href="/groups/" href="/groups/"
> >
<svg <svg
@ -320,7 +320,7 @@
{/if} {/if}
<a <a
class:activenav={$router.path === "/settings/"} class:activenav={$router.path === "/settings/"}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full" class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold"
href="/settings/" href="/settings/"
> >
<svg <svg
@ -339,7 +339,7 @@
</a> </a>
<a <a
class:activenav={$router.path === "/about/"} class:activenav={$router.path === "/about/"}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full" class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold"
href="/about/" href="/about/"
> >
<svg <svg
@ -358,7 +358,7 @@
</a> </a>
<button <button
tabindex="0" tabindex="0"
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full" class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold"
on:click={() => { on:click={() => {
AuthService.authControllerLogout(); AuthService.authControllerLogout();
logout(); logout();

View File

@ -173,19 +173,19 @@
{$_("fixed-donation")}: {$_("fixed-donation")}:
{amount_input.toFixed(2).toLocaleString("de-DE", { valute: "EUR" })} {amount_input.toFixed(2).toLocaleString("de-DE", { valute: "EUR" })}
{/if} {/if}
<span data-id="donation_actions_${original_data.id}"> <div data-id="donation_actions_${original_data.id}">
{#if store.state.jwtinfo.userdetails.permissions.includes("DONATION:DELETE")} {#if store.state.jwtinfo.userdetails.permissions.includes("DONATION:DELETE")}
{#if delete_triggered} {#if delete_triggered}
<button <button
on:click={deleteDonation} on:click={deleteDonation}
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:" class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:w-auto sm:text-sm"
>{$_("confirm-deletion")}</button >{$_("confirm-deletion")}</button
> >
<button <button
on:click={() => { on:click={() => {
delete_triggered = !delete_triggered; delete_triggered = !delete_triggered;
}} }}
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-400 text-base font-medium text-white sm:w-auto sm:" class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-400 text-base font-medium text-white sm:w-auto sm:text-sm"
>{$_("cancel")}</button >{$_("cancel")}</button
> >
{/if} {/if}
@ -195,7 +195,7 @@
delete_triggered = true; delete_triggered = true;
}} }}
type="button" type="button"
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:" class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:w-auto sm:text-sm"
>{$_("delete-donation")}</button >{$_("delete-donation")}</button
> >
{/if} {/if}
@ -206,11 +206,11 @@
class:opacity-50={!save_enabled} class:opacity-50={!save_enabled}
type="button" type="button"
on:click={submit} on:click={submit}
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:" class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:w-auto sm:text-sm"
>{$_("save-changes")}</button >{$_("save-changes")}</button
> >
{/if} {/if}
</span> </div>
</div> </div>
<!-- --> <!-- -->
<div> <div>

View File

@ -1,434 +1,435 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import store from "../../store"; import store from "../../store";
import { DonorService, DonationService } from "@odit/lfk-client-js"; import { DonorService, DonationService } from "@odit/lfk-client-js";
import PromiseError from "../base/PromiseError.svelte"; import PromiseError from "../base/PromiseError.svelte";
import isEmail from "validator/es/lib/isEmail"; import isEmail from "validator/es/lib/isEmail";
import ConfirmDonorDeletion from "./ConfirmDonorDeletion.svelte"; import ConfirmDonorDeletion from "./ConfirmDonorDeletion.svelte";
import toast from "svelte-french-toast"; import toast from "svelte-french-toast";
let data_loaded = false; let data_loaded = false;
export let params; export let params;
$: delete_triggered = false; $: delete_triggered = false;
$: original_data = {}; $: original_data = {};
$: editable = {}; $: editable = {};
$: changes_performed = !( $: changes_performed = !(
JSON.stringify(original_data) === JSON.stringify(editable) JSON.stringify(original_data) === JSON.stringify(editable)
); );
$: isEmailValid = $: isEmailValid =
(editable.email || "") === "" || (editable.email || "") === "" ||
(editable.email && isEmail(editable.email || "")); (editable.email && isEmail(editable.email || ""));
$: isFirstnameValid = editable.firstname !== ""; $: isFirstnameValid = editable.firstname !== "";
$: isLastnameValid = editable.lastname !== ""; $: isLastnameValid = editable.lastname !== "";
$: save_enabled = $: save_enabled =
changes_performed && changes_performed &&
isFirstnameValid && isFirstnameValid &&
isLastnameValid && isLastnameValid &&
isEmailValid && isEmailValid &&
isPhoneValidOrEmpty && isPhoneValidOrEmpty &&
((isAddress1Valid && iszipcodevalid && iscityvalid) || ((isAddress1Valid && iszipcodevalid && iscityvalid) ||
editable.address_checked === false); editable.address_checked === false);
const promise = DonorService.donorControllerGetOne(params.donorid).then( const promise = DonorService.donorControllerGetOne(params.donorid).then(
(data) => { (data) => {
data_loaded = true; data_loaded = true;
original_data = Object.assign(original_data, data); original_data = Object.assign(original_data, data);
editable = Object.assign(editable, original_data); editable = Object.assign(editable, original_data);
editable.address_checked = editable.address.address1 !== null; editable.address_checked = editable.address.address1 !== null;
original_data.address_checked = editable.address.address1 !== null; original_data.address_checked = editable.address.address1 !== null;
if (editable.address_checked === false) { if (editable.address_checked === false) {
editable.address = { editable.address = {
address1: "", address1: "",
address2: "", address2: "",
city: "", city: "",
postalcode: "", postalcode: "",
country: "", country: "",
}; };
} }
} }
); );
$: isPhoneValidOrEmpty = $: isPhoneValidOrEmpty =
editable.phone?.includes("+") || editable.phone?.includes("+") ||
editable.phone === "" || editable.phone === "" ||
editable.phone === null; editable.phone === null;
$: isAddress1Valid = editable.address?.address1?.trim().length !== 0; $: isAddress1Valid = editable.address?.address1?.trim().length !== 0;
$: iszipcodevalid = editable.address?.postalcode?.trim().length !== 0; $: iszipcodevalid = editable.address?.postalcode?.trim().length !== 0;
$: iscityvalid = editable.address?.city?.trim().length !== 0; $: iscityvalid = editable.address?.city?.trim().length !== 0;
let modal_open = false; let modal_open = false;
let delete_donor = {}; let delete_donor = {};
function submit() { function submit() {
if (data_loaded === true && save_enabled) { if (data_loaded === true && save_enabled) {
toast($_("donor-is-being-updated")); toast($_("donor-is-being-updated"));
editable.address.country = "DE"; editable.address.country = "DE";
if (editable.address_checked === false) { if (editable.address_checked === false) {
editable.address = null; editable.address = null;
} }
if (editable.email) editable.email = editable.email; if (editable.email) editable.email = editable.email;
else editable.email = null; else editable.email = null;
if (editable.phone) editable.phone = editable.phone; if (editable.phone) editable.phone = editable.phone;
else editable.phone = null; else editable.phone = null;
if (editable.middlename) editable.middlename = editable.middlename; if (editable.middlename) editable.middlename = editable.middlename;
editable.receiptNeeded = editable.address_checked; editable.receiptNeeded = editable.address_checked;
DonorService.donorControllerPut(original_data.id, editable) DonorService.donorControllerPut(original_data.id, editable)
.then((resp) => { .then((resp) => {
Object.assign(original_data, editable); Object.assign(original_data, editable);
original_data = original_data; original_data = original_data;
toast.success($_("updated-donor")); toast.success($_("updated-donor"));
}) })
.catch((err) => {}); .catch((err) => {});
} else { } else {
} }
} }
function deleteDonor() { function deleteDonor() {
DonorService.donorControllerRemove(original_data.id, false) DonorService.donorControllerRemove(original_data.id, false)
.then((resp) => { .then((resp) => {
toast($_("donor-deleted")); toast($_("donor-deleted"));
location.replace("./"); location.replace("./");
}) })
.catch((err) => { .catch((err) => {
modal_open = true; modal_open = true;
delete_donor = original_data; delete_donor = original_data;
}); });
} }
</script> </script>
<ConfirmDonorDeletion bind:modal_open bind:delete_donor /> <ConfirmDonorDeletion bind:modal_open bind:delete_donor />
{#await promise} {#await promise}
{$_("loading-donor-details")} {$_("loading-donor-details")}
{:then} {:then}
<section class="container p-5 select-none"> <section class="container p-5 select-none">
<div class="flex flex-row mb-4"> <div class="flex flex-row mb-4">
<div class="w-full"> <div class="w-full">
<nav class="w-full flex"> <nav class="w-full flex">
<ol class="list-none flex flex-row items-center justify-start"> <ol class="list-none flex flex-row items-center justify-start">
<li class="flex items-center"> <li class="flex items-center">
<svg <svg
fill="currentColor" fill="currentColor"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" viewBox="0 0 24 24"
width="24" width="24"
height="24" height="24"
><path fill="none" d="M0 0h24v24H0z" /> ><path fill="none" d="M0 0h24v24H0z" />
<path <path
d="M9.33 11.5h2.17A4.5 4.5 0 0 1 16 16H8.999L9 17h8v-1a5.578 5.578 0 0 0-.886-3H19a5 5 0 0 1 4.516 2.851C21.151 18.972 17.322 21 13 21c-2.761 0-5.1-.59-7-1.625L6 10.071A6.967 6.967 0 0 1 9.33 11.5zM5 19a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1v-9a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v9zM18 5a3 3 0 1 1 0 6 3 3 0 0 1 0-6zm-7-3a3 3 0 1 1 0 6 3 3 0 0 1 0-6z" d="M9.33 11.5h2.17A4.5 4.5 0 0 1 16 16H8.999L9 17h8v-1a5.578 5.578 0 0 0-.886-3H19a5 5 0 0 1 4.516 2.851C21.151 18.972 17.322 21 13 21c-2.761 0-5.1-.59-7-1.625L6 10.071A6.967 6.967 0 0 1 9.33 11.5zM5 19a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1v-9a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v9zM18 5a3 3 0 1 1 0 6 3 3 0 0 1 0-6zm-7-3a3 3 0 1 1 0 6 3 3 0 0 1 0-6z"
/></svg /></svg
> >
</li> </li>
<li class="flex items-center ml-2"> <li class="flex items-center ml-2">
<a class="mr-2" href="./">{$_("donors")}</a><svg <a class="mr-2" href="./">{$_("donors")}</a><svg
stroke="currentColor" stroke="currentColor"
fill="none" fill="none"
stroke-width="2" stroke-width="2"
viewBox="0 0 24 24" viewBox="0 0 24 24"
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
class="h-3 w-3 mr-2 stroke-current" class="h-3 w-3 mr-2 stroke-current"
height="1em" height="1em"
width="1em" width="1em"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
><line x1="5" y1="12" x2="19" y2="12" /> ><line x1="5" y1="12" x2="19" y2="12" />
<polyline points="12 5 19 12 12 19" /></svg <polyline points="12 5 19 12 12 19" /></svg
> >
</li> </li>
<li class="flex items-center"> <li class="flex items-center">
<span class="mr-2" <span class="mr-2"
>{original_data.firstname} >{original_data.firstname}
{original_data.middlename || ""} {original_data.middlename || ""}
{original_data.lastname}</span {original_data.lastname}</span
> >
</li> </li>
</ol> </ol>
</nav> </nav>
</div> </div>
</div> </div>
<div class="mb-8 text-3xl font-extrabold leading-tight"> <div class="mb-8 text-3xl font-extrabold leading-tight">
{original_data.firstname} {original_data.firstname}
{original_data.middlename || ""} {original_data.middlename || ""}
{original_data.lastname} {original_data.lastname}
<span data-id="donor_actions_${editable.id}"> <div data-id="donor_actions_${editable.id}">
{#if store.state.jwtinfo.userdetails.permissions.includes("DONOR:DELETE")} <p class="text-base">Aktionen</p>
{#if delete_triggered} {#if store.state.jwtinfo.userdetails.permissions.includes("DONOR:DELETE")}
<button {#if delete_triggered}
on:click={deleteDonor} <button
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:" on:click={deleteDonor}
>{$_("confirm-deletion")}</button class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:w-auto sm:text-sm"
> >{$_("confirm-deletion")}</button
<button >
on:click={() => { <button
delete_triggered = !delete_triggered; on:click={() => {
}} delete_triggered = !delete_triggered;
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-400 text-base font-medium text-white sm:w-auto sm:" }}
>{$_("cancel")}</button class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-400 text-base font-medium text-white sm:w-auto sm:text-sm"
> >{$_("cancel")}</button
{/if} >
{#if !delete_triggered} {/if}
<button {#if !delete_triggered}
on:click={() => { <button
delete_triggered = true; on:click={() => {
}} delete_triggered = true;
type="button" }}
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:" type="button"
>{$_("delete-donor")}</button class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:w-auto sm:text-sm"
> >{$_("delete-donor")}</button
{/if} >
{/if} {/if}
{#if !delete_triggered} {/if}
<button {#if !delete_triggered}
disabled={!save_enabled} <button
class:opacity-50={!save_enabled} disabled={!save_enabled}
type="button" class:opacity-50={!save_enabled}
on:click={submit} type="button"
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:" on:click={submit}
>{$_("save-changes")}</button class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:w-auto sm:text-sm"
> >{$_("save-changes")}</button
{/if} >
</span> {/if}
</div> </div>
<!-- --> </div>
<div> <!-- -->
<span class="font-medium text-gray-700" <div>
>{$_("total-donation-amount")}:</span <span class="font-medium text-gray-700"
> >{$_("total-donation-amount")}:</span
<span >
>{(editable.donationAmount / 100) <span
.toFixed(2) >{(editable.donationAmount / 100)
.toLocaleString("de-DE", { valute: "EUR" })}€</span .toFixed(2)
> .toLocaleString("de-DE", { valute: "EUR" })}€</span
| >
<span class="font-medium text-gray-700">{$_("total-paid-amount")}:</span> |
<span <span class="font-medium text-gray-700">{$_("total-paid-amount")}:</span>
>{(editable.paidDonationAmount / 100) <span
.toFixed(2) >{(editable.paidDonationAmount / 100)
.toLocaleString("de-DE", { valute: "EUR" })}€</span .toFixed(2)
> .toLocaleString("de-DE", { valute: "EUR" })}€</span
<br /> >
<span class="font-medium text-gray-700">{$_("donations")}:</span> <br />
{#if original_data.donations.length > 0} <span class="font-medium text-gray-700">{$_("donations")}:</span>
{#each original_data.donations as d} {#if original_data.donations.length > 0}
{#if d.responseType === "DISTANCEDONATION"} {#each original_data.donations as d}
<a {#if d.responseType === "DISTANCEDONATION"}
href="../donations/{d.id}" <a
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-blue-600 text-white mr-1" href="../donations/{d.id}"
>{d.runner.firstname} class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-blue-600 text-white mr-1"
{d.runner.middlename || ""} >{d.runner.firstname}
{d.runner.lastname}</a {d.runner.middlename || ""}
> {d.runner.lastname}</a
{:else} >
<a {:else}
href="../donations/{d.id}" <a
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-700 text-white mr-1" href="../donations/{d.id}"
>{$_("fixed-donation")}: class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-700 text-white mr-1"
{(d.amount / 100) >{$_("fixed-donation")}:
.toFixed(2) {(d.amount / 100)
.toLocaleString("de-DE", { valute: "EUR" })}€</a .toFixed(2)
> .toLocaleString("de-DE", { valute: "EUR" })}€</a
{/if} >
{/each} {/if}
{:else}{$_("donor-has-no-associated-donations")}{/if} {/each}
</div> {:else}{$_("donor-has-no-associated-donations")}{/if}
<div class=" w-full"> </div>
<label for="firstname" class="font-medium text-gray-700" <div class=" w-full">
>{$_("first-name")}</label <label for="firstname" class="font-medium text-gray-700"
> >{$_("first-name")}</label
<input >
autocomplete="off" <input
placeholder={$_("first-name")} autocomplete="off"
type="text" placeholder={$_("first-name")}
class:border-red-500={!isFirstnameValid} type="text"
class:focus:border-red-500={!isFirstnameValid} class:border-red-500={!isFirstnameValid}
class:focus:ring-red-500={!isFirstnameValid} class:focus:border-red-500={!isFirstnameValid}
bind:value={editable.firstname} class:focus:ring-red-500={!isFirstnameValid}
name="firstname" bind:value={editable.firstname}
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm: border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" name="firstname"
/> class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm: border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"
{#if !isFirstnameValid} />
<span {#if !isFirstnameValid}
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" <span
> class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
{$_("first-name-is-required")} >
</span> {$_("first-name-is-required")}
{/if} </span>
</div> {/if}
<div class=" w-full"> </div>
<label for="middlename" class="font-medium text-gray-700" <div class=" w-full">
>{$_("middle-name")}</label <label for="middlename" class="font-medium text-gray-700"
> >{$_("middle-name")}</label
<input >
autocomplete="off" <input
placeholder={$_("middle-name")} autocomplete="off"
type="text" placeholder={$_("middle-name")}
bind:value={editable.middlename} type="text"
name="middlename" bind:value={editable.middlename}
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm: border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" name="middlename"
/> class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm: border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"
</div> />
<div class=" w-full"> </div>
<label for="lastname" class="font-medium text-gray-700" <div class=" w-full">
>{$_("last-name")}</label <label for="lastname" class="font-medium text-gray-700"
> >{$_("last-name")}</label
<input >
autocomplete="off" <input
placeholder={$_("last-name")} autocomplete="off"
type="text" placeholder={$_("last-name")}
bind:value={editable.lastname} type="text"
class:border-red-500={!isLastnameValid} bind:value={editable.lastname}
class:focus:border-red-500={!isLastnameValid} class:border-red-500={!isLastnameValid}
class:focus:ring-red-500={!isLastnameValid} class:focus:border-red-500={!isLastnameValid}
name="lastname" class:focus:ring-red-500={!isLastnameValid}
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm: border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" name="lastname"
/> class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm: border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"
{#if !isLastnameValid} />
<span {#if !isLastnameValid}
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" <span
> class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
{$_("last-name-is-required")} >
</span> {$_("last-name-is-required")}
{/if} </span>
</div> {/if}
<div class=" w-full"> </div>
<label for="email" class="font-medium text-gray-700" <div class=" w-full">
>{$_("e-mail-adress")}</label <label for="email" class="font-medium text-gray-700"
> >{$_("e-mail-adress")}</label
<input >
autocomplete="off" <input
placeholder={$_("e-mail-adress")} autocomplete="off"
type="email" placeholder={$_("e-mail-adress")}
bind:value={editable.email} type="email"
class:border-red-500={!isEmailValid} bind:value={editable.email}
class:focus:border-red-500={!isEmailValid} class:border-red-500={!isEmailValid}
class:focus:ring-red-500={!isEmailValid} class:focus:border-red-500={!isEmailValid}
name="email" class:focus:ring-red-500={!isEmailValid}
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm: border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" name="email"
/> class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm: border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"
{#if !isEmailValid} />
<span {#if !isEmailValid}
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" <span
> class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
{$_("valid-email-is-required")} >
</span> {$_("valid-email-is-required")}
{/if} </span>
</div> {/if}
<div class=" w-full"> </div>
<label for="phone" class="font-medium text-gray-700">{$_("phone")}</label> <div class=" w-full">
<input <label for="phone" class="font-medium text-gray-700">{$_("phone")}</label>
autocomplete="off" <input
placeholder={$_("phone")} autocomplete="off"
type="tel" placeholder={$_("phone")}
class:border-red-500={!isPhoneValidOrEmpty} type="tel"
class:focus:border-red-500={!isPhoneValidOrEmpty} class:border-red-500={!isPhoneValidOrEmpty}
class:focus:ring-red-500={!isPhoneValidOrEmpty} class:focus:border-red-500={!isPhoneValidOrEmpty}
bind:value={editable.phone} class:focus:ring-red-500={!isPhoneValidOrEmpty}
name="phone" bind:value={editable.phone}
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm: border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" name="phone"
/> class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm: border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"
{#if !isPhoneValidOrEmpty} />
<span {#if !isPhoneValidOrEmpty}
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" <span
> class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
{$_("valid-international-phone-number-is-required")} >
</span> {$_("valid-international-phone-number-is-required")}
{/if} </span>
</div> {/if}
<div class="flex items-start mt-2"> </div>
<div class="flex items-center h-5"> <div class="flex items-start mt-2">
<input <div class="flex items-center h-5">
bind:checked={editable.address_checked} <input
id="comments" bind:checked={editable.address_checked}
name="comments" id="comments"
type="checkbox" name="comments"
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" type="checkbox"
/> class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
</div> />
<div class="ml-3"> </div>
<label for="comments" class="font-medium text-gray-700" <div class="ml-3">
>{$_("receipt-needed")}</label <label for="comments" class="font-medium text-gray-700"
> >{$_("receipt-needed")}</label
</div> >
</div> </div>
{#if editable.address_checked === true} </div>
<div class="col-span-6"> {#if editable.address_checked === true}
<label for="address1" class="block font-medium text-gray-700" <div class="col-span-6">
>{$_("address")}</label <label for="address1" class="block font-medium text-gray-700"
> >{$_("address")}</label
<input >
autocomplete="off" <input
placeholder="Address" autocomplete="off"
class:border-red-500={!isAddress1Valid} placeholder="Address"
class:focus:border-red-500={!isAddress1Valid} class:border-red-500={!isAddress1Valid}
class:focus:ring-red-500={!isAddress1Valid} class:focus:border-red-500={!isAddress1Valid}
bind:value={editable.address.address1} class:focus:ring-red-500={!isAddress1Valid}
type="text" bind:value={editable.address.address1}
name="address1" type="text"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm: border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" name="address1"
/> class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm: border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"
{#if !isAddress1Valid} />
<span {#if !isAddress1Valid}
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" <span
> class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
{$_("address-is-required")} >
</span> {$_("address-is-required")}
{/if} </span>
</div> {/if}
<div class="col-span-6"> </div>
<label for="address2" class="block font-medium text-gray-700" <div class="col-span-6">
>{$_("apartment-suite-etc")}</label <label for="address2" class="block font-medium text-gray-700"
> >{$_("apartment-suite-etc")}</label
<input >
autocomplete="off" <input
placeholder={$_("apartment-suite-etc")} autocomplete="off"
bind:value={editable.address.address2} placeholder={$_("apartment-suite-etc")}
type="text" bind:value={editable.address.address2}
name="address2" type="text"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm: border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" name="address2"
/> class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm: border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"
</div> />
<div class="col-span-6"> </div>
<label for="zipcode" class="block font-medium text-gray-700" <div class="col-span-6">
>{$_("zip-postal-code")}</label <label for="zipcode" class="block font-medium text-gray-700"
> >{$_("zip-postal-code")}</label
<input >
autocomplete="off" <input
placeholder={$_("zip-postal-code")} autocomplete="off"
class:border-red-500={!iszipcodevalid} placeholder={$_("zip-postal-code")}
class:focus:border-red-500={!iszipcodevalid} class:border-red-500={!iszipcodevalid}
class:focus:ring-red-500={!iszipcodevalid} class:focus:border-red-500={!iszipcodevalid}
bind:value={editable.address.postalcode} class:focus:ring-red-500={!iszipcodevalid}
type="text" bind:value={editable.address.postalcode}
name="zipcode" type="text"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm: border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" name="zipcode"
/> class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm: border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"
{#if !iszipcodevalid} />
<span {#if !iszipcodevalid}
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" <span
> class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
{$_("valid-zipcode-postal-code-is-required")} >
</span> {$_("valid-zipcode-postal-code-is-required")}
{/if} </span>
</div> {/if}
<div class="col-span-6"> </div>
<label for="city" class="block font-medium text-gray-700" <div class="col-span-6">
>{$_("city")}</label <label for="city" class="block font-medium text-gray-700"
> >{$_("city")}</label
<input >
autocomplete="off" <input
placeholder={$_("city")} autocomplete="off"
class:border-red-500={!iscityvalid} placeholder={$_("city")}
class:focus:border-red-500={!iscityvalid} class:border-red-500={!iscityvalid}
class:focus:ring-red-500={!iscityvalid} class:focus:border-red-500={!iscityvalid}
bind:value={editable.address.city} class:focus:ring-red-500={!iscityvalid}
type="text" bind:value={editable.address.city}
name="city" type="text"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm: border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" name="city"
/> class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm: border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"
{#if !iscityvalid} />
<span {#if !iscityvalid}
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" <span
> class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
{$_("valid-city-is-required")} >
</span> {$_("valid-city-is-required")}
{/if} </span>
</div> {/if}
{/if} </div>
</section> {/if}
</section>
{:catch error} {:catch error}
<PromiseError {error} /> <PromiseError {error} />
{/await} {/await}

View File

@ -1,244 +1,202 @@
<script> <script>
import { getLocaleFromNavigator, _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import { import {
RunnerCardService, RunnerCardService,
RunnerOrganizationService, RunnerOrganizationService,
RunnerTeamService, RunnerTeamService,
} from "@odit/lfk-client-js"; } from "@odit/lfk-client-js";
import toast from 'svelte-french-toast' import toast from "svelte-french-toast";
import DocumentServer from "./DocumentServer.ts" import DocumentServer from "./DocumentServer.ts";
import { init } from "@paralleldrive/cuid2"; import { init } from "@paralleldrive/cuid2";
const createId = init({ length: 10, fingerprint: "lfk-frontend" }); const createId = init({ length: 10, fingerprint: "lfk-frontend" });
const documentServer = new DocumentServer(config.baseurl_documentserver,config.documentserver_key); const documentServer = new DocumentServer(
config.baseurl_documentserver,
config.documentserver_key
);
export let cards_show = false; export let cards_show = false;
export let generate_cards = []; export let generate_cards = [];
export let generate_runners = []; export let generate_runners = [];
export let generate_orgs = []; export let generate_orgs = [];
export let generate_teams = []; export let generate_teams = [];
$: cards_dropdown_open = false;
document.addEventListener("click", function (e) {
if (
e.target.parentNode?.parentNode?.id != "cards:dropdown" &&
e.target.parentNode?.parentNode?.id != "cards:dropdown:menu"
) {
cards_dropdown_open = false;
}
});
function download (blob, fileName){ function download(blob, fileName) {
const url = window.URL.createObjectURL(blob); const url = window.URL.createObjectURL(blob);
let a = document.createElement("a"); let a = document.createElement("a");
a.href = url; a.href = url;
a.download = fileName; a.download = fileName;
document.body.appendChild(a); document.body.appendChild(a);
a.click(); a.click();
a.remove(); a.remove();
toast.dismiss(); toast.dismiss();
toast($_("pdf-successfully-generated")); toast($_("pdf-successfully-generated"));
} }
function generateRunnerCards(locale) { function generateRunnerCards(locale) {
cards_dropdown_open = false; if (generate_orgs.length > 0) {
generateOrgCards(locale);
} else if (generate_teams.length > 0) {
generateTeamCards(locale);
} else if (generate_runners.length > 0) {
generateRunnersCards(locale);
} else {
generateCards(locale);
}
}
if (generate_orgs.length > 0) { function generateCards(locale) {
generateOrgCards(locale); toast.loading($_("generating-pdf"));
} else if (generate_teams.length > 0) { documentServer
generateTeamCards(locale); .generateCards(generate_cards, locale)
} else if (generate_runners.length > 0) { .then((blob) => {
generateRunnersCards(locale); download(blob, `${$_("runnercards")}-${locale}-${createId()}.pdf`);
} else { })
generateCards(locale); .catch((err) => {
} console.error(err);
} });
}
function generateCards(locale) { async function generateRunnersCards(locale) {
toast.loading($_("generating-pdf")); toast.loading($_("generating-pdf"));
documentServer.generateCards(generate_cards, locale) const current_cards = await RunnerCardService.runnerCardControllerGetAll();
.then((blob) => { let cards = [];
download(blob, `${$_("runnercards")}-${locale}-${createId()}.pdf`); for (let runner of generate_runners) {
}) let card = current_cards.find((c) => c.runner?.id == runner.id);
.catch((err) => { if (!card) {
console.error(err); card = await RunnerCardService.runnerCardControllerPost({
}); runner: runner.id,
} });
}
cards.push(card);
}
documentServer
.generateCards(cards, locale)
.then((blob) => {
let fileName = `${$_("runnercards")}-${locale}-${createId()}.pdf`;
if (generate_runners.length == 1) {
fileName = `${$_("runnercards")}_${generate_runners[0].firstname}_${
generate_runners[0].lastname
}-${locale}-${createId()}.pdf`;
}
download(blob, fileName);
})
.catch((err) => {});
}
async function generateRunnersCards(locale) { async function generateTeamCards(locale) {
toast.loading($_("generating-pdf")); toast.loading($_("generating-pdfs"));
const current_cards = await RunnerCardService.runnerCardControllerGetAll(); let count = 0;
let cards = []; const current_cards = await RunnerCardService.runnerCardControllerGetAll();
for (let runner of generate_runners) { for (const t of generate_teams) {
let card = current_cards.find((c) => c.runner?.id == runner.id); const runners = await RunnerTeamService.runnerTeamControllerGetRunners(
if (!card) { t.id
card = await RunnerCardService.runnerCardControllerPost({ );
runner: runner.id, let cards = [];
}); for (let runner of runners) {
} let card = current_cards.find((c) => c.runner?.id == runner.id);
cards.push(card); if (!card) {
} card = await RunnerCardService.runnerCardControllerPost({
documentServer.generateCards(cards, locale) runner: runner.id,
.then((blob) => { });
let fileName = `${$_("runnercards")}-${locale}-${createId()}.pdf`; }
if (generate_runners.length == 1) { cards.push(card);
fileName = `${$_("runnercards")}_${generate_runners[0].firstname}_${ }
generate_runners[0].lastname documentServer
}-${locale}-${createId()}.pdf`; .generateCards(cards, locale)
} .then((blob) => {
download(blob, fileName); download(
}) blob,
.catch((err) => {}); `${$_("runnercards")}_${t.name}-${locale}-${createId()}.pdf`
} );
})
.catch((err) => {});
}
}
async function generateTeamCards(locale) { async function generateOrgCards(locale) {
toast.loading($_("generating-pdfs")); toast.loading($_("generating-pdfs"));
let count = 0; const current_cards = await RunnerCardService.runnerCardControllerGetAll();
const current_cards = await RunnerCardService.runnerCardControllerGetAll(); let count = 0;
for (const t of generate_teams) { let count_orgs = 0;
const runners = await RunnerTeamService.runnerTeamControllerGetRunners( for (const o of generate_orgs) {
t.id count_orgs++;
); let count = 0;
let cards = []; let runners =
for (let runner of runners) { await RunnerOrganizationService.runnerOrganizationControllerGetRunners(
let card = current_cards.find((c) => c.runner?.id == runner.id); o.id,
if (!card) { true
card = await RunnerCardService.runnerCardControllerPost({ );
runner: runner.id, let cards = [];
}); for (let runner of runners) {
} let card = current_cards.find((c) => c.runner?.id == runner.id);
cards.push(card); if (!card) {
} card = await RunnerCardService.runnerCardControllerPost({
documentServer.generateCards(cards, locale) runner: runner.id,
.then((blob) => { });
download(blob, `${$_("runnercards")}_${ }
t.name cards.push(card);
}-${locale}-${createId()}.pdf`) }
}) await documentServer
.catch((err) => {}); .generateCards(cards, locale)
} .then((blob) => {
} download(
blob,
async function generateOrgCards(locale) { `${$_("runnercards")}_${o.name}_direct-${locale}-${createId()}.pdf`
toast.loading($_("generating-pdfs")); );
const current_cards = await RunnerCardService.runnerCardControllerGetAll(); })
let count = 0; .catch((err) => {});
let count_orgs = 0; for (const t of o.teams) {
for (const o of generate_orgs) { count++;
count_orgs++; let runners = await RunnerTeamService.runnerTeamControllerGetRunners(
let count = 0; t.id
let runners = );
await RunnerOrganizationService.runnerOrganizationControllerGetRunners( let cards = [];
o.id, for (let runner of runners) {
true let card = current_cards.find((c) => c.runner?.id == runner.id);
); if (!card) {
let cards = []; card = await RunnerCardService.runnerCardControllerPost({
for (let runner of runners) { runner: runner.id,
let card = current_cards.find((c) => c.runner?.id == runner.id); });
if (!card) { }
card = await RunnerCardService.runnerCardControllerPost({ cards.push(card);
runner: runner.id, }
}); await documentServer
} .generateCards(cards, locale)
cards.push(card); .then((blob) => {
} download(
await documentServer.generateCards(cards, locale) blob,
.then((blob) => { `${$_("runnercards")}_${o.name}_${
download(blob, `${$_("runnercards")}_${ t.name
o.name }-${locale}-${createId()}.pdf`
}_direct-${locale}-${createId()}.pdf`) );
}) })
.catch((err) => {}); .catch((err) => {});
for (const t of o.teams) { }
count++; }
let runners = await RunnerTeamService.runnerTeamControllerGetRunners( }
t.id
);
let cards = [];
for (let runner of runners) {
let card = current_cards.find((c) => c.runner?.id == runner.id);
if (!card) {
card = await RunnerCardService.runnerCardControllerPost({
runner: runner.id,
});
}
cards.push(card);
}
await documentServer.generateCards(cards, locale)
.then((blob) => {
download(blob, `${$_("runnercards")}_${o.name}_${
t.name
}-${locale}-${createId()}.pdf`)
})
.catch((err) => {});
}
}
}
</script> </script>
{#if cards_show} {#if cards_show}
<div id="cards:dropdown" class="relative inline-block"> <div>
<div> <p class="text-base">{$_("generate-runnercards")}</p>
<button <div class="inline-flex rounded-lg shadow-2xs">
on:click={() => { <button
cards_dropdown_open = !cards_dropdown_open; on:click={() => {
}} generateRunnerCards("de");
type="button" }}
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-gray-600 text-base font-medium text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 sm:ml-3 sm:w-auto sm:text-sm inline-flex" class="py-3 px-4 inline-flex items-center gap-x-2 -ms-px first:rounded-s-lg first:ms-0 last:rounded-e-lg text-sm font-medium focus:z-10 border border-gray-200 bg-blue-600 text-white shadow-2xs hover:bg-blue-800 focus:outline-hidden focus:bg-blue-800 disabled:opacity-50 disabled:pointer-events-none"
id="options-menu" >
aria-haspopup="true" DE
aria-expanded="true" </button>
> <button
{$_("generate-runnercards")} on:click={() => {
<svg generateRunnerCards("en");
xmlns="http://www.w3.org/2000/svg" }}
width="24" class="py-3 px-4 inline-flex items-center gap-x-2 -ms-px first:rounded-s-lg first:ms-0 last:rounded-e-lg text-sm font-medium focus:z-10 border border-gray-200 bg-blue-600 text-white shadow-2xs hover:bg-blue-800 focus:outline-hidden focus:bg-blue-800 disabled:opacity-50 disabled:pointer-events-none"
height="24" >
viewBox="0 0 24 24" EN
class="-mr-1 ml-2 h-5 w-5" </button>
><path fill="none" d="M0 0h24v24H0z" /> </div>
<path </div>
fill="currentColor"
d="M3 19h18v2H3v-2zm10-5.83l6.07-6.07 1.42 1.41L12 17 3.52 8.52l1.4-1.42L11 13.17V2h2v11.17z"
/></svg
>
</button>
</div>
{#if cards_dropdown_open}
<div
class="origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 z-10"
id="cards:dropdown:menu"
>
<div
class="py-1"
role="menu"
aria-orientation="vertical"
aria-labelledby="options-menu"
>
<span class="block w-full text-left px-4 py-2 text-sm text-gray-700"
>{$_("select-language")}</span
>
<button
on:click={() => {
generateRunnerCards("de");
}}
type="submit"
class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
role="menuitem"
>
{$_("german")}
</button>
<button
on:click={() => {
generateRunnerCards("en");
}}
type="submit"
class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
role="menuitem"
>
{$_("english")}
</button>
</div>
</div>
{/if}
</div>
{/if} {/if}

View File

@ -1,224 +1,180 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import { import {
DonationService, DonationService,
RunnerTeamService, RunnerTeamService,
RunnerOrganizationService, RunnerOrganizationService,
} from "@odit/lfk-client-js"; } from "@odit/lfk-client-js";
import { init } from "@paralleldrive/cuid2"; import { init } from "@paralleldrive/cuid2";
import toast from "svelte-french-toast"; import toast from "svelte-french-toast";
import DocumentServer from "./DocumentServer"; import DocumentServer from "./DocumentServer";
const createId = init({ length: 10, fingerprint: "lfk-frontend" }); const createId = init({ length: 10, fingerprint: "lfk-frontend" });
const documentServer = new DocumentServer(config.baseurl_documentserver,config.documentserver_key); const documentServer = new DocumentServer(
config.baseurl_documentserver,
config.documentserver_key
);
export let certificates_show = false;
export let generate_runners = [];
export let generate_orgs = [];
export let generate_teams = [];
export let certificates_show = false; function generateCertificates(locale) {
export let generate_runners = []; if (generate_orgs.length > 0) {
export let generate_orgs = []; generateOrgCertificates(locale);
export let generate_teams = []; } else if (generate_teams.length > 0) {
$: certificates_dropdown_open = false; generateTeamCertificates(locale);
document.addEventListener("click", function (e) { } else {
if ( generateRunnerCertificates(locale);
e.target.parentNode?.parentNode?.id != "certificates:dropdown" && }
e.target.parentNode?.parentNode?.id != "certificates:dropdown:menu" }
) { function download(blob, fileName) {
certificates_dropdown_open = false; const url = window.URL.createObjectURL(blob);
} let a = document.createElement("a");
}); a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
a.remove();
toast.dismiss();
toast($_("pdf-successfully-generated"));
}
function generateCertificates(locale) { async function generateRunnerCertificates(locale) {
certificates_dropdown_open = false; toast.loading($_("generating-pdf"));
const current_donations =
(await DonationService.donationControllerGetAll()) || [];
let certificateRunners = [];
for (let runner of generate_runners) {
runner.distanceDonations =
current_donations.filter((d) => d.runner?.id == runner.id) || [];
certificateRunners.push(runner);
}
documentServer
.generateCertificates(certificateRunners, locale)
.then((blob) => {
let fileName = `${$_("certificates")}-${locale}.pdf`;
if (generate_runners.length == 1) {
fileName = `${$_("certificates")}_${
generate_runners[0].firstname
}_${generate_runners[0].lastname}-${locale}-${createId()}.pdf`;
}
download(blob, fileName);
})
.catch((err) => {});
}
if (generate_orgs.length > 0) { async function generateTeamCertificates(locale) {
generateOrgCertificates(locale); toast.loading($_("generating-pdfs"));
} else if (generate_teams.length > 0) { let count = 0;
generateTeamCertificates(locale); const current_donations =
} else { (await DonationService.donationControllerGetAll()) || [];
generateRunnerCertificates(locale); for (const t of generate_teams) {
} const runners = await RunnerTeamService.runnerTeamControllerGetRunners(
} t.id
function download (blob, fileName){ );
const url = window.URL.createObjectURL(blob); let certificateRunners = [];
let a = document.createElement("a"); for (let runner of runners) {
a.href = url; runner.distanceDonations =
a.download = fileName; current_donations.filter((d) => d.runner?.id == runner.id) || [];
document.body.appendChild(a); certificateRunners.push(runner);
a.click(); }
a.remove(); documentServer
toast.dismiss(); .generateCertificates(certificateRunners, locale)
toast($_("pdf-successfully-generated")); .then((blob) => {
} count++;
download(
blob,
`${$_("certificates")}_${t.name}-${locale}-${createId()}.pdf`
);
})
.catch((err) => {});
}
}
async function generateRunnerCertificates(locale) { async function generateOrgCertificates(locale) {
toast.loading($_("generating-pdf")); toast.loading($_("generating-pdfs"));
const current_donations = const current_donations =
(await DonationService.donationControllerGetAll()) || []; (await DonationService.donationControllerGetAll()) || [];
let certificateRunners = []; let count = 0;
for (let runner of generate_runners) { let count_orgs = 0;
runner.distanceDonations = for (const o of generate_orgs) {
current_donations.filter((d) => d.runner?.id == runner.id) || []; count_orgs++;
certificateRunners.push(runner); let count = 0;
} let runners =
documentServer.generateCertificates(certificateRunners, locale) await RunnerOrganizationService.runnerOrganizationControllerGetRunners(
.then((blob) => { o.id,
let fileName = `${$_("certificates")}-${locale}.pdf` true
if (generate_runners.length == 1) { );
fileName = `${$_("certificates")}_${ let certificateRunners = [];
generate_runners[0].firstname for (let runner of runners) {
}_${generate_runners[0].lastname}-${locale}-${createId()}.pdf`; runner.distanceDonations =
} current_donations.filter((d) => d.runner?.id == runner.id) || [];
download(blob, fileName); certificateRunners.push(runner);
}) }
.catch((err) => {}); await documentServer
} .generateCertificates(certificateRunners, locale)
.then((blob) => {
async function generateTeamCertificates(locale) { download(
toast.loading($_("generating-pdfs")); blob,
let count = 0; `${$_("certificates")}_${o.name}-${locale}-${createId()}.pdf`
const current_donations = );
(await DonationService.donationControllerGetAll()) || []; })
for (const t of generate_teams) { .catch((err) => {});
const runners = await RunnerTeamService.runnerTeamControllerGetRunners( for (const t of o.teams) {
t.id count++;
); let runners = await RunnerTeamService.runnerTeamControllerGetRunners(
let certificateRunners = []; t.id
for (let runner of runners) { );
runner.distanceDonations = let certificateRunners = [];
current_donations.filter((d) => d.runner?.id == runner.id) || []; for (let runner of runners) {
certificateRunners.push(runner); runner.distanceDonations =
} current_donations.filter((d) => d.runner?.id == runner.id) || [];
documentServer.generateCertificates(certificateRunners, locale) certificateRunners.push(runner);
.then((blob) => { }
count++; await documentServer
download(blob, `${$_("certificates")}_${ .generateCertificates(certificateRunners, locale)
t.name .then((blob) => {
}-${locale}-${createId()}.pdf`) download(
}) blob,
.catch((err) => {}); `${$_("certificates")}_${o.name}_${
} t.name
} }-${locale}-${createId()}.pdf`
);
async function generateOrgCertificates(locale) { if (
toast.loading($_("generating-pdfs")); count === o.teams.length &&
const current_donations = count_orgs === generate_orgs.length
(await DonationService.donationControllerGetAll()) || []; ) {
let count = 0; toast.dismiss();
let count_orgs = 0; toast($_("pdfs-successfully-generated"));
for (const o of generate_orgs) { }
count_orgs++; })
let count = 0; .catch((err) => {});
let runners = }
await RunnerOrganizationService.runnerOrganizationControllerGetRunners( }
o.id, }
true
);
let certificateRunners = [];
for (let runner of runners) {
runner.distanceDonations =
current_donations.filter((d) => d.runner?.id == runner.id) || [];
certificateRunners.push(runner);
}
await documentServer.generateCertificates(certificateRunners, locale)
.then((blob) => {
download(blob, `${$_("certificates")}_${
o.name
}-${locale}-${createId()}.pdf`)
})
.catch((err) => {});
for (const t of o.teams) {
count++;
let runners = await RunnerTeamService.runnerTeamControllerGetRunners(
t.id
);
let certificateRunners = [];
for (let runner of runners) {
runner.distanceDonations =
current_donations.filter((d) => d.runner?.id == runner.id) || [];
certificateRunners.push(runner);
}
await documentServer.generateCertificates(certificateRunners, locale)
.then((blob) => {
download(blob, `${$_("certificates")}_${o.name}_${
t.name
}-${locale}-${createId()}.pdf`)
if (
count === o.teams.length &&
count_orgs === generate_orgs.length
) {
toast.dismiss();
toast($_("pdfs-successfully-generated"));
}
})
.catch((err) => {});
}
}
}
</script> </script>
{#if certificates_show} {#if certificates_show}
<div id="certificates:dropdown" class="relative inline-block"> <div>
<div> <p class="text-base">{$_("generate-runner-certificates")}</p>
<button <div class="inline-flex rounded-lg shadow-2xs">
on:click={() => { <button
certificates_dropdown_open = !certificates_dropdown_open; on:click={() => {
}} generateCertificates("de");
type="button" }}
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-gray-600 text-base font-medium text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 sm:ml-3 sm:w-auto sm:text-sm inline-flex" class="py-3 px-4 inline-flex items-center gap-x-2 -ms-px first:rounded-s-lg first:ms-0 last:rounded-e-lg text-sm font-medium focus:z-10 border border-gray-200 bg-blue-600 text-white shadow-2xs hover:bg-blue-800 focus:outline-hidden focus:bg-blue-800 disabled:opacity-50 disabled:pointer-events-none"
id="options-menu" >
aria-haspopup="true" DE
aria-expanded="true" </button>
> <button
{$_("generate-runner-certificates")} on:click={() => {
<svg generateCertificates("en");
xmlns="http://www.w3.org/2000/svg" }}
width="24" class="py-3 px-4 inline-flex items-center gap-x-2 -ms-px first:rounded-s-lg first:ms-0 last:rounded-e-lg text-sm font-medium focus:z-10 border border-gray-200 bg-blue-600 text-white shadow-2xs hover:bg-blue-800 focus:outline-hidden focus:bg-blue-800 disabled:opacity-50 disabled:pointer-events-none"
height="24" >
viewBox="0 0 24 24" EN
class="-mr-1 ml-2 h-5 w-5" </button>
><path fill="none" d="M0 0h24v24H0z" /> </div>
<path </div>
fill="currentColor"
d="M3 19h18v2H3v-2zm10-5.83l6.07-6.07 1.42 1.41L12 17 3.52 8.52l1.4-1.42L11 13.17V2h2v11.17z"
/></svg
>
</button>
</div>
{#if certificates_dropdown_open}
<div
class="origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 z-10"
id="certificates:dropdown:menu"
>
<div
class="py-1"
role="menu"
aria-orientation="vertical"
aria-labelledby="options-menu"
>
<span class="block w-full text-left px-4 py-2 text-sm text-gray-700"
>{$_("select-language")}</span
>
<button
on:click={() => {
generateCertificates("de");
}}
type="submit"
class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
role="menuitem"
>
{$_("german")}
</button>
<button
on:click={() => {
generateCertificates("en");
}}
type="submit"
class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
role="menuitem"
>
{$_("english")}
</button>
</div>
</div>
{/if}
</div>
{/if} {/if}

View File

@ -1,188 +1,143 @@
<script> <script>
import { getLocaleFromNavigator, _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import { import {
RunnerOrganizationService, RunnerOrganizationService,
RunnerTeamService, RunnerTeamService,
} from "@odit/lfk-client-js"; } from "@odit/lfk-client-js";
import DocumentServer from "./DocumentServer"; import DocumentServer from "./DocumentServer";
import { init } from "@paralleldrive/cuid2"; import { init } from "@paralleldrive/cuid2";
import toast from "svelte-french-toast"; import toast from "svelte-french-toast";
const createId = init({ length: 10, fingerprint: "lfk-frontend" }); const createId = init({ length: 10, fingerprint: "lfk-frontend" });
const documentServer = new DocumentServer(config.baseurl_documentserver,config.documentserver_key); const documentServer = new DocumentServer(
config.baseurl_documentserver,
config.documentserver_key
);
export let sponsoring_contracts_show = false;
export let generate_runners = [];
export let generate_orgs = [];
export let generate_teams = [];
export let sponsoring_contracts_show = false; function generateSponsoringContract(locale) {
export let generate_runners = []; if (generate_orgs.length > 0) {
export let generate_orgs = []; generateOrgContracts(locale);
export let generate_teams = []; } else if (generate_teams.length > 0) {
$: sponsoring_contracts_download_open = false; generateTeamContracts(locale);
document.addEventListener("click", function (e) { } else {
if ( generateRunnerContracts(locale);
e.target.parentNode?.parentNode?.id != "sponsoring:dropdown" && }
e.target.parentNode?.parentNode?.id != "sponsoring:dropdown:menu" }
) { function download(blob, fileName) {
sponsoring_contracts_download_open = false; const url = window.URL.createObjectURL(blob);
} let a = document.createElement("a");
}); a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
a.remove();
toast.dismiss();
toast($_("pdf-successfully-generated"));
}
function generateSponsoringContract(locale) { async function generateTeamContracts(locale) {
sponsoring_contracts_download_open = false; toast.loading($_("generating-pdfs"));
let count = 0;
for (const t of generate_teams) {
count++;
const runners = await RunnerTeamService.runnerTeamControllerGetRunners(
t.id
);
documentServer
.generateContracts(runners, locale)
.then((blob) => {
download(
blob,
`${$_("sponsorings")}_${t.name}-${locale}-${createId()}.pdf`
);
})
.catch((err) => {});
}
}
if (generate_orgs.length > 0) { async function generateOrgContracts(locale) {
generateOrgContracts(locale); toast.loading($_("generating-pdf"));
} else if (generate_teams.length > 0) { let count_orgs = 0;
generateTeamContracts(locale); for (const o of generate_orgs) {
} else { count_orgs++;
generateRunnerContracts(locale); let count = 0;
} let runners =
} await RunnerOrganizationService.runnerOrganizationControllerGetRunners(
function download (blob, fileName){ o.id,
const url = window.URL.createObjectURL(blob); true
let a = document.createElement("a"); );
a.href = url; await documentServer
a.download = fileName; .generateContracts(runners, locale)
document.body.appendChild(a); .then((blob) => {
a.click(); download(
a.remove(); blob,
toast.dismiss(); `${$_("sponsorings")}_${o.name}_direct-${locale}-${createId()}.pdf`
toast($_("pdf-successfully-generated")); );
} })
.catch((err) => {});
for (const t of o.teams) {
count++;
let runners = await RunnerTeamService.runnerTeamControllerGetRunners(
t.id
);
await documentServer
.generateContracts(runners, locale)
.then((blob) => {
download(
blob,
`${$_("sponsorings")}_${o.name}_${
t.name
}-${locale}-${createId()}.pdf`
);
})
.catch((err) => {});
}
}
}
async function generateTeamContracts(locale) { function generateRunnerContracts(locale) {
toast.loading($_("generating-pdfs")); toast.loading($_("generating-pdf"));
let count = 0; documentServer
for (const t of generate_teams) { .generateContracts(generate_runners, locale)
count++; .then((blob) => {
const runners = await RunnerTeamService.runnerTeamControllerGetRunners( let fileName = `${$_("sponsorings")}-${locale}-${createId()}.pdf`;
t.id if (generate_runners.length == 1) {
); fileName = `${$_("sponsorings")}_${generate_runners[0].firstname}_${
documentServer.generateContracts(runners, locale) generate_runners[0].lastname
.then((blob) => { }-${locale}-${createId()}.pdf`;
download(blob, `${$_("sponsorings")}_${ }
t.name download(blob, fileName);
}-${locale}-${createId()}.pdf`) })
}) .catch((err) => {
.catch((err) => {}); console.error(err);
} });
} }
async function generateOrgContracts(locale) {
toast.loading($_("generating-pdf"));
let count_orgs = 0;
for (const o of generate_orgs) {
count_orgs++;
let count = 0;
let runners =
await RunnerOrganizationService.runnerOrganizationControllerGetRunners(
o.id,
true
);
await documentServer.generateContracts(runners, locale)
.then((blob) => {
download(blob, `${$_("sponsorings")}_${
o.name
}_direct-${locale}-${createId()}.pdf`)
})
.catch((err) => {});
for (const t of o.teams) {
count++;
let runners = await RunnerTeamService.runnerTeamControllerGetRunners(
t.id
);
await documentServer.generateContracts(runners, locale)
.then((blob) => {
download(blob, `${$_("sponsorings")}_${o.name}_${
t.name
}-${locale}-${createId()}.pdf`)
})
.catch((err) => {});
}
}
}
function generateRunnerContracts(locale) {
toast.loading($_("generating-pdf"));
documentServer.generateContracts(generate_runners, locale)
.then((blob) => {
let fileName = `${$_("sponsorings")}-${locale}-${createId()}.pdf`
if (generate_runners.length == 1) {
fileName= `${$_("sponsorings")}_${generate_runners[0].firstname}_${
generate_runners[0].lastname
}-${locale}-${createId()}.pdf`;
}
download(blob, fileName);
})
.catch((err) => {
console.error(err);
});
}
</script> </script>
{#if sponsoring_contracts_show} {#if sponsoring_contracts_show}
<div id="sponsoring:dropdown" class="relative inline-block"> <div>
<div> <p class="text-base">{$_("generate-sponsoring-contracts")}</p>
<button <div class="inline-flex rounded-lg shadow-2xs">
on:click={() => { <button
sponsoring_contracts_download_open = on:click={() => {
!sponsoring_contracts_download_open; generateSponsoringContract("de");
}} }}
type="button" class="py-3 px-4 inline-flex items-center gap-x-2 -ms-px first:rounded-s-lg first:ms-0 last:rounded-e-lg text-sm font-medium focus:z-10 border border-gray-200 bg-blue-600 text-white shadow-2xs hover:bg-blue-800 focus:outline-hidden focus:bg-blue-800 disabled:opacity-50 disabled:pointer-events-none"
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-gray-600 text-base font-medium text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 sm:ml-3 sm:w-auto sm:text-sm inline-flex" >
id="options-menu" DE
aria-haspopup="true" </button>
aria-expanded="true" <button
> on:click={() => {
{$_("generate-sponsoring-contracts")} generateSponsoringContract("en");
<svg }}
xmlns="http://www.w3.org/2000/svg" class="py-3 px-4 inline-flex items-center gap-x-2 -ms-px first:rounded-s-lg first:ms-0 last:rounded-e-lg text-sm font-medium focus:z-10 border border-gray-200 bg-blue-600 text-white shadow-2xs hover:bg-blue-800 focus:outline-hidden focus:bg-blue-800 disabled:opacity-50 disabled:pointer-events-none"
width="24" >
height="24" EN
viewBox="0 0 24 24" </button>
class="-mr-1 ml-2 h-5 w-5" </div>
><path fill="none" d="M0 0h24v24H0z" /> </div>
<path
fill="currentColor"
d="M3 19h18v2H3v-2zm10-5.83l6.07-6.07 1.42 1.41L12 17 3.52 8.52l1.4-1.42L11 13.17V2h2v11.17z"
/></svg
>
</button>
</div>
{#if sponsoring_contracts_download_open}
<div
class="origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 z-10"
id="sponsoring:dropdown:menu"
>
<div
class="py-1"
role="menu"
aria-orientation="vertical"
aria-labelledby="options-menu"
>
<span class="block w-full text-left px-4 py-2 text-sm text-gray-700"
>{$_("select-language")}</span
>
<button
on:click={() => {
generateSponsoringContract("de");
}}
type="submit"
class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
role="menuitem"
>
{$_("german")}
</button>
<button
on:click={() => {
generateSponsoringContract("en");
}}
type="submit"
class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
role="menuitem"
>
{$_("english")}
</button>
</div>
</div>
{/if}
</div>
{/if} {/if}

View File

@ -1,315 +1,318 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import GenerateSponsoringContracts from "../pdf_generation/GenerateSponsoringContracts.svelte"; import GenerateSponsoringContracts from "../pdf_generation/GenerateSponsoringContracts.svelte";
import GenerateRunnerCards from "../pdf_generation/GenerateRunnerCards.svelte"; import GenerateRunnerCards from "../pdf_generation/GenerateRunnerCards.svelte";
import GenerateRunnerCertificates from "../pdf_generation/GenerateRunnerCertificates.svelte"; import GenerateRunnerCertificates from "../pdf_generation/GenerateRunnerCertificates.svelte";
import store from "../../store"; import store from "../../store";
import { import {
RunnerService, RunnerService,
RunnerTeamService, RunnerTeamService,
RunnerOrganizationService, RunnerOrganizationService,
} from "@odit/lfk-client-js"; } from "@odit/lfk-client-js";
import PromiseError from "../base/PromiseError.svelte"; import PromiseError from "../base/PromiseError.svelte";
import isEmail from "validator/es/lib/isEmail"; import isEmail from "validator/es/lib/isEmail";
import Select from "svelte-select"; import Select from "svelte-select";
import toast from "svelte-french-toast"; import toast from "svelte-french-toast";
let data_loaded = false; let data_loaded = false;
export let params; export let params;
const runner_promise = RunnerService.runnerControllerGetOne(params.runnerid); const runner_promise = RunnerService.runnerControllerGetOne(params.runnerid);
$: delete_triggered = false; $: delete_triggered = false;
$: original_data_pdf = {}; $: original_data_pdf = {};
$: original_data = {}; $: original_data = {};
$: editable = {}; $: editable = {};
$: group = {}; $: group = {};
$: changes_performed = !( $: changes_performed = !(
JSON.stringify(original_data) == JSON.stringify(editable) JSON.stringify(original_data) == JSON.stringify(editable)
); );
$: isEmailValid = $: isEmailValid =
(editable.email || "") === "" || (editable.email || "") === "" ||
(editable.email && isEmail(editable.email || "")); (editable.email && isEmail(editable.email || ""));
$: isFirstnameValid = editable.firstname !== ""; $: isFirstnameValid = editable.firstname !== "";
$: isLastnameValid = editable.lastname !== ""; $: isLastnameValid = editable.lastname !== "";
$: save_enabled = $: save_enabled =
changes_performed && changes_performed &&
isFirstnameValid && isFirstnameValid &&
isLastnameValid && isLastnameValid &&
isEmailValid && isEmailValid &&
editable.group != null; editable.group != null;
$: sponsoring_contracts_show = true; $: sponsoring_contracts_show = true;
$: cards_show = true; $: cards_show = true;
$: certificates_show = true; $: certificates_show = true;
$: generate_runners = [original_data_pdf]; $: generate_runners = [original_data_pdf];
runner_promise.then((data) => { runner_promise.then((data) => {
data_loaded = true; data_loaded = true;
original_data_pdf = Object.assign(original_data_pdf, data); original_data_pdf = Object.assign(original_data_pdf, data);
data.group = data.group.id; data.group = data.group.id;
original_data = Object.assign(original_data, data); original_data = Object.assign(original_data, data);
editable = Object.assign(editable, original_data); editable = Object.assign(editable, original_data);
RunnerOrganizationService.runnerOrganizationControllerGetAll().then( RunnerOrganizationService.runnerOrganizationControllerGetAll().then(
(val) => { (val) => {
const orgs = val.map((r) => { const orgs = val.map((r) => {
return { label: r.name, value: r }; return { label: r.name, value: r };
}); });
groups = groups.concat(orgs); groups = groups.concat(orgs);
RunnerTeamService.runnerTeamControllerGetAll().then((val) => { RunnerTeamService.runnerTeamControllerGetAll().then((val) => {
const teams = val.map((r) => { const teams = val.map((r) => {
return { label: `${r.parentGroup.name} > ${r.name}`, value: r }; return { label: `${r.parentGroup.name} > ${r.name}`, value: r };
}); });
groups = groups.concat(teams); groups = groups.concat(teams);
group = groups.find((g) => g.value.id == editable.group); group = groups.find((g) => g.value.id == editable.group);
}); });
} }
); );
}); });
let groups = []; let groups = [];
function submit() { function submit() {
if (data_loaded === true && save_enabled) { if (data_loaded === true && save_enabled) {
toast.loading($_("updating-runner")); toast.loading($_("updating-runner"));
let postdata = {}; let postdata = {};
postdata = Object.assign(postdata, editable); postdata = Object.assign(postdata, editable);
if (postdata.phone === "") { if (postdata.phone === "") {
postdata.phone = null; postdata.phone = null;
} }
RunnerService.runnerControllerPut(original_data.id, postdata) RunnerService.runnerControllerPut(original_data.id, postdata)
.then((resp) => { .then((resp) => {
Object.assign(original_data, editable); Object.assign(original_data, editable);
original_data = original_data; original_data = original_data;
toast.dismiss(); toast.dismiss();
toast.success($_("runner-updated")); toast.success($_("runner-updated"));
}) })
.catch((err) => {}); .catch((err) => {});
} else { } else {
} }
} }
function deleteRunner() { function deleteRunner() {
RunnerService.runnerControllerRemove(original_data.id, true) RunnerService.runnerControllerRemove(original_data.id, true)
.then((resp) => { .then((resp) => {
location.replace("./"); location.replace("./");
}) })
.catch((err) => {}); .catch((err) => {});
} }
</script> </script>
{#await runner_promise} {#await runner_promise}
{$_("loading-runners")} {$_("loading-runners")}
{:then} {:then}
<section class="container p-5 select-none"> <section class="container p-5 select-none">
<div class="flex flex-row mb-4"> <div class="flex flex-row mb-4">
<div class="w-full"> <div class="w-full">
<nav class="w-full flex"> <nav class="w-full flex">
<ol class="list-none flex flex-row items-center justify-start"> <ol class="list-none flex flex-row items-center justify-start">
<li class="flex items-center"> <li class="flex items-center">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" viewBox="0 0 24 24"
class="flex-shrink-0 w-5 h-5 mr-2" class="flex-shrink-0 w-5 h-5 mr-2"
fill="currentColor" fill="currentColor"
width="24" width="24"
height="24" height="24"
><path fill="none" d="M0 0h24v24H0z" /> ><path fill="none" d="M0 0h24v24H0z" />
<path <path
d="M9.83 8.79L8 9.456V13H6V8.05h.015l5.268-1.918c.244-.093.51-.14.782-.131a2.616 2.616 0 0 1 2.427 1.82c.186.583.356.977.51 1.182A4.992 4.992 0 0 0 19 11v2a6.986 6.986 0 0 1-5.402-2.547l-.581 3.297L15 15.67V23h-2v-5.986l-2.05-1.987-.947 4.298-6.894-1.215.348-1.97 4.924.868L9.83 8.79zM13.5 5.5a2 2 0 1 1 0-4 2 2 0 0 1 0 4z" d="M9.83 8.79L8 9.456V13H6V8.05h.015l5.268-1.918c.244-.093.51-.14.782-.131a2.616 2.616 0 0 1 2.427 1.82c.186.583.356.977.51 1.182A4.992 4.992 0 0 0 19 11v2a6.986 6.986 0 0 1-5.402-2.547l-.581 3.297L15 15.67V23h-2v-5.986l-2.05-1.987-.947 4.298-6.894-1.215.348-1.97 4.924.868L9.83 8.79zM13.5 5.5a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"
/></svg /></svg
> >
</li> </li>
<li class="flex items-center"> <li class="flex items-center">
<a class="mr-2" href="./">{$_("runners")}</a><svg <a class="mr-2" href="./">{$_("runners")}</a><svg
stroke="currentColor" stroke="currentColor"
fill="none" fill="none"
stroke-width="2" stroke-width="2"
viewBox="0 0 24 24" viewBox="0 0 24 24"
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
class="h-3 w-3 mr-2 stroke-current" class="h-3 w-3 mr-2 stroke-current"
height="1em" height="1em"
width="1em" width="1em"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
><line x1="5" y1="12" x2="19" y2="12" /> ><line x1="5" y1="12" x2="19" y2="12" />
<polyline points="12 5 19 12 12 19" /></svg <polyline points="12 5 19 12 12 19" /></svg
> >
</li> </li>
<li class="flex items-center"> <li class="flex items-center">
<span class="mr-2" <span class="mr-2"
>{original_data.firstname} >{original_data.firstname}
{original_data.middlename || ""} {original_data.middlename || ""}
{original_data.lastname}</span {original_data.lastname}</span
> >
</li> </li>
</ol> </ol>
</nav> </nav>
</div> </div>
</div> </div>
<div class="mb-8 text-3xl font-extrabold leading-tight"> <div class="mb-8 text-3xl font-extrabold leading-tight">
{original_data.firstname} {original_data.firstname}
{original_data.middlename || ""} {original_data.middlename || ""}
{original_data.lastname} {original_data.lastname}
<span data-id="runner_actions_${editable.id}"> <span
{#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:DELETE")} class="grid md:grid-cols-3 gap-1 md:gap-2"
{#if delete_triggered} data-id="runner_actions_${editable.id}"
<button >
on:click={deleteRunner} {#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:DELETE")}
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:w-auto sm:text-sm" <GenerateSponsoringContracts
>{$_("confirm-deletion")}</button bind:sponsoring_contracts_show
> bind:generate_runners
<button />
on:click={() => { <GenerateRunnerCards bind:cards_show bind:generate_runners />
delete_triggered = !delete_triggered; <GenerateRunnerCertificates
}} bind:certificates_show
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-400 text-base font-medium text-white sm:w-auto sm:text-sm" bind:generate_runners
>{$_("cancel")}</button />
> <div>
{/if} <p class="text-base">Aktionen</p>
<GenerateSponsoringContracts {#if delete_triggered}
bind:sponsoring_contracts_show <button
bind:generate_runners on:click={deleteRunner}
/> class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:w-auto sm:text-sm"
<GenerateRunnerCards bind:cards_show bind:generate_runners /> >{$_("confirm-deletion")}</button
<GenerateRunnerCertificates >
bind:certificates_show <button
bind:generate_runners on:click={() => {
/> delete_triggered = !delete_triggered;
{#if !delete_triggered} }}
<button class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-400 text-base font-medium text-white sm:w-auto sm:text-sm"
on:click={() => { >{$_("cancel")}</button
delete_triggered = true; >
}} {:else}
type="button" <button
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:w-auto sm:text-sm" on:click={() => {
>{$_("delete-runner")}</button delete_triggered = true;
> }}
{/if} type="button"
{/if} class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:w-auto sm:text-sm"
{#if !delete_triggered} >{$_("delete-runner")}</button
<button >
disabled={!save_enabled} <button
class:opacity-50={!save_enabled} disabled={!save_enabled}
type="button" class:opacity-50={!save_enabled}
on:click={submit} type="button"
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:w-auto sm:text-sm" on:click={submit}
>{$_("save-changes")}</button class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:w-auto sm:text-sm"
> >{$_("save-changes")}</button
{/if} >
</span> {/if}
</div> </div>
<!-- --> {/if}
<div class="text-sm w-full"> </span>
<label for="firstname" class="font-medium text-gray-700" </div>
>{$_("first-name")}</label <!-- -->
> <div class="text-sm w-full">
<input <label for="firstname" class="font-medium text-gray-700"
autocomplete="off" >{$_("first-name")}</label
placeholder={$_("first-name")} >
type="text" <input
class:border-red-500={!isFirstnameValid} autocomplete="off"
class:focus:border-red-500={!isFirstnameValid} placeholder={$_("first-name")}
class:focus:ring-red-500={!isFirstnameValid} type="text"
bind:value={editable.firstname} class:border-red-500={!isFirstnameValid}
name="firstname" class:focus:border-red-500={!isFirstnameValid}
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" class:focus:ring-red-500={!isFirstnameValid}
/> bind:value={editable.firstname}
{#if !isFirstnameValid} name="firstname"
<span class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" />
> {#if !isFirstnameValid}
{$_("first-name-is-required")} <span
</span> class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
{/if} >
</div> {$_("first-name-is-required")}
<div class="text-sm w-full"> </span>
<label for="middlename" class="font-medium text-gray-700" {/if}
>{$_("middle-name")}</label </div>
> <div class="text-sm w-full">
<input <label for="middlename" class="font-medium text-gray-700"
autocomplete="off" >{$_("middle-name")}</label
placeholder={$_("middle-name")} >
type="text" <input
bind:value={editable.middlename} autocomplete="off"
name="middlename" placeholder={$_("middle-name")}
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" type="text"
/> bind:value={editable.middlename}
</div> name="middlename"
<div class="text-sm w-full"> class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"
<label for="lastname" class="font-medium text-gray-700" />
>{$_("last-name")}</label </div>
> <div class="text-sm w-full">
<input <label for="lastname" class="font-medium text-gray-700"
autocomplete="off" >{$_("last-name")}</label
placeholder={$_("last-name")} >
type="text" <input
bind:value={editable.lastname} autocomplete="off"
class:border-red-500={!isLastnameValid} placeholder={$_("last-name")}
class:focus:border-red-500={!isLastnameValid} type="text"
class:focus:ring-red-500={!isLastnameValid} bind:value={editable.lastname}
name="lastname" class:border-red-500={!isLastnameValid}
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" class:focus:border-red-500={!isLastnameValid}
/> class:focus:ring-red-500={!isLastnameValid}
{#if !isLastnameValid} name="lastname"
<span class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" />
> {#if !isLastnameValid}
{$_("last-name-is-required")} <span
</span> class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
{/if} >
</div> {$_("last-name-is-required")}
<div class="text-sm w-full"> </span>
<label for="email" class="font-medium text-gray-700" {/if}
>{$_("e-mail-adress")}</label </div>
> <div class="text-sm w-full">
<input <label for="email" class="font-medium text-gray-700"
autocomplete="off" >{$_("e-mail-adress")}</label
placeholder={$_("e-mail-adress")} >
type="email" <input
bind:value={editable.email} autocomplete="off"
class:border-red-500={!isEmailValid} placeholder={$_("e-mail-adress")}
class:focus:border-red-500={!isEmailValid} type="email"
class:focus:ring-red-500={!isEmailValid} bind:value={editable.email}
name="email" class:border-red-500={!isEmailValid}
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" class:focus:border-red-500={!isEmailValid}
/> class:focus:ring-red-500={!isEmailValid}
{#if !isEmailValid} name="email"
<span class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" />
> {#if !isEmailValid}
{$_("valid-email-is-required")} <span
</span> class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
{/if} >
</div> {$_("valid-email-is-required")}
<div class="text-sm w-full"> </span>
<label for="phone" class="font-medium text-gray-700">{$_("phone")}</label> {/if}
<input </div>
autocomplete="off" <div class="text-sm w-full">
placeholder={$_("phone")} <label for="phone" class="font-medium text-gray-700">{$_("phone")}</label>
type="tel" <input
bind:value={editable.phone} autocomplete="off"
name="phone" placeholder={$_("phone")}
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" type="tel"
/> bind:value={editable.phone}
</div> name="phone"
<div class="text-sm w-full"> class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"
<span class="font-medium text-gray-700">{$_("group")}</span> />
<Select </div>
containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" <div class="text-sm w-full">
itemFilter={(label, filterText, option) => <span class="font-medium text-gray-700">{$_("group")}</span>
label.toLowerCase().includes(filterText.toLowerCase()) || <Select
option.id.value.toString().startsWith(filterText.toLowerCase())} containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"
items={groups} itemFilter={(label, filterText, option) =>
showChevron={true} label.toLowerCase().includes(filterText.toLowerCase()) ||
placeholder={$_("search-for-an-organization-or-team-by-name-or-id")} option.id.value.toString().startsWith(filterText.toLowerCase())}
noOptionsMessage={$_("no-organization-or-team-found")} items={groups}
bind:selectedValue={group} showChevron={true}
on:select={(selectedValue) => { placeholder={$_("search-for-an-organization-or-team-by-name-or-id")}
editable.group = selectedValue.detail.value.id; noOptionsMessage={$_("no-organization-or-team-found")}
}} bind:selectedValue={group}
on:clear={() => (editable.group = null)} on:select={(selectedValue) => {
/> editable.group = selectedValue.detail.value.id;
</div> }}
<div class="text-sm w-full"> on:clear={() => (editable.group = null)}
<span class="font-medium text-gray-700">{$_("distance")}</span> />
<br /> </div>
<span class="text-gray-700">{original_data.distance / 1000} km</span> <div class="text-sm w-full">
</div> <span class="font-medium text-gray-700">{$_("distance")}</span>
</section> <br />
<span class="text-gray-700">{original_data.distance / 1000} km</span>
</div>
</section>
{:catch error} {:catch error}
<PromiseError {error} /> <PromiseError {error} />
{/await} {/await}

View File

@ -151,19 +151,19 @@
{runner.value?.middlename || ""} {runner.value?.middlename || ""}
{runner.value?.lastname} {runner.value?.lastname}
#{original_data.id} #{original_data.id}
<span data-id="donation_actions_${original_data.id}"> <div data-id="donation_actions_${original_data.id}">
{#if store.state.jwtinfo.userdetails.permissions.includes("SCAN:DELETE")} {#if store.state.jwtinfo.userdetails.permissions.includes("SCAN:DELETE")}
{#if delete_triggered} {#if delete_triggered}
<button <button
on:click={deleteScan} on:click={deleteScan}
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:" class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:w-auto sm:text-sm"
>{$_("confirm-deletion")}</button >{$_("confirm-deletion")}</button
> >
<button <button
on:click={() => { on:click={() => {
delete_triggered = !delete_triggered; delete_triggered = !delete_triggered;
}} }}
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-400 text-base font-medium text-white sm:w-auto sm:" class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-400 text-base font-medium text-white sm:w-auto sm:text-sm"
>{$_("cancel")}</button >{$_("cancel")}</button
> >
{/if} {/if}
@ -173,7 +173,7 @@
delete_triggered = true; delete_triggered = true;
}} }}
type="button" type="button"
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:" class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:w-auto sm:text-sm"
>{$_("delete-scan")}</button >{$_("delete-scan")}</button
> >
{/if} {/if}
@ -184,11 +184,11 @@
class:opacity-50={!save_enabled} class:opacity-50={!save_enabled}
type="button" type="button"
on:click={submit} on:click={submit}
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:" class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:w-auto sm:text-sm"
>{$_("save-changes")}</button >{$_("save-changes")}</button
> >
{/if} {/if}
</span> </div>
</div> </div>
<!-- --> <!-- -->
<div class="w-full inline-flex"> <div class="w-full inline-flex">

View File

@ -105,8 +105,7 @@
</div> </div>
</div> </div>
<div class="mb-8 text-3xl font-extrabold leading-tight"> <div class="mb-8 text-3xl font-extrabold leading-tight">
#{original_data.id} <div data-id="stations_actions_${editable.id}">
<span data-id="stations_actions_${editable.id}">
{#if store.state.jwtinfo.userdetails.permissions.includes("STATION:DELETE")} {#if store.state.jwtinfo.userdetails.permissions.includes("STATION:DELETE")}
{#if delete_triggered} {#if delete_triggered}
<button <button
@ -143,7 +142,7 @@
>{$_("save-changes")}</button >{$_("save-changes")}</button
> >
{/if} {/if}
</span> </div>
</div> </div>
<!-- --> <!-- -->
<div class="text-sm w-full"> <div class="text-sm w-full">