Skip to content

Commit

Permalink
Merge pull request #87 from ssciwr/add-search-function-for-milestone-…
Browse files Browse the repository at this point in the history
…components

Add search function for milestone components
  • Loading branch information
MaHaWo authored Sep 25, 2024
2 parents 3ae8dba + 7bedd88 commit c12c462
Show file tree
Hide file tree
Showing 7 changed files with 380 additions and 66 deletions.
1 change: 1 addition & 0 deletions frontend/src/lib/components/ChildrenRegistration.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@
});
console.log(childData);
await children.save();
await goto(nextpage as string);
} else {
showAlert = true;
Expand Down
46 changes: 45 additions & 1 deletion frontend/src/lib/components/Childrenpage.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,50 @@
let data: ChildData[] = [];
let loading = true;
function searchName(data: any[], key: string): any[] {
if (key === '') {
return data;
} else {
const res = data.filter((item) => {
return item.header.toLowerCase().includes(key.toLowerCase());
});
return res;
}
}
function searchRemarks(data: any[], key: string): any[] {
if (key === '') {
return data;
} else {
const res = data.filter((item) => {
return item.summary.toLowerCase().includes(key.toLowerCase());
});
return res;
}
}
function searchAll(data: any[], key: string) {
return [...new Set([...searchName(data, key), ...searchRemarks(data, key)])];
}
const searchData = [
{
label: 'Alle',
placeholder: 'Alle Kategorien durchsuchen',
filterFunction: searchAll
},
{
label: 'Name',
placeholder: 'Kinder nach Namen durchsuchen',
filterFunction: searchName
},
{
label: 'Bemerkung',
placeholder: 'Bemerkungen zu Kindern durchsuchen',
filterFunction: searchRemarks
}
];
// this fetches dummy child data for the dummy user whenever the component is mounted into the dom
// it is conceptualized as emulating an API call that would normally fetch this from the server.
onMount(init);
Expand All @@ -104,8 +148,8 @@
<GalleryDisplay
{data}
itemComponent={CardDisplay}
searchableCol={'header'}
componentProps={createStyle(data)}
{searchData}
/>
{/if}
</div>
82 changes: 68 additions & 14 deletions frontend/src/lib/components/DataDisplay/GalleryDisplay.svelte
Original file line number Diff line number Diff line change
@@ -1,25 +1,40 @@
<script lang="ts">
import { Gallery, Heading, Search } from 'flowbite-svelte';
export let filterData = (data, col, searchTerm) => {
if (searchTerm === '') {
return data;
} else {
return data.filter((item) => item[col].toLowerCase().includes(searchTerm.toLowerCase()));
}
};
import { Button, Dropdown, DropdownItem, Gallery, Heading, Search } from 'flowbite-svelte';
import { ChevronDownOutline } from 'flowbite-svelte-icons';
import { tick } from 'svelte';
export let data;
export let header: string | null = null;
export let itemComponent;
export let withSearch = true;
export let searchableCol = '';
export let componentProps;
export let searchPlaceHolder = 'Durchsuchen';
export let searchData = [
{
label: 'Alle',
placeholder: 'Durchsuchen',
filterFunction: (data: any[], searchTerm: string): any[] => {
if (searchTerm === '') {
return data;
} else {
return data.filter((item) =>
Object.values(item).some((element) => {
return element.toLowerCase().includes(searchTerm.toLowerCase());
})
);
}
}
}
];
let searchCategory: string = searchData[0].label;
let searchPlaceHolder: string = searchData[0].placeholder;
let filterData = searchData[0].filterFunction;
let dropdownOpen: boolean = false;
// dynamic statements
let searchTerm = '';
$: filteredItems = withSearch === true ? filterData(data, searchableCol, searchTerm) : data;
$: filteredItems = withSearch === true ? filterData(data, searchTerm) : data;
// Create a new array of componentProps that matches the filtered data
$: filteredComponentProps = filteredItems.map((item) => {
Expand All @@ -41,8 +56,47 @@
{/if}

{#if withSearch}
<form class="m-2 w-full p-4">
<Search size="md" placeholder={searchPlaceHolder} bind:value={searchTerm} />
<form class="m-2 flex w-full rounded p-4">
{#if searchData.length > 1}
<!-- after example: https://flowbite-svelte.com/docs/forms/search-input#Search_with_dropdown -->
<div class="relative">
<Button
class="h-full whitespace-nowrap rounded-e-none border border-e-0 border-primary-700"
>
{searchCategory}
<ChevronDownOutline class="ms-2.5 h-2.5 w-2.5" />
</Button>
<Dropdown classContainer="flex w-auto" bind:open={dropdownOpen}>
{#each searchData as { label, placeholder, filterFunction }}
<DropdownItem
on:click={async () => {
searchCategory = label;
searchPlaceHolder = placeholder;
filterData = filterFunction;
dropdownOpen = false;
await tick();
}}
class={searchCategory === label ? 'underline' : ''}
>
{label}
</DropdownItem>
{/each}
</Dropdown>
</div>
<Search
class="rounded-e rounded-s-none py-2.5"
size="md"
placeholder={searchPlaceHolder}
bind:value={searchTerm}
/>
{:else}
<Search
size="md"
class="rounded py-2.5"
placeholder={searchPlaceHolder}
bind:value={searchTerm}
/>
{/if}
</form>
{/if}

Expand Down
23 changes: 3 additions & 20 deletions frontend/src/lib/components/MilestoneGroup.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,10 @@
import Breadcrumbs from '$lib/components/Breadcrumbs.svelte';
import CardDisplay from '$lib/components/DataDisplay/CardDisplay.svelte';
import GalleryDisplay from '$lib/components/DataDisplay/GalleryDisplay.svelte';
export let breadcrumbdata: any[] = [];
export let milestonedata: any[] = [];
function filterData(data: object[], dummy: any, key: string): object[] {
if (key === '') {
return data;
} else {
return data.filter((item) => {
// button label contains info about completion status => use for search
if (key === completeKey) {
return item.progress === 1;
} else if (key === incompleteKey) {
return item.progress < 1;
} else {
return item.header.toLowerCase().includes(key.toLowerCase());
}
});
}
}
export let searchData: any[] = [];
// FIXME:styling has no business being here... not sure where to put it though given thatparts of it are data dependent
export function createStyle(data) {
Expand Down Expand Up @@ -52,11 +37,9 @@
<GalleryDisplay
data={milestonedata.sort((a, b) => a.progress - b.progress)}
itemComponent={CardDisplay}
searchableCol={'header'}
componentProps={createStyle(milestonedata)}
searchPlaceHolder={`Nach Status (${completeKey}/${incompleteKey}) oder Titel durchsuchen`}
withSearch={true}
{filterData}
{searchData}
/>
</div>
</div>
23 changes: 4 additions & 19 deletions frontend/src/lib/components/MilestoneOverview.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,6 @@
import GalleryDisplay from '$lib/components/DataDisplay/GalleryDisplay.svelte';
import { CheckCircleSolid, ExclamationCircleSolid } from 'flowbite-svelte-icons';
function filterData(data: object[], dummy: any, key: string): object[] {
if (key === '') {
return data;
} else {
return data.filter((item) => {
// button label contains info about completion status => use for search
if (key === completeKey) {
return item.complete === true;
} else if (key === incompleteKey) {
return item.complete === false;
} else {
return item.header.toLowerCase().includes(key.toLowerCase());
}
});
}
}
// FIXME: this must go eventually. Either must happen in the backend or there
// should be in a refactored version of the card component
function convertData(data: object[]): object[] {
Expand All @@ -30,6 +13,8 @@
header: item.title,
href: `${base}/milestone`, // hardcoded link for the moment
complete: item.answer !== null,
summary: item.desc,
answer: item.answer,
auxilliary: item.answer !== null ? CheckCircleSolid : ExclamationCircleSolid
};
});
Expand All @@ -38,6 +23,7 @@
const completeKey = 'fertig';
const incompleteKey = 'unfertig';
export let breadcrumbdata: object[] = [];
export let searchData: any[];
export let data: object[] = [];
const rawdata = convertData(data).sort((a, b) => a.complete - b.complete); // FIXME: the convert step should not be here and will be handeled backend-side
</script>
Expand All @@ -60,9 +46,8 @@
}
};
})}
searchPlaceHolder={`Nach Status (${completeKey}/${incompleteKey}) oder Titel durchsuchen`}
withSearch={true}
{filterData}
{searchData}
/>
</div>
</div>
Loading

0 comments on commit c12c462

Please sign in to comment.