Compare commits
18
Commits
0.16.1
...
0e5490f1c8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e5490f1c8 | ||
|
|
b82d638de1 | ||
|
|
224034dcc6 | ||
|
|
026d3d41c1 | ||
|
|
fd1a06b359 | ||
|
|
452d010183
|
||
|
|
eb1c17e3ac
|
||
|
|
a101873eb0
|
||
|
|
3d2acb692a
|
||
|
|
0900c2691e
|
||
|
|
1337676e08
|
||
|
|
2e075eafab | ||
|
|
14d64b6070 | ||
|
|
81b8fbf4e3 | ||
|
|
24d074752f | ||
|
|
08047a9307 | ||
|
|
1b0cd5b90b | ||
|
|
65e8998894 |
No files matched your search
+2
-1
@@ -10,6 +10,7 @@
|
||||
},
|
||||
"license": "CC-BY-NC-SA-4.0",
|
||||
"devDependencies": {
|
||||
"@vincjo/datatables": "^1.1.0",
|
||||
"@odit/lfk-client-js": "0.13.1",
|
||||
"@odit/license-exporter": "0.0.11",
|
||||
"@sveltejs/vite-plugin-svelte": "1.0.0-next.6",
|
||||
@@ -29,7 +30,7 @@
|
||||
"svelte-i18n": "3.3.9",
|
||||
"svelte-preprocess": "4.7.0",
|
||||
"svelte-select": "3.17.0",
|
||||
"tailwindcss": "3.2.4",
|
||||
"tailwindcss": "3.2.7",
|
||||
"tinro": "0.6.1",
|
||||
"toastify-js": "1.10.0",
|
||||
"validator": "13.5.2",
|
||||
|
||||
@@ -1 +1 @@
|
||||
[{"author":"ODIT.Services","repo":{"type":"git","url":"git+https://git.odit.services/lfk/lfk-client-js"},"description":"A lib to interact with https://git.odit.services/lfk/backend. Use this version for native JS applications.","name":"@odit/lfk-client-js","license":"CC-BY-NC-SA-4.0","version":"0.13.1","licensetext":"Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Creative\r\nCommons Corporation (\"Creative Commons\") is not a law firm and does not provide\r\nlegal services or legal advice. Distribution of Creative Commons public licenses\r\ndoes not create a lawyer-client or other relationship. Creative Commons makes\r\nits licenses and related information available on an \"as-is\" basis. Creative\r\nCommons gives no warranties regarding its licenses, any material licensed\r\nunder their terms and conditions, or any related information. Creative Commons\r\ndisclaims all liability for damages resulting from their use to the fullest\r\nextent possible.\r\n\r\nUsing Creative Commons Public Licenses\r\n\r\nCreative Commons public licenses provide a standard set of terms and conditions\r\nthat creators and other rights holders may use to share original works of\r\nauthorship and other material subject to copyright and certain other rights\r\nspecified in the public license below. The following considerations are for\r\ninformational purposes only, are not exhaustive, and do not form part of our\r\nlicenses.\r\n\r\nConsiderations for licensors: Our public licenses are intended for use by\r\nthose authorized to give the public permission to use material in ways otherwise\r\nrestricted by copyright and certain other rights. Our licenses are irrevocable.\r\nLicensors should read and understand the terms and conditions of the license\r\nthey choose before applying it. Licensors should also secure all rights necessary\r\nbefore applying our licenses so that the public can reuse the material as\r\nexpected. Licensors should clearly mark any material not subject to the license.\r\nThis includes other CC-licensed material, or material used under an exception\r\nor limitation to copyright. More considerations for licensors : wiki.creativecommons.org/Considerations_for_licensors\r\n\r\nConsiderations for the public: By using one of our public licenses, a licensor\r\ngrants the public permission to use the licensed material under specified\r\nterms and conditions. If the licensor's permission is not necessary for any\r\nreason–for example, because of any applicable exception or limitation to copyright–then\r\nthat use is not regulated by the license. Our licenses grant only permissions\r\nunder copyright and certain other rights that a licensor has authority to\r\ngrant. Use of the licensed material may still be restricted for other reasons,\r\nincluding because others have copyright or other rights in the material. A\r\nlicensor may make special requests, such as asking that all changes be marked\r\nor described. Although not required by our licenses, you are encouraged to\r\nrespect those requests where reasonable. More considerations for the public\r\n: wiki.creativecommons.org/Considerations_for_licensees\r\n\r\nCreative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public\r\nLicense\r\n\r\nBy exercising the Licensed Rights (defined below), You accept and agree to\r\nbe bound by the terms and conditions of this Creative Commons Attribution-NonCommercial-ShareAlike\r\n4.0 International Public License (\"Public License\"). To the extent this Public\r\nLicense may be interpreted as a contract, You are granted the Licensed Rights\r\nin consideration of Your acceptance of these terms and conditions, and the\r\nLicensor grants You such rights in consideration of benefits the Licensor\r\nreceives from making the Licensed Material available under these terms and\r\nconditions.\r\n\r\nSection 1 – Definitions.\r\n\r\na. Adapted Material means material subject to Copyright and Similar Rights\r\nthat is derived from or based upon the Licensed Material and in which the\r\nLicensed Material is translated, altered, arranged, transformed, or otherwise\r\nmodified in a manner requiring permission under the Copyright and Similar\r\nRights held by the Licensor. For purposes of this Public License, where the\r\nLicensed Material is a musical work, performance, or sound recording, Adapted\r\nMaterial is always produced where the Licensed Material is synched in timed\r\nrelation with a moving image.\r\n\r\nb. Adapter's License means the license You apply to Your Copyright and Similar\r\nRights in Your contributions to Adapted Material in accordance with the terms\r\nand conditions of this Public License.\r\n\r\nc. BY-NC-SA Compatible License means a license listed at creativecommons.org/compatiblelicenses,\r\napproved by Creative Commons as essentially the equivalent of this Public\r\nLicense.\r\n\r\nd. Copyright and Similar Rights means copyright and/or similar rights closely\r\nrelated to coLine truncated
|
||||
[{"author":"vincjo","repo":{"type":"git","url":"https://github.com/vincjo/datatables"},"description":"A toolkit for creating datatable components with Svelte","name":"@vincjo/datatables","license":"MIT","version":"1.2.0","licensetext":"MIT License\r\n\r\nCopyright (c) 2020 vincjo\r\n\r\nPermission is hereby granted, free of charge, to any person obtaining a copy\r\nof this software and associated documentation files (the \"Software\"), to deal\r\nin the Software without restriction, including without limitation the rights\r\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\ncopies of the Software, and to permit persons to whom the Software is\r\nfurnished to do so, subject to the following conditions:\r\n\r\nThe above copyright notice and this permission notice shall be included in all\r\ncopies or substantial portions of the Software.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r\nSOFTWARE.\r\n"},{"author":"ODIT.Services","repo":{"type":"git","url":"git+https://git.odit.services/lfk/lfk-client-js"},"description":"A lib to interact with https://git.odit.services/lfk/backend. Use this version for native JS applications.","name":"@odit/lfk-client-js","license":"CC-BY-NC-SA-4.0","version":"0.13.1","licensetext":"Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Creative\r\nCommons Corporation (\"Creative Commons\") is not a law firm and does not provide\r\nlegal services or legal advice. Distribution of Creative Commons public licenses\r\ndoes not create a lawyer-client or other relationship. Creative Commons makes\r\nits licenses and related information available on an \"as-is\" basis. Creative\r\nCommons gives no warranties regarding its licenses, any material licensed\r\nunder their terms and conditions, or any related information. Creative Commons\r\ndisclaims all liability for damages resulting from their use to the fullest\r\nextent possible.\r\n\r\nUsing Creative Commons Public Licenses\r\n\r\nCreative Commons public licenses provide a standard set of terms and conditions\r\nthat creators and other rights holders may use to share original works of\r\nauthorship and other material subject to copyright and certain other rights\r\nspecified in the public license below. The following considerations are for\r\ninformational purposes only, are not exhaustive, and do not form part of our\r\nlicenses.\r\n\r\nConsiderations for licensors: Our public licenses are intended for use by\r\nthose authorized to give the public permission to use material in ways otherwise\r\nrestricted by copyright and certain other rights. Our licenses are irrevocable.\r\nLicensors should read and understand the terms and conditions of the license\r\nthey choose before applying it. Licensors should also secure all rights necessary\r\nbefore applying our licenses so that the public can reuse the material as\r\nexpected. Licensors should clearly mark any material not subject to the license.\r\nThis includes other CC-licensed material, or material used under an exception\r\nor limitation to copyright. More considerations for licensors : wiki.creativecommons.org/Considerations_for_licensors\r\n\r\nConsiderations for the public: By using one of our public licenses, a licensor\r\ngrants the public permission to use the licensed material under specified\r\nterms and conditions. If the licensor's permission is not necessary for any\r\nreason–for example, because of any applicable exception or limitation to copyright–then\r\nthat use is not regulated by the license. Our licenses grant only permissions\r\nunder copyright and certain other rights that a licensor has authority to\r\ngrant. Use of the licensed material may still be restricted for other reasons,\r\nincluding because others have copyright or other rights in the material. A\r\nlicensor may make special requests, such as asking that all changes be marked\r\nor described. Although not required by our licenses, you are encouraged to\r\nrespect those requests where reasonable. More considerations for the public\r\n: wiki.creativecommons.org/Considerations_for_licensees\r\n\r\nCreative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public\r\nLicense\r\n\r\nBy exercising the Licensed Rights (defined below), You accept and agree to\r\nbe bound by the terms and conditions of this Creative Commons Attribution-NonCommercial-ShareAlike\r\n4.0 International Public License (\"Public License\"). To the extent this Public\r\nLicense may be interpreted as a contract, You are granted the Licensed Rights\r\nin Line truncated
|
||||
@@ -1,22 +1,157 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import StatCards from "./StatCards.svelte";
|
||||
import { StatsService } from "@odit/lfk-client-js";
|
||||
import StatCards from "./StatCard.svelte";
|
||||
import store from "../../store";
|
||||
import StatCard from "./StatCard.svelte";
|
||||
let navOpen = false;
|
||||
const stats_promise = StatsService.statsControllerGet();
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="p-5 overflow-x-hidden"
|
||||
on:click={() => {
|
||||
navOpen = false;
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<h1 class="text-3xl leading-tight">
|
||||
<span class="font-extrabold">{$_('dashboard-title')}</span>
|
||||
<span class="font-extrabold">{$_("dashboard-title")}</span>
|
||||
<span>
|
||||
-
|
||||
{$_('dashboard-greeting')},
|
||||
<span
|
||||
class="text-blue-500">{store.state.jwtinfo.userdetails.firstname} {store.state.jwtinfo.userdetails.lastname}</span></span>
|
||||
{$_("dashboard-greeting")},
|
||||
<span class="text-blue-500"
|
||||
>{store.state.jwtinfo.userdetails.firstname}
|
||||
{store.state.jwtinfo.userdetails.lastname}</span
|
||||
></span
|
||||
>
|
||||
</h1>
|
||||
<StatCards />
|
||||
<h1>{$_("general-stats")}</h1>
|
||||
{#await stats_promise}
|
||||
<div
|
||||
class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2"
|
||||
role="alert"
|
||||
>
|
||||
<p class="font-bold">{$_("stats-are-being-loaded")}</p>
|
||||
<p class="text-sm">{$_("this-might-take-a-moment")}</p>
|
||||
</div>
|
||||
{:then stats}
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5 2xl:grid-cols-6 gap-4">
|
||||
<StatCard
|
||||
title={$_("runners")}
|
||||
value={stats.total_runners}
|
||||
href="/runners/"
|
||||
>
|
||||
<svg
|
||||
height="24"
|
||||
width="24"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
><path d="M0 0h24v24H0z" fill="none" />
|
||||
<path
|
||||
d="M13.49 5.48c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm-3.6 13.9l1-4.4 2.1 2v6h2v-7.5l-2.1-2 .6-3c1.3 1.5 3.3 2.5 5.5 2.5v-2c-1.9 0-3.5-1-4.3-2.4l-1-1.6c-.4-.6-1-1-1.7-1-.3 0-.5.1-.8.1l-5.2 2.2v4.7h2v-3.4l1.8-.7-1.6 8.1-4.9-1-.4 2 7 1.4z"
|
||||
/></svg
|
||||
>
|
||||
</StatCard>
|
||||
<StatCard
|
||||
title={$_("total-scans")}
|
||||
value={stats.total_scans}
|
||||
href="/scans/"
|
||||
>
|
||||
<svg
|
||||
stroke="currentColor"
|
||||
fill="currentColor"
|
||||
stroke-width="2"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
size="24"
|
||||
class="stroke-current text-grey-500"
|
||||
height="24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
><polyline points="22 12 18 12 15 21 9 3 6 12 2 12" /></svg
|
||||
>
|
||||
</StatCard>
|
||||
<StatCard
|
||||
title={$_("total-donations")}
|
||||
value={`${(stats.total_donation / 100).toFixed(2)} €`}
|
||||
href="/donations/"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24"
|
||||
fill="currentColor"
|
||||
width="24"
|
||||
><path d="M0 0h24v24H0z" fill="none" />
|
||||
<path
|
||||
d="M15 18.5A6.48 6.48 0 019.24 15H15v-2H8.58c-.05-.33-.08-.66-.08-1s.03-.67.08-1H15V9H9.24A6.491 6.491 0 0115 5.5c1.61 0 3.09.59 4.23 1.57L21 5.3A8.955 8.955 0 0015 3c-3.92 0-7.24 2.51-8.48 6H3v2h3.06a8.262 8.262 0 000 2H3v2h3.52c1.24 3.49 4.56 6 8.48 6 2.31 0 4.41-.87 6-2.3l-1.78-1.77c-1.13.98-2.6 1.57-4.22 1.57z"
|
||||
/></svg
|
||||
>
|
||||
</StatCard>
|
||||
<StatCard
|
||||
title={$_("total-distance")}
|
||||
value={`${stats.total_distance / 1000} km`}
|
||||
href="#"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24"
|
||||
width="24"
|
||||
><path d="M0 0h24v24H0z" fill="none" />
|
||||
<path
|
||||
d="M21 6H3c-1.1 0-2 .9-2 2v8c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm0 10H3V8h2v4h2V8h2v4h2V8h2v4h2V8h2v4h2V8h2v8z"
|
||||
/></svg
|
||||
>
|
||||
</StatCard>
|
||||
<StatCard
|
||||
title={$_("count_teams")}
|
||||
value={stats.total_teams}
|
||||
href="/teams/"
|
||||
>
|
||||
<svg
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
stroke-width="2"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
size="24"
|
||||
class="stroke-current text-grey-500"
|
||||
height="24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
|
||||
<circle cx="9" cy="7" r="4" />
|
||||
<path d="M23 21v-2a4 4 0 0 0-3-3.87" />
|
||||
<path d="M16 3.13a4 4 0 0 1 0 7.75" /></svg
|
||||
>
|
||||
</StatCard>
|
||||
<StatCard
|
||||
title={$_("count_organizations")}
|
||||
value={stats.total_orgs}
|
||||
href="/orgs/"
|
||||
>
|
||||
<svg
|
||||
height="24"
|
||||
fill="currentColor"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
><path fill="none" d="M0 0h24v24H0z" />
|
||||
<path
|
||||
d="M17 11V3H7v4H3v14h8v-4h2v4h8V11h-4zM7 19H5v-2h2v2zm0-4H5v-2h2v2zm0-4H5V9h2v2zm4 4H9v-2h2v2zm0-4H9V9h2v2zm0-4H9V5h2v2zm4 8h-2v-2h2v2zm0-4h-2V9h2v2zm0-4h-2V5h2v2zm4 12h-2v-2h2v2zm0-4h-2v-2h2v2z"
|
||||
/></svg
|
||||
>
|
||||
</StatCard>
|
||||
</div>
|
||||
{:catch error}
|
||||
<div class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500">
|
||||
<span class="inline-block align-middle mr-8">
|
||||
<b class="capitalize">{$_("general_promise_error")}</b>
|
||||
{error}
|
||||
</span>
|
||||
</div>
|
||||
{/await}
|
||||
</div>
|
||||
@@ -0,0 +1,22 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
|
||||
export let href = "#"
|
||||
export let title = "";
|
||||
export let value = "";
|
||||
</script>
|
||||
|
||||
<a href={href}>
|
||||
<div
|
||||
class="p-4 rounded-lg bg-white border border-grey-100">
|
||||
<div class="flex flex-row items-center justify-between">
|
||||
<div class="flex flex-col">
|
||||
<div class="text-xs uppercase font-light text-grey-500">
|
||||
{title}
|
||||
</div>
|
||||
<div class="text-xl font-bold">{value}</div>
|
||||
</div>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
@@ -1,165 +0,0 @@
|
||||
<script>
|
||||
import { StatsService } from "@odit/lfk-client-js";
|
||||
import { _ } from "svelte-i18n";
|
||||
const stats_promise = StatsService.statsControllerGet();
|
||||
</script>
|
||||
|
||||
<!-- -->
|
||||
<h1>{$_('general-stats')}</h1>
|
||||
{#await stats_promise}
|
||||
<div
|
||||
class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2"
|
||||
role="alert">
|
||||
<p class="font-bold">{$_('stats-are-being-loaded')}</p>
|
||||
<p class="text-sm">{$_('this-might-take-a-moment')}</p>
|
||||
</div>
|
||||
{:then stats}
|
||||
<div
|
||||
class="flex flex-col lg:flex-row w-full lg:space-x-2 space-y-2 lg:space-y-0 mb-2 lg:mb-4">
|
||||
<a href="/runners/" class="w-full lg:w-1/4">
|
||||
<div
|
||||
class="widget w-full p-4 rounded-lg bg-white border border-grey-100">
|
||||
<div class="flex flex-row items-center justify-between">
|
||||
<div class="flex flex-col">
|
||||
<div class="text-xs uppercase font-light text-grey-500">
|
||||
{$_('runners')}
|
||||
</div>
|
||||
<div class="text-xl font-bold">{stats.total_runners}</div>
|
||||
</div>
|
||||
<svg
|
||||
height="24"
|
||||
width="24"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none" />
|
||||
<path
|
||||
d="M13.49 5.48c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm-3.6 13.9l1-4.4 2.1 2v6h2v-7.5l-2.1-2 .6-3c1.3 1.5 3.3 2.5 5.5 2.5v-2c-1.9 0-3.5-1-4.3-2.4l-1-1.6c-.4-.6-1-1-1.7-1-.3 0-.5.1-.8.1l-5.2 2.2v4.7h2v-3.4l1.8-.7-1.6 8.1-4.9-1-.4 2 7 1.4z" /></svg>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<div class="w-full lg:w-1/4">
|
||||
<div
|
||||
class="widget w-full p-4 rounded-lg bg-white border border-grey-100">
|
||||
<div class="flex flex-row items-center justify-between">
|
||||
<div class="flex flex-col">
|
||||
<div class="text-xs uppercase font-light text-grey-500">
|
||||
{$_('total-scans')}
|
||||
</div>
|
||||
<div class="text-xl font-bold">{stats.total_scans}</div>
|
||||
</div><svg
|
||||
stroke="currentColor"
|
||||
fill="currentColor"
|
||||
stroke-width="2"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
size="24"
|
||||
class="stroke-current text-grey-500"
|
||||
height="24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"><polyline
|
||||
points="22 12 18 12 15 21 9 3 6 12 2 12" /></svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full lg:w-1/4">
|
||||
<div
|
||||
class="widget w-full p-4 rounded-lg bg-white border border-grey-100">
|
||||
<div class="flex flex-row items-center justify-between">
|
||||
<div class="flex flex-col">
|
||||
<div class="text-xs uppercase font-light text-grey-500">
|
||||
{$_('total-donations')}
|
||||
</div>
|
||||
<div class="text-xl font-bold">{(stats.total_donation/100).toFixed(2)} €</div>
|
||||
</div><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24"
|
||||
fill="currentColor"
|
||||
width="24"><path d="M0 0h24v24H0z" fill="none" />
|
||||
<path
|
||||
d="M15 18.5A6.48 6.48 0 019.24 15H15v-2H8.58c-.05-.33-.08-.66-.08-1s.03-.67.08-1H15V9H9.24A6.491 6.491 0 0115 5.5c1.61 0 3.09.59 4.23 1.57L21 5.3A8.955 8.955 0 0015 3c-3.92 0-7.24 2.51-8.48 6H3v2h3.06a8.262 8.262 0 000 2H3v2h3.52c1.24 3.49 4.56 6 8.48 6 2.31 0 4.41-.87 6-2.3l-1.78-1.77c-1.13.98-2.6 1.57-4.22 1.57z" /></svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full lg:w-1/4">
|
||||
<div
|
||||
class="widget w-full p-4 rounded-lg bg-white border border-grey-100">
|
||||
<div class="flex flex-row items-center justify-between">
|
||||
<div class="flex flex-col">
|
||||
<div class="text-xs uppercase font-light text-grey-500">
|
||||
{$_('total-distance')}
|
||||
</div>
|
||||
<div class="text-xl font-bold">
|
||||
{stats.total_distance / 1000}
|
||||
km
|
||||
</div>
|
||||
</div>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24"
|
||||
width="24"><path d="M0 0h24v24H0z" fill="none" />
|
||||
<path
|
||||
d="M21 6H3c-1.1 0-2 .9-2 2v8c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm0 10H3V8h2v4h2V8h2v4h2V8h2v4h2V8h2v4h2V8h2v8z" /></svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a href="/teams/" class="w-full lg:w-1/4">
|
||||
<div
|
||||
class="widget w-full p-4 rounded-lg bg-white border border-grey-100">
|
||||
<div class="flex flex-row items-center justify-between">
|
||||
<div class="flex flex-col">
|
||||
<div class="text-xs uppercase font-light text-grey-500">
|
||||
{$_('count_teams')}
|
||||
</div>
|
||||
<div class="text-xl font-bold">{stats.total_teams}</div>
|
||||
</div>
|
||||
<svg
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
stroke-width="2"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
size="24"
|
||||
class="stroke-current text-grey-500"
|
||||
height="24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"><path
|
||||
d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
|
||||
<circle cx="9" cy="7" r="4" />
|
||||
<path d="M23 21v-2a4 4 0 0 0-3-3.87" />
|
||||
<path d="M16 3.13a4 4 0 0 1 0 7.75" /></svg>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<a href="/orgs/" class="w-full lg:w-1/4">
|
||||
<div
|
||||
class="widget w-full p-4 rounded-lg bg-white border border-grey-100">
|
||||
<div class="flex flex-row items-center justify-between">
|
||||
<div class="flex flex-col">
|
||||
<div class="text-xs uppercase font-light text-grey-500">
|
||||
{$_('count_organizations')}
|
||||
</div>
|
||||
<div class="text-xl font-bold">{stats.total_orgs}</div>
|
||||
</div>
|
||||
<svg
|
||||
height="24"
|
||||
fill="currentColor"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z" />
|
||||
<path
|
||||
d="M17 11V3H7v4H3v14h8v-4h2v4h8V11h-4zM7 19H5v-2h2v2zm0-4H5v-2h2v2zm0-4H5V9h2v2zm4 4H9v-2h2v2zm0-4H9V9h2v2zm0-4H9V5h2v2zm4 8h-2v-2h2v2zm0-4h-2V9h2v2zm0-4h-2V5h2v2zm4 12h-2v-2h2v2zm0-4h-2v-2h2v2z" /></svg>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{:catch error}
|
||||
<div class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500">
|
||||
<span class="inline-block align-middle mr-8">
|
||||
<b class="capitalize">{$_('general_promise_error')}</b>
|
||||
{error}
|
||||
</span>
|
||||
</div>
|
||||
{/await}
|
||||
@@ -369,7 +369,7 @@
|
||||
</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"
|
||||
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"
|
||||
|
||||
@@ -298,7 +298,7 @@
|
||||
</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"
|
||||
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"
|
||||
|
||||
@@ -272,7 +272,7 @@
|
||||
</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"
|
||||
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"
|
||||
|
||||
@@ -1,37 +1,26 @@
|
||||
<script>
|
||||
import { getLocaleFromNavigator, _ } from "svelte-i18n";
|
||||
import { _ } from "svelte-i18n";
|
||||
import {
|
||||
RunnerService,
|
||||
RunnerTeamService,
|
||||
RunnerOrganizationService,
|
||||
} from "@odit/lfk-client-js";
|
||||
import ThFilterGroup from "./ThFilterGroup.svelte";
|
||||
import { DataHandler, Datatable, Th, ThFilter } from "@vincjo/datatables";
|
||||
import store from "../../store";
|
||||
import RunnersEmptyState from "./RunnersEmptyState.svelte";
|
||||
import Select from "svelte-select";
|
||||
import GenerateSponsoringContracts from "../pdf_generation/GenerateSponsoringContracts.svelte";
|
||||
import GenerateRunnerCards from "../pdf_generation/GenerateRunnerCards.svelte";
|
||||
import GenerateRunnerCertificates from "../pdf_generation/GenerateRunnerCertificates.svelte";
|
||||
$: searchvalue = "";
|
||||
import { onMount } from "svelte";
|
||||
$: active_deletes = [];
|
||||
export let current_runners = [];
|
||||
const runners_promise = RunnerService.runnerControllerGetAll().then((val) => {
|
||||
current_runners = val;
|
||||
});
|
||||
$: selectedFilter_teams = null;
|
||||
$: selectedFilter = null;
|
||||
$: filter__teams = selectedFilter_teams || [];
|
||||
$: filter__orgs = selectedFilter || [];
|
||||
$: filterGroupIDs = filter__teams.concat(filter__orgs).map((i) => i.value);
|
||||
$: sponsoring_contracts_show = current_runners.some(
|
||||
(r) => r.is_selected === true
|
||||
);
|
||||
$: cards_show = current_runners.some(
|
||||
(r) => r.is_selected === true
|
||||
);
|
||||
$: certificates_show = current_runners.some(
|
||||
(r) => r.is_selected === true
|
||||
);
|
||||
$: generate_runners = current_runners.filter((r) => r.is_selected === true);
|
||||
let dataLoaded = false;
|
||||
let current_runners = [];
|
||||
const handler = new DataHandler(current_runners, { rowsPerPage: 50 });
|
||||
const rows = handler.getRows();
|
||||
$: sponsoring_contracts_show = generate_runners.length > 0;
|
||||
$: cards_show = generate_runners.length > 0;
|
||||
$: certificates_show = generate_runners.length > 0;
|
||||
$: generate_runners = []; //current_runners.filter((r) => r.selected === true);
|
||||
$: teams = [];
|
||||
$: orgs = [];
|
||||
$: mappedteams = teams.map(function (g) {
|
||||
@@ -42,222 +31,193 @@
|
||||
return { value: g.id, label: g.name };
|
||||
})
|
||||
.concat(mappedteams);
|
||||
|
||||
RunnerTeamService.runnerTeamControllerGetAll().then((val) => {
|
||||
teams = val;
|
||||
onMount(() => {
|
||||
RunnerService.runnerControllerGetAll().then((val) => {
|
||||
current_runners = val;
|
||||
dataLoaded = true;
|
||||
handler.setRows(val);
|
||||
});
|
||||
RunnerTeamService.runnerTeamControllerGetAll().then((val) => {
|
||||
teams = val;
|
||||
});
|
||||
RunnerOrganizationService.runnerOrganizationControllerGetAll().then(
|
||||
(val) => {
|
||||
orgs = val;
|
||||
}
|
||||
);
|
||||
});
|
||||
RunnerOrganizationService.runnerOrganizationControllerGetAll().then((val) => {
|
||||
orgs = val;
|
||||
});
|
||||
function should_display_based_on_id(id) {
|
||||
if (searchvalue.toString().slice(-1) === "*") {
|
||||
return id.toString().startsWith(searchvalue.replace("*", ""));
|
||||
}
|
||||
return id.toString() === searchvalue;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes('RUNNER:GET')}
|
||||
{#await runners_promise}
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:GET")}
|
||||
{#if !dataLoaded}
|
||||
<div
|
||||
class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2"
|
||||
role="alert">
|
||||
<p class="font-bold">{$_('runners-are-being-loaded')}</p>
|
||||
<p class="text-sm">{$_('this-might-take-a-moment')}</p>
|
||||
role="alert"
|
||||
>
|
||||
<p class="font-bold">{$_("runners-are-being-loaded")}</p>
|
||||
<p class="text-sm">{$_("this-might-take-a-moment")}</p>
|
||||
</div>
|
||||
{:then}
|
||||
{#if current_runners.length === 0}
|
||||
<RunnersEmptyState />
|
||||
{:else}
|
||||
<input
|
||||
type="search"
|
||||
bind:value={searchvalue}
|
||||
placeholder={$_('datatable.search')}
|
||||
aria-label={$_('datatable.search')}
|
||||
class="gridjs-input gridjs-search-input mb-4" />
|
||||
<div class="block mb-6">
|
||||
<label
|
||||
for="country"
|
||||
class="text-sm font-medium text-gray-700">{$_('filter-by-organization-team')}</label>
|
||||
<Select
|
||||
on:select={(event) => {
|
||||
selectedFilter = event.detail;
|
||||
}}
|
||||
selectedValue={selectedFilter}
|
||||
placeholder={$_('filter-by-organization-team')}
|
||||
containerClasses="mt-1 py-2 px-3 border border-gray-300 bg-white rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
|
||||
items={selectgroups}
|
||||
isMulti={true} />
|
||||
</div>
|
||||
<div class="h-12">
|
||||
<GenerateSponsoringContracts
|
||||
bind:sponsoring_contracts_show
|
||||
bind:generate_runners />
|
||||
<GenerateRunnerCards
|
||||
bind:cards_show
|
||||
bind:generate_runners />
|
||||
<GenerateRunnerCertificates
|
||||
bind:certificates_show
|
||||
bind:generate_runners />
|
||||
</div>
|
||||
<div
|
||||
class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll">
|
||||
<table class="divide-y divide-gray-200 w-full">
|
||||
<thead class="bg-gray-50">
|
||||
{:else}
|
||||
<div class="h-12">
|
||||
<GenerateSponsoringContracts
|
||||
bind:sponsoring_contracts_show
|
||||
bind:generate_runners
|
||||
/>
|
||||
<GenerateRunnerCards bind:cards_show bind:generate_runners />
|
||||
<GenerateRunnerCertificates
|
||||
bind:certificates_show
|
||||
bind:generate_runners
|
||||
/>
|
||||
</div>
|
||||
<Datatable {handler}>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
|
||||
checked={generate_runners.length == current_runners.length}
|
||||
on:click={() => {
|
||||
if (generate_runners.length != current_runners.length) {
|
||||
generate_runners = current_runners;
|
||||
} else {
|
||||
generate_runners = [];
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</th>
|
||||
<Th {handler} orderBy="id">ID</Th>
|
||||
<Th {handler} orderBy="firstname">First Name</Th>
|
||||
<Th {handler} orderBy="middlename">Middle Name</Th>
|
||||
<Th {handler} orderBy="lastname">Last Name</Th>
|
||||
<th>Gruppe</th>
|
||||
<Th {handler} orderBy="distance">Distanz</Th>
|
||||
<th>{$_("action")}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th />
|
||||
<ThFilter {handler} filterBy="id" />
|
||||
<ThFilter {handler} filterBy="firstname" />
|
||||
<ThFilter {handler} filterBy="middlename" />
|
||||
<ThFilter {handler} filterBy="lastname" />
|
||||
<ThFilterGroup groups={selectgroups} {handler} />
|
||||
<th />
|
||||
<th />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each $rows as row}
|
||||
<tr>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
<span
|
||||
<td>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
|
||||
checked={generate_runners.filter((i)=>i.id == row.id).length > 0}
|
||||
on:click={() => {
|
||||
const newstate = !current_runners.some((r) => r.is_selected === true);
|
||||
current_runners = current_runners.map((r) => {
|
||||
r.is_selected = newstate;
|
||||
return r;
|
||||
});
|
||||
if (
|
||||
generate_runners.findIndex((i) => i.id == row.id) == -1
|
||||
) {
|
||||
generate_runners.push(row);
|
||||
generate_runners = generate_runners;
|
||||
} else {
|
||||
generate_runners = generate_runners.filter(
|
||||
(r) => r.id != row.id
|
||||
);
|
||||
}
|
||||
console.log(generate_runners)
|
||||
}}
|
||||
class="underline cursor-pointer select-none">{#if current_runners.some((r) => r.is_selected === true)}
|
||||
{$_('deselect-all')}
|
||||
{:else}{$_('select-all')}{/if}
|
||||
</span>
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
{$_('name')}
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
{$_('contact-information')}
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
{$_('group')}
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
{$_('distance-in-km')}
|
||||
</th>
|
||||
<th scope="col" class="relative px-6 py-3">
|
||||
<span class="sr-only">{$_('action')}</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-200">
|
||||
{#each current_runners as runner}
|
||||
{#if runner.firstname
|
||||
.toLowerCase()
|
||||
.includes(
|
||||
searchvalue.toLowerCase()
|
||||
) || runner.lastname
|
||||
.toLowerCase()
|
||||
.includes(
|
||||
searchvalue.toLowerCase()
|
||||
) || should_display_based_on_id(runner.id)}
|
||||
{#if filterGroupIDs.includes(runner.group.id) || filterGroupIDs.includes(runner.group.parentGroup?.id) || filterGroupIDs.length === 0}
|
||||
<tr
|
||||
data-rowid="user_{runner.id}"
|
||||
data-groupid={runner.group.id}>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<input
|
||||
bind:checked={runner.is_selected}
|
||||
type="checkbox"
|
||||
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" />
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="flex items-center">
|
||||
<div class="ml-4">
|
||||
<div class="text-sm font-medium text-gray-900">
|
||||
{runner.firstname}
|
||||
{runner.middlename || ''}
|
||||
{runner.lastname}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
{#if runner.email}
|
||||
<div class="text-sm text-gray-500">{runner.email}</div>
|
||||
{/if}
|
||||
{#if runner.phone}
|
||||
<div class="text-sm text-gray-500">{runner.phone}</div>
|
||||
{/if}
|
||||
{#if runner.address.address1 !== null}
|
||||
{runner.address.address1}<br />
|
||||
{runner.address.address2 || ''}<br />
|
||||
{runner.address.postalcode}
|
||||
{runner.address.city}
|
||||
{runner.address.country}
|
||||
{/if}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
{#if runner.group.responseType === 'RUNNERTEAM'}
|
||||
<a
|
||||
href="../teams/{runner.group.id}"
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800">{runner.group.parentGroup.name} > {runner.group.name}</a>
|
||||
{/if}
|
||||
{#if runner.group.responseType === 'RUNNERORGANIZATION'}
|
||||
<a
|
||||
href="../orgs/{runner.group.id}"
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800">{runner.group.name}</a>
|
||||
{/if}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
{runner.distance /1000 } km
|
||||
</td>
|
||||
{#if active_deletes[runner.id] === true}
|
||||
<td
|
||||
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||
<button
|
||||
on:click={() => {
|
||||
active_deletes[runner.id] = false;
|
||||
}}
|
||||
tabindex="0"
|
||||
class="ml-4 text-indigo-600 hover:text-indigo-900 cursor-pointer">{$_('cancel-delete')}</button>
|
||||
<button
|
||||
on:click={() => {
|
||||
RunnerService.runnerControllerRemove(runner.id, true)
|
||||
.then((resp) => {
|
||||
current_runners = current_runners.filter((obj) => obj.id !== runner.id);
|
||||
})
|
||||
.catch((err) => {});
|
||||
}}
|
||||
tabindex="0"
|
||||
class="ml-4 text-red-600 hover:text-red-900 cursor-pointer">{$_('confirm-delete')}</button>
|
||||
</td>
|
||||
{:else}
|
||||
<td
|
||||
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||
<a
|
||||
href="./{runner.id}"
|
||||
class="text-indigo-600 hover:text-indigo-900">{$_('details')}</a>
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes('RUNNER:DELETE')}
|
||||
<button
|
||||
on:click={() => {
|
||||
active_deletes[runner.id] = true;
|
||||
}}
|
||||
tabindex="0"
|
||||
class="ml-4 text-red-600 hover:text-red-900 cursor-pointer">{$_('delete')}</button>
|
||||
{/if}
|
||||
</td>
|
||||
{/if}
|
||||
</tr>
|
||||
/>
|
||||
</td>
|
||||
<td>{row.id}</td>
|
||||
<td>{row.firstname}</td>
|
||||
<td>{row.middlename || ""}</td>
|
||||
<td>{row.lastname}</td>
|
||||
<td
|
||||
>{#if row.group.responseType === "RUNNERTEAM"}
|
||||
<a
|
||||
href="../teams/{row.group.id}"
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800"
|
||||
>{row.group.parentGroup.name} > {row.group.name}</a
|
||||
>
|
||||
{/if}
|
||||
{/if}
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{/if}
|
||||
{:catch error}
|
||||
<div class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500">
|
||||
<span class="inline-block align-middle mr-8">
|
||||
<b class="capitalize">{$_('general_promise_error')}</b>
|
||||
{error}
|
||||
</span>
|
||||
</div>
|
||||
{/await}
|
||||
{#if row.group.responseType === "RUNNERORGANIZATION"}
|
||||
<a
|
||||
href="../orgs/{row.group.id}"
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800"
|
||||
>{row.group.name}</a
|
||||
>
|
||||
{/if}</td
|
||||
>
|
||||
<td>{row.distance / 1000} km</td>
|
||||
<td>
|
||||
{#if active_deletes[row.id] === true}
|
||||
<button
|
||||
on:click={() => {
|
||||
active_deletes[row.id] = false;
|
||||
}}
|
||||
tabindex="0"
|
||||
class="ml-4 text-indigo-600 hover:text-indigo-900 cursor-pointer"
|
||||
>{$_("cancel-delete")}</button
|
||||
>
|
||||
<button
|
||||
on:click={() => {
|
||||
RunnerService.runnerControllerRemove(row.id, true)
|
||||
.then((resp) => {
|
||||
current_runners = current_runners.filter(
|
||||
(obj) => obj.id !== row.id
|
||||
);
|
||||
})
|
||||
.catch((err) => {});
|
||||
}}
|
||||
tabindex="0"
|
||||
class="ml-4 text-red-600 hover:text-red-900 cursor-pointer"
|
||||
>{$_("confirm-delete")}</button
|
||||
>
|
||||
{:else}
|
||||
<a
|
||||
href="./{row.id}"
|
||||
class="text-indigo-600 hover:text-indigo-900"
|
||||
>{$_("details")}</a
|
||||
>
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:DELETE")}
|
||||
<button
|
||||
on:click={() => {
|
||||
active_deletes[row.id] = true;
|
||||
}}
|
||||
tabindex="0"
|
||||
class="ml-4 text-red-600 hover:text-red-900 cursor-pointer"
|
||||
>{$_("delete")}</button
|
||||
>
|
||||
{/if}
|
||||
{/if}
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</Datatable>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
thead {
|
||||
background: #fff;
|
||||
}
|
||||
thead {
|
||||
position: sticky;
|
||||
inset-block-start: 0;
|
||||
}
|
||||
tbody td {
|
||||
padding: 4px;
|
||||
}
|
||||
tbody tr:nth-child(even) {
|
||||
background: #fafafa;
|
||||
}
|
||||
tbody tr {
|
||||
transition: all, 0.2s;
|
||||
}
|
||||
tbody tr:hover {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,34 @@
|
||||
<script>
|
||||
export let groups;
|
||||
export let handler;
|
||||
let selected = "all";
|
||||
</script>
|
||||
|
||||
<th>
|
||||
<select
|
||||
on:input={() => {
|
||||
setTimeout(() => {
|
||||
if (`${selected}`.trim()) {
|
||||
const value = selected;
|
||||
handler.filter(value, (runner) => {
|
||||
if (
|
||||
runner.group.id === value ||
|
||||
runner?.group?.parentGroup?.id === value ||
|
||||
value === "all"
|
||||
)
|
||||
return runner;
|
||||
return "";
|
||||
});
|
||||
}
|
||||
}, 50);
|
||||
}}
|
||||
bind:value={selected}
|
||||
name="groupfilter"
|
||||
id="groupfilter"
|
||||
>
|
||||
<option value="all">Alle</option>
|
||||
{#each groups as g}
|
||||
<option value={g.value}>{g.label}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</th>
|
||||
Reference in new issue
Block a user