Compare commits

...

15 Commits

Author SHA1 Message Date
827fb317bc chore(release): 1.12.1
All checks were successful
Build release images / build-container (push) Successful in 1m1s
2025-04-28 20:08:44 +02:00
edd5da89a7 fix(donations): Move amount to extra line 2025-04-28 20:08:08 +02:00
27187b428d fix(donations): Support anon donations in deletion modal 2025-04-28 20:06:27 +02:00
e28f543d89 feat(donation): Use new endpoint for creating anon donations 2025-04-28 20:06:07 +02:00
1ec8e2186b chore(deps): Fresh lock 2025-04-28 20:05:21 +02:00
657fb04f1b refactor(i18n): Shortened translation 2025-04-28 19:57:28 +02:00
dc1e6b7a67 fix(donations): Translate modal title 2025-04-28 19:56:47 +02:00
77a432817e refactor(i18n): Update translations 2025-04-28 19:56:00 +02:00
31a4ff9d90 fix(donations): Remove paid from anon donations 2025-04-28 19:55:47 +02:00
cb315d94fd chore(deps): Bump @odit/lfk-client-js 2025-04-28 19:52:39 +02:00
32f72df105 fix(donations): Don't show enter payment for anon donations 2025-04-28 19:40:57 +02:00
724e84441e feat(shared): Hide link while keeping width 2025-04-28 19:39:02 +02:00
ecd418c5db fix(donations): Don't show details for anon donations 2025-04-28 19:38:43 +02:00
5dcb4cb508 chore(release): 1.12.0
All checks were successful
Build release images / build-container (push) Successful in 1m10s
2025-04-28 11:05:59 +02:00
9c03e359a4 feat: anonymous donations
commit 8c56aa3c46
Merge: f642849d 1505080a
Author: Philipp Dormann <philipp@philippdormann.de>
Date:   Mon Apr 28 10:57:14 2025 +0200

    Merge branch 'dev' into feature/anonyme-spende

commit f642849dbb
Author: Philipp Dormann <philipp@philippdormann.de>
Date:   Mon Apr 28 10:56:45 2025 +0200

    wip

commit 7ac92ae6ca
Author: Philipp Dormann <philipp@philippdormann.de>
Date:   Mon Apr 28 10:45:47 2025 +0200

    wip

commit 95af9f9914
Author: Philipp Dormann <philipp@philippdormann.de>
Date:   Mon Apr 28 10:44:02 2025 +0200

    i18n

commit 1004aee16c
Author: Philipp Dormann <philipp@philippdormann.de>
Date:   Mon Apr 28 10:40:23 2025 +0200

    implement DonationDonor

commit 0baf2fc460
Author: Philipp Dormann <philipp@philippdormann.de>
Date:   Mon Apr 28 10:38:27 2025 +0200

    wip

commit 09b59175ee
Author: Philipp Dormann <philipp@philippdormann.de>
Date:   Mon Apr 28 10:35:45 2025 +0200

    wip

commit a21f61f3f3
Author: Philipp Dormann <philipp@philippdormann.de>
Date:   Mon Apr 28 10:24:07 2025 +0200

    wip
2025-04-28 11:05:40 +02:00
13 changed files with 462 additions and 360 deletions

View File

@@ -2,8 +2,34 @@
All notable changes to this project will be documented in this file. Dates are displayed in UTC. All notable changes to this project will be documented in this file. Dates are displayed in UTC.
#### [1.12.1](https://git.odit.services/lfk/frontend/compare/1.12.0...1.12.1)
- fix(donations): Don't show enter payment for anon donations [`32f72df`](https://git.odit.services/lfk/frontend/commit/32f72df10583a08efb26e0983c0c5c829ab03e19)
- chore(deps): Fresh lock [`1ec8e21`](https://git.odit.services/lfk/frontend/commit/1ec8e2186bdcd69c2acbc785feef4927973bc986)
- fix(donations): Support anon donations in deletion modal [`27187b4`](https://git.odit.services/lfk/frontend/commit/27187b428da1e757f85392d77d49670f51a19829)
- fix(donations): Don't show details for anon donations [`ecd418c`](https://git.odit.services/lfk/frontend/commit/ecd418c5db0910d64cdf5336d72a10ebff38e065)
- refactor(i18n): Update translations [`77a4328`](https://git.odit.services/lfk/frontend/commit/77a432817ef644ff1be8a5ebcd284b7d67f742bd)
- fix(donations): Remove paid from anon donations [`31a4ff9`](https://git.odit.services/lfk/frontend/commit/31a4ff9d909742df3ed3d6cecae57870e287afcc)
- fix(donations): Move amount to extra line [`edd5da8`](https://git.odit.services/lfk/frontend/commit/edd5da89a7e741d48078125f68b6dd6d3d88a7a1)
- refactor(i18n): Shortened translation [`657fb04`](https://git.odit.services/lfk/frontend/commit/657fb04f1b5b439ef5fac834740ba00548b758de)
- feat(donation): Use new endpoint for creating anon donations [`e28f543`](https://git.odit.services/lfk/frontend/commit/e28f543d89efff80a84131df59bfb26a5ea92d14)
- fix(donations): Translate modal title [`dc1e6b7`](https://git.odit.services/lfk/frontend/commit/dc1e6b7a67c4761a007ffe3b71fd851fb569fb7c)
- chore(deps): Bump @odit/lfk-client-js [`cb315d9`](https://git.odit.services/lfk/frontend/commit/cb315d94fd2331bd49aa6d54b9ca0bfbf11f00d9)
- feat(shared): Hide link while keeping width [`724e844`](https://git.odit.services/lfk/frontend/commit/724e84441e8b71b7d89a8c3804467edebfd58365)
#### [1.12.0](https://git.odit.services/lfk/frontend/compare/1.11.5...1.12.0)
> 28 April 2025
- feat: anonymous donations [`9c03e35`](https://git.odit.services/lfk/frontend/commit/9c03e359a4e8f43452475b02bcabcb354987ab75)
- refactor: use modern tailwindcss features [`1505080`](https://git.odit.services/lfk/frontend/commit/1505080afdd8b272b76584e2777df732001ce004)
- chore(release): 1.12.0 [`5dcb4cb`](https://git.odit.services/lfk/frontend/commit/5dcb4cb508b204c5634804811a9f37db78e764ce)
#### [1.11.5](https://git.odit.services/lfk/frontend/compare/1.11.4...1.11.5) #### [1.11.5](https://git.odit.services/lfk/frontend/compare/1.11.4...1.11.5)
> 25 April 2025
- chore(release): 1.11.5 [`8cb6093`](https://git.odit.services/lfk/frontend/commit/8cb6093f0b3474c0952a8a51a47683262fc31f8f)
- fix(cards): Update table for edit events [`27396e1`](https://git.odit.services/lfk/frontend/commit/27396e17f2cd8be72c9c8100afe6ec75ac66dceb) - fix(cards): Update table for edit events [`27396e1`](https://git.odit.services/lfk/frontend/commit/27396e17f2cd8be72c9c8100afe6ec75ac66dceb)
#### [1.11.4](https://git.odit.services/lfk/frontend/compare/1.11.3...1.11.4) #### [1.11.4](https://git.odit.services/lfk/frontend/compare/1.11.3...1.11.4)

View File

@@ -13,7 +13,7 @@
<body> <body>
<span style="display: none; visibility: hidden" id="buildinfo" <span style="display: none; visibility: hidden" id="buildinfo"
>RELEASE_INFO-1.11.5-RELEASE_INFO</span >RELEASE_INFO-1.12.1-RELEASE_INFO</span
> >
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>
<script src="/env.js"></script> <script src="/env.js"></script>

View File

@@ -1,6 +1,6 @@
{ {
"name": "@odit/lfk-frontend", "name": "@odit/lfk-frontend",
"version": "1.11.5", "version": "1.12.1",
"type": "module", "type": "module",
"scripts": { "scripts": {
"i18n-order": "node order.js", "i18n-order": "node order.js",
@@ -43,7 +43,7 @@
"dependencies": { "dependencies": {
"@bwip-js/browser": "^4.6.0", "@bwip-js/browser": "^4.6.0",
"@fontsource/athiti": "^5.2.5", "@fontsource/athiti": "^5.2.5",
"@odit/lfk-client-js": "1.2.4", "@odit/lfk-client-js": "1.2.5",
"@paralleldrive/cuid2": "2.2.2", "@paralleldrive/cuid2": "2.2.2",
"@tailwindcss/vite": "^4.1.4", "@tailwindcss/vite": "^4.1.4",
"@tanstack/svelte-table": "8.9.1", "@tanstack/svelte-table": "8.9.1",

10
pnpm-lock.yaml generated
View File

@@ -15,8 +15,8 @@ importers:
specifier: ^5.2.5 specifier: ^5.2.5
version: 5.2.5 version: 5.2.5
'@odit/lfk-client-js': '@odit/lfk-client-js':
specifier: 1.2.4 specifier: 1.2.5
version: 1.2.4 version: 1.2.5
'@paralleldrive/cuid2': '@paralleldrive/cuid2':
specifier: 2.2.2 specifier: 2.2.2
version: 2.2.2 version: 2.2.2
@@ -491,8 +491,8 @@ packages:
'@octokit/types@13.10.0': '@octokit/types@13.10.0':
resolution: {integrity: sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==} resolution: {integrity: sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==}
'@odit/lfk-client-js@1.2.4': '@odit/lfk-client-js@1.2.5':
resolution: {integrity: sha512-eJRsjtpMm/VsQ1v2I+inMWCZmzL+WoOvsA+hj8IGsyCVn0td+z/HAwQ0SuXXNZpLPL3qSlENHXjFNrgztExEgA==} resolution: {integrity: sha512-a5vwqpjFXB5cVOCmjC/tZVi9OXJS8aMesNidSqwK2cwA/oC5yTJAqxKXGDhq9k/JLLipVGDJdaKMYmYVzRWkgA==}
'@odit/license-exporter@0.2.0': '@odit/license-exporter@0.2.0':
resolution: {integrity: sha512-RRyfQzDLoyLQlGSd8ThJQ3h0fiCe4tkmm935AUvSVQWP+p88FcnI4iaktKBJJVBnIpDhkv/7sDSA5dFc/QMM5w==} resolution: {integrity: sha512-RRyfQzDLoyLQlGSd8ThJQ3h0fiCe4tkmm935AUvSVQWP+p88FcnI4iaktKBJJVBnIpDhkv/7sDSA5dFc/QMM5w==}
@@ -2412,7 +2412,7 @@ snapshots:
dependencies: dependencies:
'@octokit/openapi-types': 24.2.0 '@octokit/openapi-types': 24.2.0
'@odit/lfk-client-js@1.2.4': {} '@odit/lfk-client-js@1.2.5': {}
'@odit/license-exporter@0.2.0': '@odit/license-exporter@0.2.0':
dependencies: dependencies:

View File

@@ -1,340 +1,380 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import { clickOutside } from "../base/outsideclick"; import { clickOutside } from "../base/outsideclick";
import { import {
DonationService, DonationService,
DonorService, DonorService,
RunnerService, RunnerService,
} from "@odit/lfk-client-js"; } from "@odit/lfk-client-js";
import Select from "svelte-select"; import Select from "svelte-select";
import { createEventDispatcher, onMount } from "svelte"; import { createEventDispatcher, onMount } from "svelte";
import toast from "svelte-french-toast"; import toast from "svelte-french-toast";
export let modal_open; export let modal_open;
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
const getDonorLabel = (option) => const getDonorLabel = (option) =>
option.firstname + " " + (option.middlename || "") + " " + option.lastname; option.firstname + " " + (option.middlename || "") + " " + option.lastname;
const filterDonors = (label, filterText, option) => const filterDonors = (label, filterText, option) =>
label.toLowerCase().includes(filterText.toLowerCase()) || label.toLowerCase().includes(filterText.toLowerCase()) ||
option.value.id.toString().startsWith(filterText.toLowerCase()); option.value.id.toString().startsWith(filterText.toLowerCase());
$: donor = 0; $: donor = 0;
$: runner = 0; $: runner = 0;
$: donors = []; $: donors = [];
$: runners = []; $: runners = [];
$: is_fixed = false; $: type = "distance";
$: is_paid = false; $: is_paid = false;
$: amount_input = 0; $: amount_input = 0;
$: processed_last_submit = true; $: processed_last_submit = true;
$: is_amount_valid = amount_input > 0; $: is_amount_valid = amount_input > 0;
$: createbtnenabled = is_amount_valid; $: createbtnenabled = is_amount_valid;
(() => { (() => {
document.onkeydown = (e) => { document.onkeydown = (e) => {
e = e || window.event; e = e || window.event;
if (e.key === "Escape") { if (e.key === "Escape") {
modal_open = false; modal_open = false;
} }
if (e.keyCode === 13) { if (e.keyCode === 13) {
if (createbtnenabled === true) { if (createbtnenabled === true) {
createbtnenabled = false; createbtnenabled = false;
submit(); submit();
} }
} }
}; };
})(); })();
function submit() { function submit() {
if (processed_last_submit === true) { if (processed_last_submit === true) {
let amount_cent = Math.floor(amount_input * 100); let amount_cent = Math.floor(amount_input * 100);
processed_last_submit = false; processed_last_submit = false;
toast.loading($_("adding-donation")); toast.loading($_("adding-donation"));
if (is_fixed) { if (type === "fixed") {
let postdata = { let postdata = {
donor, donor,
amount: amount_cent, amount: amount_cent,
paidAmount: 0, paidAmount: 0,
}; };
if (is_paid) { if (is_paid) {
postdata.paidAmount = amount_cent; postdata.paidAmount = amount_cent;
} }
DonationService.donationControllerPostFixed(postdata) DonationService.donationControllerPostFixed(postdata)
.then((result) => { .then((result) => {
donor = donors[0].id || 0; donor = donors[0].id || 0;
runner = runners[0].id || 0; runner = runners[0].id || 0;
amount_input = 0; amount_input = 0;
modal_open = false; modal_open = false;
// //
toast.dismiss(); toast.dismiss();
toast.success($_("donation_added")); toast.success($_("donation_added"));
dispatch("created", { donations: [result] }); dispatch("created", { donations: [result] });
}) })
.catch((err) => { .catch((err) => {
// //
}) })
.finally(() => { .finally(() => {
processed_last_submit = true; processed_last_submit = true;
}); });
} else { } else if (type === "anonymous") {
let postdata = { let postdata = {
donor, amount: amount_cent,
runner, };
amountPerDistance: amount_cent, DonationService.donationControllerPostAnonymous(postdata)
}; .then((result) => {
DonationService.donationControllerPostDistance(postdata) amount_input = 0;
.then((result) => { modal_open = false;
donor = donors[0].id || 0; //
runner = runners[0].id || 0; toast.dismiss();
amount_input = 0; toast.success($_("donation_added"));
modal_open = false; dispatch("created", { donations: [result] });
// })
toast.dismiss(); .catch((err) => {
toast.success($_("donation_added")); //
dispatch("created", { donations: [result] }); })
}) .finally(() => {
.catch((err) => { processed_last_submit = true;
// });
}) } else if (type === "distance") {
.finally(() => { let postdata = {
processed_last_submit = true; donor,
}); runner,
} amountPerDistance: amount_cent,
} };
} DonationService.donationControllerPostDistance(postdata)
.then((result) => {
donor = donors[0].id || 0;
runner = runners[0].id || 0;
amount_input = 0;
modal_open = false;
//
toast.dismiss();
toast.success($_("donation_added"));
dispatch("created", { donations: [result] });
})
.catch((err) => {
//
})
.finally(() => {
processed_last_submit = true;
});
}
}
}
onMount(async () => { onMount(async () => {
donors = (await DonorService.donorControllerGetAll()).map( donors = (await DonorService.donorControllerGetAll()).map((r) => {
(r) => { return { label: getDonorLabel(r), value: r };
return { label: getDonorLabel(r), value: r }; });
} runners = (await RunnerService.runnerControllerGetAll()).map((r) => {
); return { label: getDonorLabel(r), value: r };
runners = (await RunnerService.runnerControllerGetAll()).map( });
(r) => { });
return { label: getDonorLabel(r), value: r };
}
);
});
</script> </script>
{#if modal_open} {#if modal_open}
<div <div
class="fixed z-10 inset-0 overflow-y-hidden" class="fixed z-10 inset-0 overflow-y-hidden"
use:clickOutside use:clickOutside
on:click_outside={() => { on:click_outside={() => {
modal_open = false; modal_open = false;
}} }}
> >
<div <div
class="flex items-end justify-center h-screen text-center sm:block p-0 lg:p-4" class="flex items-end justify-center h-screen text-center sm:block p-0 lg:p-4"
> >
<div class="fixed inset-0 transition-opacity" aria-hidden="true"> <div class="fixed inset-0 transition-opacity" aria-hidden="true">
<div <div
class="absolute inset-0 bg-gray-500 opacity-75" class="absolute inset-0 bg-neutral-500 opacity-75"
data-id="modal_backdrop" data-id="modal_backdrop"
/> />
</div> </div>
<span <span
class="hidden sm:inline-block sm:align-middle sm:h-screen" class="hidden sm:inline-block sm:align-middle sm:h-screen"
aria-hidden="true">&#8203;</span aria-hidden="true">&#8203;</span
> >
<div <div
class="inline-block align-bottom text-left shadow-xl transform transition-all sm:align-middle w-full lg:w-auto min-w-auto lg:min-w-[35vw] relative z-10" class="inline-block align-bottom text-left shadow-xl transform transition-all sm:align-middle w-full lg:w-auto min-w-auto lg:min-w-[35vw] relative z-10"
role="dialog" role="dialog"
aria-modal="true" aria-modal="true"
aria-labelledby="modal-headline" aria-labelledby="modal-headline"
> >
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4 rounded-t-xl"> <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4 rounded-t-xl">
<div class=""> <div class="">
<div <div
class="flex-shrink-0 flex items-center justify-center size-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10" class="flex-shrink-0 flex items-center justify-center size-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10"
> >
<svg <svg
class="h-6 w-6 text-blue-600" class="size-6 text-blue-600"
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="M14 2a8 8 0 013.3 15.3A8 8 0 116.7 6.7 8 8 0 0114 2zm-3 7H9v1a2.5 2.5 0 00-.16 5h2.25a.5.5 0 010 1H7v2h2v1h2v-1a2.5 2.5 0 00.16-5H8.91a.5.5 0 010-1H13v-2h-2V9zm3-5a5.99 5.99 0 00-4.48 2.01 8 8 0 018.47 8.47A6 6 0 0014 4z" d="M14 2a8 8 0 013.3 15.3A8 8 0 116.7 6.7 8 8 0 0114 2zm-3 7H9v1a2.5 2.5 0 00-.16 5h2.25a.5.5 0 010 1H7v2h2v1h2v-1a2.5 2.5 0 00.16-5H8.91a.5.5 0 010-1H13v-2h-2V9zm3-5a5.99 5.99 0 00-4.48 2.01 8 8 0 018.47 8.47A6 6 0 0014 4z"
/></svg /></svg
> >
</div> </div>
<div class="mt-3"> <div class="mt-3">
<h3 class="text-lg leading-6 font-medium text-gray-900"> <h3 class="text-xl leading-6 font-medium text-neutral-900">
{#if is_fixed} {$_("add-donation")}
{$_("create-a-new-fixed-donation")}
{:else}{$_("create-a-new-distance-donation")}{/if}
</h3> </h3>
<label class="content-center align-middle object-center"> <nav
<span class="text-base" class:text-gray-300={is_fixed} class="relative z-0 flex border border-neutral-200 rounded-xl overflow-hidden mb-2"
>{$_("distance-donation")}</span >
> <button
<input on:click={() => {
class="toggle relative w-10 h-5 transition-all duration-200 ease-in-out bg-gray-400 rounded-full shadow-inner outline-none appearance-none align-middle" type = "distance";
type="checkbox" }}
bind:checked={is_fixed} type="button"
/> id="bar-with-underline-item-1"
<span class="ml-2 text-base" class:text-gray-300={!is_fixed} class:donation_active_tab={type === "distance"}
>{$_("fixed-donation")}</span class:donation_inactive_tab={type !== "distance"}
> aria-selected={type === "distance"}
</label> role="tab"
<div class="mb-6"> >
<p class="text-sm text-gray-500"> {$_("spende_pro_km")}
{$_( </button>
"please-provide-the-nessecary-information-to-create-a-new-donation" <button
)} on:click={() => {
</p> type = "fixed";
</div> }}
<div class="grid grid-cols-6 gap-2 lg:gap-6 text-left"> type="button"
<div class="col-span-6"> id="bar-with-underline-item-2"
<label class:donation_active_tab={type === "fixed"}
for="donor" class:donation_inactive_tab={type !== "fixed"}
class="block text-sm font-medium text-gray-700" aria-selected={type === "fixed"}
>{$_("donor")}</label role="tab"
> >
<Select {$_("festbetrag")}
containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 rounded-md p-2" </button>
itemFilter={(label, filterText, option) => <button
filterDonors(label, filterText, option)} on:click={() => {
items={donors} type = "anonymous";
showChevron={true} }}
placeholder={$_("search-for-donor-name-or-id")} type="button"
noOptionsMessage={$_("no-donors-found")} id="bar-with-underline-item-3"
on:select={(selectedValue) => class:donation_active_tab={type === "anonymous"}
(donor = selectedValue.detail.value.id)} class:donation_inactive_tab={type !== "anonymous"}
on:clear={() => (donors = null)} aria-selected={type === "anonymous"}
/> role="tab"
</div> >
{#if !is_fixed} {$_("anonyme_spende")}
<div class="col-span-6"> </button>
<label </nav>
for="donor"
class="block text-sm font-medium text-gray-700" <div class="grid grid-cols-6 gap-2 lg:gap-6 text-left">
>{$_("runner")}</label {#if type === "anonymous"}
> <div class="col-span-6">
<Select <label
containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 rounded-md p-2" for="donation_amount_eur"
itemFilter={(label, filterText, option) => class="block text-sm font-medium text-neutral-900"
filterDonors(label, filterText, option)} >
items={runners} {$_("donation-amount")}</label
showChevron={true} >
placeholder={$_("search-for-runner-by-name-or-id")} <div class="mt-1 flex rounded-md shadow-sm">
noOptionsMessage={$_("no-runners-found")} <input
on:select={(selectedValue) => autocomplete="off"
(runner = selectedValue.detail.value.id)} class:border-red-500={!is_amount_valid}
on:clear={() => (runner = null)} class:focus:border-red-500={!is_amount_valid}
/> class:focus:ring-red-500={!is_amount_valid}
</div> bind:value={amount_input}
{/if} type="number"
<div class="col-span-6"> step="0.01"
<label name="donation_amount_eur"
for="donation_amount_eur" class="focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-l-md sm:text-sm border-neutral-300 border bg-neutral-50 text-neutral-800 p-2"
class="block text-sm font-medium text-gray-700" placeholder="2.00"
> />
{#if !is_fixed} <span
{$_("amount-per-kilometer")} class="inline-flex items-center px-3 rounded-r-md border border-neutral-300 bg-neutral-50 text-neutral-500 text-sm"
{:else}{$_("donation-amount")}{/if}</label ></span
> >
<div class="mt-1 flex rounded-md shadow-sm"> </div>
<input {#if !is_amount_valid}
autocomplete="off" <span
class:border-red-500={!is_amount_valid} class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
class:focus:border-red-500={!is_amount_valid} >
class:focus:ring-red-500={!is_amount_valid} {$_("donation-amount-must-be-greater-that-0-00eur")}
bind:value={amount_input} </span>
type="number" {/if}
step="0.01" </div>
name="donation_amount_eur" {:else}
class="focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 p-2" <div class="col-span-6">
placeholder="2.00" <label
/> for="donor"
<span class="block text-sm font-medium text-neutral-900"
class="inline-flex items-center px-3 rounded-r-md border border-gray-300 bg-gray-50 text-gray-500 text-sm" >{$_("donor")}</label
></span >
> <Select
</div> containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-neutral-300 border bg-neutral-50 text-neutral-800 rounded-md p-2"
{#if !is_amount_valid} itemFilter={(label, filterText, option) =>
<span filterDonors(label, filterText, option)}
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" items={donors}
> showChevron={true}
{$_("donation-amount-must-be-greater-that-0-00eur")} placeholder={$_("search-for-donor-name-or-id")}
</span> noOptionsMessage={$_("no-donors-found")}
{/if} on:select={(selectedValue) =>
</div> (donor = selectedValue.detail.value.id)}
{#if is_fixed} on:clear={() => (donors = null)}
<div class="col-span-6"> />
<label </div>
for="paid" {#if type === "distance"}
class="block text-sm font-medium text-gray-700" <div class="col-span-6">
>{$_("already-paid")}</label <label
> for="donor"
<p class="text-gray-500"> class="block text-sm font-medium text-neutral-900"
<input >{$_("runner")}</label
id="paid" >
bind:checked={is_paid} <Select
name="paid" containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-neutral-300 border bg-neutral-50 text-neutral-800 rounded-md p-2"
type="checkbox" itemFilter={(label, filterText, option) =>
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" filterDonors(label, filterText, option)}
/> items={runners}
<span class="align-text-bottom"> showChevron={true}
{#if is_paid} placeholder={$_("search-for-runner-by-name-or-id")}
{$_("paid")} noOptionsMessage={$_("no-runners-found")}
{:else} on:select={(selectedValue) =>
{$_("open")} (runner = selectedValue.detail.value.id)}
{/if} on:clear={() => (runner = null)}
</span> />
</p> </div>
</div> {/if}
{/if} <div class="col-span-6">
</div> <label
</div> for="donation_amount_eur"
</div> class="block text-sm font-medium text-neutral-900"
</div> >
<div class="bg-gray-50 px-4 lg:py-3 sm:px-6 grid gap-2 lg:rounded-b-xl pt-3 pb-10"> {#if type === "fixed"}
<button {$_("donation-amount")}
disabled={!createbtnenabled} {:else}{$_("amount-per-kilometer")}{/if}</label
class:opacity-50={!createbtnenabled} >
on:click={submit} <div class="mt-1 flex rounded-md shadow-sm">
type="button" <input
class="w-full inline-flex 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" autocomplete="off"
> class:border-red-500={!is_amount_valid}
{$_("create")} class:focus:border-red-500={!is_amount_valid}
</button> class:focus:ring-red-500={!is_amount_valid}
<button bind:value={amount_input}
on:click={() => { type="number"
modal_open = false; step="0.01"
}} name="donation_amount_eur"
type="button" class="focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-l-md sm:text-sm border-neutral-300 border bg-neutral-50 text-neutral-800 p-2"
class="w-full justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block" placeholder="2.00"
> />
{$_("cancel")} <span
</button> class="inline-flex items-center px-3 rounded-r-md border border-neutral-300 bg-neutral-50 text-neutral-500 text-sm"
</div> ></span
</div> >
</div> </div>
</div> {#if !is_amount_valid}
<span
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
>
{$_("donation-amount-must-be-greater-that-0-00eur")}
</span>
{/if}
</div>
{/if}
{#if type === "fixed"}
<div class="flex">
<input
bind:checked={is_paid}
type="checkbox"
class="shrink-0 mt-0.5 border-neutral-200 rounded-sm text-blue-600 focus:ring-blue-500 checked:border-blue-500 disabled:opacity-50 disabled:pointer-events-none"
id="hs-default-checkbox"
/>
<label
for="hs-default-checkbox"
class="text-base text-neutral-900 ms-2 font-medium"
>{$_("already-paid")}</label
>
</div>
{/if}
</div>
</div>
</div>
</div>
<div
class="bg-neutral-50 px-4 lg:py-3 sm:px-6 grid gap-2 lg:rounded-b-xl pt-3 pb-10"
>
<button
disabled={!createbtnenabled}
class:opacity-50={!createbtnenabled}
on:click={submit}
type="button"
class="w-full inline-flex 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"
>
{$_("create")}
</button>
<button
on:click={() => {
modal_open = false;
}}
type="button"
class="w-full justify-center rounded-md border border-neutral-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-neutral-900 hover:bg-neutral-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block"
>
{$_("cancel")}
</button>
</div>
</div>
</div>
</div>
{/if} {/if}
<style>
.toggle:before {
content: "";
position: absolute;
width: 1.25rem;
height: 1.25rem;
border-radius: 50%;
top: 0;
left: 0;
transform: scale(1.1);
box-shadow: 0 0.125rem 0.5rem rgba(0, 0, 0, 0.2);
background-color: white;
transition: 0.2s ease-in-out;
}
.toggle:checked {
/* @apply: bg-indigo-400; */
background-color: #7f9cf5;
}
.toggle:checked:before {
left: 1.25rem;
}
</style>

View File

@@ -13,6 +13,7 @@
firstname: "", firstname: "",
lastname: "", lastname: "",
}, },
amount: 0,
}; };
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
onMount(() => { onMount(() => {
@@ -86,8 +87,12 @@
</h3> </h3>
<div class="w-full"> <div class="w-full">
<span class="inline-block" <span class="inline-block"
><b>{$_("donor")}</b>: {delete_donation.donor.firstname} >{#if delete_donation.donor}<b>{$_("donor")}</b>: {delete_donation.donor.firstname}
{delete_donation.donor.lastname}</span {delete_donation.donor.lastname}{:else}{$_("anonymer_sponsor")}{/if}
<br>
<b>{$_("amount")}</b>: {`${(delete_donation.amount / 100)
.toFixed(2)
.toLocaleString("de-DE", { valute: "EUR" })}€`}</span
> >
</div> </div>
</div> </div>

View File

@@ -1,18 +1,21 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
export let donor; export let donor;
</script> </script>
{#if !donor || donor.firstname == 0} {#if !donor || donor.firstname == 0}
{$_("donor-has-no-associated-donations")} <span
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800 border border-current"
>{$_('anonymer_sponsor')}</span
>
{:else} {:else}
<div class="flex items-center"> <div class="flex items-center">
<a <a
href="../donors/{donor.id}" href="../donors/{donor.id}"
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800 border border-current" class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800 border border-current"
>{donor.firstname} >{donor.firstname}
{#if donor.middlename}{donor.middlename}{/if} {#if donor.middlename}{donor.middlename}{/if}
{donor.lastname}</a {donor.lastname}</a
> >
</div> </div>
{/if} {/if}

View File

@@ -9,10 +9,15 @@
export let paymentAction; export let paymentAction;
</script> </script>
{#if paymentAction}
<button <button
on:click={paymentAction} on:click={paymentAction}
class="text-[#025a21] hover:text-green-900 mr-4">{$_("enter-payment")}</button class="text-[#025a21] hover:text-green-900 mr-4">{$_("enter-payment")}</button
> >
{:else}
<span class="inline-block opacity-0 cursor-default mr-4" style="">{$_("enter-payment")}</span>
{/if}
<TableActions <TableActions
bind:detailsAction bind:detailsAction
bind:detailsLink bind:detailsLink

View File

@@ -112,18 +112,25 @@
accessorKey: "actions", accessorKey: "actions",
header: () => $_("action"), header: () => $_("action"),
cell: (info) => { cell: (info) => {
let detailsLink
let paymentAction
if (info.row.original.donor != undefined){
detailsLink = `./${info.row.original.id}`
paymentAction = () => {
active_edits = current_donations.filter(
(r) => r.id == info.row.original.id
);
}
}
return renderComponent(DonationTableAction, { return renderComponent(DonationTableAction, {
detailsLink: `./${info.row.original.id}`, detailsLink: detailsLink,
deleteAction: () => { deleteAction: () => {
active_deletes = current_donations.filter( active_deletes = current_donations.filter(
(r) => r.id == info.row.original.id (r) => r.id == info.row.original.id
); );
}, },
paymentAction: () => { paymentAction: paymentAction,
active_edits = current_donations.filter(
(r) => r.id == info.row.original.id
);
},
deleteEnabled: deleteEnabled:
store.state.jwtinfo.userdetails.permissions.includes( store.state.jwtinfo.userdetails.permissions.includes(
"DONATION:DELETE" "DONATION:DELETE"

View File

@@ -15,6 +15,8 @@
<button on:click={detailsAction} class="text-indigo-600 hover:text-indigo-900" <button on:click={detailsAction} class="text-indigo-600 hover:text-indigo-900"
>{$_("details")}</button >{$_("details")}</button
> >
{:else}
<span class="inline-block opacity-0 cursor-default" style="">{$_("details")}</span>
{/if} {/if}
{#if deleteEnabled} {#if deleteEnabled}
<button <button

View File

@@ -41,6 +41,8 @@
"already-paid": "Bereits bezahlt", "already-paid": "Bereits bezahlt",
"amount": "Anzahl", "amount": "Anzahl",
"amount-per-kilometer": "Betrag pro Kilometer", "amount-per-kilometer": "Betrag pro Kilometer",
"anonyme_spende": "Anonyme Spende",
"anonymer_sponsor": "👻 Anonym",
"apartment-suite-etc": "Apartment, Wohnung, etc.", "apartment-suite-etc": "Apartment, Wohnung, etc.",
"api-endpoint": "API-Endpunkt", "api-endpoint": "API-Endpunkt",
"application_name": "Lauf für Kaya! - Admin", "application_name": "Lauf für Kaya! - Admin",
@@ -224,6 +226,7 @@
"error_on_login": "😢Fehler beim Login", "error_on_login": "😢Fehler beim Login",
"everything-concerning-your-profile": "Alles zu deinem Profil", "everything-concerning-your-profile": "Alles zu deinem Profil",
"faq": "FAQ", "faq": "FAQ",
"festbetrag": "Festbetrag",
"filename_sponsoringquittungsliste": "SponsoringQuittungsListe", "filename_sponsoringquittungsliste": "SponsoringQuittungsListe",
"filter-by-organization-team": "Filtern nach Organisation / Team", "filter-by-organization-team": "Filtern nach Organisation / Team",
"first-name": "Vorname", "first-name": "Vorname",
@@ -418,6 +421,7 @@
"settings": "Einstellungen", "settings": "Einstellungen",
"settings-for-your-profile": "Die Einstellungen deines Accounts", "settings-for-your-profile": "Die Einstellungen deines Accounts",
"something-about-the-group": "Infos zur Gruppe", "something-about-the-group": "Infos zur Gruppe",
"spende_pro_km": "Spende pro km",
"sponsoring-quittungs-liste_herunterladen": "Sponsoring-Quittungs-Liste herunterladen", "sponsoring-quittungs-liste_herunterladen": "Sponsoring-Quittungs-Liste herunterladen",
"sponsorings": "Sponsoringerklaerungen", "sponsorings": "Sponsoringerklaerungen",
"station-deleted": "Scannerstation gelöscht", "station-deleted": "Scannerstation gelöscht",

View File

@@ -41,6 +41,8 @@
"already-paid": "Already paid", "already-paid": "Already paid",
"amount": "Amount", "amount": "Amount",
"amount-per-kilometer": "Amount per kilometer", "amount-per-kilometer": "Amount per kilometer",
"anonyme_spende": "Anonymous",
"anonymer_sponsor": "👻 Anonymous",
"apartment-suite-etc": "Apartment, suite, etc.", "apartment-suite-etc": "Apartment, suite, etc.",
"api-endpoint": "API-Endpoint", "api-endpoint": "API-Endpoint",
"application_name": "Lauf für Kaya! - Admin", "application_name": "Lauf für Kaya! - Admin",
@@ -224,6 +226,7 @@
"error_on_login": "Error on login", "error_on_login": "Error on login",
"everything-concerning-your-profile": "Everything concerning your profile", "everything-concerning-your-profile": "Everything concerning your profile",
"faq": "FAQ", "faq": "FAQ",
"festbetrag": "Fixed amount",
"filename_sponsoringquittungsliste": "DonorReceiptList", "filename_sponsoringquittungsliste": "DonorReceiptList",
"filter-by-organization-team": "Filter by Organization/ Team", "filter-by-organization-team": "Filter by Organization/ Team",
"first-name": "First name", "first-name": "First name",
@@ -418,6 +421,7 @@
"settings": "Settings", "settings": "Settings",
"settings-for-your-profile": "Settings for your profile", "settings-for-your-profile": "Settings for your profile",
"something-about-the-group": "Something about the group...", "something-about-the-group": "Something about the group...",
"spende_pro_km": "Per Kilometer",
"sponsoring-quittungs-liste_herunterladen": "Download donor receipt list", "sponsoring-quittungs-liste_herunterladen": "Download donor receipt list",
"sponsorings": "Sponsorings", "sponsorings": "Sponsorings",
"station-deleted": "station deleted", "station-deleted": "station deleted",

View File

@@ -25,3 +25,9 @@
#html5-qrcode-button-camera-stop { #html5-qrcode-button-camera-stop {
@apply px-2 inline-flex text-lg leading-5 font-semibold rounded-md border border-current bg-red-100 text-red-800 mb-2 cursor-pointer; @apply px-2 inline-flex text-lg leading-5 font-semibold rounded-md border border-current bg-red-100 text-red-800 mb-2 cursor-pointer;
} }
.donation_inactive_tab {
@apply min-w-0 flex-1 bg-white first:border-s-0 border-s border-b-2 border-neutral-200 py-4 px-4 text-neutral-800 hover:text-neutral-700 text-sm font-medium text-center overflow-hidden hover:bg-neutral-200 cursor-pointer focus:z-10 focus:outline-hidden focus:bg-neutral-200 disabled:opacity-50 disabled:pointer-events-none;
}
.donation_active_tab {
@apply min-w-0 flex-1 bg-blue-400 text-white first:border-s-0 border-s border-b-2 border-neutral-200 py-4 px-4 text-sm font-medium text-center overflow-hidden cursor-pointer focus:outline-hidden;
}