Compare commits

...

21 Commits

Author SHA1 Message Date
96c55db63d Added translation
ref #143
2023-02-02 17:13:16 +01:00
fecb07ee37 Re-added copy modal
ref #143
2023-02-02 17:11:46 +01:00
e10c6480a5 Removed Key after creation
ref #143
2023-02-02 17:08:29 +01:00
f3cc07c009 Fixed imports and naming 2023-02-02 17:05:34 +01:00
068076dd47 Added Statsclients to sidebar
ref #143
2023-02-02 17:03:18 +01:00
02158605be Basic statsclient detail
ref #143
2023-02-02 17:00:49 +01:00
674e6a90ec Updated mounted variables 2023-02-02 16:54:37 +01:00
f679330466 Updated Add modal
ref #143
2023-02-02 16:51:39 +01:00
93fc7c2e83 Updated deletion modal
ref #143
2023-02-02 16:46:18 +01:00
f299617c60 First page for statsclients
ref #143
2023-02-02 16:42:17 +01:00
28cbc5b98c Bumped apiclient
ref #143
2023-02-02 16:21:11 +01:00
c28f1ee0bc Bumped apiclient
ref #143
2023-02-02 16:04:21 +01:00
cff112d705 Pinned versions
Some checks failed
continuous-integration/drone/push Build is failing
2023-02-02 16:02:52 +01:00
9fc4ad63c4 🚀RELEASE v0.15.6
All checks were successful
continuous-integration/drone/push Build is passing
2021-07-19 17:52:02 +02:00
97054a71c1 new license file version [CI SKIP] 2021-07-19 15:49:32 +00:00
2391668a25 Fixed donations getting reduced to the first one on certificates
All checks were successful
continuous-integration/drone/push Build is passing
2021-07-19 17:47:27 +02:00
717d33547c 🚀RELEASE v0.15.5
All checks were successful
continuous-integration/drone/push Build is passing
2021-07-05 17:23:43 +02:00
997be32679 Merge pull request 'Fixed kilometer conversion' (#142) from bugfix/141-runner_kilometers into dev
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #142
2021-07-05 15:22:55 +00:00
134f00c40e Fixed kilometer conversion
ref #141
2021-07-05 17:15:50 +02:00
47c898bdfd Merge branch 'dev' of https://git.odit.services/lfk/frontend into dev
All checks were successful
continuous-integration/drone/push Build is passing
2021-07-05 17:01:59 +02:00
e752ee12d1 new license file version [CI SKIP] 2021-07-05 15:01:42 +00:00
20 changed files with 1708 additions and 935 deletions

1
.gitignore vendored
View File

@@ -8,3 +8,4 @@ public/index.html
.yarn .yarn
.pnp.js .pnp.js
.yarnrc.yml .yarnrc.yml
pnpm-lock.yaml

View File

@@ -2,9 +2,26 @@
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.
#### [0.15.6](https://git.odit.services/lfk/frontend/compare/0.15.5...0.15.6)
- Fixed donations getting reduced to the first one on certificates [`2391668`](https://git.odit.services/lfk/frontend/commit/2391668a25a1e11a1409df572d77ad1635070fbc)
- new license file version [CI SKIP] [`97054a7`](https://git.odit.services/lfk/frontend/commit/97054a71c1ab8a045762a55148124965c6994373)
#### [0.15.5](https://git.odit.services/lfk/frontend/compare/0.15.4...0.15.5)
> 5 July 2021
- 🚀RELEASE v0.15.5 [`717d335`](https://git.odit.services/lfk/frontend/commit/717d33547c3378424dd720005da9952f8a753f1a)
- Merge pull request 'Fixed kilometer conversion' (#142) from bugfix/141-runner_kilometers into dev [`997be32`](https://git.odit.services/lfk/frontend/commit/997be32679dc38c9fb0e92b6ce011057b854d99d)
- Fixed kilometer conversion [`134f00c`](https://git.odit.services/lfk/frontend/commit/134f00c40e0c8252e7604a73151e8d6685b2c61d)
- new license file version [CI SKIP] [`e752ee1`](https://git.odit.services/lfk/frontend/commit/e752ee12d17a4423f4364f7766eafbe7d4cef2d1)
#### [0.15.4](https://git.odit.services/lfk/frontend/compare/0.15.3...0.15.4) #### [0.15.4](https://git.odit.services/lfk/frontend/compare/0.15.3...0.15.4)
> 5 July 2021
- Merge pull request 'fix total donation sum in dashboard' (#140) from bugfix/139-total-donation-sum-is-wrong into dev [`#139`](https://git.odit.services/lfk/frontend/issues/139) - Merge pull request 'fix total donation sum in dashboard' (#140) from bugfix/139-total-donation-sum-is-wrong into dev [`#139`](https://git.odit.services/lfk/frontend/issues/139)
- 🚀RELEASE v0.15.4 [`cc4515f`](https://git.odit.services/lfk/frontend/commit/cc4515ff66b1c1de3747d0ee6cc465574accedb7)
- divide by 100 + toFixes(2) [`b246f2b`](https://git.odit.services/lfk/frontend/commit/b246f2b349b06d1adea318dfad58f97fb1a249bb) - divide by 100 + toFixes(2) [`b246f2b`](https://git.odit.services/lfk/frontend/commit/b246f2b349b06d1adea318dfad58f97fb1a249bb)
#### [0.15.3](https://git.odit.services/lfk/frontend/compare/0.15.2...0.15.3) #### [0.15.3](https://git.odit.services/lfk/frontend/compare/0.15.2...0.15.3)

View File

@@ -13,7 +13,7 @@
</head> </head>
<body> <body>
<span style="display: none;visibility: hidden;" id="buildinfo">RELEASE_INFO-0.15.4-RELEASE_INFO</span> <span style="display: none;visibility: hidden;" id="buildinfo">RELEASE_INFO-0.15.6-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>
<script type="module" src="/src/main.js"></script> <script type="module" src="/src/main.js"></script>

View File

@@ -1,6 +1,6 @@
{ {
"name": "@odit/lfk-frontend", "name": "@odit/lfk-frontend",
"version": "0.15.4", "version": "0.15.6",
"scripts": { "scripts": {
"i18n-order": "node order.js", "i18n-order": "node order.js",
"dev": "vite", "dev": "vite",
@@ -10,26 +10,26 @@
}, },
"license": "CC-BY-NC-SA-4.0", "license": "CC-BY-NC-SA-4.0",
"devDependencies": { "devDependencies": {
"@odit/lfk-client-js": "0.11.0", "@odit/lfk-client-js": "0.13.1",
"@odit/license-exporter": "0.0.11", "@odit/license-exporter": "0.0.11",
"@sveltejs/vite-plugin-svelte": "1.0.0-next.6", "@sveltejs/vite-plugin-svelte": "1.0.0-next.6",
"@types/html-minifier": "4.0.0", "@types/html-minifier": "4.0.0",
"auto-changelog": "2.2.1", "auto-changelog": "2.2.1",
"autoprefixer": "^10.2.5", "autoprefixer": "10.2.5",
"check-password-strength": "2.0.2", "check-password-strength": "2.0.2",
"csvtojson": "2.0.10", "csvtojson": "2.0.10",
"gridjs": "3.4.0", "gridjs": "3.4.0",
"html-minifier": "4.0.0", "html-minifier": "4.0.0",
"localforage": "1.9.0", "localforage": "1.9.0",
"marked": "2.0.3", "marked": "2.0.3",
"postcss": "^8.2.10", "postcss": "8.2.10",
"release-it": "14.6.1", "release-it": "14.6.1",
"svelte": "3.37.0", "svelte": "3.37.0",
"svelte-focus-trap": "1.2.0", "svelte-focus-trap": "1.2.0",
"svelte-i18n": "3.3.9", "svelte-i18n": "3.3.9",
"svelte-preprocess": "^4.7.0", "svelte-preprocess": "4.7.0",
"svelte-select": "3.17.0", "svelte-select": "3.17.0",
"tailwindcss": "^2.1.1", "tailwindcss": "2.1.1",
"tinro": "0.6.1", "tinro": "0.6.1",
"toastify-js": "1.10.0", "toastify-js": "1.10.0",
"validator": "13.5.2", "validator": "13.5.2",

File diff suppressed because one or more lines are too long

View File

@@ -72,6 +72,8 @@
import Scans from "./components/scans/Scans.svelte"; import Scans from "./components/scans/Scans.svelte";
import ScanDetail from "./components/scans/ScanDetail.svelte"; import ScanDetail from "./components/scans/ScanDetail.svelte";
import Cards from "./components/cards/Cards.svelte"; import Cards from "./components/cards/Cards.svelte";
import StatsClients from "./components/statsclients/StatsClients.svelte";
import StatsClientDetail from "./components/statsclients/StatsClientDetail.svelte";
store.init(); store.init();
</script> </script>
@@ -206,6 +208,14 @@
<ScanStationDetail {params} /> <ScanStationDetail {params} />
</Route> </Route>
</Route> </Route>
<Route path="/statsclients/*">
<Route path="/">
<StatsClients />
</Route>
<Route path="/:clientid" let:params>
<StatsClientDetail {params} />
</Route>
</Route>
<Route path="/about"> <Route path="/about">
<About /> <About />
</Route> </Route>

View File

@@ -256,6 +256,26 @@
<span>{$_('scanstations')}</span> <span>{$_('scanstations')}</span>
</a> </a>
{/if} {/if}
{#if store.state.jwtinfo.userdetails.permissions.includes('STATSCLIENT:GET')}
<a
class:bg-gray-100={$router.path === '/statsclients/'}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
href="/statsclients/">
<svg
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600"
fill="currentColor"
width="24"
height="24"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"><path
fill="none"
d="M0 0h24v24H0z" />
<path
fill="currentColor"
d="M4 5v11h16V5H4zM2 4a1 1 0 011-1h18a1 1 0 011 1v14H2V4zM1 19h22v2H1v-2z" /></svg>
<span>{$_('statsclients')}</span>
</a>
{/if}
<a <a
class:bg-gray-100={$router.path === '/settings/'} class:bg-gray-100={$router.path === '/settings/'}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"

View File

@@ -40,7 +40,8 @@
const current_donations = (await DonationService.donationControllerGetAll()) || []; const current_donations = (await DonationService.donationControllerGetAll()) || [];
let certificateRunners = []; let certificateRunners = [];
for (let runner of generate_runners) { for (let runner of generate_runners) {
runner.distanceDonations = current_donations.find((d) => d.runner?.id == runner.id) || []; runner.distanceDonations = current_donations.filter((d) => d.runner?.id == runner.id) || [];
console.log(runner.distanceDonations)
certificateRunners.push(runner); certificateRunners.push(runner);
} }
fetch( fetch(
@@ -103,7 +104,7 @@
); );
let certificateRunners = []; let certificateRunners = [];
for (let runner of runners) { for (let runner of runners) {
runner.distanceDonations = current_donations.find((d) => d.runner?.id == runner.id) || []; runner.distanceDonations = current_donations.filter((d) => d.runner?.id == runner.id) || [];
certificateRunners.push(runner); certificateRunners.push(runner);
} }
fetch( fetch(
@@ -166,7 +167,7 @@
let runners = await RunnerOrganizationService.runnerOrganizationControllerGetRunners(o.id, true) let runners = await RunnerOrganizationService.runnerOrganizationControllerGetRunners(o.id, true)
let certificateRunners = []; let certificateRunners = [];
for (let runner of runners) { for (let runner of runners) {
runner.distanceDonations = current_donations.find((d) => d.runner?.id == runner.id) || []; runner.distanceDonations = current_donations.filter((d) => d.runner?.id == runner.id) || [];
certificateRunners.push(runner); certificateRunners.push(runner);
} }
await fetch( await fetch(
@@ -219,7 +220,7 @@
); );
let certificateRunners = []; let certificateRunners = [];
for (let runner of runners) { for (let runner of runners) {
runner.distanceDonations = current_donations.find((d) => d.runner?.id == runner.id) || []; runner.distanceDonations = current_donations.filter((d) => d.runner?.id == runner.id) || [];
certificateRunners.push(runner); certificateRunners.push(runner);
} }
await fetch( await fetch(

View File

@@ -294,7 +294,7 @@
<div class="text-sm w-full"> <div class="text-sm w-full">
<span class="font-medium text-gray-700">{$_('distance')}</span> <span class="font-medium text-gray-700">{$_('distance')}</span>
<br /> <br />
<span class="text-gray-700">{original_data.distance} km</span> <span class="text-gray-700">{original_data.distance /1000 } km</span>
</div> </div>
</section> </section>
{:catch error} {:catch error}

View File

@@ -206,7 +206,7 @@
{/if} {/if}
</td> </td>
<td class="px-6 py-4 whitespace-nowrap"> <td class="px-6 py-4 whitespace-nowrap">
{runner.distance} {runner.distance /1000 } km
</td> </td>
{#if active_deletes[runner.id] === true} {#if active_deletes[runner.id] === true}
<td <td

View File

@@ -0,0 +1,151 @@
<script>
import { _ } from "svelte-i18n";
import { clickOutside } from "../base/outsideclick";
import { focusTrap } from "svelte-focus-trap";
import { StatsClientService } from "@odit/lfk-client-js";
import Toastify from "toastify-js";
export let modal_open;
export let new_client;
export let current_clients;
export let copy_modal_open;
function focus(el) {
el.focus();
}
$: description = "";
$: createbtnenabled = description != "";
$: processed_last_submit = true;
(() => {
document.onkeydown = (e) => {
e = e || window.event;
if (e.key === "Escape") {
modal_open = false;
}
if (e.keyCode === 13) {
if (createbtnenabled === true) {
createbtnenabled = false;
submit();
}
}
};
})();
function submit() {
if (processed_last_submit === true) {
processed_last_submit = false;
const toast = Toastify({
text: $_("statsclient-is-being-added"),
duration: -1,
}).showToast();
StatsClientService.statsClientControllerPost({description})
.then((result) => {
description = "";
modal_open = false;
//
Toastify({
text: $_("scanstation-added"),
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
current_clients.push(result);
current_clients = current_clients;
new_client = result;
copy_modal_open = true;
})
.catch((err) => {
//
})
.finally(() => {
processed_last_submit = true;
//
toast.hideToast();
});
}
}
</script>
{#if modal_open}
<div
class="fixed z-10 inset-0 overflow-y-auto"
use:focusTrap
use:clickOutside
on:click_outside={() => {
modal_open = false;
}}>
<div
class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
<div
class="absolute inset-0 bg-gray-500 opacity-75"
data-id="modal_backdrop" />
</div>
<span
class="hidden sm:inline-block sm:align-middle sm:h-screen"
aria-hidden="true">&#8203;</span>
<div
class="inline-block align-bottom bg-white rounded-lg text-left shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full"
role="dialog"
aria-modal="true"
aria-labelledby="modal-headline">
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div class="sm:flex sm:items-start">
<div
class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10">
<svg
class="h-6 w-6 text-blue-600"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"><path fill="none" d="M0 0h24v24H0z" />
<path
d="M4 5v11h16V5H4zM2 4a1 1 0 011-1h18a1 1 0 011 1v14H2V4zM1 19h22v2H1v-2z" /></svg>
</div>
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<h3 class="text-lg leading-6 font-medium text-gray-900">
{$_('create-a-new-statsclient')}
</h3>
<div class="mt-2 mb-6">
<p class="text-sm text-gray-500">
{$_('please-provide-the-required-information-to-create-a-new-statsclient')}
</p>
</div>
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6">
<label
for="description"
class="block text-sm font-medium text-gray-700">{$_('description')}</label>
<input
use:focus
autocomplete="off"
placeholder={$_('description')}
bind:value={description}
type="text"
name="description"
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" />
</div>
</div>
</div>
</div>
</div>
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<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 sm:ml-3 sm:w-auto sm:text-sm">
{$_('create')}
</button>
<button
on:click={() => {
modal_open = false;
}}
type="button"
class="mt-3 w-full inline-flex 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 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
{$_('cancel')}
</button>
</div>
</div>
</div>
</div>
{/if}

View File

@@ -0,0 +1,92 @@
<script>
import { _ } from "svelte-i18n";
import { clickOutside } from "../base/outsideclick";
import { focusTrap } from "svelte-focus-trap";
import { ScanStationService } from "@odit/lfk-client-js";
import Toastify from "toastify-js";
import { createEventDispatcher } from "svelte";
export let modal_open;
export let delete_station;
const dispatch = createEventDispatcher();
function cancelDelete() {
modal_open = false;
dispatch("cancelDelete", { id: delete_station.id });
}
function deleteClient() {
ScanStationService.donorControllerRemove(
delete_station.id,
true
)
.then((resp) => {
Toastify({
text: $_('statsclient-deleted'),
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
location.replace("./");
})
.catch((err) => {});
}
</script>
{#if modal_open}
<div
class="fixed z-10 inset-0 overflow-y-auto"
use:focusTrap
use:clickOutside
on:click_outside={cancelDelete}>
<div
class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
<div
class="absolute inset-0 bg-gray-500 opacity-75"
data-id="modal_backdrop" />
</div>
<span
class="hidden sm:inline-block sm:align-middle sm:h-screen"
aria-hidden="true">&#8203;</span>
<div
class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full"
role="dialog"
aria-modal="true"
aria-labelledby="modal-headline">
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div class="sm:flex sm:items-start">
<div
class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10">
<svg class="h-6 w-6 text-blue-600" fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M4 5v11h16V5H4zM2 4a1 1 0 011-1h18a1 1 0 011 1v14H2V4zM1 19h22v2H1v-2z"/></svg>
</div>
<!-- <div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<h3 class="text-lg leading-6 font-medium text-gray-900">
{$_('attention')}
</h3>
<div class="mt-2 mb-6">
<p class="text-sm text-gray-500">
{$_(
'do-you-want-to-delete-this-donor-with-all-related-donations'
)}
<br />
{$_('all-associated-scans-will-get-deleted-as-well')}
</p>
</div>
</div> -->
</div>
</div>
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<button
on:click={deleteClient}
type="button"
class="w-full inline-flex 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:text-sm">
{$_('confirm-delete-statsclient')}
</button>
<button
on:click={cancelDelete}
type="button"
class="mt-3 w-full inline-flex 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 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
{$_('cancel-keep-statsclient')}
</button>
</div>
</div>
</div>
</div>
{/if}

View File

@@ -0,0 +1,129 @@
<script>
import { _ } from "svelte-i18n";
import { focusTrap } from "svelte-focus-trap";
import Toastify from "toastify-js";
import { tick, createEventDispatcher } from "svelte";
export let copy_modal_open;
export let new_client;
const dispatch = createEventDispatcher();
let valueCopy = null;
let areaDom;
let copied = false;
function close() {
copy_modal_open = false;
}
async function copy() {
valueCopy = new_client.key;
await tick();
areaDom.focus();
areaDom.select();
try {
const successful = document.execCommand("copy");
if (!successful) {
throw new Error();
}
Toastify({
text: $_("copied-token-to-clipboard"),
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
copied = true;
} catch (err) {
Toastify({
text: $_("error-whyile-copying-to-clipboard"),
duration: 500,
backgroundColor:
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)",
}).showToast();
}
// we can notifi by event or storage about copy status
valueCopy = null;
}
</script>
{#if copy_modal_open}
{#if valueCopy != null}
<textarea bind:this={areaDom}>{valueCopy}</textarea>
{/if}
<div class="fixed z-10 inset-0 overflow-y-auto" use:focusTrap>
<div
class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
<div
class="absolute inset-0 bg-gray-500 opacity-75"
data-id="modal_backdrop" />
</div>
<span
class="hidden sm:inline-block sm:align-middle sm:h-screen"
aria-hidden="true">&#8203;</span>
<div
class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full"
role="dialog"
aria-modal="true"
aria-labelledby="modal-headline">
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div class="sm:flex sm:items-start">
<div
class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10">
<svg
class="h-6 w-6 text-blue-600"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z" />
<path
d="M4 5v11h16V5H4zM2 4a1 1 0 011-1h18a1 1 0 011 1v14H2V4zM1 19h22v2H1v-2z" /></svg>
</div>
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<h3 class="text-lg leading-6 font-medium text-gray-900">
{$_('token')}
</h3>
<div class="mt-2 mb-6">
<p class="text-sm text-gray-500">
{$_('the-statsclient-api-token-will-only-get-displayed-once-you-wont-be-able-to-change-or-view-it-again')}
<br />
{$_('please-copy-the-token-and-store-it-somewhere-save')}
</p>
</div>
<div class="mt-2 mb-6">
<label
for="token"
class="block text-sm font-medium text-gray-700">{$_('token')}</label>
<div on:click={copy} class="inline-flex">
<p
name="token"
class:bg-green-200={copied}
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 p-2">
{new_client.key}
</p>
<div
class="bg-gray-200 border-gray-300 border-t border-b border-r text-black rounded-r-md sm:text-sm p-2 mt-1 cursor-pointer">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"><path fill="none" d="M0 0h24v24H0z" />
<path
fill="currentColor"
d="M7 4V2h10v2h3l1 1v16a1 1 0 01-1 1H4a1 1 0 01-1-1V5l1-1h3zm0 2H5v14h14V6h-2v2H7V6zm2-2v2h6V4H9z" /></svg>
</div>
</div>
<p class="text-gray-500 text-xs">
{$_('click-to-copy-token-to-clipboard')}
</p>
</div>
</div>
</div>
</div>
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<button
on:click={close}
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-green-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">
{$_('yes-i-copied-the-token')}
</button>
</div>
</div>
</div>
</div>
{/if}

View File

@@ -0,0 +1,119 @@
<script>
import { t, _ } from "svelte-i18n";
import store from "../../store";
import Toastify from "toastify-js";
import PromiseError from "../base/PromiseError.svelte";
import ConfirmStatsClientDeletion from "./ConfirmStatsClientDeletion.svelte";
import { StatsClientService } from "@odit/lfk-client-js";
let data_loaded = false;
let modal_open;
let delete_client;
export let params;
$: delete_triggered = false;
$: original_data = {};
const promise = StatsClientService.statsClientControllerGetOne(
params.clientid
).then((data) => {
data_loaded = true;
original_data = Object.assign(original_data, data);
});
function deleteClient() {
StatsClientService.statsClientControllerRemove(original_data.id, false)
.then((resp) => {
Toastify({
text: $_("statsclient-deleted"),
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
location.replace("./");
})
.catch((err) => {
modal_open = true;
delete_client = original_data;
});
}
</script>
<ConfirmStatsClientDeletion bind:modal_open bind:delete_client />
{#await promise}
{$_('loading-statsclient-details')}
{:then}
<section class="container p-5 select-none">
<div class="flex flex-row mb-4">
<div class="w-full">
<nav class="w-full flex">
<ol class="list-none flex flex-row items-center justify-start">
<li class="flex items-center">
<svg
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"><path fill="none" d="M0 0h24v24H0z" />
<path
d="M4 5v11h16V5H4zM2 4a1 1 0 011-1h18a1 1 0 011 1v14H2V4zM1 19h22v2H1v-2z" /></svg>
</li>
<li class="flex items-center ml-2">
<a class="mr-2" href="./">{$_('statsclient')}</a><svg
stroke="currentColor"
fill="none"
stroke-width="2"
viewBox="0 0 24 24"
stroke-linecap="round"
stroke-linejoin="round"
class="h-3 w-3 mr-2 stroke-current"
height="1em"
width="1em"
xmlns="http://www.w3.org/2000/svg"><line
x1="5"
y1="12"
x2="19"
y2="12" />
<polyline points="12 5 19 12 12 19" /></svg>
</li>
<li class="flex items-center">
<span class="mr-2">#{original_data.id}</span>
</li>
</ol>
</nav>
</div>
</div>
<div class="mb-8 text-3xl font-extrabold leading-tight">
#{original_data.id}
<span data-id="stations_actions_${original_data.id}">
{#if store.state.jwtinfo.userdetails.permissions.includes('STATSCLIENT:DELETE')}
{#if delete_triggered}
<button
on:click={deleteClient}
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:text-sm">{$_('confirm-deletion')}</button>
<button
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:text-sm">{$_('cancel')}</button>
{/if}
{#if !delete_triggered}
<button
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:text-sm">{$_('delete-statsclient')}</button>
{/if}
{/if}
</span>
</div>
<!-- -->
<div class="text-sm w-full">
<label
for="description"
class="font-medium text-gray-700">{$_('description')}</label>
<p
name="description"
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" >
{original_data.description}</p>
</div>
</section>
{:catch error}
<PromiseError {error} />
{/await}

View File

@@ -0,0 +1,33 @@
<script>
import { _ } from "svelte-i18n";
import store from "../../store";
import AddStatsClientModal from "./AddStatsClientModal.svelte";
import CopyStatsClientTokenModal from "./CopyStatsClientTokenModal.svelte";
import StatsClientsOverview from "./StatsClientsOverview.svelte";
export let modal_open = false;
export let copy_modal_open = false;
export let new_client = {};
let current_clients = [];
</script>
<section class="container p-5">
<span class="mb-1 text-3xl font-extrabold leading-tight">
{$_('statsclients')}
{#if store.state.jwtinfo.userdetails.permissions.includes('STATSCLIENT:CREATE')}
<button
on:click={() => {
modal_open = true;
}}
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 sm:ml-3 sm:w-auto sm:text-sm">
{$_('create-a-new-statsclient')}
</button>
{/if}
</span>
<StatsClientsOverview bind:current_clients bind:modal_open bind:new_client bind:copy_modal_open />
</section>
{#if store.state.jwtinfo.userdetails.permissions.includes('STATSCLIENT:CREATE')}
<AddStatsClientModal bind:modal_open bind:current_clients bind:new_client bind:copy_modal_open/>
<CopyStatsClientTokenModal bind:copy_modal_open bind:new_client />
{/if}

View File

@@ -0,0 +1,21 @@
<script>
import { _ } from "svelte-i18n";
import AddStatsClientModal from "./AddStatsClientModal.svelte";
import CopyScanStationTokenModal from "./CopyStatsClientTokenModal.svelte";
import scanstations_empty from "./statsclients_empty.svg";
let modal_open = false;
let copy_modal_open = false;
let new_client = {};
let current_clients = [];
</script>
<div class="text-center items-center justify-center">
<p class="mb-16 text-lg text-gray-500">
<img class="w-full h-44" src={scanstations_empty} alt="" />
<span class="font-bold">{$_('you-dont-have-any-scanclients-yet')}.</span><br />
<span>{$_('add-the-first-statsclient')}</span>
</p>
</div>
<AddStatsClientModal bind:modal_open bind:current_clients bind:new_client bind:copy_modal_open/>
<CopyScanStationTokenModal bind:copy_modal_open bind:new_client />

View File

@@ -0,0 +1,150 @@
<script>
import { _ } from "svelte-i18n";
import Toastify from "toastify-js";
import { StatsClientService } from "@odit/lfk-client-js";
const promise = StatsClientService.statsClientControllerGetAll().then(
(result) => {
current_clients = result;
}
);
import store from "../../store";
import StatsClientsEmptyState from "./StatsClientsEmptyState.svelte";
import ConfirmStatsClientDeletion from "./ConfirmStatsClientDeletion.svelte";
$: searchvalue = "";
$: active_deletes = [];
let delete_client = {};
let modal_open = false;
export let current_clients = [];
</script>
<ConfirmStatsClientDeletion
on:cancelDelete={(event) => {
modal_open = false;
active_deletes[event.detail.id] = false;
}}
bind:modal_open
bind:delete_client />
{#if store.state.jwtinfo.userdetails.permissions.includes('STATSCLIENT:GET')}
{#await 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">{$_('statsclients-are-being-loaded')}</p>
<p class="text-sm">{$_('this-might-take-a-moment')}</p>
</div>
{:then}
{#if current_clients.length === 0}
<StatsClientsEmptyState />
{:else}
<input
type="search"
bind:value={searchvalue}
placeholder={$_('datatable.search')}
aria-label={$_('datatable.search')}
class="gridjs-input gridjs-search-input mb-4" />
<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">
<tr>
<th
scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
{$_('description')}
</th>
<th
scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
{$_('prefix')}
</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_clients as c}
{#if Object.values(c)
.toString()
.toLowerCase()
.includes(searchvalue)}
<tr data-rowid="station_{c.id}">
<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">
{c.description}
</div>
</div>
</div>
</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">
{c.prefix}
</div>
</div>
</div>
</td>
{#if active_deletes[c.id] === true}
<td
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<button
on:click={() => {
active_deletes[c.id] = false;
}}
tabindex="0"
class="ml-4 text-indigo-600 hover:text-indigo-900 cursor-pointer">{$_('cancel-delete')}</button>
<button
on:click={() => {
StatsClientService.statsClientControllerRemove(c.id, false)
.then((resp) => {
current_clients = current_clients.filter((obj) => obj.id !== c.id);
Toastify({
text: $_('statsclient-deleted'),
duration: 500,
backgroundColor:
'linear-gradient(to right, #00b09b, #96c93d)',
}).showToast();
})
.catch((err) => {
modal_open = true;
delete_client = c;
});
}}
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="/statsclients/{c.id}"
class="text-indigo-600 hover:text-indigo-900">{$_('details')}</a>
{#if store.state.jwtinfo.userdetails.permissions.includes('STATSCLIENT:DELETE')}
<button
on:click={() => {
active_deletes[c.id] = true;
}}
tabindex="0"
class="ml-4 text-red-600 hover:text-red-900 cursor-pointer">{$_('delete')}</button>
{/if}
</td>
{/if}
</tr>
{/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}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@@ -457,5 +457,19 @@
"you-have-to-provide-an-organization": "Du musst eine Organisation angeben", "you-have-to-provide-an-organization": "Du musst eine Organisation angeben",
"you-have-to-save-your-changes-to-generate-a-link": "Du musst deine Änderungen speichern, um einen Link zu generieren.", "you-have-to-save-your-changes-to-generate-a-link": "Du musst deine Änderungen speichern, um einen Link zu generieren.",
"you-must-create-at-least-one-card-or-cancel": "Du musst mindestens eine Blankokarte erstellen (oder abbrechen).", "you-must-create-at-least-one-card-or-cancel": "Du musst mindestens eine Blankokarte erstellen (oder abbrechen).",
"zip-postal-code": "Postleitzahl" "zip-postal-code": "Postleitzahl",
"statsclients": "Statsclient (aka Beamershow)",
"create-a-new-statsclient": "Neuen Statsclient erstellen",
"statsclients-are-being-loaded": "Statsclients werden geladen",
"prefix": "Prefix",
"key": "Schlüssel",
"statsclient-deleted": "Statsclient wurde gelöscht",
"cancel-keep-statsclient": "Abbrechen und Statsclient behalten",
"confirm-delete-statsclient": "Bestätigung, Statsclient löschen",
"statsclient-is-being-added": "Statsclient wird angelegt...",
"please-provide-the-required-information-to-create-a-new-statsclient": "Bitte gebe alle für einen Statsclient notwendigen Informationen an",
"you-dont-have-any-scanclients-yet": "Es gibt noch keine Statsclients",
"add-the-first-statsclient": "Erstelle deinen ersten Statsclient.",
"the-statsclient-api-token-will-only-get-displayed-once-you-wont-be-able-to-change-or-view-it-again": "Der Statsclient Token wird nur einmal angezeigt - du kannst ihn nicht ändern oder ihn dir nochmal anzeigen lassen!",
"delete-statsclient": "Statsclient löschen"
} }

View File

@@ -458,5 +458,19 @@
"you-have-to-provide-an-organization": "You have to provide an organization", "you-have-to-provide-an-organization": "You have to provide an organization",
"you-have-to-save-your-changes-to-generate-a-link": "You have to save your changes to generate a link.", "you-have-to-save-your-changes-to-generate-a-link": "You have to save your changes to generate a link.",
"you-must-create-at-least-one-card-or-cancel": "You must create at least one card (or cancel).", "you-must-create-at-least-one-card-or-cancel": "You must create at least one card (or cancel).",
"zip-postal-code": "ZIP/ postal code" "zip-postal-code": "ZIP/ postal code",
"statsclients": "Statsclients (aka Beamershow)",
"create-a-new-statsclient": "Create a new statsclient",
"statsclients-are-being-loaded": "Loading statsclients",
"prefix": "Prefix",
"key": "Key",
"statsclient-deleted": "Deleted statsclient",
"cancel-keep-statsclient": "Cancel and keep statsclient",
"confirm-delete-statsclient": "Confirm, delete statsclient",
"statsclient-is-being-added": "Statsclient is being added...",
"please-provide-the-required-information-to-create-a-new-statsclient": "Please provide the required information to create a new statsclient",
"you-dont-have-any-scanclients-yet": "You don't have any statsclients yet",
"add-the-first-statsclient": "Add your first statsclient.",
"the-statsclient-api-token-will-only-get-displayed-once-you-wont-be-able-to-change-or-view-it-again": "The statsclient api token will only get displayed once - you won't be able to change or view it again!",
"delete-statsclient": "Delete statsclient"
} }