formatting, full migration to svelte-french-toast

This commit is contained in:
Philipp Dormann 2023-04-26 22:47:42 +02:00
parent 38a665024e
commit 46d076af9d
Signed by: philipp
GPG Key ID: 3BB9ADD52DCA4314
137 changed files with 11984 additions and 9212 deletions

View File

@ -1,6 +1,5 @@
FROM mcr.microsoft.com/vscode/devcontainers/base:alpine-3.12 FROM mcr.microsoft.com/vscode/devcontainers/base:alpine-3.12
RUN apk update RUN apk update
RUN apk add --upgrade nodejs-current npm RUN apk add --upgrade nodejs-current npm
RUN npm i -g yarn rimraf RUN npm i -g pnpm rimraf
RUN rimraf node_modules RUN rimraf node_modules
RUN yarn set version berry

View File

@ -1,20 +1,20 @@
{ {
"name": "Node.js", "name": "Node.js",
"build": { "build": {
"dockerfile": "Dockerfile" "dockerfile": "Dockerfile"
}, },
"settings": { "settings": {
"terminal.integrated.shell.linux": "/bin/sh" "terminal.integrated.shell.linux": "/bin/sh"
}, },
"extensions": [ "extensions": [
"dbaeumer.vscode-eslint", "dbaeumer.vscode-eslint",
"2gua.rainbow-brackets", "2gua.rainbow-brackets",
"christian-kohler.npm-intellisense", "christian-kohler.npm-intellisense",
"remimarsal.prettier-now", "remimarsal.prettier-now",
"svelte.svelte-vscode", "svelte.svelte-vscode",
"lokalise.i18n-ally", "lokalise.i18n-ally",
"fivethree.vscode-svelte-snippets", "fivethree.vscode-svelte-snippets",
"voorjaar.windicss-intellisense" "voorjaar.windicss-intellisense"
], ],
"postCreateCommand": "yarn && yarn dev --open" "postCreateCommand": "yarn && yarn dev --open"
} }

View File

@ -98,4 +98,4 @@ steps:
registry: registry.odit.services registry: registry.odit.services
trigger: trigger:
event: event:
- tag - tag

View File

@ -1,14 +1,12 @@
{ {
"recommendations": [ "recommendations": [
"2gua.rainbow-brackets", "2gua.rainbow-brackets",
"christian-kohler.npm-intellisense", "christian-kohler.npm-intellisense",
"remimarsal.prettier-now", "remimarsal.prettier-now",
"svelte.svelte-vscode", "svelte.svelte-vscode",
"lokalise.i18n-ally", "lokalise.i18n-ally",
"fivethree.vscode-svelte-snippets", "fivethree.vscode-svelte-snippets",
"voorjaar.windicss-intellisense" "voorjaar.windicss-intellisense"
], ],
"unwantedRecommendations": [ "unwantedRecommendations": ["antfu.i18n-ally"]
"antfu.i18n-ally" }
]
}

View File

@ -1,7 +1,7 @@
languageIds: languageIds:
- javascript - javascript
- svelte - svelte
- html - html
monopoly: false monopoly: false
refactorTemplates: refactorTemplates:
- "{$_('$1')}" - "{$_('$1')}"

View File

@ -1,5 +1,5 @@
{ {
"i18n-ally.localesPaths": "src/locales", "i18n-ally.localesPaths": "src/locales",
"i18n-ally.keystyle": "nested", "i18n-ally.keystyle": "nested",
"windicss.enableCodeFolding": false, "windicss.enableCodeFolding": false
} }

View File

@ -1,21 +1,27 @@
# @odit/lfk-frontend # @odit/lfk-frontend
## ✒️ Overview ## ✒️ Overview
This is an API client for [https://git.odit.services/lfk/backend](@lfk/backend) This is an API client for [https://git.odit.services/lfk/backend](@lfk/backend)
This application is intended for use by admin users/ members only. This application is intended for use by admin users/ members only.
## 🚀 Getting Started ## 🚀 Getting Started
``` ```
pnpm i pnpm i
``` ```
## Development ## Development
``` ```
pnpm dev pnpm dev
/ /
pnpm dev --open pnpm dev --open
``` ```
## Build ## Build
``` ```
pnpm build pnpm build
``` ```

View File

@ -1,8 +1,8 @@
version: "3" version: "3"
services: services:
httpd: httpd:
build: . build: .
volumes: volumes:
- ./public/env.sample.js:/usr/share/nginx/html/env.js - ./public/env.sample.js:/usr/share/nginx/html/env.js
ports: ports:
- 4050:80 - 4050:80

View File

@ -1,22 +1,22 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head>
<head> <meta charset="utf-8" />
<meta charset="utf-8" /> <link rel="icon" href="/favicon.png" />
<link rel="icon" href="/favicon.png" /> <link rel="manifest" href="/manifest.webmanifest" />
<link rel="manifest" href="/manifest.webmanifest"> <link rel="apple-touch-icon" href="/lfk-logo.png" />
<link rel="apple-touch-icon" href="/lfk-logo.png"> <meta name="theme-color" content="#FFFFFF" />
<meta name="theme-color" content="#FFFFFF"> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="description" content="Lauf Für Kaya! - Admin" />
<meta name="description" content="Lauf Für Kaya! - Admin" /> <title>Lauf für Kaya! - Admin</title>
<title>Lauf für Kaya! - Admin</title> </head>
</head>
<body>
<body> <span style="display: none; visibility: hidden" id="buildinfo"
<span style="display: none;visibility: hidden;" id="buildinfo">RELEASE_INFO-1.3.4-RELEASE_INFO</span> >RELEASE_INFO-1.3.4-RELEASE_INFO</span
<noscript>You need to enable JavaScript to run this app.</noscript> >
<script src="/env.js"></script> <noscript>You need to enable JavaScript to run this app.</noscript>
<script type="module" src="/src/main.js"></script> <script src="/env.js"></script>
</body> <script type="module" src="/src/main.js"></script>
</body>
</html> </html>

View File

@ -1,16 +1,18 @@
import fs from 'fs' import fs from "fs";
// get all language files // get all language files
const files = fs.readdirSync('./src/locales/'); const files = fs.readdirSync("./src/locales/");
files.forEach((f) => { files.forEach((f) => {
// read file as object // read file as object
const unordered = JSON.parse(fs.readFileSync(`src/locales/${f}`)); const unordered = JSON.parse(fs.readFileSync(`src/locales/${f}`));
// order object by keys alpabetically A-Z // order object by keys alpabetically A-Z
const ordered = Object.keys(unordered).sort().reduce((obj, key) => { const ordered = Object.keys(unordered)
obj[key] = unordered[key]; .sort()
return obj; .reduce((obj, key) => {
}, {}); obj[key] = unordered[key];
// format output as json for commit diff compatibility return obj;
const out = JSON.stringify(ordered, 0, 4); }, {});
// write output file // format output as json for commit diff compatibility
fs.writeFileSync(`src/locales/${f}`, out); const out = JSON.stringify(ordered, 0, 4);
// write output file
fs.writeFileSync(`src/locales/${f}`, out);
}); });

View File

@ -1,62 +1,64 @@
{ {
"name": "@odit/lfk-frontend", "name": "@odit/lfk-frontend",
"version": "1.3.4", "version": "1.3.4",
"type": "module", "type": "module",
"scripts": { "scripts": {
"i18n-order": "node order.js", "i18n-order": "node order.js",
"dev": "vite", "dev": "vite",
"build": "vite build", "format": "prettier --write --plugin-search-dir=. .",
"release": "release-it", "build": "vite build",
"licenses:export": "license-exporter --json -o public" "release": "release-it",
}, "licenses:export": "license-exporter --json -o public"
"license": "CC-BY-NC-SA-4.0", },
"devDependencies": { "license": "CC-BY-NC-SA-4.0",
"@odit/license-exporter": "0.0.12", "devDependencies": {
"@sveltejs/vite-plugin-svelte": "2.1.0", "@odit/license-exporter": "0.0.12",
"auto-changelog": "2.4.0", "@sveltejs/vite-plugin-svelte": "2.1.1",
"autoprefixer": "10.4.14", "auto-changelog": "2.4.0",
"postcss": "8.4.23", "autoprefixer": "10.4.14",
"release-it": "15.10.1", "postcss": "8.4.23",
"svelte-select": "3.17.0", "prettier": "^2.8.8",
"tailwindcss": "3.3.1", "prettier-plugin-svelte": "^2.10.0",
"vite": "4.3.1" "release-it": "15.10.1",
}, "svelte-select": "3.17.0",
"release-it": { "tailwindcss": "3.3.2",
"git": { "vite": "4.3.3"
"commit": true, },
"requireCleanWorkingDir": false, "release-it": {
"commitMessage": "🚀RELEASE v${version}", "git": {
"push": true, "commit": true,
"tag": true, "requireCleanWorkingDir": false,
"tagName": null, "commitMessage": "🚀RELEASE v${version}",
"tagAnnotation": "v${version}" "push": true,
}, "tag": true,
"npm": { "tagName": null,
"publish": false "tagAnnotation": "v${version}"
}, },
"hooks": { "npm": {
"after:bump": "npx auto-changelog --commit-limit false -p -u --hide-credit && git add CHANGELOG.md && node versionbuilder.js && git add index.html && node order.js && git add src/locales" "publish": false
} },
}, "hooks": {
"dependencies": { "after:bump": "npx auto-changelog --commit-limit false -p -u --hide-credit && git add CHANGELOG.md && node versionbuilder.js && git add index.html && node order.js && git add src/locales"
"@odit/lfk-client-js": "1.1.1", }
"@paralleldrive/cuid2": "^2.2.0", },
"@tanstack/svelte-table": "^8.8.5", "dependencies": {
"bwip-js": "^3.4.0", "@odit/lfk-client-js": "1.1.1",
"check-password-strength": "2.0.7", "@paralleldrive/cuid2": "^2.2.0",
"csvtojson": "2.0.10", "@tanstack/svelte-table": "^8.8.6",
"gridjs": "3.4.0", "bwip-js": "^3.4.0",
"localforage": "1.10.0", "check-password-strength": "2.0.7",
"marked": "2.0.3", "csvtojson": "2.0.10",
"svelte": "3.58.0", "gridjs": "3.4.0",
"svelte-french-toast": "1.0.4-beta.0", "localforage": "1.10.0",
"svelte-i18n": "3.6.0", "marked": "4.3.0",
"tinro": "0.6.12", "svelte": "3.58.0",
"toastify-js": "1.12.0", "svelte-french-toast": "1.0.4-beta.0",
"validator": "13.9.0", "svelte-i18n": "3.6.0",
"xlsx": "0.18.5" "tinro": "0.6.12",
}, "validator": "13.9.0",
"volta": { "xlsx": "0.18.5"
"node": "20.0.0" },
} "volta": {
"node": "20.0.0"
}
} }

3779
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
module.exports = { module.exports = {
plugins: { plugins: {
tailwindcss: {}, tailwindcss: {},
autoprefixer: {} autoprefixer: {},
} },
}; };

View File

@ -1,9 +1,10 @@
const config = { const config = {
baseurl: 'http://localhost:4010', baseurl: "http://localhost:4010",
baseurl_documentserver: 'http://localhost:4010/documents', baseurl_documentserver: "http://localhost:4010/documents",
documentserver_key: 'NqZSYTy5AFQ7MppbLW5moqpTk7u7YrNUHKYhKYuThnnya2WpCOIU694hIZT1FzYe', documentserver_key:
// optional "NqZSYTy5AFQ7MppbLW5moqpTk7u7YrNUHKYhKYuThnnya2WpCOIU694hIZT1FzYe",
default_username: 'demo', // optional
default_password: 'demo', default_username: "demo",
prefersHashRouting: true default_password: "demo",
prefersHashRouting: true,
}; };

File diff suppressed because one or more lines are too long

View File

@ -1,34 +1,39 @@
{ {
"name": "Lauf für Kaya! - Admin", "name": "Lauf für Kaya! - Admin",
"short_name": "LfK!Admin", "short_name": "LfK!Admin",
"start_url": "/?utm_source=pwa", "start_url": "/?utm_source=pwa",
"orientation": "portrait-primary", "orientation": "portrait-primary",
"display": "standalone", "display": "standalone",
"background_color": "#fff", "background_color": "#fff",
"theme_color": "#fff", "theme_color": "#fff",
"description": "Lauf für Kaya! - Admin", "description": "Lauf für Kaya! - Admin",
"shortcuts": [ "shortcuts": [
{ {
"name": "Users", "name": "Users",
"url": "/users/?utm_source=pwa" "url": "/users/?utm_source=pwa"
} }
], ],
"icons": [ "icons": [
{ {
"src": "/favicon.png", "src": "/favicon.png",
"sizes": "48x48", "sizes": "48x48",
"type": "image/png" "type": "image/png"
}, },
{ {
"src": "/favicon.png", "src": "/favicon.png",
"sizes": "144x144", "sizes": "144x144",
"type": "image/png" "type": "image/png"
}, },
{ {
"src": "/lfk-logo.png", "src": "/lfk-logo.png",
"sizes": "1540x144", "sizes": "1540x144",
"type": "image/png" "type": "image/png"
}, },
{ "src": "/maskable_icon_x1.png", "sizes": "750x750", "type": "image/png", "purpose": "any maskable" } {
] "src": "/maskable_icon_x1.png",
"sizes": "750x750",
"type": "image/png",
"purpose": "any maskable"
}
]
} }

View File

@ -1 +1 @@
Nostrud tempor dolor aute ea excepteur aute mollit elit eiusmod exercitation. Magna laborum pariatur adipisicing pariatur cupidatat exercitation duis aliquip pariatur sint exercitation deserunt labore. Consectetur id laboris dolore nostrud do velit ipsum. Eu laboris velit do commodo ad ea sint ex cillum. Cillum ipsum qui eiusmod laborum mollit sunt dolore incididunt. Cillum sunt culpa veniam voluptate et qui ut magna anim occaecat ut mollit dolor. Duis irure proident eu incididunt dolore sunt nisi aute dolore amet eu fugiat laboris quis. Nostrud tempor dolor aute ea excepteur aute mollit elit eiusmod exercitation. Magna laborum pariatur adipisicing pariatur cupidatat exercitation duis aliquip pariatur sint exercitation deserunt labore. Consectetur id laboris dolore nostrud do velit ipsum. Eu laboris velit do commodo ad ea sint ex cillum. Cillum ipsum qui eiusmod laborum mollit sunt dolore incididunt. Cillum sunt culpa veniam voluptate et qui ut magna anim occaecat ut mollit dolor. Duis irure proident eu incididunt dolore sunt nisi aute dolore amet eu fugiat laboris quis.

View File

@ -1,5 +1,4 @@
<script> <script>
import "toastify-js/src/toastify.css";
import "gridjs/dist/theme/mermaid.css"; import "gridjs/dist/theme/mermaid.css";
import { Route, router } from "tinro"; import { Route, router } from "tinro";
router.subscribe((routeInfo) => { router.subscribe((routeInfo) => {
@ -28,7 +27,6 @@
}); });
window.onunhandledrejection = (event) => { window.onunhandledrejection = (event) => {
if (event.reason.toString() == "Error: Unauthorized") { if (event.reason.toString() == "Error: Unauthorized") {
console.log("Found 1");
localForage.clear(); localForage.clear();
location.replace("/"); location.replace("/");
} }
@ -72,29 +70,29 @@
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 StatsClients from "./components/statsclients/StatsClients.svelte";
import StatsClientDetail from "./components/statsclients/StatsClientDetail.svelte"; import StatsClientDetail from "./components/statsclients/StatsClientDetail.svelte";
store.init(); store.init();
</script> </script>
<Route> <Route>
{#if $router.path === '/forgot_password'} {#if $router.path === "/forgot_password"}
<Route path="/forgot_password"> <Route path="/forgot_password">
<ForgotPassword /> <ForgotPassword />
</Route> </Route>
{:else if $router.path.includes('/reset')} {:else if $router.path.includes("/reset")}
<Route path="/reset/:resetkey" let:params> <Route path="/reset/:resetkey" let:params>
<ResetPassword {params} /> <ResetPassword {params} />
</Route> </Route>
{:else if $router.path === '/about'} {:else if $router.path === "/about"}
<Route path="/about"> <Route path="/about">
<About /> <About />
</Route> </Route>
{:else if $router.path === '/imprint'} {:else if $router.path === "/imprint"}
<Route path="/imprint"> <Route path="/imprint">
<Imprint /> <Imprint />
</Route> </Route>
{:else if $router.path === '/privacy'} {:else if $router.path === "/privacy"}
<Route path="/privacy"> <Route path="/privacy">
<Privacy /> <Privacy />
</Route> </Route>

View File

@ -1,28 +1,22 @@
<script> <script>
import { ApiError, AuthService } from "@odit/lfk-client-js"; import { AuthService } from "@odit/lfk-client-js";
import toast from "svelte-french-toast";
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import Toastify from "toastify-js";
import "toastify-js/src/toastify.css";
import isEmail from "validator/es/lib/isEmail"; import isEmail from "validator/es/lib/isEmail";
let reset_mail_sent = false; let reset_mail_sent = false;
let usersEmail = ""; let usersEmail = "";
function reset() { function reset() {
if (isEmail(usersEmail)) { if (isEmail(usersEmail)) {
toast.loading($_("mail-validation-in-progress"));
AuthService.authControllerGetResetToken("de", { email: usersEmail }) AuthService.authControllerGetResetToken("de", { email: usersEmail })
.then((resp) => { .then((resp) => {
Toastify({ toast.dismiss();
text: $_("mail-validation-in-progress"),
duration: 3500,
}).showToast();
reset_mail_sent = true; reset_mail_sent = true;
}) })
.catch((err) => {}); .catch((err) => {});
} else { } else {
Toastify({ toast($_("invalid-mail-reset"));
text: $_("invalid-mail-reset"),
duration: 3500,
}).showToast();
} }
} }
</script> </script>
@ -32,17 +26,18 @@
<div class="max-w-md w-full py-12 px-6"> <div class="max-w-md w-full py-12 px-6">
<img style="height:10rem;" class="mx-auto" src="/lfk-logo.png" alt="" /> <img style="height:10rem;" class="mx-auto" src="/lfk-logo.png" alt="" />
<p class="mt-6 text-lg text-center font-bold text-gray-900"> <p class="mt-6 text-lg text-center font-bold text-gray-900">
{$_('application_name')} {$_("application_name")}
</p> </p>
<p class="mt-2 mb-2 text-sm text-center text-gray-900"> <p class="mt-2 mb-2 text-sm text-center text-gray-900">
{$_('password-reset-mail-sent', { values: { usersEmail: usersEmail } })} {$_("password-reset-mail-sent", { values: { usersEmail: usersEmail } })}
</p> </p>
<div class="mt-6"> <div class="mt-6">
<div class="mt-6"> <div class="mt-6">
<a <a
href="/" href="/"
class="block w-full text-center py-2 px-3 border border-gray-300 rounded-md text-gray-900 font-medium hover:border-gray-400 focus:outline-none focus:border-gray-400 sm:text-sm"> class="block w-full text-center py-2 px-3 border border-gray-300 rounded-md text-gray-900 font-medium hover:border-gray-400 focus:outline-none focus:border-gray-400 sm:text-sm"
{$_('goback')} >
{$_("goback")}
</a> </a>
</div> </div>
</div> </div>
@ -53,25 +48,26 @@
<div class="max-w-md w-full py-12 px-6"> <div class="max-w-md w-full py-12 px-6">
<img style="height:10rem;" class="mx-auto" src="/lfk-logo.png" alt="" /> <img style="height:10rem;" class="mx-auto" src="/lfk-logo.png" alt="" />
<p class="mt-6 text-lg text-center font-bold text-gray-900"> <p class="mt-6 text-lg text-center font-bold text-gray-900">
{$_('application_name')} {$_("application_name")}
</p> </p>
<p class="mt-6 text-sm text-center text-gray-900"> <p class="mt-6 text-sm text-center text-gray-900">
{$_('forgot_password')} {$_("forgot_password")}
</p> </p>
<p class="mt-2 mb-2 text-sm text-center text-gray-900"> <p class="mt-2 mb-2 text-sm text-center text-gray-900">
{$_('dont-panic-were-resetting-it')} {$_("dont-panic-were-resetting-it")}
</p> </p>
<div> <div>
<div class="rounded-md shadow-sm"> <div class="rounded-md shadow-sm">
<div> <div>
<input <input
aria-label={$_('e-mail-adress')} aria-label={$_("e-mail-adress")}
name="email" name="email"
type="email" type="email"
required="" required=""
class="border-gray-300 placeholder-gray-500 appearance-none rounded-none relative block w-full px-3 py-2 border text-gray-900 rounded-t-md focus:outline-none focus:shadow-outline-blue focus:border-blue-300 focus:z-10 sm:text-sm" class="border-gray-300 placeholder-gray-500 appearance-none rounded-none relative block w-full px-3 py-2 border text-gray-900 rounded-t-md focus:outline-none focus:shadow-outline-blue focus:border-blue-300 focus:z-10 sm:text-sm"
placeholder={$_('e-mail-adress')} placeholder={$_("e-mail-adress")}
bind:value={usersEmail} /> bind:value={usersEmail}
/>
</div> </div>
</div> </div>
@ -79,19 +75,22 @@
<button <button
on:click={reset} on:click={reset}
type="submit" type="submit"
class="relative block w-full py-2 px-3 border border-transparent rounded-md text-white font-semibold bg-gray-800 hover:bg-gray-700 focus:bg-gray-900 focus:outline-none focus:shadow-outline sm:text-sm"> class="relative block w-full py-2 px-3 border border-transparent rounded-md text-white font-semibold bg-gray-800 hover:bg-gray-700 focus:bg-gray-900 focus:outline-none focus:shadow-outline sm:text-sm"
>
<span class="absolute left-0 inset-y pl-3"> <span class="absolute left-0 inset-y pl-3">
<svg <svg
class="h-5 w-5 text-gray-500" class="h-5 w-5 text-gray-500"
fill="currentColor" fill="currentColor"
viewBox="0 0 20 20"> viewBox="0 0 20 20"
>
<path <path
fill-rule="evenodd" fill-rule="evenodd"
d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z" d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z"
clip-rule="evenodd" /> clip-rule="evenodd"
/>
</svg> </svg>
</span> </span>
{$_('reset-my-password')} {$_("reset-my-password")}
</button> </button>
</div> </div>
<div class="mt-6"> <div class="mt-6">
@ -100,24 +99,30 @@
<div class="w-full border-t border-gray-300" /> <div class="w-full border-t border-gray-300" />
</div> </div>
<div class="relative flex justify-center text-sm"> <div class="relative flex justify-center text-sm">
<span <span class="px-2 bg-gray-100 text-gray-500"
class="px-2 bg-gray-100 text-gray-500">{$_('dont-have-your-email-connected')}</span> >{$_("dont-have-your-email-connected")}</span
>
</div> </div>
</div> </div>
<span <span
class="mt-2 text-sm px-2 bg-gray-100 text-gray-500 justify-center relative flex">{$_('cannot-reset-your-password-directly')}</span> class="mt-2 text-sm px-2 bg-gray-100 text-gray-500 justify-center relative flex"
>{$_("cannot-reset-your-password-directly")}</span
>
<div class="mt-6"> <div class="mt-6">
<a <a
href="mailto:lfk@odit.services" href="mailto:lfk@odit.services"
class="block w-full text-center py-2 px-3 border border-gray-300 rounded-md text-gray-900 font-medium hover:border-gray-400 focus:outline-none focus:border-gray-400 sm:text-sm"> class="block w-full text-center py-2 px-3 border border-gray-300 rounded-md text-gray-900 font-medium hover:border-gray-400 focus:outline-none focus:border-gray-400 sm:text-sm"
{$_('send-a-mail-to-lfk-odit-services')} >
{$_("send-a-mail-to-lfk-odit-services")}
</a> </a>
</div> </div>
<div class="mt-6"> <div class="mt-6">
<a <a
href="/" href="/"
class="block w-full text-center py-2 px-3 border border-gray-300 rounded-md text-gray-900 font-medium hover:border-gray-400 focus:outline-none focus:border-gray-400 sm:text-sm">{$_('goback')}</a> class="block w-full text-center py-2 px-3 border border-gray-300 rounded-md text-gray-900 font-medium hover:border-gray-400 focus:outline-none focus:border-gray-400 sm:text-sm"
>{$_("goback")}</a
>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,52 +1,52 @@
<script context="module"> <script context="module">
import { passwordStrength } from "check-password-strength"; import { passwordStrength } from "check-password-strength";
export function password_strong_enough(password_change) { export function password_strong_enough(password_change) {
let strength = passwordStrength(password_change); let strength = passwordStrength(password_change);
return ( return (
strength?.contains.includes("lowercase") && strength?.contains.includes("lowercase") &&
strength?.contains.includes("uppercase") && strength?.contains.includes("uppercase") &&
strength?.contains.includes("number") && strength?.contains.includes("number") &&
strength?.length > 9 strength?.length > 9
); );
} }
export function password_strong_enough_and_equal( export function password_strong_enough_and_equal(
password_change, password_change,
password_confirm password_confirm
) { ) {
return ( return (
password_strong_enough(password_change) && password_strong_enough(password_change) &&
password_change === password_confirm password_change === password_confirm
); );
} }
</script> </script>
<script> <script>
import { getLocaleFromNavigator, _ } from "svelte-i18n"; import { getLocaleFromNavigator, _ } from "svelte-i18n";
import { passwordStrength as Strength } from "check-password-strength"; import { passwordStrength as Strength } from "check-password-strength";
export let password_change; export let password_change;
export let password_confirm; export let password_confirm;
$: strength = Strength(password_change); $: strength = Strength(password_change);
$: passwords_match = $: passwords_match =
!password_confirm || password_confirm === password_change; !password_confirm || password_confirm === password_change;
</script> </script>
<div class="ml-4"> <div class="ml-4">
<ul class="list-disc font-medium tracking-wide text-red-500 text-xs"> <ul class="list-disc font-medium tracking-wide text-red-500 text-xs">
{#if !strength.contains.includes('lowercase')} {#if !strength.contains.includes("lowercase")}
<li>{$_('must-contain-a-lowercase-letter')}</li> <li>{$_("must-contain-a-lowercase-letter")}</li>
{/if} {/if}
{#if !strength.contains.includes('uppercase')} {#if !strength.contains.includes("uppercase")}
<li>{$_('must-contain-a-uppercase-letter')}</li> <li>{$_("must-contain-a-uppercase-letter")}</li>
{/if} {/if}
{#if !strength.contains.includes('number')} {#if !strength.contains.includes("number")}
<li>{$_('must-contain-a-number')}</li> <li>{$_("must-contain-a-number")}</li>
{/if} {/if}
{#if !(strength.length > 9)} {#if !(strength.length > 9)}
<li>{$_('must-be-at-least-10-characters-long')}</li> <li>{$_("must-be-at-least-10-characters-long")}</li>
{/if} {/if}
{#if !(passwords_match == true)} {#if !(passwords_match == true)}
<li>{$_('passwords-dont-match')}</li> <li>{$_("passwords-dont-match")}</li>
{/if} {/if}
</ul> </ul>
</div> </div>

View File

@ -1,8 +1,7 @@
<script> <script>
import { AuthService } from "@odit/lfk-client-js"; import { AuthService } from "@odit/lfk-client-js";
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import Toastify from "toastify-js"; import toast from "svelte-french-toast";
import "toastify-js/src/toastify.css";
import PasswordStrength, { import PasswordStrength, {
password_strong_enough, password_strong_enough,
} from "../auth/PasswordStrength.svelte"; } from "../auth/PasswordStrength.svelte";
@ -11,101 +10,97 @@
export let params; export let params;
function set_new_password() { function set_new_password() {
if (password.trim() !== "") { if (password.trim() !== "") {
Toastify({ toast.loading($_("password-reset-in-progress"));
text: $_("password-reset-in-progress"),
duration: 3500,
}).showToast();
AuthService.authControllerResetPassword(atob(params.resetkey), { AuthService.authControllerResetPassword(atob(params.resetkey), {
password, password,
}) })
.then((resp) => { .then((resp) => {
Toastify({ toast.dismiss();
text: $_("password-reset-successful"), toast($_("password-reset-successful"));
duration: 3500,
}).showToast();
state = "reset_success"; state = "reset_success";
}) })
.catch((err) => { .catch((err) => {
state = "reset_error"; state = "reset_error";
}); });
} else { } else {
Toastify({ toast.dismiss();
text: $_("please-provide-a-password"), toast.error($_("please-provide-a-password"));
duration: 3500,
}).showToast();
} }
} }
</script> </script>
{#if state === 'reset_success'} {#if state === "reset_success"}
<div class="min-h-screen flex items-center justify-center bg-gray-100"> <div class="min-h-screen flex items-center justify-center bg-gray-100">
<div class="max-w-md w-full py-12 px-6"> <div class="max-w-md w-full py-12 px-6">
<img style="height:10rem;" class="mx-auto" src="/lfk-logo.png" alt="" /> <img style="height:10rem;" class="mx-auto" src="/lfk-logo.png" alt="" />
<p class="mt-6 text-lg text-center font-bold text-gray-900"> <p class="mt-6 text-lg text-center font-bold text-gray-900">
{$_('application_name')} {$_("application_name")}
</p> </p>
<p class="mt-2 mb-2 text-sm text-center text-gray-900 font-bold"> <p class="mt-2 mb-2 text-sm text-center text-gray-900 font-bold">
{$_('successful-password-reset')} {$_("successful-password-reset")}
</p> </p>
<p class="mt-2 mb-2 text-sm text-center text-gray-900"> <p class="mt-2 mb-2 text-sm text-center text-gray-900">
{$_('you-can-now-use-your-new-password-to-log-in-to-your-account')} {$_("you-can-now-use-your-new-password-to-log-in-to-your-account")}
</p> </p>
<div class="mt-6"> <div class="mt-6">
<div class="mt-6"> <div class="mt-6">
<a <a
href="/login/" href="/login/"
class="text-center relative block w-full py-2 px-3 border border-transparent rounded-md text-white font-semibold bg-gray-800 hover:bg-gray-700 focus:bg-gray-900 focus:outline-none focus:shadow-outline sm:text-sm"> class="text-center relative block w-full py-2 px-3 border border-transparent rounded-md text-white font-semibold bg-gray-800 hover:bg-gray-700 focus:bg-gray-900 focus:outline-none focus:shadow-outline sm:text-sm"
{$_('go-to-login')} >
{$_("go-to-login")}
</a> </a>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{:else if state === 'reset_error'} {:else if state === "reset_error"}
<div class="min-h-screen flex items-center justify-center bg-gray-100"> <div class="min-h-screen flex items-center justify-center bg-gray-100">
<div class="max-w-md w-full py-12 px-6"> <div class="max-w-md w-full py-12 px-6">
<img style="height:10rem;" class="mx-auto" src="/lfk-logo.png" alt="" /> <img style="height:10rem;" class="mx-auto" src="/lfk-logo.png" alt="" />
<p class="mt-6 text-lg text-center font-bold text-gray-900"> <p class="mt-6 text-lg text-center font-bold text-gray-900">
{$_('application_name')} {$_("application_name")}
</p> </p>
<p class="mt-2 mb-2 text-sm text-center text-gray-900 font-bold"> <p class="mt-2 mb-2 text-sm text-center text-gray-900 font-bold">
{$_('password-reset-failed')} {$_("password-reset-failed")}
</p> </p>
<p class="mt-2 mb-2 text-sm text-center text-gray-900"> <p class="mt-2 mb-2 text-sm text-center text-gray-900">
{$_('please-request-a-new-reset-mail')} {$_("please-request-a-new-reset-mail")}
</p> </p>
<div class="mt-6"> <div class="mt-6">
<div class="mt-6"> <div class="mt-6">
<a <a
href="/forgot_password/" href="/forgot_password/"
class="text-center relative block w-full py-2 px-3 border border-transparent rounded-md text-white font-semibold bg-gray-800 hover:bg-gray-700 focus:bg-gray-900 focus:outline-none focus:shadow-outline sm:text-sm"> class="text-center relative block w-full py-2 px-3 border border-transparent rounded-md text-white font-semibold bg-gray-800 hover:bg-gray-700 focus:bg-gray-900 focus:outline-none focus:shadow-outline sm:text-sm"
{$_('request-a-new-reset-mail')} >
{$_("request-a-new-reset-mail")}
</a> </a>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{:else if state === 'reset_in_progress'} {:else if state === "reset_in_progress"}
<div class="min-h-screen flex items-center justify-center bg-gray-100"> <div class="min-h-screen flex items-center justify-center bg-gray-100">
<div class="max-w-md w-full py-12 px-6"> <div class="max-w-md w-full py-12 px-6">
<img style="height:10rem;" class="mx-auto" src="/lfk-logo.png" alt="" /> <img style="height:10rem;" class="mx-auto" src="/lfk-logo.png" alt="" />
<p class="mt-6 text-lg text-center font-bold text-gray-900"> <p class="mt-6 text-lg text-center font-bold text-gray-900">
{$_('application_name')} {$_("application_name")}
</p> </p>
<p class="mt-2 mb-4 text-md text-center text-gray-900"> <p class="mt-2 mb-4 text-md text-center text-gray-900">
{$_('reset-password')} {$_("reset-password")}
</p> </p>
<div> <div>
<div class="rounded-md shadow-sm"> <div class="rounded-md shadow-sm">
<div> <div>
<input <input
aria-label={$_('new-password')} aria-label={$_("new-password")}
name="password" name="password"
type="password" type="password"
required="" required=""
class="border-gray-300 placeholder-gray-500 appearance-none rounded-md relative block w-full px-3 py-2 border text-gray-900 focus:outline-none focus:shadow-outline-blue focus:border-blue-300 focus:z-10 sm:text-sm" class="border-gray-300 placeholder-gray-500 appearance-none rounded-md relative block w-full px-3 py-2 border text-gray-900 focus:outline-none focus:shadow-outline-blue focus:border-blue-300 focus:z-10 sm:text-sm"
placeholder={$_('new-password')} placeholder={$_("new-password")}
bind:value={password} /> bind:value={password}
/>
</div> </div>
<PasswordStrength bind:password_change={password} /> <PasswordStrength bind:password_change={password} />
</div> </div>
@ -116,19 +111,22 @@
disabled={!password_strong_enough(password)} disabled={!password_strong_enough(password)}
class:opacity-50={!password_strong_enough(password)} class:opacity-50={!password_strong_enough(password)}
type="submit" type="submit"
class="relative block w-full py-2 px-3 border border-transparent rounded-md text-white font-semibold bg-gray-800 hover:bg-gray-700 focus:bg-gray-900 focus:outline-none focus:shadow-outline sm:text-sm"> class="relative block w-full py-2 px-3 border border-transparent rounded-md text-white font-semibold bg-gray-800 hover:bg-gray-700 focus:bg-gray-900 focus:outline-none focus:shadow-outline sm:text-sm"
>
<span class="absolute left-0 inset-y pl-3"> <span class="absolute left-0 inset-y pl-3">
<svg <svg
class="h-5 w-5 text-gray-500" class="h-5 w-5 text-gray-500"
fill="currentColor" fill="currentColor"
viewBox="0 0 20 20"> viewBox="0 0 20 20"
>
<path <path
fill-rule="evenodd" fill-rule="evenodd"
d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z" d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z"
clip-rule="evenodd" /> clip-rule="evenodd"
/>
</svg> </svg>
</span> </span>
{$_('reset-my-password')} {$_("reset-my-password")}
</button> </button>
</div> </div>
</div> </div>

View File

@ -1,274 +0,0 @@
<!--
This example requires Tailwind CSS v2.0+
This example requires some changes to your config:
```
// tailwind.config.js
module.exports = {
// ...
plugins: [
// ...
require('@tailwindcss/forms'),
]
}
```
-->
<div>
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
<div class="px-4 sm:px-0">
<h3 class="text-lg font-medium leading-6 text-gray-900">Profile</h3>
<p class="mt-1 text-sm text-gray-600">
This information will be displayed publicly so be careful what you share.
</p>
</div>
</div>
<div class="mt-5 md:mt-0 md:col-span-2">
<form action="#" method="POST">
<div class="shadow sm:rounded-md sm:overflow-hidden">
<div class="px-4 py-5 bg-white space-y-6 sm:p-6">
<div class="grid grid-cols-3 gap-6">
<div class="col-span-3 sm:col-span-2">
<label for="company_website" class="block text-sm font-medium text-gray-700">
Website
</label>
<div class="mt-1 flex rounded-md shadow-sm">
<span class="inline-flex items-center px-3 rounded-l-md border border-r-0 border-gray-300 bg-gray-50 text-gray-500 text-sm">
http://
</span>
<input type="text" name="company_website" id="company_website" class="focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-r-md sm:text-sm border-gray-300" placeholder="www.example.com">
</div>
</div>
</div>
<div>
<label for="about" class="block text-sm font-medium text-gray-700">
About
</label>
<div class="mt-1">
<textarea id="about" name="about" rows="3" class="shadow-sm focus:ring-indigo-500 focus:border-indigo-500 mt-1 block w-full sm:text-sm border-gray-300 rounded-md" placeholder="you@example.com"></textarea>
</div>
<p class="mt-2 text-sm text-gray-500">
Brief description for your profile. URLs are hyperlinked.
</p>
</div>
<div>
<!-- svelte-ignore a11y-label-has-associated-control -->
<label class="block text-sm font-medium text-gray-700">
Photo
</label>
<div class="mt-2 flex items-center">
<span class="inline-block h-12 w-12 rounded-full overflow-hidden bg-gray-100">
<svg class="h-full w-full text-gray-300" fill="currentColor" viewBox="0 0 24 24">
<path d="M24 20.993V24H0v-2.996A14.977 14.977 0 0112.004 15c4.904 0 9.26 2.354 11.996 5.993zM16.002 8.999a4 4 0 11-8 0 4 4 0 018 0z" />
</svg>
</span>
<button type="button" class="ml-5 bg-white py-2 px-3 border border-gray-300 rounded-md shadow-sm text-sm leading-4 font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
Change
</button>
</div>
</div>
<div>
<!-- svelte-ignore a11y-label-has-associated-control -->
<label class="block text-sm font-medium text-gray-700">
Cover photo
</label>
<div class="mt-2 flex justify-center px-6 pt-5 pb-6 border-2 border-gray-300 border-dashed rounded-md">
<div class="space-y-1 text-center">
<svg class="mx-auto h-12 w-12 text-gray-400" stroke="currentColor" fill="none" viewBox="0 0 48 48" aria-hidden="true">
<path d="M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
</svg>
<div class="flex text-sm text-gray-600">
<label for="file-upload" class="relative cursor-pointer bg-white rounded-md font-medium text-indigo-600 hover:text-indigo-500 focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-indigo-500">
<span>Upload a file</span>
<input id="file-upload" name="file-upload" type="file" class="sr-only">
</label>
<p class="pl-1">or drag and drop</p>
</div>
<p class="text-xs text-gray-500">
PNG, JPG, GIF up to 10MB
</p>
</div>
</div>
</div>
</div>
<div class="px-4 py-3 bg-gray-50 text-right sm:px-6">
<button type="submit" class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
Save
</button>
</div>
</div>
</form>
</div>
</div>
</div>
<div class="hidden sm:block" aria-hidden="true">
<div class="py-5">
<div class="border-t border-gray-200"></div>
</div>
</div>
<div class="mt-10 sm:mt-0">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
<div class="px-4 sm:px-0">
<h3 class="text-lg font-medium leading-6 text-gray-900">Personal Information</h3>
<p class="mt-1 text-sm text-gray-600">
Use a permanent address where you can receive mail.
</p>
</div>
</div>
<div class="mt-5 md:mt-0 md:col-span-2">
<form action="#" method="POST">
<div class="shadow overflow-hidden sm:rounded-md">
<div class="px-4 py-5 bg-white sm:p-6">
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-3">
<label for="first_name" class="block text-sm font-medium text-gray-700">First name</label>
<input type="text" name="first_name" id="first_name" autocomplete="given-name" class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md">
</div>
<div class="col-span-6 sm:col-span-3">
<label for="last_name" class="block text-sm font-medium text-gray-700">Last name</label>
<input type="text" name="last_name" id="last_name" autocomplete="family-name" class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md">
</div>
<div class="col-span-6 sm:col-span-4">
<label for="email_address" class="block text-sm font-medium text-gray-700">Email address</label>
<input type="text" name="email_address" id="email_address" autocomplete="email" class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md">
</div>
<div class="col-span-6 sm:col-span-3">
<label for="country" class="block text-sm font-medium text-gray-700">Country / Region</label>
<select id="country" name="country" autocomplete="country" class="mt-1 block w-full 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">
<option>United States</option>
<option>Canada</option>
<option>Mexico</option>
</select>
</div>
<div class="col-span-6">
<label for="street_address" class="block text-sm font-medium text-gray-700">Street address</label>
<input type="text" name="street_address" id="street_address" autocomplete="street-address" class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md">
</div>
<div class="col-span-6 sm:col-span-6 lg:col-span-2">
<label for="city" class="block text-sm font-medium text-gray-700">City</label>
<input type="text" name="city" id="city" class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md">
</div>
<div class="col-span-6 sm:col-span-3 lg:col-span-2">
<label for="state" class="block text-sm font-medium text-gray-700">State / Province</label>
<input type="text" name="state" id="state" class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md">
</div>
<div class="col-span-6 sm:col-span-3 lg:col-span-2">
<label for="postal_code" class="block text-sm font-medium text-gray-700">ZIP / Postal</label>
<input type="text" name="postal_code" id="postal_code" autocomplete="postal-code" class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md">
</div>
</div>
</div>
<div class="px-4 py-3 bg-gray-50 text-right sm:px-6">
<button type="submit" class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
Save
</button>
</div>
</div>
</form>
</div>
</div>
</div>
<div class="hidden sm:block" aria-hidden="true">
<div class="py-5">
<div class="border-t border-gray-200"></div>
</div>
</div>
<div class="mt-10 sm:mt-0">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
<div class="px-4 sm:px-0">
<h3 class="text-lg font-medium leading-6 text-gray-900">Notifications</h3>
<p class="mt-1 text-sm text-gray-600">
Decide which communications you'd like to receive and how.
</p>
</div>
</div>
<div class="mt-5 md:mt-0 md:col-span-2">
<form action="#" method="POST">
<div class="shadow overflow-hidden sm:rounded-md">
<div class="px-4 py-5 bg-white space-y-6 sm:p-6">
<fieldset>
<legend class="text-base font-medium text-gray-900">By Email</legend>
<div class="mt-4 space-y-4">
<div class="flex items-start">
<div class="flex items-center h-5">
<input id="comments" name="comments" type="checkbox" class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded">
</div>
<div class="ml-3 text-sm">
<label for="comments" class="font-medium text-gray-700">Comments</label>
<p class="text-gray-500">Get notified when someones posts a comment on a posting.</p>
</div>
</div>
<div class="flex items-start">
<div class="flex items-center h-5">
<input id="candidates" name="candidates" type="checkbox" class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded">
</div>
<div class="ml-3 text-sm">
<label for="candidates" class="font-medium text-gray-700">Candidates</label>
<p class="text-gray-500">Get notified when a candidate applies for a job.</p>
</div>
</div>
<div class="flex items-start">
<div class="flex items-center h-5">
<input id="offers" name="offers" type="checkbox" class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded">
</div>
<div class="ml-3 text-sm">
<label for="offers" class="font-medium text-gray-700">Offers</label>
<p class="text-gray-500">Get notified when a candidate accepts or rejects an offer.</p>
</div>
</div>
</div>
</fieldset>
<fieldset>
<div>
<legend class="text-base font-medium text-gray-900">Push Notifications</legend>
<p class="text-sm text-gray-500">These are delivered via SMS to your mobile phone.</p>
</div>
<div class="mt-4 space-y-4">
<div class="flex items-center">
<input id="push_everything" name="push_notifications" type="radio" class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300">
<label for="push_everything" class="ml-3 block text-sm font-medium text-gray-700">
Everything
</label>
</div>
<div class="flex items-center">
<input id="push_email" name="push_notifications" type="radio" class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300">
<label for="push_email" class="ml-3 block text-sm font-medium text-gray-700">
Same as email
</label>
</div>
<div class="flex items-center">
<input id="push_nothing" name="push_notifications" type="radio" class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300">
<label for="push_nothing" class="ml-3 block text-sm font-medium text-gray-700">
No push notifications
</label>
</div>
</div>
</fieldset>
</div>
<div class="px-4 py-3 bg-gray-50 text-right sm:px-6">
<button type="submit" class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
Save
</button>
</div>
</div>
</form>
</div>
</div>
</div>

View File

@ -4,19 +4,22 @@
<body class="antialiased font-sans"> <body class="antialiased font-sans">
<div class="flex min-h-screen"> <div class="flex min-h-screen">
<div class="w-full bg-white flex items-center justify-center "> <div class="w-full bg-white flex items-center justify-center">
<div class="max-w-sm m-8"> <div class="max-w-sm m-8">
<div class="text-black text-5xl md:text-15xl font-black"> <div class="text-black text-5xl md:text-15xl font-black">
{$_('internal-error')} {$_("internal-error")}
</div> </div>
<div class="w-16 h-1 bg-purple-light my-3 md:my-6" /> <div class="w-16 h-1 bg-purple-light my-3 md:my-6" />
<p <p
class="text-grey-darker text-2xl md:text-3xl font-light mb-8 leading-normal"> class="text-grey-darker text-2xl md:text-3xl font-light mb-8 leading-normal"
{$_('generic-ui-logic-error')} >
{$_("generic-ui-logic-error")}
</p> </p>
<a <a
href="/" href="/"
class="bg-transparent text-grey-darkest font-bold uppercase tracking-wide py-3 px-6 border-2 border-grey-light hover:border-grey rounded-lg">{$_('goback')}</a> class="bg-transparent text-grey-darkest font-bold uppercase tracking-wide py-3 px-6 border-2 border-grey-light hover:border-grey rounded-lg"
>{$_("goback")}</a
>
</div> </div>
</div> </div>
</div> </div>

View File

@ -5,7 +5,7 @@
<div class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500"> <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"> <span class="inline-block align-middle mr-8">
<b class="capitalize">{$_('general_promise_error')}</b> <b class="capitalize">{$_("general_promise_error")}</b>
{error} {error}
</span> </span>
</div> </div>

View File

@ -1,24 +1,25 @@
export function getlang(langkeys) { export function getlang(langkeys) {
return { return {
search: { search: {
placeholder: langkeys.search placeholder: langkeys.search,
}, },
sort: { sort: {
sortAsc: langkeys.sort_column_ascending, sortAsc: langkeys.sort_column_ascending,
sortDesc: langkeys.sort_column_descending sortDesc: langkeys.sort_column_descending,
}, },
pagination: { pagination: {
previous: langkeys.previous, previous: langkeys.previous,
next: langkeys.next, next: langkeys.next,
navigate: (page, pages) => `${langkeys.page} ${page} ${langkeys.of} ${pages}`, navigate: (page, pages) =>
page: (page) => `${langkeys.page} ${page}`, `${langkeys.page} ${page} ${langkeys.of} ${pages}`,
showing: langkeys.showing, page: (page) => `${langkeys.page} ${page}`,
of: langkeys.of, showing: langkeys.showing,
to: langkeys.to, of: langkeys.of,
results: langkeys.records to: langkeys.to,
}, results: langkeys.records,
loading: langkeys.loading, },
noRecordsFound: langkeys.no_matching_records_found, loading: langkeys.loading,
error: langkeys.an_error_happened_while_fetching_the_data noRecordsFound: langkeys.no_matching_records_found,
}; error: langkeys.an_error_happened_while_fetching_the_data,
};
} }

View File

@ -3,4 +3,4 @@
Or as others may call it: Real big bullshit time. Or as others may call it: Real big bullshit time.
Issue: https://git.odit.services/lfk/frontend/issues/136 Issue: https://git.odit.services/lfk/frontend/issues/136
--> -->
<div class="opacity-50"></div> <div class="opacity-50" />

View File

@ -1,10 +1,10 @@
/** Dispatch event on click outside of node */ /** Dispatch event on click outside of node */
export function clickOutside(node) { export function clickOutside(node) {
const handleClick = (event) => { const handleClick = (event) => {
if (event.target.getAttribute('data-id') === 'modal_backdrop') { if (event.target.getAttribute("data-id") === "modal_backdrop") {
node.dispatchEvent(new CustomEvent('click_outside', node)); node.dispatchEvent(new CustomEvent("click_outside", node));
} }
}; };
document.removeEventListener('click', handleClick, true); document.removeEventListener("click", handleClick, true);
document.addEventListener('click', handleClick, true); document.addEventListener("click", handleClick, true);
} }

File diff suppressed because one or more lines are too long

View File

@ -1,243 +1,232 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import { clickOutside } from "../base/outsideclick"; import { clickOutside } from "../base/outsideclick";
import { RunnerCardService } from "@odit/lfk-client-js"; import { RunnerCardService } from "@odit/lfk-client-js";
import Toastify from "toastify-js"; import { createEventDispatcher } from "svelte";
import { createEventDispatcher } from "svelte"; import toast from "svelte-french-toast";
export let bulk_modal_open; export let bulk_modal_open;
function focus(el) { const dispatch = createEventDispatcher();
el.focus();
} $: card_count = 0;
const dispatch = createEventDispatcher(); $: is_card_count_valid = card_count > 0;
$: processed_last_submit = true;
$: card_count = 0; $: createbtnenabled = is_card_count_valid;
$: is_card_count_valid = card_count > 0; (() => {
$: processed_last_submit = true; document.onkeydown = (e) => {
$: createbtnenabled = is_card_count_valid; e = e || window.event;
(() => { if (e.key === "Escape") {
document.onkeydown = (e) => { bulk_modal_open = false;
e = e || window.event; }
if (e.key === "Escape") { if (e.keyCode === 13) {
bulk_modal_open = false; if (createbtnenabled === true) {
} createbtnenabled = false;
if (e.keyCode === 13) { submit_with_print();
if (createbtnenabled === true) { }
createbtnenabled = false; }
submit_with_print(); };
} })();
} function submit_without_print() {
}; if (processed_last_submit === true) {
})(); processed_last_submit = false;
function submit_without_print() { toast.loading($_("creating-blanco-cards"));
if (processed_last_submit === true) { RunnerCardService.runnerCardControllerPostBlancoBulk(card_count, true)
processed_last_submit = false; .then((result) => {
const toast = Toastify({ bulk_modal_open = false;
text: $_("creating-blanco-cards"), //
duration: -1, toast.dismiss();
}).showToast(); toast.success($_("created-blanco-cards"));
RunnerCardService.runnerCardControllerPostBlancoBulk(card_count, true) dispatch("created", { cards: result });
.then((result) => { })
bulk_modal_open = false; .catch((err) => {
// //
Toastify({ })
text: $_("created-blanco-cards"), .finally(() => {
duration: 500, processed_last_submit = true;
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", });
}).showToast(); }
dispatch("created", {cards: result}) }
})
.catch((err) => { function submit_with_print() {
// if (processed_last_submit === true) {
}) processed_last_submit = false;
.finally(() => { toast.dismiss();
processed_last_submit = true; toast.loading($_("creating-blanco-cards"));
// RunnerCardService.runnerCardControllerPostBlancoBulk(card_count, true)
toast.hideToast(); .then((result) => {
}); bulk_modal_open = false;
} //
} toast.dismiss();
toast.success($_("created-blanco-cards"));
function submit_with_print() { toast.loading($_("generating-pdf"));
if (processed_last_submit === true) { dispatch("created", { cards: result });
processed_last_submit = false; fetch(
const toast = Toastify({ `${config.baseurl_documentserver}/cards?&download=true&key=${config.documentserver_key}`,
text: $_("creating-blanco-cards"), {
duration: -1, method: "POST",
}).showToast(); headers: {
RunnerCardService.runnerCardControllerPostBlancoBulk(card_count, true) "Content-Type": "application/json",
.then((result) => { },
bulk_modal_open = false; body: JSON.stringify(result),
// }
Toastify({ )
text: $_("created-blanco-cards"), .then((response) => {
duration: 500, if (response.status != "200") {
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", toast.dismiss();
}).showToast(); toast.error($_("pdf-generation-failed"));
const toast = Toastify({ } else {
text: $_("generating-pdf"), return response.blob();
duration: -1, }
}).showToast(); })
dispatch("created", {cards: result}) .then((blob) => {
fetch( const url = window.URL.createObjectURL(blob);
`${config.baseurl_documentserver}/cards?&download=true&key=${config.documentserver_key}`, let a = document.createElement("a");
{ a.href = url;
method: "POST", a.download = "Bulkcards.pdf";
headers: { document.body.appendChild(a);
"Content-Type": "application/json", a.click();
}, a.remove();
body: JSON.stringify(result), toast.dismiss();
} toast.success($_("pdf-successfully-generated"));
) })
.then((response) => { .catch((err) => {
if (response.status != "200") { console.error(err);
toast.hideToast(); });
Toastify({ })
text: $_("pdf-generation-failed"), .catch((err) => {
duration: 3500, //
backgroundColor: })
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)", .finally(() => {
}).showToast(); processed_last_submit = true;
} else { });
return response.blob(); }
} }
}) </script>
.then((blob) => {
const url = window.URL.createObjectURL(blob); {#if bulk_modal_open}
let a = document.createElement("a"); <div
a.href = url; class="fixed z-10 inset-0 overflow-y-auto"
a.download = "Bulkcards.pdf"; use:clickOutside
document.body.appendChild(a); on:click_outside={() => {
a.click(); bulk_modal_open = false;
a.remove(); }}
toast.hideToast(); >
Toastify({ <div
text: $_("pdf-successfully-generated"), class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"
duration: 3500, >
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", <div class="fixed inset-0 transition-opacity" aria-hidden="true">
}).showToast(); <div
}) class="absolute inset-0 bg-gray-500 opacity-75"
.catch((err) => { data-id="modal_backdrop"
console.error(err); />
}); </div>
}) <span
.catch((err) => { class="hidden sm:inline-block sm:align-middle sm:h-screen"
// aria-hidden="true">&#8203;</span
}) >
.finally(() => { <div
processed_last_submit = true; 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-2xl sm:w-full"
// role="dialog"
toast.hideToast(); aria-modal="true"
}); aria-labelledby="modal-headline"
} >
} <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
</script> <div class="sm:flex sm:items-start">
<div
{#if bulk_modal_open} class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w- rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10"
<div >
class="fixed z-10 inset-0 overflow-y-auto" <svg
class="h-6 w-6 text-blue-600"
use:clickOutside fill="currentColor"
on:click_outside={() => { xmlns="http://www.w3.org/2000/svg"
bulk_modal_open = false; viewBox="0 0 24 24"
}}> width="24"
<div height="24"
class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> ><path fill="none" d="M0 0h24v24H0z" />
<div class="fixed inset-0 transition-opacity" aria-hidden="true"> <path
<div fill="currentColor"
class="absolute inset-0 bg-gray-500 opacity-75" d="M22 10v10a1 1 0 01-1 1H3a1 1 0 01-1-1V10h20zm0-2H2V4a1 1 0 011-1h18a1 1 0 011 1v4zm-7 8v2h4v-2h-4z"
data-id="modal_backdrop" /> /></svg
</div> >
<span </div>
class="hidden sm:inline-block sm:align-middle sm:h-screen" <div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
aria-hidden="true">&#8203;</span> <h3 class="text-lg leading-6 font-medium text-gray-900">
<div {$_("create-bulk-blanco-cards")}
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-2xl sm:w-full" </h3>
role="dialog" <div class="mt-2 mb-6">
aria-modal="true" <p class="text-sm text-gray-500">
aria-labelledby="modal-headline"> {$_(
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> "just-enter-how-many-you-want-and-the-system-will-create-them"
<div class="sm:flex sm:items-start"> )}
<div </p>
class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w- rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10"> </div>
<svg <div class="grid grid-cols-6 gap-6">
class="h-6 w-6 text-blue-600" <div class="col-span-6">
fill="currentColor" <label
xmlns="http://www.w3.org/2000/svg" for="amount"
viewBox="0 0 24 24" class="block text-sm font-medium text-gray-700"
width="24" >{$_("amount")}</label
height="24"><path fill="none" d="M0 0h24v24H0z" /> >
<path <div class="mt-1 flex rounded-md shadow-sm">
fill="currentColor" <input
d="M22 10v10a1 1 0 01-1 1H3a1 1 0 01-1-1V10h20zm0-2H2V4a1 1 0 011-1h18a1 1 0 011 1v4zm-7 8v2h4v-2h-4z" /></svg> autocomplete="off"
</div> class:border-red-500={!is_card_count_valid}
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"> class:focus:border-red-500={!is_card_count_valid}
<h3 class="text-lg leading-6 font-medium text-gray-900"> class:focus:ring-red-500={!is_card_count_valid}
{$_('create-bulk-blanco-cards')} bind:value={card_count}
</h3> type="number"
<div class="mt-2 mb-6"> step="1"
<p class="text-sm text-gray-500"> name="amount"
{$_('just-enter-how-many-you-want-and-the-system-will-create-them')} 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-gray-500 p-2"
</p> placeholder="400"
</div> />
<div class="grid grid-cols-6 gap-6"> <span
<div class="col-span-6"> class="inline-flex items-center px-3 rounded-r-md border border-gray-300 bg-gray-50 text-gray-500 text-sm"
<label >{$_("cards")}</span
for="amount" >
class="block text-sm font-medium text-gray-700">{$_('amount')}</label> </div>
<div class="mt-1 flex rounded-md shadow-sm"> {#if !is_card_count_valid}
<input <span
autocomplete="off" class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
class:border-red-500={!is_card_count_valid} >
class:focus:border-red-500={!is_card_count_valid} {$_("you-must-create-at-least-one-card-or-cancel")}
class:focus:ring-red-500={!is_card_count_valid} </span>
bind:value={card_count} {/if}
type="number" </div>
step="1" </div>
name="amount" </div>
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-gray-500 p-2" </div>
placeholder="400" /> </div>
<span <div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
class="inline-flex items-center px-3 rounded-r-md border border-gray-300 bg-gray-50 text-gray-500 text-sm">{$_('cards')}</span> <button
</div> disabled={!createbtnenabled}
{#if !is_card_count_valid} class:opacity-50={!createbtnenabled}
<span on:click={submit_with_print}
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> type="button"
{$_('you-must-create-at-least-one-card-or-cancel')} 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"
</span> >
{/if} {$_("create-and-generate-pdf")}
</div> </button>
</div> <button
</div> disabled={!createbtnenabled}
</div> class:opacity-50={!createbtnenabled}
</div> on:click={submit_without_print}
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse"> type="button"
<button class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-gray-400 text-base font-medium text-white hover:bg-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm"
disabled={!createbtnenabled} >
class:opacity-50={!createbtnenabled} {$_("create-without-pdf")}
on:click={submit_with_print} </button>
type="button" <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"> on:click={() => {
{$_('create-and-generate-pdf')} bulk_modal_open = false;
</button> }}
<button type="button"
disabled={!createbtnenabled} class="mr-auto 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"
class:opacity-50={!createbtnenabled} >
on:click={submit_without_print} {$_("cancel")}
type="button" </button>
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-gray-400 text-base font-medium text-white hover:bg-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm"> </div>
{$_('create-without-pdf')} </div>
</button> </div>
<button </div>
on:click={() => { {/if}
bulk_modal_open = false;
}}
type="button"
class="mr-auto 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

@ -1,203 +1,191 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import { clickOutside } from "../base/outsideclick"; import { clickOutside } from "../base/outsideclick";
import { import { RunnerCardService, RunnerService } from "@odit/lfk-client-js";
RunnerCardService, import Select from "svelte-select";
RunnerService, import { createEventDispatcher } from "svelte";
ScanService, import toast from "svelte-french-toast";
} from "@odit/lfk-client-js"; export let modal_open;
import Select from "svelte-select";
import Toastify from "toastify-js"; const dispatch = createEventDispatcher();
import { createEventDispatcher } from "svelte"; const getRunnerLabel = (option) => {
export let modal_open; if (option.middlename) {
return option.firstname + " " + option.middlename + " " + option.lastname;
const dispatch = createEventDispatcher(); }
const getRunnerLabel = (option) => { return option.firstname + " " + option.lastname;
if (option.middlename) { };
return option.firstname + " " + option.middlename + " " + option.lastname;
} const filterRunners = (label, filterText, option) => {
return option.firstname + " " + option.lastname; if (filterText.startsWith("#")) {
}; return option.value.id == parseInt(filterText.replace("#", ""));
}
const filterRunners = (label, filterText, option) => { return (
if (filterText.startsWith("#")) { label.toLowerCase().includes(filterText.toLowerCase()) ||
return option.value.id == parseInt(filterText.replace("#", "")); option.value.toString().startsWith(filterText.toLowerCase())
} );
return ( };
label.toLowerCase().includes(filterText.toLowerCase()) || function focus(el) {
option.value.toString().startsWith(filterText.toLowerCase()) el.focus();
); }
}; $: runner = 0;
function focus(el) { $: enabled = true;
el.focus(); $: processed_last_submit = true;
}
$: runner = 0; let loading = true;
$: enabled = true; let runners = [];
$: processed_last_submit = true; RunnerService.runnerControllerGetAll().then((val) => {
runners = val.map((r) => {
let loading = true; return { label: getRunnerLabel(r), value: r };
let runners = []; });
RunnerService.runnerControllerGetAll().then((val) => { loading = false;
runners = val.map((r) => { });
return { label: getRunnerLabel(r), value: r }; $: createbtnenabled = true;
}); (() => {
loading = false; document.onkeydown = (e) => {
}); e = e || window.event;
$: createbtnenabled = true; if (e.key === "Escape") {
(() => { modal_open = false;
document.onkeydown = (e) => { }
e = e || window.event; if (e.keyCode === 13) {
if (e.key === "Escape") { if (createbtnenabled === true) {
modal_open = false; createbtnenabled = false;
} submit();
if (e.keyCode === 13) { }
if (createbtnenabled === true) { }
createbtnenabled = false; };
submit(); })();
} function submit() {
} if (processed_last_submit === true) {
}; processed_last_submit = false;
})(); toast.loading($_("adding-card"));
function submit() { let postdata = {
if (processed_last_submit === true) { runner,
processed_last_submit = false; enabled,
const toast = Toastify({ };
text: $_("adding-card"), RunnerCardService.runnerCardControllerPost(postdata)
duration: -1, .then((result) => {
}).showToast(); runner = 0;
let postdata = { modal_open = false;
runner, //
enabled, toast.dismiss();
}; toast.success($_("card-added"));
RunnerCardService.runnerCardControllerPost(postdata) dispatch("created", { cards: [result] });
.then((result) => { })
runner = 0; .catch((err) => {
modal_open = false; //
// })
Toastify({ .finally(() => {
text: $_("card-added"), processed_last_submit = true;
duration: 500, });
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", }
}).showToast(); }
dispatch("created", { cards: [result] }); </script>
})
.catch((err) => { {#if modal_open}
// <div
}) class="fixed z-10 inset-0 overflow-y-auto"
.finally(() => { use:clickOutside
processed_last_submit = true; on:click_outside={() => {
// modal_open = false;
toast.hideToast(); }}
}); >
} <div
} class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"
</script> >
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
{#if modal_open} <div
<div class="absolute inset-0 bg-gray-500 opacity-75"
class="fixed z-10 inset-0 overflow-y-auto" data-id="modal_backdrop"
use:clickOutside />
on:click_outside={() => { </div>
modal_open = false; <span
}} class="hidden sm:inline-block sm:align-middle sm:h-screen"
> aria-hidden="true">&#8203;</span
<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="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"
<div class="fixed inset-0 transition-opacity" aria-hidden="true"> role="dialog"
<div aria-modal="true"
class="absolute inset-0 bg-gray-500 opacity-75" aria-labelledby="modal-headline"
data-id="modal_backdrop" >
/> <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
</div> <div class="sm:flex sm:items-start">
<span <div
class="hidden sm:inline-block sm:align-middle sm:h-screen" 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"
aria-hidden="true">&#8203;</span >
> <svg
<div class="h-6 w-6 text-blue-600"
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" fill="currentColor"
role="dialog" xmlns="http://www.w3.org/2000/svg"
aria-modal="true" viewBox="0 0 24 24"
aria-labelledby="modal-headline" width="24"
> height="24"
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> ><path fill="none" d="M0 0h24v24H0z" />
<div class="sm:flex sm:items-start"> <path
<div fill="currentColor"
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" d="M22 10v10a1 1 0 01-1 1H3a1 1 0 01-1-1V10h20zm0-2H2V4a1 1 0 011-1h18a1 1 0 011 1v4zm-7 8v2h4v-2h-4z"
> /></svg
<svg >
class="h-6 w-6 text-blue-600" </div>
fill="currentColor" <div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
xmlns="http://www.w3.org/2000/svg" <h3 class="text-lg leading-6 font-medium text-gray-900">
viewBox="0 0 24 24" {$_("create-a-new-card")}
width="24" </h3>
height="24" <div class="mt-2 mb-6">
><path fill="none" d="M0 0h24v24H0z" /> <p class="text-sm text-gray-500">
<path {$_("you-can-provide-a-runner-but-you-dont-have-to")}
fill="currentColor" {$_(
d="M22 10v10a1 1 0 01-1 1H3a1 1 0 01-1-1V10h20zm0-2H2V4a1 1 0 011-1h18a1 1 0 011 1v4zm-7 8v2h4v-2h-4z" "if-you-want-to-create-multiple-blanco-cards-try-the-add-bulk-button"
/></svg )}
> </p>
</div> </div>
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"> <div class="grid grid-cols-6 gap-6">
<h3 class="text-lg leading-6 font-medium text-gray-900"> <div class="col-span-6">
{$_("create-a-new-card")} <label
</h3> for="donor"
<div class="mt-2 mb-6"> class="block text-sm font-medium text-gray-700"
<p class="text-sm text-gray-500"> >{$_("runner")}</label
{$_("you-can-provide-a-runner-but-you-dont-have-to")} >
{$_( <Select
"if-you-want-to-create-multiple-blanco-cards-try-the-add-bulk-button" containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"
)} itemFilter={(label, filterText, option) =>
</p> filterRunners(label, filterText, option)}
</div> items={runners}
<div class="grid grid-cols-6 gap-6"> bind:loading
<div class="col-span-6"> showChevron={!loading}
<label placeholder={$_("search-for-runner-by-name-or-id")}
for="donor" noOptionsMessage={$_("no-runners-found")}
class="block text-sm font-medium text-gray-700" on:select={(selectedValue) =>
>{$_("runner")}</label (runner = selectedValue.detail.value.id)}
> on:clear={() => (runner = null)}
<Select />
containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" </div>
itemFilter={(label, filterText, option) => </div>
filterRunners(label, filterText, option)} </div>
items={runners} </div>
bind:loading </div>
showChevron={!loading} <div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
placeholder={$_("search-for-runner-by-name-or-id")} <button
noOptionsMessage={$_("no-runners-found")} disabled={!createbtnenabled}
on:select={(selectedValue) => class:opacity-50={!createbtnenabled}
(runner = selectedValue.detail.value.id)} on:click={submit}
on:clear={() => (runner = null)} 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"
</div> >
</div> {$_("create")}
</div> </button>
</div> <button
</div> on:click={() => {
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse"> modal_open = false;
<button }}
disabled={!createbtnenabled} type="button"
class:opacity-50={!createbtnenabled} 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"
on:click={submit} >
type="button" {$_("cancel")}
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" </button>
> </div>
{$_("create")} </div>
</button> </div>
<button </div>
on:click={() => { {/if}
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

@ -1,193 +1,200 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import { clickOutside } from "../base/outsideclick"; import { clickOutside } from "../base/outsideclick";
import { RunnerCardService, RunnerService } from "@odit/lfk-client-js"; import { RunnerCardService, RunnerService } from "@odit/lfk-client-js";
import Select from "svelte-select"; import Select from "svelte-select";
import Toastify from "toastify-js"; import { createEventDispatcher } from "svelte";
import { createEventDispatcher } from "svelte"; import toast from "svelte-french-toast";
export let edit_modal_open; export let edit_modal_open;
export let runner = {}; export let runner = {};
export let editable = {}; export let editable = {};
export let original_data = {}; export let original_data = {};
const getRunnerLabel = (option) => const getRunnerLabel = (option) =>
option.firstname + " " + (option.middlename || "") + " " + option.lastname; option.firstname + " " + (option.middlename || "") + " " + option.lastname;
const filterRunners = (label, filterText, option) => { const filterRunners = (label, filterText, option) => {
if (filterText.startsWith("#")) { if (filterText.startsWith("#")) {
return option.value.id == parseInt(filterText.replace("#","")) return option.value.id == parseInt(filterText.replace("#", ""));
} }
return ( return (
label.toLowerCase().includes(filterText.toLowerCase()) || label.toLowerCase().includes(filterText.toLowerCase()) ||
option.value.toString().startsWith(filterText.toLowerCase()) option.value.toString().startsWith(filterText.toLowerCase())
); );
}; };
function focus(el) { function focus(el) {
el.focus(); el.focus();
} }
$: runners = []; $: runners = [];
$: enabled = true; $: enabled = true;
$: processed_last_submit = true; $: processed_last_submit = true;
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
RunnerService.runnerControllerGetAll().then((val) => { RunnerService.runnerControllerGetAll().then((val) => {
runners = val.map((r) => { runners = val.map((r) => {
return { label: getRunnerLabel(r), value: r }; return { label: getRunnerLabel(r), value: r };
}); });
}); });
$: createbtnenabled = !( $: createbtnenabled = !(
JSON.stringify(editable) === JSON.stringify(original_data) JSON.stringify(editable) === JSON.stringify(original_data)
); );
(() => { (() => {
document.onkeydown = (e) => { document.onkeydown = (e) => {
e = e || window.event; e = e || window.event;
if (e.key === "Escape") { if (e.key === "Escape") {
edit_modal_open = false; edit_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) {
processed_last_submit = false; processed_last_submit = false;
const toast = Toastify({ toast.loading($_("updating-card"));
text: $_("updating-card"), RunnerCardService.runnerCardControllerPut(original_data.id, editable)
duration: -1, .then((result) => {
}).showToast(); runner = {};
RunnerCardService.runnerCardControllerPut(original_data.id, editable) editable = {};
.then((result) => { original_data = {};
let id = original_data.id; edit_modal_open = false;
runner = {}; //
editable = {}; toast.dismiss();
original_data = {}; toast.success($_("card-updated"));
edit_modal_open = false; dispatch("dataUpdated", { card: result });
// })
Toastify({ .catch((err) => {
text: $_("card-updated"), //
duration: 500, })
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", .finally(() => {
}).showToast(); processed_last_submit = true;
dispatch('dataUpdated', {card: result}); });
}) }
.catch((err) => { }
// </script>
})
.finally(() => { {#if edit_modal_open}
processed_last_submit = true; <div
// class="fixed z-10 inset-0 overflow-y-auto"
toast.hideToast(); use:clickOutside
}); on:click_outside={() => {
} edit_modal_open = false;
} }}
</script> >
<div
{#if edit_modal_open} 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 z-10 inset-0 overflow-y-auto" <div class="fixed inset-0 transition-opacity" aria-hidden="true">
<div
use:clickOutside class="absolute inset-0 bg-gray-500 opacity-75"
on:click_outside={() => { data-id="modal_backdrop"
edit_modal_open = false; />
}}> </div>
<div <span
class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> class="hidden sm:inline-block sm:align-middle sm:h-screen"
<div class="fixed inset-0 transition-opacity" aria-hidden="true"> aria-hidden="true">&#8203;</span
<div >
class="absolute inset-0 bg-gray-500 opacity-75" <div
data-id="modal_backdrop" /> 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"
</div> role="dialog"
<span aria-modal="true"
class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-labelledby="modal-headline"
aria-hidden="true">&#8203;</span> >
<div <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
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" <div class="sm:flex sm:items-start">
role="dialog" <div
aria-modal="true" 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"
aria-labelledby="modal-headline"> >
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> <svg
<div class="sm:flex sm:items-start"> class="h-6 w-6 text-blue-600"
<div fill="currentColor"
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"> xmlns="http://www.w3.org/2000/svg"
<svg viewBox="0 0 24 24"
class="h-6 w-6 text-blue-600" width="24"
fill="currentColor" height="24"
xmlns="http://www.w3.org/2000/svg" ><path fill="none" d="M0 0h24v24H0z" />
viewBox="0 0 24 24" <path
width="24" fill="currentColor"
height="24"><path fill="none" d="M0 0h24v24H0z" /> d="M22 10v10a1 1 0 01-1 1H3a1 1 0 01-1-1V10h20zm0-2H2V4a1 1 0 011-1h18a1 1 0 011 1v4zm-7 8v2h4v-2h-4z"
<path /></svg
fill="currentColor" >
d="M22 10v10a1 1 0 01-1 1H3a1 1 0 01-1-1V10h20zm0-2H2V4a1 1 0 011-1h18a1 1 0 011 1v4zm-7 8v2h4v-2h-4z" /></svg> </div>
</div> <div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<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">
<h3 class="text-lg leading-6 font-medium text-gray-900"> {$_("edit-a-card")}
{$_('edit-a-card')} </h3>
</h3> <div class="mt-2 mb-6">
<div class="mt-2 mb-6"> <p class="text-sm text-gray-500">
<p class="text-sm text-gray-500"> {$_("you-can-provide-a-runner-but-you-dont-have-to")}
{$_('you-can-provide-a-runner-but-you-dont-have-to')} </p>
</p> </div>
</div> <div class="grid grid-cols-6 gap-6">
<div class="grid grid-cols-6 gap-6"> <div class="col-span-6">
<div class="col-span-6"> <label
<label for="runner"
for="runner" class="block text-sm font-medium text-gray-700"
class="block text-sm font-medium text-gray-700">{$_('runner')}</label> >{$_("runner")}</label
<Select >
containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" <Select
itemFilter={(label, filterText, option) => filterRunners(label, filterText, option)} containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"
items={runners} itemFilter={(label, filterText, option) =>
showChevron={true} filterRunners(label, filterText, option)}
placeholder={$_('search-for-runner-by-name-or-id')} items={runners}
noOptionsMessage={$_('no-runners-found')} showChevron={true}
bind:selectedValue={runner} placeholder={$_("search-for-runner-by-name-or-id")}
on:select={(selectedValue) => (editable.runner = selectedValue.detail.value.id)} noOptionsMessage={$_("no-runners-found")}
on:clear={() => (editable.runner = null)} /> bind:selectedValue={runner}
</div> on:select={(selectedValue) =>
<div class="col-span-6"> (editable.runner = selectedValue.detail.value.id)}
<p class="text-gray-500"> on:clear={() => (editable.runner = null)}
<input />
id="enabled" </div>
on:change={() => { <div class="col-span-6">
editable.enabled = !editable.enabled; <p class="text-gray-500">
}} <input
name="enabled" id="enabled"
type="checkbox" on:change={() => {
checked={editable.enabled} editable.enabled = !editable.enabled;
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" /> }}
{$_('this-card-is')} name="enabled"
{#if editable.enabled} type="checkbox"
{$_('enabled')} checked={editable.enabled}
{:else}{$_('disabled')}{/if} class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
</p> />
</div> {$_("this-card-is")}
</div> {#if editable.enabled}
</div> {$_("enabled")}
</div> {:else}{$_("disabled")}{/if}
</div> </p>
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse"> </div>
<button </div>
disabled={!createbtnenabled} </div>
class:opacity-50={!createbtnenabled} </div>
on:click={submit} </div>
type="button" <div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
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"> <button
{$_('save-changes')} disabled={!createbtnenabled}
</button> class:opacity-50={!createbtnenabled}
<button on:click={submit}
on:click={() => { type="button"
edit_modal_open = false; 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"
}} >
type="button" {$_("save-changes")}
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"> </button>
{$_('cancel')} <button
</button> on:click={() => {
</div> edit_modal_open = false;
</div> }}
</div> type="button"
</div> 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"
{/if} >
{$_("cancel")}
</button>
</div>
</div>
</div>
</div>
{/if}

View File

@ -1,54 +1,53 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import store from "../../store"; import store from "../../store";
import AddCardBulkModal from "./AddCardBulkModal.svelte"; import AddCardBulkModal from "./AddCardBulkModal.svelte";
import AddCardModal from "./AddCardModal.svelte"; import AddCardModal from "./AddCardModal.svelte";
import CardsOverview from "./CardsOverview.svelte"; import CardsOverview from "./CardsOverview.svelte";
$: current_cards = []; $: current_cards = [];
export let modal_open = false; export let modal_open = false;
export let bulk_modal_open = false; export let bulk_modal_open = false;
let addCards; let addCards;
</script> </script>
<section class="container p-5"> <section class="container p-5">
<span class="mb-1 text-3xl font-extrabold leading-tight"> <span class="mb-1 text-3xl font-extrabold leading-tight">
{$_("cards")} {$_("cards")}
{#if store.state.jwtinfo.userdetails.permissions.includes("CARD:CREATE")} {#if store.state.jwtinfo.userdetails.permissions.includes("CARD:CREATE")}
<button <button
on:click={() => { on:click={() => {
modal_open = true; modal_open = true;
}} }}
type="button" 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" 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"
> >
{$_("add-card")} {$_("add-card")}
</button> </button>
<button <button
on:click={() => { on:click={() => {
bulk_modal_open = true; bulk_modal_open = true;
}} }}
type="button" 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" 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-bulk-cards")} {$_("create-bulk-cards")}
</button> </button>
{/if} {/if}
</span> </span>
<CardsOverview bind:current_cards bind:addCards /> <CardsOverview bind:current_cards bind:addCards />
</section> </section>
{#if store.state.jwtinfo.userdetails.permissions.includes("CARD:CREATE")} {#if store.state.jwtinfo.userdetails.permissions.includes("CARD:CREATE")}
<AddCardModal <AddCardModal
bind:modal_open bind:modal_open
on:created={(event) => { on:created={(event) => {
console.log(event) addCards(event.detail.cards);
addCards(event.detail.cards); }}
}} />
/> <AddCardBulkModal
<AddCardBulkModal bind:bulk_modal_open
bind:bulk_modal_open on:created={(event) => {
on:created={(event) => { addCards(event.detail.cards);
addCards(event.detail.cards); }}
}} />
/> {/if}
{/if}

View File

@ -1,12 +1,12 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import cards_empty from "./cards.svg"; import cards_empty from "./cards.svg";
</script> </script>
<div class="text-center items-center justify-center"> <div class="text-center items-center justify-center">
<p class="mb-16 text-lg text-gray-500"> <p class="mb-16 text-lg text-gray-500">
<img class="m-auto" style="height:15rem" src={cards_empty} alt="" /> <img class="m-auto" style="height:15rem" src={cards_empty} alt="" />
<span class="font-bold">{$_('there-are-no-cards-yet')}</span><br /> <span class="font-bold">{$_("there-are-no-cards-yet")}</span><br />
<span>{$_('add-your-first-card')}</span> <span>{$_("add-your-first-card")}</span>
</p> </p>
</div> </div>

View File

@ -1,313 +1,312 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import { RunnerCardService } from "@odit/lfk-client-js"; import { RunnerCardService } from "@odit/lfk-client-js";
import store from "../../store"; import store from "../../store";
import toast from "svelte-french-toast"; import toast from "svelte-french-toast";
import CardsEmptyState from "./CardsEmptyState.svelte"; import CardsEmptyState from "./CardsEmptyState.svelte";
import CardDetailModal from "./CardDetailModal.svelte"; import CardDetailModal from "./CardDetailModal.svelte";
import GenerateRunnerCards from "../pdf_generation/GenerateRunnerCards.svelte"; import GenerateRunnerCards from "../pdf_generation/GenerateRunnerCards.svelte";
import InputElement from "../shared/InputElement.svelte"; import InputElement from "../shared/InputElement.svelte";
import { import {
createSvelteTable, createSvelteTable,
flexRender, flexRender,
getCoreRowModel, getCoreRowModel,
getFilteredRowModel, getFilteredRowModel,
getPaginationRowModel, getPaginationRowModel,
getSortedRowModel, getSortedRowModel,
renderComponent, renderComponent,
} from "@tanstack/svelte-table"; } from "@tanstack/svelte-table";
import { writable } from "svelte/store"; import { writable } from "svelte/store";
import TableBottom from "../shared/TableBottom.svelte"; import TableBottom from "../shared/TableBottom.svelte";
import TableActions from "../shared/TableActions.svelte"; import TableActions from "../shared/TableActions.svelte";
import TableHeader from "../shared/TableHeader.svelte"; import TableHeader from "../shared/TableHeader.svelte";
import CardStatus from "./CardStatus.svelte"; import CardStatus from "./CardStatus.svelte";
import CardRunner from "./CardRunner.svelte"; import CardRunner from "./CardRunner.svelte";
import { onMount } from "svelte"; import { onMount } from "svelte";
import { runnerFilter, statusFilter } from "../shared/tablefilters"; import { runnerFilter, statusFilter } from "../shared/tablefilters";
import DeleteCardModal from "./DeleteCardModal.svelte"; import DeleteCardModal from "./DeleteCardModal.svelte";
export let edit_modal_open = false; export let edit_modal_open = false;
export let runner = {}; export let runner = {};
export let editable = {}; export let editable = {};
export let original_data = {}; export let original_data = {};
export let current_cards = []; export let current_cards = [];
export const addCards = (cards) => { export const addCards = (cards) => {
console.log(cards); current_cards = current_cards.concat(...cards);
current_cards = current_cards.concat(...cards); options.update((options) => ({
options.update((options) => ({ ...options,
...options, data: current_cards,
data: current_cards, }));
})); };
};
$: dataLoaded = false;
$: dataLoaded = false; $: selected =
$: selected = $table?.getSelectedRowModel().rows.map((row) => row.index) || [];
$table?.getSelectedRowModel().rows.map((row) => row.index) || []; $: selectedCards =
$: selectedCards = $table?.getSelectedRowModel().rows.map((row) => row.original) || [];
$table?.getSelectedRowModel().rows.map((row) => row.original) || []; $: active_delete = undefined;
$: active_delete = undefined; $: cards_show = generate_cards.length > 0;
$: cards_show = generate_cards.length > 0; $: generate_cards = [];
$: generate_cards = [];
const columns = [
const columns = [ {
{ accessorKey: "code",
accessorKey: "code", header: () => $_("code"),
header: () => $_("code"), filterFn: `includesString`,
filterFn: `includesString`, },
}, {
{ accessorKey: "runner",
accessorKey: "runner", header: () => $_("runner"),
header: () => $_("runner"), cell: (info) => {
cell: (info) => { return renderComponent(CardRunner, { runner: info.getValue() });
return renderComponent(CardRunner, { runner: info.getValue() }); },
}, filterFn: `runner`,
filterFn: `runner`, },
}, {
{ accessorKey: "enabled",
accessorKey: "enabled", cell: (info) => {
cell: (info) => { return renderComponent(CardStatus, { enabled: info.getValue() });
return renderComponent(CardStatus, { enabled: info.getValue() }); },
}, header: () => $_("status"),
header: () => $_("status"), filterFn: `status`,
filterFn: `status`, },
}, {
{ accessorKey: "actions",
accessorKey: "actions", header: () => $_("action"),
header: () => $_("action"), cell: (info) => {
cell: (info) => { return renderComponent(TableActions, {
return renderComponent(TableActions, { detailsAction: () => {
detailsAction: () => { open_edit_modal(
open_edit_modal( current_cards[
current_cards[ current_cards.findIndex((r) => r.id == info.row.original.id)
current_cards.findIndex((r) => r.id == info.row.original.id) ]
] );
); },
}, deleteAction: () => {
deleteAction: () => { active_delete =
active_delete = current_cards[
current_cards[ current_cards.findIndex((r) => r.id == info.row.original.id)
current_cards.findIndex((r) => r.id == info.row.original.id) ];
]; },
}, deleteEnabled:
deleteEnabled: store.state.jwtinfo.userdetails.permissions.includes("CARD:DELETE"),
store.state.jwtinfo.userdetails.permissions.includes("CARD:DELETE"), });
}); },
}, enableColumnFilter: false,
enableColumnFilter: false, enableSorting: false,
enableSorting: false, },
}, ];
];
const options = writable({
const options = writable({ data: [],
data: [], columns: columns,
columns: columns, initialState: {
initialState: { pagination: {
pagination: { pageSize: 50,
pageSize: 50, },
}, },
}, filterFns: {
filterFns: { runner: runnerFilter,
runner: runnerFilter, status: statusFilter,
status: statusFilter, },
}, enableRowSelection: true,
enableRowSelection: true, getCoreRowModel: getCoreRowModel(),
getCoreRowModel: getCoreRowModel(), getFilteredRowModel: getFilteredRowModel(),
getFilteredRowModel: getFilteredRowModel(), getPaginationRowModel: getPaginationRowModel(),
getPaginationRowModel: getPaginationRowModel(), getSortedRowModel: getSortedRowModel(),
getSortedRowModel: getSortedRowModel(), });
});
const table = createSvelteTable(options);
const table = createSvelteTable(options);
function open_edit_modal(card) {
function open_edit_modal(card) { const getRunnerLabel = (option) =>
const getRunnerLabel = (option) => option.firstname +
option.firstname + " " +
" " + (option.middlename || "") +
(option.middlename || "") + " " +
" " + option.lastname;
option.lastname; if (card.runner?.id) {
if (card.runner?.id) { runner = Object.assign(
runner = Object.assign( { runner },
{ runner }, { label: getRunnerLabel(card.runner), value: card.runner }
{ label: getRunnerLabel(card.runner), value: card.runner } );
); card.runner = card.runner.id;
card.runner = card.runner.id; } else {
} else { card.runner = null;
card.runner = null; runner = null;
runner = null; }
} editable = Object.assign(editable, card);
editable = Object.assign(editable, card); original_data = Object.assign(original_data, card);
original_data = Object.assign(original_data, card); edit_modal_open = true;
edit_modal_open = true; }
}
async function deleteCard(delete_card_id) {
async function deleteCard(delete_card_id) { await RunnerCardService.runnerCardControllerRemove(delete_card_id, true);
await RunnerCardService.runnerCardControllerRemove(delete_card_id, true); current_cards = current_cards.filter((r) => r.id !== delete_card_id);
current_cards = current_cards.filter((r) => r.id !== delete_card_id); options.update((options) => ({
options.update((options) => ({ ...options,
...options, data: current_cards,
data: current_cards, }));
})); toast.success($_("card-deleted"));
toast.success($_("card-deleted")); }
}
onMount(async () => {
onMount(async () => { toast.loading($_("loading-cards"));
toast.loading($_("loading-cards")); let page = 0;
let page = 0; while (page >= 0) {
while (page >= 0) { const cards = await RunnerCardService.runnerCardControllerGetAll(
const cards = await RunnerCardService.runnerCardControllerGetAll( page,
page, 150
500 );
); if (cards.length == 0) {
if (cards.length == 0) { page = -2;
page = -2; }
}
current_cards = current_cards.concat(...cards);
current_cards = current_cards.concat(...cards); options.update((options) => ({
options.update((options) => ({ ...options,
...options, data: current_cards,
data: current_cards, }));
}));
dataLoaded = true;
dataLoaded = true; page++;
page++; }
} toast.dismiss();
toast.dismiss(); toast.success($_("all-cards-loaded"));
toast.success($_('all-cards-loaded')); });
}); </script>
</script>
{#if store.state.jwtinfo.userdetails.permissions.includes("CARD:UPDATE")}
{#if store.state.jwtinfo.userdetails.permissions.includes("CARD:UPDATE")} <CardDetailModal
<CardDetailModal bind:edit_modal_open
bind:edit_modal_open bind:runner
bind:runner bind:editable
bind:editable bind:original_data
bind:original_data on:dataUpdated={(event) => {
on:dataUpdated={(event) => { current_cards[
current_cards[ current_cards.findIndex((c) => c.id === event.detail.card.id)
current_cards.findIndex((c) => c.id === event.detail.card.id) ] = event.detail.card;
] = event.detail.card; current_cards = current_cards;
current_cards = current_cards; options.update((options) => ({
options.update((options) => ({ ...options,
...options, data: current_cards,
data: current_cards, }));
})); }}
}} />
/> {/if}
{/if}
{#if store.state.jwtinfo.userdetails.permissions.includes("CARD:GET")}
{#if store.state.jwtinfo.userdetails.permissions.includes("CARD:GET")} <DeleteCardModal
<DeleteCardModal delete_card={active_delete}
delete_card={active_delete} modal_open={active_delete != undefined}
modal_open={active_delete != undefined} on:delete={(event) => {
on:delete={(event) => { deleteCard(event.detail.id);
deleteCard(event.detail.id); }}
}} />
/> {#if !dataLoaded}
{#if !dataLoaded} <div
<div class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2"
class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2" role="alert"
role="alert" >
> <p class="font-bold">{$_("loading-cards")}</p>
<p class="font-bold">{$_("loading-cards")}</p> <p class="text-sm">{$_("this-might-take-a-moment")}</p>
<p class="text-sm">{$_("this-might-take-a-moment")}</p> </div>
</div> {:else if current_cards.length === 0}
{:else if current_cards.length === 0} <CardsEmptyState />
<CardsEmptyState /> {:else}
{:else} <div class="h-12 mt-1">
<div class="h-12 mt-1"> {#if selected.length > 0}
{#if selected.length > 0} <button
<button type="button"
type="button" class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm inline-flex"
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 inline-flex" id="options-menu"
id="options-menu" on:click={async () => {
on:click={async () => { const prom = [];
const prom = []; for (const card of selectedCards) {
for (const card of selectedCards) { prom.push(
prom.push( await RunnerCardService.runnerCardControllerRemove(
await RunnerCardService.runnerCardControllerRemove( card.id,
card.id, true
true )
) );
); }
} await Promise.all(prom);
await Promise.all(prom); for (const card of selectedCards) {
for (const card of selectedCards) { current_cards = current_cards.filter((r) => r.id !== card.id);
current_cards = current_cards.filter((r) => r.id !== card.id); }
} options.update((options) => ({
options.update((options) => ({ ...options,
...options, data: current_cards,
data: current_cards, }));
})); $table.resetRowSelection();
$table.resetRowSelection(); }}
}} >
> {$_("delete-cards")}
{$_("delete-cards")} <svg
<svg xmlns="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg" fill="none"
fill="none" viewBox="0 0 24 24"
viewBox="0 0 24 24" stroke-width="1.5"
stroke-width="1.5" stroke="currentColor"
stroke="currentColor" class="w-5 h-5"
class="w-5 h-5" >
> <path
<path stroke-linecap="round"
stroke-linecap="round" stroke-linejoin="round"
stroke-linejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0"
d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" />
/> </svg>
</svg> </button>
</button> {/if}
{/if} <GenerateRunnerCards
<GenerateRunnerCards cards_show={selected.length > 0}
cards_show={selected.length > 0} bind:generate_cards={selectedCards}
bind:generate_cards={selectedCards} />
/> </div>
</div> <div class="overflow-x-auto">
<div class="overflow-x-auto"> <table class="w-full">
<table class="w-full"> <thead>
<thead> {#each $table.getHeaderGroups() as headerGroup}
{#each $table.getHeaderGroups() as headerGroup} <tr class="select-none">
<tr class="select-none"> <th class="inset-y-0 left-0 px-4 py-2 text-left w-px">
<th class="inset-y-0 left-0 px-4 py-2 text-left w-px"> <InputElement
<InputElement type="checkbox"
type="checkbox" checked={$table.getIsAllRowsSelected()}
checked={$table.getIsAllRowsSelected()} indeterminate={$table.getIsSomeRowsSelected()}
indeterminate={$table.getIsSomeRowsSelected()} on:change={() => $table.toggleAllRowsSelected()}
on:change={() => $table.toggleAllRowsSelected()} />
/> </th>
</th> {#each headerGroup.headers as header}
{#each headerGroup.headers as header} <TableHeader {header} />
<TableHeader {header} /> {/each}
{/each} </tr>
</tr> {/each}
{/each} </thead>
</thead> <tbody>
<tbody> {#each $table.getRowModel().rows as row}
{#each $table.getRowModel().rows as row} <tr>
<tr> <td class="inset-y-0 left-0 px-4 py-2 text-center w-px">
<td class="inset-y-0 left-0 px-4 py-2 text-center w-px"> <InputElement
<InputElement type="checkbox"
type="checkbox" checked={row.getIsSelected()}
checked={row.getIsSelected()} on:change={() => row.toggleSelected()}
on:change={() => row.toggleSelected()} />
/> </td>
</td> {#each row.getVisibleCells() as cell}
{#each row.getVisibleCells() as cell} <td>
<td> <svelte:component
<svelte:component this={flexRender(
this={flexRender( cell.column.columnDef.cell,
cell.column.columnDef.cell, cell.getContext()
cell.getContext() )}
)} />
/> </td>
</td> {/each}
{/each} </tr>
</tr> {/each}
{/each} </tbody>
</tbody> </table>
</table> </div>
</div> <TableBottom {table} {selected} />
<TableBottom {table} {selected} /> {/if}
{/if} {/if}
{/if}

View File

@ -1,7 +1,7 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import { clickOutside } from "../base/outsideclick"; import { clickOutside } from "../base/outsideclick";
import { import {
GroupContactService, GroupContactService,
RunnerTeamService, RunnerTeamService,
@ -9,7 +9,7 @@
} from "@odit/lfk-client-js"; } from "@odit/lfk-client-js";
import isEmail from "validator/es/lib/isEmail"; import isEmail from "validator/es/lib/isEmail";
import isMobilePhone from "validator/es/lib/isMobilePhone"; import isMobilePhone from "validator/es/lib/isMobilePhone";
import Toastify from "toastify-js"; import toast from "svelte-french-toast";
export let modal_open; export let modal_open;
export let current_contacts; export let current_contacts;
$: selected_team = []; $: selected_team = [];
@ -85,10 +85,7 @@
function submit() { function submit() {
if (processed_last_submit === true) { if (processed_last_submit === true) {
processed_last_submit = false; processed_last_submit = false;
const toast = Toastify({ toast.loading($_("contact-is-being-added"));
text: $_('contact-is-being-added'),
duration: -1,
}).showToast();
let address = {}; let address = {};
if (address_checked === true) { if (address_checked === true) {
address = { address = {
@ -122,11 +119,8 @@
email_input_value = ""; email_input_value = "";
modal_open = false; modal_open = false;
// //
Toastify({ toast.dismiss();
text: $_('contact-added'), toast.success($_("contact-added"));
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
current_contacts.push(result); current_contacts.push(result);
current_contacts = current_contacts; current_contacts = current_contacts;
}) })
@ -135,8 +129,6 @@
}) })
.finally(() => { .finally(() => {
processed_last_submit = true; processed_last_submit = true;
//
toast.hideToast();
}); });
} }
} }
@ -145,58 +137,70 @@
{#if modal_open} {#if modal_open}
<div <div
class="fixed z-10 inset-0 overflow-y-auto" class="fixed z-10 inset-0 overflow-y-auto"
use:clickOutside use:clickOutside
on:click_outside={() => { on:click_outside={() => {
modal_open = false; modal_open = false;
}}> }}
>
<div <div
class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> 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="fixed inset-0 transition-opacity" aria-hidden="true">
<div <div
class="absolute inset-0 bg-gray-500 opacity-75" class="absolute inset-0 bg-gray-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 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" 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" 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"> <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="sm:flex sm:items-start">
<div <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"> 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 <svg
class="h-6 w-6 text-blue-600" class="h-6 w-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"><path fill="none" d="M0 0h24v24H0z" /> height="24"
><path fill="none" d="M0 0h24v24H0z" />
<path <path
d="M2 22a8 8 0 1 1 16 0H2zm8-9c-3.315 0-6-2.685-6-6s2.685-6 6-6 6 2.685 6 6-2.685 6-6 6zm10 4h4v2h-4v-2zm-3-5h7v2h-7v-2zm2-5h5v2h-5V7z" /></svg> d="M2 22a8 8 0 1 1 16 0H2zm8-9c-3.315 0-6-2.685-6-6s2.685-6 6-6 6 2.685 6 6-2.685 6-6 6zm10 4h4v2h-4v-2zm-3-5h7v2h-7v-2zm2-5h5v2h-5V7z"
/></svg
>
</div> </div>
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"> <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"> <h3 class="text-lg leading-6 font-medium text-gray-900">
{$_('create-a-new-contact')} {$_("create-a-new-contact")}
</h3> </h3>
<div class="mt-2 mb-6"> <div class="mt-2 mb-6">
<p class="text-sm text-gray-500"> <p class="text-sm text-gray-500">
{$_('please-provide-the-required-information-to-add-a-new-contact')} {$_(
"please-provide-the-required-information-to-add-a-new-contact"
)}
</p> </p>
</div> </div>
<div class="grid grid-cols-6 gap-6"> <div class="grid grid-cols-6 gap-6">
<div class="col-span-6"> <div class="col-span-6">
<label <label
for="firstname" for="firstname"
class="block text-sm font-medium text-gray-700">{$_('first-name')}</label> class="block text-sm font-medium text-gray-700"
>{$_("first-name")}</label
>
<input <input
use:focus use:focus
autocomplete="off" autocomplete="off"
placeholder={$_('first-name')} placeholder={$_("first-name")}
class:border-red-500={!isFirstnameValid} class:border-red-500={!isFirstnameValid}
class:focus:border-red-500={!isFirstnameValid} class:focus:border-red-500={!isFirstnameValid}
class:focus:ring-red-500={!isFirstnameValid} class:focus:ring-red-500={!isFirstnameValid}
@ -204,34 +208,41 @@
bind:this={firstname_input} bind:this={firstname_input}
type="text" type="text"
name="firstname" name="firstname"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> class="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"
/>
{#if !isFirstnameValid} {#if !isFirstnameValid}
<span <span
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
{$_('first-name-is-required')} >
{$_("first-name-is-required")}
</span> </span>
{/if} {/if}
</div> </div>
<div class="col-span-6"> <div class="col-span-6">
<label <label
for="trackname" for="trackname"
class="block text-sm font-medium text-gray-700">{$_('middle-name')}</label> class="block text-sm font-medium text-gray-700"
>{$_("middle-name")}</label
>
<input <input
autocomplete="off" autocomplete="off"
placeholder={$_('middle-name')} placeholder={$_("middle-name")}
bind:value={middlename_input_value} bind:value={middlename_input_value}
bind:this={middlename_input} bind:this={middlename_input}
type="text" type="text"
name="trackname" name="trackname"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> class="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 class="col-span-6"> <div class="col-span-6">
<label <label
for="lastname" for="lastname"
class="block text-sm font-medium text-gray-700">{$_('last-name')}</label> class="block text-sm font-medium text-gray-700"
>{$_("last-name")}</label
>
<input <input
autocomplete="off" autocomplete="off"
placeholder="{$_('last-name')}" placeholder={$_("last-name")}
class:border-red-500={!isLastnameValid} class:border-red-500={!isLastnameValid}
class:focus:border-red-500={!isLastnameValid} class:focus:border-red-500={!isLastnameValid}
class:focus:ring-red-500={!isLastnameValid} class:focus:ring-red-500={!isLastnameValid}
@ -239,23 +250,28 @@
bind:this={lastname_input} bind:this={lastname_input}
type="text" type="text"
name="lastname" name="lastname"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> class="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"
/>
{#if !isLastnameValid} {#if !isLastnameValid}
<span <span
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
{$_('last-name-is-required')} >
{$_("last-name-is-required")}
</span> </span>
{/if} {/if}
</div> </div>
<div class="col-span-6"> <div class="col-span-6">
<label <label
for="team" for="team"
class="block text-sm font-medium text-gray-700">{$_('teams')}</label> class="block text-sm font-medium text-gray-700"
>{$_("teams")}</label
>
<select <select
name="team" name="team"
multiple multiple
bind:value={selected_team} bind:value={selected_team}
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"> class="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"
>
{#each teams as team} {#each teams as team}
<option value={team.id}> <option value={team.id}>
{team.parentGroup.name} {team.parentGroup.name}
@ -271,10 +287,12 @@
<div class="col-span-6"> <div class="col-span-6">
<label <label
for="phone" for="phone"
class="block text-sm font-medium text-gray-700">{$_('phone')}</label> class="block text-sm font-medium text-gray-700"
>{$_("phone")}</label
>
<input <input
autocomplete="off" autocomplete="off"
placeholder={$_('phone')} placeholder={$_("phone")}
class:border-red-500={!isPhoneValidOrEmpty} class:border-red-500={!isPhoneValidOrEmpty}
class:focus:border-red-500={!isPhoneValidOrEmpty} class:focus:border-red-500={!isPhoneValidOrEmpty}
class:focus:ring-red-500={!isPhoneValidOrEmpty} class:focus:ring-red-500={!isPhoneValidOrEmpty}
@ -282,21 +300,27 @@
bind:this={phone_input} bind:this={phone_input}
type="tel" type="tel"
name="phone" name="phone"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> 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"
/>
{#if !isPhoneValidOrEmpty} {#if !isPhoneValidOrEmpty}
<span <span
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
{@html $_('the-provided-phone-number-is-invalid-less-than-br-greater-than-please-enter-a-valid-international-number')} >
{@html $_(
"the-provided-phone-number-is-invalid-less-than-br-greater-than-please-enter-a-valid-international-number"
)}
</span> </span>
{/if} {/if}
</div> </div>
<div class="col-span-6"> <div class="col-span-6">
<label <label
for="email" for="email"
class="block text-sm font-medium text-gray-700">{$_('e-mail-adress')}</label> class="block text-sm font-medium text-gray-700"
>{$_("e-mail-adress")}</label
>
<input <input
autocomplete="off" autocomplete="off"
placeholder={$_('e-mail-adress')} placeholder={$_("e-mail-adress")}
class:border-red-500={!isEmailValidOrEmpty} class:border-red-500={!isEmailValidOrEmpty}
class:focus:border-red-500={!isEmailValidOrEmpty} class:focus:border-red-500={!isEmailValidOrEmpty}
class:focus:ring-red-500={!isEmailValidOrEmpty} class:focus:ring-red-500={!isEmailValidOrEmpty}
@ -304,11 +328,13 @@
bind:this={email_input} bind:this={email_input}
type="email" type="email"
name="email" name="email"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> class="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"
/>
{#if !isEmailValidOrEmpty} {#if !isEmailValidOrEmpty}
<span <span
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
{$_('valid-email-is-required')} >
{$_("valid-email-is-required")}
</span> </span>
{/if} {/if}
</div> </div>
@ -319,22 +345,25 @@
id="comments" id="comments"
name="comments" name="comments"
type="checkbox" type="checkbox"
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" /> class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
/>
</div> </div>
<div class="ml-3 text-sm"> <div class="ml-3 text-sm">
<label <label for="comments" class="font-medium text-gray-700"
for="comments" >{$_("address")}</label
class="font-medium text-gray-700">{$_('address')}</label> >
</div> </div>
</div> </div>
{#if address_checked === true} {#if address_checked === true}
<div class="col-span-6"> <div class="col-span-6">
<label <label
for="address1" for="address1"
class="block text-sm font-medium text-gray-700">{$_('address')}</label> class="block text-sm font-medium text-gray-700"
>{$_("address")}</label
>
<input <input
autocomplete="off" autocomplete="off"
placeholder="{$_('address')}" placeholder={$_("address")}
class:border-red-500={!isAddress1Valid} class:border-red-500={!isAddress1Valid}
class:focus:border-red-500={!isAddress1Valid} class:focus:border-red-500={!isAddress1Valid}
class:focus:ring-red-500={!isAddress1Valid} class:focus:ring-red-500={!isAddress1Valid}
@ -342,34 +371,41 @@
bind:this={address_input1} bind:this={address_input1}
type="text" type="text"
name="address1" name="address1"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> class="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"
/>
{#if !isAddress1Valid} {#if !isAddress1Valid}
<span <span
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
{$_('address-is-required')} >
{$_("address-is-required")}
</span> </span>
{/if} {/if}
</div> </div>
<div class="col-span-6"> <div class="col-span-6">
<label <label
for="address2" for="address2"
class="block text-sm font-medium text-gray-700">{$_('apartment-suite-etc')}</label> class="block text-sm font-medium text-gray-700"
>{$_("apartment-suite-etc")}</label
>
<input <input
autocomplete="off" autocomplete="off"
placeholder={$_('apartment-suite-etc')} placeholder={$_("apartment-suite-etc")}
bind:value={address_input2_value} bind:value={address_input2_value}
bind:this={address_input2} bind:this={address_input2}
type="text" type="text"
name="address2" name="address2"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> class="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 class="col-span-6"> <div class="col-span-6">
<label <label
for="zipcode" for="zipcode"
class="block text-sm font-medium text-gray-700">{$_('zip-postal-code')}</label> class="block text-sm font-medium text-gray-700"
>{$_("zip-postal-code")}</label
>
<input <input
autocomplete="off" autocomplete="off"
placeholder={$_('zip-postal-code')} placeholder={$_("zip-postal-code")}
class:border-red-500={!iszipcodevalid} class:border-red-500={!iszipcodevalid}
class:focus:border-red-500={!iszipcodevalid} class:focus:border-red-500={!iszipcodevalid}
class:focus:ring-red-500={!iszipcodevalid} class:focus:ring-red-500={!iszipcodevalid}
@ -377,21 +413,25 @@
bind:this={address_zipcode} bind:this={address_zipcode}
type="text" type="text"
name="zipcode" name="zipcode"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> class="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"
/>
{#if !iszipcodevalid} {#if !iszipcodevalid}
<span <span
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
{$_('valid-zipcode-postal-code-is-required')} >
{$_("valid-zipcode-postal-code-is-required")}
</span> </span>
{/if} {/if}
</div> </div>
<div class="col-span-6"> <div class="col-span-6">
<label <label
for="city" for="city"
class="block text-sm font-medium text-gray-700">{$_('city')}</label> class="block text-sm font-medium text-gray-700"
>{$_("city")}</label
>
<input <input
autocomplete="off" autocomplete="off"
placeholder="{$_('city')}" placeholder={$_("city")}
class:border-red-500={!iscityvalid} class:border-red-500={!iscityvalid}
class:focus:border-red-500={!iscityvalid} class:focus:border-red-500={!iscityvalid}
class:focus:ring-red-500={!iscityvalid} class:focus:ring-red-500={!iscityvalid}
@ -399,11 +439,13 @@
bind:this={address_city} bind:this={address_city}
type="text" type="text"
name="city" name="city"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> class="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"
/>
{#if !iscityvalid} {#if !iscityvalid}
<span <span
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
{$_('valid-city-is-required')} >
{$_("valid-city-is-required")}
</span> </span>
{/if} {/if}
</div> </div>
@ -418,16 +460,18 @@
class:opacity-50={!createbtnenabled} class:opacity-50={!createbtnenabled}
on:click={submit} on:click={submit}
type="button" 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"> 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')} >
{$_("create")}
</button> </button>
<button <button
on:click={() => { on:click={() => {
modal_open = false; modal_open = false;
}} }}
type="button" 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"> 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')} >
{$_("cancel")}
</button> </button>
</div> </div>
</div> </div>

View File

@ -6,9 +6,9 @@
RunnerTeamService, RunnerTeamService,
RunnerOrganizationService, RunnerOrganizationService,
} from "@odit/lfk-client-js"; } from "@odit/lfk-client-js";
import Toastify from "toastify-js";
import PromiseError from "../base/PromiseError.svelte"; import PromiseError from "../base/PromiseError.svelte";
import isEmail from "validator/es/lib/isEmail"; import isEmail from "validator/es/lib/isEmail";
import toast from "svelte-french-toast";
let data_loaded = false; let data_loaded = false;
let orgs = []; let orgs = [];
let teams = []; let teams = [];
@ -31,7 +31,7 @@
isEmailValid && isEmailValid &&
isPhoneValidOrEmpty && isPhoneValidOrEmpty &&
((isAddress1Valid && iszipcodevalid && iscityvalid) || ((isAddress1Valid && iszipcodevalid && iscityvalid) ||
editable.address_checked === false); editable.address_checked === false);
const promise = GroupContactService.groupContactControllerGetOne( const promise = GroupContactService.groupContactControllerGetOne(
params.contact params.contact
).then((data) => { ).then((data) => {
@ -42,14 +42,14 @@
original_data.groups = original_data.groups.map((g) => g.id); original_data.groups = original_data.groups.map((g) => g.id);
editable.address_checked = editable.address.address1 !== null; editable.address_checked = editable.address.address1 !== null;
original_data.address_checked = editable.address.address1 !== null; original_data.address_checked = editable.address.address1 !== null;
if(editable.address_checked===false){ if (editable.address_checked === false) {
editable.address = { editable.address = {
address1: "", address1: "",
address2: "", address2: "",
city: "", city: "",
postalcode: "", postalcode: "",
country: "" country: "",
} };
} }
}); });
RunnerOrganizationService.runnerOrganizationControllerGetAll().then((val) => { RunnerOrganizationService.runnerOrganizationControllerGetAll().then((val) => {
@ -67,10 +67,7 @@
$: iscityvalid = editable.address?.city?.trim().length !== 0; $: iscityvalid = editable.address?.city?.trim().length !== 0;
function submit() { function submit() {
if (data_loaded === true && save_enabled) { if (data_loaded === true && save_enabled) {
Toastify({ toast.loading($_("contact-is-being-updated"));
text: $_("contact-is-being-updated"),
duration: 2500,
}).showToast();
editable.address.country = "DE"; editable.address.country = "DE";
if (editable.address_checked === false) { if (editable.address_checked === false) {
editable.address = null; editable.address = null;
@ -81,12 +78,9 @@
GroupContactService.groupContactControllerPut(original_data.id, editable) GroupContactService.groupContactControllerPut(original_data.id, editable)
.then((resp) => { .then((resp) => {
Object.assign(original_data, editable); Object.assign(original_data, editable);
original_data=original_data; original_data = original_data;
Toastify({ toast.dismiss();
text: $_("updated-contact"), toast.success($_("updated-contact"));
duration: 2500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
}) })
.catch((err) => {}); .catch((err) => {});
} else { } else {
@ -102,7 +96,7 @@
</script> </script>
{#await promise} {#await promise}
{$_('loading-contact-details')} {$_("loading-contact-details")}
{:then} {:then}
<section class="container p-5 select-none"> <section class="container p-5 select-none">
<div class="flex flex-row mb-4"> <div class="flex flex-row mb-4">
@ -115,12 +109,15 @@
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"><path fill="none" d="M0 0h24v24H0z" /> height="24"
><path fill="none" d="M0 0h24v24H0z" />
<path <path
d="M2 22a8 8 0 1 1 16 0H2zm8-9c-3.315 0-6-2.685-6-6s2.685-6 6-6 6 2.685 6 6-2.685 6-6 6zm10 4h4v2h-4v-2zm-3-5h7v2h-7v-2zm2-5h5v2h-5V7z" /></svg> d="M2 22a8 8 0 1 1 16 0H2zm8-9c-3.315 0-6-2.685-6-6s2.685-6 6-6 6 2.685 6 6-2.685 6-6 6zm10 4h4v2h-4v-2zm-3-5h7v2h-7v-2zm2-5h5v2h-5V7z"
/></svg
>
</li> </li>
<li class="flex items-center ml-2"> <li class="flex items-center ml-2">
<a class="mr-2" href="./">{$_('contacts')}</a><svg <a class="mr-2" href="./">{$_("contacts")}</a><svg
stroke="currentColor" stroke="currentColor"
fill="none" fill="none"
stroke-width="2" stroke-width="2"
@ -130,17 +127,17 @@
class="h-3 w-3 mr-2 stroke-current" class="h-3 w-3 mr-2 stroke-current"
height="1em" height="1em"
width="1em" width="1em"
xmlns="http://www.w3.org/2000/svg"><line xmlns="http://www.w3.org/2000/svg"
x1="5" ><line x1="5" y1="12" x2="19" y2="12" />
y1="12" <polyline points="12 5 19 12 12 19" /></svg
x2="19" >
y2="12" />
<polyline points="12 5 19 12 12 19" /></svg>
</li> </li>
<li class="flex items-center"> <li class="flex items-center">
<span class="mr-2">{original_data.firstname} <span class="mr-2"
{original_data.middlename || ''} >{original_data.firstname}
{original_data.lastname}</span> {original_data.middlename || ""}
{original_data.lastname}</span
>
</li> </li>
</ol> </ol>
</nav> </nav>
@ -148,19 +145,23 @@
</div> </div>
<div class="mb-8 text-3xl font-extrabold leading-tight"> <div class="mb-8 text-3xl font-extrabold leading-tight">
{original_data.firstname} {original_data.firstname}
{original_data.middlename || ''} {original_data.middlename || ""}
{original_data.lastname} {original_data.lastname}
<span data-id="contact_actions_${editable.id}"> <span data-id="contact_actions_${editable.id}">
{#if store.state.jwtinfo.userdetails.permissions.includes('CONTACT:DELETE')} {#if store.state.jwtinfo.userdetails.permissions.includes("CONTACT:DELETE")}
{#if delete_triggered} {#if delete_triggered}
<button <button
on:click={deleteContact} on:click={deleteContact}
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> 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 <button
on:click={() => { on:click={() => {
delete_triggered = !delete_triggered; delete_triggered = !delete_triggered;
}} }}
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-400 text-base font-medium text-white sm:w-auto sm:text-sm">{$_('cancel')}</button> class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-400 text-base font-medium text-white sm:w-auto sm:text-sm"
>{$_("cancel")}</button
>
{/if} {/if}
{#if !delete_triggered} {#if !delete_triggered}
<button <button
@ -168,7 +169,9 @@
delete_triggered = true; delete_triggered = true;
}} }}
type="button" type="button"
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">{$_('delete-contact')}</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-contact")}</button
>
{/if} {/if}
{/if} {/if}
{#if !delete_triggered} {#if !delete_triggered}
@ -177,112 +180,124 @@
class:opacity-50={!save_enabled} class:opacity-50={!save_enabled}
type="button" type="button"
on:click={submit} on:click={submit}
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm">{$_('save-changes')}</button> class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm"
>{$_("save-changes")}</button
>
{/if} {/if}
</span> </span>
</div> </div>
<!-- --> <!-- -->
<div class="text-sm w-full"> <div class="text-sm w-full">
<label <label for="firstname" class="font-medium text-gray-700"
for="firstname" >{$_("first-name")}</label
class="font-medium text-gray-700">{$_('first-name')}</label> >
<input <input
autocomplete="off" autocomplete="off"
placeholder={$_('first-name')} placeholder={$_("first-name")}
type="text" type="text"
class:border-red-500={!isFirstnameValid} class:border-red-500={!isFirstnameValid}
class:focus:border-red-500={!isFirstnameValid} class:focus:border-red-500={!isFirstnameValid}
class:focus:ring-red-500={!isFirstnameValid} class:focus:ring-red-500={!isFirstnameValid}
bind:value={editable.firstname} bind:value={editable.firstname}
name="firstname" name="firstname"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> class="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"
/>
{#if !isFirstnameValid} {#if !isFirstnameValid}
<span <span
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
{$_('first-name-is-required')} >
{$_("first-name-is-required")}
</span> </span>
{/if} {/if}
</div> </div>
<div class="text-sm w-full"> <div class="text-sm w-full">
<label <label for="middlename" class="font-medium text-gray-700"
for="middlename" >{$_("middle-name")}</label
class="font-medium text-gray-700">{$_('middle-name')}</label> >
<input <input
autocomplete="off" autocomplete="off"
placeholder={$_('middle-name')} placeholder={$_("middle-name")}
type="text" type="text"
bind:value={editable.middlename} bind:value={editable.middlename}
name="middlename" name="middlename"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> class="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 class="text-sm w-full"> <div class="text-sm w-full">
<label <label for="lastname" class="font-medium text-gray-700"
for="lastname" >{$_("last-name")}</label
class="font-medium text-gray-700">{$_('last-name')}</label> >
<input <input
autocomplete="off" autocomplete="off"
placeholder={$_('last-name')} placeholder={$_("last-name")}
type="text" type="text"
bind:value={editable.lastname} bind:value={editable.lastname}
class:border-red-500={!isLastnameValid} class:border-red-500={!isLastnameValid}
class:focus:border-red-500={!isLastnameValid} class:focus:border-red-500={!isLastnameValid}
class:focus:ring-red-500={!isLastnameValid} class:focus:ring-red-500={!isLastnameValid}
name="lastname" name="lastname"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> class="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"
/>
{#if !isLastnameValid} {#if !isLastnameValid}
<span <span
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
{$_('last-name-is-required')} >
{$_("last-name-is-required")}
</span> </span>
{/if} {/if}
</div> </div>
<div class="text-sm w-full"> <div class="text-sm w-full">
<label <label for="email" class="font-medium text-gray-700"
for="email" >{$_("e-mail-adress")}</label
class="font-medium text-gray-700">{$_('e-mail-adress')}</label> >
<input <input
autocomplete="off" autocomplete="off"
placeholder={$_('e-mail-adress')} placeholder={$_("e-mail-adress")}
type="email" type="email"
bind:value={editable.email} bind:value={editable.email}
class:border-red-500={!isEmailValid} class:border-red-500={!isEmailValid}
class:focus:border-red-500={!isEmailValid} class:focus:border-red-500={!isEmailValid}
class:focus:ring-red-500={!isEmailValid} class:focus:ring-red-500={!isEmailValid}
name="email" name="email"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> class="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"
/>
{#if !isEmailValid} {#if !isEmailValid}
<span <span
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
{$_('valid-email-is-required')} >
{$_("valid-email-is-required")}
</span> </span>
{/if} {/if}
</div> </div>
<div class="text-sm w-full"> <div class="text-sm w-full">
<label for="phone" class="font-medium text-gray-700">{$_('phone')}</label> <label for="phone" class="font-medium text-gray-700">{$_("phone")}</label>
<input <input
autocomplete="off" autocomplete="off"
placeholder={$_('phone')} placeholder={$_("phone")}
type="tel" type="tel"
class:border-red-500={!isPhoneValidOrEmpty} class:border-red-500={!isPhoneValidOrEmpty}
class:focus:border-red-500={!isPhoneValidOrEmpty} class:focus:border-red-500={!isPhoneValidOrEmpty}
class:focus:ring-red-500={!isPhoneValidOrEmpty} class:focus:ring-red-500={!isPhoneValidOrEmpty}
bind:value={editable.phone} bind:value={editable.phone}
name="phone" name="phone"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> 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"
/>
{#if !isPhoneValidOrEmpty} {#if !isPhoneValidOrEmpty}
<span <span
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
{$_('valid-international-phone-number-is-required')} >
{$_("valid-international-phone-number-is-required")}
</span> </span>
{/if} {/if}
</div> </div>
<div class="text-sm w-full"> <div class="text-sm w-full">
<span class="font-medium text-gray-700">{$_('groups')}</span> <span class="font-medium text-gray-700">{$_("groups")}</span>
<select <select
bind:value={editable.groups} bind:value={editable.groups}
name="team" name="team"
multiple multiple
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"> class="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"
>
{#each teams as team} {#each teams as team}
<option value={team.id}> <option value={team.id}>
{team.parentGroup.name} {team.parentGroup.name}
@ -303,19 +318,20 @@
id="comments" id="comments"
name="comments" name="comments"
type="checkbox" type="checkbox"
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" /> class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
/>
</div> </div>
<div class="ml-3 text-sm"> <div class="ml-3 text-sm">
<label <label for="comments" class="font-medium text-gray-700"
for="comments" >{$_("address")}</label
class="font-medium text-gray-700">{$_('address')}</label> >
</div> </div>
</div> </div>
{#if editable.address_checked === true} {#if editable.address_checked === true}
<div class="col-span-6"> <div class="col-span-6">
<label <label for="address1" class="block text-sm font-medium text-gray-700"
for="address1" >{$_("address")}</label
class="block text-sm font-medium text-gray-700">{$_('address')}</label> >
<input <input
autocomplete="off" autocomplete="off"
placeholder="Address" placeholder="Address"
@ -325,65 +341,72 @@
bind:value={editable.address.address1} bind:value={editable.address.address1}
type="text" type="text"
name="address1" name="address1"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> class="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"
/>
{#if !isAddress1Valid} {#if !isAddress1Valid}
<span <span
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
{$_('address-is-required')} >
{$_("address-is-required")}
</span> </span>
{/if} {/if}
</div> </div>
<div class="col-span-6"> <div class="col-span-6">
<label <label for="address2" class="block text-sm font-medium text-gray-700"
for="address2" >{$_("apartment-suite-etc")}</label
class="block text-sm font-medium text-gray-700">{$_('apartment-suite-etc')}</label> >
<input <input
autocomplete="off" autocomplete="off"
placeholder={$_('apartment-suite-etc')} placeholder={$_("apartment-suite-etc")}
bind:value={editable.address.address2} bind:value={editable.address.address2}
type="text" type="text"
name="address2" name="address2"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> class="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 class="col-span-6"> <div class="col-span-6">
<label <label for="zipcode" class="block text-sm font-medium text-gray-700"
for="zipcode" >{$_("zip-postal-code")}</label
class="block text-sm font-medium text-gray-700">{$_('zip-postal-code')}</label> >
<input <input
autocomplete="off" autocomplete="off"
placeholder={$_('zip-postal-code')} placeholder={$_("zip-postal-code")}
class:border-red-500={!iszipcodevalid} class:border-red-500={!iszipcodevalid}
class:focus:border-red-500={!iszipcodevalid} class:focus:border-red-500={!iszipcodevalid}
class:focus:ring-red-500={!iszipcodevalid} class:focus:ring-red-500={!iszipcodevalid}
bind:value={editable.address.postalcode} bind:value={editable.address.postalcode}
type="text" type="text"
name="zipcode" name="zipcode"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> class="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"
/>
{#if !iszipcodevalid} {#if !iszipcodevalid}
<span <span
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
{$_('valid-zipcode-postal-code-is-required')} >
{$_("valid-zipcode-postal-code-is-required")}
</span> </span>
{/if} {/if}
</div> </div>
<div class="col-span-6"> <div class="col-span-6">
<label <label for="city" class="block text-sm font-medium text-gray-700"
for="city" >{$_("city")}</label
class="block text-sm font-medium text-gray-700">{$_('city')}</label> >
<input <input
autocomplete="off" autocomplete="off"
placeholder={$_('city')} placeholder={$_("city")}
class:border-red-500={!iscityvalid} class:border-red-500={!iscityvalid}
class:focus:border-red-500={!iscityvalid} class:focus:border-red-500={!iscityvalid}
class:focus:ring-red-500={!iscityvalid} class:focus:ring-red-500={!iscityvalid}
bind:value={editable.address.city} bind:value={editable.address.city}
type="text" type="text"
name="city" name="city"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> class="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"
/>
{#if !iscityvalid} {#if !iscityvalid}
<span <span
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
{$_('valid-city-is-required')} >
{$_("valid-city-is-required")}
</span> </span>
{/if} {/if}
</div> </div>

View File

@ -9,21 +9,22 @@
<section class="container p-5"> <section class="container p-5">
<span class="mb-1 text-3xl font-extrabold leading-tight"> <span class="mb-1 text-3xl font-extrabold leading-tight">
{$_('contacts')} {$_("contacts")}
{#if store.state.jwtinfo.userdetails.permissions.includes('CONTACT:CREATE')} {#if store.state.jwtinfo.userdetails.permissions.includes("CONTACT:CREATE")}
<button <button
on:click={() => { on:click={() => {
modal_open = true; modal_open = true;
}} }}
type="button" 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"> 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-contact')} >
{$_("create-a-new-contact")}
</button> </button>
{/if} {/if}
</span> </span>
<ContactsOverview bind:current_contacts /> <ContactsOverview bind:current_contacts />
</section> </section>
{#if store.state.jwtinfo.userdetails.permissions.includes('CONTACT:CREATE')} {#if store.state.jwtinfo.userdetails.permissions.includes("CONTACT:CREATE")}
<AddContactModal bind:current_contacts bind:modal_open /> <AddContactModal bind:current_contacts bind:modal_open />
{/if} {/if}

View File

@ -9,8 +9,8 @@
<div class="text-center items-center justify-center"> <div class="text-center items-center justify-center">
<p class="mb-16 text-lg text-gray-500"> <p class="mb-16 text-lg text-gray-500">
<img class="w-full h-44" src={team_empty} alt="" /> <img class="w-full h-44" src={team_empty} alt="" />
<span class="font-bold">{$_('there-are-no-contacts-added-yet')}</span><br /> <span class="font-bold">{$_("there-are-no-contacts-added-yet")}</span><br />
<span>{$_('add-your-first-contact')}</span> <span>{$_("add-your-first-contact")}</span>
</p> </p>
</div> </div>

View File

@ -1,6 +1,5 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import Toastify from "toastify-js";
import { GroupContactService } from "@odit/lfk-client-js"; import { GroupContactService } from "@odit/lfk-client-js";
const promise = GroupContactService.groupContactControllerGetAll().then( const promise = GroupContactService.groupContactControllerGetAll().then(
(result) => { (result) => {
@ -9,18 +8,20 @@
); );
import store from "../../store"; import store from "../../store";
import ContactsEmptyState from "./ContactsEmptyState.svelte"; import ContactsEmptyState from "./ContactsEmptyState.svelte";
import toast from "svelte-french-toast";
$: searchvalue = ""; $: searchvalue = "";
$: active_deletes = []; $: active_deletes = [];
export let current_contacts = []; export let current_contacts = [];
</script> </script>
{#if store.state.jwtinfo.userdetails.permissions.includes('TEAM:GET')} {#if store.state.jwtinfo.userdetails.permissions.includes("TEAM:GET")}
{#await promise} {#await promise}
<div <div
class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2" class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2"
role="alert"> role="alert"
<p class="font-bold">{$_('contacts-are-being-loaded')}</p> >
<p class="text-sm">{$_('this-might-take-a-moment')}</p> <p class="font-bold">{$_("contacts-are-being-loaded")}</p>
<p class="text-sm">{$_("this-might-take-a-moment")}</p>
</div> </div>
{:then} {:then}
{#if current_contacts.length === 0} {#if current_contacts.length === 0}
@ -29,31 +30,36 @@
<input <input
type="search" type="search"
bind:value={searchvalue} bind:value={searchvalue}
placeholder={$_('datatable.search')} placeholder={$_("datatable.search")}
aria-label={$_('datatable.search')} aria-label={$_("datatable.search")}
class="mb-4" /> class="mb-4"
/>
<div <div
class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll"> class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll"
>
<table class="divide-y divide-gray-200 w-full"> <table class="divide-y divide-gray-200 w-full">
<thead class="bg-gray-50"> <thead class="bg-gray-50">
<tr> <tr>
<th <th
scope="col" scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
{$_('name')} >
{$_("name")}
</th> </th>
<th <th
scope="col" scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
{$_('groups')} >
{$_("groups")}
</th> </th>
<th <th
scope="col" scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
{$_('address')} >
{$_("address")}
</th> </th>
<th scope="col" class="relative px-6 py-3"> <th scope="col" class="relative px-6 py-3">
<span class="sr-only">{$_('action')}</span> <span class="sr-only">{$_("action")}</span>
</th> </th>
</tr> </tr>
</thead> </thead>
@ -69,7 +75,7 @@
<div class="ml-4"> <div class="ml-4">
<div class="text-sm font-medium text-gray-900"> <div class="text-sm font-medium text-gray-900">
{t.firstname} {t.firstname}
{t.middlename || ''} {t.middlename || ""}
{t.lastname} {t.lastname}
</div> </div>
</div> </div>
@ -81,20 +87,24 @@
<div class="text-sm font-medium text-gray-900"> <div class="text-sm font-medium text-gray-900">
{#if t.groups.length > 0} {#if t.groups.length > 0}
{#each t.groups as g} {#each t.groups as g}
{#if g.responseType === 'RUNNERORGANIZATION'} {#if g.responseType === "RUNNERORGANIZATION"}
<a <a
href="../orgs/{g.id}" href="../orgs/{g.id}"
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800">{g.name}</a> class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800"
>{g.name}</a
>
{:else} {:else}
<a <a
href="../teams/{g.id}" href="../teams/{g.id}"
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800">{g.parentGroup.name} class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800"
>{g.parentGroup.name}
&gt; &gt;
{g.name}</a> {g.name}</a
>
{/if} {/if}
{/each} {/each}
{:else} {:else}
{$_('contact-is-not-a-member-in-any-group')} {$_("contact-is-not-a-member-in-any-group")}
{/if} {/if}
</div> </div>
</div> </div>
@ -106,7 +116,7 @@
<div class="text-sm font-medium text-gray-900"> <div class="text-sm font-medium text-gray-900">
{#if t.address.address1 !== null} {#if t.address.address1 !== null}
{t.address.address1}<br /> {t.address.address1}<br />
{t.address.address2 || ''}<br /> {t.address.address2 || ""}<br />
{t.address.postalcode} {t.address.postalcode}
{t.address.city} {t.address.city}
{t.address.country} {t.address.country}
@ -117,45 +127,53 @@
</td> </td>
{#if active_deletes[t.id] === true} {#if active_deletes[t.id] === true}
<td <td
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"
>
<button <button
on:click={() => { on:click={() => {
active_deletes[t.id] = false; active_deletes[t.id] = false;
}} }}
tabindex="0" tabindex="0"
class="ml-4 text-indigo-600 hover:text-indigo-900 cursor-pointer">{$_('cancel-delete')}</button> class="ml-4 text-indigo-600 hover:text-indigo-900 cursor-pointer"
>{$_("cancel-delete")}</button
>
<button <button
on:click={() => { on:click={() => {
GroupContactService.groupContactControllerRemove(t.id, false).then( toast.loading($_("deleting-contact"));
(resp) => { GroupContactService.groupContactControllerRemove(
current_contacts = current_contacts.filter( t.id,
(obj) => obj.id !== t.id false
); ).then((resp) => {
Toastify({ current_contacts = current_contacts.filter(
text: $_('contact-deleted'), (obj) => obj.id !== t.id
duration: 500, );
backgroundColor: toast.dismiss();
'linear-gradient(to right, #00b09b, #96c93d)', toast($_("contact-deleted"));
}).showToast(); });
}
);
}} }}
tabindex="0" tabindex="0"
class="ml-4 text-red-600 hover:text-red-900 cursor-pointer">{$_('confirm-delete')}</button> class="ml-4 text-red-600 hover:text-red-900 cursor-pointer"
>{$_("confirm-delete")}</button
>
</td> </td>
{:else} {:else}
<td <td
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"
>
<a <a
href="./{t.id}" href="./{t.id}"
class="text-indigo-600 hover:text-indigo-900">{$_('details')}</a> class="text-indigo-600 hover:text-indigo-900"
{#if store.state.jwtinfo.userdetails.permissions.includes('TEAM:DELETE')} >{$_("details")}</a
>
{#if store.state.jwtinfo.userdetails.permissions.includes("TEAM:DELETE")}
<button <button
on:click={() => { on:click={() => {
active_deletes[t.id] = true; active_deletes[t.id] = true;
}} }}
tabindex="0" tabindex="0"
class="ml-4 text-red-600 hover:text-red-900 cursor-pointer">{$_('delete')}</button> class="ml-4 text-red-600 hover:text-red-900 cursor-pointer"
>{$_("delete")}</button
>
{/if} {/if}
</td> </td>
{/if} {/if}
@ -169,7 +187,7 @@
{:catch error} {:catch error}
<div class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500"> <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"> <span class="inline-block align-middle mr-8">
<b class="capitalize">{$_('general_promise_error')}</b> <b class="capitalize">{$_("general_promise_error")}</b>
{error} {error}
</span> </span>
</div> </div>

View File

@ -1,363 +1,431 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import localForage from "localforage"; import localForage from "localforage";
import store from "../../store"; import store from "../../store";
import { router } from "tinro"; import { router } from "tinro";
import NoComponentLoaded from "../base/NoComponentLoaded.svelte"; import NoComponentLoaded from "../base/NoComponentLoaded.svelte";
import { AuthService } from "@odit/lfk-client-js"; import { AuthService } from "@odit/lfk-client-js";
import { Toaster } from 'svelte-french-toast'; import { Toaster } from "svelte-french-toast";
$: navOpen = false; $: navOpen = false;
function logout() { function logout() {
localForage.clear(); localForage.clear();
location.replace("/"); location.replace("/");
} }
</script> </script>
<style> <section class="min-h-screen bg-gray-50">
.collapsed_navigation { <div
transform: translateX(-100%); class:collapsed_navigation={!navOpen}
} class="select-none fixed top-0 left-0 z-20 h-full pb-10 overflow-x-hidden overflow-y-auto transition origin-left transform border-r w-60 bg-gray-50"
@media (min-width: 768px) { >
.collapsed_navigation { <a href="/" class="flex items-center px-4 py-5">
transform: translateX(0px); <img src="/lfk-logo.png" alt="Logo" class="h-10" />
} <h3 class="text-lg">Lauf für Kaya! Admin</h3>
} </a>
</style> <nav class="text-sm font-medium text-gray-600" aria-label="Main Navigation">
<a
<section class="min-h-screen bg-gray-50"> class:bg-gray-100={$router.path === "/"}
<div class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
class:collapsed_navigation={!navOpen} href="/"
class="select-none fixed top-0 left-0 z-20 h-full pb-10 overflow-x-hidden overflow-y-auto transition origin-left transform border-r w-60 bg-gray-50"> >
<a href="/" class="flex items-center px-4 py-5"> <svg
<img src="/lfk-logo.png" alt="Logo" class="h-10" /> class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600"
<h3 class="text-lg">Lauf für Kaya! Admin</h3> xmlns="http://www.w3.org/2000/svg"
</a> viewBox="0 0 20 20"
<nav class="text-sm font-medium text-gray-600" aria-label="Main Navigation"> fill="currentColor"
<a >
class:bg-gray-100={$router.path === '/'} <path
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"
href="/"> />
<svg </svg>
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" <span>{$_("dashboard-title")}</span>
xmlns="http://www.w3.org/2000/svg" </a>
viewBox="0 0 20 20" {#if store.state.jwtinfo.userdetails.permissions.includes("ORGANIZATION:GET")}
fill="currentColor"> <a
<path class:bg-gray-100={$router.path.includes("/orgs/")}
d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z" /> class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
</svg> href="/orgs/"
<span>{$_('dashboard-title')}</span> >
</a> <svg
{#if store.state.jwtinfo.userdetails.permissions.includes('ORGANIZATION:GET')} class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600"
<a fill="currentColor"
class:bg-gray-100={$router.path.includes('/orgs/')} xmlns="http://www.w3.org/2000/svg"
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" viewBox="0 0 24 24"
href="/orgs/"> width="24"
<svg height="24"
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" ><path fill="none" d="M0 0h24v24H0z" />
fill="currentColor" <path
xmlns="http://www.w3.org/2000/svg" d="M17 19h2v-8h-6v8h2v-6h2v6zM3 19V4a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v5h2v10h1v2H2v-2h1zm4-8v2h2v-2H7zm0 4v2h2v-2H7zm0-8v2h2V7H7z"
viewBox="0 0 24 24" /></svg
width="24" >
height="24"><path fill="none" d="M0 0h24v24H0z" /> <span>{$_("orgs")}</span>
<path </a>
d="M17 19h2v-8h-6v8h2v-6h2v6zM3 19V4a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v5h2v10h1v2H2v-2h1zm4-8v2h2v-2H7zm0 4v2h2v-2H7zm0-8v2h2V7H7z" /></svg> {/if}
<span>{$_('orgs')}</span> {#if store.state.jwtinfo.userdetails.permissions.includes("USER:GET")}
</a> <a
{/if} class:bg-gray-100={$router.path === "/users/"}
{#if store.state.jwtinfo.userdetails.permissions.includes('USER:GET')} class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
<a href="/users/"
class:bg-gray-100={$router.path === '/users/'} >
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" <svg
href="/users/"> xmlns="http://www.w3.org/2000/svg"
<svg width="24"
xmlns="http://www.w3.org/2000/svg" height="24"
width="24" class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600"
height="24" fill="currentColor"
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" viewBox="0 0 24 24"
fill="currentColor" ><path fill="none" d="M0 0h24v24H0z" />
viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z" /> <path
<path d="M12 14v8H4a8 8 0 018-8zm0-1a6 6 0 110-12 6 6 0 010 12zm2.6 5.81a3.51 3.51 0 010-1.62l-1-.57 1-1.74 1 .58a3.5 3.5 0 011.4-.82V13.5h2v1.15a3.5 3.5 0 011.4.8l1-.57 1 1.74-1 .57a3.51 3.51 0 010 1.62l1 .57-1 1.74-1-.58a3.5 3.5 0 01-1.4.82v1.14h-2v-1.15a3.5 3.5 0 01-1.4-.8l-1 .57-1-1.74 1-.57zM18 17a1 1 0 100 2 1 1 0 000-2z"
d="M12 14v8H4a8 8 0 018-8zm0-1a6 6 0 110-12 6 6 0 010 12zm2.6 5.81a3.51 3.51 0 010-1.62l-1-.57 1-1.74 1 .58a3.5 3.5 0 011.4-.82V13.5h2v1.15a3.5 3.5 0 011.4.8l1-.57 1 1.74-1 .57a3.51 3.51 0 010 1.62l1 .57-1 1.74-1-.58a3.5 3.5 0 01-1.4.82v1.14h-2v-1.15a3.5 3.5 0 01-1.4-.8l-1 .57-1-1.74 1-.57zM18 17a1 1 0 100 2 1 1 0 000-2z" /></svg> /></svg
<span>{$_('users')}</span> >
</a> <span>{$_("users")}</span>
{/if} </a>
{#if store.state.jwtinfo.userdetails.permissions.includes('USERGROUP:GET')} {/if}
<a {#if store.state.jwtinfo.userdetails.permissions.includes("USERGROUP:GET")}
class:bg-gray-100={$router.path === '/groups/'} <a
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" class:bg-gray-100={$router.path === "/groups/"}
href="/groups/"> class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
<svg href="/groups/"
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" >
fill="currentColor" <svg
width="24" class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600"
height="24" fill="currentColor"
xmlns="http://www.w3.org/2000/svg" width="24"
viewBox="0 0 640 512"><path height="24"
fill="currentColor" xmlns="http://www.w3.org/2000/svg"
d="M610.5 341.3c2.6-14.1 2.6-28.5 0-42.6l25.8-14.9c3-1.7 4.3-5.2 3.3-8.5-6.7-21.6-18.2-41.2-33.2-57.4-2.3-2.5-6-3.1-9-1.4l-25.8 14.9c-10.9-9.3-23.4-16.5-36.9-21.3v-29.8c0-3.4-2.4-6.4-5.7-7.1-22.3-5-45-4.8-66.2 0-3.3.7-5.7 3.7-5.7 7.1v29.8c-13.5 4.8-26 12-36.9 21.3l-25.8-14.9c-2.9-1.7-6.7-1.1-9 1.4-15 16.2-26.5 35.8-33.2 57.4-1 3.3.4 6.8 3.3 8.5l25.8 14.9c-2.6 14.1-2.6 28.5 0 42.6l-25.8 14.9c-3 1.7-4.3 5.2-3.3 8.5 6.7 21.6 18.2 41.1 33.2 57.4 2.3 2.5 6 3.1 9 1.4l25.8-14.9c10.9 9.3 23.4 16.5 36.9 21.3v29.8c0 3.4 2.4 6.4 5.7 7.1 22.3 5 45 4.8 66.2 0 3.3-.7 5.7-3.7 5.7-7.1v-29.8c13.5-4.8 26-12 36.9-21.3l25.8 14.9c2.9 1.7 6.7 1.1 9-1.4 15-16.2 26.5-35.8 33.2-57.4 1-3.3-.4-6.8-3.3-8.5l-25.8-14.9zM496 368.5c-26.8 0-48.5-21.8-48.5-48.5s21.8-48.5 48.5-48.5 48.5 21.8 48.5 48.5-21.7 48.5-48.5 48.5zM96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm224 32c1.9 0 3.7-.5 5.6-.6 8.3-21.7 20.5-42.1 36.3-59.2 7.4-8 17.9-12.6 28.9-12.6 6.9 0 13.7 1.8 19.6 5.3l7.9 4.6c.8-.5 1.6-.9 2.4-1.4 7-14.6 11.2-30.8 11.2-48 0-61.9-50.1-112-112-112S208 82.1 208 144c0 61.9 50.1 112 112 112zm105.2 194.5c-2.3-1.2-4.6-2.6-6.8-3.9-8.2 4.8-15.3 9.8-27.5 9.8-10.9 0-21.4-4.6-28.9-12.6-18.3-19.8-32.3-43.9-40.2-69.6-10.7-34.5 24.9-49.7 25.8-50.3-.1-2.6-.1-5.2 0-7.8l-7.9-4.6c-3.8-2.2-7-5-9.8-8.1-3.3.2-6.5.6-9.8.6-24.6 0-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h255.4c-3.7-6-6.2-12.8-6.2-20.3v-9.2zM173.1 274.6C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z" /></svg> viewBox="0 0 640 512"
<span>{$_('user-groups')}</span> ><path
</a> fill="currentColor"
{/if} d="M610.5 341.3c2.6-14.1 2.6-28.5 0-42.6l25.8-14.9c3-1.7 4.3-5.2 3.3-8.5-6.7-21.6-18.2-41.2-33.2-57.4-2.3-2.5-6-3.1-9-1.4l-25.8 14.9c-10.9-9.3-23.4-16.5-36.9-21.3v-29.8c0-3.4-2.4-6.4-5.7-7.1-22.3-5-45-4.8-66.2 0-3.3.7-5.7 3.7-5.7 7.1v29.8c-13.5 4.8-26 12-36.9 21.3l-25.8-14.9c-2.9-1.7-6.7-1.1-9 1.4-15 16.2-26.5 35.8-33.2 57.4-1 3.3.4 6.8 3.3 8.5l25.8 14.9c-2.6 14.1-2.6 28.5 0 42.6l-25.8 14.9c-3 1.7-4.3 5.2-3.3 8.5 6.7 21.6 18.2 41.1 33.2 57.4 2.3 2.5 6 3.1 9 1.4l25.8-14.9c10.9 9.3 23.4 16.5 36.9 21.3v29.8c0 3.4 2.4 6.4 5.7 7.1 22.3 5 45 4.8 66.2 0 3.3-.7 5.7-3.7 5.7-7.1v-29.8c13.5-4.8 26-12 36.9-21.3l25.8 14.9c2.9 1.7 6.7 1.1 9-1.4 15-16.2 26.5-35.8 33.2-57.4 1-3.3-.4-6.8-3.3-8.5l-25.8-14.9zM496 368.5c-26.8 0-48.5-21.8-48.5-48.5s21.8-48.5 48.5-48.5 48.5 21.8 48.5 48.5-21.7 48.5-48.5 48.5zM96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm224 32c1.9 0 3.7-.5 5.6-.6 8.3-21.7 20.5-42.1 36.3-59.2 7.4-8 17.9-12.6 28.9-12.6 6.9 0 13.7 1.8 19.6 5.3l7.9 4.6c.8-.5 1.6-.9 2.4-1.4 7-14.6 11.2-30.8 11.2-48 0-61.9-50.1-112-112-112S208 82.1 208 144c0 61.9 50.1 112 112 112zm105.2 194.5c-2.3-1.2-4.6-2.6-6.8-3.9-8.2 4.8-15.3 9.8-27.5 9.8-10.9 0-21.4-4.6-28.9-12.6-18.3-19.8-32.3-43.9-40.2-69.6-10.7-34.5 24.9-49.7 25.8-50.3-.1-2.6-.1-5.2 0-7.8l-7.9-4.6c-3.8-2.2-7-5-9.8-8.1-3.3.2-6.5.6-9.8.6-24.6 0-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h255.4c-3.7-6-6.2-12.8-6.2-20.3v-9.2zM173.1 274.6C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z"
{#if store.state.jwtinfo.userdetails.permissions.includes('RUNNER:GET')} /></svg
<a >
class:bg-gray-100={$router.path === '/runners/'} <span>{$_("user-groups")}</span>
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" </a>
href="/runners/"> {/if}
<svg {#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:GET")}
xmlns="http://www.w3.org/2000/svg" <a
viewBox="0 0 24 24" class:bg-gray-100={$router.path === "/runners/"}
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
fill="currentColor" href="/runners/"
width="24" >
height="24"><path fill="none" d="M0 0h24v24H0z" /> <svg
<path xmlns="http://www.w3.org/2000/svg"
d="M9.83 8.79L8 9.456V13H6V8.05h.015l5.268-1.918c.244-.093.51-.14.782-.131a2.616 2.616 0 0 1 2.427 1.82c.186.583.356.977.51 1.182A4.992 4.992 0 0 0 19 11v2a6.986 6.986 0 0 1-5.402-2.547l-.581 3.297L15 15.67V23h-2v-5.986l-2.05-1.987-.947 4.298-6.894-1.215.348-1.97 4.924.868L9.83 8.79zM13.5 5.5a2 2 0 1 1 0-4 2 2 0 0 1 0 4z" /></svg> viewBox="0 0 24 24"
<span>{$_('runners')}</span> class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600"
</a> fill="currentColor"
{/if} width="24"
{#if store.state.jwtinfo.userdetails.permissions.includes('TEAM:GET')} height="24"
<a ><path fill="none" d="M0 0h24v24H0z" />
class:bg-gray-100={$router.path === '/teams/'} <path
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" d="M9.83 8.79L8 9.456V13H6V8.05h.015l5.268-1.918c.244-.093.51-.14.782-.131a2.616 2.616 0 0 1 2.427 1.82c.186.583.356.977.51 1.182A4.992 4.992 0 0 0 19 11v2a6.986 6.986 0 0 1-5.402-2.547l-.581 3.297L15 15.67V23h-2v-5.986l-2.05-1.987-.947 4.298-6.894-1.215.348-1.97 4.924.868L9.83 8.79zM13.5 5.5a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"
href="/teams/"> /></svg
<svg >
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" <span>{$_("runners")}</span>
fill="currentColor" </a>
width="24" {/if}
height="24" {#if store.state.jwtinfo.userdetails.permissions.includes("TEAM:GET")}
xmlns="http://www.w3.org/2000/svg" <a
viewBox="0 0 640 512"><path class:bg-gray-100={$router.path === "/teams/"}
fill="currentColor" class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
d="M96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm448 0c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm32 32h-64c-17.6 0-33.5 7.1-45.1 18.6 40.3 22.1 68.9 62 75.1 109.4h66c17.7 0 32-14.3 32-32v-32c0-35.3-28.7-64-64-64zm-256 0c61.9 0 112-50.1 112-112S381.9 32 320 32 208 82.1 208 144s50.1 112 112 112zm76.8 32h-8.3c-20.8 10-43.9 16-68.5 16s-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h288c26.5 0 48-21.5 48-48v-28.8c0-63.6-51.6-115.2-115.2-115.2zm-223.7-13.4C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z" /></svg> href="/teams/"
<span>{$_('teams')}</span> >
</a> <svg
{/if} class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600"
{#if store.state.jwtinfo.userdetails.permissions.includes('DONOR:GET')} fill="currentColor"
<a width="24"
class:bg-gray-100={$router.path.includes('/donors/')} height="24"
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" xmlns="http://www.w3.org/2000/svg"
href="/donors/"> viewBox="0 0 640 512"
<svg ><path
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" fill="currentColor"
fill="currentColor" d="M96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm448 0c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm32 32h-64c-17.6 0-33.5 7.1-45.1 18.6 40.3 22.1 68.9 62 75.1 109.4h66c17.7 0 32-14.3 32-32v-32c0-35.3-28.7-64-64-64zm-256 0c61.9 0 112-50.1 112-112S381.9 32 320 32 208 82.1 208 144s50.1 112 112 112zm76.8 32h-8.3c-20.8 10-43.9 16-68.5 16s-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h288c26.5 0 48-21.5 48-48v-28.8c0-63.6-51.6-115.2-115.2-115.2zm-223.7-13.4C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z"
xmlns="http://www.w3.org/2000/svg" /></svg
viewBox="0 0 24 24" >
width="24" <span>{$_("teams")}</span>
height="24"><path fill="none" d="M0 0h24v24H0z" /> </a>
<path {/if}
d="M9.33 11.5h2.17A4.5 4.5 0 0 1 16 16H8.999L9 17h8v-1a5.578 5.578 0 0 0-.886-3H19a5 5 0 0 1 4.516 2.851C21.151 18.972 17.322 21 13 21c-2.761 0-5.1-.59-7-1.625L6 10.071A6.967 6.967 0 0 1 9.33 11.5zM5 19a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1v-9a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v9zM18 5a3 3 0 1 1 0 6 3 3 0 0 1 0-6zm-7-3a3 3 0 1 1 0 6 3 3 0 0 1 0-6z" /></svg> {#if store.state.jwtinfo.userdetails.permissions.includes("DONOR:GET")}
<span>{$_('donors')}</span> <a
</a> class:bg-gray-100={$router.path.includes("/donors/")}
{/if} class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
{#if store.state.jwtinfo.userdetails.permissions.includes('DONATION:GET')} href="/donors/"
<a >
class:bg-gray-100={$router.path.includes('/donations/')} <svg
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600"
href="/donations/"> fill="currentColor"
<svg xmlns="http://www.w3.org/2000/svg"
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" viewBox="0 0 24 24"
fill="currentColor" width="24"
xmlns="http://www.w3.org/2000/svg" height="24"
viewBox="0 0 24 24" ><path fill="none" d="M0 0h24v24H0z" />
width="24" <path
height="24"><path fill="none" d="M0 0h24v24H0z" /> d="M9.33 11.5h2.17A4.5 4.5 0 0 1 16 16H8.999L9 17h8v-1a5.578 5.578 0 0 0-.886-3H19a5 5 0 0 1 4.516 2.851C21.151 18.972 17.322 21 13 21c-2.761 0-5.1-.59-7-1.625L6 10.071A6.967 6.967 0 0 1 9.33 11.5zM5 19a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1v-9a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v9zM18 5a3 3 0 1 1 0 6 3 3 0 0 1 0-6zm-7-3a3 3 0 1 1 0 6 3 3 0 0 1 0-6z"
<path /></svg
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> >
<span>{$_('donations')}</span> <span>{$_("donors")}</span>
</a> </a>
{/if} {/if}
{#if store.state.jwtinfo.userdetails.permissions.includes('TRACK:GET')} {#if store.state.jwtinfo.userdetails.permissions.includes("DONATION:GET")}
<a <a
class:bg-gray-100={$router.path === '/tracks/'} class:bg-gray-100={$router.path.includes("/donations/")}
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"
href="/tracks/"> href="/donations/"
<svg >
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" <svg
fill="currentColor" class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600"
width="24" fill="currentColor"
height="24" xmlns="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
viewBox="0 0 640 512"><path width="24"
fill="currentColor" height="24"
d="M635.7 167.2L556.1 31.7c-8.8-15-28.3-20.1-43.5-11.5l-69 39.1L503.3 161c2.2 3.8.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9L416 75l-55.2 31.3 27.9 47.4c2.2 3.8.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9L333.2 122 278 153.3 337.8 255c2.2 3.7.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9l-59.7-101.7-55.2 31.3 27.9 47.4c2.2 3.8.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9l-27.9-47.5-55.2 31.3 59.7 101.7c2.2 3.7.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9L84.9 262.9l-69 39.1C.7 310.7-4.6 329.8 4.2 344.8l79.6 135.6c8.8 15 28.3 20.1 43.5 11.5L624.1 210c15.2-8.6 20.4-27.8 11.6-42.8z" /></svg> ><path fill="none" d="M0 0h24v24H0z" />
<span>{$_('tracks')}</span> <path
</a> 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"
{/if} /></svg
{#if store.state.jwtinfo.userdetails.permissions.includes('CARD:GET')} >
<a <span>{$_("donations")}</span>
class:bg-gray-100={$router.path === '/cards/'} </a>
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" {/if}
href="/cards/"> {#if store.state.jwtinfo.userdetails.permissions.includes("TRACK:GET")}
<svg <a
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" class:bg-gray-100={$router.path === "/tracks/"}
fill="currentColor" class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
width="24" href="/tracks/"
height="24" >
xmlns="http://www.w3.org/2000/svg" <svg
viewBox="0 0 24 24"> class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600"
<path fill="none" d="M0 0h24v24H0z" /> fill="currentColor"
<path width="24"
fill="currentColor" height="24"
d="M22 10v10a1 1 0 01-1 1H3a1 1 0 01-1-1V10h20zm0-2H2V4a1 1 0 011-1h18a1 1 0 011 1v4zm-7 8v2h4v-2h-4z" /></svg> xmlns="http://www.w3.org/2000/svg"
<span>{$_('cards')}</span> viewBox="0 0 640 512"
</a> ><path
{/if} fill="currentColor"
{#if store.state.jwtinfo.userdetails.permissions.includes('SCAN:GET')} d="M635.7 167.2L556.1 31.7c-8.8-15-28.3-20.1-43.5-11.5l-69 39.1L503.3 161c2.2 3.8.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9L416 75l-55.2 31.3 27.9 47.4c2.2 3.8.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9L333.2 122 278 153.3 337.8 255c2.2 3.7.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9l-59.7-101.7-55.2 31.3 27.9 47.4c2.2 3.8.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9l-27.9-47.5-55.2 31.3 59.7 101.7c2.2 3.7.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9L84.9 262.9l-69 39.1C.7 310.7-4.6 329.8 4.2 344.8l79.6 135.6c8.8 15 28.3 20.1 43.5 11.5L624.1 210c15.2-8.6 20.4-27.8 11.6-42.8z"
<a /></svg
class:bg-gray-100={$router.path === '/scans/'} >
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" <span>{$_("tracks")}</span>
href="/scans/"> </a>
<svg {/if}
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" {#if store.state.jwtinfo.userdetails.permissions.includes("CARD:GET")}
fill="currentColor" <a
width="24" class:bg-gray-100={$router.path === "/cards/"}
height="24" class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
xmlns="http://www.w3.org/2000/svg" href="/cards/"
viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z" /> >
<path <svg
fill="currentColor" class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600"
d="M2 4h2v16H2V4zm4 0h1v16H6V4zm2 0h2v16H8V4zm3 0h2v16h-2V4zm3 0h2v16h-2V4zm3 0h1v16h-1V4zm2 0h3v16h-3V4z" /></svg> fill="currentColor"
<span>Scans</span> width="24"
</a> height="24"
{/if} xmlns="http://www.w3.org/2000/svg"
{#if store.state.jwtinfo.userdetails.permissions.includes('CONTACT:GET')} viewBox="0 0 24 24"
<a >
class:bg-gray-100={$router.path === '/contacts/'} <path fill="none" d="M0 0h24v24H0z" />
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" <path
href="/contacts/"> fill="currentColor"
<svg d="M22 10v10a1 1 0 01-1 1H3a1 1 0 01-1-1V10h20zm0-2H2V4a1 1 0 011-1h18a1 1 0 011 1v4zm-7 8v2h4v-2h-4z"
fill="currentColor" /></svg
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" >
xmlns="http://www.w3.org/2000/svg" <span>{$_("cards")}</span>
viewBox="0 0 24 24" </a>
width="24" {/if}
height="24"><path fill="none" d="M0 0h24v24H0z" /> {#if store.state.jwtinfo.userdetails.permissions.includes("SCAN:GET")}
<path <a
d="M2 22a8 8 0 1 1 16 0H2zm8-9c-3.315 0-6-2.685-6-6s2.685-6 6-6 6 2.685 6 6-2.685 6-6 6zm10 4h4v2h-4v-2zm-3-5h7v2h-7v-2zm2-5h5v2h-5V7z" /></svg> class:bg-gray-100={$router.path === "/scans/"}
<span>{$_('contacts')}</span> class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
</a> href="/scans/"
{/if} >
{#if store.state.jwtinfo.userdetails.permissions.includes('STATION:GET')} <svg
<a class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600"
class:bg-gray-100={$router.path === '/scanstations/'} fill="currentColor"
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" width="24"
href="/scanstations/"> height="24"
<svg xmlns="http://www.w3.org/2000/svg"
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" viewBox="0 0 24 24"
fill="currentColor" ><path fill="none" d="M0 0h24v24H0z" />
width="24" <path
height="24" fill="currentColor"
viewBox="0 0 24 24" d="M2 4h2v16H2V4zm4 0h1v16H6V4zm2 0h2v16H8V4zm3 0h2v16h-2V4zm3 0h2v16h-2V4zm3 0h1v16h-1V4zm2 0h3v16h-3V4z"
xmlns="http://www.w3.org/2000/svg"><path /></svg
fill="none" >
d="M0 0h24v24H0z" /> <span>Scans</span>
<path </a>
fill="currentColor" {/if}
d="M4 5v11h16V5H4zM2 4a1 1 0 011-1h18a1 1 0 011 1v14H2V4zM1 19h22v2H1v-2z" /></svg> {#if store.state.jwtinfo.userdetails.permissions.includes("CONTACT:GET")}
<span>{$_('scanstations')}</span> <a
</a> class:bg-gray-100={$router.path === "/contacts/"}
{/if} class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
{#if store.state.jwtinfo.userdetails.permissions.includes('STATSCLIENT:GET')} href="/contacts/"
<a >
class:bg-gray-100={$router.path === '/statsclients/'} <svg
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" fill="currentColor"
href="/statsclients/"> class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600"
<svg xmlns="http://www.w3.org/2000/svg"
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" viewBox="0 0 24 24"
fill="currentColor" width="24"
width="24" height="24"
height="24" ><path fill="none" d="M0 0h24v24H0z" />
viewBox="0 0 24 24" <path
xmlns="http://www.w3.org/2000/svg"><path d="M2 22a8 8 0 1 1 16 0H2zm8-9c-3.315 0-6-2.685-6-6s2.685-6 6-6 6 2.685 6 6-2.685 6-6 6zm10 4h4v2h-4v-2zm-3-5h7v2h-7v-2zm2-5h5v2h-5V7z"
fill="none" /></svg
d="M0 0h24v24H0z" /> >
<path <span>{$_("contacts")}</span>
fill="currentColor" </a>
d="M4 5v11h16V5H4zM2 4a1 1 0 011-1h18a1 1 0 011 1v14H2V4zM1 19h22v2H1v-2z" /></svg> {/if}
<span>{$_('statsclients')}</span> {#if store.state.jwtinfo.userdetails.permissions.includes("STATION:GET")}
</a> <a
{/if} class:bg-gray-100={$router.path === "/scanstations/"}
<a class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
class:bg-gray-100={$router.path === '/settings/'} href="/scanstations/"
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" >
href="/settings/"> <svg
<svg class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600"
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" fill="currentColor"
xmlns="http://www.w3.org/2000/svg" width="24"
viewBox="0 0 20 20" height="24"
fill="currentColor"> viewBox="0 0 24 24"
<path xmlns="http://www.w3.org/2000/svg"
fill-rule="evenodd" ><path fill="none" d="M0 0h24v24H0z" />
d="M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.532 1.532 0 01-2.286.948c-1.372-.836-2.942.734-2.106 2.106.54.886.061 2.042-.947 2.287-1.561.379-1.561 2.6 0 2.978a1.532 1.532 0 01.947 2.287c-.836 1.372.734 2.942 2.106 2.106a1.532 1.532 0 012.287.947c.379 1.561 2.6 1.561 2.978 0a1.533 1.533 0 012.287-.947c1.372.836 2.942-.734 2.106-2.106a1.533 1.533 0 01.947-2.287c1.561-.379 1.561-2.6 0-2.978a1.532 1.532 0 01-.947-2.287c.836-1.372-.734-2.942-2.106-2.106a1.532 1.532 0 01-2.287-.947zM10 13a3 3 0 100-6 3 3 0 000 6z" <path
clip-rule="evenodd" /> fill="currentColor"
</svg> d="M4 5v11h16V5H4zM2 4a1 1 0 011-1h18a1 1 0 011 1v14H2V4zM1 19h22v2H1v-2z"
<span>{$_('settings')}</span> /></svg
</a> >
<a <span>{$_("scanstations")}</span>
class:bg-gray-100={$router.path === '/about/'} </a>
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" {/if}
href="/about/"> {#if store.state.jwtinfo.userdetails.permissions.includes("STATSCLIENT:GET")}
<svg <a
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" class:bg-gray-100={$router.path === "/statsclients/"}
xmlns="http://www.w3.org/2000/svg" class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
fill="none" href="/statsclients/"
stroke="currentColor" >
stroke-width="2" <svg
stroke-linecap="round" class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600"
stroke-linejoin="round" fill="currentColor"
viewBox="0 0 24 24"><circle cx="12" cy="12" r="10" /> width="24"
<path d="M12 16v-4M12 8h.01" /></svg> height="24"
<span>{$_('about')}</span> viewBox="0 0 24 24"
</a> xmlns="http://www.w3.org/2000/svg"
<span ><path fill="none" d="M0 0h24v24H0z" />
tabindex="0" <path
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" fill="currentColor"
on:click={() => { d="M4 5v11h16V5H4zM2 4a1 1 0 011-1h18a1 1 0 011 1v14H2V4zM1 19h22v2H1v-2z"
AuthService.authControllerLogout(); /></svg
logout(); >
}}> <span>{$_("statsclients")}</span>
<svg </a>
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" {/if}
fill="currentColor" <a
width="24" class:bg-gray-100={$router.path === "/settings/"}
height="24" class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
xmlns="http://www.w3.org/2000/svg" href="/settings/"
viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z" /> >
<path <svg
d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2a9.985 9.985 0 0 1 8 4h-2.71a8 8 0 1 0 .001 12h2.71A9.985 9.985 0 0 1 12 22zm7-6v-3h-8v-2h8V8l5 4-5 4z" /></svg> class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600"
<span>{$_('logout')}</span> xmlns="http://www.w3.org/2000/svg"
</span> viewBox="0 0 20 20"
</nav> fill="currentColor"
</div> >
<div class="ml-0 transition md:ml-60"> <path
<header fill-rule="evenodd"
class="flex items-center justify-between w-full px-4 bg-white border-b h-14 md:hidden"> d="M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.532 1.532 0 01-2.286.948c-1.372-.836-2.942.734-2.106 2.106.54.886.061 2.042-.947 2.287-1.561.379-1.561 2.6 0 2.978a1.532 1.532 0 01.947 2.287c-.836 1.372.734 2.942 2.106 2.106a1.532 1.532 0 012.287.947c.379 1.561 2.6 1.561 2.978 0a1.533 1.533 0 012.287-.947c1.372.836 2.942-.734 2.106-2.106a1.533 1.533 0 01.947-2.287c1.561-.379 1.561-2.6 0-2.978a1.532 1.532 0 01-.947-2.287c.836-1.372-.734-2.942-2.106-2.106a1.532 1.532 0 01-2.287-.947zM10 13a3 3 0 100-6 3 3 0 000 6z"
<button clip-rule="evenodd"
on:click={() => { />
navOpen = true; </svg>
}} <span>{$_("settings")}</span>
class="block btn btn-light md:hidden"> </a>
<span class="sr-only">Menu</span><svg <a
class="w-4 h-4" class:bg-gray-100={$router.path === "/about/"}
xmlns="http://www.w3.org/2000/svg" class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
viewBox="0 0 20 20" href="/about/"
fill="currentcolor"><path >
fill-rule="evenodd" <svg
d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4A1 1 0 013 5zm0 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm0 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z" class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600"
clip-rule="evenodd" /></svg></button> xmlns="http://www.w3.org/2000/svg"
</header> fill="none"
<Toaster position="top-right" /> stroke="currentColor"
<slot> stroke-width="2"
<NoComponentLoaded /> stroke-linecap="round"
</slot> stroke-linejoin="round"
</div> viewBox="0 0 24 24"
{#if navOpen === true} ><circle cx="12" cy="12" r="10" />
<div <path d="M12 16v-4M12 8h.01" /></svg
on:click={() => { >
navOpen = false; <span>{$_("about")}</span>
console.log({ navOpen }); </a>
}} <span
class:hidden={!navOpen} tabindex="0"
class="fixed inset-0 z-10 w-screen h-screen bg-black bg-opacity-25 md:hidden" /> class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
{/if} on:click={() => {
</section> AuthService.authControllerLogout();
logout();
}}
>
<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"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
><path fill="none" d="M0 0h24v24H0z" />
<path
d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2a9.985 9.985 0 0 1 8 4h-2.71a8 8 0 1 0 .001 12h2.71A9.985 9.985 0 0 1 12 22zm7-6v-3h-8v-2h8V8l5 4-5 4z"
/></svg
>
<span>{$_("logout")}</span>
</span>
</nav>
</div>
<div class="ml-0 transition md:ml-60">
<header
class="flex items-center justify-between w-full px-4 bg-white border-b h-14 md:hidden"
>
<button
on:click={() => {
navOpen = true;
}}
class="block btn btn-light md:hidden"
>
<span class="sr-only">Menu</span><svg
class="w-4 h-4"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentcolor"
><path
fill-rule="evenodd"
d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4A1 1 0 013 5zm0 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm0 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z"
clip-rule="evenodd"
/></svg
></button
>
</header>
<Toaster position="top-right" />
<slot>
<NoComponentLoaded />
</slot>
</div>
{#if navOpen === true}
<div
on:click={() => {
navOpen = false;
}}
class:hidden={!navOpen}
class="fixed inset-0 z-10 w-screen h-screen bg-black bg-opacity-25 md:hidden"
/>
{/if}
</section>
<style>
.collapsed_navigation {
transform: translateX(-100%);
}
@media (min-width: 768px) {
.collapsed_navigation {
transform: translateX(0px);
}
}
</style>

View File

@ -1,14 +1,13 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
export let href = "#" export let href = "#";
export let title = ""; export let title = "";
export let value = ""; export let value = "";
</script> </script>
<a href={href}> <a {href}>
<div <div class="p-4 rounded-lg bg-white border border-grey-100">
class="p-4 rounded-lg bg-white border border-grey-100">
<div class="flex flex-row items-center justify-between"> <div class="flex flex-row items-center justify-between">
<div class="flex flex-col"> <div class="flex flex-col">
<div class="text-xs uppercase font-light text-grey-500"> <div class="text-xs uppercase font-light text-grey-500">
@ -16,7 +15,7 @@
</div> </div>
<div class="text-xl font-bold">{value}</div> <div class="text-xl font-bold">{value}</div>
</div> </div>
<slot></slot> <slot />
</div> </div>
</div> </div>
</a> </a>

View File

@ -8,9 +8,8 @@
RunnerService, RunnerService,
} from "@odit/lfk-client-js"; } from "@odit/lfk-client-js";
import Select from "svelte-select"; import Select from "svelte-select";
import Toastify from "toastify-js";
import { is_promise } from "svelte/internal";
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
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) =>
@ -18,9 +17,6 @@
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());
function focus(el) {
el.focus();
}
$: donor = 0; $: donor = 0;
$: runner = 0; $: runner = 0;
$: donors = []; $: donors = [];
@ -59,10 +55,7 @@
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;
const toast = Toastify({ toast.loading($_("adding-donation"));
text: $_("adding-donation"),
duration: -1,
}).showToast();
if (is_fixed) { if (is_fixed) {
let postdata = { let postdata = {
donor, donor,
@ -79,11 +72,8 @@
amount_input = 0; amount_input = 0;
modal_open = false; modal_open = false;
// //
Toastify({ toast.dismiss();
text: $_("donation_added"), toast.success($_("donation_added"));
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
dispatch("created", { donations: [result] }); dispatch("created", { donations: [result] });
}) })
.catch((err) => { .catch((err) => {
@ -91,8 +81,6 @@
}) })
.finally(() => { .finally(() => {
processed_last_submit = true; processed_last_submit = true;
//
toast.hideToast();
}); });
} else { } else {
let postdata = { let postdata = {
@ -107,11 +95,7 @@
amount_input = 0; amount_input = 0;
modal_open = false; modal_open = false;
// //
Toastify({ toast.success($_("donation_added"));
text: $_("donation_added"),
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
dispatch("created", { donations: [result] }); dispatch("created", { donations: [result] });
}) })
.catch((err) => { .catch((err) => {
@ -119,8 +103,6 @@
}) })
.finally(() => { .finally(() => {
processed_last_submit = true; processed_last_submit = true;
//
toast.hideToast();
}); });
} }
} }

View File

@ -1,18 +1,14 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import { clickOutside } from "../base/outsideclick"; import { clickOutside } from "../base/outsideclick";
import { DonationService } from "@odit/lfk-client-js"; import { DonationService } from "@odit/lfk-client-js";
import Toastify from "toastify-js";
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
import toast from "svelte-french-toast";
export let payment_modal_open = false; export let payment_modal_open = false;
export let original_data = {}; export let original_data = {};
export let paid_amount_input = 0; export let paid_amount_input = 0;
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
$: processed_last_submit = true; $: processed_last_submit = true;
function focus(el) {
el.focus();
}
$: createbtnenabled = $: createbtnenabled =
is_paid_amount_valid && is_paid_amount_valid &&
!(paid_amount_input * 100 == original_data.paidAmount); !(paid_amount_input * 100 == original_data.paidAmount);
@ -34,10 +30,7 @@
function submit() { function submit() {
if (processed_last_submit === true) { if (processed_last_submit === true) {
processed_last_submit = false; processed_last_submit = false;
const toast = Toastify({ toast.loading($_("updating-donation"));
text: $_("updating-donation"),
duration: -1,
}).showToast();
const editable = Object.assign({}, original_data); const editable = Object.assign({}, original_data);
editable.donor = editable.donor.id; editable.donor = editable.donor.id;
editable.paidAmount = paid_amount_input * 100; editable.paidAmount = paid_amount_input * 100;
@ -50,11 +43,9 @@
.then((result) => { .then((result) => {
payment_modal_open = false; payment_modal_open = false;
// //
Toastify({ toast.dismiss();
text: $_("donation-updated"),
duration: 500, toast.success($_("donation-updated"));
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
dispatch("created", { donation: response }); dispatch("created", { donation: response });
}) })
.catch((err) => { .catch((err) => {
@ -62,19 +53,14 @@
}) })
.finally(() => { .finally(() => {
processed_last_submit = true; processed_last_submit = true;
//
toast.hideToast();
}); });
} else { } else {
DonationService.donationControllerPutFixed(original_data.id, editable) DonationService.donationControllerPutFixed(original_data.id, editable)
.then((result) => { .then((result) => {
payment_modal_open = false; payment_modal_open = false;
// //
Toastify({ toast.dismiss();
text: $_("donation-updated"), toast.success($_("donation-updated"));
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
dispatch("created", { donation: response }); dispatch("created", { donation: response });
}) })
.catch((err) => { .catch((err) => {
@ -82,8 +68,6 @@
}) })
.finally(() => { .finally(() => {
processed_last_submit = true; processed_last_submit = true;
//
toast.hideToast();
}); });
} }
} }

View File

@ -6,7 +6,7 @@
DonorService, DonorService,
RunnerService, RunnerService,
} from "@odit/lfk-client-js"; } from "@odit/lfk-client-js";
import Toastify from "toastify-js";
import PromiseError from "../base/PromiseError.svelte"; import PromiseError from "../base/PromiseError.svelte";
import Select from "svelte-select"; import Select from "svelte-select";
let data_loaded = false; let data_loaded = false;
@ -33,7 +33,7 @@
!(Math.floor(amount_input * 100) === original_data.amountPerDistance)) || !(Math.floor(amount_input * 100) === original_data.amountPerDistance)) ||
(original_data.responseType !== "DISTANCEDONATION" && (original_data.responseType !== "DISTANCEDONATION" &&
!(Math.floor(amount_input * 100) === original_data.amount)) || !(Math.floor(amount_input * 100) === original_data.amount)) ||
!(Math.floor(paid_amount_input * 100) === original_data.paidAmount); !(Math.floor(paid_amount_input * 100) === original_data.paidAmount);
$: save_enabled = changes_performed && is_amount_valid && is_everything_set; $: save_enabled = changes_performed && is_amount_valid && is_everything_set;
const promise = DonationService.donationControllerGetOne( const promise = DonationService.donationControllerGetOne(
@ -69,12 +69,9 @@
function submit() { function submit() {
if (data_loaded === true && save_enabled) { if (data_loaded === true && save_enabled) {
Toastify({ toast($_("updating-donation"));
text: $_('updating-donation'),
duration: 2500,
}).showToast();
let postdata = {}; let postdata = {};
editable.paidAmount = paid_amount_input*100; editable.paidAmount = paid_amount_input * 100;
if (original_data.responseType === "DISTANCEDONATION") { if (original_data.responseType === "DISTANCEDONATION") {
editable.amountPerDistance = Math.floor(amount_input * 100); editable.amountPerDistance = Math.floor(amount_input * 100);
postdata = Object.assign(postdata, editable); postdata = Object.assign(postdata, editable);
@ -87,11 +84,7 @@
.then((resp) => { .then((resp) => {
Object.assign(original_data, editable); Object.assign(original_data, editable);
original_data = original_data; original_data = original_data;
Toastify({ toast.success($_("donation-updated"));
text: $_('donation-updated'),
duration: 2500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
}) })
.catch((err) => {}); .catch((err) => {});
} else { } else {
@ -102,11 +95,7 @@
.then((resp) => { .then((resp) => {
Object.assign(original_data, editable); Object.assign(original_data, editable);
original_data = original_data; original_data = original_data;
Toastify({ toast.success($_("donation-updated"));
text: $_('donation-updated'),
duration: 2500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
}) })
.catch((err) => {}); .catch((err) => {});
} }
@ -116,11 +105,7 @@
function deleteDonation() { function deleteDonation() {
DonationService.donationControllerRemove(original_data.id, false) DonationService.donationControllerRemove(original_data.id, false)
.then((resp) => { .then((resp) => {
Toastify({ toast($_("donation-deleted"));
text: $_('donation-deleted'),
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
location.replace("./"); location.replace("./");
}) })
.catch((err) => { .catch((err) => {
@ -131,7 +116,7 @@
</script> </script>
{#await promise} {#await promise}
{$_('loading-donation-details')} {$_("loading-donation-details")}
{:then} {:then}
<section class="container p-5 select-none"> <section class="container p-5 select-none">
<div class="flex flex-row mb-4"> <div class="flex flex-row mb-4">
@ -144,12 +129,15 @@
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"><path fill="none" d="M0 0h24v24H0z" /> height="24"
><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" /></svg> 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
>
</li> </li>
<li class="flex items-center ml-2"> <li class="flex items-center ml-2">
<a class="mr-2" href="./">{$_('donations')}</a><svg <a class="mr-2" href="./">{$_("donations")}</a><svg
stroke="currentColor" stroke="currentColor"
fill="none" fill="none"
stroke-width="2" stroke-width="2"
@ -159,12 +147,10 @@
class="h-3 w-3 mr-2 stroke-current" class="h-3 w-3 mr-2 stroke-current"
height="1em" height="1em"
width="1em" width="1em"
xmlns="http://www.w3.org/2000/svg"><line xmlns="http://www.w3.org/2000/svg"
x1="5" ><line x1="5" y1="12" x2="19" y2="12" />
y1="12" <polyline points="12 5 19 12 12 19" /></svg
x2="19" >
y2="12" />
<polyline points="12 5 19 12 12 19" /></svg>
</li> </li>
<li class="flex items-center"> <li class="flex items-center">
<span class="mr-2">{original_data.id}</span> <span class="mr-2">{original_data.id}</span>
@ -175,28 +161,32 @@
</div> </div>
<div class="mb-8 text-3xl font-extrabold leading-tight"> <div class="mb-8 text-3xl font-extrabold leading-tight">
{original_data.donor.firstname} {original_data.donor.firstname}
{original_data.donor.middlename || ''} {original_data.donor.middlename || ""}
{original_data.donor.lastname} {original_data.donor.lastname}
&gt; &gt;
{#if original_data.responseType == 'DISTANCEDONATION'} {#if original_data.responseType == "DISTANCEDONATION"}
{original_data.runner.firstname} {original_data.runner.firstname}
{original_data.runner.middlename || ''} {original_data.runner.middlename || ""}
{original_data.runner.lastname} {original_data.runner.lastname}
{:else} {:else}
{$_('fixed-donation')}: {$_("fixed-donation")}:
{amount_input.toFixed(2).toLocaleString('de-DE', { valute: 'EUR' })} {amount_input.toFixed(2).toLocaleString("de-DE", { valute: "EUR" })}
{/if} {/if}
<span data-id="donation_actions_${original_data.id}"> <span data-id="donation_actions_${original_data.id}">
{#if store.state.jwtinfo.userdetails.permissions.includes('DONATION:DELETE')} {#if store.state.jwtinfo.userdetails.permissions.includes("DONATION:DELETE")}
{#if delete_triggered} {#if delete_triggered}
<button <button
on:click={deleteDonation} on:click={deleteDonation}
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:">{$_('confirm-deletion')}</button> class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:"
>{$_("confirm-deletion")}</button
>
<button <button
on:click={() => { on:click={() => {
delete_triggered = !delete_triggered; delete_triggered = !delete_triggered;
}} }}
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-400 text-base font-medium text-white sm:w-auto sm:">{$_('cancel')}</button> class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-400 text-base font-medium text-white sm:w-auto sm:"
>{$_("cancel")}</button
>
{/if} {/if}
{#if !delete_triggered} {#if !delete_triggered}
<button <button
@ -204,7 +194,9 @@
delete_triggered = true; delete_triggered = true;
}} }}
type="button" type="button"
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:">{$_('delete-donation')}</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:"
>{$_("delete-donation")}</button
>
{/if} {/if}
{/if} {/if}
{#if !delete_triggered} {#if !delete_triggered}
@ -213,72 +205,91 @@
class:opacity-50={!save_enabled} class:opacity-50={!save_enabled}
type="button" type="button"
on:click={submit} on:click={submit}
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:">{$_('save-changes')}</button> class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:"
>{$_("save-changes")}</button
>
{/if} {/if}
</span> </span>
</div> </div>
<!-- --> <!-- -->
<div> <div>
<span class="font-medium text-gray-700"
>{$_("total-donation-amount")}:</span
>
<span <span
class="font-medium text-gray-700">{$_('total-donation-amount')}:</span> >{(editable.amount / 100)
<span>{(editable.amount / 100)
.toFixed(2) .toFixed(2)
.toLocaleString('de-DE', { valute: 'EUR' })}€</span> .toLocaleString("de-DE", { valute: "EUR" })}€</span
>
| |
<span class="font-medium text-gray-700">{$_("paid-amount")}:</span>
<span <span
class="font-medium text-gray-700">{$_('paid-amount')}:</span> >{(editable.paidAmount / 100)
<span>{(editable.paidAmount / 100)
.toFixed(2) .toFixed(2)
.toLocaleString('de-DE', { valute: 'EUR' })}€</span> .toLocaleString("de-DE", { valute: "EUR" })}€</span
>
| |
<span <span class="font-medium text-gray-700">{$_("status")}:</span>
class="font-medium text-gray-700">{$_('status')}:</span> {#if editable.status == "PAID"}
{#if editable.status =="PAID"} <span
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800"
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">{$_('paid')}</span> >{$_("paid")}</span
{:else} >
<span {:else}
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800">{$_('open')}</span> <span
{/if} class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800"
>{$_("open")}</span
>
{/if}
</div> </div>
<br> <br />
<div class=" w-full"> <div class=" w-full">
<label <label for="donor" class="block font-medium text-gray-700"
for="donor" >{$_("donor")}</label
class="block font-medium text-gray-700">{$_('donor')}</label> >
<Select <Select
containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"
itemFilter={(label, filterText, option) => filterDonors(label, filterText, option)} itemFilter={(label, filterText, option) =>
filterDonors(label, filterText, option)}
items={current_donors} items={current_donors}
showChevron={true} showChevron={true}
placeholder={$_('search-for-donor-name-or-id')} placeholder={$_("search-for-donor-name-or-id")}
noOptionsMessage={$_('no-donors-found')} noOptionsMessage={$_("no-donors-found")}
bind:selectedValue={donor} bind:selectedValue={donor}
on:select={(selectedValue) => {editable.donor = selectedValue.detail.value; editable.donor.donationAmount=original_data.donor.donationAmount; editable.donor.paidDonationAmount =original_data.donor.paidDonationAmount}} on:select={(selectedValue) => {
on:clear={() => (editable.donor = null)} /> editable.donor = selectedValue.detail.value;
editable.donor.donationAmount = original_data.donor.donationAmount;
editable.donor.paidDonationAmount =
original_data.donor.paidDonationAmount;
}}
on:clear={() => (editable.donor = null)}
/>
</div> </div>
{#if original_data.responseType == 'DISTANCEDONATION'} {#if original_data.responseType == "DISTANCEDONATION"}
<div class=" w-full"> <div class=" w-full">
<label <label for="donor" class="block font-medium text-gray-700"
for="donor" >{$_("runner")}</label
class="block font-medium text-gray-700">{$_('runner')}</label> >
<Select <Select
containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"
itemFilter={(label, filterText, option) => filterDonors(label, filterText, option)} itemFilter={(label, filterText, option) =>
filterDonors(label, filterText, option)}
items={current_runners} items={current_runners}
showChevron={true} showChevron={true}
placeholder={$_('search-for-runner-by-name-or-id')} placeholder={$_("search-for-runner-by-name-or-id")}
noOptionsMessage={$_('no-runners-found')} noOptionsMessage={$_("no-runners-found")}
bind:selectedValue={runner} bind:selectedValue={runner}
on:select={(selectedValue) => (editable.runner = selectedValue.detail.value)} on:select={(selectedValue) =>
on:clear={() => (editable.runner = null)} /> (editable.runner = selectedValue.detail.value)}
on:clear={() => (editable.runner = null)}
/>
</div> </div>
{/if} {/if}
<div class=" w-full"> <div class=" w-full">
<label for="lastname" class="font-medium text-gray-700"> <label for="lastname" class="font-medium text-gray-700">
{#if original_data.responseType == 'DISTANCEDONATION'} {#if original_data.responseType == "DISTANCEDONATION"}
{$_('amount-per-kilometer')} {$_("amount-per-kilometer")}
{:else}{$_('donation-amount')}{/if} {:else}{$_("donation-amount")}{/if}
</label> </label>
<div class="mt-1 flex rounded-md shadow-sm"> <div class="mt-1 flex rounded-md shadow-sm">
<input <input
@ -291,49 +302,61 @@
step="0.01" step="0.01"
name="donation_amount_eur" name="donation_amount_eur"
class="focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-l-md sm: border-gray-300 border bg-gray-50 text-gray-500 p-2" class="focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-l-md sm: border-gray-300 border bg-gray-50 text-gray-500 p-2"
placeholder="2.00" /> placeholder="2.00"
/>
<span <span
class="inline-flex items-center px-3 rounded-r-md border border-gray-300 bg-gray-50 text-gray-500 ">€</span> class="inline-flex items-center px-3 rounded-r-md border border-gray-300 bg-gray-50 text-gray-500"
>€</span
>
</div> </div>
{#if !is_amount_valid} {#if !is_amount_valid}
<span <span
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> 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')} >
{$_("donation-amount-must-be-greater-that-0-00eur")}
</span> </span>
{/if} {/if}
</div> </div>
<div class="w-full"> <div class="w-full">
<label <label for="token" class="block text-sm font-medium text-gray-700"
for="token" >{$_("paid-amount")}</label
class="block text-sm font-medium text-gray-700">{$_('paid-amount')}</label> >
<div class="inline-flex border-gray-300 border rounded-l-md rounded-r-md bg-gray-50 text-gray-500 w-full"> <div
class="inline-flex border-gray-300 border rounded-l-md rounded-r-md bg-gray-50 text-gray-500 w-full"
>
<input <input
autocomplete="off" autocomplete="off"
class:border-red-500={!is_amount_valid} class:border-red-500={!is_amount_valid}
class:focus:border-red-500={!is_amount_valid} class:focus:border-red-500={!is_amount_valid}
class:focus:ring-red-500={!is_amount_valid} class:focus:ring-red-500={!is_amount_valid}
bind:value={paid_amount_input} bind:value={paid_amount_input}
type="number" type="number"
step="0.01" step="0.01"
name="donation_amount_eur" name="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 p-2" class="focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-l-md sm:text-sm p-2"
placeholder="2.00" /> placeholder="2.00"
<button />
on:click={ <button
()=>{ on:click={() => {
paid_amount_input=paid_amount_input = (original_data.amount/100).toFixed(2); paid_amount_input = paid_amount_input = (
} original_data.amount / 100
} ).toFixed(2);
class="inline-flex items-center p-r-2 text-indigo-300 hover:text-indigo-700 text-sm">MAX</button> }}
<span class="inline-flex items-center p-r-2 text-indigo-300 hover:text-indigo-700 text-sm"
class="inline-flex items-center px-3 rounded-r-md border border-gray-300 bg-gray-50 text-gray-500 text-sm">€</span> >MAX</button
</div> >
{#if !is_paid_amount_valid} <span
<span class="inline-flex items-center px-3 rounded-r-md border border-gray-300 bg-gray-50 text-gray-500 text-sm"
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> >€</span
{$_('payment-amount-must-be-greater-than-0-00eur')} >
</span> </div>
{/if} {#if !is_paid_amount_valid}
<span
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
>
{$_("payment-amount-must-be-greater-than-0-00eur")}
</span>
{/if}
</div> </div>
</section> </section>
{:catch error} {:catch error}

View File

@ -29,7 +29,6 @@
{#if store.state.jwtinfo.userdetails.permissions.includes("DONATION:CREATE")} {#if store.state.jwtinfo.userdetails.permissions.includes("DONATION:CREATE")}
<AddDonationModal <AddDonationModal
on:created={(event) => { on:created={(event) => {
console.log(event)
addDonations(event.detail.donations); addDonations(event.detail.donations);
}} }}
bind:modal_open bind:modal_open

View File

@ -6,7 +6,7 @@
<div class="text-center items-center justify-center"> <div class="text-center items-center justify-center">
<p class="mb-16 text-lg text-gray-500"> <p class="mb-16 text-lg text-gray-500">
<img class="m-auto" style="height:15rem" src={donations_empty} alt="" /> <img class="m-auto" style="height:15rem" src={donations_empty} alt="" />
<span class="font-bold">{$_('there-are-no-donations-yet')}</span><br /> <span class="font-bold">{$_("there-are-no-donations-yet")}</span><br />
<span>{$_('add-your-fist-donation')}</span> <span>{$_("add-your-fist-donation")}</span>
</p> </p>
</div> </div>

View File

@ -27,11 +27,10 @@
donationDonorFilter, donationDonorFilter,
donationRunnerFilter, donationRunnerFilter,
} from "../shared/tablefilters"; } from "../shared/tablefilters";
import toast from "svelte-french-toast";
$: searchvalue = ""; $: searchvalue = "";
$: active_deletes = []; $: active_deletes = [];
$: active_edits = []; $: active_edits = [];
$: selectedDonations =
$table?.getSelectedRowModel().rows.map((row) => row.original) || [];
$: selected = $: selected =
$table?.getSelectedRowModel().rows.map((row) => row.index) || []; $table?.getSelectedRowModel().rows.map((row) => row.index) || [];
$: dataLoaded = false; $: dataLoaded = false;
@ -164,11 +163,7 @@
...options, ...options,
data: current_donations, data: current_donations,
})); }));
Toastify({ toast($_("donation-deleted"));
text: $_("donation-deleted"),
duration: 3500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
} }
onMount(async () => { onMount(async () => {
@ -191,7 +186,6 @@
dataLoaded = true; dataLoaded = true;
page++; page++;
} }
console.log("All donations loaded");
}); });
</script> </script>

View File

@ -5,8 +5,9 @@
import { DonorService } from "@odit/lfk-client-js"; import { DonorService } from "@odit/lfk-client-js";
import isEmail from "validator/es/lib/isEmail"; import isEmail from "validator/es/lib/isEmail";
import isMobilePhone from "validator/es/lib/isMobilePhone"; import isMobilePhone from "validator/es/lib/isMobilePhone";
import Toastify from "toastify-js";
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
import toast from "svelte-french-toast";
export let modal_open; export let modal_open;
let firstname_input; let firstname_input;
let lastname_input; let lastname_input;
@ -73,10 +74,7 @@
function submit() { function submit() {
if (processed_last_submit === true) { if (processed_last_submit === true) {
processed_last_submit = false; processed_last_submit = false;
const toast = Toastify({ toast.loading($_("donor-is-being-added"));
text: $_("donor-is-being-added"),
duration: -1,
}).showToast();
let address = {}; let address = {};
if (address_checked === true) { if (address_checked === true) {
address = { address = {
@ -110,11 +108,8 @@
email_input_value = ""; email_input_value = "";
modal_open = false; modal_open = false;
// //
Toastify({ toast.dismiss();
text: $_("donor-added"), toast.success($_("donor-added"));
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
dispatch("created", { donors: [result] }); dispatch("created", { donors: [result] });
}) })
.catch((err) => { .catch((err) => {
@ -122,8 +117,6 @@
}) })
.finally(() => { .finally(() => {
processed_last_submit = true; processed_last_submit = true;
//
toast.hideToast();
}); });
} }
} }

View File

@ -3,7 +3,7 @@
import { clickOutside } from "../base/outsideclick"; import { clickOutside } from "../base/outsideclick";
import { DonorService } from "@odit/lfk-client-js"; import { DonorService } from "@odit/lfk-client-js";
import Toastify from "toastify-js";
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
export let modal_open; export let modal_open;
export let delete_donor; export let delete_donor;

View File

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

View File

@ -4,7 +4,7 @@
</script> </script>
{#if !donations || donations.length == 0} {#if !donations || donations.length == 0}
{$_('donor-has-no-associated-donations')} {$_("donor-has-no-associated-donations")}
{:else} {:else}
{#each donations as donation} {#each donations as donation}
{#if donation.responseType === "DISTANCEDONATION"} {#if donation.responseType === "DISTANCEDONATION"}

View File

@ -6,7 +6,7 @@
<div class="text-center items-center justify-center"> <div class="text-center items-center justify-center">
<p class="mb-16 text-lg text-gray-500"> <p class="mb-16 text-lg text-gray-500">
<img class="w-full" style="height:15rem" src={donors_empty} alt="" /> <img class="w-full" style="height:15rem" src={donors_empty} alt="" />
<span class="font-bold">{$_('there-are-no-donors-yet')}</span><br /> <span class="font-bold">{$_("there-are-no-donors-yet")}</span><br />
<span>{$_('add-your-first-donor')}</span> <span>{$_("add-your-first-donor")}</span>
</p> </p>
</div> </div>

View File

@ -1,10 +1,9 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import { DonationService, DonorService } from "@odit/lfk-client-js"; import { DonorService } from "@odit/lfk-client-js";
import store from "../../store"; import store from "../../store";
import DonorsEmptyState from "./DonorsEmptyState.svelte"; import DonorsEmptyState from "./DonorsEmptyState.svelte";
import ConfirmDonorDeletion from "./ConfirmDonorDeletion.svelte"; import ConfirmDonorDeletion from "./ConfirmDonorDeletion.svelte";
import Toastify from "toastify-js";
import TableBottom from "../shared/TableBottom.svelte"; import TableBottom from "../shared/TableBottom.svelte";
import { import {
createSvelteTable, createSvelteTable,
@ -23,6 +22,7 @@
import DonorAddress from "./DonorAddress.svelte"; import DonorAddress from "./DonorAddress.svelte";
import DonorDonations from "./DonorDonations.svelte"; import DonorDonations from "./DonorDonations.svelte";
import { filterAddress, filterName } from "../shared/tablefilters"; import { filterAddress, filterName } from "../shared/tablefilters";
import toast from "svelte-french-toast";
$: searchvalue = ""; $: searchvalue = "";
$: active_deletes = []; $: active_deletes = [];
$: selectedDonors = $: selectedDonors =
@ -162,8 +162,6 @@
dataLoaded = true; dataLoaded = true;
page++; page++;
} }
console.log("All donors loaded");
}); });
</script> </script>
@ -172,12 +170,10 @@
active_deletes = active_deletes.filter((a) => a.id !== event.detail.id); active_deletes = active_deletes.filter((a) => a.id !== event.detail.id);
}} }}
on:delete={async (event) => { on:delete={async (event) => {
toast.loading($_("deleting-donor"));
await DonorService.donorControllerRemove(event.detail.id, true); await DonorService.donorControllerRemove(event.detail.id, true);
Toastify({ toast.dismiss();
text: $_("donor-deleted"), toast($_("donor-deleted"));
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
current_donors = current_donors.filter((d) => d.id !== event.detail.id); current_donors = current_donors.filter((d) => d.id !== event.detail.id);
active_deletes = active_deletes.filter((a) => a.id !== event.detail.id); active_deletes = active_deletes.filter((a) => a.id !== event.detail.id);
options.update((options) => ({ options.update((options) => ({

View File

@ -1,7 +1,7 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import { clickOutside } from "../base/outsideclick"; import { clickOutside } from "../base/outsideclick";
export let modal_open; export let modal_open;
(function () { (function () {
document.onkeydown = function (e) { document.onkeydown = function (e) {
@ -25,43 +25,51 @@
{#if modal_open} {#if modal_open}
<div <div
class="fixed z-10 inset-0 overflow-y-auto" class="fixed z-10 inset-0 overflow-y-auto"
use:clickOutside use:clickOutside
on:click_outside={() => { on:click_outside={() => {
modal_open = false; modal_open = false;
}}> }}
>
<div <div
class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> 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="fixed inset-0 transition-opacity" aria-hidden="true">
<div <div
class="absolute inset-0 bg-gray-500 opacity-75" class="absolute inset-0 bg-gray-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 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" 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" 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"> <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="sm:flex sm:items-start">
<div <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"> 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 <svg
fill="currentColor" fill="currentColor"
class="h-6 w-6 text-blue-600" class="h-6 w-6 text-blue-600"
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"><path fill="none" d="M0 0h24v24H0z" /> height="24"
><path fill="none" d="M0 0h24v24H0z" />
<path <path
d="M14 20v2H2v-2h12zM14.586.686l7.778 7.778L20.95 9.88l-1.06-.354L17.413 12l5.657 5.657-1.414 1.414L16 13.414l-2.404 2.404.283 1.132-1.415 1.414-7.778-7.778 1.415-1.414 1.13.282 6.294-6.293-.353-1.06L14.586.686z" /></svg> d="M14 20v2H2v-2h12zM14.586.686l7.778 7.778L20.95 9.88l-1.06-.354L17.413 12l5.657 5.657-1.414 1.414L16 13.414l-2.404 2.404.283 1.132-1.415 1.414-7.778-7.778 1.415-1.414 1.13.282 6.294-6.293-.353-1.06L14.586.686z"
/></svg
>
</div> </div>
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"> <div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<h3 class="text-lg leading-6 font-medium"> <h3 class="text-lg leading-6 font-medium">
{$_('read-license')} {$_("read-license")}
</h3> </h3>
<div class="mt-2 mb-6"> <div class="mt-2 mb-6">
<p class="text-sm text-gray-500">{currentlicense}</p> <p class="text-sm text-gray-500">{currentlicense}</p>
@ -78,8 +86,9 @@
modal_open = false; modal_open = false;
}} }}
type="button" 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"> 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"
{$_('close')} >
{$_("close")}
</button> </button>
</div> </div>
</div> </div>
@ -90,19 +99,21 @@
<div class="pt-12 px-4 sm:px-6 lg:px-8 lg:pt-20 bg-gray-900 pb-12"> <div class="pt-12 px-4 sm:px-6 lg:px-8 lg:pt-20 bg-gray-900 pb-12">
<div class="text-center mb-8"> <div class="text-center mb-8">
<h1 <h1
class="mt-9 font-display text-4xl leading-none font-semibold text-white sm:text-5xl lg:text-6xl"> class="mt-9 font-display text-4xl leading-none font-semibold text-white sm:text-5xl lg:text-6xl"
{$_('about')} >
{$_("about")}
🧾 🧾
</h1> </h1>
<p <p
class="mt-2 max-w-xl mx-auto text-xl lg:max-w-3xl lg:text-2xl text-gray-300"> class="mt-2 max-w-xl mx-auto text-xl lg:max-w-3xl lg:text-2xl text-gray-300"
>
Lauf für Kaya! Lauf für Kaya!
<strong class="text-white font-medium"> <strong class="text-white font-medium">
{$_('by')} {$_("by")}
<a href="https://odit.services" class="underline">ODIT.Services</a> <a href="https://odit.services" class="underline">ODIT.Services</a>
</strong> </strong>
<br /> <br />
<span class="text-lg">{$_('lfk-is-os')}</span> <span class="text-lg">{$_("lfk-is-os")}</span>
</p> </p>
</div> </div>
</div> </div>
@ -110,23 +121,23 @@
<div class="pt-0 pb-16 overflow-hidden lg:pt-12 lg:py-24"> <div class="pt-0 pb-16 overflow-hidden lg:pt-12 lg:py-24">
<div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8"> <div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
<h2 class="text-4xl font-display font-semibold md:text-5xl"> <h2 class="text-4xl font-display font-semibold md:text-5xl">
{$_('credits')} {$_("credits")}
</h2> </h2>
<div class="max-w-3xl mx-auto text-xl leading-8 font-medium mt-8"> <div class="max-w-3xl mx-auto text-xl leading-8 font-medium mt-8">
<p class="text-center">{$_('oss_credit_description')}</p> <p class="text-center">{$_("oss_credit_description")}</p>
</div> </div>
<div class="w-screen leading-8 pl-5 mt-5"> <div class="w-screen leading-8 pl-5 mt-5">
{#await license_promise} {#await license_promise}
<p class="text-center w-full">{$_('licenses-are-being-loaded')}</p> <p class="text-center w-full">{$_("licenses-are-being-loaded")}</p>
{:then} {:then}
<table> <table>
<thead> <thead>
<tr> <tr>
<th>{$_('dependency_name')}</th> <th>{$_("dependency_name")}</th>
<th>{$_('license')}</th> <th>{$_("license")}</th>
<th>{$_('repo_link')}</th> <th>{$_("repo_link")}</th>
<th>{$_('installed-version')}</th> <th>{$_("installed-version")}</th>
<th>{$_('author')}</th> <th>{$_("author")}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -134,58 +145,64 @@
<tr> <tr>
<td>{l.name}</td> <td>{l.name}</td>
<td> <td>
{l.license || '?'}<br /><span {l.license || "?"}<br /><span
class="underline cursor-pointer" class="underline cursor-pointer"
on:click={() => { on:click={() => {
modal_open = true; modal_open = true;
currentlicense = l.name + '@' + l.version; currentlicense = l.name + "@" + l.version;
licensetext = l.licensetext || $_('no-license-text-could-be-found'); licensetext =
}}>{$_('read-license')}</span> l.licensetext || $_("no-license-text-could-be-found");
}}>{$_("read-license")}</span
>
</td> </td>
<td> <td>
{(l.repo?.url || l.repo) {(l.repo?.url || l.repo)
.replace('git+', '') .replace("git+", "")
.replace('git://', '')} .replace("git://", "")}
</td> </td>
<td>{l.version || '?'}</td> <td>{l.version || "?"}</td>
<td>{l.author?.name || l.author || '?'}</td> <td>{l.author?.name || l.author || "?"}</td>
</tr> </tr>
{/each} {/each}
</tbody> </tbody>
</table> </table>
{:catch error} {:catch error}
<div <div
class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500"> class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500"
>
<span class="inline-block align-middle mr-8"> <span class="inline-block align-middle mr-8">
<b class="capitalize">{$_('general_promise_error')}</b> <b class="capitalize">{$_("general_promise_error")}</b>
{error} {error}
</span> </span>
</div> </div>
{/await} {/await}
</div> </div>
<div class="w-full leading-8 mt-8"> <div class="w-full leading-8 mt-8">
<p class="text-xl font-medium">{$_('icon-image-credits')}</p> <p class="text-xl font-medium">{$_("icon-image-credits")}</p>
<ul class="list-disc"> <ul class="list-disc">
<li> <li>
<a <a
class="underline" class="underline"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
href="https://storyset.com">https://storyset.com</a> href="https://storyset.com">https://storyset.com</a
>
</li> </li>
<li> <li>
<a <a
class="underline" class="underline"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
href="https://undraw.co">https://undraw.co</a> href="https://undraw.co">https://undraw.co</a
>
</li> </li>
<li> <li>
<a <a
class="underline" class="underline"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
href="https://remixicon.com">https://remixicon.com</a> href="https://remixicon.com">https://remixicon.com</a
>
</li> </li>
</ul> </ul>
</div> </div>

View File

@ -20,27 +20,32 @@
class="underline" class="underline"
href="https://odit.services" href="https://odit.services"
rel="noopener,noreferrer" rel="noopener,noreferrer"
target="_blank">ODIT.Services</a> target="_blank">ODIT.Services</a
>
</p> </p>
<p class="text-sm text-gray-500 mt-4"> <p class="text-sm text-gray-500 mt-4">
<a <a
class="underline" class="underline"
target="_blank" target="_blank"
rel="noopener, noreferrer" rel="noopener, noreferrer"
href="https://git.odit.services/lfk/frontend/">LfK!Frontend</a>@<a href="https://git.odit.services/lfk/frontend/">LfK!Frontend</a
>@<a
class="underline" class="underline"
target="_blank" target="_blank"
rel="noopener, noreferrer" rel="noopener, noreferrer"
href="https://git.odit.services/lfk/frontend/src/tag/{releaseinfo}">{releaseinfo}</a> href="https://git.odit.services/lfk/frontend/src/tag/{releaseinfo}"
>{releaseinfo}</a
>
- -
<a <a
rel="noopener, noreferrer" rel="noopener, noreferrer"
class="underline" class="underline"
href="https://docs.lauf-fuer-kaya.de" href="https://docs.lauf-fuer-kaya.de"
target="_blank">{$_('documentation')}</a> target="_blank">{$_("documentation")}</a
>
- -
<a class="underline" href="/privacy">{$_('privacy')}</a> <a class="underline" href="/privacy">{$_("privacy")}</a>
- -
<a class="underline" href="/imprint">{$_('imprint')}</a> <a class="underline" href="/imprint">{$_("imprint")}</a>
</p> </p>
</footer> </footer>

View File

@ -1,20 +1,20 @@
<script> <script>
import { _, getLocaleFromNavigator } from "svelte-i18n"; import { _, getLocaleFromNavigator } from "svelte-i18n";
import marked from "marked"; import { parse } from "marked";
import Footer from "./Footer.svelte"; import Footer from "./Footer.svelte";
import * as css from "../base/simple.css"; // import * as css from "../base/simple.css";
let html = ""; let html = "";
async function load() { async function load() {
let md = await fetch("/imprint_" + getLocaleFromNavigator() + ".md"); let md = await fetch("/imprint_" + getLocaleFromNavigator() + ".md");
let text = (await md.text()).toString(); let text = (await md.text()).toString();
if(text.includes("<meta")){ if (text.includes("<meta")) {
md.ok=false md.ok = false;
} }
if (!md.ok) { if (!md.ok) {
md = await fetch("/imprint_en.md"); md = await fetch("/imprint_en.md");
text = await md.text(); text = await md.text();
} }
html = marked(text); html = parse(text);
} }
const promise = load(); const promise = load();
</script> </script>
@ -22,8 +22,9 @@
<div class="pt-12 px-4 sm:px-6 lg:px-8 lg:pt-20 bg-gray-900 pb-12"> <div class="pt-12 px-4 sm:px-6 lg:px-8 lg:pt-20 bg-gray-900 pb-12">
<div class="text-center mb-8"> <div class="text-center mb-8">
<h1 <h1
class="mt-9 font-display text-4xl leading-none font-semibold text-white sm:text-5xl lg:text-6xl"> class="mt-9 font-display text-4xl leading-none font-semibold text-white sm:text-5xl lg:text-6xl"
{$_('imprint')} >
{$_("imprint")}
</h1> </h1>
</div> </div>
</div> </div>
@ -31,16 +32,17 @@
<div class="pt-0 pb-16 overflow-hidden lg:pt-12 lg:py-24"> <div class="pt-0 pb-16 overflow-hidden lg:pt-12 lg:py-24">
<div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8"> <div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
{#await promise} {#await promise}
<p class="text-center w-full">{$_('imprint-loading')}</p> <p class="text-center w-full">{$_("imprint-loading")}</p>
{:then} {:then}
<div class="simplecontent"> <div class="simplecontent">
{@html html} {@html html}
</div> </div>
{:catch error} {:catch error}
<div <div
class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500"> class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500"
>
<span class="inline-block align-middle mr-8"> <span class="inline-block align-middle mr-8">
<b class="capitalize">{$_('general_promise_error')}</b> <b class="capitalize">{$_("general_promise_error")}</b>
{error} {error}
</span> </span>
</div> </div>

View File

@ -4,19 +4,22 @@
<body class="antialiased font-sans"> <body class="antialiased font-sans">
<div class="flex min-h-screen"> <div class="flex min-h-screen">
<div class="w-full bg-white flex items-center justify-center "> <div class="w-full bg-white flex items-center justify-center">
<div class="max-w-sm m-8"> <div class="max-w-sm m-8">
<div class="text-black text-5xl md:text-15xl font-black"> <div class="text-black text-5xl md:text-15xl font-black">
{$_('404title')} {$_("404title")}
</div> </div>
<div class="w-16 h-1 bg-purple-light my-3 md:my-6" /> <div class="w-16 h-1 bg-purple-light my-3 md:my-6" />
<p <p
class="text-grey-darker text-2xl md:text-3xl font-light mb-8 leading-normal"> class="text-grey-darker text-2xl md:text-3xl font-light mb-8 leading-normal"
{$_('404message')} >
{$_("404message")}
</p> </p>
<a <a
href="/" href="/"
class="bg-transparent text-grey-darkest font-bold uppercase tracking-wide py-3 px-6 border-2 border-grey-light hover:border-grey rounded-lg">{$_('goback')}</a> class="bg-transparent text-grey-darkest font-bold uppercase tracking-wide py-3 px-6 border-2 border-grey-light hover:border-grey rounded-lg"
>{$_("goback")}</a
>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,20 +1,20 @@
<script> <script>
import { _, getLocaleFromNavigator } from "svelte-i18n"; import { _, getLocaleFromNavigator } from "svelte-i18n";
import marked from "marked"; import { parse } from "marked";
import Footer from "./Footer.svelte"; import Footer from "./Footer.svelte";
// import * as css from "../base/simple.css?inline"; // import * as css from "../base/simple.css?inline";
let html = ""; let html = "";
async function load() { async function load() {
let md = await fetch("/privacy_" + getLocaleFromNavigator() + ".md"); let md = await fetch("/privacy_" + getLocaleFromNavigator() + ".md");
let text = (await md.text()).toString(); let text = (await md.text()).toString();
if(text.includes("<meta")){ if (text.includes("<meta")) {
md.ok=false md.ok = false;
} }
if (!md.ok) { if (!md.ok) {
md = await fetch("/privacy_en.md"); md = await fetch("/privacy_en.md");
text = await md.text(); text = await md.text();
} }
html = marked(text); html = parse(text);
} }
const promise = load(); const promise = load();
</script> </script>
@ -22,8 +22,9 @@
<div class="pt-12 px-4 sm:px-6 lg:px-8 lg:pt-20 bg-gray-900 pb-12"> <div class="pt-12 px-4 sm:px-6 lg:px-8 lg:pt-20 bg-gray-900 pb-12">
<div class="text-center mb-8"> <div class="text-center mb-8">
<h1 <h1
class="mt-9 font-display text-4xl leading-none font-semibold text-white sm:text-5xl lg:text-6xl"> class="mt-9 font-display text-4xl leading-none font-semibold text-white sm:text-5xl lg:text-6xl"
{$_('privacy')} >
{$_("privacy")}
</h1> </h1>
</div> </div>
</div> </div>
@ -31,16 +32,17 @@
<div class="pt-0 pb-16 overflow-hidden lg:pt-12 lg:py-24"> <div class="pt-0 pb-16 overflow-hidden lg:pt-12 lg:py-24">
<div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8"> <div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
{#await promise} {#await promise}
<p class="text-center w-full">{$_('privacy-loading')}</p> <p class="text-center w-full">{$_("privacy-loading")}</p>
{:then} {:then}
<div class="simplecontent"> <div class="simplecontent">
{@html html} {@html html}
</div> </div>
{:catch error} {:catch error}
<div <div
class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500"> class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500"
>
<span class="inline-block align-middle mr-8"> <span class="inline-block align-middle mr-8">
<b class="capitalize">{$_('general_promise_error')}</b> <b class="capitalize">{$_("general_promise_error")}</b>
{error} {error}
</span> </span>
</div> </div>

View File

@ -4,26 +4,33 @@
<div class="md:flex flex-col md:flex-row h-screen w-full"> <div class="md:flex flex-col md:flex-row h-screen w-full">
<div <div
class="flex flex-col w-full md:w-64 text-gray-700 bg-white dark-mode:text-gray-200 dark-mode:bg-gray-800 flex-shrink-0"> class="flex flex-col w-full md:w-64 text-gray-700 bg-white dark-mode:text-gray-200 dark-mode:bg-gray-800 flex-shrink-0"
>
<div <div
class="flex-shrink-0 px-8 py-4 flex flex-row items-center justify-between"> class="flex-shrink-0 px-8 py-4 flex flex-row items-center justify-between"
>
<a <a
href="/#/test" href="/#/test"
class="text-lg font-semibold tracking-widest text-gray-900 uppercase rounded-lg dark-mode:text-white focus:outline-none focus:shadow-outline">Sidebar</a> class="text-lg font-semibold tracking-widest text-gray-900 uppercase rounded-lg dark-mode:text-white focus:outline-none focus:shadow-outline"
>Sidebar</a
>
<button <button
class="rounded-lg md:hidden focus:outline-none focus:shadow-outline"> class="rounded-lg md:hidden focus:outline-none focus:shadow-outline"
>
<svg fill="currentColor" viewBox="0 0 20 20" class="w-6 h-6"> <svg fill="currentColor" viewBox="0 0 20 20" class="w-6 h-6">
{#if open} {#if open}
<path <path
fill-rule="evenodd" fill-rule="evenodd"
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
clip-rule="evenodd" /> clip-rule="evenodd"
/>
{/if} {/if}
{#if !open} {#if !open}
<path <path
fill-rule="evenodd" fill-rule="evenodd"
d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM9 15a1 1 0 011-1h6a1 1 0 110 2h-6a1 1 0 01-1-1z" d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM9 15a1 1 0 011-1h6a1 1 0 110 2h-6a1 1 0 01-1-1z"
clip-rule="evenodd" /> clip-rule="evenodd"
/>
{/if} {/if}
</svg> </svg>
</button> </button>
@ -31,49 +38,63 @@
<nav <nav
:class:block={open} :class:block={open}
:class:hidden={!open} :class:hidden={!open}
class="flex-grow md:block px-4 pb-4 md:pb-0 md:overflow-y-auto"> class="flex-grow md:block px-4 pb-4 md:pb-0 md:overflow-y-auto"
>
<a <a
class="block px-4 py-2 mt-2 text-sm font-semibold text-gray-900 bg-gray-200 rounded-lg dark-mode:bg-gray-700 dark-mode:hover:bg-gray-600 dark-mode:focus:bg-gray-600 dark-mode:focus:text-white dark-mode:hover:text-white dark-mode:text-gray-200 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:bg-gray-200 focus:outline-none focus:shadow-outline" class="block px-4 py-2 mt-2 text-sm font-semibold text-gray-900 bg-gray-200 rounded-lg dark-mode:bg-gray-700 dark-mode:hover:bg-gray-600 dark-mode:focus:bg-gray-600 dark-mode:focus:text-white dark-mode:hover:text-white dark-mode:text-gray-200 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:bg-gray-200 focus:outline-none focus:shadow-outline"
href="#">Blog</a> href="#">Blog</a
>
<a <a
class="block px-4 py-2 mt-2 text-sm font-semibold text-gray-900 bg-transparent rounded-lg dark-mode:bg-transparent dark-mode:hover:bg-gray-600 dark-mode:focus:bg-gray-600 dark-mode:focus:text-white dark-mode:hover:text-white dark-mode:text-gray-200 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:bg-gray-200 focus:outline-none focus:shadow-outline" class="block px-4 py-2 mt-2 text-sm font-semibold text-gray-900 bg-transparent rounded-lg dark-mode:bg-transparent dark-mode:hover:bg-gray-600 dark-mode:focus:bg-gray-600 dark-mode:focus:text-white dark-mode:hover:text-white dark-mode:text-gray-200 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:bg-gray-200 focus:outline-none focus:shadow-outline"
href="#">Portfolio</a> href="#">Portfolio</a
>
<a <a
class="block px-4 py-2 mt-2 text-sm font-semibold text-gray-900 bg-transparent rounded-lg dark-mode:bg-transparent dark-mode:hover:bg-gray-600 dark-mode:focus:bg-gray-600 dark-mode:focus:text-white dark-mode:hover:text-white dark-mode:text-gray-200 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:bg-gray-200 focus:outline-none focus:shadow-outline" class="block px-4 py-2 mt-2 text-sm font-semibold text-gray-900 bg-transparent rounded-lg dark-mode:bg-transparent dark-mode:hover:bg-gray-600 dark-mode:focus:bg-gray-600 dark-mode:focus:text-white dark-mode:hover:text-white dark-mode:text-gray-200 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:bg-gray-200 focus:outline-none focus:shadow-outline"
href="#">About</a> href="#">About</a
>
<a <a
class="block px-4 py-2 mt-2 text-sm font-semibold text-gray-900 bg-transparent rounded-lg dark-mode:bg-transparent dark-mode:hover:bg-gray-600 dark-mode:focus:bg-gray-600 dark-mode:focus:text-white dark-mode:hover:text-white dark-mode:text-gray-200 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:bg-gray-200 focus:outline-none focus:shadow-outline" class="block px-4 py-2 mt-2 text-sm font-semibold text-gray-900 bg-transparent rounded-lg dark-mode:bg-transparent dark-mode:hover:bg-gray-600 dark-mode:focus:bg-gray-600 dark-mode:focus:text-white dark-mode:hover:text-white dark-mode:text-gray-200 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:bg-gray-200 focus:outline-none focus:shadow-outline"
href="#">Contact</a> href="#">Contact</a
>
<div class="relative"> <div class="relative">
<button <button
on:click={() => { on:click={() => {
open = !open; open = !open;
}} }}
class="flex flex-row items-center w-full px-4 py-2 mt-2 text-sm font-semibold text-left bg-transparent rounded-lg dark-mode:bg-transparent dark-mode:focus:text-white dark-mode:hover:text-white dark-mode:focus:bg-gray-600 dark-mode:hover:bg-gray-600 md:block hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:bg-gray-200 focus:outline-none focus:shadow-outline"> class="flex flex-row items-center w-full px-4 py-2 mt-2 text-sm font-semibold text-left bg-transparent rounded-lg dark-mode:bg-transparent dark-mode:focus:text-white dark-mode:hover:text-white dark-mode:focus:bg-gray-600 dark-mode:hover:bg-gray-600 md:block hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:bg-gray-200 focus:outline-none focus:shadow-outline"
>
<span>Dropdown</span> <span>Dropdown</span>
<svg <svg
fill="currentColor" fill="currentColor"
viewBox="0 0 20 20" viewBox="0 0 20 20"
class="inline w-4 h-4 mt-1 ml-1 transition-transform duration-200 transform md:-mt-1"><path class="inline w-4 h-4 mt-1 ml-1 transition-transform duration-200 transform md:-mt-1"
><path
fill-rule="evenodd" fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd" /></svg> clip-rule="evenodd"
/></svg
>
</button> </button>
<div <div
class:block={open} class:block={open}
class:hidden={!open} class:hidden={!open}
class="absolute right-0 w-full mt-2 origin-top-right rounded-md shadow-lg"> class="absolute right-0 w-full mt-2 origin-top-right rounded-md shadow-lg"
>
<div <div
class="px-2 py-2 bg-white rounded-md shadow dark-mode:bg-gray-800"> class="px-2 py-2 bg-white rounded-md shadow dark-mode:bg-gray-800"
>
<a <a
class="block px-4 py-2 mt-2 text-sm font-semibold bg-transparent rounded-lg dark-mode:bg-transparent dark-mode:hover:bg-gray-600 dark-mode:focus:bg-gray-600 dark-mode:focus:text-white dark-mode:hover:text-white dark-mode:text-gray-200 md:mt-0 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:bg-gray-200 focus:outline-none focus:shadow-outline" class="block px-4 py-2 mt-2 text-sm font-semibold bg-transparent rounded-lg dark-mode:bg-transparent dark-mode:hover:bg-gray-600 dark-mode:focus:bg-gray-600 dark-mode:focus:text-white dark-mode:hover:text-white dark-mode:text-gray-200 md:mt-0 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:bg-gray-200 focus:outline-none focus:shadow-outline"
href="#">Link #1</a> href="#">Link #1</a
>
<a <a
class="block px-4 py-2 mt-2 text-sm font-semibold bg-transparent rounded-lg dark-mode:bg-transparent dark-mode:hover:bg-gray-600 dark-mode:focus:bg-gray-600 dark-mode:focus:text-white dark-mode:hover:text-white dark-mode:text-gray-200 md:mt-0 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:bg-gray-200 focus:outline-none focus:shadow-outline" class="block px-4 py-2 mt-2 text-sm font-semibold bg-transparent rounded-lg dark-mode:bg-transparent dark-mode:hover:bg-gray-600 dark-mode:focus:bg-gray-600 dark-mode:focus:text-white dark-mode:hover:text-white dark-mode:text-gray-200 md:mt-0 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:bg-gray-200 focus:outline-none focus:shadow-outline"
href="#">Link #2</a> href="#">Link #2</a
>
<a <a
class="block px-4 py-2 mt-2 text-sm font-semibold bg-transparent rounded-lg dark-mode:bg-transparent dark-mode:hover:bg-gray-600 dark-mode:focus:bg-gray-600 dark-mode:focus:text-white dark-mode:hover:text-white dark-mode:text-gray-200 md:mt-0 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:bg-gray-200 focus:outline-none focus:shadow-outline" class="block px-4 py-2 mt-2 text-sm font-semibold bg-transparent rounded-lg dark-mode:bg-transparent dark-mode:hover:bg-gray-600 dark-mode:focus:bg-gray-600 dark-mode:focus:text-white dark-mode:hover:text-white dark-mode:text-gray-200 md:mt-0 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:bg-gray-200 focus:outline-none focus:shadow-outline"
href="#">Link #3</a> href="#">Link #3</a
>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,9 +1,8 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import { clickOutside } from "../base/outsideclick"; import { clickOutside } from "../base/outsideclick";
import Toastify from "toastify-js";
import { UserGroupService } from "@odit/lfk-client-js"; import { UserGroupService } from "@odit/lfk-client-js";
import toast from "svelte-french-toast";
export let modal_open; export let modal_open;
export let current_groups; export let current_groups;
let description_input_value; let description_input_value;
@ -32,10 +31,7 @@
function submit() { function submit() {
if (processed_last_submit === true) { if (processed_last_submit === true) {
processed_last_submit = false; processed_last_submit = false;
const toast = Toastify({ toast.loading($_("group-is-being-added"));
text: $_('group-is-being-added'),
duration: -1,
}).showToast();
let postdata = { let postdata = {
name: name_input_value, name: name_input_value,
description: description_input_value, description: description_input_value,
@ -46,11 +42,8 @@
description_input_value = ""; description_input_value = "";
modal_open = false; modal_open = false;
// //
Toastify({ toast.dismiss();
text: $_('group-added'), toast.success($_("group-added"));
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
current_groups.push(result); current_groups.push(result);
current_groups = current_groups; current_groups = current_groups;
}) })
@ -59,8 +52,6 @@
}) })
.finally(() => { .finally(() => {
processed_last_submit = true; processed_last_submit = true;
//
toast.hideToast();
}); });
} }
} }
@ -69,83 +60,100 @@
{#if modal_open} {#if modal_open}
<div <div
class="fixed z-10 inset-0 overflow-y-auto" class="fixed z-10 inset-0 overflow-y-auto"
use:clickOutside use:clickOutside
on:click_outside={() => { on:click_outside={() => {
modal_open = false; modal_open = false;
}}> }}
>
<div <div
class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> 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="fixed inset-0 transition-opacity" aria-hidden="true">
<div <div
class="absolute inset-0 bg-gray-500 opacity-75" class="absolute inset-0 bg-gray-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 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" 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" 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"> <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="sm:flex sm:items-start">
<div <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"> 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 <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 640 512" viewBox="0 0 640 512"
class="h-6 w-6 text-blue-600" class="h-6 w-6 text-blue-600"
fill="currentColor" fill="currentColor"
width="24" width="24"
height="24"><path fill="none" d="M0 0h24v24H0z" /> height="24"
><path fill="none" d="M0 0h24v24H0z" />
<path <path
d="M610.5 341.3c2.6-14.1 2.6-28.5 0-42.6l25.8-14.9c3-1.7 4.3-5.2 3.3-8.5-6.7-21.6-18.2-41.2-33.2-57.4-2.3-2.5-6-3.1-9-1.4l-25.8 14.9c-10.9-9.3-23.4-16.5-36.9-21.3v-29.8c0-3.4-2.4-6.4-5.7-7.1-22.3-5-45-4.8-66.2 0-3.3.7-5.7 3.7-5.7 7.1v29.8c-13.5 4.8-26 12-36.9 21.3l-25.8-14.9c-2.9-1.7-6.7-1.1-9 1.4-15 16.2-26.5 35.8-33.2 57.4-1 3.3.4 6.8 3.3 8.5l25.8 14.9c-2.6 14.1-2.6 28.5 0 42.6l-25.8 14.9c-3 1.7-4.3 5.2-3.3 8.5 6.7 21.6 18.2 41.1 33.2 57.4 2.3 2.5 6 3.1 9 1.4l25.8-14.9c10.9 9.3 23.4 16.5 36.9 21.3v29.8c0 3.4 2.4 6.4 5.7 7.1 22.3 5 45 4.8 66.2 0 3.3-.7 5.7-3.7 5.7-7.1v-29.8c13.5-4.8 26-12 36.9-21.3l25.8 14.9c2.9 1.7 6.7 1.1 9-1.4 15-16.2 26.5-35.8 33.2-57.4 1-3.3-.4-6.8-3.3-8.5l-25.8-14.9zM496 368.5c-26.8 0-48.5-21.8-48.5-48.5s21.8-48.5 48.5-48.5 48.5 21.8 48.5 48.5-21.7 48.5-48.5 48.5zM96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm224 32c1.9 0 3.7-.5 5.6-.6 8.3-21.7 20.5-42.1 36.3-59.2 7.4-8 17.9-12.6 28.9-12.6 6.9 0 13.7 1.8 19.6 5.3l7.9 4.6c.8-.5 1.6-.9 2.4-1.4 7-14.6 11.2-30.8 11.2-48 0-61.9-50.1-112-112-112S208 82.1 208 144c0 61.9 50.1 112 112 112zm105.2 194.5c-2.3-1.2-4.6-2.6-6.8-3.9-8.2 4.8-15.3 9.8-27.5 9.8-10.9 0-21.4-4.6-28.9-12.6-18.3-19.8-32.3-43.9-40.2-69.6-10.7-34.5 24.9-49.7 25.8-50.3-.1-2.6-.1-5.2 0-7.8l-7.9-4.6c-3.8-2.2-7-5-9.8-8.1-3.3.2-6.5.6-9.8.6-24.6 0-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h255.4c-3.7-6-6.2-12.8-6.2-20.3v-9.2zM173.1 274.6C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z" /></svg> d="M610.5 341.3c2.6-14.1 2.6-28.5 0-42.6l25.8-14.9c3-1.7 4.3-5.2 3.3-8.5-6.7-21.6-18.2-41.2-33.2-57.4-2.3-2.5-6-3.1-9-1.4l-25.8 14.9c-10.9-9.3-23.4-16.5-36.9-21.3v-29.8c0-3.4-2.4-6.4-5.7-7.1-22.3-5-45-4.8-66.2 0-3.3.7-5.7 3.7-5.7 7.1v29.8c-13.5 4.8-26 12-36.9 21.3l-25.8-14.9c-2.9-1.7-6.7-1.1-9 1.4-15 16.2-26.5 35.8-33.2 57.4-1 3.3.4 6.8 3.3 8.5l25.8 14.9c-2.6 14.1-2.6 28.5 0 42.6l-25.8 14.9c-3 1.7-4.3 5.2-3.3 8.5 6.7 21.6 18.2 41.1 33.2 57.4 2.3 2.5 6 3.1 9 1.4l25.8-14.9c10.9 9.3 23.4 16.5 36.9 21.3v29.8c0 3.4 2.4 6.4 5.7 7.1 22.3 5 45 4.8 66.2 0 3.3-.7 5.7-3.7 5.7-7.1v-29.8c13.5-4.8 26-12 36.9-21.3l25.8 14.9c2.9 1.7 6.7 1.1 9-1.4 15-16.2 26.5-35.8 33.2-57.4 1-3.3-.4-6.8-3.3-8.5l-25.8-14.9zM496 368.5c-26.8 0-48.5-21.8-48.5-48.5s21.8-48.5 48.5-48.5 48.5 21.8 48.5 48.5-21.7 48.5-48.5 48.5zM96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm224 32c1.9 0 3.7-.5 5.6-.6 8.3-21.7 20.5-42.1 36.3-59.2 7.4-8 17.9-12.6 28.9-12.6 6.9 0 13.7 1.8 19.6 5.3l7.9 4.6c.8-.5 1.6-.9 2.4-1.4 7-14.6 11.2-30.8 11.2-48 0-61.9-50.1-112-112-112S208 82.1 208 144c0 61.9 50.1 112 112 112zm105.2 194.5c-2.3-1.2-4.6-2.6-6.8-3.9-8.2 4.8-15.3 9.8-27.5 9.8-10.9 0-21.4-4.6-28.9-12.6-18.3-19.8-32.3-43.9-40.2-69.6-10.7-34.5 24.9-49.7 25.8-50.3-.1-2.6-.1-5.2 0-7.8l-7.9-4.6c-3.8-2.2-7-5-9.8-8.1-3.3.2-6.5.6-9.8.6-24.6 0-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h255.4c-3.7-6-6.2-12.8-6.2-20.3v-9.2zM173.1 274.6C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z"
/></svg
>
</div> </div>
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"> <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"> <h3 class="text-lg leading-6 font-medium text-gray-900">
{$_('create-a-new-user-group')} {$_("create-a-new-user-group")}
</h3> </h3>
<div class="mt-2 mb-6"> <div class="mt-2 mb-6">
<p class="text-sm text-gray-500"> <p class="text-sm text-gray-500">
{$_('please-provide-the-required-information-for-creating-a-new-user-group')} {$_(
"please-provide-the-required-information-for-creating-a-new-user-group"
)}
</p> </p>
</div> </div>
<div class="grid grid-cols-6 gap-6"> <div class="grid grid-cols-6 gap-6">
<div class="col-span-6"> <div class="col-span-6">
<label <label
for="firstname" for="firstname"
class="block text-sm font-medium text-gray-700">{$_('name')}</label> class="block text-sm font-medium text-gray-700"
>{$_("name")}</label
>
<input <input
use:focus use:focus
autocomplete="off" autocomplete="off"
placeholder="{$_('name')}" placeholder={$_("name")}
class:border-red-500={!isNameValid} class:border-red-500={!isNameValid}
class:focus:border-red-500={!isNameValid} class:focus:border-red-500={!isNameValid}
class:focus:ring-red-500={!isNameValid} class:focus:ring-red-500={!isNameValid}
bind:value={name_input_value} bind:value={name_input_value}
type="text" type="text"
name="firstname" name="firstname"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> class="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"
/>
{#if !isNameValid} {#if !isNameValid}
<span <span
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
{$_('name-is-required')} >
{$_("name-is-required")}
</span> </span>
{/if} {/if}
</div> </div>
<div class="col-span-6"> <div class="col-span-6">
<label <label
for="trackname" for="trackname"
class="block text-sm font-medium text-gray-700">{$_('description-optional')}</label> class="block text-sm font-medium text-gray-700"
>{$_("description-optional")}</label
>
<input <input
autocomplete="off" autocomplete="off"
placeholder="{$_('something-about-the-group')}" placeholder={$_("something-about-the-group")}
bind:value={description_input_value} bind:value={description_input_value}
type="text" type="text"
name="trackname" name="trackname"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> class="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>
@ -157,16 +165,18 @@
class:opacity-50={!createbtnenabled} class:opacity-50={!createbtnenabled}
on:click={submit} on:click={submit}
type="button" 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"> 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')} >
{$_("create")}
</button> </button>
<button <button
on:click={() => { on:click={() => {
modal_open = false; modal_open = false;
}} }}
type="button" 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"> 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')} >
{$_("cancel")}
</button> </button>
</div> </div>
</div> </div>

View File

@ -1,220 +1,237 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import store from "../../store"; import store from "../../store";
import { import { UserGroupService } from "@odit/lfk-client-js";
UserGroupService
} from "@odit/lfk-client-js"; import PromiseError from "../base/PromiseError.svelte";
import Toastify from "toastify-js"; let data_loaded = false;
import PromiseError from "../base/PromiseError.svelte"; export let params;
let data_loaded = false; const promise = UserGroupService.userGroupControllerGetOne(params.groupid);
export let params; const colors = [
const promise = UserGroupService.userGroupControllerGetOne(params.groupid); "#f3558e",
const colors = [ "#17b978",
"#f3558e", "#3498db",
"#17b978", "#3f3b3b",
"#3498db", "#775ada",
"#3f3b3b", "#7ed6df_#000000",
"#775ada", "#000000",
"#7ed6df_#000000", "#21e6c1_#000000",
"#000000", "#c0392b",
"#21e6c1_#000000", "#d35400",
"#c0392b", "#7f8c8d",
"#d35400", "#6ab04c",
"#7f8c8d", "#4834d4",
"#6ab04c", "#ff1f5a",
"#4834d4", "#eac100",
"#ff1f5a", ];
"#eac100", let matched_colors = [];
]; $: delete_triggered = false;
let matched_colors = []; $: search_permission = "";
$: delete_triggered = false; $: original_data = {};
$: search_permission = ""; $: editable = {};
$: original_data = {}; $: changes_performed = !(
$: editable = {}; JSON.stringify(original_data) == JSON.stringify(editable)
$: changes_performed = !(JSON.stringify(original_data) == JSON.stringify(editable)); );
$: isGroupnameValid = editable.name !== ""; $: isGroupnameValid = editable.name !== "";
$: save_enabled = $: save_enabled = changes_performed && isGroupnameValid;
changes_performed && isGroupnameValid promise.then((data) => {
promise.then((data) => { let current_target = "";
let current_target = ""; let colorindex = -1;
let colorindex = -1; data.permissions = data.permissions.sort();
data.permissions = data.permissions.sort(); data.permissions.forEach((p) => {
data.permissions.forEach((p) => { const target = p.split(":")[0];
const target = p.split(":")[0]; if (current_target !== p.split(":")[0]) {
if (current_target !== p.split(":")[0]) { colorindex++;
colorindex++; current_target = p.split(":")[0];
current_target = p.split(":")[0]; }
} let background = colors[colorindex];
let background = colors[colorindex]; let foreground = "#fff";
let foreground = "#fff"; if (background.includes("_")) {
if (background.includes("_")) { foreground = background.split("_")[1];
foreground = background.split("_")[1]; background = background.split("_")[0];
background = background.split("_")[0]; }
} matched_colors[target] = [background, foreground];
matched_colors[target] = [background, foreground]; });
}); data_loaded = true;
data_loaded = true; original_data = Object.assign(original_data, data);
original_data = Object.assign(original_data, data); editable = Object.assign(editable, original_data);
editable = Object.assign(editable, original_data); });
}); function submit() {
function submit() { if (data_loaded === true && save_enabled) {
if (data_loaded === true && save_enabled) { toast($_("updateing-group"));
Toastify({ UserGroupService.userGroupControllerPut(original_data.id, editable)
text: $_('updateing-group'), .then((resp) => {
duration: 2500, Object.assign(original_data, editable);
}).showToast(); original_data = editable;
UserGroupService.userGroupControllerPut(original_data.id, editable) Object.assign(original_data, editable);
.then((resp) => { toast.success($_("group-updated"));
Object.assign(original_data, editable); })
original_data = editable; .catch((err) => {});
Object.assign(original_data, editable); } else {
Toastify({ }
text: $_('group-updated'), }
duration: 2500, function deleteGroup() {
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", UserGroupService.userGroupControllerRemove(original_data.id, true)
}).showToast(); .then((resp) => {
}) location.replace("./");
.catch((err) => {}); })
} else { .catch((err) => {});
} }
} </script>
function deleteGroup() {
UserGroupService.userGroupControllerRemove(original_data.id, true) {#await promise}
.then((resp) => { {$_("loading-group-detail")}
location.replace("./"); {:then}
}) <section class="container p-5 select-none">
.catch((err) => {}); <div class="flex flex-row mb-4">
} <div class="w-full">
</script> <nav class="w-full flex">
<ol class="list-none flex flex-row items-center justify-start">
{#await promise} <li class="flex items-center">
{$_('loading-group-detail')} <svg
{:then} class="flex-shrink-0 w-5 h-5 mr-2"
<section class="container p-5 select-none"> fill="currentColor"
<div class="flex flex-row mb-4"> width="24"
<div class="w-full"> height="24"
<nav class="w-full flex"> xmlns="http://www.w3.org/2000/svg"
<ol class="list-none flex flex-row items-center justify-start"> viewBox="0 0 640 512"
<li class="flex items-center"> ><path
<svg class="flex-shrink-0 w-5 h-5 mr-2" fill="currentColor" width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M610.5 341.3c2.6-14.1 2.6-28.5 0-42.6l25.8-14.9c3-1.7 4.3-5.2 3.3-8.5-6.7-21.6-18.2-41.2-33.2-57.4-2.3-2.5-6-3.1-9-1.4l-25.8 14.9c-10.9-9.3-23.4-16.5-36.9-21.3v-29.8c0-3.4-2.4-6.4-5.7-7.1-22.3-5-45-4.8-66.2 0-3.3.7-5.7 3.7-5.7 7.1v29.8c-13.5 4.8-26 12-36.9 21.3l-25.8-14.9c-2.9-1.7-6.7-1.1-9 1.4-15 16.2-26.5 35.8-33.2 57.4-1 3.3.4 6.8 3.3 8.5l25.8 14.9c-2.6 14.1-2.6 28.5 0 42.6l-25.8 14.9c-3 1.7-4.3 5.2-3.3 8.5 6.7 21.6 18.2 41.1 33.2 57.4 2.3 2.5 6 3.1 9 1.4l25.8-14.9c10.9 9.3 23.4 16.5 36.9 21.3v29.8c0 3.4 2.4 6.4 5.7 7.1 22.3 5 45 4.8 66.2 0 3.3-.7 5.7-3.7 5.7-7.1v-29.8c13.5-4.8 26-12 36.9-21.3l25.8 14.9c2.9 1.7 6.7 1.1 9-1.4 15-16.2 26.5-35.8 33.2-57.4 1-3.3-.4-6.8-3.3-8.5l-25.8-14.9zM496 368.5c-26.8 0-48.5-21.8-48.5-48.5s21.8-48.5 48.5-48.5 48.5 21.8 48.5 48.5-21.7 48.5-48.5 48.5zM96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm224 32c1.9 0 3.7-.5 5.6-.6 8.3-21.7 20.5-42.1 36.3-59.2 7.4-8 17.9-12.6 28.9-12.6 6.9 0 13.7 1.8 19.6 5.3l7.9 4.6c.8-.5 1.6-.9 2.4-1.4 7-14.6 11.2-30.8 11.2-48 0-61.9-50.1-112-112-112S208 82.1 208 144c0 61.9 50.1 112 112 112zm105.2 194.5c-2.3-1.2-4.6-2.6-6.8-3.9-8.2 4.8-15.3 9.8-27.5 9.8-10.9 0-21.4-4.6-28.9-12.6-18.3-19.8-32.3-43.9-40.2-69.6-10.7-34.5 24.9-49.7 25.8-50.3-.1-2.6-.1-5.2 0-7.8l-7.9-4.6c-3.8-2.2-7-5-9.8-8.1-3.3.2-6.5.6-9.8.6-24.6 0-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h255.4c-3.7-6-6.2-12.8-6.2-20.3v-9.2zM173.1 274.6C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z"></path></svg> fill="currentColor"
</li> d="M610.5 341.3c2.6-14.1 2.6-28.5 0-42.6l25.8-14.9c3-1.7 4.3-5.2 3.3-8.5-6.7-21.6-18.2-41.2-33.2-57.4-2.3-2.5-6-3.1-9-1.4l-25.8 14.9c-10.9-9.3-23.4-16.5-36.9-21.3v-29.8c0-3.4-2.4-6.4-5.7-7.1-22.3-5-45-4.8-66.2 0-3.3.7-5.7 3.7-5.7 7.1v29.8c-13.5 4.8-26 12-36.9 21.3l-25.8-14.9c-2.9-1.7-6.7-1.1-9 1.4-15 16.2-26.5 35.8-33.2 57.4-1 3.3.4 6.8 3.3 8.5l25.8 14.9c-2.6 14.1-2.6 28.5 0 42.6l-25.8 14.9c-3 1.7-4.3 5.2-3.3 8.5 6.7 21.6 18.2 41.1 33.2 57.4 2.3 2.5 6 3.1 9 1.4l25.8-14.9c10.9 9.3 23.4 16.5 36.9 21.3v29.8c0 3.4 2.4 6.4 5.7 7.1 22.3 5 45 4.8 66.2 0 3.3-.7 5.7-3.7 5.7-7.1v-29.8c13.5-4.8 26-12 36.9-21.3l25.8 14.9c2.9 1.7 6.7 1.1 9-1.4 15-16.2 26.5-35.8 33.2-57.4 1-3.3-.4-6.8-3.3-8.5l-25.8-14.9zM496 368.5c-26.8 0-48.5-21.8-48.5-48.5s21.8-48.5 48.5-48.5 48.5 21.8 48.5 48.5-21.7 48.5-48.5 48.5zM96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm224 32c1.9 0 3.7-.5 5.6-.6 8.3-21.7 20.5-42.1 36.3-59.2 7.4-8 17.9-12.6 28.9-12.6 6.9 0 13.7 1.8 19.6 5.3l7.9 4.6c.8-.5 1.6-.9 2.4-1.4 7-14.6 11.2-30.8 11.2-48 0-61.9-50.1-112-112-112S208 82.1 208 144c0 61.9 50.1 112 112 112zm105.2 194.5c-2.3-1.2-4.6-2.6-6.8-3.9-8.2 4.8-15.3 9.8-27.5 9.8-10.9 0-21.4-4.6-28.9-12.6-18.3-19.8-32.3-43.9-40.2-69.6-10.7-34.5 24.9-49.7 25.8-50.3-.1-2.6-.1-5.2 0-7.8l-7.9-4.6c-3.8-2.2-7-5-9.8-8.1-3.3.2-6.5.6-9.8.6-24.6 0-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h255.4c-3.7-6-6.2-12.8-6.2-20.3v-9.2zM173.1 274.6C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z"
<li class="flex items-center"> /></svg
<a class="mr-2" href="../">{$_('groups')}</a><svg >
stroke="currentColor" </li>
fill="none" <li class="flex items-center">
stroke-width="2" <a class="mr-2" href="../">{$_("groups")}</a><svg
viewBox="0 0 24 24" stroke="currentColor"
stroke-linecap="round" fill="none"
stroke-linejoin="round" stroke-width="2"
class="h-3 w-3 mr-2 stroke-current" viewBox="0 0 24 24"
height="1em" stroke-linecap="round"
width="1em" stroke-linejoin="round"
xmlns="http://www.w3.org/2000/svg"><line class="h-3 w-3 mr-2 stroke-current"
x1="5" height="1em"
y1="12" width="1em"
x2="19" xmlns="http://www.w3.org/2000/svg"
y2="12" /> ><line x1="5" y1="12" x2="19" y2="12" />
<polyline points="12 5 19 12 12 19" /></svg> <polyline points="12 5 19 12 12 19" /></svg
</li> >
<li class="flex items-center"> </li>
<span class="mr-2">{editable.name}</span> <li class="flex items-center">
</li> <span class="mr-2">{editable.name}</span>
</ol> </li>
</nav> </ol>
</div> </nav>
</div> </div>
<div class="mb-8 text-3xl font-extrabold leading-tight"> </div>
{original_data.name} <div class="mb-8 text-3xl font-extrabold leading-tight">
<span data-id="group_actions_${editable.id}"> {original_data.name}
{#if store.state.jwtinfo.userdetails.permissions.includes('USERGROUP:DELETE')} <span data-id="group_actions_${editable.id}">
{#if delete_triggered} {#if store.state.jwtinfo.userdetails.permissions.includes("USERGROUP:DELETE")}
<button {#if delete_triggered}
on:click={deleteGroup} <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">{$_('confirm-deletion')}</button> on:click={deleteGroup}
<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"
on:click={() => { >{$_("confirm-deletion")}</button
delete_triggered = !delete_triggered; >
}} <button
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-400 text-base font-medium text-white sm:w-auto sm:text-sm">{$_('cancel')}</button> on:click={() => {
{/if} delete_triggered = !delete_triggered;
{#if !delete_triggered} }}
<button class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-400 text-base font-medium text-white sm:w-auto sm:text-sm"
on:click={() => { >{$_("cancel")}</button
delete_triggered = true; >
}} {/if}
type="button" {#if !delete_triggered}
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-group')}</button> <button
{/if} on:click={() => {
{/if} delete_triggered = true;
{#if !delete_triggered} }}
<button type="button"
disabled={!save_enabled} 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"
class:opacity-50={!save_enabled} >{$_("delete-group")}</button
type="button" >
on:click={submit} {/if}
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm">{$_('save-changes')}</button> {/if}
{/if} {#if !delete_triggered}
</span> <button
</div> disabled={!save_enabled}
<!-- --> class:opacity-50={!save_enabled}
<div class="text-sm w-full"> type="button"
<label on:click={submit}
for="title" class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm"
class="font-medium text-gray-700">{$_('name')}</label> >{$_("save-changes")}</button
<input >
autocomplete="off" {/if}
placeholder={$_('name')} </span>
type="text" </div>
bind:value={editable.name} <!-- -->
class:border-red-500={!isGroupnameValid} <div class="text-sm w-full">
class:focus:border-red-500={!isGroupnameValid} <label for="title" class="font-medium text-gray-700">{$_("name")}</label>
class:focus:ring-red-500={!isGroupnameValid} <input
name="title" autocomplete="off"
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" /> placeholder={$_("name")}
{#if !isGroupnameValid} type="text"
<span bind:value={editable.name}
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> class:border-red-500={!isGroupnameValid}
{$_('group-name-is-required')} class:focus:border-red-500={!isGroupnameValid}
</span> class:focus:ring-red-500={!isGroupnameValid}
{/if} name="title"
</div> 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 class="text-sm w-full"> />
<label {#if !isGroupnameValid}
for="firstname" <span
class="font-medium text-gray-700">{$_('description')}</label> class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
<input >
autocomplete="off" {$_("group-name-is-required")}
placeholder={$_('description')} </span>
type="text" {/if}
bind:value={editable.description} </div>
name="firstname" <div class="text-sm w-full">
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> <label for="firstname" class="font-medium text-gray-700"
</div> >{$_("description")}</label
<div class="text-sm w-full mt-8"> >
<p class="font-medium mb-4"> <input
{$_('permissions')} autocomplete="off"
<a placeholder={$_("description")}
class="px-4 py-2 bg-gray-500 rounded-md text-white" type="text"
href="/groups/{params.groupid}/permissions/">{$_('edit-permissions')}</a> bind:value={editable.description}
</p> name="firstname"
<div class="w-full sm:my-px sm:px-px sm:w-1/2"> 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"
<input />
autocomplete="off" </div>
placeholder="{$_('search-for-permission')}" <div class="text-sm w-full mt-8">
type="text" <p class="font-medium mb-4">
bind:value={search_permission} {$_("permissions")}
class="mt-4 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 dark:bg-gray-900 dark:text-gray-100 rounded-md p-2" /> <a
</div> class="px-4 py-2 bg-gray-500 rounded-md text-white"
{#each original_data.permissions as p} href="/groups/{params.groupid}/permissions/"
{#if p.toLowerCase().includes(search_permission.toLowerCase())} >{$_("edit-permissions")}</a
<span >
style="background:{matched_colors[p.split(':')[0]][0]};color:{matched_colors[p.split(':')[0]][1]};" </p>
class="mt-1 inline-flex items-center justify-center px-2 py-1 text-xs font-bold leading-none text-indigo-100 rounded">{p}</span> <div class="w-full sm:my-px sm:px-px sm:w-1/2">
<!-- --> <input
{/if} autocomplete="off"
{/each} placeholder={$_("search-for-permission")}
</div> type="text"
</section> bind:value={search_permission}
{:catch error} class="mt-4 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 dark:bg-gray-900 dark:text-gray-100 rounded-md p-2"
<PromiseError {error} /> />
{/await} </div>
{#each original_data.permissions as p}
{#if p.toLowerCase().includes(search_permission.toLowerCase())}
<span
style="background:{matched_colors[
p.split(':')[0]
][0]};color:{matched_colors[p.split(':')[0]][1]};"
class="mt-1 inline-flex items-center justify-center px-2 py-1 text-xs font-bold leading-none text-indigo-100 rounded"
>{p}</span
>
<!-- -->
{/if}
{/each}
</div>
</section>
{:catch error}
<PromiseError {error} />
{/await}

View File

@ -3,9 +3,9 @@
import { import {
PermissionService, PermissionService,
CreatePermission, CreatePermission,
UserGroupService, UserGroupService,
} from "@odit/lfk-client-js"; } from "@odit/lfk-client-js";
import Toastify from "toastify-js";
import PromiseError from "../base/PromiseError.svelte"; import PromiseError from "../base/PromiseError.svelte";
export let params; export let params;
let [ let [
@ -20,15 +20,14 @@ UserGroupService,
$: save_enabled = $: save_enabled =
JSON.stringify(grantedPermissions) === JSON.stringify(grantedPermissions) ===
JSON.stringify(grantedPermissions_initial); JSON.stringify(grantedPermissions_initial);
const group_promise = UserGroupService.userGroupControllerGetOne(params.groupid); const group_promise = UserGroupService.userGroupControllerGetOne(
params.groupid
);
group_promise.then((data) => { group_promise.then((data) => {
original_data = Object.assign(original_data, data); original_data = Object.assign(original_data, data);
}); });
function submit() { function submit() {
Toastify({ toast.loading($_("updating-permissions"));
text: $_('updating-permissions'),
duration: 2500,
}).showToast();
to_delete.forEach((d) => { to_delete.forEach((d) => {
promises = promises.concat([ promises = promises.concat([
PermissionService.permissionControllerRemove(d, true), PermissionService.permissionControllerRemove(d, true),
@ -50,11 +49,7 @@ UserGroupService,
); );
}); });
grantedPermissions_initial = grantedPermissions; grantedPermissions_initial = grantedPermissions;
Toastify({ toast($_("permissions-updated"));
text: $_("permissions-updated"),
duration: 2500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
}); });
} }
Object.values(CreatePermission.target).forEach((t) => { Object.values(CreatePermission.target).forEach((t) => {
@ -62,13 +57,15 @@ UserGroupService,
allpermissions = allpermissions.concat([{ target: t, action: a }]); allpermissions = allpermissions.concat([{ target: t, action: a }]);
}); });
}); });
UserGroupService.userGroupControllerGetPermissions(params.groupid).then((val) => { UserGroupService.userGroupControllerGetPermissions(params.groupid).then(
val.directlyGranted.forEach((p) => { (val) => {
delete p.responseType; val.directlyGranted.forEach((p) => {
grantedPermissions = grantedPermissions.concat([p]); delete p.responseType;
}); grantedPermissions = grantedPermissions.concat([p]);
grantedPermissions_initial = grantedPermissions; });
}); grantedPermissions_initial = grantedPermissions;
}
);
</script> </script>
{#await group_promise} {#await group_promise}
@ -86,12 +83,15 @@ UserGroupService,
width="24" width="24"
height="24" height="24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 640 512"><path viewBox="0 0 640 512"
><path
fill="currentColor" fill="currentColor"
d="M610.5 341.3c2.6-14.1 2.6-28.5 0-42.6l25.8-14.9c3-1.7 4.3-5.2 3.3-8.5-6.7-21.6-18.2-41.2-33.2-57.4-2.3-2.5-6-3.1-9-1.4l-25.8 14.9c-10.9-9.3-23.4-16.5-36.9-21.3v-29.8c0-3.4-2.4-6.4-5.7-7.1-22.3-5-45-4.8-66.2 0-3.3.7-5.7 3.7-5.7 7.1v29.8c-13.5 4.8-26 12-36.9 21.3l-25.8-14.9c-2.9-1.7-6.7-1.1-9 1.4-15 16.2-26.5 35.8-33.2 57.4-1 3.3.4 6.8 3.3 8.5l25.8 14.9c-2.6 14.1-2.6 28.5 0 42.6l-25.8 14.9c-3 1.7-4.3 5.2-3.3 8.5 6.7 21.6 18.2 41.1 33.2 57.4 2.3 2.5 6 3.1 9 1.4l25.8-14.9c10.9 9.3 23.4 16.5 36.9 21.3v29.8c0 3.4 2.4 6.4 5.7 7.1 22.3 5 45 4.8 66.2 0 3.3-.7 5.7-3.7 5.7-7.1v-29.8c13.5-4.8 26-12 36.9-21.3l25.8 14.9c2.9 1.7 6.7 1.1 9-1.4 15-16.2 26.5-35.8 33.2-57.4 1-3.3-.4-6.8-3.3-8.5l-25.8-14.9zM496 368.5c-26.8 0-48.5-21.8-48.5-48.5s21.8-48.5 48.5-48.5 48.5 21.8 48.5 48.5-21.7 48.5-48.5 48.5zM96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm224 32c1.9 0 3.7-.5 5.6-.6 8.3-21.7 20.5-42.1 36.3-59.2 7.4-8 17.9-12.6 28.9-12.6 6.9 0 13.7 1.8 19.6 5.3l7.9 4.6c.8-.5 1.6-.9 2.4-1.4 7-14.6 11.2-30.8 11.2-48 0-61.9-50.1-112-112-112S208 82.1 208 144c0 61.9 50.1 112 112 112zm105.2 194.5c-2.3-1.2-4.6-2.6-6.8-3.9-8.2 4.8-15.3 9.8-27.5 9.8-10.9 0-21.4-4.6-28.9-12.6-18.3-19.8-32.3-43.9-40.2-69.6-10.7-34.5 24.9-49.7 25.8-50.3-.1-2.6-.1-5.2 0-7.8l-7.9-4.6c-3.8-2.2-7-5-9.8-8.1-3.3.2-6.5.6-9.8.6-24.6 0-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h255.4c-3.7-6-6.2-12.8-6.2-20.3v-9.2zM173.1 274.6C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z" /></svg> d="M610.5 341.3c2.6-14.1 2.6-28.5 0-42.6l25.8-14.9c3-1.7 4.3-5.2 3.3-8.5-6.7-21.6-18.2-41.2-33.2-57.4-2.3-2.5-6-3.1-9-1.4l-25.8 14.9c-10.9-9.3-23.4-16.5-36.9-21.3v-29.8c0-3.4-2.4-6.4-5.7-7.1-22.3-5-45-4.8-66.2 0-3.3.7-5.7 3.7-5.7 7.1v29.8c-13.5 4.8-26 12-36.9 21.3l-25.8-14.9c-2.9-1.7-6.7-1.1-9 1.4-15 16.2-26.5 35.8-33.2 57.4-1 3.3.4 6.8 3.3 8.5l25.8 14.9c-2.6 14.1-2.6 28.5 0 42.6l-25.8 14.9c-3 1.7-4.3 5.2-3.3 8.5 6.7 21.6 18.2 41.1 33.2 57.4 2.3 2.5 6 3.1 9 1.4l25.8-14.9c10.9 9.3 23.4 16.5 36.9 21.3v29.8c0 3.4 2.4 6.4 5.7 7.1 22.3 5 45 4.8 66.2 0 3.3-.7 5.7-3.7 5.7-7.1v-29.8c13.5-4.8 26-12 36.9-21.3l25.8 14.9c2.9 1.7 6.7 1.1 9-1.4 15-16.2 26.5-35.8 33.2-57.4 1-3.3-.4-6.8-3.3-8.5l-25.8-14.9zM496 368.5c-26.8 0-48.5-21.8-48.5-48.5s21.8-48.5 48.5-48.5 48.5 21.8 48.5 48.5-21.7 48.5-48.5 48.5zM96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm224 32c1.9 0 3.7-.5 5.6-.6 8.3-21.7 20.5-42.1 36.3-59.2 7.4-8 17.9-12.6 28.9-12.6 6.9 0 13.7 1.8 19.6 5.3l7.9 4.6c.8-.5 1.6-.9 2.4-1.4 7-14.6 11.2-30.8 11.2-48 0-61.9-50.1-112-112-112S208 82.1 208 144c0 61.9 50.1 112 112 112zm105.2 194.5c-2.3-1.2-4.6-2.6-6.8-3.9-8.2 4.8-15.3 9.8-27.5 9.8-10.9 0-21.4-4.6-28.9-12.6-18.3-19.8-32.3-43.9-40.2-69.6-10.7-34.5 24.9-49.7 25.8-50.3-.1-2.6-.1-5.2 0-7.8l-7.9-4.6c-3.8-2.2-7-5-9.8-8.1-3.3.2-6.5.6-9.8.6-24.6 0-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h255.4c-3.7-6-6.2-12.8-6.2-20.3v-9.2zM173.1 274.6C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z"
/></svg
>
</li> </li>
<li class="flex items-center"> <li class="flex items-center">
<a class="mr-2" href="../../">{$_('user-groups')}</a><svg <a class="mr-2" href="../../">{$_("user-groups")}</a><svg
stroke="currentColor" stroke="currentColor"
fill="none" fill="none"
stroke-width="2" stroke-width="2"
@ -101,12 +101,10 @@ UserGroupService,
class="h-3 w-3 mr-2 stroke-current" class="h-3 w-3 mr-2 stroke-current"
height="1em" height="1em"
width="1em" width="1em"
xmlns="http://www.w3.org/2000/svg"><line xmlns="http://www.w3.org/2000/svg"
x1="5" ><line x1="5" y1="12" x2="19" y2="12" />
y1="12" <polyline points="12 5 19 12 12 19" /></svg
x2="19" >
y2="12" />
<polyline points="12 5 19 12 12 19" /></svg>
</li> </li>
<li class="flex items-center"> <li class="flex items-center">
<span class="mr-2"><a href="../">{original_data.name}</a></span> <span class="mr-2"><a href="../">{original_data.name}</a></span>
@ -122,22 +120,20 @@ UserGroupService,
class="h-3 w-3 mr-2 stroke-current" class="h-3 w-3 mr-2 stroke-current"
height="1em" height="1em"
width="1em" width="1em"
xmlns="http://www.w3.org/2000/svg"><line xmlns="http://www.w3.org/2000/svg"
x1="5" ><line x1="5" y1="12" x2="19" y2="12" />
y1="12" <polyline points="12 5 19 12 12 19" /></svg
x2="19" >
y2="12" />
<polyline points="12 5 19 12 12 19" /></svg>
</li> </li>
<li class="flex items-center"> <li class="flex items-center">
<span class="mr-2">{$_('permissions')}</span> <span class="mr-2">{$_("permissions")}</span>
</li> </li>
</ol> </ol>
</nav> </nav>
</div> </div>
</div> </div>
<div class="mb-8 text-3xl font-extrabold"> <div class="mb-8 text-3xl font-extrabold">
{$_('permissions')}: {$_("permissions")}:
{original_data.name} {original_data.name}
<span> <span>
{#if promises.length === 0} {#if promises.length === 0}
@ -146,21 +142,25 @@ UserGroupService,
class:opacity-50={save_enabled} class:opacity-50={save_enabled}
type="button" type="button"
on:click={submit} on:click={submit}
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm">{$_('save-changes')}</button> class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm"
>{$_("save-changes")}</button
>
{:else} {:else}
<button <button
type="button" type="button"
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-yellow-600 text-base font-medium text-white hover:bg-yellow-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-yellow-500 sm:ml-3 sm:w-auto sm:text-sm">{$_('applying-changes')}</button> class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-yellow-600 text-base font-medium text-white hover:bg-yellow-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-yellow-500 sm:ml-3 sm:w-auto sm:text-sm"
>{$_("applying-changes")}</button
>
{/if} {/if}
</span> </span>
</div> </div>
<!-- --> <!-- -->
<div class="flex flex-wrap -mx-1 overflow-hidden"> <div class="flex flex-wrap -mx-1 overflow-hidden">
<div class="my-1 px-1 w-full overflow-hidden sm:w-1/2"> <div class="my-1 px-1 w-full overflow-hidden sm:w-1/2">
{$_('verfuegbare')} {$_("available-permissions")}
</div> </div>
<div class="my-1 px-1 w-full overflow-hidden sm:w-1/2"> <div class="my-1 px-1 w-full overflow-hidden sm:w-1/2">
{$_('granted')} {$_("granted")}
</div> </div>
</div> </div>
<!-- --> <!-- -->
@ -168,12 +168,14 @@ UserGroupService,
{#if allpermissions.length > 0} {#if allpermissions.length > 0}
<div class="my-1 px-1 w-full overflow-hidden sm:w-1/2"> <div class="my-1 px-1 w-full overflow-hidden sm:w-1/2">
<div <div
class="border-4 border-dashed rounded mb-4 p-5 text-lg text-center"> class="border-4 border-dashed rounded mb-4 p-5 text-lg text-center"
>
{#each allpermissions as p} {#each allpermissions as p}
{#if !(grantedPermissions.filter((o)=>p.target == o.target && p.action == o.action).length > 0)} {#if !(grantedPermissions.filter((o) => p.target == o.target && p.action == o.action).length > 0)}
<p <p
class="block w-full mt-1 text-sm dark:border-gray-600 dark:bg-gray-700 bg-gray-200 p-2 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:text-gray-300 dark:focus:shadow-outline-gray form-input"> class="block w-full mt-1 text-sm dark:border-gray-600 dark:bg-gray-700 bg-gray-200 p-2 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:text-gray-300 dark:focus:shadow-outline-gray form-input"
{p.target + ':' + p.action} >
{p.target + ":" + p.action}
<button <button
on:click={() => { on:click={() => {
grantedPermissions = grantedPermissions.concat([p]); grantedPermissions = grantedPermissions.concat([p]);
@ -190,7 +192,9 @@ UserGroupService,
} }
}} }}
type="button" type="button"
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-green-200 text-base font-medium text-black hover:bg-green-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 sm:ml-3 sm:w-auto sm:text-sm">+</button> class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-green-200 text-base font-medium text-black hover:bg-green-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 sm:ml-3 sm:w-auto sm:text-sm"
>+</button
>
</p> </p>
{/if} {/if}
{/each} {/each}
@ -198,22 +202,39 @@ UserGroupService,
</div> </div>
<div class="my-1 px-1 w-full overflow-hidden sm:w-1/2"> <div class="my-1 px-1 w-full overflow-hidden sm:w-1/2">
<div <div
class="border-4 border-dashed rounded mb-4 p-5 text-lg text-center"> class="border-4 border-dashed rounded mb-4 p-5 text-lg text-center"
>
{#each grantedPermissions as p} {#each grantedPermissions as p}
<p <p
class="block w-full mt-1 text-sm dark:border-gray-600 dark:bg-gray-700 bg-gray-200 p-2 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:text-gray-300 dark:focus:shadow-outline-gray form-input"> class="block w-full mt-1 text-sm dark:border-gray-600 dark:bg-gray-700 bg-gray-200 p-2 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:text-gray-300 dark:focus:shadow-outline-gray form-input"
{p.target + ':' + p.action} >
{p.target + ":" + p.action}
<button <button
on:click={() => { on:click={() => {
grantedPermissions = grantedPermissions.filter((o) => o.target + ':' + o.action !== p.target + ':' + p.action); grantedPermissions = grantedPermissions.filter(
if (to_add.some((o) => o.target + ':' + o.action === p.target + ':' + p.action)) { (o) =>
to_add = to_add.filter((o) => o.target + ':' + o.action !== p.target + ':' + p.action); o.target + ":" + o.action !== p.target + ":" + p.action
);
if (
to_add.some(
(o) =>
o.target + ":" + o.action ===
p.target + ":" + p.action
)
) {
to_add = to_add.filter(
(o) =>
o.target + ":" + o.action !==
p.target + ":" + p.action
);
} else { } else {
to_delete = to_delete.concat([p.id]); to_delete = to_delete.concat([p.id]);
} }
}} }}
type="button" type="button"
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-300 text-base font-medium text-black hover:bg-red-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 sm:ml-3 sm:w-auto sm:text-sm">-</button> class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-300 text-base font-medium text-black hover:bg-red-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 sm:ml-3 sm:w-auto sm:text-sm"
>-</button
>
</p> </p>
{/each} {/each}
</div> </div>

View File

@ -9,21 +9,22 @@
<section class="container p-5"> <section class="container p-5">
<span class="mb-1 text-3xl font-extrabold leading-tight"> <span class="mb-1 text-3xl font-extrabold leading-tight">
{$_('user-groups')} {$_("user-groups")}
{#if store.state.jwtinfo.userdetails.permissions.includes('USERGROUP:CREATE')} {#if store.state.jwtinfo.userdetails.permissions.includes("USERGROUP:CREATE")}
<button <button
on:click={() => { on:click={() => {
modal_open = true; modal_open = true;
}} }}
type="button" 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"> 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"
{$_('add-user-group')} >
{$_("add-user-group")}
</button> </button>
{/if} {/if}
</span> </span>
<UserGroupsOverview bind:current_groups /> <UserGroupsOverview bind:current_groups />
</section> </section>
{#if store.state.jwtinfo.userdetails.permissions.includes('USERGROUP:CREATE')} {#if store.state.jwtinfo.userdetails.permissions.includes("USERGROUP:CREATE")}
<AddGroupModal bind:current_groups bind:modal_open /> <AddGroupModal bind:current_groups bind:modal_open />
{/if} {/if}

View File

@ -6,7 +6,7 @@
<div class="text-center items-center justify-center"> <div class="text-center items-center justify-center">
<p class="mb-16 text-lg text-gray-500"> <p class="mb-16 text-lg text-gray-500">
<img class="w-full h-44" src={groups_empty} alt="" /> <img class="w-full h-44" src={groups_empty} alt="" />
<span class="font-bold">{$_('there-are-no-groups-yet')}.</span><br /> <span class="font-bold">{$_("there-are-no-groups-yet")}.</span><br />
<span>{$_('add-your-first-group')}</span> <span>{$_("add-your-first-group")}</span>
</p> </p>
</div> </div>

View File

@ -13,13 +13,14 @@
); );
</script> </script>
{#if store.state.jwtinfo.userdetails.permissions.includes('USERGROUP:GET')} {#if store.state.jwtinfo.userdetails.permissions.includes("USERGROUP:GET")}
{#await groups_promise} {#await groups_promise}
<div <div
class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2" class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2"
role="alert"> role="alert"
<p class="font-bold">{$_('groups-are-being-loaded')}</p> >
<p class="text-sm">{$_('this-might-take-a-moment')}</p> <p class="font-bold">{$_("groups-are-being-loaded")}</p>
<p class="text-sm">{$_("this-might-take-a-moment")}</p>
</div> </div>
{:then} {:then}
{#if current_groups.length === 0} {#if current_groups.length === 0}
@ -28,26 +29,30 @@
<input <input
type="search" type="search"
bind:value={searchvalue} bind:value={searchvalue}
placeholder={$_('datatable.search')} placeholder={$_("datatable.search")}
aria-label={$_('datatable.search')} aria-label={$_("datatable.search")}
class="mb-4" /> class="mb-4"
/>
<div <div
class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll"> class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll"
>
<table class="divide-y divide-gray-200 w-full"> <table class="divide-y divide-gray-200 w-full">
<thead class="bg-gray-50"> <thead class="bg-gray-50">
<tr> <tr>
<th <th
scope="col" scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
{$_('name')} >
{$_("name")}
</th> </th>
<th <th
scope="col" scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
{$_('description')} >
{$_("description")}
</th> </th>
<th scope="col" class="relative px-6 py-3"> <th scope="col" class="relative px-6 py-3">
<span class="sr-only">{$_('action')}</span> <span class="sr-only">{$_("action")}</span>
</th> </th>
</tr> </tr>
</thead> </thead>
@ -72,39 +77,53 @@
</td> </td>
{#if active_deletes[group.id] === true} {#if active_deletes[group.id] === true}
<td <td
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"
>
<button <button
on:click={() => { on:click={() => {
active_deletes[group.id] = false; active_deletes[group.id] = false;
}} }}
tabindex="0" tabindex="0"
class="ml-4 text-indigo-600 hover:text-indigo-900 cursor-pointer">{$_('cancel-delete')}</button> class="ml-4 text-indigo-600 hover:text-indigo-900 cursor-pointer"
>{$_("cancel-delete")}</button
>
<button <button
on:click={() => { on:click={() => {
UserGroupService.userGroupControllerRemove(group.id, true) UserGroupService.userGroupControllerRemove(
group.id,
true
)
.then((resp) => { .then((resp) => {
current_groups = current_groups.filter((obj) => obj.id !== group.id); current_groups = current_groups.filter(
(obj) => obj.id !== group.id
);
}) })
.catch((err) => { .catch((err) => {
// error deleting user // error deleting user
}); });
}} }}
tabindex="0" tabindex="0"
class="ml-4 text-red-600 hover:text-red-900 cursor-pointer">{$_('confirm-delete')}</button> class="ml-4 text-red-600 hover:text-red-900 cursor-pointer"
>{$_("confirm-delete")}</button
>
</td> </td>
{:else} {:else}
<td <td
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"
>
<a <a
href="./{group.id}" href="./{group.id}"
class="text-indigo-600 hover:text-indigo-900">Details</a> class="text-indigo-600 hover:text-indigo-900">Details</a
{#if store.state.jwtinfo.userdetails.permissions.includes('USERGROUP:DELETE')} >
{#if store.state.jwtinfo.userdetails.permissions.includes("USERGROUP:DELETE")}
<button <button
on:click={() => { on:click={() => {
active_deletes[group.id] = true; active_deletes[group.id] = true;
}} }}
tabindex="0" tabindex="0"
class="ml-4 text-red-600 hover:text-red-900 cursor-pointer">{$_('delete')}</button> class="ml-4 text-red-600 hover:text-red-900 cursor-pointer"
>{$_("delete")}</button
>
{/if} {/if}
</td> </td>
{/if} {/if}
@ -118,7 +137,7 @@
{:catch error} {:catch error}
<div class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500"> <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"> <span class="inline-block align-middle mr-8">
<b class="capitalize">{$_('general_promise_error')}</b> <b class="capitalize">{$_("general_promise_error")}</b>
{error} {error}
</span> </span>
</div> </div>

View File

@ -1,9 +1,10 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import { clickOutside } from "../base/outsideclick"; import { clickOutside } from "../base/outsideclick";
import { RunnerOrganizationService } from "@odit/lfk-client-js"; import { RunnerOrganizationService } from "@odit/lfk-client-js";
import Toastify from "toastify-js";
import toast from "svelte-french-toast";
export let modal_open; export let modal_open;
export let current_organizations; export let current_organizations;
let name_input_dom; let name_input_dom;
@ -48,10 +49,7 @@
function submit() { function submit() {
if (processed_last_submit === true) { if (processed_last_submit === true) {
processed_last_submit = false; processed_last_submit = false;
const toast = Toastify({ toast.loading($_("organization-is-being-added"));
text: $_("organization-is-being-added"),
duration: -1,
}).showToast();
let address = {}; let address = {};
if (address_checked === true) { if (address_checked === true) {
address = { address = {
@ -70,17 +68,13 @@
.then((result) => { .then((result) => {
name = ""; name = "";
modal_open = false; modal_open = false;
Toastify({ toast.dismiss();
text: $_("organization-added"), toast.success($_("organization-added"));
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
current_organizations = current_organizations.concat([result]); current_organizations = current_organizations.concat([result]);
}) })
.catch((err) => {}) .catch((err) => {})
.finally(() => { .finally(() => {
processed_last_submit = true; processed_last_submit = true;
toast.hideToast();
}); });
} }
} }
@ -89,57 +83,69 @@
{#if modal_open} {#if modal_open}
<div <div
class="fixed z-10 inset-0 overflow-y-auto" class="fixed z-10 inset-0 overflow-y-auto"
use:clickOutside use:clickOutside
on:click_outside={() => { on:click_outside={() => {
modal_open = false; modal_open = false;
}}> }}
>
<div <div
class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> 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="fixed inset-0 transition-opacity" aria-hidden="true">
<div <div
class="absolute inset-0 bg-gray-500 opacity-75" class="absolute inset-0 bg-gray-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 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" 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" 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"> <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="sm:flex sm:items-start">
<div <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"> 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 <svg
class="h-6 w-6 text-blue-600" class="h-6 w-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"><path height="24"
d="M17 19h2v-8h-6v8h2v-6h2v6zM3 19V4a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v5h2v10h1v2H2v-2h1zm4-8v2h2v-2H7zm0 4v2h2v-2H7zm0-8v2h2V7H7z" /></svg> ><path
d="M17 19h2v-8h-6v8h2v-6h2v6zM3 19V4a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v5h2v10h1v2H2v-2h1zm4-8v2h2v-2H7zm0 4v2h2v-2H7zm0-8v2h2V7H7z"
/></svg
>
</div> </div>
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"> <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"> <h3 class="text-lg leading-6 font-medium text-gray-900">
{$_('create-a-new-organization')} {$_("create-a-new-organization")}
</h3> </h3>
<div class="mt-2 mb-6"> <div class="mt-2 mb-6">
<p class="text-sm text-gray-500"> <p class="text-sm text-gray-500">
{$_('please-provide-the-required-information-to-add-a-new-organization')} {$_(
"please-provide-the-required-information-to-add-a-new-organization"
)}
</p> </p>
</div> </div>
<div class="grid grid-cols-6 gap-6"> <div class="grid grid-cols-6 gap-6">
<div class="col-span-6"> <div class="col-span-6">
<label <label
for="firstname" for="firstname"
class="block text-sm font-medium text-gray-700">{$_('name')}</label> class="block text-sm font-medium text-gray-700"
>{$_("name")}</label
>
<input <input
use:focus use:focus
autocomplete="off" autocomplete="off"
placeholder={$_('name')} placeholder={$_("name")}
class:border-red-500={!isOrgnameValid} class:border-red-500={!isOrgnameValid}
class:focus:border-red-500={!isOrgnameValid} class:focus:border-red-500={!isOrgnameValid}
class:focus:ring-red-500={!isOrgnameValid} class:focus:ring-red-500={!isOrgnameValid}
@ -147,11 +153,13 @@
bind:this={name_input_dom} bind:this={name_input_dom}
type="text" type="text"
name="firstname" name="firstname"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> class="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"
/>
{#if !isOrgnameValid} {#if !isOrgnameValid}
<span <span
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
{$_('organization-name-is-required')} >
{$_("organization-name-is-required")}
</span> </span>
{/if} {/if}
</div> </div>
@ -162,96 +170,112 @@
id="comments" id="comments"
name="comments" name="comments"
type="checkbox" type="checkbox"
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" /> class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
/>
</div> </div>
<div class="ml-3 text-sm"> <div class="ml-3 text-sm">
<label <label for="comments" class="font-medium text-gray-700"
for="comments" >{$_("address")}</label
class="font-medium text-gray-700">{$_('address')}</label> >
</div> </div>
</div> </div>
{#if address_checked === true} {#if address_checked === true}
<div class="col-span-6"> <div class="col-span-6">
<label <label
for="address1" for="address1"
class="block text-sm font-medium text-gray-700">{$_('address')}</label> class="block text-sm font-medium text-gray-700"
<input >{$_("address")}</label
autocomplete="off" >
placeholder="{$_('address')}" <input
class:border-red-500={!isAddress1Valid} autocomplete="off"
class:focus:border-red-500={!isAddress1Valid} placeholder={$_("address")}
class:focus:ring-red-500={!isAddress1Valid} class:border-red-500={!isAddress1Valid}
bind:value={address_input1_value} class:focus:border-red-500={!isAddress1Valid}
bind:this={address_input1} class:focus:ring-red-500={!isAddress1Valid}
type="text" bind:value={address_input1_value}
name="address1" bind:this={address_input1}
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> type="text"
{#if !isAddress1Valid} name="address1"
<span class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> />
{$_('address-is-required')} {#if !isAddress1Valid}
</span> <span
{/if} class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
</div> >
<div class="col-span-6"> {$_("address-is-required")}
<label </span>
for="address2" {/if}
class="block text-sm font-medium text-gray-700">{$_('apartment-suite-etc')}</label> </div>
<input <div class="col-span-6">
autocomplete="off" <label
placeholder="{$_('apartment-suite-etc')}" for="address2"
bind:value={address_input2_value} class="block text-sm font-medium text-gray-700"
bind:this={address_input2} >{$_("apartment-suite-etc")}</label
type="text" >
name="address2" <input
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" /> autocomplete="off"
</div> placeholder={$_("apartment-suite-etc")}
<div class="col-span-6"> bind:value={address_input2_value}
<label bind:this={address_input2}
for="zipcode" type="text"
class="block text-sm font-medium text-gray-700">{$_('zip-postal-code')}</label> name="address2"
<input 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"
autocomplete="off" />
placeholder="{$_('zip-postal-code')}" </div>
class:border-red-500={!iszipcodevalid} <div class="col-span-6">
class:focus:border-red-500={!iszipcodevalid} <label
class:focus:ring-red-500={!iszipcodevalid} for="zipcode"
bind:value={address_zipcode_value} class="block text-sm font-medium text-gray-700"
bind:this={address_zipcode} >{$_("zip-postal-code")}</label
type="text" >
name="zipcode" <input
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" /> autocomplete="off"
{#if !iszipcodevalid} placeholder={$_("zip-postal-code")}
<span class:border-red-500={!iszipcodevalid}
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> class:focus:border-red-500={!iszipcodevalid}
{$_('valid-zipcode-postal-code-is-required')} class:focus:ring-red-500={!iszipcodevalid}
</span> bind:value={address_zipcode_value}
{/if} bind:this={address_zipcode}
</div> type="text"
<div class="col-span-6"> name="zipcode"
<label 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"
for="city" />
class="block text-sm font-medium text-gray-700">{$_('city')}</label> {#if !iszipcodevalid}
<input <span
autocomplete="off" class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
placeholder="{$_('city')}" >
class:border-red-500={!iscityvalid} {$_("valid-zipcode-postal-code-is-required")}
class:focus:border-red-500={!iscityvalid} </span>
class:focus:ring-red-500={!iscityvalid} {/if}
bind:value={address_city_value} </div>
bind:this={address_city} <div class="col-span-6">
type="text" <label
name="city" for="city"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> class="block text-sm font-medium text-gray-700"
{#if !iscityvalid} >{$_("city")}</label
<span >
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> <input
{$_('valid-city-is-required')} autocomplete="off"
</span> placeholder={$_("city")}
{/if} class:border-red-500={!iscityvalid}
</div> class:focus:border-red-500={!iscityvalid}
{/if} class:focus:ring-red-500={!iscityvalid}
</div> bind:value={address_city_value}
bind:this={address_city}
type="text"
name="city"
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"
/>
{#if !iscityvalid}
<span
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
>
{$_("valid-city-is-required")}
</span>
{/if}
</div>
{/if}
</div>
</div> </div>
</div> </div>
</div> </div>
@ -261,16 +285,18 @@
class:opacity-50={!createbtnenabled} class:opacity-50={!createbtnenabled}
on:click={submit} on:click={submit}
type="button" 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"> 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')} >
{$_("create")}
</button> </button>
<button <button
on:click={() => { on:click={() => {
modal_open = false; modal_open = false;
}} }}
type="button" 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"> 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')} >
{$_("cancel")}
</button> </button>
</div> </div>
</div> </div>

View File

@ -1,9 +1,9 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import { clickOutside } from "../base/outsideclick"; import { clickOutside } from "../base/outsideclick";
import { RunnerOrganizationService } from "@odit/lfk-client-js"; import { RunnerOrganizationService } from "@odit/lfk-client-js";
import Toastify from "toastify-js";
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
export let modal_open; export let modal_open;
export let delete_org; export let delete_org;
@ -18,11 +18,7 @@
true true
) )
.then((resp) => { .then((resp) => {
Toastify({ toast($_("organization-deleted"));
text: $_('organization-deleted'),
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
location.replace("./"); location.replace("./");
}) })
.catch((err) => {}); .catch((err) => {});
@ -32,51 +28,59 @@
{#if modal_open} {#if modal_open}
<div <div
class="fixed z-10 inset-0 overflow-y-auto" class="fixed z-10 inset-0 overflow-y-auto"
use:clickOutside use:clickOutside
on:click_outside={cancelDelete}> on:click_outside={cancelDelete}
>
<div <div
class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> 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="fixed inset-0 transition-opacity" aria-hidden="true">
<div <div
class="absolute inset-0 bg-gray-500 opacity-75" class="absolute inset-0 bg-gray-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 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" 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" 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"> <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="sm:flex sm:items-start">
<div <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"> 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 <svg
class="h-6 w-6 text-blue-600" class="h-6 w-6 text-blue-600"
fill="currentColor" fill="currentColor"
width="24" width="24"
height="24" height="24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 640 512"><path viewBox="0 0 640 512"
><path
fill="currentColor" fill="currentColor"
d="M96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm448 0c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm32 32h-64c-17.6 0-33.5 7.1-45.1 18.6 40.3 22.1 68.9 62 75.1 109.4h66c17.7 0 32-14.3 32-32v-32c0-35.3-28.7-64-64-64zm-256 0c61.9 0 112-50.1 112-112S381.9 32 320 32 208 82.1 208 144s50.1 112 112 112zm76.8 32h-8.3c-20.8 10-43.9 16-68.5 16s-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h288c26.5 0 48-21.5 48-48v-28.8c0-63.6-51.6-115.2-115.2-115.2zm-223.7-13.4C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z" /></svg> d="M96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm448 0c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm32 32h-64c-17.6 0-33.5 7.1-45.1 18.6 40.3 22.1 68.9 62 75.1 109.4h66c17.7 0 32-14.3 32-32v-32c0-35.3-28.7-64-64-64zm-256 0c61.9 0 112-50.1 112-112S381.9 32 320 32 208 82.1 208 144s50.1 112 112 112zm76.8 32h-8.3c-20.8 10-43.9 16-68.5 16s-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h288c26.5 0 48-21.5 48-48v-28.8c0-63.6-51.6-115.2-115.2-115.2zm-223.7-13.4C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z"
/></svg
>
</div> </div>
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"> <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"> <h3 class="text-lg leading-6 font-medium text-gray-900">
{$_('attention')} {$_("attention")}
</h3> </h3>
<div class="mt-2 mb-6"> <div class="mt-2 mb-6">
<p class="text-sm text-gray-500"> <p class="text-sm text-gray-500">
{$_( {$_(
'do-you-want-to-delete-the-organization-delete_org-name', "do-you-want-to-delete-the-organization-delete_org-name",
{ {
values: { orgname: delete_org.name }, values: { orgname: delete_org.name },
} }
)}<br /> )}<br />
{$_('all-associated-teams-and-runners-will-be-deleted-too')} {$_("all-associated-teams-and-runners-will-be-deleted-too")}
</p> </p>
</div> </div>
</div> </div>
@ -86,14 +90,16 @@
<button <button
on:click={deleteOrg} on:click={deleteOrg}
type="button" 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"> 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-organization-and-associated-teams-runners')} >
{$_("confirm-delete-organization-and-associated-teams-runners")}
</button> </button>
<button <button
on:click={cancelDelete} on:click={cancelDelete}
type="button" 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"> 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-organization')} >
{$_("cancel-keep-organization")}
</button> </button>
</div> </div>
</div> </div>

View File

@ -1,507 +1,482 @@
<script> <script>
import { import {
GroupContactService, GroupContactService,
RunnerOrganizationService, RunnerOrganizationService,
} from "@odit/lfk-client-js"; } from "@odit/lfk-client-js";
import { getLocaleFromNavigator, _ } from "svelte-i18n"; import { getLocaleFromNavigator, _ } from "svelte-i18n";
import Toastify from "toastify-js";
import store from "../../store"; import store from "../../store";
import ConfirmOrgDeletion from "./ConfirmOrgDeletion.svelte"; import ConfirmOrgDeletion from "./ConfirmOrgDeletion.svelte";
import ImportRunnerModal from "../runners/ImportRunnerModal.svelte"; import ImportRunnerModal from "../runners/ImportRunnerModal.svelte";
import PromiseError from "../base/PromiseError.svelte"; import PromiseError from "../base/PromiseError.svelte";
import Select from "svelte-select"; import Select from "svelte-select";
import GenerateSponsoringContracts from "../pdf_generation/GenerateSponsoringContracts.svelte"; import GenerateSponsoringContracts from "../pdf_generation/GenerateSponsoringContracts.svelte";
import GenerateRunnerCards from "../pdf_generation/GenerateRunnerCards.svelte"; import GenerateRunnerCards from "../pdf_generation/GenerateRunnerCards.svelte";
import GenerateRunnerCertificates from "../pdf_generation/GenerateRunnerCertificates.svelte"; import GenerateRunnerCertificates from "../pdf_generation/GenerateRunnerCertificates.svelte";
import { tick } from "svelte"; import { tick } from "svelte";
$: delete_triggered = false; $: delete_triggered = false;
$: address_valid_or_none = $: address_valid_or_none =
(isAddress1Valid && iszipcodevalid && iscityvalid) || (isAddress1Valid && iszipcodevalid && iscityvalid) ||
editable.address_checked === false; editable.address_checked === false;
$: save_enabled = data_changed && address_valid_or_none; $: save_enabled = data_changed && address_valid_or_none;
let original = ""; let original = "";
let original_object = {}; let original_object = {};
let contacts = []; let contacts = [];
let valueCopy = null; let valueCopy = null;
let areaDom; let areaDom;
let copied = false; let copied = false;
export let params; export let params;
$: editable = {}; $: editable = {};
$: contact = {}; $: contact = {};
$: data_loaded = false; $: data_loaded = false;
$: data_changed = !(JSON.stringify(editable) === original); $: data_changed = !(JSON.stringify(editable) === original);
$: isAddress1Valid = editable.address?.address1?.trim().length !== 0; $: isAddress1Valid = editable.address?.address1?.trim().length !== 0;
$: iszipcodevalid = editable.address?.postalcode?.trim().length !== 0; $: iszipcodevalid = editable.address?.postalcode?.trim().length !== 0;
$: iscityvalid = editable.address?.city?.trim().length !== 0; $: iscityvalid = editable.address?.city?.trim().length !== 0;
$: sponsoring_contracts_show = true; $: sponsoring_contracts_show = true;
$: cards_show = true; $: cards_show = true;
$: certificates_show = true; $: certificates_show = true;
$: generate_orgs = [original_object]; $: generate_orgs = [original_object];
$: registrationLink = `${config.baseurl}/selfservice/register/${editable.registrationKey}`; $: registrationLink = `${config.baseurl}/selfservice/register/${editable.registrationKey}`;
const getContactLabel = (option) => const getContactLabel = (option) =>
option.firstname + " " + (option.middlename || "") + " " + option.lastname; option.firstname + " " + (option.middlename || "") + " " + option.lastname;
const promise = RunnerOrganizationService.runnerOrganizationControllerGetOne( const promise = RunnerOrganizationService.runnerOrganizationControllerGetOne(
params.orgid params.orgid
).then((value) => { ).then((value) => {
data_loaded = true; data_loaded = true;
value.address_checked = value.address.address1 !== null; value.address_checked = value.address.address1 !== null;
if (value.address_checked === false) { if (value.address_checked === false) {
value.address = { value.address = {
address1: "", address1: "",
address2: "", address2: "",
city: "", city: "",
postalcode: "", postalcode: "",
country: "", country: "",
}; };
} }
editable = Object.assign(editable, value); editable = Object.assign(editable, value);
editable = editable; editable = editable;
original_object = Object.assign(editable, value); original_object = Object.assign(editable, value);
original = JSON.stringify(value); original = JSON.stringify(value);
GroupContactService.groupContactControllerGetAll().then((val) => { GroupContactService.groupContactControllerGetAll().then((val) => {
contacts = val.map((r) => { contacts = val.map((r) => {
return { label: getContactLabel(r), value: r }; return { label: getContactLabel(r), value: r };
}); });
if (editable.contact) { if (editable.contact) {
contact = contacts.find((g) => g.value.id == editable.contact.id); contact = contacts.find((g) => g.value.id == editable.contact.id);
} else { } else {
contact = null; contact = null;
} }
}); });
}); });
let modal_open = false; let modal_open = false;
let delete_org = {}; let delete_org = {};
function deleteOrganization() { function deleteOrganization() {
RunnerOrganizationService.runnerOrganizationControllerRemove( RunnerOrganizationService.runnerOrganizationControllerRemove(
original_object.id, original_object.id,
false false
) )
.then((resp) => { .then((resp) => {
Toastify({ toast($_("organization-deleted"));
text: $_("organization-deleted"), location.replace("./");
duration: 500, })
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", .catch((err) => {
}).showToast(); modal_open = true;
location.replace("./"); delete_org = original_object;
}) });
.catch((err) => { }
modal_open = true; function submit() {
delete_org = original_object; if (data_loaded === true && save_enabled) {
}); toast($_("updating-organization"));
} let postdata = Object.assign({}, editable);
function submit() { if (postdata.address_checked === false) {
if (data_loaded === true && save_enabled) { postdata.address = null;
Toastify({ }
text: $_("updating-organization"), postdata.contact = postdata.contact?.id;
duration: 2500, RunnerOrganizationService.runnerOrganizationControllerPut(
}).showToast(); original_object.id,
let postdata = Object.assign({}, editable); postdata
if (postdata.address_checked === false) { )
postdata.address = null; .then((resp) => {
} editable.registrationKey = resp.registrationKey;
postdata.contact = postdata.contact?.id; original_object = Object.assign({}, editable);
RunnerOrganizationService.runnerOrganizationControllerPut( original = JSON.stringify(original_object);
original_object.id, toast.success($_("updated-organization"));
postdata })
) .catch((err) => {});
.then((resp) => { } else {
editable.registrationKey = resp.registrationKey; }
original_object = Object.assign({}, editable); }
original = JSON.stringify(original_object); async function copy() {
Toastify({ if (!editable.registrationKey) {
text: $_("updated-organization"), toast.error($_("you-have-to-save-your-changes-to-generate-a-link"))
duration: 2500, return;
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", }
}).showToast(); valueCopy = registrationLink;
}) await tick();
.catch((err) => {}); areaDom.focus();
} else { areaDom.select();
} try {
} const successful = document.execCommand("copy");
async function copy() { if (!successful) {
if (!editable.registrationKey) { throw new Error();
Toastify({ }
text: $_("you-have-to-save-your-changes-to-generate-a-link"), toast($_("copied-link-to-clipboard"));
duration: 500, copied = true;
backgroundColor: } catch (err) {
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)", toast.error($_("error-whyile-copying-to-clipboard"))
}).showToast(); }
return; // we can notifi by event or storage about copy status
} valueCopy = null;
valueCopy = registrationLink; }
await tick(); export let import_modal_open = false;
areaDom.focus(); </script>
areaDom.select();
try { {#if valueCopy != null}<textarea bind:this={areaDom}>{valueCopy}</textarea>{/if}
const successful = document.execCommand("copy"); <ImportRunnerModal
if (!successful) { on:cancelDelete={(event) => {
throw new Error(); import_modal_open = false;
} }}
Toastify({ current_runners={[]}
text: $_("copied-link-to-clipboard"), passed_team={{}}
duration: 500, passed_orgs={[]}
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", passed_org={editable}
}).showToast(); opened_from="OrgDetail"
copied = true; bind:import_modal_open
} catch (err) { />
Toastify({ <ConfirmOrgDeletion bind:modal_open bind:delete_org />
text: $_("error-whyile-copying-to-clipboard"), {#if data_loaded}
duration: 500, <section class="container p-5">
backgroundColor: <div class="mb-8 text-3xl font-extrabold leading-tight">
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)", {original_object.name}
}).showToast(); <span data-id="org_actions_${editable.id}">
} <GenerateSponsoringContracts
// we can notifi by event or storage about copy status bind:sponsoring_contracts_show
valueCopy = null; bind:generate_orgs
} />
export let import_modal_open = false; <GenerateRunnerCards bind:cards_show bind:generate_orgs />
</script> <GenerateRunnerCertificates bind:certificates_show bind:generate_orgs />
{#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:IMPORT")}
{#if valueCopy != null}<textarea bind:this={areaDom}>{valueCopy}</textarea>{/if} <button
<ImportRunnerModal on:click={() => {
on:cancelDelete={(event) => { import_modal_open = true;
import_modal_open = false; }}
}} type="button"
current_runners={[]} 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"
passed_team={{}} >
passed_orgs={[]} {$_("import-runners")}
passed_org={editable} </button>
opened_from="OrgDetail" {/if}
bind:import_modal_open {#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:DELETE")}
/> {#if delete_triggered}
<ConfirmOrgDeletion bind:modal_open bind:delete_org /> <button
{#if data_loaded} on:click={deleteOrganization}
<section class="container p-5"> 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"
<div class="mb-8 text-3xl font-extrabold leading-tight"> >{$_("confirm-delete")}</button
{original_object.name} >
<span data-id="org_actions_${editable.id}"> <button
<GenerateSponsoringContracts on:click={() => {
bind:sponsoring_contracts_show delete_triggered = !delete_triggered;
bind:generate_orgs }}
/> 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"
<GenerateRunnerCards bind:cards_show bind:generate_orgs /> >{$_("cancel")}</button
<GenerateRunnerCertificates bind:certificates_show bind:generate_orgs /> >
{#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:IMPORT")} {/if}
<button {#if !delete_triggered}
on:click={() => { <button
import_modal_open = true; on:click={() => {
}} delete_triggered = 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" 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"
{$_("import-runners")} >{$_("delete-organization")}</button
</button> >
{/if} {/if}
{#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:DELETE")} {/if}
{#if delete_triggered} {#if !delete_triggered}
<button <button
on:click={deleteOrganization} on:click={submit}
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" disabled={!save_enabled}
>{$_("confirm-delete")}</button class:opacity-50={!save_enabled}
> type="button"
<button class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm"
on:click={() => { >{$_("save-changes")}</button
delete_triggered = !delete_triggered; >
}} {/if}
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" </span>
>{$_("cancel")}</button </div>
> <div class="flex flex-row mb-4">
{/if} <div class="w-full">
{#if !delete_triggered} <nav class="w-full flex">
<button <ol class="list-none flex flex-row items-center justify-start">
on:click={() => { <li class="mr-2 flex items-center">
delete_triggered = true; <svg
}} stroke="currentColor"
type="button" fill="none"
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" stroke-width="2"
>{$_("delete-organization")}</button viewBox="0 0 24 24"
> stroke-linecap="round"
{/if} stroke-linejoin="round"
{/if} class="h-3 w-3 stroke-current"
{#if !delete_triggered} height="1em"
<button width="1em"
on:click={submit} xmlns="http://www.w3.org/2000/svg"
disabled={!save_enabled} ><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
class:opacity-50={!save_enabled} <polyline points="9 22 9 12 15 12 15 22" /></svg
type="button" >
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm" </li>
>{$_("save-changes")}</button <li class="flex items-center">
> <a class="mr-2" href="/">{$_("home")}</a><svg
{/if} stroke="currentColor"
</span> fill="none"
</div> stroke-width="2"
<div class="flex flex-row mb-4"> viewBox="0 0 24 24"
<div class="w-full"> stroke-linecap="round"
<nav class="w-full flex"> stroke-linejoin="round"
<ol class="list-none flex flex-row items-center justify-start"> class="h-3 w-3 mr-2 stroke-current"
<li class="mr-2 flex items-center"> height="1em"
<svg width="1em"
stroke="currentColor" xmlns="http://www.w3.org/2000/svg"
fill="none" ><line x1="5" y1="12" x2="19" y2="12" />
stroke-width="2" <polyline points="12 5 19 12 12 19" /></svg
viewBox="0 0 24 24" >
stroke-linecap="round" </li>
stroke-linejoin="round" <li class="mr-2 flex items-center">
class="h-3 w-3 stroke-current" <svg
height="1em" xmlns="http://www.w3.org/2000/svg"
width="1em" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" width="24"
><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" /> height="24"
<polyline points="9 22 9 12 15 12 15 22" /></svg ><path fill="none" d="M0 0h24v24H0z" />
> <path
</li> d="M21 20h2v2H1v-2h2V3a1 1 0 0 1 1-1h16a1 1 0 0 1 1 1v17zm-2 0V4H5v16h14zM8 11h3v2H8v-2zm0-4h3v2H8V7zm0 8h3v2H8v-2zm5 0h3v2h-3v-2zm0-4h3v2h-3v-2zm0-4h3v2h-3V7z"
<li class="flex items-center"> /></svg
<a class="mr-2" href="/">{$_("home")}</a><svg >
stroke="currentColor" </li>
fill="none" <li class="flex items-center">
stroke-width="2" <a class="mr-2" href="./">{$_("organizations")}</a><svg
viewBox="0 0 24 24" stroke="currentColor"
stroke-linecap="round" fill="none"
stroke-linejoin="round" stroke-width="2"
class="h-3 w-3 mr-2 stroke-current" viewBox="0 0 24 24"
height="1em" stroke-linecap="round"
width="1em" stroke-linejoin="round"
xmlns="http://www.w3.org/2000/svg" class="h-3 w-3 mr-2 stroke-current"
><line x1="5" y1="12" x2="19" y2="12" /> height="1em"
<polyline points="12 5 19 12 12 19" /></svg width="1em"
> xmlns="http://www.w3.org/2000/svg"
</li> ><line x1="5" y1="12" x2="19" y2="12" />
<li class="mr-2 flex items-center"> <polyline points="12 5 19 12 12 19" /></svg
<svg >
xmlns="http://www.w3.org/2000/svg" </li>
viewBox="0 0 24 24" <li class="flex items-center">
width="24" <span class="mr-2">Org-Details #{params.orgid}</span>
height="24" </li>
><path fill="none" d="M0 0h24v24H0z" /> </ol>
<path </nav>
d="M21 20h2v2H1v-2h2V3a1 1 0 0 1 1-1h16a1 1 0 0 1 1 1v17zm-2 0V4H5v16h14zM8 11h3v2H8v-2zm0-4h3v2H8V7zm0 8h3v2H8v-2zm5 0h3v2h-3v-2zm0-4h3v2h-3v-2zm0-4h3v2h-3V7z" </div>
/></svg </div>
> <div class="text-sm w-full">
</li> <label for="name" class="font-medium text-gray-700">{$_("name")}</label>
<li class="flex items-center"> <input
<a class="mr-2" href="./">{$_("organizations")}</a><svg autocomplete="off"
stroke="currentColor" placeholder={$_("name")}
fill="none" type="text"
stroke-width="2" bind:value={editable.name}
viewBox="0 0 24 24" name="name"
stroke-linecap="round" 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"
stroke-linejoin="round" />
class="h-3 w-3 mr-2 stroke-current" </div>
height="1em" <div class="text-sm w-full">
width="1em" <label for="contact" class="font-medium text-gray-700"
xmlns="http://www.w3.org/2000/svg" >{$_("contact")}</label
><line x1="5" y1="12" x2="19" y2="12" /> >
<polyline points="12 5 19 12 12 19" /></svg <Select
> containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"
</li> itemFilter={(label, filterText, option) =>
<li class="flex items-center"> label.toLowerCase().includes(filterText.toLowerCase()) ||
<span class="mr-2">Org-Details #{params.orgid}</span> option.value.id.toString().startsWith(filterText.toLowerCase())}
</li> items={contacts}
</ol> showChevron={true}
</nav> placeholder={$_("no-contact-selected")}
</div> noOptionsMessage={$_("no-contact-found")}
</div> bind:selectedValue={contact}
<div class="text-sm w-full"> on:select={(selectedValue) =>
<label for="name" class="font-medium text-gray-700">{$_("name")}</label> (editable.contact = selectedValue.detail.value)}
<input on:clear={() => (editable.contact = null)}
autocomplete="off" />
placeholder={$_("name")} </div>
type="text" <div>
bind:value={editable.name} <div class="flex items-start mt-2">
name="name" <div class="flex items-center h-5">
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" <input
/> bind:checked={editable.registrationEnabled}
</div> id="toggle_selfservice_feature"
<div class="text-sm w-full"> name="toggle_selfservice_feature"
<label for="contact" class="font-medium text-gray-700" type="checkbox"
>{$_("contact")}</label class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
> />
<Select </div>
containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" <div class="ml-3 text-sm">
itemFilter={(label, filterText, option) => <label
label.toLowerCase().includes(filterText.toLowerCase()) || for="toggle_selfservice_feature"
option.value.id.toString().startsWith(filterText.toLowerCase())} class="font-medium text-gray-700"
items={contacts} >{$_("selfservice-registration")}</label
showChevron={true} >
placeholder={$_("no-contact-selected")} </div>
noOptionsMessage={$_("no-contact-found")} </div>
bind:selectedValue={contact} <div>
on:select={(selectedValue) => {#if editable.registrationEnabled}
(editable.contact = selectedValue.detail.value)} <div class="text-sm w-full">
on:clear={() => (editable.contact = null)} <button on:click={copy} class="inline-flex w-full">
/> <p
</div> name="token"
<div> 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"
<div class="flex items-start mt-2"> >
<div class="flex items-center h-5"> {#if editable.registrationKey}
<input {registrationLink}
bind:checked={editable.registrationEnabled} {:else}
id="toggle_selfservice_feature" {$_("you-have-to-save-your-changes-to-generate-a-link")}
name="toggle_selfservice_feature" {/if}
type="checkbox" </p>
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" <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"
</div> >
<div class="ml-3 text-sm"> <svg
<label xmlns="http://www.w3.org/2000/svg"
for="toggle_selfservice_feature" viewBox="0 0 24 24"
class="font-medium text-gray-700" width="24"
>{$_("selfservice-registration")}</label height="24"
> ><path fill="none" d="M0 0h24v24H0z" />
</div> <path
</div> fill="currentColor"
<div> d="M7 4V2h10v2h3l1 1v16a1 1 0 01-1 1H4a1 1 0 01-1-1V5l1-1h3zm0 2H5v14h14V6h-2v2H7V6zm2-2v2h6V4H9z"
{#if editable.registrationEnabled} /></svg
<div class="text-sm w-full"> >
<button on:click={copy} class="inline-flex w-full"> </div>
<p </button>
name="token" {#if editable.registrationKey}
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" <p class="text-gray-500 text-xs">
> {$_("click-to-copy-the-link-into-your-clipboard")}
{#if editable.registrationKey} </p>
{registrationLink} {/if}
{:else} </div>
{$_("you-have-to-save-your-changes-to-generate-a-link")} {/if}
{/if} <!-- -->
</p> <div>
<div <div class="flex items-start mt-2">
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" <div class="flex items-center h-5">
> <input
<svg bind:checked={editable.address_checked}
xmlns="http://www.w3.org/2000/svg" id="toggle_address_checkbox"
viewBox="0 0 24 24" name="toggle_address_checkbox"
width="24" type="checkbox"
height="24" class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
><path fill="none" d="M0 0h24v24H0z" /> />
<path </div>
fill="currentColor" <div class="ml-3 text-sm">
d="M7 4V2h10v2h3l1 1v16a1 1 0 01-1 1H4a1 1 0 01-1-1V5l1-1h3zm0 2H5v14h14V6h-2v2H7V6zm2-2v2h6V4H9z" <label
/></svg for="toggle_address_checkbox"
> class="font-medium text-gray-700">{$_("address")}</label
</div> >
</button> </div>
{#if editable.registrationKey} </div>
<p class="text-gray-500 text-xs"> </div>
{$_("click-to-copy-the-link-into-your-clipboard")} {#if editable.address_checked === true}
</p> <div class="col-span-6">
{/if} <label
</div> for="address1"
{/if} class="block text-sm font-medium text-gray-700"
<!-- --> >{$_("address")}</label
<div> >
<div class="flex items-start mt-2"> <input
<div class="flex items-center h-5"> autocomplete="off"
<input placeholder="Address"
bind:checked={editable.address_checked} class:border-red-500={!isAddress1Valid}
id="toggle_address_checkbox" class:focus:border-red-500={!isAddress1Valid}
name="toggle_address_checkbox" class:focus:ring-red-500={!isAddress1Valid}
type="checkbox" bind:value={editable.address.address1}
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" type="text"
/> name="address1"
</div> 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 class="ml-3 text-sm"> />
<label {#if !isAddress1Valid}
for="toggle_address_checkbox" <span
class="font-medium text-gray-700">{$_("address")}</label class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
> >
</div> {$_("address-is-required")}
</div> </span>
</div> {/if}
{#if editable.address_checked === true} </div>
<div class="col-span-6"> <div class="col-span-6">
<label <label
for="address1" for="address2"
class="block text-sm font-medium text-gray-700" class="block text-sm font-medium text-gray-700"
>{$_("address")}</label >{$_("apartment-suite-etc")}</label
> >
<input <input
autocomplete="off" autocomplete="off"
placeholder="Address" placeholder={$_("apartment-suite-etc")}
class:border-red-500={!isAddress1Valid} bind:value={editable.address.address2}
class:focus:border-red-500={!isAddress1Valid} type="text"
class:focus:ring-red-500={!isAddress1Valid} name="address2"
bind:value={editable.address.address1} class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"
type="text" />
name="address1" </div>
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 class="col-span-6">
/> <label for="zipcode" class="block text-sm font-medium text-gray-700"
{#if !isAddress1Valid} >{$_("zip-postal-code")}</label
<span >
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" <input
> autocomplete="off"
{$_("address-is-required")} placeholder={$_("zip-postal-code")}
</span> class:border-red-500={!iszipcodevalid}
{/if} class:focus:border-red-500={!iszipcodevalid}
</div> class:focus:ring-red-500={!iszipcodevalid}
<div class="col-span-6"> bind:value={editable.address.postalcode}
<label type="text"
for="address2" name="zipcode"
class="block text-sm font-medium text-gray-700" 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"
>{$_("apartment-suite-etc")}</label />
> {#if !iszipcodevalid}
<input <span
autocomplete="off" class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
placeholder={$_("apartment-suite-etc")} >
bind:value={editable.address.address2} {$_("valid-zipcode-postal-code-is-required")}
type="text" </span>
name="address2" {/if}
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 class="col-span-6">
</div> <label for="city" class="block text-sm font-medium text-gray-700"
<div class="col-span-6"> >{$_("city")}</label
<label for="zipcode" class="block text-sm font-medium text-gray-700" >
>{$_("zip-postal-code")}</label <input
> autocomplete="off"
<input placeholder={$_("city")}
autocomplete="off" class:border-red-500={!iscityvalid}
placeholder={$_("zip-postal-code")} class:focus:border-red-500={!iscityvalid}
class:border-red-500={!iszipcodevalid} class:focus:ring-red-500={!iscityvalid}
class:focus:border-red-500={!iszipcodevalid} bind:value={editable.address.city}
class:focus:ring-red-500={!iszipcodevalid} type="text"
bind:value={editable.address.postalcode} name="city"
type="text" 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"
name="zipcode" />
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" {#if !iscityvalid}
/> <span
{#if !iszipcodevalid} class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
<span >
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" {$_("valid-city-is-required")}
> </span>
{$_("valid-zipcode-postal-code-is-required")} {/if}
</span> </div>
{/if} {/if}
</div> </div>
<div class="col-span-6"> </div>
<label for="city" class="block text-sm font-medium text-gray-700" </section>
>{$_("city")}</label {:else}
> {#await promise}
<input {$_("organization-detail-is-being-loaded")}
autocomplete="off" {:catch error}
placeholder={$_("city")} <PromiseError />
class:border-red-500={!iscityvalid} {/await}
class:focus:border-red-500={!iscityvalid} {/if}
class:focus:ring-red-500={!iscityvalid}
bind:value={editable.address.city}
type="text"
name="city"
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"
/>
{#if !iscityvalid}
<span
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
>
{$_("valid-city-is-required")}
</span>
{/if}
</div>
{/if}
</div>
</div>
</section>
{:else}
{#await promise}
{$_("organization-detail-is-being-loaded")}
{:catch error}
<PromiseError />
{/await}
{/if}

View File

@ -1,219 +1,247 @@
<script> <script>
import { getLocaleFromNavigator, _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import GenerateSponsoringContracts from "../pdf_generation/GenerateSponsoringContracts.svelte"; import GenerateSponsoringContracts from "../pdf_generation/GenerateSponsoringContracts.svelte";
let modal_open = false; let modal_open = false;
let delete_org = {}; let delete_org = {};
import { RunnerOrganizationService } from "@odit/lfk-client-js"; import { RunnerOrganizationService } from "@odit/lfk-client-js";
import store from "../../store"; import store from "../../store";
import OrgsEmptyState from "./OrgsEmptyState.svelte"; import OrgsEmptyState from "./OrgsEmptyState.svelte";
import Toastify from "toastify-js"; import ConfirmOrgDeletion from "./ConfirmOrgDeletion.svelte";
import ConfirmOrgDeletion from "./ConfirmOrgDeletion.svelte"; import GenerateRunnerCards from "../pdf_generation/GenerateRunnerCards.svelte";
import GenerateRunnerCards from "../pdf_generation/GenerateRunnerCards.svelte"; import GenerateRunnerCertificates from "../pdf_generation/GenerateRunnerCertificates.svelte";
import GenerateRunnerCertificates from "../pdf_generation/GenerateRunnerCertificates.svelte"; import toast from "svelte-french-toast";
$: searchvalue = ""; $: searchvalue = "";
$: active_deletes = []; $: active_deletes = [];
$: sponsoring_contracts_show = current_organizations.some((r) => r.is_selected === true); $: sponsoring_contracts_show = current_organizations.some(
$: cards_show = current_organizations.some((r) => r.is_selected === true); (r) => r.is_selected === true
$: generate_orgs = current_organizations.filter((r) => r.is_selected === true); );
$: certificates_show = current_organizations.some( $: cards_show = current_organizations.some((r) => r.is_selected === true);
(r) => r.is_selected === true $: generate_orgs = current_organizations.filter(
); (r) => r.is_selected === true
export let current_organizations = []; );
$: certificates_show = current_organizations.some(
const promise = RunnerOrganizationService.runnerOrganizationControllerGetAll().then( (r) => r.is_selected === true
(val) => { );
current_organizations = val; export let current_organizations = [];
}
); const promise =
</script> RunnerOrganizationService.runnerOrganizationControllerGetAll().then(
(val) => {
<ConfirmOrgDeletion current_organizations = val;
on:cancelDelete={(event) => { }
modal_open = false; );
active_deletes[event.detail.id] = false; </script>
}}
bind:modal_open <ConfirmOrgDeletion
bind:delete_org /> on:cancelDelete={(event) => {
{#if store.state.jwtinfo.userdetails.permissions.includes('ORGANIZATION:GET')} modal_open = false;
{#await promise} active_deletes[event.detail.id] = false;
<div }}
class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2" bind:modal_open
role="alert"> bind:delete_org
<p class="font-bold">{$_('organizations-are-being-loaded')}</p> />
<p class="text-sm">{$_('this-might-take-a-moment')}</p> {#if store.state.jwtinfo.userdetails.permissions.includes("ORGANIZATION:GET")}
</div> {#await promise}
{:then} <div
{#if current_organizations.length === 0} class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2"
<OrgsEmptyState /> role="alert"
{:else} >
<input <p class="font-bold">{$_("organizations-are-being-loaded")}</p>
type="search" <p class="text-sm">{$_("this-might-take-a-moment")}</p>
bind:value={searchvalue} </div>
placeholder={$_('datatable.search')} {:then}
aria-label={$_('datatable.search')} {#if current_organizations.length === 0}
class="mb-4" /> <OrgsEmptyState />
<div class="h-12"> {:else}
<GenerateSponsoringContracts <input
bind:sponsoring_contracts_show type="search"
bind:generate_orgs /> bind:value={searchvalue}
<GenerateRunnerCards placeholder={$_("datatable.search")}
bind:cards_show aria-label={$_("datatable.search")}
bind:generate_orgs /> class="mb-4"
<GenerateRunnerCertificates />
bind:certificates_show <div class="h-12">
bind:generate_orgs /> <GenerateSponsoringContracts
</div> bind:sponsoring_contracts_show
<div bind:generate_orgs
class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll"> />
<table class="divide-y divide-gray-200 w-full"> <GenerateRunnerCards bind:cards_show bind:generate_orgs />
<thead class="bg-gray-50"> <GenerateRunnerCertificates bind:certificates_show bind:generate_orgs />
<tr> </div>
<th <div
scope="col" class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> >
<span <table class="divide-y divide-gray-200 w-full">
on:click={() => { <thead class="bg-gray-50">
const newstate = !current_organizations.some((r) => r.is_selected === true); <tr>
current_organizations = current_organizations.map((r) => { <th
r.is_selected = newstate; scope="col"
return r; class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
}); >
}} <span
class="underline cursor-pointer select-none">{#if current_organizations.some((r) => r.is_selected === true)} on:click={() => {
{$_('deselect-all')} const newstate = !current_organizations.some(
{:else}{$_('select-all')}{/if} (r) => r.is_selected === true
</span> );
</th> current_organizations = current_organizations.map((r) => {
<th r.is_selected = newstate;
scope="col" return r;
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> });
{$_('name')} }}
</th> class="underline cursor-pointer select-none"
<th >{#if current_organizations.some((r) => r.is_selected === true)}
scope="col" {$_("deselect-all")}
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> {:else}{$_("select-all")}{/if}
{$_('address')} </span>
</th> </th>
<th <th
scope="col" scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
{$_('contact')} >
</th> {$_("name")}
<th scope="col" class="relative px-6 py-3"> </th>
<span class="sr-only">{$_('action')}</span> <th
</th> scope="col"
</tr> class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
</thead> >
<tbody class="divide-y divide-gray-200"> {$_("address")}
{#each current_organizations as o} </th>
{#if Object.values(o) <th
.toString() scope="col"
.toLowerCase() class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
.includes(searchvalue)} >
<tr data-rowid="org_{o.id}"> {$_("contact")}
<td class="px-6 py-4 whitespace-nowrap"> </th>
<input <th scope="col" class="relative px-6 py-3">
bind:checked={o.is_selected} <span class="sr-only">{$_("action")}</span>
type="checkbox" </th>
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" /> </tr>
</td> </thead>
<td class="px-6 py-4 whitespace-nowrap"> <tbody class="divide-y divide-gray-200">
<div class="flex items-center"> {#each current_organizations as o}
<div class="ml-4"> {#if Object.values(o)
<div class="text-sm font-medium text-gray-900"> .toString()
{o.name} .toLowerCase()
</div> .includes(searchvalue)}
</div> <tr data-rowid="org_{o.id}">
</div> <td class="px-6 py-4 whitespace-nowrap">
</td> <input
<td class="px-6 py-4 whitespace-nowrap"> bind:checked={o.is_selected}
<div class="flex items-center"> type="checkbox"
<div class="ml-4"> class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
<div class="text-sm font-medium text-gray-900"> />
{#if o.address.address1 !== null} </td>
{o.address.address1}<br /> <td class="px-6 py-4 whitespace-nowrap">
<!-- {o.address.address2 || ''}<br /> --> <div class="flex items-center">
{o.address.postalcode} <div class="ml-4">
{o.address.city} <div class="text-sm font-medium text-gray-900">
{o.address.country} {o.name}
{/if} </div>
</div> </div>
</div> </div>
</div> </td>
</td> <td class="px-6 py-4 whitespace-nowrap">
<td class="px-6 py-4 whitespace-nowrap"> <div class="flex items-center">
<div class="flex items-center"> <div class="ml-4">
<div class="ml-4"> <div class="text-sm font-medium text-gray-900">
<div class="text-sm font-medium text-gray-900"> {#if o.address.address1 !== null}
{#if o.contact} {o.address.address1}<br />
<a <!-- {o.address.address2 || ''}<br /> -->
href="../contacts/{o.contact.id}" {o.address.postalcode}
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800">{o.contact.firstname} {o.address.city}
{o.contact.middlename || ''} {o.address.country}
{o.contact.lastname}</a> {/if}
{:else}{$_('no-contact-specified')}{/if} </div>
</div> </div>
</div> </div>
</div> </td>
</td> <td class="px-6 py-4 whitespace-nowrap">
{#if active_deletes[o.id] === true} <div class="flex items-center">
<td <div class="ml-4">
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> <div class="text-sm font-medium text-gray-900">
<button {#if o.contact}
on:click={() => { <a
active_deletes[o.id] = false; href="../contacts/{o.contact.id}"
}} class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800"
tabindex="0" >{o.contact.firstname}
class="ml-4 text-indigo-600 hover:text-indigo-900 cursor-pointer">{$_('cancel-delete')}</button> {o.contact.middlename || ""}
<button {o.contact.lastname}</a
on:click={() => { >
RunnerOrganizationService.runnerOrganizationControllerRemove(o.id, false) {:else}{$_("no-contact-specified")}{/if}
.then((resp) => { </div>
current_organizations = current_organizations.filter((obj) => obj.id !== o.id); </div>
Toastify({ </div>
text: 'Organization deleted', </td>
duration: 500, {#if active_deletes[o.id] === true}
backgroundColor: <td
'linear-gradient(to right, #00b09b, #96c93d)', class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"
}).showToast(); >
}) <button
.catch((err) => { on:click={() => {
modal_open = true; active_deletes[o.id] = false;
delete_org = o; }}
}); tabindex="0"
}} class="ml-4 text-indigo-600 hover:text-indigo-900 cursor-pointer"
tabindex="0" >{$_("cancel-delete")}</button
class="ml-4 text-red-600 hover:text-red-900 cursor-pointer">{$_('confirm-delete')}</button> >
</td> <button
{:else} on:click={() => {
<td toast.loading($_("deleting-organization"));
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> RunnerOrganizationService.runnerOrganizationControllerRemove(
<a o.id,
href="./{o.id}" false
class="text-indigo-600 hover:text-indigo-900">{$_('details')}</a> )
{#if store.state.jwtinfo.userdetails.permissions.includes('ORGANIZATION:DELETE')} .then((resp) => {
<button current_organizations =
on:click={() => { current_organizations.filter(
active_deletes[o.id] = true; (obj) => obj.id !== o.id
}} );
tabindex="0" toast($_("organization-deleted"));
class="ml-4 text-red-600 hover:text-red-900 cursor-pointer">{$_('delete')}</button> })
{/if} .catch((err) => {
</td> modal_open = true;
{/if} delete_org = o;
</tr> });
{/if} }}
{/each} tabindex="0"
</tbody> class="ml-4 text-red-600 hover:text-red-900 cursor-pointer"
</table> >{$_("confirm-delete")}</button
</div> >
{/if} </td>
{:catch error} {:else}
<div class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500"> <td
<span class="inline-block align-middle mr-8"> class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"
<b class="capitalize">{$_('general_promise_error')}</b> >
{error} <a
</span> href="./{o.id}"
</div> class="text-indigo-600 hover:text-indigo-900"
{/await} >{$_("details")}</a
{/if} >
{#if store.state.jwtinfo.userdetails.permissions.includes("ORGANIZATION:DELETE")}
<button
on:click={() => {
active_deletes[o.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}

View File

@ -11,32 +11,34 @@
<section class="container p-5"> <section class="container p-5">
<span class="mb-1 text-3xl font-extrabold leading-tight"> <span class="mb-1 text-3xl font-extrabold leading-tight">
{$_('organizations')} {$_("organizations")}
{#if store.state.jwtinfo.userdetails.permissions.includes('ORGANIZATION:CREATE')} {#if store.state.jwtinfo.userdetails.permissions.includes("ORGANIZATION:CREATE")}
<button <button
on:click={() => { on:click={() => {
modal_open = true; modal_open = true;
}} }}
type="button" 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"> 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-organization')} >
{$_("create-organization")}
</button> </button>
{/if} {/if}
{#if store.state.jwtinfo.userdetails.permissions.includes('RUNNER:IMPORT')} {#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:IMPORT")}
<button <button
on:click={() => { on:click={() => {
import_modal_open = true; import_modal_open = true;
}} }}
type="button" 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"> 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"
{$_('import-runners')} >
{$_("import-runners")}
</button> </button>
{/if} {/if}
</span> </span>
<OrgOverview bind:current_organizations /> <OrgOverview bind:current_organizations />
</section> </section>
{#if store.state.jwtinfo.userdetails.permissions.includes('ORGANIZATION:CREATE')} {#if store.state.jwtinfo.userdetails.permissions.includes("ORGANIZATION:CREATE")}
<AddOrgModal bind:current_organizations bind:modal_open /> <AddOrgModal bind:current_organizations bind:modal_open />
<ImportRunnerModal <ImportRunnerModal
on:cancelDelete={(event) => { on:cancelDelete={(event) => {
@ -47,5 +49,6 @@
passed_orgs={current_organizations} passed_orgs={current_organizations}
opened_from="OrgOverview" opened_from="OrgOverview"
current_runners={[]} current_runners={[]}
bind:import_modal_open /> bind:import_modal_open
/>
{/if} {/if}

View File

@ -9,9 +9,9 @@
<div class="text-center items-center justify-center"> <div class="text-center items-center justify-center">
<p class="mb-16 text-lg text-gray-500"> <p class="mb-16 text-lg text-gray-500">
<img class="w-full h-44" src={org_empty} alt="" /> <img class="w-full h-44" src={org_empty} alt="" />
<span <span class="font-bold">{$_("there-are-no-organizations-added-yet")}</span
class="font-bold">{$_('there-are-no-organizations-added-yet')}</span><br /> ><br />
<span>{$_('add-your-first-organization')}</span> <span>{$_("add-your-first-organization")}</span>
</p> </p>
</div> </div>

View File

@ -1,418 +1,364 @@
<script> <script>
import { getLocaleFromNavigator, _ } from "svelte-i18n"; import { getLocaleFromNavigator, _ } from "svelte-i18n";
import { import {
RunnerCardService, RunnerCardService,
RunnerOrganizationService, RunnerOrganizationService,
RunnerTeamService, RunnerTeamService,
} from "@odit/lfk-client-js"; } from "@odit/lfk-client-js";
import Toastify from "toastify-js";
import { init } from "@paralleldrive/cuid2"; import { init } from "@paralleldrive/cuid2";
const createId = init({ length: 10, fingerprint: "lfk-frontend" }); const createId = init({ length: 10, fingerprint: "lfk-frontend" });
export let cards_show = false; export let cards_show = false;
export let generate_cards = []; export let generate_cards = [];
export let generate_runners = []; export let generate_runners = [];
export let generate_orgs = []; export let generate_orgs = [];
export let generate_teams = []; export let generate_teams = [];
$: cards_dropdown_open = false; $: cards_dropdown_open = false;
document.addEventListener("click", function (e) { document.addEventListener("click", function (e) {
if ( if (
e.target.parentNode?.parentNode?.id != "cards:dropdown" && e.target.parentNode?.parentNode?.id != "cards:dropdown" &&
e.target.parentNode?.parentNode?.id != "cards:dropdown:menu" e.target.parentNode?.parentNode?.id != "cards:dropdown:menu"
) { ) {
cards_dropdown_open = false; cards_dropdown_open = false;
} }
}); });
function generateRunnerCards(locale) { function generateRunnerCards(locale) {
cards_dropdown_open = false; cards_dropdown_open = false;
if (generate_orgs.length > 0) { if (generate_orgs.length > 0) {
generateOrgCards(locale); generateOrgCards(locale);
} else if (generate_teams.length > 0) { } else if (generate_teams.length > 0) {
generateTeamCards(locale); generateTeamCards(locale);
} else if (generate_runners.length > 0) { } else if (generate_runners.length > 0) {
generateRunnersCards(locale); generateRunnersCards(locale);
} else { } else {
generateCards(locale); generateCards(locale);
} }
} }
function generateCards(locale) { function generateCards(locale) {
const toast = Toastify({ toast.loading($_("generating-pdf"));
text: $_("generating-pdf"), fetch(
duration: -1, `${config.baseurl_documentserver}/cards?locale=${locale}&download=true&key=${config.documentserver_key}`,
}).showToast(); {
fetch( method: "POST",
`${config.baseurl_documentserver}/cards?locale=${locale}&download=true&key=${config.documentserver_key}`, headers: {
{ "Content-Type": "application/json",
method: "POST", },
headers: { body: JSON.stringify(generate_cards),
"Content-Type": "application/json", }
}, )
body: JSON.stringify(generate_cards), .then((response) => {
} if (response.status != "200") {
) toast.dismiss();
.then((response) => { toast.error($_("pdf-generation-failed"));
if (response.status != "200") { } else {
toast.hideToast(); return response.blob();
Toastify({ }
text: $_("pdf-generation-failed"), })
duration: 3500, .then((blob) => {
backgroundColor: const url = window.URL.createObjectURL(blob);
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)", let a = document.createElement("a");
}).showToast(); a.href = url;
} else { a.download = `${$_("runnercards")}-${locale}-${createId()}.pdf`;
return response.blob(); document.body.appendChild(a);
} a.click();
}) a.remove();
.then((blob) => { toast.dismiss();
const url = window.URL.createObjectURL(blob); toast($_("pdf-successfully-generated"));
let a = document.createElement("a"); })
a.href = url; .catch((err) => {
a.download = `${$_("runnercards")}-${locale}-${createId()}.pdf`; console.error(err);
document.body.appendChild(a); });
a.click(); }
a.remove();
toast.hideToast(); async function generateRunnersCards(locale) {
Toastify({ toast.loading($_("generating-pdf"));
text: $_("pdf-successfully-generated"), const current_cards = await RunnerCardService.runnerCardControllerGetAll();
duration: 3500, let cards = [];
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", for (let runner of generate_runners) {
}).showToast(); let card = current_cards.find((c) => c.runner?.id == runner.id);
}) if (!card) {
.catch((err) => { card = await RunnerCardService.runnerCardControllerPost({
console.error(err); runner: runner.id,
}); });
} }
cards.push(card);
async function generateRunnersCards(locale) { }
const toast = Toastify({ fetch(
text: $_("generating-pdf"), `${config.baseurl_documentserver}/cards?locale=${locale}&download=true&key=${config.documentserver_key}`,
duration: -1, {
}).showToast(); method: "POST",
const current_cards = await RunnerCardService.runnerCardControllerGetAll(); headers: {
let cards = []; "Content-Type": "application/json",
for (let runner of generate_runners) { },
let card = current_cards.find((c) => c.runner?.id == runner.id); body: JSON.stringify(cards),
if (!card) { }
card = await RunnerCardService.runnerCardControllerPost({ )
runner: runner.id, .then((response) => {
}); if (response.status != "200") {
} toast.dismiss();
cards.push(card); toast.error($_("pdf-generation-failed"));
} } else {
fetch( return response.blob();
`${config.baseurl_documentserver}/cards?locale=${locale}&download=true&key=${config.documentserver_key}`, }
{ })
method: "POST", .then((blob) => {
headers: { const url = window.URL.createObjectURL(blob);
"Content-Type": "application/json", let a = document.createElement("a");
}, a.href = url;
body: JSON.stringify(cards), if (generate_runners.length == 1) {
} a.download = `${$_("runnercards")}_${generate_runners[0].firstname}_${
) generate_runners[0].lastname
.then((response) => { }-${locale}-${createId()}.pdf`;
if (response.status != "200") { } else {
toast.hideToast(); a.download = `${$_("runnercards")}-${locale}-${createId()}.pdf`;
Toastify({ }
text: $_("pdf-generation-failed"), document.body.appendChild(a);
duration: 3500, a.click();
backgroundColor: a.remove();
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)", toast.dismiss();
}).showToast(); toast($_("pdf-successfully-generated"));
} else { })
return response.blob(); .catch((err) => {});
} }
})
.then((blob) => { async function generateTeamCards(locale) {
const url = window.URL.createObjectURL(blob); toast.loading($_("generating-pdfs"));
let a = document.createElement("a"); let count = 0;
a.href = url; const current_cards = await RunnerCardService.runnerCardControllerGetAll();
if (generate_runners.length == 1) { for (const t of generate_teams) {
a.download = `${$_("runnercards")}_${generate_runners[0].firstname}_${ const runners = await RunnerTeamService.runnerTeamControllerGetRunners(
generate_runners[0].lastname t.id
}-${locale}-${createId()}.pdf`; );
} else { let cards = [];
a.download = `${$_("runnercards")}-${locale}-${createId()}.pdf`; for (let runner of runners) {
} let card = current_cards.find((c) => c.runner?.id == runner.id);
document.body.appendChild(a); if (!card) {
a.click(); card = await RunnerCardService.runnerCardControllerPost({
a.remove(); runner: runner.id,
toast.hideToast(); });
Toastify({ }
text: $_("pdf-successfully-generated"), cards.push(card);
duration: 3500, }
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", fetch(
}).showToast(); `${config.baseurl_documentserver}/cards?locale=${locale}&download=true&key=${config.documentserver_key}`,
}) {
.catch((err) => {}); method: "POST",
} headers: {
"Content-Type": "application/json",
async function generateTeamCards(locale) { },
const toast = Toastify({ body: JSON.stringify(cards),
text: $_("generating-pdfs"), }
duration: -1, )
}).showToast(); .then((response) => {
let count = 0; if (response.status != "200") {
const current_cards = await RunnerCardService.runnerCardControllerGetAll(); toast.dismiss();
for (const t of generate_teams) { toast.error($_("pdf-generation-failed"));
const runners = await RunnerTeamService.runnerTeamControllerGetRunners( } else {
t.id return response.blob();
); }
let cards = []; })
for (let runner of runners) { .then((blob) => {
let card = current_cards.find((c) => c.runner?.id == runner.id); count++;
if (!card) { const url = window.URL.createObjectURL(blob);
card = await RunnerCardService.runnerCardControllerPost({ let a = document.createElement("a");
runner: runner.id, a.href = url;
}); a.download = `${$_("runnercards")}_${
} t.name
cards.push(card); }-${locale}-${createId()}.pdf`;
} document.body.appendChild(a);
fetch( a.click();
`${config.baseurl_documentserver}/cards?locale=${locale}&download=true&key=${config.documentserver_key}`, a.remove();
{ if (count === generate_teams.length) {
method: "POST", toast.dismiss();
headers: { toast.success($_("pdfs-successfully-generated"));
"Content-Type": "application/json", }
}, })
body: JSON.stringify(cards), .catch((err) => {});
} }
) }
.then((response) => {
if (response.status != "200") { async function generateOrgCards(locale) {
toast.hideToast(); toast.loading($_("generating-pdfs"));
Toastify({ const current_cards = await RunnerCardService.runnerCardControllerGetAll();
text: $_("pdf-generation-failed"), let count = 0;
duration: 3500, let count_orgs = 0;
backgroundColor: for (const o of generate_orgs) {
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)", count_orgs++;
}).showToast(); let count = 0;
} else { let runners =
return response.blob(); await RunnerOrganizationService.runnerOrganizationControllerGetRunners(
} o.id,
}) true
.then((blob) => { );
count++; let cards = [];
const url = window.URL.createObjectURL(blob); for (let runner of runners) {
let a = document.createElement("a"); let card = current_cards.find((c) => c.runner?.id == runner.id);
a.href = url; if (!card) {
a.download = `${$_("runnercards")}_${t.name}-${locale}-${createId()}.pdf`; card = await RunnerCardService.runnerCardControllerPost({
document.body.appendChild(a); runner: runner.id,
a.click(); });
a.remove(); }
if (count === generate_teams.length) { cards.push(card);
toast.hideToast(); }
Toastify({ await fetch(
text: $_("pdfs-successfully-generated"), `${config.baseurl_documentserver}/cards?locale=${locale}&download=true&key=${config.documentserver_key}`,
duration: 3500, {
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", method: "POST",
}).showToast(); headers: {
} "Content-Type": "application/json",
}) },
.catch((err) => {}); body: JSON.stringify(cards),
} }
} )
.then((response) => {
async function generateOrgCards(locale) { if (response.status != "200") {
const toast = Toastify({ toast.dismiss();
text: $_("generating-pdfs"), toast.error($_("pdf-generation-failed"));
duration: -1, } else {
}).showToast(); return response.blob();
const current_cards = await RunnerCardService.runnerCardControllerGetAll(); }
let count = 0; })
let count_orgs = 0; .then((blob) => {
for (const o of generate_orgs) { const url = window.URL.createObjectURL(blob);
count_orgs++; let a = document.createElement("a");
let count = 0; a.href = url;
let runners = a.download = `${$_("runnercards")}_${
await RunnerOrganizationService.runnerOrganizationControllerGetRunners( o.name
o.id, }_direct-${locale}-${createId()}.pdf`;
true document.body.appendChild(a);
); a.click();
let cards = []; a.remove();
for (let runner of runners) { if (count === o.teams.length && count_orgs === generate_orgs.length) {
let card = current_cards.find((c) => c.runner?.id == runner.id); toast.dismiss();
if (!card) { toast.success($_("pdfs-successfully-generated"));
card = await RunnerCardService.runnerCardControllerPost({ }
runner: runner.id, })
}); .catch((err) => {});
} for (const t of o.teams) {
cards.push(card); count++;
} let runners = await RunnerTeamService.runnerTeamControllerGetRunners(
await fetch( t.id
`${config.baseurl_documentserver}/cards?locale=${locale}&download=true&key=${config.documentserver_key}`, );
{ let cards = [];
method: "POST", for (let runner of runners) {
headers: { let card = current_cards.find((c) => c.runner?.id == runner.id);
"Content-Type": "application/json", if (!card) {
}, card = await RunnerCardService.runnerCardControllerPost({
body: JSON.stringify(cards), runner: runner.id,
} });
) }
.then((response) => { cards.push(card);
if (response.status != "200") { }
toast.hideToast(); await fetch(
Toastify({ `${config.baseurl_documentserver}/cards?locale=${locale}&download=true&key=${config.documentserver_key}`,
text: $_("pdf-generation-failed"), {
duration: 3500, method: "POST",
backgroundColor: headers: {
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)", "Content-Type": "application/json",
}).showToast(); },
} else { body: JSON.stringify(cards),
return response.blob(); }
} )
}) .then((response) => {
.then((blob) => { if (response.status != "200") {
const url = window.URL.createObjectURL(blob); toast.dismiss();
let a = document.createElement("a"); toast.error($_("pdf-generation-failed"));
a.href = url; } else {
a.download = `${$_("runnercards")}_${o.name}_direct-${locale}-${createId()}.pdf`; return response.blob();
document.body.appendChild(a); }
a.click(); })
a.remove(); .then((blob) => {
if (count === o.teams.length && count_orgs === generate_orgs.length) { const url = window.URL.createObjectURL(blob);
toast.hideToast(); let a = document.createElement("a");
console.log("here"); a.href = url;
Toastify({ a.download = `${$_("runnercards")}_${o.name}_${
text: $_("pdfs-successfully-generated"), t.name
duration: 3500, }-${locale}-${createId()}.pdf`;
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", document.body.appendChild(a);
}).showToast(); a.click();
} a.remove();
}) if (
.catch((err) => {}); count === o.teams.length &&
for (const t of o.teams) { count_orgs === generate_orgs.length
count++; ) {
let runners = await RunnerTeamService.runnerTeamControllerGetRunners( toast.dismiss();
t.id toast($_("pdfs-successfully-generated"));
); }
let cards = []; })
for (let runner of runners) { .catch((err) => {});
let card = current_cards.find((c) => c.runner?.id == runner.id); }
if (!card) { }
card = await RunnerCardService.runnerCardControllerPost({ }
runner: runner.id, </script>
});
} {#if cards_show}
cards.push(card); <div id="cards:dropdown" class="relative inline-block">
} <div>
await fetch( <button
`${config.baseurl_documentserver}/cards?locale=${locale}&download=true&key=${config.documentserver_key}`, on:click={() => {
{ cards_dropdown_open = !cards_dropdown_open;
method: "POST", }}
headers: { type="button"
"Content-Type": "application/json", class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-gray-600 text-base font-medium text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 sm:ml-3 sm:w-auto sm:text-sm inline-flex"
}, id="options-menu"
body: JSON.stringify(cards), aria-haspopup="true"
} aria-expanded="true"
) >
.then((response) => { {$_("generate-runnercards")}
if (response.status != "200") { <svg
toast.hideToast(); xmlns="http://www.w3.org/2000/svg"
Toastify({ width="24"
text: $_("pdf-generation-failed"), height="24"
duration: 3500, viewBox="0 0 24 24"
backgroundColor: class="-mr-1 ml-2 h-5 w-5"
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)", ><path fill="none" d="M0 0h24v24H0z" />
}).showToast(); <path
} else { fill="currentColor"
return response.blob(); d="M3 19h18v2H3v-2zm10-5.83l6.07-6.07 1.42 1.41L12 17 3.52 8.52l1.4-1.42L11 13.17V2h2v11.17z"
} /></svg
}) >
.then((blob) => { </button>
const url = window.URL.createObjectURL(blob); </div>
let a = document.createElement("a"); {#if cards_dropdown_open}
a.href = url; <div
a.download = `${$_("runnercards")}_${o.name}_${ 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"
t.name id="cards:dropdown:menu"
}-${locale}-${createId()}.pdf`; >
document.body.appendChild(a); <div
a.click(); class="py-1"
a.remove(); role="menu"
if ( aria-orientation="vertical"
count === o.teams.length && aria-labelledby="options-menu"
count_orgs === generate_orgs.length >
) { <span class="block w-full text-left px-4 py-2 text-sm text-gray-700"
toast.hideToast(); >{$_("select-language")}</span
Toastify({ >
text: $_("pdfs-successfully-generated"), <button
duration: 3500, on:click={() => {
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", generateRunnerCards("de");
}).showToast(); }}
} type="submit"
}) class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
.catch((err) => {}); role="menuitem"
} >
} {$_("german")}
} </button>
</script> <button
on:click={() => {
{#if cards_show} generateRunnerCards("en");
<div id="cards:dropdown" class="relative inline-block"> }}
<div> type="submit"
<button class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
on:click={() => { role="menuitem"
cards_dropdown_open = !cards_dropdown_open; >
}} {$_("english")}
type="button" </button>
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-gray-600 text-base font-medium text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 sm:ml-3 sm:w-auto sm:text-sm inline-flex" </div>
id="options-menu" </div>
aria-haspopup="true" {/if}
aria-expanded="true" </div>
> {/if}
{$_("generate-runnercards")}
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
class="-mr-1 ml-2 h-5 w-5"
><path fill="none" d="M0 0h24v24H0z" />
<path
fill="currentColor"
d="M3 19h18v2H3v-2zm10-5.83l6.07-6.07 1.42 1.41L12 17 3.52 8.52l1.4-1.42L11 13.17V2h2v11.17z"
/></svg
>
</button>
</div>
{#if cards_dropdown_open}
<div
class="origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 z-10"
id="cards:dropdown:menu"
>
<div
class="py-1"
role="menu"
aria-orientation="vertical"
aria-labelledby="options-menu"
>
<span class="block w-full text-left px-4 py-2 text-sm text-gray-700"
>{$_("select-language")}</span
>
<button
on:click={() => {
generateRunnerCards("de");
}}
type="submit"
class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
role="menuitem"
>
{$_("german")}
</button>
<button
on:click={() => {
generateRunnerCards("en");
}}
type="submit"
class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
role="menuitem"
>
{$_("english")}
</button>
</div>
</div>
{/if}
</div>
{/if}

View File

@ -1,355 +1,312 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import { import {
DonationService, DonationService,
RunnerTeamService, RunnerTeamService,
RunnerOrganizationService, RunnerOrganizationService,
} from "@odit/lfk-client-js"; } from "@odit/lfk-client-js";
import Toastify from "toastify-js";
import { init } from "@paralleldrive/cuid2"; import { init } from "@paralleldrive/cuid2";
const createId = init({ length: 10, fingerprint: "lfk-frontend" }); const createId = init({ length: 10, fingerprint: "lfk-frontend" });
export let certificates_show = false; export let certificates_show = false;
export let generate_runners = []; export let generate_runners = [];
export let generate_orgs = []; export let generate_orgs = [];
export let generate_teams = []; export let generate_teams = [];
$: certificates_dropdown_open = false; $: certificates_dropdown_open = false;
document.addEventListener("click", function (e) { document.addEventListener("click", function (e) {
if ( if (
e.target.parentNode?.parentNode?.id != "certificates:dropdown" && e.target.parentNode?.parentNode?.id != "certificates:dropdown" &&
e.target.parentNode?.parentNode?.id != "certificates:dropdown:menu" e.target.parentNode?.parentNode?.id != "certificates:dropdown:menu"
) { ) {
certificates_dropdown_open = false; certificates_dropdown_open = false;
} }
}); });
function generateCertificates(locale) { function generateCertificates(locale) {
certificates_dropdown_open = false; certificates_dropdown_open = false;
if (generate_orgs.length > 0) { if (generate_orgs.length > 0) {
generateOrgCertificates(locale); generateOrgCertificates(locale);
} else if (generate_teams.length > 0) { } else if (generate_teams.length > 0) {
generateTeamCertificates(locale); generateTeamCertificates(locale);
} else { } else {
generateRunnerCertificates(locale); generateRunnerCertificates(locale);
} }
} }
async function generateRunnerCertificates(locale) { async function generateRunnerCertificates(locale) {
const toast = Toastify({ toast.loading($_("generating-pdf"));
text: $_("generating-pdf"), const current_donations =
duration: -1, (await DonationService.donationControllerGetAll()) || [];
}).showToast(); let certificateRunners = [];
const current_donations = for (let runner of generate_runners) {
(await DonationService.donationControllerGetAll()) || []; runner.distanceDonations =
let certificateRunners = []; current_donations.filter((d) => d.runner?.id == runner.id) || [];
for (let runner of generate_runners) { certificateRunners.push(runner);
runner.distanceDonations = }
current_donations.filter((d) => d.runner?.id == runner.id) || []; fetch(
console.log(runner.distanceDonations); `${config.baseurl_documentserver}/certificates?locale=${locale}&download=true&key=${config.documentserver_key}`,
certificateRunners.push(runner); {
} method: "POST",
fetch( headers: {
`${config.baseurl_documentserver}/certificates?locale=${locale}&download=true&key=${config.documentserver_key}`, "Content-Type": "application/json",
{ },
method: "POST", body: JSON.stringify(certificateRunners),
headers: { }
"Content-Type": "application/json", )
}, .then((response) => {
body: JSON.stringify(certificateRunners), if (response.status != "200") {
} toast.dismiss();
) toast.error($_("pdf-generation-failed"));
.then((response) => { } else {
if (response.status != "200") { return response.blob();
toast.hideToast(); }
Toastify({ })
text: $_("pdf-generation-failed"), .then((blob) => {
duration: 3500, const url = window.URL.createObjectURL(blob);
backgroundColor: let a = document.createElement("a");
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)", a.href = url;
}).showToast(); if (generate_runners.length == 1) {
} else { a.download = `${$_("certificates")}_${
return response.blob(); generate_runners[0].firstname
} }_${generate_runners[0].lastname}-${locale}-${createId()}.pdf`;
}) } else {
.then((blob) => { a.download = `${$_("certificates")}-${locale}.pdf`;
const url = window.URL.createObjectURL(blob); }
let a = document.createElement("a"); document.body.appendChild(a);
a.href = url; a.click();
if (generate_runners.length == 1) { a.remove();
a.download = `${$_("certificates")}_${ toast.dismiss();
generate_runners[0].firstname toast($_("pdf-successfully-generated"));
}_${generate_runners[0].lastname}-${locale}-${createId()}.pdf`; })
} else { .catch((err) => {});
a.download = `${$_("certificates")}-${locale}.pdf`; }
}
document.body.appendChild(a); async function generateTeamCertificates(locale) {
a.click(); toast.loading($_("generating-pdfs"));
a.remove(); let count = 0;
toast.hideToast(); const current_donations =
Toastify({ (await DonationService.donationControllerGetAll()) || [];
text: $_("pdf-successfully-generated"), for (const t of generate_teams) {
duration: 3500, const runners = await RunnerTeamService.runnerTeamControllerGetRunners(
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", t.id
}).showToast(); );
}) let certificateRunners = [];
.catch((err) => {}); for (let runner of runners) {
} runner.distanceDonations =
current_donations.filter((d) => d.runner?.id == runner.id) || [];
async function generateTeamCertificates(locale) { certificateRunners.push(runner);
const toast = Toastify({ }
text: $_("generating-pdfs"), fetch(
duration: -1, `${config.baseurl_documentserver}/certificates?locale=${locale}&download=true&key=${config.documentserver_key}`,
}).showToast(); {
let count = 0; method: "POST",
const current_donations = headers: {
(await DonationService.donationControllerGetAll()) || []; "Content-Type": "application/json",
for (const t of generate_teams) { },
const runners = await RunnerTeamService.runnerTeamControllerGetRunners( body: JSON.stringify(certificateRunners),
t.id }
); )
let certificateRunners = []; .then((response) => {
for (let runner of runners) { if (response.status != "200") {
runner.distanceDonations = toast.dismiss();
current_donations.filter((d) => d.runner?.id == runner.id) || []; toast.error($_("pdf-generation-failed"));
certificateRunners.push(runner); } else {
} return response.blob();
fetch( }
`${config.baseurl_documentserver}/certificates?locale=${locale}&download=true&key=${config.documentserver_key}`, })
{ .then((blob) => {
method: "POST", count++;
headers: { const url = window.URL.createObjectURL(blob);
"Content-Type": "application/json", let a = document.createElement("a");
}, a.href = url;
body: JSON.stringify(certificateRunners), a.download = `${$_("certificates")}_${
} t.name
) }-${locale}-${createId()}.pdf`;
.then((response) => { document.body.appendChild(a);
if (response.status != "200") { a.click();
toast.hideToast(); a.remove();
Toastify({ if (count === generate_teams.length) {
text: $_("pdf-generation-failed"), toast.dismiss();
duration: 3500, toast.success($_("pdfs-successfully-generated"));
backgroundColor: }
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)", })
}).showToast(); .catch((err) => {});
} else { }
return response.blob(); }
}
}) async function generateOrgCertificates(locale) {
.then((blob) => { toast.loading($_("generating-pdfs"));
count++; const current_donations =
const url = window.URL.createObjectURL(blob); (await DonationService.donationControllerGetAll()) || [];
let a = document.createElement("a"); let count = 0;
a.href = url; let count_orgs = 0;
a.download = `${$_("certificates")}_${t.name}-${locale}-${createId()}.pdf`; for (const o of generate_orgs) {
document.body.appendChild(a); count_orgs++;
a.click(); let count = 0;
a.remove(); let runners =
if (count === generate_teams.length) { await RunnerOrganizationService.runnerOrganizationControllerGetRunners(
toast.hideToast(); o.id,
Toastify({ true
text: $_("pdfs-successfully-generated"), );
duration: 3500, let certificateRunners = [];
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", for (let runner of runners) {
}).showToast(); runner.distanceDonations =
} current_donations.filter((d) => d.runner?.id == runner.id) || [];
}) certificateRunners.push(runner);
.catch((err) => {}); }
} await fetch(
} `${config.baseurl_documentserver}/certificates?locale=${locale}&download=true&key=${config.documentserver_key}`,
{
async function generateOrgCertificates(locale) { method: "POST",
const toast = Toastify({ headers: {
text: $_("generating-pdfs"), "Content-Type": "application/json",
duration: -1, },
}).showToast(); body: JSON.stringify(certificateRunners),
const current_donations = }
(await DonationService.donationControllerGetAll()) || []; )
let count = 0; .then((response) => {
let count_orgs = 0; if (response.status != "200") {
for (const o of generate_orgs) { toast.dismiss();
count_orgs++; toast.error($_("pdf-generation-failed"));
let count = 0; } else {
let runners = return response.blob();
await RunnerOrganizationService.runnerOrganizationControllerGetRunners( }
o.id, })
true .then((blob) => {
); const url = window.URL.createObjectURL(blob);
let certificateRunners = []; let a = document.createElement("a");
for (let runner of runners) { a.href = url;
runner.distanceDonations = a.download = `${$_("certificates")}_${
current_donations.filter((d) => d.runner?.id == runner.id) || []; o.name
certificateRunners.push(runner); }-${locale}-${createId()}.pdf`;
} document.body.appendChild(a);
await fetch( a.click();
`${config.baseurl_documentserver}/certificates?locale=${locale}&download=true&key=${config.documentserver_key}`, a.remove();
{ if (count === o.teams.length && count_orgs === generate_orgs.length) {
method: "POST", toast.dismiss();
headers: { toast.success($_("pdfs-successfully-generated"));
"Content-Type": "application/json", }
}, })
body: JSON.stringify(certificateRunners), .catch((err) => {});
} for (const t of o.teams) {
) count++;
.then((response) => { let runners = await RunnerTeamService.runnerTeamControllerGetRunners(
if (response.status != "200") { t.id
toast.hideToast(); );
Toastify({ let certificateRunners = [];
text: $_("pdf-generation-failed"), for (let runner of runners) {
duration: 3500, runner.distanceDonations =
backgroundColor: current_donations.filter((d) => d.runner?.id == runner.id) || [];
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)", certificateRunners.push(runner);
}).showToast(); }
} else { await fetch(
return response.blob(); `${config.baseurl_documentserver}/certificates?locale=${locale}&download=true&key=${config.documentserver_key}`,
} {
}) method: "POST",
.then((blob) => { headers: {
const url = window.URL.createObjectURL(blob); "Content-Type": "application/json",
let a = document.createElement("a"); },
a.href = url; body: JSON.stringify(certificateRunners),
a.download = `${$_("certificates")}_${o.name}-${locale}-${createId()}.pdf`; }
document.body.appendChild(a); )
a.click(); .then((response) => {
a.remove(); if (response.status != "200") {
if (count === o.teams.length && count_orgs === generate_orgs.length) { toast.dismiss();
toast.hideToast(); toast.error($_("pdf-generation-failed"));
console.log("here"); } else {
Toastify({ return response.blob();
text: $_("pdfs-successfully-generated"), }
duration: 3500, })
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", .then((blob) => {
}).showToast(); const url = window.URL.createObjectURL(blob);
} let a = document.createElement("a");
}) a.href = url;
.catch((err) => {}); a.download = `${$_("certificates")}_${o.name}_${
for (const t of o.teams) { t.name
count++; }-${locale}-${createId()}.pdf`;
let runners = await RunnerTeamService.runnerTeamControllerGetRunners( document.body.appendChild(a);
t.id a.click();
); a.remove();
let certificateRunners = []; if (
for (let runner of runners) { count === o.teams.length &&
runner.distanceDonations = count_orgs === generate_orgs.length
current_donations.filter((d) => d.runner?.id == runner.id) || []; ) {
certificateRunners.push(runner); toast.dismiss();
} toast($_("pdfs-successfully-generated"));
await fetch( }
`${config.baseurl_documentserver}/certificates?locale=${locale}&download=true&key=${config.documentserver_key}`, })
{ .catch((err) => {});
method: "POST", }
headers: { }
"Content-Type": "application/json", }
}, </script>
body: JSON.stringify(certificateRunners),
} {#if certificates_show}
) <div id="certificates:dropdown" class="relative inline-block">
.then((response) => { <div>
if (response.status != "200") { <button
toast.hideToast(); on:click={() => {
Toastify({ certificates_dropdown_open = !certificates_dropdown_open;
text: $_("pdf-generation-failed"), }}
duration: 3500, type="button"
backgroundColor: class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-gray-600 text-base font-medium text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 sm:ml-3 sm:w-auto sm:text-sm inline-flex"
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)", id="options-menu"
}).showToast(); aria-haspopup="true"
} else { aria-expanded="true"
return response.blob(); >
} {$_("generate-runner-certificates")}
}) <svg
.then((blob) => { xmlns="http://www.w3.org/2000/svg"
const url = window.URL.createObjectURL(blob); width="24"
let a = document.createElement("a"); height="24"
a.href = url; viewBox="0 0 24 24"
a.download = `${$_("certificates")}_${o.name}_${ class="-mr-1 ml-2 h-5 w-5"
t.name ><path fill="none" d="M0 0h24v24H0z" />
}-${locale}-${createId()}.pdf`; <path
document.body.appendChild(a); fill="currentColor"
a.click(); d="M3 19h18v2H3v-2zm10-5.83l6.07-6.07 1.42 1.41L12 17 3.52 8.52l1.4-1.42L11 13.17V2h2v11.17z"
a.remove(); /></svg
if ( >
count === o.teams.length && </button>
count_orgs === generate_orgs.length </div>
) { {#if certificates_dropdown_open}
toast.hideToast(); <div
Toastify({ 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"
text: $_("pdfs-successfully-generated"), id="certificates:dropdown:menu"
duration: 3500, >
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", <div
}).showToast(); class="py-1"
} role="menu"
}) aria-orientation="vertical"
.catch((err) => {}); aria-labelledby="options-menu"
} >
} <span class="block w-full text-left px-4 py-2 text-sm text-gray-700"
} >{$_("select-language")}</span
</script> >
<button
{#if certificates_show} on:click={() => {
<div id="certificates:dropdown" class="relative inline-block"> generateCertificates("de");
<div> }}
<button type="submit"
on:click={() => { class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
certificates_dropdown_open = !certificates_dropdown_open; role="menuitem"
}} >
type="button" {$_("german")}
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-gray-600 text-base font-medium text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 sm:ml-3 sm:w-auto sm:text-sm inline-flex" </button>
id="options-menu" <button
aria-haspopup="true" on:click={() => {
aria-expanded="true" generateCertificates("en");
> }}
{$_("generate-runner-certificates")} type="submit"
<svg class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
xmlns="http://www.w3.org/2000/svg" role="menuitem"
width="24" >
height="24" {$_("english")}
viewBox="0 0 24 24" </button>
class="-mr-1 ml-2 h-5 w-5" </div>
><path fill="none" d="M0 0h24v24H0z" /> </div>
<path {/if}
fill="currentColor" </div>
d="M3 19h18v2H3v-2zm10-5.83l6.07-6.07 1.42 1.41L12 17 3.52 8.52l1.4-1.42L11 13.17V2h2v11.17z" {/if}
/></svg
>
</button>
</div>
{#if certificates_dropdown_open}
<div
class="origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 z-10"
id="certificates:dropdown:menu"
>
<div
class="py-1"
role="menu"
aria-orientation="vertical"
aria-labelledby="options-menu"
>
<span class="block w-full text-left px-4 py-2 text-sm text-gray-700"
>{$_("select-language")}</span
>
<button
on:click={() => {
generateCertificates("de");
}}
type="submit"
class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
role="menuitem"
>
{$_("german")}
</button>
<button
on:click={() => {
generateCertificates("en");
}}
type="submit"
class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
role="menuitem"
>
{$_("english")}
</button>
</div>
</div>
{/if}
</div>
{/if}

View File

@ -1,324 +1,283 @@
<script> <script>
import { getLocaleFromNavigator, _ } from "svelte-i18n"; import { getLocaleFromNavigator, _ } from "svelte-i18n";
import { import {
RunnerOrganizationService, RunnerOrganizationService,
RunnerTeamService, RunnerTeamService,
} from "@odit/lfk-client-js"; } from "@odit/lfk-client-js";
import Toastify from "toastify-js";
import { init } from "@paralleldrive/cuid2"; import { init } from "@paralleldrive/cuid2";
const createId = init({ length: 10, fingerprint: "lfk-frontend" }); import toast from "svelte-french-toast";
const createId = init({ length: 10, fingerprint: "lfk-frontend" });
export let sponsoring_contracts_show = false;
export let generate_runners = []; export let sponsoring_contracts_show = false;
export let generate_orgs = []; export let generate_runners = [];
export let generate_teams = []; export let generate_orgs = [];
$: sponsoring_contracts_download_open = false; export let generate_teams = [];
document.addEventListener("click", function (e) { $: sponsoring_contracts_download_open = false;
if ( document.addEventListener("click", function (e) {
e.target.parentNode?.parentNode?.id != "sponsoring:dropdown" && if (
e.target.parentNode?.parentNode?.id != "sponsoring:dropdown:menu" e.target.parentNode?.parentNode?.id != "sponsoring:dropdown" &&
) { e.target.parentNode?.parentNode?.id != "sponsoring:dropdown:menu"
sponsoring_contracts_download_open = false; ) {
} sponsoring_contracts_download_open = false;
}); }
});
function generateSponsoringContract(locale) {
sponsoring_contracts_download_open = false; function generateSponsoringContract(locale) {
sponsoring_contracts_download_open = false;
if (generate_orgs.length > 0) {
generateOrgContracts(locale); if (generate_orgs.length > 0) {
} else if (generate_teams.length > 0) { generateOrgContracts(locale);
generateTeamContracts(locale); } else if (generate_teams.length > 0) {
} else { generateTeamContracts(locale);
generateRunnerContracts(locale); } else {
} generateRunnerContracts(locale);
} }
}
async function generateTeamContracts(locale) {
const toast = Toastify({ async function generateTeamContracts(locale) {
text: $_("generating-pdfs"), toast.loading($_("generating-pdfs"));
duration: -1, let count = 0;
}).showToast(); for (const t of generate_teams) {
let count = 0; count++;
for (const t of generate_teams) { const runners = await RunnerTeamService.runnerTeamControllerGetRunners(
count++; t.id
const runners = await RunnerTeamService.runnerTeamControllerGetRunners( );
t.id fetch(
); `${config.baseurl_documentserver}/contracts?locale=${locale}&download=true&key=${config.documentserver_key}`,
fetch( {
`${config.baseurl_documentserver}/contracts?locale=${locale}&download=true&key=${config.documentserver_key}`, method: "POST",
{ headers: {
method: "POST", "Content-Type": "application/json",
headers: { },
"Content-Type": "application/json", body: JSON.stringify(runners),
}, }
body: JSON.stringify(runners), )
} .then((response) => {
) if (response.status != "200") {
.then((response) => { toast.dismiss();
if (response.status != "200") { toast.error($_("pdf-generation-failed"));
toast.hideToast(); } else {
Toastify({ return response.blob();
text: $_("pdf-generation-failed"), }
duration: 3500, })
backgroundColor: .then((blob) => {
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)", const url = window.URL.createObjectURL(blob);
}).showToast(); let a = document.createElement("a");
} else { a.href = url;
return response.blob(); a.download = `${$_("sponsorings")}_${
} t.name
}) }-${locale}-${createId()}.pdf`;
.then((blob) => { document.body.appendChild(a);
const url = window.URL.createObjectURL(blob); a.click();
let a = document.createElement("a"); a.remove();
a.href = url; if (count === generate_teams.length) {
a.download = `${$_("sponsorings")}_${t.name}-${locale}-${createId()}.pdf`; toast.dismiss();
document.body.appendChild(a); toast.success($_("pdfs-successfully-generated"));
a.click(); }
a.remove(); })
if (count === generate_teams.length) { .catch((err) => {});
toast.hideToast(); }
Toastify({ }
text: $_("pdfs-successfully-generated"),
duration: 3500, async function generateOrgContracts(locale) {
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", toast.loading($_("generating-pdf"));
}).showToast(); let count_orgs = 0;
} for (const o of generate_orgs) {
}) count_orgs++;
.catch((err) => {}); let count = 0;
} let runners =
} await RunnerOrganizationService.runnerOrganizationControllerGetRunners(
o.id,
async function generateOrgContracts(locale) { true
const toast = Toastify({ );
text: $_("generating-pdf"), await fetch(
duration: -1, `${config.baseurl_documentserver}/contracts?locale=${locale}&download=true&key=${config.documentserver_key}`,
}).showToast(); {
let count_orgs = 0; method: "POST",
for (const o of generate_orgs) { headers: {
count_orgs++; "Content-Type": "application/json",
let count = 0; },
let runners = body: JSON.stringify(runners),
await RunnerOrganizationService.runnerOrganizationControllerGetRunners( }
o.id, )
true .then((response) => {
); if (response.status != "200") {
await fetch( toast.dismiss();
`${config.baseurl_documentserver}/contracts?locale=${locale}&download=true&key=${config.documentserver_key}`, toast.error($_("pdf-generation-failed"));
{ } else {
method: "POST", return response.blob();
headers: { }
"Content-Type": "application/json", })
}, .then((blob) => {
body: JSON.stringify(runners), const url = window.URL.createObjectURL(blob);
} let a = document.createElement("a");
) a.href = url;
.then((response) => { a.download = `${$_("sponsorings")}_${
if (response.status != "200") { o.name
toast.hideToast(); }_direct-${locale}-${createId()}.pdf`;
Toastify({ document.body.appendChild(a);
text: $_("pdf-generation-failed"), a.click();
duration: 3500, a.remove();
backgroundColor: if (count === o.teams.length && count_orgs === generate_orgs.length) {
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)", toast.dismiss();
}).showToast(); toast.success($_("pdfs-successfully-generated"));
} else { }
return response.blob(); })
} .catch((err) => {});
}) for (const t of o.teams) {
.then((blob) => { count++;
const url = window.URL.createObjectURL(blob); let runners = await RunnerTeamService.runnerTeamControllerGetRunners(
let a = document.createElement("a"); t.id
a.href = url; );
a.download = `${$_("sponsorings")}_${o.name}_direct-${locale}-${createId()}.pdf`; await fetch(
document.body.appendChild(a); `${config.baseurl_documentserver}/contracts?locale=${locale}&download=true&key=${config.documentserver_key}`,
a.click(); {
a.remove(); method: "POST",
if (count === o.teams.length && count_orgs === generate_orgs.length) { headers: {
toast.hideToast(); "Content-Type": "application/json",
console.log("here"); },
Toastify({ body: JSON.stringify(runners),
text: $_("pdfs-successfully-generated"), }
duration: 3500, )
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", .then((response) => {
}).showToast(); if (response.status != "200") {
} toast.dismiss();
}) toast.error($_("pdf-generation-failed"));
.catch((err) => {}); } else {
for (const t of o.teams) { return response.blob();
count++; }
let runners = await RunnerTeamService.runnerTeamControllerGetRunners( })
t.id .then((blob) => {
); const url = window.URL.createObjectURL(blob);
await fetch( let a = document.createElement("a");
`${config.baseurl_documentserver}/contracts?locale=${locale}&download=true&key=${config.documentserver_key}`, a.href = url;
{ a.download = `${$_("sponsorings")}_${o.name}_${
method: "POST", t.name
headers: { }-${locale}-${createId()}.pdf`;
"Content-Type": "application/json", document.body.appendChild(a);
}, a.click();
body: JSON.stringify(runners), a.remove();
} if (
) count === o.teams.length &&
.then((response) => { count_orgs === generate_orgs.length
if (response.status != "200") { ) {
toast.hideToast(); toast.dismiss();
Toastify({ toast($_("pdfs-successfully-generated"));
text: $_("pdf-generation-failed"), }
duration: 3500, })
backgroundColor: .catch((err) => {});
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)", }
}).showToast(); }
} else { }
return response.blob();
} function generateRunnerContracts(locale) {
}) toast.loading($_("generating-pdf"));
.then((blob) => { fetch(
const url = window.URL.createObjectURL(blob); `${config.baseurl_documentserver}/contracts?locale=${locale}&download=true&key=${config.documentserver_key}`,
let a = document.createElement("a"); {
a.href = url; method: "POST",
a.download = `${$_("sponsorings")}_${o.name}_${ headers: {
t.name "Content-Type": "application/json",
}-${locale}-${createId()}.pdf`; },
document.body.appendChild(a); body: JSON.stringify(generate_runners),
a.click(); }
a.remove(); )
if ( .then((response) => {
count === o.teams.length && if (response.status != "200") {
count_orgs === generate_orgs.length toast.dismiss();
) { toast.error($_("pdf-generation-failed"));
toast.hideToast(); } else {
Toastify({ return response.blob();
text: $_("pdfs-successfully-generated"), }
duration: 3500, })
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", .then((blob) => {
}).showToast(); const url = window.URL.createObjectURL(blob);
} let a = document.createElement("a");
}) a.href = url;
.catch((err) => {}); if (generate_runners.length == 1) {
} a.download = `${$_("sponsorings")}_${generate_runners[0].firstname}_${
} generate_runners[0].lastname
} }-${locale}-${createId()}.pdf`;
}
function generateRunnerContracts(locale) { a.download = `${$_("sponsorings")}-${locale}-${createId()}.pdf`;
const toast = Toastify({ document.body.appendChild(a);
text: $_("generating-pdf"), a.click();
duration: -1, a.remove();
}).showToast(); toast.dismiss();
fetch( toast($_("pdf-successfully-generated"));
`${config.baseurl_documentserver}/contracts?locale=${locale}&download=true&key=${config.documentserver_key}`, })
{ .catch((err) => {
method: "POST", console.error(err);
headers: { });
"Content-Type": "application/json", }
}, </script>
body: JSON.stringify(generate_runners),
} {#if sponsoring_contracts_show}
) <div id="sponsoring:dropdown" class="relative inline-block">
.then((response) => { <div>
if (response.status != "200") { <button
toast.hideToast(); on:click={() => {
Toastify({ sponsoring_contracts_download_open =
text: $_("pdf-generation-failed"), !sponsoring_contracts_download_open;
duration: 3500, }}
backgroundColor: type="button"
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)", class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-gray-600 text-base font-medium text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 sm:ml-3 sm:w-auto sm:text-sm inline-flex"
}).showToast(); id="options-menu"
} else { aria-haspopup="true"
return response.blob(); aria-expanded="true"
} >
}) {$_("generate-sponsoring-contracts")}
.then((blob) => { <svg
const url = window.URL.createObjectURL(blob); xmlns="http://www.w3.org/2000/svg"
let a = document.createElement("a"); width="24"
a.href = url; height="24"
if (generate_runners.length == 1) { viewBox="0 0 24 24"
a.download = `${$_("sponsorings")}_${generate_runners[0].firstname}_${ class="-mr-1 ml-2 h-5 w-5"
generate_runners[0].lastname ><path fill="none" d="M0 0h24v24H0z" />
}-${locale}-${createId()}.pdf`; <path
} fill="currentColor"
a.download = `${$_("sponsorings")}-${locale}-${createId()}.pdf`; d="M3 19h18v2H3v-2zm10-5.83l6.07-6.07 1.42 1.41L12 17 3.52 8.52l1.4-1.42L11 13.17V2h2v11.17z"
document.body.appendChild(a); /></svg
a.click(); >
a.remove(); </button>
toast.hideToast(); </div>
Toastify({ {#if sponsoring_contracts_download_open}
text: $_("pdf-successfully-generated"), <div
duration: 3500, 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"
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", id="sponsoring:dropdown:menu"
}).showToast(); >
}) <div
.catch((err) => { class="py-1"
console.error(err); role="menu"
}); aria-orientation="vertical"
} aria-labelledby="options-menu"
</script> >
<span class="block w-full text-left px-4 py-2 text-sm text-gray-700"
{#if sponsoring_contracts_show} >{$_("select-language")}</span
<div id="sponsoring:dropdown" class="relative inline-block"> >
<div> <button
<button on:click={() => {
on:click={() => { generateSponsoringContract("de");
sponsoring_contracts_download_open = }}
!sponsoring_contracts_download_open; type="submit"
}} class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
type="button" role="menuitem"
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-gray-600 text-base font-medium text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 sm:ml-3 sm:w-auto sm:text-sm inline-flex" >
id="options-menu" {$_("german")}
aria-haspopup="true" </button>
aria-expanded="true" <button
> on:click={() => {
{$_("generate-sponsoring-contracts")} generateSponsoringContract("en");
<svg }}
xmlns="http://www.w3.org/2000/svg" type="submit"
width="24" class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
height="24" role="menuitem"
viewBox="0 0 24 24" >
class="-mr-1 ml-2 h-5 w-5" {$_("english")}
><path fill="none" d="M0 0h24v24H0z" /> </button>
<path </div>
fill="currentColor" </div>
d="M3 19h18v2H3v-2zm10-5.83l6.07-6.07 1.42 1.41L12 17 3.52 8.52l1.4-1.42L11 13.17V2h2v11.17z" {/if}
/></svg </div>
> {/if}
</button>
</div>
{#if sponsoring_contracts_download_open}
<div
class="origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 z-10"
id="sponsoring:dropdown:menu"
>
<div
class="py-1"
role="menu"
aria-orientation="vertical"
aria-labelledby="options-menu"
>
<span class="block w-full text-left px-4 py-2 text-sm text-gray-700"
>{$_("select-language")}</span
>
<button
on:click={() => {
generateSponsoringContract("de");
}}
type="submit"
class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
role="menuitem"
>
{$_("german")}
</button>
<button
on:click={() => {
generateSponsoringContract("en");
}}
type="submit"
class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
role="menuitem"
>
{$_("english")}
</button>
</div>
</div>
{/if}
</div>
{/if}

View File

@ -3,78 +3,90 @@
<img <img
alt="" alt=""
src="https://gustui.s3.amazonaws.com/avatar.png" src="https://gustui.s3.amazonaws.com/avatar.png"
class="absolute left-0 top-0 w-full h-full rounded-full object-cover" /> class="absolute left-0 top-0 w-full h-full rounded-full object-cover"
/>
</div> </div>
<div class="relative rounded-full w-8 h-8"> <div class="relative rounded-full w-8 h-8">
<img <img
alt="" alt=""
src="https://gustui.s3.amazonaws.com/avatar.png" src="https://gustui.s3.amazonaws.com/avatar.png"
class="absolute left-0 top-0 w-full h-full rounded-full object-cover" /> class="absolute left-0 top-0 w-full h-full rounded-full object-cover"
/>
</div> </div>
<div class="relative rounded-full w-12 h-12"> <div class="relative rounded-full w-12 h-12">
<img <img
alt="" alt=""
src="https://gustui.s3.amazonaws.com/avatar.png" src="https://gustui.s3.amazonaws.com/avatar.png"
class="absolute left-0 top-0 w-full h-full rounded-full object-cover" /> class="absolute left-0 top-0 w-full h-full rounded-full object-cover"
/>
</div> </div>
<div class="relative rounded-full w-16 h-16"> <div class="relative rounded-full w-16 h-16">
<img <img
alt="" alt=""
src="https://gustui.s3.amazonaws.com/avatar.png" src="https://gustui.s3.amazonaws.com/avatar.png"
class="absolute left-0 top-0 w-full h-full rounded-full object-cover" /> class="absolute left-0 top-0 w-full h-full rounded-full object-cover"
/>
</div> </div>
<div class="relative rounded-full w-20 h-20"> <div class="relative rounded-full w-20 h-20">
<img <img
alt="" alt=""
src="https://gustui.s3.amazonaws.com/avatar.png" src="https://gustui.s3.amazonaws.com/avatar.png"
class="absolute left-0 top-0 w-full h-full rounded-full object-cover" /> class="absolute left-0 top-0 w-full h-full rounded-full object-cover"
/>
</div> </div>
<div class="relative rounded-full w-24 h-24"> <div class="relative rounded-full w-24 h-24">
<img <img
alt="" alt=""
src="https://gustui.s3.amazonaws.com/avatar.png" src="https://gustui.s3.amazonaws.com/avatar.png"
class="absolute left-0 top-0 w-full h-full rounded-full object-cover" /> class="absolute left-0 top-0 w-full h-full rounded-full object-cover"
/>
</div> </div>
<h3 class="text-lg">Status Avatars</h3> <h3 class="text-lg">Status Avatars</h3>
<div class="relative rounded-full w-4 h-4"> <div class="relative rounded-full w-4 h-4">
<img <img
alt="" alt=""
src="https://gustui.s3.amazonaws.com/avatar.png" src="https://gustui.s3.amazonaws.com/avatar.png"
class="absolute left-0 top-0 w-full h-full rounded-full object-cover" /> class="absolute left-0 top-0 w-full h-full rounded-full object-cover"
/>
<div class="absolute rounded-full right-0 bottom-0 w-1 h-1 bg-gray-200" /> <div class="absolute rounded-full right-0 bottom-0 w-1 h-1 bg-gray-200" />
</div> </div>
<div class="relative rounded-full w-8 h-8"> <div class="relative rounded-full w-8 h-8">
<img <img
alt="" alt=""
src="https://gustui.s3.amazonaws.com/avatar.png" src="https://gustui.s3.amazonaws.com/avatar.png"
class="absolute left-0 top-0 w-full h-full rounded-full object-cover" /> class="absolute left-0 top-0 w-full h-full rounded-full object-cover"
/>
<div class="absolute rounded-full right-0 bottom-0 w-2 h-2 bg-green-400" /> <div class="absolute rounded-full right-0 bottom-0 w-2 h-2 bg-green-400" />
</div> </div>
<div class="relative rounded-full w-12 h-12"> <div class="relative rounded-full w-12 h-12">
<img <img
alt="" alt=""
src="https://gustui.s3.amazonaws.com/avatar.png" src="https://gustui.s3.amazonaws.com/avatar.png"
class="absolute left-0 top-0 w-full h-full rounded-full object-cover" /> class="absolute left-0 top-0 w-full h-full rounded-full object-cover"
/>
<div class="absolute rounded-full right-0 bottom-0 w-4 h-4 bg-red-600" /> <div class="absolute rounded-full right-0 bottom-0 w-4 h-4 bg-red-600" />
</div> </div>
<div class="relative rounded-full w-16 h-16"> <div class="relative rounded-full w-16 h-16">
<img <img
alt="" alt=""
src="https://gustui.s3.amazonaws.com/avatar.png" src="https://gustui.s3.amazonaws.com/avatar.png"
class="absolute left-0 top-0 w-full h-full rounded-full object-cover" /> class="absolute left-0 top-0 w-full h-full rounded-full object-cover"
/>
<div class="absolute rounded-full right-0 bottom-0 w-5 h-5 bg-gray-200" /> <div class="absolute rounded-full right-0 bottom-0 w-5 h-5 bg-gray-200" />
</div> </div>
<div class="relative rounded-full w-20 h-20"> <div class="relative rounded-full w-20 h-20">
<img <img
alt="" alt=""
src="https://gustui.s3.amazonaws.com/avatar.png" src="https://gustui.s3.amazonaws.com/avatar.png"
class="absolute left-0 top-0 w-full h-full rounded-full object-cover" /> class="absolute left-0 top-0 w-full h-full rounded-full object-cover"
/>
<div class="absolute rounded-full right-0 bottom-0 w-6 h-6 bg-green-400" /> <div class="absolute rounded-full right-0 bottom-0 w-6 h-6 bg-green-400" />
</div> </div>
<div class="relative rounded-full w-24 h-24"> <div class="relative rounded-full w-24 h-24">
<img <img
alt="" alt=""
src="https://gustui.s3.amazonaws.com/avatar.png" src="https://gustui.s3.amazonaws.com/avatar.png"
class="absolute left-0 top-0 w-full h-full rounded-full object-cover" /> class="absolute left-0 top-0 w-full h-full rounded-full object-cover"
/>
<div class="absolute rounded-full right-0 bottom-0 w-6 h-6 bg-red-600" /> <div class="absolute rounded-full right-0 bottom-0 w-6 h-6 bg-red-600" />
</div> </div>

View File

@ -1,24 +1,36 @@
<h3 class="text-lg">badges</h3> <h3 class="text-lg">badges</h3>
<span <span
class="text-sm font-medium bg-green-100 py-1 px-2 rounded text-green-500 align-middle">Paid</span> class="text-sm font-medium bg-green-100 py-1 px-2 rounded text-green-500 align-middle"
>Paid</span
>
<span <span
class="text-sm font-medium bg-red-100 py-1 px-2 rounded text-red-500 align-middle">Overdue</span> class="text-sm font-medium bg-red-100 py-1 px-2 rounded text-red-500 align-middle"
<span >Overdue</span
class="rounded-sm py-1 px-2 text-xs font-medium text-white bg-blue-600">Primary</span> >
<span <span class="rounded-sm py-1 px-2 text-xs font-medium text-white bg-blue-600"
class="rounded-sm py-1 px-2 text-xs font-medium text-white bg-gray-600">Secondary</span> >Primary</span
<span >
class="rounded-sm py-1 px-2 text-xs font-medium text-white bg-green-600">Success</span> <span class="rounded-sm py-1 px-2 text-xs font-medium text-white bg-gray-600"
<span >Secondary</span
class="rounded-sm py-1 px-2 text-xs font-medium text-white bg-red-600">Danger</span> >
<span <span class="rounded-sm py-1 px-2 text-xs font-medium text-white bg-green-600"
class="rounded-sm py-1 px-2 text-xs font-medium text-black bg-yellow-400">Warning</span> >Success</span
<span >
class="rounded-sm py-1 px-2 text-xs font-medium text-white bg-indigo-300">Info</span> <span class="rounded-sm py-1 px-2 text-xs font-medium text-white bg-red-600"
<span >Danger</span
class="rounded-sm py-1 px-2 text-xs font-medium text-black bg-gray-200">Light</span> >
<span <span class="rounded-sm py-1 px-2 text-xs font-medium text-black bg-yellow-400"
class="rounded-sm py-1 px-2 text-xs font-medium text-white bg-gray-900">Dark</span> >Warning</span
>
<span class="rounded-sm py-1 px-2 text-xs font-medium text-white bg-indigo-300"
>Info</span
>
<span class="rounded-sm py-1 px-2 text-xs font-medium text-black bg-gray-200"
>Light</span
>
<span class="rounded-sm py-1 px-2 text-xs font-medium text-white bg-gray-900"
>Dark</span
>
<h3 class="text-lg">closable badges</h3> <h3 class="text-lg">closable badges</h3>
<span class="rounded-sm py-1 px-2 text-xs font-medium text-white bg-blue-600"> <span class="rounded-sm py-1 px-2 text-xs font-medium text-white bg-blue-600">
Primary Primary

View File

@ -13,9 +13,10 @@
class="h-3 w-3 stroke-current" class="h-3 w-3 stroke-current"
height="1em" height="1em"
width="1em" width="1em"
xmlns="http://www.w3.org/2000/svg"><path xmlns="http://www.w3.org/2000/svg"
d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" /> ><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
<polyline points="9 22 9 12 15 12 15 22" /></svg> <polyline points="9 22 9 12 15 12 15 22" /></svg
>
</li> </li>
<li class="flex items-center"> <li class="flex items-center">
<a class="mr-2" href="/">Home</a><svg <a class="mr-2" href="/">Home</a><svg
@ -28,12 +29,10 @@
class="h-3 w-3 mr-2 stroke-current" class="h-3 w-3 mr-2 stroke-current"
height="1em" height="1em"
width="1em" width="1em"
xmlns="http://www.w3.org/2000/svg"><line xmlns="http://www.w3.org/2000/svg"
x1="5" ><line x1="5" y1="12" x2="19" y2="12" />
y1="12" <polyline points="12 5 19 12 12 19" /></svg
x2="19" >
y2="12" />
<polyline points="12 5 19 12 12 19" /></svg>
</li> </li>
<li class="flex items-center"> <li class="flex items-center">
<a class="mr-2" href="/">Second level</a><svg <a class="mr-2" href="/">Second level</a><svg
@ -46,12 +45,10 @@
class="h-3 w-3 mr-2 stroke-current" class="h-3 w-3 mr-2 stroke-current"
height="1em" height="1em"
width="1em" width="1em"
xmlns="http://www.w3.org/2000/svg"><line xmlns="http://www.w3.org/2000/svg"
x1="5" ><line x1="5" y1="12" x2="19" y2="12" />
y1="12" <polyline points="12 5 19 12 12 19" /></svg
x2="19" >
y2="12" />
<polyline points="12 5 19 12 12 19" /></svg>
</li> </li>
<li class="flex items-center"> <li class="flex items-center">
<a class="mr-2" href="/">Third level</a> <a class="mr-2" href="/">Third level</a>

View File

@ -29,8 +29,7 @@
<div class="mb-8"> <div class="mb-8">
<Table /> <Table />
</div> </div>
<div <div class="widget w-full p-4 mb-4 rounded-lg bg-white border border-grey-100">
class="widget w-full p-4 mb-4 rounded-lg bg-white border border-grey-100">
<div class="flex flex-row items-center justify-between mb-6"> <div class="flex flex-row items-center justify-between mb-6">
<div class="flex flex-col"> <div class="flex flex-col">
<div class="text-sm font-light text-grey-500">Regular</div> <div class="text-sm font-light text-grey-500">Regular</div>
@ -39,32 +38,38 @@
</div> </div>
<div class="flex flex-col lg:flex-row lg:flex-wrap w-full lg:space-x-4"> <div class="flex flex-col lg:flex-row lg:flex-wrap w-full lg:space-x-4">
<div class="w-full lg:w-1/4"> <div class="w-full lg:w-1/4">
<div class="form-element "> <div class="form-element">
<div class="form-label">Label</div><input <div class="form-label">Label</div>
<input
name="name" name="name"
type="text" type="text"
class="form-input" class="form-input"
placeholder="Enter something..." /> placeholder="Enter something..."
/>
<div class="form-hint">This is a hint</div> <div class="form-hint">This is a hint</div>
</div> </div>
</div> </div>
<div class="w-full lg:w-1/4"> <div class="w-full lg:w-1/4">
<div class="form-element "> <div class="form-element">
<div class="form-label">First name</div><input <div class="form-label">First name</div>
<input
name="name" name="name"
type="text" type="text"
class="form-input form-input-invalid" class="form-input form-input-invalid"
placeholder="john@example.com" /> placeholder="john@example.com"
/>
<div class="form-error">First name is required</div> <div class="form-error">First name is required</div>
</div> </div>
</div> </div>
<div class="w-full lg:w-1/4"> <div class="w-full lg:w-1/4">
<div class="form-element "> <div class="form-element">
<div class="form-label">First name</div><input <div class="form-label">First name</div>
<input
name="name" name="name"
type="text" type="text"
class="form-input form-input-valid" class="form-input form-input-valid"
placeholder="john@example.com" /> placeholder="john@example.com"
/>
<div class="form-success">First name is valid</div> <div class="form-success">First name is valid</div>
</div> </div>
</div> </div>

View File

@ -4,31 +4,55 @@
<div class="text-sm font-light text-grey-500">Conversions</div> <div class="text-sm font-light text-grey-500">Conversions</div>
<div class="text-sm font-bold"><span>This year</span></div> <div class="text-sm font-bold"><span>This year</span></div>
</div> </div>
<div class="relative"><button <div class="relative">
class="btn btn-default btn-circle btn-icon bg-transparent hover:bg-transparent active:bg-transparent relative"><svg <button
stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" class="btn btn-default btn-circle btn-icon bg-transparent hover:bg-transparent active:bg-transparent relative"
stroke-linejoin="round" class="stroke-current stroke-1" size="18" height="18" width="18" ><svg
xmlns="http://www.w3.org/2000/svg"> stroke="currentColor"
<circle cx="12" cy="12" r="1"></circle> fill="none"
<circle cx="12" cy="5" r="1"></circle> stroke-width="2"
<circle cx="12" cy="19" r="1"></circle> viewBox="0 0 24 24"
</svg></button> stroke-linecap="round"
<div class="dropdown absolute top-0 right-0 mt-8 "> stroke-linejoin="round"
class="stroke-current stroke-1"
size="18"
height="18"
width="18"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="12" cy="12" r="1" />
<circle cx="12" cy="5" r="1" />
<circle cx="12" cy="19" r="1" />
</svg></button
>
<div class="dropdown absolute top-0 right-0 mt-8">
<div class="dropdown-content w-48 bottom-start"> <div class="dropdown-content w-48 bottom-start">
<div class="flex flex-col w-full"> <div class="flex flex-col w-full">
<ul class="list-none"> <ul class="list-none">
<li><a <li>
<a
class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100" class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100"
href="/">Today</a></li> href="/">Today</a
<li><a >
</li>
<li>
<a
class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100" class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100"
href="/">This week</a></li> href="/">This week</a
<li><a >
</li>
<li>
<a
class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100" class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100"
href="/">This month</a></li> href="/">This month</a
<li><a >
</li>
<li>
<a
class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100" class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100"
href="/">This year</a></li> href="/">This year</a
>
</li>
</ul> </ul>
</div> </div>
</div> </div>
@ -38,167 +62,418 @@
<div class="flex flex-row w-full"> <div class="flex flex-row w-full">
<div style="width:100%;height:240px"> <div style="width:100%;height:240px">
<div class="recharts-responsive-container" style="width:100%;height:100%"> <div class="recharts-responsive-container" style="width:100%;height:100%">
<div class="recharts-wrapper" <div
style="position: relative; cursor: default; width: 704px; height: 240px;"><svg class="recharts-wrapper"
class="recharts-surface" width="704" height="240" viewBox="0 0 704 240" version="1.1"> style="position: relative; cursor: default; width: 704px; height: 240px;"
>
<svg
class="recharts-surface"
width="704"
height="240"
viewBox="0 0 704 240"
version="1.1"
>
<defs> <defs>
<clipPath id="recharts3-clip"> <clipPath id="recharts3-clip">
<rect x="40" y="10" height="190" width="654"></rect> <rect x="40" y="10" height="190" width="654" />
</clipPath> </clipPath>
</defs> </defs>
<g class="recharts-layer recharts-cartesian-axis recharts-xAxis xAxis"> <g
class="recharts-layer recharts-cartesian-axis recharts-xAxis xAxis"
>
<g class="recharts-cartesian-axis-ticks"> <g class="recharts-cartesian-axis-ticks">
<g class="recharts-layer recharts-cartesian-axis-tick"><text width="654" height="30" x="67.25" <g class="recharts-layer recharts-cartesian-axis-tick"
y="208" stroke="none" fill="#666" class="recharts-text recharts-cartesian-axis-tick-value" ><text
text-anchor="middle"> width="654"
height="30"
x="67.25"
y="208"
stroke="none"
fill="#666"
class="recharts-text recharts-cartesian-axis-tick-value"
text-anchor="middle"
>
<tspan x="67.25" dy="0.71em">Jan</tspan> <tspan x="67.25" dy="0.71em">Jan</tspan>
</text></g> </text></g
<g class="recharts-layer recharts-cartesian-axis-tick"><text width="654" height="30" >
x="121.75" y="208" stroke="none" fill="#666" <g class="recharts-layer recharts-cartesian-axis-tick"
class="recharts-text recharts-cartesian-axis-tick-value" text-anchor="middle"> ><text
width="654"
height="30"
x="121.75"
y="208"
stroke="none"
fill="#666"
class="recharts-text recharts-cartesian-axis-tick-value"
text-anchor="middle"
>
<tspan x="121.75" dy="0.71em">Feb</tspan> <tspan x="121.75" dy="0.71em">Feb</tspan>
</text></g> </text></g
<g class="recharts-layer recharts-cartesian-axis-tick"><text width="654" height="30" >
x="176.25" y="208" stroke="none" fill="#666" <g class="recharts-layer recharts-cartesian-axis-tick"
class="recharts-text recharts-cartesian-axis-tick-value" text-anchor="middle"> ><text
width="654"
height="30"
x="176.25"
y="208"
stroke="none"
fill="#666"
class="recharts-text recharts-cartesian-axis-tick-value"
text-anchor="middle"
>
<tspan x="176.25" dy="0.71em">Mar</tspan> <tspan x="176.25" dy="0.71em">Mar</tspan>
</text></g> </text></g
<g class="recharts-layer recharts-cartesian-axis-tick"><text width="654" height="30" >
x="230.75" y="208" stroke="none" fill="#666" <g class="recharts-layer recharts-cartesian-axis-tick"
class="recharts-text recharts-cartesian-axis-tick-value" text-anchor="middle"> ><text
width="654"
height="30"
x="230.75"
y="208"
stroke="none"
fill="#666"
class="recharts-text recharts-cartesian-axis-tick-value"
text-anchor="middle"
>
<tspan x="230.75" dy="0.71em">Apr</tspan> <tspan x="230.75" dy="0.71em">Apr</tspan>
</text></g> </text></g
<g class="recharts-layer recharts-cartesian-axis-tick"><text width="654" height="30" >
x="285.25" y="208" stroke="none" fill="#666" <g class="recharts-layer recharts-cartesian-axis-tick"
class="recharts-text recharts-cartesian-axis-tick-value" text-anchor="middle"> ><text
width="654"
height="30"
x="285.25"
y="208"
stroke="none"
fill="#666"
class="recharts-text recharts-cartesian-axis-tick-value"
text-anchor="middle"
>
<tspan x="285.25" dy="0.71em">May</tspan> <tspan x="285.25" dy="0.71em">May</tspan>
</text></g> </text></g
<g class="recharts-layer recharts-cartesian-axis-tick"><text width="654" height="30" >
x="339.75" y="208" stroke="none" fill="#666" <g class="recharts-layer recharts-cartesian-axis-tick"
class="recharts-text recharts-cartesian-axis-tick-value" text-anchor="middle"> ><text
width="654"
height="30"
x="339.75"
y="208"
stroke="none"
fill="#666"
class="recharts-text recharts-cartesian-axis-tick-value"
text-anchor="middle"
>
<tspan x="339.75" dy="0.71em">Jun</tspan> <tspan x="339.75" dy="0.71em">Jun</tspan>
</text></g> </text></g
<g class="recharts-layer recharts-cartesian-axis-tick"><text width="654" height="30" >
x="394.25" y="208" stroke="none" fill="#666" <g class="recharts-layer recharts-cartesian-axis-tick"
class="recharts-text recharts-cartesian-axis-tick-value" text-anchor="middle"> ><text
width="654"
height="30"
x="394.25"
y="208"
stroke="none"
fill="#666"
class="recharts-text recharts-cartesian-axis-tick-value"
text-anchor="middle"
>
<tspan x="394.25" dy="0.71em">Jul</tspan> <tspan x="394.25" dy="0.71em">Jul</tspan>
</text></g> </text></g
<g class="recharts-layer recharts-cartesian-axis-tick"><text width="654" height="30" >
x="448.75" y="208" stroke="none" fill="#666" <g class="recharts-layer recharts-cartesian-axis-tick"
class="recharts-text recharts-cartesian-axis-tick-value" text-anchor="middle"> ><text
width="654"
height="30"
x="448.75"
y="208"
stroke="none"
fill="#666"
class="recharts-text recharts-cartesian-axis-tick-value"
text-anchor="middle"
>
<tspan x="448.75" dy="0.71em">Aug</tspan> <tspan x="448.75" dy="0.71em">Aug</tspan>
</text></g> </text></g
<g class="recharts-layer recharts-cartesian-axis-tick"><text width="654" height="30" >
x="503.25" y="208" stroke="none" fill="#666" <g class="recharts-layer recharts-cartesian-axis-tick"
class="recharts-text recharts-cartesian-axis-tick-value" text-anchor="middle"> ><text
width="654"
height="30"
x="503.25"
y="208"
stroke="none"
fill="#666"
class="recharts-text recharts-cartesian-axis-tick-value"
text-anchor="middle"
>
<tspan x="503.25" dy="0.71em">Sep</tspan> <tspan x="503.25" dy="0.71em">Sep</tspan>
</text></g> </text></g
<g class="recharts-layer recharts-cartesian-axis-tick"><text width="654" height="30" >
x="557.75" y="208" stroke="none" fill="#666" <g class="recharts-layer recharts-cartesian-axis-tick"
class="recharts-text recharts-cartesian-axis-tick-value" text-anchor="middle"> ><text
width="654"
height="30"
x="557.75"
y="208"
stroke="none"
fill="#666"
class="recharts-text recharts-cartesian-axis-tick-value"
text-anchor="middle"
>
<tspan x="557.75" dy="0.71em">Oct</tspan> <tspan x="557.75" dy="0.71em">Oct</tspan>
</text></g> </text></g
<g class="recharts-layer recharts-cartesian-axis-tick"><text width="654" height="30" >
x="612.25" y="208" stroke="none" fill="#666" <g class="recharts-layer recharts-cartesian-axis-tick"
class="recharts-text recharts-cartesian-axis-tick-value" text-anchor="middle"> ><text
width="654"
height="30"
x="612.25"
y="208"
stroke="none"
fill="#666"
class="recharts-text recharts-cartesian-axis-tick-value"
text-anchor="middle"
>
<tspan x="612.25" dy="0.71em">Nov</tspan> <tspan x="612.25" dy="0.71em">Nov</tspan>
</text></g> </text></g
<g class="recharts-layer recharts-cartesian-axis-tick"><text width="654" height="30" >
x="666.75" y="208" stroke="none" fill="#666" <g class="recharts-layer recharts-cartesian-axis-tick"
class="recharts-text recharts-cartesian-axis-tick-value" text-anchor="middle"> ><text
width="654"
height="30"
x="666.75"
y="208"
stroke="none"
fill="#666"
class="recharts-text recharts-cartesian-axis-tick-value"
text-anchor="middle"
>
<tspan x="666.75" dy="0.71em">Dec</tspan> <tspan x="666.75" dy="0.71em">Dec</tspan>
</text></g> </text></g
>
</g> </g>
</g> </g>
<g class="recharts-layer recharts-cartesian-axis recharts-yAxis yAxis"> <g
class="recharts-layer recharts-cartesian-axis recharts-yAxis yAxis"
>
<g class="recharts-cartesian-axis-ticks"> <g class="recharts-cartesian-axis-ticks">
<g class="recharts-layer recharts-cartesian-axis-tick"><text width="30" height="190" x="32" <g class="recharts-layer recharts-cartesian-axis-tick"
y="200" stroke="none" fill="#666" class="recharts-text recharts-cartesian-axis-tick-value" ><text
text-anchor="end"> width="30"
height="190"
x="32"
y="200"
stroke="none"
fill="#666"
class="recharts-text recharts-cartesian-axis-tick-value"
text-anchor="end"
>
<tspan x="32" dy="0.355em">0</tspan> <tspan x="32" dy="0.355em">0</tspan>
</text></g> </text></g
<g class="recharts-layer recharts-cartesian-axis-tick"><text width="30" height="190" x="32" >
y="152.5" stroke="none" fill="#666" <g class="recharts-layer recharts-cartesian-axis-tick"
class="recharts-text recharts-cartesian-axis-tick-value" text-anchor="end"> ><text
width="30"
height="190"
x="32"
y="152.5"
stroke="none"
fill="#666"
class="recharts-text recharts-cartesian-axis-tick-value"
text-anchor="end"
>
<tspan x="32" dy="0.355em">65</tspan> <tspan x="32" dy="0.355em">65</tspan>
</text></g> </text></g
<g class="recharts-layer recharts-cartesian-axis-tick"><text width="30" height="190" x="32" >
y="105" stroke="none" fill="#666" class="recharts-text recharts-cartesian-axis-tick-value" <g class="recharts-layer recharts-cartesian-axis-tick"
text-anchor="end"> ><text
width="30"
height="190"
x="32"
y="105"
stroke="none"
fill="#666"
class="recharts-text recharts-cartesian-axis-tick-value"
text-anchor="end"
>
<tspan x="32" dy="0.355em">130</tspan> <tspan x="32" dy="0.355em">130</tspan>
</text></g> </text></g
<g class="recharts-layer recharts-cartesian-axis-tick"><text width="30" height="190" x="32" >
y="57.5" stroke="none" fill="#666" <g class="recharts-layer recharts-cartesian-axis-tick"
class="recharts-text recharts-cartesian-axis-tick-value" text-anchor="end"> ><text
width="30"
height="190"
x="32"
y="57.5"
stroke="none"
fill="#666"
class="recharts-text recharts-cartesian-axis-tick-value"
text-anchor="end"
>
<tspan x="32" dy="0.355em">195</tspan> <tspan x="32" dy="0.355em">195</tspan>
</text></g> </text></g
<g class="recharts-layer recharts-cartesian-axis-tick"><text width="30" height="190" x="32" >
y="10" stroke="none" fill="#666" class="recharts-text recharts-cartesian-axis-tick-value" <g class="recharts-layer recharts-cartesian-axis-tick"
text-anchor="end"> ><text
width="30"
height="190"
x="32"
y="10"
stroke="none"
fill="#666"
class="recharts-text recharts-cartesian-axis-tick-value"
text-anchor="end"
>
<tspan x="32" dy="0.355em">260</tspan> <tspan x="32" dy="0.355em">260</tspan>
</text></g> </text></g
>
</g> </g>
</g> </g>
<g class="recharts-layer recharts-bar"> <g class="recharts-layer recharts-bar">
<g class="recharts-layer recharts-bar-rectangles"> <g class="recharts-layer recharts-bar-rectangles">
<g class="recharts-layer"> <g class="recharts-layer">
<g class="recharts-layer recharts-bar-rectangle"> <g class="recharts-layer recharts-bar-rectangle">
<path fill="#90caf9" width="10" height="119.11538461538461" x="55" y="80.88461538461539" <path
radius="0" class="recharts-rectangle" fill="#90caf9"
d="M 55,80.88461538461539 h 10 v 119.11538461538461 h -10 Z"></path> width="10"
height="119.11538461538461"
x="55"
y="80.88461538461539"
radius="0"
class="recharts-rectangle"
d="M 55,80.88461538461539 h 10 v 119.11538461538461 h -10 Z"
/>
</g> </g>
<g class="recharts-layer recharts-bar-rectangle"> <g class="recharts-layer recharts-bar-rectangle">
<path fill="#90caf9" width="10" height="95" x="109.5" y="105" radius="0" <path
class="recharts-rectangle" d="M 109.5,105 h 10 v 95 h -10 Z"></path> fill="#90caf9"
width="10"
height="95"
x="109.5"
y="105"
radius="0"
class="recharts-rectangle"
d="M 109.5,105 h 10 v 95 h -10 Z"
/>
</g> </g>
<g class="recharts-layer recharts-bar-rectangle"> <g class="recharts-layer recharts-bar-rectangle">
<path fill="#90caf9" width="10" height="122.03846153846155" x="164" y="77.96153846153845" <path
radius="0" class="recharts-rectangle" fill="#90caf9"
d="M 164,77.96153846153845 h 10 v 122.03846153846155 h -10 Z"></path> width="10"
height="122.03846153846155"
x="164"
y="77.96153846153845"
radius="0"
class="recharts-rectangle"
d="M 164,77.96153846153845 h 10 v 122.03846153846155 h -10 Z"
/>
</g> </g>
<g class="recharts-layer recharts-bar-rectangle"> <g class="recharts-layer recharts-bar-rectangle">
<path fill="#90caf9" width="10" height="81.11538461538461" x="218.5" <path
y="118.88461538461539" radius="0" class="recharts-rectangle" fill="#90caf9"
d="M 218.5,118.88461538461539 h 10 v 81.11538461538461 h -10 Z"></path> width="10"
height="81.11538461538461"
x="218.5"
y="118.88461538461539"
radius="0"
class="recharts-rectangle"
d="M 218.5,118.88461538461539 h 10 v 81.11538461538461 h -10 Z"
/>
</g> </g>
<g class="recharts-layer recharts-bar-rectangle"> <g class="recharts-layer recharts-bar-rectangle">
<path fill="#90caf9" width="10" height="114" x="273" y="86" radius="0" <path
class="recharts-rectangle" d="M 273,86 h 10 v 114 h -10 Z"></path> fill="#90caf9"
width="10"
height="114"
x="273"
y="86"
radius="0"
class="recharts-rectangle"
d="M 273,86 h 10 v 114 h -10 Z"
/>
</g> </g>
<g class="recharts-layer recharts-bar-rectangle"> <g class="recharts-layer recharts-bar-rectangle">
<path fill="#90caf9" width="10" height="117.65384615384616" x="327.5" <path
y="82.34615384615384" radius="0" class="recharts-rectangle" fill="#90caf9"
d="M 327.5,82.34615384615384 h 10 v 117.65384615384616 h -10 Z"></path> width="10"
height="117.65384615384616"
x="327.5"
y="82.34615384615384"
radius="0"
class="recharts-rectangle"
d="M 327.5,82.34615384615384 h 10 v 117.65384615384616 h -10 Z"
/>
</g> </g>
<g class="recharts-layer recharts-bar-rectangle"> <g class="recharts-layer recharts-bar-rectangle">
<path fill="#90caf9" width="10" height="103.76923076923076" x="382" y="96.23076923076924" <path
radius="0" class="recharts-rectangle" fill="#90caf9"
d="M 382,96.23076923076924 h 10 v 103.76923076923076 h -10 Z"></path> width="10"
height="103.76923076923076"
x="382"
y="96.23076923076924"
radius="0"
class="recharts-rectangle"
d="M 382,96.23076923076924 h 10 v 103.76923076923076 h -10 Z"
/>
</g> </g>
<g class="recharts-layer recharts-bar-rectangle"> <g class="recharts-layer recharts-bar-rectangle">
<path fill="#90caf9" width="10" height="92.80769230769232" x="436.5" <path
y="107.19230769230768" radius="0" class="recharts-rectangle" fill="#90caf9"
d="M 436.5,107.19230769230768 h 10 v 92.80769230769232 h -10 Z"></path> width="10"
height="92.80769230769232"
x="436.5"
y="107.19230769230768"
radius="0"
class="recharts-rectangle"
d="M 436.5,107.19230769230768 h 10 v 92.80769230769232 h -10 Z"
/>
</g> </g>
<g class="recharts-layer recharts-bar-rectangle"> <g class="recharts-layer recharts-bar-rectangle">
<path fill="#90caf9" width="10" height="92.80769230769232" x="491" y="107.19230769230768" <path
radius="0" class="recharts-rectangle" fill="#90caf9"
d="M 491,107.19230769230768 h 10 v 92.80769230769232 h -10 Z"></path> width="10"
height="92.80769230769232"
x="491"
y="107.19230769230768"
radius="0"
class="recharts-rectangle"
d="M 491,107.19230769230768 h 10 v 92.80769230769232 h -10 Z"
/>
</g> </g>
<g class="recharts-layer recharts-bar-rectangle"> <g class="recharts-layer recharts-bar-rectangle">
<path fill="#90caf9" width="10" height="127.8846153846154" x="545.5" y="72.1153846153846" <path
radius="0" class="recharts-rectangle" fill="#90caf9"
d="M 545.5,72.1153846153846 h 10 v 127.8846153846154 h -10 Z"></path> width="10"
height="127.8846153846154"
x="545.5"
y="72.1153846153846"
radius="0"
class="recharts-rectangle"
d="M 545.5,72.1153846153846 h 10 v 127.8846153846154 h -10 Z"
/>
</g> </g>
<g class="recharts-layer recharts-bar-rectangle"> <g class="recharts-layer recharts-bar-rectangle">
<path fill="#90caf9" width="10" height="105.23076923076924" x="600" y="94.76923076923076" <path
radius="0" class="recharts-rectangle" fill="#90caf9"
d="M 600,94.76923076923076 h 10 v 105.23076923076924 h -10 Z"></path> width="10"
height="105.23076923076924"
x="600"
y="94.76923076923076"
radius="0"
class="recharts-rectangle"
d="M 600,94.76923076923076 h 10 v 105.23076923076924 h -10 Z"
/>
</g> </g>
<g class="recharts-layer recharts-bar-rectangle"> <g class="recharts-layer recharts-bar-rectangle">
<path fill="#90caf9" width="10" height="115.46153846153845" x="654.5" <path
y="84.53846153846155" radius="0" class="recharts-rectangle" fill="#90caf9"
d="M 654.5,84.53846153846155 h 10 v 115.46153846153845 h -10 Z"></path> width="10"
height="115.46153846153845"
x="654.5"
y="84.53846153846155"
radius="0"
class="recharts-rectangle"
d="M 654.5,84.53846153846155 h 10 v 115.46153846153845 h -10 Z"
/>
</g> </g>
</g> </g>
</g> </g>
@ -207,75 +482,162 @@
<g class="recharts-layer recharts-bar-rectangles"> <g class="recharts-layer recharts-bar-rectangles">
<g class="recharts-layer"> <g class="recharts-layer">
<g class="recharts-layer recharts-bar-rectangle"> <g class="recharts-layer recharts-bar-rectangle">
<path fill="#1e88e5" width="10" height="112.53846153846155" x="69" y="87.46153846153845" <path
radius="0" class="recharts-rectangle" fill="#1e88e5"
d="M 69,87.46153846153845 h 10 v 112.53846153846155 h -10 Z"></path> width="10"
height="112.53846153846155"
x="69"
y="87.46153846153845"
radius="0"
class="recharts-rectangle"
d="M 69,87.46153846153845 h 10 v 112.53846153846155 h -10 Z"
/>
</g> </g>
<g class="recharts-layer recharts-bar-rectangle"> <g class="recharts-layer recharts-bar-rectangle">
<path fill="#1e88e5" width="10" height="151.26923076923077" x="123.5" <path
y="48.730769230769226" radius="0" class="recharts-rectangle" fill="#1e88e5"
d="M 123.5,48.730769230769226 h 10 v 151.26923076923077 h -10 Z"></path> width="10"
height="151.26923076923077"
x="123.5"
y="48.730769230769226"
radius="0"
class="recharts-rectangle"
d="M 123.5,48.730769230769226 h 10 v 151.26923076923077 h -10 Z"
/>
</g> </g>
<g class="recharts-layer recharts-bar-rectangle"> <g class="recharts-layer recharts-bar-rectangle">
<path fill="#1e88e5" width="10" height="181.23076923076923" x="178" y="18.769230769230774" <path
radius="0" class="recharts-rectangle" fill="#1e88e5"
d="M 178,18.769230769230774 h 10 v 181.23076923076923 h -10 Z"></path> width="10"
height="181.23076923076923"
x="178"
y="18.769230769230774"
radius="0"
class="recharts-rectangle"
d="M 178,18.769230769230774 h 10 v 181.23076923076923 h -10 Z"
/>
</g> </g>
<g class="recharts-layer recharts-bar-rectangle"> <g class="recharts-layer recharts-bar-rectangle">
<path fill="#1e88e5" width="10" height="165.8846153846154" x="232.5" y="34.11538461538461" <path
radius="0" class="recharts-rectangle" fill="#1e88e5"
d="M 232.5,34.11538461538461 h 10 v 165.8846153846154 h -10 Z"></path> width="10"
height="165.8846153846154"
x="232.5"
y="34.11538461538461"
radius="0"
class="recharts-rectangle"
d="M 232.5,34.11538461538461 h 10 v 165.8846153846154 h -10 Z"
/>
</g> </g>
<g class="recharts-layer recharts-bar-rectangle"> <g class="recharts-layer recharts-bar-rectangle">
<path fill="#1e88e5" width="10" height="156.38461538461536" x="287" y="43.61538461538464" <path
radius="0" class="recharts-rectangle" fill="#1e88e5"
d="M 287,43.61538461538464 h 10 v 156.38461538461536 h -10 Z"></path> width="10"
height="156.38461538461536"
x="287"
y="43.61538461538464"
radius="0"
class="recharts-rectangle"
d="M 287,43.61538461538464 h 10 v 156.38461538461536 h -10 Z"
/>
</g> </g>
<g class="recharts-layer recharts-bar-rectangle"> <g class="recharts-layer recharts-bar-rectangle">
<path fill="#1e88e5" width="10" height="118.38461538461539" x="341.5" <path
y="81.61538461538461" radius="0" class="recharts-rectangle" fill="#1e88e5"
d="M 341.5,81.61538461538461 h 10 v 118.38461538461539 h -10 Z"></path> width="10"
height="118.38461538461539"
x="341.5"
y="81.61538461538461"
radius="0"
class="recharts-rectangle"
d="M 341.5,81.61538461538461 h 10 v 118.38461538461539 h -10 Z"
/>
</g> </g>
<g class="recharts-layer recharts-bar-rectangle"> <g class="recharts-layer recharts-bar-rectangle">
<path fill="#1e88e5" width="10" height="138.84615384615384" x="396" y="61.15384615384616" <path
radius="0" class="recharts-rectangle" fill="#1e88e5"
d="M 396,61.15384615384616 h 10 v 138.84615384615384 h -10 Z"></path> width="10"
height="138.84615384615384"
x="396"
y="61.15384615384616"
radius="0"
class="recharts-rectangle"
d="M 396,61.15384615384616 h 10 v 138.84615384615384 h -10 Z"
/>
</g> </g>
<g class="recharts-layer recharts-bar-rectangle"> <g class="recharts-layer recharts-bar-rectangle">
<path fill="#1e88e5" width="10" height="175.3846153846154" x="450.5" <path
y="24.615384615384613" radius="0" class="recharts-rectangle" fill="#1e88e5"
d="M 450.5,24.615384615384613 h 10 v 175.3846153846154 h -10 Z"></path> width="10"
height="175.3846153846154"
x="450.5"
y="24.615384615384613"
radius="0"
class="recharts-rectangle"
d="M 450.5,24.615384615384613 h 10 v 175.3846153846154 h -10 Z"
/>
</g> </g>
<g class="recharts-layer recharts-bar-rectangle"> <g class="recharts-layer recharts-bar-rectangle">
<path fill="#1e88e5" width="10" height="155.65384615384613" x="505" y="44.34615384615387" <path
radius="0" class="recharts-rectangle" fill="#1e88e5"
d="M 505,44.34615384615387 h 10 v 155.65384615384613 h -10 Z"></path> width="10"
height="155.65384615384613"
x="505"
y="44.34615384615387"
radius="0"
class="recharts-rectangle"
d="M 505,44.34615384615387 h 10 v 155.65384615384613 h -10 Z"
/>
</g> </g>
<g class="recharts-layer recharts-bar-rectangle"> <g class="recharts-layer recharts-bar-rectangle">
<path fill="#1e88e5" width="10" height="179.76923076923077" x="559.5" <path
y="20.230769230769226" radius="0" class="recharts-rectangle" fill="#1e88e5"
d="M 559.5,20.230769230769226 h 10 v 179.76923076923077 h -10 Z"></path> width="10"
height="179.76923076923077"
x="559.5"
y="20.230769230769226"
radius="0"
class="recharts-rectangle"
d="M 559.5,20.230769230769226 h 10 v 179.76923076923077 h -10 Z"
/>
</g> </g>
<g class="recharts-layer recharts-bar-rectangle"> <g class="recharts-layer recharts-bar-rectangle">
<path fill="#1e88e5" width="10" height="173.19230769230768" x="614" y="26.80769230769232" <path
radius="0" class="recharts-rectangle" fill="#1e88e5"
d="M 614,26.80769230769232 h 10 v 173.19230769230768 h -10 Z"></path> width="10"
height="173.19230769230768"
x="614"
y="26.80769230769232"
radius="0"
class="recharts-rectangle"
d="M 614,26.80769230769232 h 10 v 173.19230769230768 h -10 Z"
/>
</g> </g>
<g class="recharts-layer recharts-bar-rectangle"> <g class="recharts-layer recharts-bar-rectangle">
<path fill="#1e88e5" width="10" height="146.15384615384616" x="668.5" <path
y="53.84615384615384" radius="0" class="recharts-rectangle" fill="#1e88e5"
d="M 668.5,53.84615384615384 h 10 v 146.15384615384616 h -10 Z"></path> width="10"
height="146.15384615384616"
x="668.5"
y="53.84615384615384"
radius="0"
class="recharts-rectangle"
d="M 668.5,53.84615384615384 h 10 v 146.15384615384616 h -10 Z"
/>
</g> </g>
</g> </g>
</g> </g>
</g> </g>
</svg> </svg>
<div class="recharts-tooltip-wrapper" <div
style="pointer-events: none; visibility: hidden; position: absolute; top: 0px; transform: translate(538.875px, 126px);"> class="recharts-tooltip-wrapper"
</div> style="pointer-events: none; visibility: hidden; position: absolute; top: 0px; transform: translate(538.875px, 126px);"
/>
</div> </div>
<div style="position:absolute;width:0;height:0;visibility:hidden;display:none"></div> <div
style="position:absolute;width:0;height:0;visibility:hidden;display:none"
/>
</div> </div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,15 +1,18 @@
<!-- This example requires Tailwind CSS v2.0+ --> <!-- This example requires Tailwind CSS v2.0+ -->
<div <div
class="bg-white px-4 py-3 flex items-center justify-between border-t border-gray-200 sm:px-6"> class="bg-white px-4 py-3 flex items-center justify-between border-t border-gray-200 sm:px-6"
>
<div class="flex-1 flex justify-between sm:hidden"> <div class="flex-1 flex justify-between sm:hidden">
<a <a
href="#" href="#"
class="relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:text-gray-500"> class="relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:text-gray-500"
>
Previous Previous
</a> </a>
<a <a
href="#" href="#"
class="ml-3 relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:text-gray-500"> class="ml-3 relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:text-gray-500"
>
Next Next
</a> </a>
</div> </div>
@ -28,10 +31,12 @@
<div> <div>
<nav <nav
class="relative z-0 inline-flex shadow-sm -space-x-px" class="relative z-0 inline-flex shadow-sm -space-x-px"
aria-label="Pagination"> aria-label="Pagination"
>
<a <a
href="#" href="#"
class="relative inline-flex items-center px-2 py-2 rounded-l-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50"> class="relative inline-flex items-center px-2 py-2 rounded-l-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50"
>
<span class="sr-only">Previous</span> <span class="sr-only">Previous</span>
<!-- Heroicon name: chevron-left --> <!-- Heroicon name: chevron-left -->
<svg <svg
@ -39,50 +44,60 @@
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20" viewBox="0 0 20 20"
fill="currentColor" fill="currentColor"
aria-hidden="true"> aria-hidden="true"
>
<path <path
fill-rule="evenodd" fill-rule="evenodd"
d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z"
clip-rule="evenodd" /> clip-rule="evenodd"
/>
</svg> </svg>
</a> </a>
<a <a
href="#" href="#"
class="relative inline-flex items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50"> class="relative inline-flex items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50"
>
1 1
</a> </a>
<a <a
href="#" href="#"
class="relative inline-flex items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50"> class="relative inline-flex items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50"
>
2 2
</a> </a>
<a <a
href="#" href="#"
class="hidden md:inline-flex relative items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50"> class="hidden md:inline-flex relative items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50"
>
3 3
</a> </a>
<span <span
class="relative inline-flex items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700"> class="relative inline-flex items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700"
>
... ...
</span> </span>
<a <a
href="#" href="#"
class="hidden md:inline-flex relative items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50"> class="hidden md:inline-flex relative items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50"
>
8 8
</a> </a>
<a <a
href="#" href="#"
class="relative inline-flex items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50"> class="relative inline-flex items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50"
>
9 9
</a> </a>
<a <a
href="#" href="#"
class="relative inline-flex items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50"> class="relative inline-flex items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50"
>
10 10
</a> </a>
<a <a
href="#" href="#"
class="relative inline-flex items-center px-2 py-2 rounded-r-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50"> class="relative inline-flex items-center px-2 py-2 rounded-r-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50"
>
<span class="sr-only">Next</span> <span class="sr-only">Next</span>
<!-- Heroicon name: chevron-right --> <!-- Heroicon name: chevron-right -->
<svg <svg
@ -90,11 +105,13 @@
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20" viewBox="0 0 20 20"
fill="currentColor" fill="currentColor"
aria-hidden="true"> aria-hidden="true"
>
<path <path
fill-rule="evenodd" fill-rule="evenodd"
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
clip-rule="evenodd" /> clip-rule="evenodd"
/>
</svg> </svg>
</a> </a>
</nav> </nav>

View File

@ -7,14 +7,14 @@
</div> </div>
</div> </div>
</div> </div>
<div <div class="w-full p-4 mb-4 rounded-lg bg-white border border-grey-100">
class="w-full p-4 mb-4 rounded-lg bg-white border border-grey-100">
<div class="flex flex-row items-center justify-start p-4"> <div class="flex flex-row items-center justify-start p-4">
<div class="flex-shrink-0 w-24"> <div class="flex-shrink-0 w-24">
<img <img
src="/images/faces/m1.png" src="/images/faces/m1.png"
alt="media" alt="media"
class="shadow rounded-full h-20 w-20 shadow-outline mb-2" /> class="shadow rounded-full h-20 w-20 shadow-outline mb-2"
/>
</div> </div>
<div class="py-2 px-2"> <div class="py-2 px-2">
<p class="text-base font-bold whitespace-no-wrap">Lucas Smith</p> <p class="text-base font-bold whitespace-no-wrap">Lucas Smith</p>
@ -22,7 +22,8 @@
Vital Database Dude Vital Database Dude
</p> </p>
<div <div
class="flex flex-row items-center justify-start w-full py-1 space-x-2"> class="flex flex-row items-center justify-start w-full py-1 space-x-2"
>
<svg <svg
stroke="currentColor" stroke="currentColor"
fill="none" fill="none"
@ -33,8 +34,11 @@
class="stroke-current text-xl text-twitter" class="stroke-current text-xl text-twitter"
height="1em" height="1em"
width="1em" width="1em"
xmlns="http://www.w3.org/2000/svg"><path xmlns="http://www.w3.org/2000/svg"
d="M23 3a10.9 10.9 0 0 1-3.14 1.53 4.48 4.48 0 0 0-7.86 3v1A10.66 10.66 0 0 1 3 4s-4 9 5 13a11.64 11.64 0 0 1-7 2c9 5 20 0 20-11.5a4.5 4.5 0 0 0-.08-.83A7.72 7.72 0 0 0 23 3z" /></svg><svg ><path
d="M23 3a10.9 10.9 0 0 1-3.14 1.53 4.48 4.48 0 0 0-7.86 3v1A10.66 10.66 0 0 1 3 4s-4 9 5 13a11.64 11.64 0 0 1-7 2c9 5 20 0 20-11.5a4.5 4.5 0 0 0-.08-.83A7.72 7.72 0 0 0 23 3z"
/></svg
><svg
stroke="currentColor" stroke="currentColor"
fill="none" fill="none"
stroke-width="2" stroke-width="2"
@ -44,8 +48,11 @@
class="stroke-current text-xl text-facebook" class="stroke-current text-xl text-facebook"
height="1em" height="1em"
width="1em" width="1em"
xmlns="http://www.w3.org/2000/svg"><path xmlns="http://www.w3.org/2000/svg"
d="M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z" /></svg><svg ><path
d="M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z"
/></svg
><svg
stroke="currentColor" stroke="currentColor"
fill="none" fill="none"
stroke-width="2" stroke-width="2"
@ -55,21 +62,21 @@
class="stroke-current text-xl text-instagram" class="stroke-current text-xl text-instagram"
height="1em" height="1em"
width="1em" width="1em"
xmlns="http://www.w3.org/2000/svg"><rect xmlns="http://www.w3.org/2000/svg"
x="2" ><rect x="2" y="2" width="20" height="20" rx="5" ry="5" />
y="2"
width="20"
height="20"
rx="5"
ry="5" />
<path d="M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z" /> <path d="M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z" />
<line x1="17.5" y1="6.5" x2="17.5" y2="6.5" /></svg> <line x1="17.5" y1="6.5" x2="17.5" y2="6.5" /></svg
>
</div> </div>
</div> </div>
<div class="ml-auto flex-shrink-0 space-x-2 hidden lg:flex"> <div class="ml-auto flex-shrink-0 space-x-2 hidden lg:flex">
<button <button
class="btn btn-default btn-rounded bg-blue-500 hover:bg-blue-600 text-white">Subscribe</button><button class="btn btn-default btn-rounded bg-blue-500 hover:bg-blue-600 text-white"
class="btn btn-default btn-rounded bg-blue-500 hover:bg-blue-600 text-white">Follow</button> >Subscribe</button
><button
class="btn btn-default btn-rounded bg-blue-500 hover:bg-blue-600 text-white"
>Follow</button
>
</div> </div>
</div> </div>
<div class="flex flex-wrap"> <div class="flex flex-wrap">
@ -77,14 +84,19 @@
<div class="flex flex-wrap flex-col w-full tabs"> <div class="flex flex-wrap flex-col w-full tabs">
<div class="flex lg:flex-wrap flex-row lg:space-x-2"> <div class="flex lg:flex-wrap flex-row lg:space-x-2">
<div class="flex-none"> <div class="flex-none">
<button class="tab tab-underline tab-active" type="button">Account <button class="tab tab-underline tab-active" type="button"
settings</button> >Account settings</button
>
</div> </div>
<div class="flex-none"> <div class="flex-none">
<button class="tab tab-underline" type="button">Email preferences</button> <button class="tab tab-underline" type="button"
>Email preferences</button
>
</div> </div>
<div class="flex-none"> <div class="flex-none">
<button class="tab tab-underline" type="button">Security settings</button> <button class="tab tab-underline" type="button"
>Security settings</button
>
</div> </div>
</div> </div>
<div class="tab-content block"> <div class="tab-content block">
@ -93,51 +105,64 @@
<form class="form flex flex-wrap w-full"> <form class="form flex flex-wrap w-full">
<div class="w-full"> <div class="w-full">
<div class="form-element"> <div class="form-element">
<div class="form-label">First name</div><input <div class="form-label">First name</div>
<input
name="first-name" name="first-name"
type="text" type="text"
class="form-input " class="form-input"
placeholder="Enter you first name" /> placeholder="Enter you first name"
/>
</div> </div>
<div class="form-element"> <div class="form-element">
<div class="form-label">Last name</div><input <div class="form-label">Last name</div>
<input
name="last-name" name="last-name"
type="text" type="text"
class="form-input " class="form-input"
placeholder="Enter you last name" /> placeholder="Enter you last name"
/>
</div> </div>
<div class="form-element"> <div class="form-element">
<div class="form-label">Email address</div><input <div class="form-label">Email address</div>
<input
name="email" name="email"
type="email" type="email"
class="form-input " class="form-input"
placeholder="Enter you email address" /> placeholder="Enter you email address"
/>
</div> </div>
<div class="form-element"> <div class="form-element">
<div class="form-label">Company</div><input <div class="form-label">Company</div>
<input
name="company" name="company"
type="text" type="text"
class="form-input " class="form-input"
placeholder="Enter you company" /> placeholder="Enter you company"
/>
</div> </div>
<div class="form-element"> <div class="form-element">
<div class="form-label">Position</div><input <div class="form-label">Position</div>
<input
name="position" name="position"
type="text" type="text"
class="form-input " class="form-input"
placeholder="Enter you position" /> placeholder="Enter you position"
/>
</div> </div>
<div class="form-element"> <div class="form-element">
<div class="form-label">Language</div><select <div class="form-label">Language</div>
name="language" <select name="language" class="form-select"
class="form-select "><option>Select language</option> ><option>Select language</option>
<option value="english">English</option> <option value="english">English</option>
<option value="spanish">Spanish</option> <option value="spanish">Spanish</option>
<option value="portuguese">Portuguese</option></select> <option value="portuguese">Portuguese</option></select
>
</div> </div>
</div><input </div>
<input
type="submit" type="submit"
class="btn btn-default bg-blue-500 hover:bg-blue-600 text-white btn-rounded" /> class="btn btn-default bg-blue-500 hover:bg-blue-600 text-white btn-rounded"
/>
</form> </form>
</div> </div>
</div> </div>
@ -148,56 +173,70 @@
<form class="form flex flex-wrap w-full"> <form class="form flex flex-wrap w-full">
<div class="w-full"> <div class="w-full">
<div class="form-element"> <div class="form-element">
<div class="form-label">Current email</div><input <div class="form-label">Current email</div>
<input
name="email" name="email"
type="email" type="email"
class="form-input " class="form-input"
placeholder="Enter you current email address" /> placeholder="Enter you current email address"
/>
</div> </div>
<div class="form-element"> <div class="form-element">
<div class="form-label">New email</div><input <div class="form-label">New email</div>
<input
name="email" name="email"
type="email" type="email"
class="form-input " class="form-input"
placeholder="Enter you new email address" /> placeholder="Enter you new email address"
/>
</div> </div>
<div class="form-element"> <div class="form-element">
<div class="form-label">Daily updates</div> <div class="form-label">Daily updates</div>
<div class="flex items-center justify-start space-x-2"> <div class="flex items-center justify-start space-x-2">
<label <label class="flex items-center justify-start space-x-2"
class="flex items-center justify-start space-x-2"><input ><input
type="radio" type="radio"
name="daily-updates" name="daily-updates"
class="form-radio h-4 w-4 " class="form-radio h-4 w-4"
value="yes" /><span value="yes"
class="">Yes</span></label><label /><span class="">Yes</span></label
class="flex items-center justify-start space-x-2"><input ><label
class="flex items-center justify-start space-x-2"
><input
type="radio" type="radio"
name="daily-updates" name="daily-updates"
class="form-radio h-4 w-4 " class="form-radio h-4 w-4"
value="no" /><span class="">No</span></label> value="no"
/><span class="">No</span></label
>
</div> </div>
</div> </div>
<div class="form-element"> <div class="form-element">
<div class="form-label">Weekly updates</div> <div class="form-label">Weekly updates</div>
<div class="flex items-center justify-start space-x-2"> <div class="flex items-center justify-start space-x-2">
<label <label class="flex items-center justify-start space-x-2"
class="flex items-center justify-start space-x-2"><input ><input
type="radio" type="radio"
name="weekle-updates" name="weekle-updates"
class="form-radio h-4 w-4 " class="form-radio h-4 w-4"
value="yes" /><span value="yes"
class="">Yes</span></label><label /><span class="">Yes</span></label
class="flex items-center justify-start space-x-2"><input ><label
class="flex items-center justify-start space-x-2"
><input
type="radio" type="radio"
name="weekle-updates" name="weekle-updates"
class="form-radio h-4 w-4 " class="form-radio h-4 w-4"
value="no" /><span class="">No</span></label> value="no"
/><span class="">No</span></label
>
</div> </div>
</div> </div>
</div><input </div>
<input
type="submit" type="submit"
class="btn btn-default bg-blue-500 hover:bg-blue-600 text-white btn-rounded" /> class="btn btn-default bg-blue-500 hover:bg-blue-600 text-white btn-rounded"
/>
</form> </form>
</div> </div>
</div> </div>
@ -208,29 +247,37 @@
<form class="form flex flex-wrap w-full"> <form class="form flex flex-wrap w-full">
<div class="w-full"> <div class="w-full">
<div class="form-element"> <div class="form-element">
<div class="form-label">Current password</div><input <div class="form-label">Current password</div>
<input
name="current-password" name="current-password"
type="password" type="password"
class="form-input " class="form-input"
placeholder="Enter your current password" /> placeholder="Enter your current password"
/>
</div> </div>
<div class="form-element"> <div class="form-element">
<div class="form-label">New password</div><input <div class="form-label">New password</div>
<input
name="new-password" name="new-password"
type="password" type="password"
class="form-input " class="form-input"
placeholder="Enter your new password" /> placeholder="Enter your new password"
/>
</div> </div>
<div class="form-element"> <div class="form-element">
<div class="form-label">Confirm new password</div><input <div class="form-label">Confirm new password</div>
<input
name="confirm-new-password" name="confirm-new-password"
type="password" type="password"
class="form-input " class="form-input"
placeholder="Enter your new password confirmation" /> placeholder="Enter your new password confirmation"
/>
</div> </div>
</div><input </div>
<input
type="submit" type="submit"
class="btn btn-default bg-blue-500 hover:bg-blue-600 text-white btn-rounded" /> class="btn btn-default bg-blue-500 hover:bg-blue-600 text-white btn-rounded"
/>
</form> </form>
</div> </div>
</div> </div>

View File

@ -3,32 +3,37 @@
<div class="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8"> <div class="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
<div class="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8"> <div class="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8">
<div <div
class="shadow overflow-hidden border-b border-gray-200 sm:rounded-lg"> class="shadow overflow-hidden border-b border-gray-200 sm:rounded-lg"
>
<table class="min-w-full divide-y divide-gray-200"> <table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50"> <thead class="bg-gray-50">
<tr> <tr>
<th <th
scope="col" scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Name Name
</th> </th>
<th <th
scope="col" scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Title Title
</th> </th>
<th <th
scope="col" scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Status Status
</th> </th>
<th <th
scope="col" scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Role Role
</th> </th>
<th scope="col" class="relative px-6 py-3"> <th scope="col" class="relative px-6 py-3">
<span class="sr-only">{$_('edit')}</span> <span class="sr-only">{$_("edit")}</span>
</th> </th>
</tr> </tr>
</thead> </thead>
@ -40,7 +45,8 @@
<img <img
class="h-10 w-10 rounded-full" class="h-10 w-10 rounded-full"
src="https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-1.2.1&amp;ixid=eyJhcHBfaWQiOjEyMDd9&amp;auto=format&amp;fit=facearea&amp;facepad=4&amp;w=256&amp;h=256&amp;q=60" src="https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-1.2.1&amp;ixid=eyJhcHBfaWQiOjEyMDd9&amp;auto=format&amp;fit=facearea&amp;facepad=4&amp;w=256&amp;h=256&amp;q=60"
alt="" /> alt=""
/>
</div> </div>
<div class="ml-4"> <div class="ml-4">
<div class="text-sm font-medium text-gray-900"> <div class="text-sm font-medium text-gray-900">
@ -60,7 +66,8 @@
</td> </td>
<td class="px-6 py-4 whitespace-nowrap"> <td class="px-6 py-4 whitespace-nowrap">
<span <span
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800"> class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800"
>
Active Active
</span> </span>
</td> </td>
@ -68,10 +75,11 @@
Admin Admin
</td> </td>
<td <td
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"
<a >
href="#" <a href="#" class="text-indigo-600 hover:text-indigo-900"
class="text-indigo-600 hover:text-indigo-900">{$_('edit')}</a> >{$_("edit")}</a
>
</td> </td>
</tr> </tr>

View File

@ -1,18 +1,23 @@
<h3 class="text-lg">Tabs</h3> <h3 class="text-lg">Tabs</h3>
<div <div
class="w-full flex sm:border-b sm:border-gray-300 relative flex-col sm:flex-row"> class="w-full flex sm:border-b sm:border-gray-300 relative flex-col sm:flex-row"
>
<div <div
class="flex-1 sm:text-center font-medium pb-3 cursor-pointer hover:text-blue-400 false"> class="flex-1 sm:text-center font-medium pb-3 cursor-pointer hover:text-blue-400 false"
>
1 1
</div> </div>
<div <div
class="flex-1 sm:text-center font-medium pb-3 cursor-pointer hover:text-blue-400 false"> class="flex-1 sm:text-center font-medium pb-3 cursor-pointer hover:text-blue-400 false"
>
2 2
</div> </div>
<div <div
class="flex-1 sm:text-center font-medium pb-3 cursor-pointer hover:text-blue-400 false"> class="flex-1 sm:text-center font-medium pb-3 cursor-pointer hover:text-blue-400 false"
>
3 3
</div> </div>
<div <div
class="hidden sm:block absolute bottom-0 left-0 h-1 bg-blue-400 transition-transform duration-300 ease-out w-1/4 transform translate-x-double" /> class="hidden sm:block absolute bottom-0 left-0 h-1 bg-blue-400 transition-transform duration-300 ease-out w-1/4 transform translate-x-double"
/>
</div> </div>

View File

@ -1,6 +1,7 @@
<div> <div>
<div <div
class="text-xs inline-flex items-center font-bold leading-sm uppercase px-3 py-1 bg-blue-200 text-blue-700 rounded-full"> class="text-xs inline-flex items-center font-bold leading-sm uppercase px-3 py-1 bg-blue-200 text-blue-700 rounded-full"
>
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="16" width="16"
@ -11,7 +12,8 @@
stroke-width="2" stroke-width="2"
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
class="feather feather-bell-off mr-2"> class="feather feather-bell-off mr-2"
>
<path d="M13.73 21a2 2 0 0 1-3.46 0" /> <path d="M13.73 21a2 2 0 0 1-3.46 0" />
<path d="M18.63 13A17.89 17.89 0 0 1 18 8" /> <path d="M18.63 13A17.89 17.89 0 0 1 18 8" />
<path d="M6.26 6.26A5.86 5.86 0 0 0 6 8c0 7-3 9-3 9h14" /> <path d="M6.26 6.26A5.86 5.86 0 0 0 6 8c0 7-3 9-3 9h14" />
@ -22,7 +24,8 @@
</div> </div>
<div <div
class="ml-4 text-xs inline-flex items-center font-bold leading-sm uppercase px-3 py-1 bg-green-200 text-green-700 rounded-full"> class="ml-4 text-xs inline-flex items-center font-bold leading-sm uppercase px-3 py-1 bg-green-200 text-green-700 rounded-full"
>
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="16" width="16"
@ -33,7 +36,8 @@
stroke-width="2" stroke-width="2"
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
class="feather feather-arrow-right mr-2"> class="feather feather-arrow-right mr-2"
>
<line x1="5" y1="12" x2="19" y2="12" /> <line x1="5" y1="12" x2="19" y2="12" />
<polyline points="12 5 19 12 12 19" /> <polyline points="12 5 19 12 12 19" />
</svg> </svg>
@ -41,7 +45,8 @@
</div> </div>
<div <div
class="ml-4 text-xs inline-flex items-center font-bold leading-sm uppercase px-3 py-1 bg-orange-200 text-orange-700 rounded-full"> class="ml-4 text-xs inline-flex items-center font-bold leading-sm uppercase px-3 py-1 bg-orange-200 text-orange-700 rounded-full"
>
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="16" width="16"
@ -52,14 +57,16 @@
stroke-width="2" stroke-width="2"
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
class="feather feather-activity mr-2"> class="feather feather-activity mr-2"
>
<polyline points="22 12 18 12 15 21 9 3 6 12 2 12" /> <polyline points="22 12 18 12 15 21 9 3 6 12 2 12" />
</svg> </svg>
Tag Tag
</div> </div>
<div <div
class="ml-4 text-xs inline-flex items-center font-bold leading-sm uppercase px-3 py-1 bg-red-200 text-red-700 rounded-full"> class="ml-4 text-xs inline-flex items-center font-bold leading-sm uppercase px-3 py-1 bg-red-200 text-red-700 rounded-full"
>
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="16" width="16"
@ -70,7 +77,8 @@
stroke-width="2" stroke-width="2"
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
class="feather feather-archive mr-2"> class="feather feather-archive mr-2"
>
<polyline points="21 8 21 21 3 21 3 8" /> <polyline points="21 8 21 21 3 21 3 8" />
<rect x="1" y="3" width="22" height="5" /> <rect x="1" y="3" width="22" height="5" />
<line x1="10" y1="12" x2="14" y2="12" /> <line x1="10" y1="12" x2="14" y2="12" />
@ -79,7 +87,8 @@
</div> </div>
<div <div
class="ml-4 text-xs inline-flex items-center font-bold leading-sm uppercase px-3 py-1 rounded-full bg-white text-gray-700 border"> class="ml-4 text-xs inline-flex items-center font-bold leading-sm uppercase px-3 py-1 rounded-full bg-white text-gray-700 border"
>
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="16" width="16"
@ -90,10 +99,12 @@
stroke-width="2" stroke-width="2"
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
class="feather feather-hard-drive mr-2"> class="feather feather-hard-drive mr-2"
>
<line x1="22" y1="12" x2="2" y2="12" /> <line x1="22" y1="12" x2="2" y2="12" />
<path <path
d="M5.45 5.11L2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z" /> d="M5.45 5.11L2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"
/>
<line x1="6" y1="16" x2="6.01" y2="16" /> <line x1="6" y1="16" x2="6.01" y2="16" />
<line x1="10" y1="16" x2="10.01" y2="16" /> <line x1="10" y1="16" x2="10.01" y2="16" />
</svg> </svg>

View File

@ -1,7 +1,7 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import { clickOutside } from "../base/outsideclick"; import { clickOutside } from "../base/outsideclick";
import { import {
RunnerService, RunnerService,
RunnerTeamService, RunnerTeamService,
@ -9,7 +9,6 @@
} from "@odit/lfk-client-js"; } from "@odit/lfk-client-js";
import isEmail from "validator/es/lib/isEmail"; import isEmail from "validator/es/lib/isEmail";
import isMobilePhone from "validator/es/lib/isMobilePhone"; import isMobilePhone from "validator/es/lib/isMobilePhone";
import Toastify from "toastify-js";
import Select from "svelte-select"; import Select from "svelte-select";
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
@ -78,10 +77,6 @@
function submit() { function submit() {
if (processed_last_submit === true) { if (processed_last_submit === true) {
processed_last_submit = false; processed_last_submit = false;
const toast = Toastify({
text: $_("runner-is-being-added"),
duration: -1,
}).showToast();
let postdata = { let postdata = {
group: selected_team, group: selected_team,
firstname: firstname_input_value, firstname: firstname_input_value,
@ -96,6 +91,7 @@
if (email_input_value) { if (email_input_value) {
postdata.email = email_input_value; postdata.email = email_input_value;
} }
toast.loading($_("runner-is-being-added"));
RunnerService.runnerControllerPost(postdata) RunnerService.runnerControllerPost(postdata)
.then((result) => { .then((result) => {
firstname_input_value = ""; firstname_input_value = "";
@ -104,11 +100,8 @@
email_input_value = ""; email_input_value = "";
modal_open = false; modal_open = false;
// //
Toastify({ toast.dismiss();
text: $_("runner-added"), toast.success($_("runner-added"));
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
dispatch("created", { runners: [result] }); dispatch("created", { runners: [result] });
}) })
.catch((err) => { .catch((err) => {
@ -116,8 +109,6 @@
}) })
.finally(() => { .finally(() => {
processed_last_submit = true; processed_last_submit = true;
//
toast.hideToast();
}); });
} }
} }
@ -126,58 +117,70 @@
{#if modal_open} {#if modal_open}
<div <div
class="fixed z-10 inset-0 overflow-y-auto" class="fixed z-10 inset-0 overflow-y-auto"
use:clickOutside use:clickOutside
on:click_outside={() => { on:click_outside={() => {
modal_open = false; modal_open = false;
}}> }}
>
<div <div
class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> 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="fixed inset-0 transition-opacity" aria-hidden="true">
<div <div
class="absolute inset-0 bg-gray-500 opacity-75" class="absolute inset-0 bg-gray-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 bg-white rounded-lg text-left shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full" 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" 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"> <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="sm:flex sm:items-start">
<div <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"> 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 <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" viewBox="0 0 24 24"
class="h-6 w-6 text-blue-600" class="h-6 w-6 text-blue-600"
fill="currentColor" fill="currentColor"
width="24" width="24"
height="24"><path fill="none" d="M0 0h24v24H0z" /> height="24"
><path fill="none" d="M0 0h24v24H0z" />
<path <path
d="M9.83 8.79L8 9.456V13H6V8.05h.015l5.268-1.918c.244-.093.51-.14.782-.131a2.616 2.616 0 0 1 2.427 1.82c.186.583.356.977.51 1.182A4.992 4.992 0 0 0 19 11v2a6.986 6.986 0 0 1-5.402-2.547l-.581 3.297L15 15.67V23h-2v-5.986l-2.05-1.987-.947 4.298-6.894-1.215.348-1.97 4.924.868L9.83 8.79zM13.5 5.5a2 2 0 1 1 0-4 2 2 0 0 1 0 4z" /></svg> d="M9.83 8.79L8 9.456V13H6V8.05h.015l5.268-1.918c.244-.093.51-.14.782-.131a2.616 2.616 0 0 1 2.427 1.82c.186.583.356.977.51 1.182A4.992 4.992 0 0 0 19 11v2a6.986 6.986 0 0 1-5.402-2.547l-.581 3.297L15 15.67V23h-2v-5.986l-2.05-1.987-.947 4.298-6.894-1.215.348-1.97 4.924.868L9.83 8.79zM13.5 5.5a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"
/></svg
>
</div> </div>
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"> <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"> <h3 class="text-lg leading-6 font-medium text-gray-900">
{$_('create-a-new-runner')} {$_("create-a-new-runner")}
</h3> </h3>
<div class="mt-2 mb-6"> <div class="mt-2 mb-6">
<p class="text-sm text-gray-500"> <p class="text-sm text-gray-500">
{$_('please-provide-the-required-information-to-add-a-new-runner')} {$_(
"please-provide-the-required-information-to-add-a-new-runner"
)}
</p> </p>
</div> </div>
<div class="grid grid-cols-6 gap-6"> <div class="grid grid-cols-6 gap-6">
<div class="col-span-6"> <div class="col-span-6">
<label <label
for="firstname" for="firstname"
class="block text-sm font-medium text-gray-700">{$_('first-name')}</label> class="block text-sm font-medium text-gray-700"
>{$_("first-name")}</label
>
<input <input
use:focus use:focus
autocomplete="off" autocomplete="off"
placeholder={$_('first-name')} placeholder={$_("first-name")}
class:border-red-500={!isFirstnameValid} class:border-red-500={!isFirstnameValid}
class:focus:border-red-500={!isFirstnameValid} class:focus:border-red-500={!isFirstnameValid}
class:focus:ring-red-500={!isFirstnameValid} class:focus:ring-red-500={!isFirstnameValid}
@ -185,34 +188,41 @@
bind:this={firstname_input} bind:this={firstname_input}
type="text" type="text"
name="firstname" name="firstname"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> class="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"
/>
{#if !isFirstnameValid} {#if !isFirstnameValid}
<span <span
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
{$_('first-name-is-required')} >
{$_("first-name-is-required")}
</span> </span>
{/if} {/if}
</div> </div>
<div class="col-span-6"> <div class="col-span-6">
<label <label
for="trackname" for="trackname"
class="block text-sm font-medium text-gray-700">{$_('middle-name')}</label> class="block text-sm font-medium text-gray-700"
>{$_("middle-name")}</label
>
<input <input
autocomplete="off" autocomplete="off"
placeholder={$_('middle-name')} placeholder={$_("middle-name")}
bind:value={middlename_input_value} bind:value={middlename_input_value}
bind:this={middlename_input} bind:this={middlename_input}
type="text" type="text"
name="trackname" name="trackname"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> class="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 class="col-span-6"> <div class="col-span-6">
<label <label
for="lastname" for="lastname"
class="block text-sm font-medium text-gray-700">{$_('last-name')}</label> class="block text-sm font-medium text-gray-700"
>{$_("last-name")}</label
>
<input <input
autocomplete="off" autocomplete="off"
placeholder={$_('last-name')} placeholder={$_("last-name")}
class:border-red-500={!isLastnameValid} class:border-red-500={!isLastnameValid}
class:focus:border-red-500={!isLastnameValid} class:focus:border-red-500={!isLastnameValid}
class:focus:ring-red-500={!isLastnameValid} class:focus:ring-red-500={!isLastnameValid}
@ -220,41 +230,49 @@
bind:this={lastname_input} bind:this={lastname_input}
type="text" type="text"
name="lastname" name="lastname"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> class="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"
/>
{#if !isLastnameValid} {#if !isLastnameValid}
<span <span
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
{$_('last-name-is-required')} >
{$_("last-name-is-required")}
</span> </span>
{/if} {/if}
</div> </div>
<div class="col-span-6"> <div class="col-span-6">
<label <label
for="team" for="team"
class="block text-sm font-medium text-gray-700">{$_('team')}</label> class="block text-sm font-medium text-gray-700"
>{$_("team")}</label
>
<Select <Select
containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"
itemFilter={(label, filterText, option) => label itemFilter={(label, filterText, option) =>
.toLowerCase() label.toLowerCase().includes(filterText.toLowerCase()) ||
.includes( option.value.id
filterText.toLowerCase()
) || option.value.id
.toString() .toString()
.startsWith(filterText.toLowerCase())} .startsWith(filterText.toLowerCase())}
items={orgs.concat(teams)} items={orgs.concat(teams)}
showChevron={true} showChevron={true}
placeholder={$_('search-for-an-organization-or-team-by-name-or-id')} placeholder={$_(
noOptionsMessage={$_('no-organization-or-team-found')} "search-for-an-organization-or-team-by-name-or-id"
on:select={(selectedValue) => (selected_team = selectedValue.detail.value.id)} )}
on:clear={() => (selected_team = null)} /> noOptionsMessage={$_("no-organization-or-team-found")}
on:select={(selectedValue) =>
(selected_team = selectedValue.detail.value.id)}
on:clear={() => (selected_team = null)}
/>
</div> </div>
<div class="col-span-6"> <div class="col-span-6">
<label <label
for="phone" for="phone"
class="block text-sm font-medium text-gray-700">{$_('phone')}</label> class="block text-sm font-medium text-gray-700"
>{$_("phone")}</label
>
<input <input
autocomplete="off" autocomplete="off"
placeholder={$_('phone')} placeholder={$_("phone")}
class:border-red-500={!isPhoneValidOrEmpty} class:border-red-500={!isPhoneValidOrEmpty}
class:focus:border-red-500={!isPhoneValidOrEmpty} class:focus:border-red-500={!isPhoneValidOrEmpty}
class:focus:ring-red-500={!isPhoneValidOrEmpty} class:focus:ring-red-500={!isPhoneValidOrEmpty}
@ -262,21 +280,27 @@
bind:this={phone_input} bind:this={phone_input}
type="tel" type="tel"
name="phone" name="phone"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> 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"
/>
{#if !isPhoneValidOrEmpty} {#if !isPhoneValidOrEmpty}
<span <span
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
{@html $_('the-provided-phone-number-is-invalid-less-than-br-greater-than-please-enter-a-valid-international-number')} >
{@html $_(
"the-provided-phone-number-is-invalid-less-than-br-greater-than-please-enter-a-valid-international-number"
)}
</span> </span>
{/if} {/if}
</div> </div>
<div class="col-span-6"> <div class="col-span-6">
<label <label
for="email" for="email"
class="block text-sm font-medium text-gray-700">{$_('e-mail-adress')}</label> class="block text-sm font-medium text-gray-700"
>{$_("e-mail-adress")}</label
>
<input <input
autocomplete="off" autocomplete="off"
placeholder={$_('e-mail-adress')} placeholder={$_("e-mail-adress")}
class:border-red-500={!isEmailValidOrEmpty} class:border-red-500={!isEmailValidOrEmpty}
class:focus:border-red-500={!isEmailValidOrEmpty} class:focus:border-red-500={!isEmailValidOrEmpty}
class:focus:ring-red-500={!isEmailValidOrEmpty} class:focus:ring-red-500={!isEmailValidOrEmpty}
@ -284,11 +308,13 @@
bind:this={email_input} bind:this={email_input}
type="email" type="email"
name="email" name="email"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> class="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"
/>
{#if !isEmailValidOrEmpty} {#if !isEmailValidOrEmpty}
<span <span
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
{$_('valid-email-is-required')} >
{$_("valid-email-is-required")}
</span> </span>
{/if} {/if}
</div> </div>
@ -302,16 +328,18 @@
class:opacity-50={!createbtnenabled} class:opacity-50={!createbtnenabled}
on:click={submit} on:click={submit}
type="button" 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"> 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')} >
{$_("create")}
</button> </button>
<button <button
on:click={() => { on:click={() => {
modal_open = false; modal_open = false;
}} }}
type="button" 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"> 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')} >
{$_("cancel")}
</button> </button>
</div> </div>
</div> </div>

View File

@ -25,7 +25,7 @@
}); });
async function submit() { async function submit() {
dispatch("delete", { id: delete_runner.id }); dispatch("delete", { id: delete_runner.id });
modal_open=false; modal_open = false;
} }
</script> </script>

View File

@ -3,8 +3,7 @@
import { read as readXlsx, utils as xlsx_utils } from "xlsx"; import { read as readXlsx, utils as xlsx_utils } from "xlsx";
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import { clickOutside } from "../base/outsideclick"; import { clickOutside } from "../base/outsideclick";
import Toastify from "toastify-js";
import { import {
ImportService, ImportService,
RunnerTeamService, RunnerTeamService,
@ -12,6 +11,7 @@
} from "@odit/lfk-client-js"; } from "@odit/lfk-client-js";
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
import Select from "svelte-select"; import Select from "svelte-select";
import toast from "svelte-french-toast";
export let opened_from; export let opened_from;
export let passed_org; export let passed_org;
export let passed_orgs; export let passed_orgs;
@ -90,10 +90,7 @@
} }
function importAction() { function importAction() {
if (recent_processed === true) { if (recent_processed === true) {
const toast = Toastify({ toast.loading($_("runners-are-being-imported"));
text: $_("runners-are-being-imported"),
duration: -1,
}).showToast();
recent_processed = false; recent_processed = false;
const mapped = json_output.map(function (runner) { const mapped = json_output.map(function (runner) {
return { return {
@ -115,48 +112,30 @@
if (opened_from === "OrgOverview" || opened_from === "OrgDetail") { if (opened_from === "OrgOverview" || opened_from === "OrgDetail") {
ImportService.importControllerPostOrgsJson(org, mapped) ImportService.importControllerPostOrgsJson(org, mapped)
.then((resp) => { .then((resp) => {
toast.hideToast(); toast.dismiss();
recent_processed = true; recent_processed = true;
Toastify({ toast.success($_("import-finished"));
text: $_("import-finished"),
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
cancelModal(); cancelModal();
}) })
.catch((err) => { .catch((err) => {
toast.hideToast(); toast.dismiss();
recent_processed = true; recent_processed = true;
Toastify({ toast.error($_("error-during-import"));
text: $_("error-during-import"),
duration: 500,
backgroundColor:
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)",
}).showToast();
cancelModal(); cancelModal();
}); });
} }
if (opened_from === "TeamDetail") { if (opened_from === "TeamDetail") {
ImportService.importControllerPostTeamsJson(passed_team.id, mapped) ImportService.importControllerPostTeamsJson(passed_team.id, mapped)
.then((resp) => { .then((resp) => {
toast.hideToast(); toast.dismiss();
recent_processed = true; recent_processed = true;
Toastify({ toast.success($_("import-finished"));
text: $_("import-finished"),
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
cancelModal(); cancelModal();
}) })
.catch((err) => { .catch((err) => {
toast.hideToast(); toast.dismiss();
recent_processed = true; recent_processed = true;
Toastify({ toast.error($_("error-during-import"));
text: $_("error-during-import"),
duration: 500,
backgroundColor:
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)",
}).showToast();
cancelModal(); cancelModal();
}); });
} }
@ -169,24 +148,15 @@
) )
.then((resp) => { .then((resp) => {
dispatch("created", { runners: resp }); dispatch("created", { runners: resp });
toast.hideToast(); toast.dismiss();
recent_processed = true; recent_processed = true;
Toastify({ toast($_("import-finished"));
text: $_("import-finished"),
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
cancelModal(); cancelModal();
}) })
.catch((err) => { .catch((err) => {
toast.hideToast(); toast.dismiss();
recent_processed = true; recent_processed = true;
Toastify({ toast.error($_("error-during-import"));
text: $_("error-during-import"),
duration: 500,
backgroundColor:
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)",
}).showToast();
cancelModal(); cancelModal();
}); });
} }
@ -198,24 +168,15 @@
) )
.then((resp) => { .then((resp) => {
dispatch("created", { runners: resp }); dispatch("created", { runners: resp });
toast.hideToast(); toast.dismiss();
recent_processed = true; recent_processed = true;
Toastify({ toast($_("import-finished"));
text: $_('import-finished'),
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
cancelModal(); cancelModal();
}) })
.catch((err) => { .catch((err) => {
toast.hideToast(); toast.dismiss();
recent_processed = true; recent_processed = true;
Toastify({ toast.error($_("error-during-import"));
text: $_("error-during-import"),
duration: 500,
backgroundColor:
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)",
}).showToast();
cancelModal(); cancelModal();
}); });
} }
@ -227,43 +188,51 @@
{#if import_modal_open} {#if import_modal_open}
<div <div
class="fixed z-10 inset-0 overflow-y-auto" class="fixed z-10 inset-0 overflow-y-auto"
use:clickOutside use:clickOutside
on:click_outside={() => { on:click_outside={() => {
cancelModal(); cancelModal();
}}> }}
>
<div <div
class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> 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="fixed inset-0 transition-opacity" aria-hidden="true">
<div <div
class="absolute inset-0 bg-gray-500 opacity-75" class="absolute inset-0 bg-gray-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 bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-max sm:w-full" 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-max sm:w-full"
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"> <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="sm:flex sm:items-start">
<div <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"> 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 <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" viewBox="0 0 24 24"
class="h-6 w-6 text-blue-600" class="h-6 w-6 text-blue-600"
fill="currentColor" fill="currentColor"
width="24" width="24"
height="24"><path fill="none" d="M0 0h24v24H0z" /> height="24"
><path fill="none" d="M0 0h24v24H0z" />
<path <path
d="M9.83 8.79L8 9.456V13H6V8.05h.015l5.268-1.918c.244-.093.51-.14.782-.131a2.616 2.616 0 0 1 2.427 1.82c.186.583.356.977.51 1.182A4.992 4.992 0 0 0 19 11v2a6.986 6.986 0 0 1-5.402-2.547l-.581 3.297L15 15.67V23h-2v-5.986l-2.05-1.987-.947 4.298-6.894-1.215.348-1.97 4.924.868L9.83 8.79zM13.5 5.5a2 2 0 1 1 0-4 2 2 0 0 1 0 4z" /></svg> d="M9.83 8.79L8 9.456V13H6V8.05h.015l5.268-1.918c.244-.093.51-.14.782-.131a2.616 2.616 0 0 1 2.427 1.82c.186.583.356.977.51 1.182A4.992 4.992 0 0 0 19 11v2a6.986 6.986 0 0 1-5.402-2.547l-.581 3.297L15 15.67V23h-2v-5.986l-2.05-1.987-.947 4.298-6.894-1.215.348-1.97 4.924.868L9.83 8.79zM13.5 5.5a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"
/></svg
>
</div> </div>
<div class="mt-3 text-center sm:mt-0 sm:ml-2 sm:text-left w-full"> <div class="mt-3 text-center sm:mt-0 sm:ml-2 sm:text-left w-full">
<h3 class="text-lg leading-6 font-bold mt-2 text-gray-900"> <h3 class="text-lg leading-6 font-bold mt-2 text-gray-900">
{$_('runner-import')} {$_("runner-import")}
</h3> </h3>
</div> </div>
</div> </div>
@ -271,14 +240,15 @@
{#if json_output.length === 0} {#if json_output.length === 0}
<div class="mt-2 mb-6"> <div class="mt-2 mb-6">
<p class="text-sm text-gray-500"> <p class="text-sm text-gray-500">
{$_('please-provide-the-required-csv-xlsx-file')} {$_("please-provide-the-required-csv-xlsx-file")}
</p> </p>
</div> </div>
<div class="overflow-hidden relative mt-4 mb-4"> <div class="overflow-hidden relative mt-4 mb-4">
<input <input
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
bind:files bind:files
type="file" /> type="file"
/>
</div> </div>
<div class="overflow-hidden relative mt-4 mb-4"> <div class="overflow-hidden relative mt-4 mb-4">
<button <button
@ -286,47 +256,50 @@
cancelModal(); cancelModal();
}} }}
type="button" type="button"
class="w-full 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 md:ml-40 mr-0 sm:ml-0 sm:w-auto sm:text-sm"> class="w-full 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 md:ml-40 mr-0 sm:ml-0 sm:w-auto sm:text-sm"
{$_('cancel')} >
{$_("cancel")}
</button> </button>
</div> </div>
{/if} {/if}
{#if json_output.length > 0} {#if json_output.length > 0}
{#if opened_from === 'OrgOverview'} {#if opened_from === "OrgOverview"}
<p>{$_('import__target-organization')}</p> <p>{$_("import__target-organization")}</p>
<select <select
name="team" name="team"
bind:value={selected_org} bind:value={selected_org}
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"> class="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"
>
{#each passed_orgs as o} {#each passed_orgs as o}
<option value={o.id}>{o.name}</option> <option value={o.id}>{o.name}</option>
{/each} {/each}
</select> </select>
<p>{$_('bitte-bestaetige-diese-laeufer-fuer-den-import')}</p> <p>{$_("confirm-runner-import")}</p>
{/if} {/if}
{#if opened_from === 'RunnerOverview'} {#if opened_from === "RunnerOverview"}
<p>{$_('group')}</p> <p>{$_("group")}</p>
<Select <Select
containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"
itemFilter={(label, filterText, option) => label itemFilter={(label, filterText, option) =>
.toLowerCase() label.toLowerCase().includes(filterText.toLowerCase()) ||
.includes( option.id.value
filterText.toLowerCase()
) || option.id.value
.toString() .toString()
.startsWith(filterText.toLowerCase())} .startsWith(filterText.toLowerCase())}
items={groups} items={groups}
showChevron={true} showChevron={true}
placeholder={$_('search-for-an-organization-or-team-by-name-or-id')} placeholder={$_(
noOptionsMessage={$_('no-organization-or-team-found')} "search-for-an-organization-or-team-by-name-or-id"
)}
noOptionsMessage={$_("no-organization-or-team-found")}
on:select={(selectedValue) => { on:select={(selectedValue) => {
selected_org_or_team = selectedValue.detail.value; selected_org_or_team = selectedValue.detail.value;
}} }}
on:clear={() => (selected_org_or_team = null)} /> on:clear={() => (selected_org_or_team = null)}
/>
{/if} {/if}
{#if opened_from === 'OrgDetail'} {#if opened_from === "OrgDetail"}
<p> <p>
{$_('runnerimport_verify_runners_org', { {$_("runnerimport_verify_runners_org", {
values: { org_name: passed_org.name }, values: { org_name: passed_org.name },
})} })}
</p> </p>
@ -334,9 +307,10 @@
<input <input
type="search" type="search"
bind:value={searchvalue} bind:value={searchvalue}
placeholder={$_('datatable.search')} placeholder={$_("datatable.search")}
aria-label={$_('datatable.search')} aria-label={$_("datatable.search")}
class="p-2 w-full" /> class="p-2 w-full"
/>
<div class="relative w-full mt-4 mb-4"> <div class="relative w-full mt-4 mb-4">
<div class="w-full overflow-x-auto"> <div class="w-full overflow-x-auto">
<table class="divide-y divide-gray-200 w-full"> <table class="divide-y divide-gray-200 w-full">
@ -344,24 +318,28 @@
<tr> <tr>
<th <th
scope="col" scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
{$_('csv_import__firstname')} >
{$_("csv_import__firstname")}
</th> </th>
<th <th
scope="col" scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
{$_('csv_import__middlename')} >
{$_("csv_import__middlename")}
</th> </th>
<th <th
scope="col" scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
{$_('csv_import__lastname')} >
{$_("csv_import__lastname")}
</th> </th>
{#if (opened_from !== 'TeamDetail' && opened_from !== 'RunnerOverview') || (opened_from === 'RunnerOverview' && selected_org_or_team.includes('ORG_'))} {#if (opened_from !== "TeamDetail" && opened_from !== "RunnerOverview") || (opened_from === "RunnerOverview" && selected_org_or_team.includes("ORG_"))}
<th <th
scope="col" scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
{$_('csv_import__team')} >
{$_("csv_import__team")}
</th> </th>
{/if} {/if}
</tr> </tr>
@ -374,17 +352,19 @@
.includes(searchvalue)} .includes(searchvalue)}
<tr> <tr>
<td class="px-6 py-4 whitespace-nowrap"> <td class="px-6 py-4 whitespace-nowrap">
{runner[`${$_('csv_import__firstname')}`]} {runner[`${$_("csv_import__firstname")}`]}
</td> </td>
<td class="px-6 py-4 whitespace-nowrap"> <td class="px-6 py-4 whitespace-nowrap">
{runner[`${$_('csv_import__middlename')}`] || ''} {runner[`${$_("csv_import__middlename")}`] || ""}
</td> </td>
<td class="px-6 py-4 whitespace-nowrap"> <td class="px-6 py-4 whitespace-nowrap">
{runner[`${$_('csv_import__lastname')}`]} {runner[`${$_("csv_import__lastname")}`]}
</td> </td>
{#if (opened_from !== 'TeamDetail' && opened_from !== 'RunnerOverview') || (opened_from === 'RunnerOverview' && selected_org_or_team.includes('ORG_'))} {#if (opened_from !== "TeamDetail" && opened_from !== "RunnerOverview") || (opened_from === "RunnerOverview" && selected_org_or_team.includes("ORG_"))}
<td class="px-6 py-4 whitespace-nowrap"> <td class="px-6 py-4 whitespace-nowrap">
{runner[`${$_('csv_import__team')}`] || runner[`${$_('csv_import__class')}`] || '---'} {runner[`${$_("csv_import__team")}`] ||
runner[`${$_("csv_import__class")}`] ||
"---"}
</td> </td>
{/if} {/if}
</tr> </tr>
@ -398,16 +378,18 @@
class:opacity-50={!importButtonEnabled} class:opacity-50={!importButtonEnabled}
on:click={importAction} on:click={importAction}
type="button" 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"> 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"
{$_('import-runners')} >
{$_("import-runners")}
</button> </button>
<button <button
on:click={() => { on:click={() => {
cancelModal(); cancelModal();
}} }}
type="button" 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"> 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"
{$_('cancel')} >
{$_("cancel")}
</button> </button>
</div> </div>
{/if} {/if}

View File

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

View File

@ -6,7 +6,7 @@
<div class="text-center items-center justify-center"> <div class="text-center items-center justify-center">
<p class="mb-16 text-lg text-gray-500"> <p class="mb-16 text-lg text-gray-500">
<img class="w-full h-44" src={runners_empty} alt="" /> <img class="w-full h-44" src={runners_empty} alt="" />
<span class="font-bold">{$_('there-are-no-runners-added-yet')}</span><br /> <span class="font-bold">{$_("there-are-no-runners-added-yet")}</span><br />
<span>{$_('add-your-first-runner')}</span> <span>{$_("add-your-first-runner")}</span>
</p> </p>
</div> </div>

View File

@ -1,267 +1,261 @@
<script> <script>
import { import {
RunnerOrganizationService, RunnerOrganizationService,
RunnerService, RunnerService,
RunnerTeamService, RunnerTeamService,
} from "@odit/lfk-client-js"; } from "@odit/lfk-client-js";
import { import {
createSvelteTable, createSvelteTable,
flexRender, flexRender,
getCoreRowModel, getCoreRowModel,
getFilteredRowModel, getFilteredRowModel,
getPaginationRowModel, getPaginationRowModel,
getSortedRowModel, getSortedRowModel,
renderComponent, renderComponent,
} from "@tanstack/svelte-table"; } from "@tanstack/svelte-table";
import { onMount } from "svelte"; import { onMount } from "svelte";
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import { writable } from "svelte/store"; import { writable } from "svelte/store";
import store from "../../store"; import store from "../../store";
import GenerateRunnerCards from "../pdf_generation/GenerateRunnerCards.svelte"; import GenerateRunnerCards from "../pdf_generation/GenerateRunnerCards.svelte";
import GenerateRunnerCertificates from "../pdf_generation/GenerateRunnerCertificates.svelte"; import GenerateRunnerCertificates from "../pdf_generation/GenerateRunnerCertificates.svelte";
import GenerateSponsoringContracts from "../pdf_generation/GenerateSponsoringContracts.svelte"; import GenerateSponsoringContracts from "../pdf_generation/GenerateSponsoringContracts.svelte";
import InputElement from "../shared/InputElement.svelte"; import InputElement from "../shared/InputElement.svelte";
import TableActions from "../shared/TableActions.svelte"; import TableActions from "../shared/TableActions.svelte";
import { groupFilter } from "../shared/tablefilters"; import { groupFilter } from "../shared/tablefilters";
import DeleteRunnerModal from "./DeleteRunnerModal.svelte"; import DeleteRunnerModal from "./DeleteRunnerModal.svelte";
import Toastify from "toastify-js"; import TableBottom from "../shared/TableBottom.svelte";
import TableBottom from "../shared/TableBottom.svelte"; import TableHeader from "../shared/TableHeader.svelte";
import TableHeader from "../shared/TableHeader.svelte";
$: selectedRunners =
$: selectedRunners = $table?.getSelectedRowModel().rows.map((row) => row.original) || [];
$table?.getSelectedRowModel().rows.map((row) => row.original) || []; $: selected =
$: selected = $table?.getSelectedRowModel().rows.map((row) => row.index) || [];
$table?.getSelectedRowModel().rows.map((row) => row.index) || [];
$: active_delete = undefined;
$: active_delete = undefined; let dataLoaded = false;
let dataLoaded = false; export let current_runners = [];
export let current_runners = []; $: sponsoring_contracts_show = selected.length > 0;
$: sponsoring_contracts_show = selected.length > 0; $: cards_show = selected.length > 0;
$: cards_show = selected.length > 0; $: certificates_show = selected.length > 0;
$: certificates_show = selected.length > 0; $: teams = [];
$: teams = []; $: orgs = [];
$: orgs = [];
export const addRunners = (runners) => {
export const addRunners = (runners) => { current_runners = current_runners.concat(...runners);
current_runners = current_runners.concat(...runners); options.update((options) => ({
options.update((options) => ({ ...options,
...options, data: current_runners,
data: current_runners, }));
})); };
};
//Section table
//Section table const columns = [
const columns = [ {
{ accessorKey: "id",
accessorKey: "id", header: () => "id",
header: () => "id", filterFn: `equalsString`,
filterFn: `equalsString`, },
}, {
{ accessorKey: "firstname",
accessorKey: "firstname", header: () => $_("first-name"),
header: () => $_("first-name"), filterFn: `includesString`,
filterFn: `includesString`, },
}, {
{ accessorKey: "middlename",
accessorKey: "middlename", header: () => $_("middle-name"),
header: () => $_("middle-name"), cell: (info) => {
cell: (info) => { if (!info || !info.getValue()) {
if (!info || !info.getValue()) { return "";
return ""; }
} return info.getValue();
return info.getValue(); },
}, filterFn: `includesString`,
filterFn: `includesString`, },
}, {
{ accessorKey: "lastname",
accessorKey: "lastname", header: () => $_("last-name"),
header: () => $_("last-name"), filterFn: `includesString`,
filterFn: `includesString`, },
}, {
{ accessorKey: "group",
accessorKey: "group", header: () => $_("group"),
header: () => $_("group"), cell: (info) => {
cell: (info) => { const group = info.getValue();
const group = info.getValue(); if (group.responseType === "RUNNERORGANIZATION") {
if (group.responseType === "RUNNERORGANIZATION") { return group.name;
return group.name; }
} return `${group.parentGroup.name} > ${group.name}`;
return `${group.parentGroup.name} > ${group.name}`; },
}, filterFn: `group`,
filterFn: `group`, },
}, {
{ accessorKey: "distance",
accessorKey: "distance", header: () => $_("distance"),
header: () => $_("distance"), cell: (info) => {
cell: (info) => { if (info.getValue() < 1000) {
if (info.getValue() < 1000) { return `${info.getValue()} m`;
return `${info.getValue()} m`; }
} return `${(info.getValue() / 1000).toFixed(1)} km`;
return `${(info.getValue() / 1000).toFixed(1)} km`; },
}, enableColumnFilter: false,
enableColumnFilter: false, },
}, {
{ accessorKey: "actions",
accessorKey: "actions", header: () => $_("action"),
header: () => $_("action"), cell: (info) => {
cell: (info) => { return renderComponent(TableActions, {
return renderComponent(TableActions, { detailsLink: `./${info.row.original.id}`,
detailsLink: `./${info.row.original.id}`, deleteAction: () => {
deleteAction: () => { active_delete =
active_delete = current_runners[
current_runners[ current_runners.findIndex((r) => r.id == info.row.original.id)
current_runners.findIndex((r) => r.id == info.row.original.id) ];
]; },
}, deleteEnabled:
deleteEnabled: store.state.jwtinfo.userdetails.permissions.includes(
store.state.jwtinfo.userdetails.permissions.includes( "RUNNER:DELETE"
"RUNNER:DELETE" ),
), });
}); },
}, enableColumnFilter: false,
enableColumnFilter: false, enableSorting: false,
enableSorting: false, },
}, ];
]; const options = writable({
const options = writable({ data: [],
data: [], columns: columns,
columns: columns, filterFns: {
filterFns: { group: groupFilter,
group: groupFilter, },
}, initialState: {
initialState: { pagination: {
pagination: { pageSize: 50,
pageSize: 50, },
}, },
}, enableRowSelection: true,
enableRowSelection: true, getCoreRowModel: getCoreRowModel(),
getCoreRowModel: getCoreRowModel(), getFilteredRowModel: getFilteredRowModel(),
getFilteredRowModel: getFilteredRowModel(), getPaginationRowModel: getPaginationRowModel(),
getPaginationRowModel: getPaginationRowModel(), getSortedRowModel: getSortedRowModel(),
getSortedRowModel: getSortedRowModel(), });
}); const table = createSvelteTable(options);
const table = createSvelteTable(options);
async function deleteRunner(delete_runner_id) {
async function deleteRunner(delete_runner_id) { await RunnerService.runnerControllerRemove(delete_runner_id, true);
await RunnerService.runnerControllerRemove(delete_runner_id, true); current_runners = current_runners.filter((r) => r.id !== delete_runner_id);
current_runners = current_runners.filter((r) => r.id !== delete_runner_id); options.update((options) => ({
options.update((options) => ({ ...options,
...options, data: current_runners,
data: current_runners, }));
})); toast($_("runner-deleted"));
Toastify({ }
text: $_("runner-deleted"),
duration: 3500, onMount(async () => {
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", RunnerTeamService.runnerTeamControllerGetAll().then((val) => {
}).showToast(); teams = val;
} });
RunnerOrganizationService.runnerOrganizationControllerGetAll().then(
onMount(async () => { (val) => {
RunnerTeamService.runnerTeamControllerGetAll().then((val) => { orgs = val;
teams = val; }
}); );
RunnerOrganizationService.runnerOrganizationControllerGetAll().then(
(val) => { let page = 0;
orgs = val; while (page >= 0) {
} const runners = await RunnerService.runnerControllerGetAll(page, 1000);
); if (runners.length == 0) {
page = -2;
let page = 0; }
while (page >= 0) {
const runners = await RunnerService.runnerControllerGetAll(page, 1000); current_runners = current_runners.concat(...runners);
if (runners.length == 0) { options.update((options) => ({
page = -2; ...options,
} data: current_runners,
}));
current_runners = current_runners.concat(...runners);
options.update((options) => ({ dataLoaded = true;
...options, page++;
data: current_runners, }
})); });
</script>
dataLoaded = true;
page++; <DeleteRunnerModal
} delete_runner={active_delete}
console.log("All runners loaded"); modal_open={active_delete != undefined}
}); on:delete={(event) => {
</script> deleteRunner(event.detail.id);
}}
<DeleteRunnerModal />
delete_runner={active_delete} {#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:GET")}
modal_open={active_delete != undefined} {#if !dataLoaded}
on:delete={(event) => { <div
deleteRunner(event.detail.id); class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2"
}} role="alert"
/> >
{#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:GET")} <p class="font-bold">{$_("runners-are-being-loaded")}</p>
{#if !dataLoaded} <p class="text-sm">{$_("this-might-take-a-moment")}</p>
<div </div>
class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2" {:else}
role="alert" <div class="h-12 mt-2">
> <GenerateSponsoringContracts
<p class="font-bold">{$_("runners-are-being-loaded")}</p> bind:sponsoring_contracts_show
<p class="text-sm">{$_("this-might-take-a-moment")}</p> bind:generate_runners={selectedRunners}
</div> />
{:else} <GenerateRunnerCards
<div class="h-12 mt-2"> bind:cards_show
<GenerateSponsoringContracts bind:generate_runners={selectedRunners}
bind:sponsoring_contracts_show />
bind:generate_runners={selectedRunners} <GenerateRunnerCertificates
/> bind:certificates_show
<GenerateRunnerCards bind:generate_runners={selectedRunners}
bind:cards_show />
bind:generate_runners={selectedRunners} </div>
/> <div class="overflow-x-auto">
<GenerateRunnerCertificates <table class="w-full">
bind:certificates_show <thead>
bind:generate_runners={selectedRunners} {#each $table.getHeaderGroups() as headerGroup}
/> <tr class="select-none">
</div> <th class="inset-y-0 left-0 px-4 py-2 text-left w-px">
<div class="overflow-x-auto"> <InputElement
<table class="w-full"> type="checkbox"
<thead> checked={$table.getIsAllRowsSelected()}
{#each $table.getHeaderGroups() as headerGroup} indeterminate={$table.getIsSomeRowsSelected()}
<tr class="select-none"> on:change={() => $table.toggleAllRowsSelected()}
<th class="inset-y-0 left-0 px-4 py-2 text-left w-px"> />
<InputElement </th>
type="checkbox" {#each headerGroup.headers as header}
checked={$table.getIsAllRowsSelected()} <TableHeader {header} />
indeterminate={$table.getIsSomeRowsSelected()} {/each}
on:change={() => $table.toggleAllRowsSelected()} </tr>
/> {/each}
</th> </thead>
{#each headerGroup.headers as header} <tbody>
<TableHeader {header} /> {#each $table.getRowModel().rows as row}
{/each} <tr>
</tr> <td class="inset-y-0 left-0 px-4 py-2 text-center w-px">
{/each} <InputElement
</thead> type="checkbox"
<tbody> checked={row.getIsSelected()}
{#each $table.getRowModel().rows as row} on:change={() => row.toggleSelected()}
<tr> />
<td class="inset-y-0 left-0 px-4 py-2 text-center w-px"> </td>
<InputElement {#each row.getVisibleCells() as cell}
type="checkbox" <td>
checked={row.getIsSelected()} <svelte:component
on:change={() => row.toggleSelected()} this={flexRender(
/> cell.column.columnDef.cell,
</td> cell.getContext()
{#each row.getVisibleCells() as cell} )}
<td> />
<svelte:component </td>
this={flexRender( {/each}
cell.column.columnDef.cell, </tr>
cell.getContext() {/each}
)} </tbody>
/> </table>
</td> </div>
{/each} <div class="h-2" />
</tr> {/if}
{/each} {/if}
</tbody> <TableBottom {table} {selected} />
</table>
</div>
<div class="h-2" />
{/if}
{/if}
<TableBottom {table} {selected} />

View File

@ -1,195 +1,206 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import { clickOutside } from "../base/outsideclick"; import { clickOutside } from "../base/outsideclick";
import { import { RunnerService, ScanService } from "@odit/lfk-client-js";
RunnerService, import Select from "svelte-select";
ScanService, import { createEventDispatcher } from "svelte";
} from "@odit/lfk-client-js"; import toast from "svelte-french-toast";
import Select from "svelte-select"; export let modal_open;
import Toastify from "toastify-js"; const getRunnerLabel = (option) =>
import { createEventDispatcher } from "svelte"; option.firstname + " " + (option.middlename || "") + " " + option.lastname;
export let modal_open; const filterRunners = (label, filterText, option) =>
const getRunnerLabel = (option) => label.toLowerCase().includes(filterText.toLowerCase()) ||
option.firstname + " " + (option.middlename || "") + " " + option.lastname; option.value.id.toString().startsWith(filterText.toLowerCase());
const filterRunners = (label, filterText, option) => const dispatch = createEventDispatcher();
label.toLowerCase().includes(filterText.toLowerCase()) || $: runner = 0;
option.value.id.toString().startsWith(filterText.toLowerCase()); $: runners = [];
function focus(el) { RunnerService.runnerControllerGetAll().then((val) => {
el.focus(); runners = val.map((r) => {
} return { label: getRunnerLabel(r), value: r };
const dispatch = createEventDispatcher(); });
$: runner = 0; });
$: runners = []; $: distance_input = 0;
RunnerService.runnerControllerGetAll().then((val) => { $: processed_last_submit = true;
runners = val.map(r => {return {label: getRunnerLabel(r), value: r}}); $: is_distance_valid = distance_input > 0;
}); $: createbtnenabled = is_distance_valid;
$: distance_input = 0; (() => {
$: processed_last_submit = true; document.onkeydown = (e) => {
$: is_distance_valid = distance_input > 0; e = e || window.event;
$: createbtnenabled = is_distance_valid; if (e.key === "Escape") {
(() => { modal_open = false;
document.onkeydown = (e) => { }
e = e || window.event; if (e.keyCode === 13) {
if (e.key === "Escape") { if (createbtnenabled === true) {
modal_open = false; createbtnenabled = false;
} submit();
if (e.keyCode === 13) { }
if (createbtnenabled === true) { }
createbtnenabled = false; };
submit(); })();
} function submit() {
} if (processed_last_submit === true) {
}; processed_last_submit = false;
})(); toast.loading($_("adding-scan"));
function submit() { let postdata = {
if (processed_last_submit === true) { runner,
processed_last_submit = false; distance: distance_input,
const toast = Toastify({ };
text: $_('adding-scan'), ScanService.scanControllerPost(postdata)
duration: -1, .then((result) => {
}).showToast(); runner = 0;
let postdata = { distance_input = 0;
runner, modal_open = false;
distance: distance_input, //
}; toast.dismiss();
ScanService.scanControllerPost(postdata) toast.success($_("scan-added"));
.then((result) => { dispatch("created", { scans: [result] });
runner = 0; })
distance_input = 0; .catch((err) => {
modal_open = false; //
// })
Toastify({ .finally(() => {
text: $_('scan-added'), processed_last_submit = true;
duration: 500, });
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", }
}).showToast(); }
dispatch("created", { scans: [result] }); </script>
})
.catch((err) => { {#if modal_open}
// <div
}) class="fixed z-10 inset-0 overflow-y-auto"
.finally(() => { use:clickOutside
processed_last_submit = true; on:click_outside={() => {
// modal_open = false;
toast.hideToast(); }}
}); >
} <div
} class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"
</script> >
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
{#if modal_open} <div
<div class="absolute inset-0 bg-gray-500 opacity-75"
class="fixed z-10 inset-0 overflow-y-auto" data-id="modal_backdrop"
/>
use:clickOutside </div>
on:click_outside={() => { <span
modal_open = false; class="hidden sm:inline-block sm:align-middle sm:h-screen"
}}> aria-hidden="true">&#8203;</span
<div >
class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> <div
<div class="fixed inset-0 transition-opacity" aria-hidden="true"> 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"
<div role="dialog"
class="absolute inset-0 bg-gray-500 opacity-75" aria-modal="true"
data-id="modal_backdrop" /> aria-labelledby="modal-headline"
</div> >
<span <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
class="hidden sm:inline-block sm:align-middle sm:h-screen" <div class="sm:flex sm:items-start">
aria-hidden="true">&#8203;</span> <div
<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"
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" <svg
aria-modal="true" class="h-6 w-6 text-blue-600"
aria-labelledby="modal-headline"> fill="currentColor"
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> xmlns="http://www.w3.org/2000/svg"
<div class="sm:flex sm:items-start"> viewBox="0 0 24 24"
<div width="24"
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"> height="24"
<svg ><path fill="none" d="M0 0h24v24H0z" />
class="h-6 w-6 text-blue-600" <path
fill="currentColor" fill="currentColor"
xmlns="http://www.w3.org/2000/svg" d="M2 4h2v16H2V4zm4 0h1v16H6V4zm2 0h2v16H8V4zm3 0h2v16h-2V4zm3 0h2v16h-2V4zm3 0h1v16h-1V4zm2 0h3v16h-3V4z"
viewBox="0 0 24 24" /></svg
width="24" >
height="24"><path fill="none" d="M0 0h24v24H0z" /> </div>
<path <div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
fill="currentColor" <h3 class="text-lg leading-6 font-medium text-gray-900">
d="M2 4h2v16H2V4zm4 0h1v16H6V4zm2 0h2v16H8V4zm3 0h2v16h-2V4zm3 0h2v16h-2V4zm3 0h1v16h-1V4zm2 0h3v16h-3V4z" /></svg> {$_("create-a-new-scan-fixed-only")}
</div> </h3>
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"> <div class="mt-2 mb-6">
<h3 class="text-lg leading-6 font-medium text-gray-900"> <p class="text-sm text-gray-500">
{$_('create-a-new-scan-fixed-only')} {$_(
</h3> "please-provide-the-nessecary-information-to-create-a-new-scan"
<div class="mt-2 mb-6"> )}
<p class="text-sm text-gray-500"> </p>
{$_('please-provide-the-nessecary-information-to-create-a-new-scan')} </div>
</p> <div class="grid grid-cols-6 gap-6">
</div> <div class="col-span-6">
<div class="grid grid-cols-6 gap-6"> <label
<div class="col-span-6"> for="donor"
<label class="block text-sm font-medium text-gray-700"
for="donor" >{$_("runner")}</label
class="block text-sm font-medium text-gray-700">{$_('runner')}</label> >
<Select <Select
containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"
itemFilter={(label, filterText, option) => filterRunners(label, filterText, option)} itemFilter={(label, filterText, option) =>
items={runners} filterRunners(label, filterText, option)}
showChevron={true} items={runners}
placeholder={$_('search-for-runner-by-name-or-id')} showChevron={true}
noOptionsMessage={$_('no-runners-found')} placeholder={$_("search-for-runner-by-name-or-id")}
on:select={(selectedValue) => (runner = selectedValue.detail.value.id)} noOptionsMessage={$_("no-runners-found")}
on:clear={() => (runner = null)} /> on:select={(selectedValue) =>
</div> (runner = selectedValue.detail.value.id)}
<div class="col-span-6"> on:clear={() => (runner = null)}
<label />
for="donation_amount_eur" </div>
class="block text-sm font-medium text-gray-700"> <div class="col-span-6">
{$_('distance')}</label> <label
<div class="mt-1 flex rounded-md shadow-sm"> for="donation_amount_eur"
<input class="block text-sm font-medium text-gray-700"
autocomplete="off" >
class:border-red-500={!is_distance_valid} {$_("distance")}</label
class:focus:border-red-500={!is_distance_valid} >
class:focus:ring-red-500={!is_distance_valid} <div class="mt-1 flex rounded-md shadow-sm">
bind:value={distance_input} <input
type="number" autocomplete="off"
step="1" class:border-red-500={!is_distance_valid}
name="donation_amount_eur" class:focus:border-red-500={!is_distance_valid}
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-gray-500 p-2" class:focus:ring-red-500={!is_distance_valid}
placeholder="400" /> bind:value={distance_input}
<span type="number"
class="inline-flex items-center px-3 rounded-r-md border border-gray-300 bg-gray-50 text-gray-500 text-sm">m</span> step="1"
</div> name="donation_amount_eur"
{#if !is_distance_valid} 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-gray-500 p-2"
<span placeholder="400"
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> />
{$_('the-scans-distance-must-be-greater-than-0m')} <span
</span> class="inline-flex items-center px-3 rounded-r-md border border-gray-300 bg-gray-50 text-gray-500 text-sm"
{/if} >m</span
</div> >
</div> </div>
</div> {#if !is_distance_valid}
</div> <span
</div> class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse"> >
<button {$_("the-scans-distance-must-be-greater-than-0m")}
disabled={!createbtnenabled} </span>
class:opacity-50={!createbtnenabled} {/if}
on:click={submit} </div>
type="button" </div>
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"> </div>
{$_('create')} </div>
</button> </div>
<button <div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
on:click={() => { <button
modal_open = false; disabled={!createbtnenabled}
}} class:opacity-50={!createbtnenabled}
type="button" on:click={submit}
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"> type="button"
{$_('cancel')} 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"
</button> >
</div> {$_("create")}
</div> </button>
</div> <button
</div> on:click={() => {
{/if} 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

@ -1,11 +1,8 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import store from "../../store"; import store from "../../store";
import { import { RunnerService, ScanService } from "@odit/lfk-client-js";
RunnerService,
ScanService,
} from "@odit/lfk-client-js";
import Toastify from "toastify-js";
import PromiseError from "../base/PromiseError.svelte"; import PromiseError from "../base/PromiseError.svelte";
import Select from "svelte-select"; import Select from "svelte-select";
let data_loaded = false; let data_loaded = false;
@ -31,14 +28,12 @@
original_data = Object.assign(original_data, data); original_data = Object.assign(original_data, data);
original_data.runner = original_data.runner.id; original_data.runner = original_data.runner.id;
editable = Object.assign(editable, original_data); editable = Object.assign(editable, original_data);
RunnerService.runnerControllerGetAll().then( RunnerService.runnerControllerGetAll().then((val) => {
(val) => { current_runners = val.map((r) => {
current_runners = val.map((r) => { return { label: getRunnerLabel(r), value: r };
return { label: getRunnerLabel(r), value: r }; });
}); runner = current_runners.find((r) => r.value.id == editable.runner);
runner = current_runners.find(r => r.value.id == editable.runner); });
}
);
} }
); );
const getRunnerLabel = (option) => const getRunnerLabel = (option) =>
@ -49,10 +44,7 @@
function submit() { function submit() {
if (data_loaded === true && save_enabled) { if (data_loaded === true && save_enabled) {
Toastify({ toast($_("scan-is-being-updated"));
text: $_('scan-is-being-updated'),
duration: 2500,
}).showToast();
let postdata = {}; let postdata = {};
if (original_data.responseType === "TRACKSCAN") { if (original_data.responseType === "TRACKSCAN") {
postdata = Object.assign(postdata, editable); postdata = Object.assign(postdata, editable);
@ -61,11 +53,7 @@
.then((resp) => { .then((resp) => {
Object.assign(original_data, editable); Object.assign(original_data, editable);
original_data = original_data; original_data = original_data;
Toastify({ toast.success($_("updated-scan"));
text: $_('updated-scan'),
duration: 2500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
}) })
.catch((err) => {}); .catch((err) => {});
} else { } else {
@ -74,11 +62,7 @@
.then((resp) => { .then((resp) => {
Object.assign(original_data, editable); Object.assign(original_data, editable);
original_data = original_data; original_data = original_data;
Toastify({ toast.success($_("updated-scan"));
text: $_('updated-scan'),
duration: 2500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
}) })
.catch((err) => {}); .catch((err) => {});
} }
@ -88,11 +72,7 @@
function deleteScan() { function deleteScan() {
ScanService.scanControllerRemove(original_data.id, false) ScanService.scanControllerRemove(original_data.id, false)
.then((resp) => { .then((resp) => {
Toastify({ toast($_("deleted-scan"));
text: $_('deleted-scan'),
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
location.replace("./"); location.replace("./");
}) })
.catch((err) => { .catch((err) => {
@ -100,11 +80,25 @@
delete_scan = original_data; delete_scan = original_data;
}); });
} }
function format_laptime(laptime){ function format_laptime(laptime) {
if(laptime == 0 || laptime == null){return $_('first-scan-of-the-day')} if (laptime == 0 || laptime == null) {
if(laptime < 60){return `${laptime}s`} return $_("first-scan-of-the-day");
if(laptime < 3600){return `${Math.floor(laptime / 60)}min ${laptime - (Math.floor(laptime / 60)*60)}s`} }
return `${Math.floor(laptime / 3600)}h ${laptime - (Math.floor(laptime / 3600)*3600)}min ${laptime - (Math.floor(laptime / 3600)*3600) - (Math.floor(laptime / 60)*60)}` if (laptime < 60) {
return `${laptime}s`;
}
if (laptime < 3600) {
return `${Math.floor(laptime / 60)}min ${
laptime - Math.floor(laptime / 60) * 60
}s`;
}
return `${Math.floor(laptime / 3600)}h ${
laptime - Math.floor(laptime / 3600) * 3600
}min ${
laptime -
Math.floor(laptime / 3600) * 3600 -
Math.floor(laptime / 60) * 60
}`;
} }
</script> </script>
@ -122,9 +116,12 @@
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"><path height="24"
><path
fill="currentColor" fill="currentColor"
d="M2 4h2v16H2V4zm4 0h1v16H6V4zm2 0h2v16H8V4zm3 0h2v16h-2V4zm3 0h2v16h-2V4zm3 0h1v16h-1V4zm2 0h3v16h-3V4z" /></svg> d="M2 4h2v16H2V4zm4 0h1v16H6V4zm2 0h2v16H8V4zm3 0h2v16h-2V4zm3 0h2v16h-2V4zm3 0h1v16h-1V4zm2 0h3v16h-3V4z"
/></svg
>
</li> </li>
<li class="flex items-center ml-2"> <li class="flex items-center ml-2">
<a class="mr-2" href="./">Scans</a><svg <a class="mr-2" href="./">Scans</a><svg
@ -137,12 +134,10 @@
class="h-3 w-3 mr-2 stroke-current" class="h-3 w-3 mr-2 stroke-current"
height="1em" height="1em"
width="1em" width="1em"
xmlns="http://www.w3.org/2000/svg"><line xmlns="http://www.w3.org/2000/svg"
x1="5" ><line x1="5" y1="12" x2="19" y2="12" />
y1="12" <polyline points="12 5 19 12 12 19" /></svg
x2="19" >
y2="12" />
<polyline points="12 5 19 12 12 19" /></svg>
</li> </li>
<li class="flex items-center"> <li class="flex items-center">
<span class="mr-2">{original_data.id}</span> <span class="mr-2">{original_data.id}</span>
@ -153,20 +148,24 @@
</div> </div>
<div class="mb-8 text-3xl font-extrabold leading-tight"> <div class="mb-8 text-3xl font-extrabold leading-tight">
{runner.value?.firstname} {runner.value?.firstname}
{runner.value?.middlename || ''} {runner.value?.middlename || ""}
{runner.value?.lastname} {runner.value?.lastname}
#{original_data.id} #{original_data.id}
<span data-id="donation_actions_${original_data.id}"> <span data-id="donation_actions_${original_data.id}">
{#if store.state.jwtinfo.userdetails.permissions.includes('SCAN:DELETE')} {#if store.state.jwtinfo.userdetails.permissions.includes("SCAN:DELETE")}
{#if delete_triggered} {#if delete_triggered}
<button <button
on:click={deleteScan} on:click={deleteScan}
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:">{$_('confirm-deletion')}</button> class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:"
>{$_("confirm-deletion")}</button
>
<button <button
on:click={() => { on:click={() => {
delete_triggered = !delete_triggered; delete_triggered = !delete_triggered;
}} }}
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-400 text-base font-medium text-white sm:w-auto sm:">{$_('cancel')}</button> class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-400 text-base font-medium text-white sm:w-auto sm:"
>{$_("cancel")}</button
>
{/if} {/if}
{#if !delete_triggered} {#if !delete_triggered}
<button <button
@ -174,7 +173,9 @@
delete_triggered = true; delete_triggered = true;
}} }}
type="button" type="button"
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:">{$_('delete-scan')}</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:"
>{$_("delete-scan")}</button
>
{/if} {/if}
{/if} {/if}
{#if !delete_triggered} {#if !delete_triggered}
@ -183,13 +184,16 @@
class:opacity-50={!save_enabled} class:opacity-50={!save_enabled}
type="button" type="button"
on:click={submit} on:click={submit}
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:">{$_('save-changes')}</button> class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:"
>{$_("save-changes")}</button
>
{/if} {/if}
</span> </span>
</div> </div>
<!-- --> <!-- -->
<div class="w-full inline-flex"> <div class="w-full inline-flex">
<label for="valid" class="block font-medium text-gray-700">{$_('status')}: <label for="valid" class="block font-medium text-gray-700"
>{$_("status")}:
</label> </label>
&nbsp; &nbsp;
<input <input
@ -200,49 +204,57 @@
name="valid" name="valid"
type="checkbox" type="checkbox"
checked={editable.valid} checked={editable.valid}
class="focus:ring-indigo-500 align-bottom h-7 w-5font-medium text-indigo-600 border-gray-300 rounded" /> class="focus:ring-indigo-500 align-bottom h-7 w-5font-medium text-indigo-600 border-gray-300 rounded"
/>
&nbsp; &nbsp;
<p class="font-medium"> <p class="font-medium">
{#if editable.valid}{$_('valid')}{:else}{$_('invalid')}{/if} {#if editable.valid}{$_("valid")}{:else}{$_("invalid")}{/if}
</p> </p>
</div> </div>
{#if editable.responseType === 'TRACKSCAN'} {#if editable.responseType === "TRACKSCAN"}
<div class="w-full inline-flex"> <div class="w-full inline-flex">
<label for="valid" class="block font-semibold text-gray-700">{$_('track')}: <label for="valid" class="block font-semibold text-gray-700"
>{$_("track")}:
</label> </label>
<a <a
href="../tracks" href="../tracks"
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800">{editable.track.name} class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800"
>{editable.track.name}
</a> </a>
</div> </div>
<div class="w-full inline-flex pb-3"> <div class="w-full inline-flex pb-3">
<label for="valid" class="block font-semibold text-gray-700">{$_('laptime')}: {format_laptime(editable.laptime)} <label for="valid" class="block font-semibold text-gray-700"
>{$_("laptime")}: {format_laptime(editable.laptime)}
</label> </label>
</div> </div>
{/if} {/if}
<div class=" w-full"> <div class=" w-full">
<label <label for="runner" class="block font-medium text-gray-700"
for="runner" >{$_("runner")}</label
class="block font-medium text-gray-700">{$_('runner')}</label> >
<Select <Select
containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"
itemFilter={(label, filterText, option) => filterRunners(label, filterText, option)} itemFilter={(label, filterText, option) =>
filterRunners(label, filterText, option)}
items={current_runners} items={current_runners}
showChevron={true} showChevron={true}
isDisabled={editable.responseType === 'TRACKSCAN'} isDisabled={editable.responseType === "TRACKSCAN"}
placeholder={$_('search-for-runner-by-name-or-id')} placeholder={$_("search-for-runner-by-name-or-id")}
noOptionsMessage={$_('no-runners-found')} noOptionsMessage={$_("no-runners-found")}
bind:selectedValue={runner} bind:selectedValue={runner}
on:select={(selectedValue) => { on:select={(selectedValue) => {
editable.runner = selectedValue.detail.value.id; editable.runner = selectedValue.detail.value.id;
}} }}
on:clear={() => (editable.runner = null)} /> on:clear={() => (editable.runner = null)}
/>
</div> </div>
<div class=" w-full"> <div class=" w-full">
<label <label
for="scan_distance" for="scan_distance"
class="block text-sm font-medium text-gray-700"> class="block text-sm font-medium text-gray-700"
{$_('distance')}</label> >
{$_("distance")}</label
>
<div class="mt-1 flex rounded-md shadow-sm"> <div class="mt-1 flex rounded-md shadow-sm">
<input <input
autocomplete="off" autocomplete="off"
@ -250,19 +262,23 @@
class:focus:border-red-500={!is_distance_valid} class:focus:border-red-500={!is_distance_valid}
class:focus:ring-red-500={!is_distance_valid} class:focus:ring-red-500={!is_distance_valid}
bind:value={editable.distance} bind:value={editable.distance}
disabled={editable.responseType === 'TRACKSCAN'} disabled={editable.responseType === "TRACKSCAN"}
type="number" type="number"
step="1" step="1"
name="scan_distance" name="scan_distance"
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-gray-500 p-2" 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-gray-500 p-2"
placeholder="400" /> placeholder="400"
/>
<span <span
class="inline-flex items-center px-3 rounded-r-md border border-gray-300 bg-gray-50 text-gray-500 text-sm">m</span> class="inline-flex items-center px-3 rounded-r-md border border-gray-300 bg-gray-50 text-gray-500 text-sm"
>m</span
>
</div> </div>
{#if !is_distance_valid} {#if !is_distance_valid}
<span <span
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
{$_('the-scans-distance-must-be-greater-than-0m')} >
{$_("the-scans-distance-must-be-greater-than-0m")}
</span> </span>
{/if} {/if}
</div> </div>

View File

@ -10,23 +10,27 @@
<section class="container p-5"> <section class="container p-5">
<span class="mb-1 text-3xl font-extrabold leading-tight"> <span class="mb-1 text-3xl font-extrabold leading-tight">
{$_('scans')} {$_("scans")}
{#if store.state.jwtinfo.userdetails.permissions.includes('SCAN:CREATE')} {#if store.state.jwtinfo.userdetails.permissions.includes("SCAN:CREATE")}
<button <button
on:click={() => { on:click={() => {
modal_open = true; modal_open = true;
}} }}
type="button" 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"> 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"
{$_('add-scan')} >
{$_("add-scan")}
</button> </button>
{/if} {/if}
</span> </span>
<ScansOverview bind:current_scans bind:addScans /> <ScansOverview bind:current_scans bind:addScans />
</section> </section>
{#if store.state.jwtinfo.userdetails.permissions.includes('SCAN:CREATE')} {#if store.state.jwtinfo.userdetails.permissions.includes("SCAN:CREATE")}
<AddScanModal bind:modal_open on:created={(event)=>{ <AddScanModal
addScans(event.detail.scans) bind:modal_open
}} /> on:created={(event) => {
addScans(event.detail.scans);
}}
/>
{/if} {/if}

View File

@ -6,7 +6,7 @@
<div class="text-center items-center justify-center"> <div class="text-center items-center justify-center">
<p class="mb-16 text-lg text-gray-500"> <p class="mb-16 text-lg text-gray-500">
<img class="m-auto" style="height:15rem" src={scans_empty} alt="" /> <img class="m-auto" style="height:15rem" src={scans_empty} alt="" />
<span class="font-bold">{$_('there-are-no-scans-yet')}</span><br /> <span class="font-bold">{$_("there-are-no-scans-yet")}</span><br />
<span>{$_('add-your-fist-scan')}</span> <span>{$_("add-your-fist-scan")}</span>
</p> </p>
</div> </div>

View File

@ -13,7 +13,7 @@
} from "@tanstack/svelte-table"; } from "@tanstack/svelte-table";
import { onMount } from "svelte"; import { onMount } from "svelte";
import { writable } from "svelte/store"; import { writable } from "svelte/store";
import Toastify from "toastify-js";
import TableBottom from "../shared/TableBottom.svelte"; import TableBottom from "../shared/TableBottom.svelte";
import TableHeader from "../shared/TableHeader.svelte"; import TableHeader from "../shared/TableHeader.svelte";
import ScansEmptyState from "./ScansEmptyState.svelte"; import ScansEmptyState from "./ScansEmptyState.svelte";
@ -23,6 +23,7 @@
import CardRunner from "../cards/CardRunner.svelte"; import CardRunner from "../cards/CardRunner.svelte";
import ScanValid from "./ScanValid.svelte"; import ScanValid from "./ScanValid.svelte";
import DeleteScanModal from "./DeleteScanModal.svelte"; import DeleteScanModal from "./DeleteScanModal.svelte";
import toast from "svelte-french-toast";
$: selectedScans = $: selectedScans =
$table?.getSelectedRowModel().rows.map((row) => row.original) || []; $table?.getSelectedRowModel().rows.map((row) => row.original) || [];
@ -169,11 +170,7 @@
...options, ...options,
data: current_scans, data: current_scans,
})); }));
Toastify({ toast($_("scan-deleted"));
text: $_("scan-deleted"),
duration: 3500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
} }
onMount(async () => { onMount(async () => {
@ -193,7 +190,6 @@
dataLoaded = true; dataLoaded = true;
page++; page++;
} }
console.log("All scans loaded");
}); });
</script> </script>
@ -235,11 +231,7 @@
data: current_scans, data: current_scans,
})); }));
$table.resetRowSelection(); $table.resetRowSelection();
Toastify({ toast.success($_("scan-deleted"));
text: $_("scan-deleted"),
duration: 3500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
}} }}
> >
{$_("delete-scans")} {$_("delete-scans")}

View File

@ -1,10 +1,10 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import { clickOutside } from "../base/outsideclick"; import { clickOutside } from "../base/outsideclick";
import { ScanStationService, TrackService } from "@odit/lfk-client-js"; import { ScanStationService, TrackService } from "@odit/lfk-client-js";
import Toastify from "toastify-js";
import Select from "svelte-select"; import Select from "svelte-select";
import toast from "svelte-french-toast";
export let modal_open; export let modal_open;
export let new_station; export let new_station;
export let current_stations; export let current_stations;
@ -40,10 +40,7 @@
function submit() { function submit() {
if (processed_last_submit === true) { if (processed_last_submit === true) {
processed_last_submit = false; processed_last_submit = false;
const toast = Toastify({ toast.loading($_("scanstation-is-being-added"));
text: $_("scanstation-is-being-added"),
duration: -1,
}).showToast();
let postdata = { let postdata = {
description, description,
enabled, enabled,
@ -56,11 +53,8 @@
enabled = true; enabled = true;
modal_open = false; modal_open = false;
// //
Toastify({ toast.dismiss();
text: $_("scanstation-added"), toast.success($_("scanstation-added"));
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
current_stations.push(result); current_stations.push(result);
current_stations = current_stations; current_stations = current_stations;
new_station = result; new_station = result;
@ -71,8 +65,6 @@
}) })
.finally(() => { .finally(() => {
processed_last_submit = true; processed_last_submit = true;
//
toast.hideToast();
}); });
} }
} }
@ -81,87 +73,101 @@
{#if modal_open} {#if modal_open}
<div <div
class="fixed z-10 inset-0 overflow-y-auto" class="fixed z-10 inset-0 overflow-y-auto"
use:clickOutside use:clickOutside
on:click_outside={() => { on:click_outside={() => {
modal_open = false; modal_open = false;
}}> }}
>
<div <div
class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> 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="fixed inset-0 transition-opacity" aria-hidden="true">
<div <div
class="absolute inset-0 bg-gray-500 opacity-75" class="absolute inset-0 bg-gray-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 bg-white rounded-lg text-left shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full" 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" 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"> <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="sm:flex sm:items-start">
<div <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"> 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 <svg
class="h-6 w-6 text-blue-600" class="h-6 w-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"><path fill="none" d="M0 0h24v24H0z" /> height="24"
><path fill="none" d="M0 0h24v24H0z" />
<path <path
d="M4 5v11h16V5H4zM2 4a1 1 0 011-1h18a1 1 0 011 1v14H2V4zM1 19h22v2H1v-2z" /></svg> d="M4 5v11h16V5H4zM2 4a1 1 0 011-1h18a1 1 0 011 1v14H2V4zM1 19h22v2H1v-2z"
/></svg
>
</div> </div>
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"> <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"> <h3 class="text-lg leading-6 font-medium text-gray-900">
{$_('create-a-new-scanstation')} {$_("create-a-new-scanstation")}
</h3> </h3>
<div class="mt-2 mb-6"> <div class="mt-2 mb-6">
<p class="text-sm text-gray-500"> <p class="text-sm text-gray-500">
{$_('please-provide-the-required-information-to-create-a-new-scanstation')} {$_(
"please-provide-the-required-information-to-create-a-new-scanstation"
)}
</p> </p>
</div> </div>
<div class="grid grid-cols-6 gap-6"> <div class="grid grid-cols-6 gap-6">
<div class="col-span-6"> <div class="col-span-6">
<label <label
for="track" for="track"
class="block text-sm font-medium text-gray-700">Track</label> class="block text-sm font-medium text-gray-700">Track</label
>
<Select <Select
containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"
itemFilter={(label, filterText, option) => label itemFilter={(label, filterText, option) =>
.toLowerCase() label.toLowerCase().includes(filterText.toLowerCase()) ||
.includes( option.value.id
filterText.toLowerCase()
) || option.value.id
.toString() .toString()
.startsWith(filterText.toLowerCase())} .startsWith(filterText.toLowerCase())}
items={tracks} items={tracks}
showChevron={true} showChevron={true}
placeholder="Search for a track (by name or id)." placeholder="Search for a track (by name or id)."
noOptionsMessage="No track found" noOptionsMessage="No track found"
on:select={(selectedValue) => (track = selectedValue.detail.value.id)} on:select={(selectedValue) =>
on:clear={() => (track = null)} /> (track = selectedValue.detail.value.id)}
on:clear={() => (track = null)}
/>
</div> </div>
<div class="col-span-6"> <div class="col-span-6">
<label <label
for="description" for="description"
class="block text-sm font-medium text-gray-700">{$_('description')}</label> class="block text-sm font-medium text-gray-700"
>{$_("description")}</label
>
<input <input
use:focus use:focus
autocomplete="off" autocomplete="off"
placeholder={$_('description')} placeholder={$_("description")}
bind:value={description} bind:value={description}
type="text" type="text"
name="description" 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" /> 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 class="col-span-6"> <div class="col-span-6">
<label <label for="enabled" class="font-medium text-gray-700"
for="enabled" >{$_("enabled_large")}</label
class="font-medium text-gray-700">{$_('enabled_large')}</label> >
<br /> <br />
<p class="text-gray-500"> <p class="text-gray-500">
<input <input
@ -172,9 +178,10 @@
name="enabled" name="enabled"
type="checkbox" type="checkbox"
checked={enabled} checked={enabled}
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" /> class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
{$_('this-scanstation-is')} />
{#if enabled}{$_('enabled')}{:else}{$_('disabled')}{/if} {$_("this-scanstation-is")}
{#if enabled}{$_("enabled")}{:else}{$_("disabled")}{/if}
</p> </p>
</div> </div>
</div> </div>
@ -187,16 +194,18 @@
class:opacity-50={!createbtnenabled} class:opacity-50={!createbtnenabled}
on:click={submit} on:click={submit}
type="button" 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"> 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')} >
{$_("create")}
</button> </button>
<button <button
on:click={() => { on:click={() => {
modal_open = false; modal_open = false;
}} }}
type="button" 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"> 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')} >
{$_("cancel")}
</button> </button>
</div> </div>
</div> </div>

View File

@ -1,10 +1,10 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import { clickOutside } from "../base/outsideclick"; import { clickOutside } from "../base/outsideclick";
import { ScanStationService } from "@odit/lfk-client-js"; import { ScanStationService } from "@odit/lfk-client-js";
import Toastify from "toastify-js";
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
import toast from "svelte-french-toast";
export let modal_open; export let modal_open;
export let delete_station; export let delete_station;
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
@ -13,16 +13,9 @@
dispatch("cancelDelete", { id: delete_station.id }); dispatch("cancelDelete", { id: delete_station.id });
} }
function deleteStation() { function deleteStation() {
ScanStationService.scanStationControllerRemove( ScanStationService.scanStationControllerRemove(delete_station.id, true)
delete_station.id,
true
)
.then((resp) => { .then((resp) => {
Toastify({ toast($_("station-deleted"));
text: $_('station-deleted'),
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
location.replace("./"); location.replace("./");
}) })
.catch((err) => {}); .catch((err) => {});
@ -32,41 +25,54 @@
{#if modal_open} {#if modal_open}
<div <div
class="fixed z-10 inset-0 overflow-y-auto" class="fixed z-10 inset-0 overflow-y-auto"
use:clickOutside use:clickOutside
on:click_outside={cancelDelete}> on:click_outside={cancelDelete}
>
<div <div
class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> 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="fixed inset-0 transition-opacity" aria-hidden="true">
<div <div
class="absolute inset-0 bg-gray-500 opacity-75" class="absolute inset-0 bg-gray-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 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" 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" 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"> <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="sm:flex sm:items-start">
<div <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"> 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> >
<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>
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"> <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"> <h3 class="text-lg leading-6 font-medium text-gray-900">
{$_('attention')} {$_("attention")}
</h3> </h3>
<div class="mt-2 mb-6"> <div class="mt-2 mb-6">
<p class="text-sm text-gray-500"> <p class="text-sm text-gray-500">
{$_( {$_(
'do-you-want-to-delete-this-donor-with-all-related-donations' "do-you-want-to-delete-this-donor-with-all-related-donations"
)} )}
<br /> <br />
{$_('all-associated-scans-will-get-deleted-as-well')} {$_("all-associated-scans-will-get-deleted-as-well")}
</p> </p>
</div> </div>
</div> </div>
@ -76,14 +82,16 @@
<button <button
on:click={deleteStation} on:click={deleteStation}
type="button" 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"> 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-station-with-all-scans')} >
{$_("confirm-delete-station-with-all-scans")}
</button> </button>
<button <button
on:click={cancelDelete} on:click={cancelDelete}
type="button" 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"> 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-station')} >
{$_("cancel-keep-station")}
</button> </button>
</div> </div>
</div> </div>

View File

@ -1,6 +1,6 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import Toastify from "toastify-js";
import { tick, createEventDispatcher } from "svelte"; import { tick, createEventDispatcher } from "svelte";
import bwipjs from "bwip-js"; import bwipjs from "bwip-js";
@ -26,21 +26,12 @@
if (!successful) { if (!successful) {
throw new Error(); throw new Error();
} }
Toastify({ toast($_("copied-token-to-clipboard"));
text: $_("copied-token-to-clipboard"),
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
copied = true; copied = true;
} catch (err) { } catch (err) {
Toastify({ toast.Error($_("error-whyile-copying-to-clipboard"));
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 // we can notify by event or storage about copy status
valueCopy = null; valueCopy = null;
} }
@ -182,7 +173,7 @@
class:w-full={!is_qrcode} class:w-full={!is_qrcode}
class="md:w-auto mb-2 mx-auto" class="md:w-auto mb-2 mx-auto"
alt="Registrierungscode" alt="Registrierungscode"
src={textToBase64Barcode(config.baseurl, is_qrcode)} src={textToBase64Barcode(window.config.baseurl, is_qrcode)}
/> />
<h3 class="leading-6 font-medium text-gray-900">{$_("token")}</h3> <h3 class="leading-6 font-medium text-gray-900">{$_("token")}</h3>
<img <img

View File

@ -2,7 +2,7 @@
import { t, _ } from "svelte-i18n"; import { t, _ } from "svelte-i18n";
import store from "../../store"; import store from "../../store";
import { ScanStationService, TrackService } from "@odit/lfk-client-js"; import { ScanStationService, TrackService } from "@odit/lfk-client-js";
import Toastify from "toastify-js";
import PromiseError from "../base/PromiseError.svelte"; import PromiseError from "../base/PromiseError.svelte";
import ConfirmScanStationDeletion from "./ConfirmScanStationDeletion.svelte"; import ConfirmScanStationDeletion from "./ConfirmScanStationDeletion.svelte";
import Select from "svelte-select"; import Select from "svelte-select";
@ -35,19 +35,12 @@
}); });
function submit() { function submit() {
if (data_loaded === true && save_enabled) { if (data_loaded === true && save_enabled) {
Toastify({ toast($_("station-is-being-updated"));
text: $_("station-is-being-updated"),
duration: 2500,
}).showToast();
ScanStationService.scanStationControllerPut(original_data.id, editable) ScanStationService.scanStationControllerPut(original_data.id, editable)
.then((resp) => { .then((resp) => {
Object.assign(original_data, editable); Object.assign(original_data, editable);
original_data = original_data; original_data = original_data;
Toastify({ toast.success($_("updated-station"));
text: $_("updated-station"),
duration: 2500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
}) })
.catch((err) => {}); .catch((err) => {});
} else { } else {
@ -56,11 +49,7 @@
function deleteStation() { function deleteStation() {
ScanStationService.scanStationControllerRemove(original_data.id, false) ScanStationService.scanStationControllerRemove(original_data.id, false)
.then((resp) => { .then((resp) => {
Toastify({ toast($_("station-deleted"));
text: $_("station-deleted"),
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
location.replace("./"); location.replace("./");
}) })
.catch((err) => { .catch((err) => {
@ -72,7 +61,7 @@
<ConfirmScanStationDeletion bind:modal_open bind:delete_station /> <ConfirmScanStationDeletion bind:modal_open bind:delete_station />
{#await promise} {#await promise}
{$_('loading-station-details')} {$_("loading-station-details")}
{:then} {:then}
<section class="container p-5 select-none"> <section class="container p-5 select-none">
<div class="flex flex-row mb-4"> <div class="flex flex-row mb-4">
@ -85,12 +74,15 @@
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"><path fill="none" d="M0 0h24v24H0z" /> height="24"
><path fill="none" d="M0 0h24v24H0z" />
<path <path
d="M4 5v11h16V5H4zM2 4a1 1 0 011-1h18a1 1 0 011 1v14H2V4zM1 19h22v2H1v-2z" /></svg> d="M4 5v11h16V5H4zM2 4a1 1 0 011-1h18a1 1 0 011 1v14H2V4zM1 19h22v2H1v-2z"
/></svg
>
</li> </li>
<li class="flex items-center ml-2"> <li class="flex items-center ml-2">
<a class="mr-2" href="./">{$_('scanstation')}</a><svg <a class="mr-2" href="./">{$_("scanstation")}</a><svg
stroke="currentColor" stroke="currentColor"
fill="none" fill="none"
stroke-width="2" stroke-width="2"
@ -100,12 +92,10 @@
class="h-3 w-3 mr-2 stroke-current" class="h-3 w-3 mr-2 stroke-current"
height="1em" height="1em"
width="1em" width="1em"
xmlns="http://www.w3.org/2000/svg"><line xmlns="http://www.w3.org/2000/svg"
x1="5" ><line x1="5" y1="12" x2="19" y2="12" />
y1="12" <polyline points="12 5 19 12 12 19" /></svg
x2="19" >
y2="12" />
<polyline points="12 5 19 12 12 19" /></svg>
</li> </li>
<li class="flex items-center"> <li class="flex items-center">
<span class="mr-2">#{original_data.id}</span> <span class="mr-2">#{original_data.id}</span>
@ -117,16 +107,20 @@
<div class="mb-8 text-3xl font-extrabold leading-tight"> <div class="mb-8 text-3xl font-extrabold leading-tight">
#{original_data.id} #{original_data.id}
<span data-id="stations_actions_${editable.id}"> <span data-id="stations_actions_${editable.id}">
{#if store.state.jwtinfo.userdetails.permissions.includes('STATION:DELETE')} {#if store.state.jwtinfo.userdetails.permissions.includes("STATION:DELETE")}
{#if delete_triggered} {#if delete_triggered}
<button <button
on:click={deleteStation} on:click={deleteStation}
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> 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 <button
on:click={() => { on:click={() => {
delete_triggered = !delete_triggered; delete_triggered = !delete_triggered;
}} }}
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-400 text-base font-medium text-white sm:w-auto sm:text-sm">{$_('cancel')}</button> class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-400 text-base font-medium text-white sm:w-auto sm:text-sm"
>{$_("cancel")}</button
>
{/if} {/if}
{#if !delete_triggered} {#if !delete_triggered}
<button <button
@ -134,7 +128,9 @@
delete_triggered = true; delete_triggered = true;
}} }}
type="button" type="button"
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">{$_('delete-station')}</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-station")}</button
>
{/if} {/if}
{/if} {/if}
{#if !delete_triggered} {#if !delete_triggered}
@ -143,48 +139,49 @@
class:opacity-50={!save_enabled} class:opacity-50={!save_enabled}
type="button" type="button"
on:click={submit} on:click={submit}
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm">{$_('save-changes')}</button> class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm"
>{$_("save-changes")}</button
>
{/if} {/if}
</span> </span>
</div> </div>
<!-- --> <!-- -->
<div class="text-sm w-full"> <div class="text-sm w-full">
<label <label for="track" class="block text-sm font-medium text-gray-700"
for="track" >Track</label
class="block text-sm font-medium text-gray-700">Track</label> >
<Select <Select
containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"
itemFilter={(label, filterText, option) => label itemFilter={(label, filterText, option) =>
.toLowerCase() label.toLowerCase().includes(filterText.toLowerCase()) ||
.includes( option.value.id.toString().startsWith(filterText.toLowerCase())}
filterText.toLowerCase()
) || option.value.id
.toString()
.startsWith(filterText.toLowerCase())}
items={tracks} items={tracks}
showChevron={true} showChevron={true}
placeholder="Search for a track (by name or id)." placeholder="Search for a track (by name or id)."
noOptionsMessage="No track found" noOptionsMessage="No track found"
bind:selectedValue={track} bind:selectedValue={track}
on:select={(selectedValue) => (editable.track = selectedValue.detail.value.id)} on:select={(selectedValue) =>
on:clear={() => (track = null)} /> (editable.track = selectedValue.detail.value.id)}
on:clear={() => (track = null)}
/>
</div> </div>
<div class="text-sm w-full"> <div class="text-sm w-full">
<label <label for="description" class="font-medium text-gray-700"
for="description" >{$_("description")}</label
class="font-medium text-gray-700">{$_('description')}</label> >
<input <input
autocomplete="off" autocomplete="off"
placeholder={$_('description')} placeholder={$_("description")}
type="text" type="text"
bind:value={editable.description} bind:value={editable.description}
name="description" 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" /> 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 class="text-sm w-full"> <div class="text-sm w-full">
<label <label for="enabled" class="ml-1 font-medium text-gray-700"
for="enabled" >{$_("enabled")}</label
class="ml-1 font-medium text-gray-700">{$_('enabled')}</label> >
<br /> <br />
<p class="text-gray-500"> <p class="text-gray-500">
<input <input
@ -195,9 +192,10 @@
name="enabled" name="enabled"
type="checkbox" type="checkbox"
checked={editable.enabled} checked={editable.enabled}
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" /> class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
{$_('this-scanstation-is')} />
{#if editable.enabled}{$_('enabled')}{:else}{$_('disabled')}{/if} {$_("this-scanstation-is")}
{#if editable.enabled}{$_("enabled")}{:else}{$_("disabled")}{/if}
</p> </p>
</div> </div>
</section> </section>

View File

@ -2,7 +2,7 @@
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import store from "../../store"; import store from "../../store";
import AddScanStationModal from "./AddScanStationModal.svelte"; import AddScanStationModal from "./AddScanStationModal.svelte";
import CopyScanStationTokenModal from "./CopyScanStationTokenModal.svelte"; import CopyScanStationTokenModal from "./CopyScanStationTokenModal.svelte";
import ScanStationsOverview from "./ScanStationsOverview.svelte"; import ScanStationsOverview from "./ScanStationsOverview.svelte";
export let modal_open = false; export let modal_open = false;
export let copy_modal_open = false; export let copy_modal_open = false;
@ -12,22 +12,33 @@ import CopyScanStationTokenModal from "./CopyScanStationTokenModal.svelte";
<section class="container p-5"> <section class="container p-5">
<span class="mb-1 text-3xl font-extrabold leading-tight"> <span class="mb-1 text-3xl font-extrabold leading-tight">
{$_('scanstations')} {$_("scanstations")}
{#if store.state.jwtinfo.userdetails.permissions.includes('STATION:CREATE')} {#if store.state.jwtinfo.userdetails.permissions.includes("STATION:CREATE")}
<button <button
on:click={() => { on:click={() => {
modal_open = true; modal_open = true;
}} }}
type="button" 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"> 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-scanstation')} >
{$_("create-a-new-scanstation")}
</button> </button>
{/if} {/if}
</span> </span>
<ScanStationsOverview bind:current_stations bind:modal_open bind:new_station bind:copy_modal_open /> <ScanStationsOverview
bind:current_stations
bind:modal_open
bind:new_station
bind:copy_modal_open
/>
</section> </section>
{#if store.state.jwtinfo.userdetails.permissions.includes('STATION:CREATE')} {#if store.state.jwtinfo.userdetails.permissions.includes("STATION:CREATE")}
<AddScanStationModal bind:modal_open bind:current_stations bind:new_station bind:copy_modal_open/> <AddScanStationModal
<CopyScanStationTokenModal bind:copy_modal_open bind:new_station /> bind:modal_open
bind:current_stations
bind:new_station
bind:copy_modal_open
/>
<CopyScanStationTokenModal bind:copy_modal_open bind:new_station />
{/if} {/if}

Some files were not shown because too many files have changed in this diff Show More