frontend/src/components/teams/TeamDetail.svelte
Philipp Dormann a2cd54fba4
All checks were successful
Build Latest and dev images / build-container (push) Successful in 1m0s
feat: improve ConfirmTeamDeletionModal
2025-03-28 11:16:44 +01:00

263 lines
8.8 KiB
Svelte

<script>
import {
GroupContactService,
RunnerOrganizationService,
RunnerTeamService,
} from "@odit/lfk-client-js";
import { _ } from "svelte-i18n";
import toast from "svelte-french-toast";
import store from "../../store";
import Select from "svelte-select";
import ImportRunnerModal from "../runners/ImportRunnerModal.svelte";
import PromiseError from "../base/PromiseError.svelte";
import ConfirmTeamDeletionModal from "./ConfirmTeamDeletionModal.svelte";
import GenerateSponsoringContracts from "../pdf_generation/GenerateSponsoringContracts.svelte";
import GenerateRunnerCards from "../pdf_generation/GenerateRunnerCards.svelte";
import GenerateRunnerCertificates from "../pdf_generation/GenerateRunnerCertificates.svelte";
let [teamdata, original, delete_team, orgs, contacts, modal_open] = [
{},
{},
{},
[],
[],
false,
];
export let params;
export let import_modal_open = false;
$: delete_triggered = false;
$: save_enabled = data_changed && teamdata.parentGroup != null;
$: data_loaded = false;
$: data_changed = !(JSON.stringify(teamdata) === JSON.stringify(original));
$: sponsoring_contracts_show = true;
$: cards_show = true;
$: certificates_show = true;
$: generate_teams = [original];
$: group = {};
$: contact = {};
//
const getContactLabel = (option) =>
option.firstname + " " + (option.middlename || "") + " " + option.lastname;
const promise = RunnerTeamService.runnerTeamControllerGetOne(
params.teamid
).then((value) => {
data_loaded = true;
teamdata = Object.assign(teamdata, value);
original = Object.assign(original, value);
RunnerOrganizationService.runnerOrganizationControllerGetAll().then(
(val) => {
orgs = val.map((r) => {
delete r.contact;
r.teams = [];
return { label: r.name, value: r };
});
group = orgs.find((g) => g.value.id == teamdata.parentGroup.id);
}
);
GroupContactService.groupContactControllerGetAll().then((val) => {
contacts = val.map((r) => {
return { label: getContactLabel(r), value: r };
});
if (teamdata.contact) {
contact = contacts.find((g) => g.value.id == teamdata.contact.id);
} else {
contact = null;
}
});
});
function deleteTeam() {
RunnerTeamService.runnerTeamControllerRemove(original.id, false)
.then((resp) => {
toast($_("team-deleted"));
location.replace("./");
})
.catch((err) => {
modal_open = true;
delete_team = original;
});
}
function submit() {
if (data_loaded === true && save_enabled) {
toast($_("updating-team"));
let postdata = teamdata;
postdata.parentGroup = teamdata.parentGroup.id;
postdata.contact = teamdata.contact?.id;
RunnerTeamService.runnerTeamControllerPut(original.id, postdata)
.then((resp) => {
Object.assign(original, teamdata);
original = original;
toast.success($_("updated-team"));
})
.catch((err) => {});
}
}
</script>
<ImportRunnerModal
current_runners={[]}
on:cancelDelete={(event) => {
import_modal_open = false;
}}
passed_team={teamdata}
passed_orgs={[]}
passed_org={{}}
opened_from="TeamDetail"
bind:import_modal_open
/>
<ConfirmTeamDeletionModal bind:modal_open bind:delete_team />
{#if data_loaded}
<section class="container p-5">
<div class="flex flex-row mb-4">
<div class="w-full">
<nav class="w-full flex">
<ol class="list-none flex flex-row items-center justify-start">
<li class="flex items-center">
<a class="mr-2" href="./"
><svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="inline-block"
><path d="m12 19-7-7 7-7" /><path d="M19 12H5" /></svg
>
{$_("teams")}</a
>
</li>
</ol>
</nav>
</div>
</div>
<div class="mb-4 text-3xl font-extrabold leading-tight">
{#if group}
{group.label}{" > "}
{/if}
{original.name} [#{params.teamid}]
<div data-id="org_actions_${teamdata.id}">
<GenerateSponsoringContracts
bind:sponsoring_contracts_show
bind:generate_teams
/>
<GenerateRunnerCards bind:cards_show bind:generate_teams />
<GenerateRunnerCertificates
bind:certificates_show
bind:generate_teams
/>
{#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:IMPORT")}
<button
on:click={() => {
import_modal_open = true;
}}
type="button"
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:w-auto sm:text-sm"
>
{$_("import-runners")}
</button>
{/if}
{#if store.state.jwtinfo.userdetails.permissions.includes("TEAM:DELETE")}
{#if delete_triggered}
<button
on:click={deleteTeam}
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:w-auto sm:text-sm"
>{$_("confirm-delete")}</button
>
<button
on:click={() => {
delete_triggered = !delete_triggered;
}}
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-400 text-base font-medium text-white sm:w-auto sm:text-sm"
>{$_("cancel")}</button
>
{/if}
{#if !delete_triggered}
<button
on:click={() => {
delete_triggered = true;
}}
type="button"
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:w-auto sm:text-sm"
>{$_("delete-team")}</button
>
{/if}
{/if}
{#if !delete_triggered}
<button
on:click={submit}
disabled={!save_enabled}
class:opacity-50={!save_enabled}
type="button"
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:w-auto sm:text-sm mb-1 lg:mb-0"
>{$_("save-changes")}</button
>
{/if}
</div>
</div>
<div class="text-sm w-full mt-2">
<label for="name" class="font-semibold text-gray-700">Name</label>
<input
autocomplete="off"
placeholder="Name"
type="text"
bind:value={teamdata.name}
name="name"
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 rounded-md p-2"
/>
</div>
<div class="text-sm w-full mt-2">
<label for="contact" class="font-semibold text-gray-700"
>{$_("contact")}</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-neutral-800 rounded-md p-2"
itemFilter={(label, filterText, option) =>
label.toLowerCase().includes(filterText.toLowerCase()) ||
option.value.id.toString().startsWith(filterText.toLowerCase())}
items={contacts}
showChevron={true}
placeholder={$_("no-contact-selected")}
noOptionsMessage={$_("no-contact-found")}
bind:selectedValue={contact}
on:select={(selectedValue) =>
(teamdata.contact = selectedValue.detail.value)}
on:clear={() => (teamdata.contact = null)}
/>
</div>
<div class="text-sm w-full mt-2">
<label for="org" class="font-semibold text-gray-700"
>{$_("organization")}</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-neutral-800 rounded-md p-2"
itemFilter={(label, filterText, option) =>
label.toLowerCase().includes(filterText.toLowerCase()) ||
option.id.value.toString().startsWith(filterText.toLowerCase())}
items={orgs}
showChevron={true}
placeholder={$_("search-for-an-organization-by-name-or-id")}
noOptionsMessage={$_("no-organizations-found")}
bind:selectedValue={group}
on:select={(selectedValue) =>
(teamdata.parentGroup = selectedValue.detail.value)}
on:clear={() => (teamdata.parentGroup = null)}
/>
</div>
<div class="text-sm w-full mt-2">
<span class="font-semibold text-gray-700">{$_("distance")}</span>
<br />
<span class="text-gray-700"
>{(original.total_distance / 1000).toFixed(2)} km</span
>
</div>
</section>
{:else}
{#await promise}
{$_("team-detail-is-being-loaded")}
{:catch error}
<PromiseError />
{/await}
{/if}