All checks were successful
		
		
	
	Build Latest and dev images / build-container (push) Successful in 1m0s
				
			
		
			
				
	
	
		
			263 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Svelte
		
	
	
	
	
	
			
		
		
	
	
			263 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Svelte
		
	
	
	
	
	
| <script>
 | |
|   import { _ } from "svelte-i18n";
 | |
|   import { DonorService } from "@odit/lfk-client-js";
 | |
|   import store from "../../store";
 | |
|   import DonorsEmptyState from "./DonorsEmptyState.svelte";
 | |
|   import ConfirmDonorDeletion from "./ConfirmDonorDeletion.svelte";
 | |
|   import TableBottom from "../shared/TableBottom.svelte";
 | |
|   import {
 | |
|     createSvelteTable,
 | |
|     flexRender,
 | |
|     getCoreRowModel,
 | |
|     getFilteredRowModel,
 | |
|     getPaginationRowModel,
 | |
|     getSortedRowModel,
 | |
|     renderComponent,
 | |
|   } from "@tanstack/svelte-table";
 | |
|   import { writable } from "svelte/store";
 | |
|   import { onMount } from "svelte";
 | |
|   import InputElement from "../shared/InputElement.svelte";
 | |
|   import TableHeader from "../shared/TableHeader.svelte";
 | |
|   import TableActions from "../shared/TableActions.svelte";
 | |
|   import DonorAddress from "./DonorAddress.svelte";
 | |
|   import DonorDonations from "./DonorDonations.svelte";
 | |
|   import { filterAddress, filterName } from "../shared/tablefilters";
 | |
|   import toast from "svelte-french-toast";
 | |
|   $: searchvalue = "";
 | |
|   $: active_deletes = [];
 | |
|   $: selectedDonors =
 | |
|     $table?.getSelectedRowModel().rows.map((row) => row.original) || [];
 | |
|   $: selected =
 | |
|     $table?.getSelectedRowModel().rows.map((row) => row.index) || [];
 | |
| 
 | |
|   $: dataLoaded = false;
 | |
| 
 | |
|   export let current_donors = [];
 | |
|   export const addDonors = (donors) => {
 | |
|     current_donors = current_donors.concat(...donors);
 | |
|     options.update((options) => ({
 | |
|       ...options,
 | |
|       data: current_donors,
 | |
|     }));
 | |
|   };
 | |
| 
 | |
|   //Section table
 | |
|   const columns = [
 | |
|     {
 | |
|       accessorKey: "id",
 | |
|       header: () => "id",
 | |
|       filterFn: `equalsString`,
 | |
|     },
 | |
|     {
 | |
|       accessorKey: "name",
 | |
|       header: () => $_("name"),
 | |
|       cell: (info) => {
 | |
|         const d = info.row.original;
 | |
|         if (d.middlename) {
 | |
|           return `${d.firstname} ${d.middlename} ${d.lastname}`;
 | |
|         } else {
 | |
|           return `${d.firstname} ${d.lastname}`;
 | |
|         }
 | |
|       },
 | |
|       filterFn: `name`,
 | |
|     },
 | |
|     {
 | |
|       accessorKey: "address",
 | |
|       header: () => $_("contact-information"),
 | |
|       cell: (info) => {
 | |
|         return renderComponent(DonorAddress, { address: info.getValue() });
 | |
|       },
 | |
|       filterFn: `address`,
 | |
|     },
 | |
|     {
 | |
|       accessorKey: "donations",
 | |
|       header: () => $_("sponsorings"),
 | |
|       cell: (info) => {
 | |
|         return renderComponent(DonorDonations, { donations: info.getValue() });
 | |
|       },
 | |
|       enableColumnFilter: false,
 | |
|     },
 | |
|     {
 | |
|       accessorKey: "donationAmount",
 | |
|       header: () => $_("total-donation-amount"),
 | |
|       cell: (info) => {
 | |
|         return `${(info.getValue() / 100)
 | |
|           .toFixed(2)
 | |
|           .toLocaleString("de-DE", { valute: "EUR" })}€`;
 | |
|       },
 | |
|       enableColumnFilter: false,
 | |
|     },
 | |
|     {
 | |
|       accessorKey: "paidDonationAmount",
 | |
|       header: () => $_("total-paid-amount"),
 | |
|       cell: (info) => {
 | |
|         return `${(info.getValue() / 100)
 | |
|           .toFixed(2)
 | |
|           .toLocaleString("de-DE", { valute: "EUR" })}€`;
 | |
|       },
 | |
|       enableColumnFilter: false,
 | |
|     },
 | |
|     {
 | |
|       accessorKey: "actions",
 | |
|       header: () => $_("action"),
 | |
|       cell: (info) => {
 | |
|         return renderComponent(TableActions, {
 | |
|           detailsLink: `./${info.row.original.id}`,
 | |
|           deleteAction: () => {
 | |
|             active_deletes = current_donors.filter(
 | |
|               (r) => r.id == info.row.original.id
 | |
|             );
 | |
|           },
 | |
|           deleteEnabled:
 | |
|             store.state.jwtinfo.userdetails.permissions.includes(
 | |
|               "DONOR:DELETE"
 | |
|             ),
 | |
|         });
 | |
|       },
 | |
|       enableColumnFilter: false,
 | |
|       enableSorting: false,
 | |
|     },
 | |
|   ];
 | |
|   const options = writable({
 | |
|     data: [],
 | |
|     columns: columns,
 | |
|     initialState: {
 | |
|       pagination: {
 | |
|         pageSize: 50,
 | |
|       },
 | |
|     },
 | |
|     filterFns: {
 | |
|       name: filterName,
 | |
|       address: filterAddress,
 | |
|     },
 | |
|     enableRowSelection: true,
 | |
|     getCoreRowModel: getCoreRowModel(),
 | |
|     getFilteredRowModel: getFilteredRowModel(),
 | |
|     getPaginationRowModel: getPaginationRowModel(),
 | |
|     getSortedRowModel: getSortedRowModel(),
 | |
|   });
 | |
|   const table = createSvelteTable(options);
 | |
| 
 | |
|   function should_display_based_on_id(id) {
 | |
|     if (searchvalue.toString().slice(-1) === "*") {
 | |
|       return id.toString().startsWith(searchvalue.replace("*", ""));
 | |
|     }
 | |
|     return id.toString() === searchvalue;
 | |
|   }
 | |
| 
 | |
|   onMount(async () => {
 | |
|     let page = 0;
 | |
|     let pagesize = 300;
 | |
|     while (page >= 0) {
 | |
|       const donors = await DonorService.donorControllerGetAll(page, pagesize);
 | |
|       if (donors.length == 0) {
 | |
|         page = -2;
 | |
|       }
 | |
| 
 | |
|       current_donors = current_donors.concat(...donors);
 | |
|       options.update((options) => ({
 | |
|         ...options,
 | |
|         data: current_donors,
 | |
|       }));
 | |
| 
 | |
|       dataLoaded = true;
 | |
|       page++;
 | |
|     }
 | |
|   });
 | |
| </script>
 | |
| 
 | |
| <ConfirmDonorDeletion
 | |
|   on:cancelDelete={(event) => {
 | |
|     active_deletes = active_deletes.filter((a) => a.id !== event.detail.id);
 | |
|   }}
 | |
|   on:delete={async (event) => {
 | |
|     toast.loading($_("deleting-donor"));
 | |
|     await DonorService.donorControllerRemove(event.detail.id, true);
 | |
|     toast.dismiss();
 | |
|     toast.success($_("donor-deleted"));
 | |
|     current_donors = current_donors.filter((d) => d.id !== event.detail.id);
 | |
|     active_deletes = active_deletes.filter((a) => a.id !== event.detail.id);
 | |
|     options.update((options) => ({
 | |
|       ...options,
 | |
|       data: current_donors,
 | |
|     }));
 | |
|   }}
 | |
|   modal_open={active_deletes.length > 0}
 | |
|   delete_donor={active_deletes[0]}
 | |
| />
 | |
| {#if store.state.jwtinfo.userdetails.permissions.includes("DONOR:GET")}
 | |
|   {#if !dataLoaded}
 | |
|     <div
 | |
|       class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2"
 | |
|       role="alert"
 | |
|     >
 | |
|       <p class="font-bold">{$_("donors-are-being-loaded")}</p>
 | |
|       <p class="text-sm">{$_("this-might-take-a-moment")}</p>
 | |
|     </div>
 | |
|   {:else if current_donors.length === 0}
 | |
|     <DonorsEmptyState />
 | |
|   {:else}
 | |
|     <input
 | |
|       type="search"
 | |
|       bind:value={searchvalue}
 | |
|       placeholder={$_("datatable.search")}
 | |
|       aria-label={$_("datatable.search")}
 | |
|       class="mb-2 w-full sm:w-auto mt-1 sm:mt-0 p-2 rounded-md border"
 | |
|     />
 | |
|     <div
 | |
|       class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll"
 | |
|     >
 | |
|       <table class="w-full">
 | |
|         <thead class="border-b border-gray-400">
 | |
|           {#each $table.getHeaderGroups() as headerGroup}
 | |
|             <tr class="select-none">
 | |
|               <th class="inset-y-0 left-0 px-4 py-2 text-left w-px">
 | |
|                 <InputElement
 | |
|                   type="checkbox"
 | |
|                   checked={$table.getIsAllRowsSelected()}
 | |
|                   indeterminate={$table.getIsSomeRowsSelected()}
 | |
|                   on:change={() => $table.toggleAllRowsSelected()}
 | |
|                 />
 | |
|               </th>
 | |
|               {#each headerGroup.headers as header}
 | |
|                 <TableHeader {header} />
 | |
|               {/each}
 | |
|             </tr>
 | |
|           {/each}
 | |
|         </thead>
 | |
|         <tbody>
 | |
|           {#each $table.getRowModel().rows as row}
 | |
|             <tr class="odd:bg-white even:bg-gray-100">
 | |
|               <td class="inset-y-0 left-0 px-4 py-2 text-center w-px">
 | |
|                 <InputElement
 | |
|                   type="checkbox"
 | |
|                   checked={row.getIsSelected()}
 | |
|                   on:change={() => row.toggleSelected()}
 | |
|                 />
 | |
|               </td>
 | |
|               {#each row.getVisibleCells() as cell}
 | |
|                 <td>
 | |
|                   <svelte:component
 | |
|                     this={flexRender(
 | |
|                       cell.column.columnDef.cell,
 | |
|                       cell.getContext()
 | |
|                     )}
 | |
|                   />
 | |
|                 </td>
 | |
|               {/each}
 | |
|             </tr>
 | |
|           {/each}
 | |
|         </tbody>
 | |
|       </table>
 | |
|     </div>
 | |
|     <div class="h-2" />
 | |
|     <TableBottom {table} {selected} />
 | |
|   {/if}
 | |
| {/if}
 | |
| 
 | |
| <style>
 | |
|   table tbody tr td:nth-child(2) {
 | |
|     font-family: monospace;
 | |
|   }
 | |
| </style>
 |