2021-12-18 20:10:02 +01:00
< script lang = "ts" >
2022-04-25 19:29:46 +02:00
import { Link } from 'svelte-navigator';
2021-12-19 00:58:55 +01:00
import { Card , CardHeader , CardBody , CardTitle , Alert , Accordion , AccordionItem , InputGroupText , InputGroup , Input , Label , Row , Col , Spinner , Button , Tooltip } from 'sveltestrap';
2021-12-19 14:24:21 +01:00
import FaUsers from 'svelte-icons/fa/FaUsers.svelte'
2021-12-18 20:10:02 +01:00
import { onMount } from 'svelte';
import FaSearch from 'svelte-icons/fa/FaSearch.svelte'
import { useParams } from 'svelte-navigator';
import CardsHeader from '../CardsHeader.svelte';
import ListPagination from '../ListPagination.svelte';
2021-12-19 00:58:55 +01:00
import Body from './Body.svelte';
import Svelecte, { addFormatter } from 'svelecte';
2021-12-19 14:24:21 +01:00
import FaLock from 'svelte-icons/fa/FaLock.svelte';
2022-03-02 10:57:04 +01:00
import NewGroup from './NewGroup.svelte';
2021-12-18 20:10:02 +01:00
2022-02-01 15:24:45 -05:00
import { Member , Group } from '../../api/types';
import api from '../../api';
2021-12-18 20:10:02 +01:00
export let isPublic: boolean;
2021-12-19 00:58:55 +01:00
export let list: Group[];
export let members: Member[];
2021-12-30 10:23:03 +01:00
$: memberlist = members && members.map(function(member) { return { name : member.name , shortid : member.id , id : member.uuid , display_name : member.display_name }; } ).sort((a, b) => a.name.localeCompare(b.name));
2021-12-19 00:58:55 +01:00
2021-12-18 20:10:02 +01:00
let token = localStorage.getItem("pk-token");
let listLoading = true;
let err: string;
2022-04-25 19:29:46 +02:00
let settings = JSON.parse(localStorage.getItem("pk-settings"));
let itemsPerPageValue = settings & & settings.accessibility & & settings.accessibility.expandedcards ? "10" : "25";
2021-12-18 20:10:02 +01:00
$: itemsPerPage = parseInt(itemsPerPageValue);
let searchBy = "name";
let sortBy = "name";
let sortOrder = "ascending";
let privacyFilter = "all";
2021-12-19 00:58:55 +01:00
let memberSearchMode = "include";
let selectedMembers = [];
2021-12-18 20:10:02 +01:00
let currentPage = 1;
let params = useParams();
$: id = $params.id;
onMount(() => {
if (token || isPublic) fetchGroups();
});
async function fetchGroups() {
2022-03-01 12:29:21 +01:00
err = "";
2021-12-18 20:10:02 +01:00
listLoading = true;
try {
2022-02-01 15:24:45 -05:00
const res: Group[] = await api().systems(isPublic ? id : "@me").groups.get({ auth : ! isPublic , query : { with_members : ! isPublic } } );
2021-12-18 20:10:02 +01:00
list = res;
listLoading = false;
} catch (error) {
console.log(error);
err = error.message;
listLoading = false;
}
}
let searchValue: string;
$: { searchValue ; privacyFilter ; currentPage = 1 } ;
$: searchedList = list.filter((item) => {
2021-12-30 08:27:52 +01:00
if (!searchValue & & searchBy !== "description" & & searchBy !== "display name") return true;
2021-12-18 20:10:02 +01:00
switch (searchBy) {
case "name": if (item.name.toLowerCase().includes(searchValue.toLowerCase())) return true;
break;
2021-12-30 08:27:52 +01:00
case "display name": if (!searchValue) {
if (!item.display_name) return true;
else return false;
}
if (item.display_name & & item.display_name.toLowerCase().includes(searchValue.toLowerCase())) return true;
2021-12-18 20:10:02 +01:00
break;
2021-12-30 08:27:52 +01:00
case "description": if (!searchValue) {
if (!item.description) return true;
else return false;
}
else if (item.description & & item.description.toLowerCase().includes(searchValue.toLowerCase())) return true;
2021-12-18 20:10:02 +01:00
break;
case "ID": if (item.id.toLowerCase().includes(searchValue.toLowerCase())) return true;
break;
default: if (item.name.toLowerCase().includes(searchValue.toLowerCase())) return true;
break;
}
return false;
})
$: filteredList = searchedList.filter((item) => {
if (privacyFilter === "all") return true;
if (privacyFilter === "public" & & item.privacy.visibility === "public") return true;
if (privacyFilter === "private" & & item.privacy.visibility === "private") return true;
return false;
});
let sortedList = [];
$: if (filteredList) {
switch (sortBy) {
case "name": sortedList = filteredList.sort((a, b) => a.name.localeCompare(b.name));
break;
case "display name": sortedList = filteredList.sort((a, b) => {
if (a.display_name & & b.display_name) return a.display_name.localeCompare(b.display_name);
else if (a.display_name & & !b.display_name) return a.display_name.localeCompare(b.name);
else if (!a.display_name & & b.display_name) return a.name.localeCompare(b.display_name);
else return a.name.localeCompare(b.name);
});
break;
2021-12-19 15:19:43 +01:00
case "creation date": sortedList = filteredList.sort((a, b) => {
if (a.created & & b.created) return a.created.localeCompare(b.created);
});
2021-12-18 20:10:02 +01:00
break;
case "ID": sortedList = filteredList.sort((a, b) => a.id.localeCompare(b.id));
break;
default: sortedList = filteredList.sort((a, b) => a.name.localeCompare(b.name));
break;
}
}
2021-12-19 00:58:55 +01:00
let memberFilteredList = [];
$: memberFilteredList = sortedList.filter((item: Group) => {
2021-12-21 00:20:25 +01:00
if (memberSearchMode === "none") {
if (item.members & & item.members.length > 0) return false;
}
2021-12-19 00:58:55 +01:00
if (selectedMembers.length < 1 ) return true ;
switch (memberSearchMode) {
2021-12-21 00:20:25 +01:00
case "include": if (item.members & & selectedMembers.some(value => item.members.includes(value))) return true;
2021-12-19 00:58:55 +01:00
break;
2021-12-21 00:20:25 +01:00
case "exclude": if (item.members & & selectedMembers.every(value => !item.members.includes(value))) return true;
2021-12-19 00:58:55 +01:00
break;
2021-12-21 00:20:25 +01:00
case "match": if (item.members & & selectedMembers.every(value => item.members.includes(value))) return true;
2021-12-19 00:58:55 +01:00
break;
default: return true;
}
return false;
});
let finalList = [];
$:{ sortOrder ; if ( sortOrder === "descending" ) finalList = memberFilteredList . reverse (); else finalList = memberFilteredList ;}
$: finalList = finalList;
2021-12-18 20:10:02 +01:00
$: indexOfLastItem = currentPage * itemsPerPage;
$: indexOfFirstItem = indexOfLastItem - itemsPerPage;
2021-12-19 00:58:55 +01:00
$: pageAmount = Math.ceil(finalList.length / itemsPerPage);
2021-12-18 20:10:02 +01:00
2021-12-19 12:01:55 +01:00
let slicedList = [];
2021-12-19 00:58:55 +01:00
$: slicedList = finalList.slice(indexOfFirstItem, indexOfLastItem);
function memberListRenderer(item: any) {
return `${ item . name } (< code > ${ item . shortid } </ code > )`;
}
addFormatter({
'member-list': memberListRenderer
});
2021-12-18 20:10:02 +01:00
2021-12-19 12:01:55 +01:00
function updateList(event: any) {
list = list.map(group => group.id !== event.detail.id ? group : event.detail)
}
2022-01-09 15:01:24 +01:00
function updateDelete(event: any) {
list = list.filter(group => group.id !== event.detail);
}
2022-03-02 10:57:04 +01:00
function addGroupToList(event: any) {
2022-04-28 20:29:23 +02:00
// reference types my beloathed
// the stringify/parse sequence is here so that a previous added group doesn't get overwritten
let group = JSON.parse(JSON.stringify(event.detail));
2022-04-27 20:46:39 +02:00
group.members = [];
2022-04-27 20:15:57 +02:00
list.push(group);
2022-03-02 10:57:04 +01:00
list = list;
}
2021-12-18 20:10:02 +01:00
< / script >
< Card class = "mb-3" >
< CardHeader >
< CardTitle >
< CardTitle style = "margin-top: 8px; outline: none;" >
< div class = "icon d-inline-block" >
< FaSearch / >
< / div > Search groups
< / CardTitle >
< / CardTitle >
< / CardHeader >
< CardBody >
< Row >
< Col xs = { 12 } lg= { 3 } class = "mb-2" >
< InputGroup >
< InputGroupText > Page length< / InputGroupText >
2022-05-20 12:29:20 +02:00
< Input bind:value = { itemsPerPageValue } type="select" aria-label = "page length" >
2021-12-18 20:10:02 +01:00
< option > 10< / option >
< option > 25< / option >
< option > 50< / option >
< / Input >
< / InputGroup >
< / Col >
< Col xs = { 12 } lg= { 3 } class = "mb-2" >
< InputGroup >
< InputGroupText > Search by< / InputGroupText >
2022-05-20 12:29:20 +02:00
< Input bind:value = { searchBy } type="select" aria-label = "search by" >
2021-12-18 20:10:02 +01:00
< option > name< / option >
< option > display name< / option >
< option > description< / option >
< option > ID< / option >
< / Input >
< / InputGroup >
< / Col >
< Col xs = { 12 } lg= { 3 } class = "mb-2" >
< InputGroup >
< InputGroupText > Sort by< / InputGroupText >
2022-05-20 12:29:20 +02:00
< Input bind:value = { sortBy } type="select" aria-label = "sort by" >
2021-12-18 20:10:02 +01:00
< option > name< / option >
< option > display name< / option >
{ #if ! isPublic } < option > creation date</ option > { /if }
< option > ID< / option >
< / Input >
< / InputGroup >
< / Col >
< Col xs = { 12 } lg= { 3 } class = "mb-2" >
< InputGroup >
< InputGroupText > Sort order< / InputGroupText >
2022-05-20 12:29:20 +02:00
< Input bind:value = { sortOrder } type="select" aria-label = "sort order" >
2021-12-18 20:10:02 +01:00
< option > ascending< / option >
< option > descending< / option >
< / Input >
< / InputGroup >
< / Col >
{ #if ! isPublic }
2021-12-19 00:58:55 +01:00
< Col xs = { 12 } lg= { 3 } class = "mb-2" >
2021-12-18 20:10:02 +01:00
< InputGroup >
< InputGroupText > Only show< / InputGroupText >
2022-05-20 12:29:20 +02:00
< Input bind:value = { privacyFilter } type="select" aria-label = "only show" >
2021-12-18 20:10:02 +01:00
< option > all< / option >
< option > public< / option >
< option > private< / option >
< / Input >
< / InputGroup >
< / Col >
{ /if }
< / Row >
2021-12-19 00:58:55 +01:00
{ #if ! isPublic }
< hr / >
< Label > Filter groups by member< / Label >
2021-12-21 00:21:46 +01:00
< Svelecte renderer = "member-list" bind:value = { selectedMembers } disableHighlight options = { memberlist } multiple style = "margin-bottom: 0.5rem" >
2021-12-19 00:58:55 +01:00
< / Svelecte >
2021-12-29 10:26:36 +01:00
< span style = "cursor: pointer" id = "g-include" on:click = {() => memberSearchMode = "include" } > { @html memberSearchMode === "include" ? "<b>include</b>" : "include" } </span >
| < span style = "cursor: pointer" id = "g-exclude" on:click = {() => memberSearchMode = "exclude" } > { @html memberSearchMode === "exclude" ? "<b>exclude</b>" : "exclude" } </span >
| < span style = "cursor: pointer" id = "g-match" on:click = {() => memberSearchMode = "match" } > { @html memberSearchMode === "match" ? "<b>exact match</b>" : "exact match" } </span >
| < span style = "cursor: pointer" id = "g-none" on:click = {() => memberSearchMode = "none" } > { @html memberSearchMode === "none" ? "<b>none</b>" : "none" } </span >
< Tooltip placement = "bottom" target = "g-include" > Includes every group with any of the members.< / Tooltip >
< Tooltip placement = "bottom" target = "g-exclude" > Excludes every group with any of the members, opposite of include.< / Tooltip >
< Tooltip placement = "bottom" target = "g-match" > Only includes groups which have all the members selected.< / Tooltip >
< Tooltip placement = "bottom" target = "g-none" > Only includes groups that have no members.< / Tooltip >
2021-12-19 00:58:55 +01:00
{ /if }
2021-12-18 20:10:02 +01:00
< / CardBody >
< / Card >
{ #if listLoading && ! err }
< div class = "mx-auto text-center" >
< Spinner class = "d-inline-block" / >
< / div >
{ :else if err }
2022-03-01 12:29:21 +01:00
< Row >
< Col xs = { 12 } lg= { 10 } >
< Alert color = "danger" > { err } </ Alert >
< / Col >
< Col xs = { 12 } lg= { 2 } >
2022-05-20 12:29:20 +02:00
< Button class = "w-100 mb-3" color = "primary" on:click = { fetchGroups } aria-label="refresh group list " > Refresh</ Button >
2022-03-01 12:29:21 +01:00
< / Col >
< / Row >
2021-12-18 20:10:02 +01:00
{ : else }
< Row >
< Col xs = { 12 } lg= { 10 } class = "mb-2 mb-lg-0" >
< Input class = "mb-3" bind:value = { searchValue } placeholder="search by { searchBy } ..." />
< / Col >
< Col xs = { 12 } lg= { 2 } >
2022-05-20 12:29:20 +02:00
< Button class = "w-100 mb-3" color = "primary" on:click = { fetchGroups } aria-label="refresh group list " > Refresh</ Button >
2021-12-18 20:10:02 +01:00
< / Col >
< / Row >
< ListPagination bind:currentPage bind:pageAmount / >
2022-03-02 16:49:07 +01:00
{ #if ! isPublic }
2022-03-02 10:57:04 +01:00
< NewGroup on:create = { addGroupToList } / >
2022-03-02 16:49:07 +01:00
{ /if }
2022-04-25 20:39:44 +02:00
{ #if settings && settings . accessibility ? ( ! settings . accessibility . expandedcards && ! settings . accessibility . pagelinks ) : true }
2021-12-18 20:10:02 +01:00
< Accordion class = "my-3" stayOpen >
2021-12-19 09:53:13 +01:00
{ #each slicedList as group , index ( group . id )}
2021-12-19 14:24:21 +01:00
{ #if ( ! isPublic && group . privacy . visibility === "public" ) || isPublic }
2021-12-18 20:10:02 +01:00
< AccordionItem >
2021-12-30 10:22:08 +01:00
< CardsHeader bind:item = { group } slot="header" >
2021-12-19 14:24:21 +01:00
< FaUsers slot = "icon" / >
2021-12-18 20:10:02 +01:00
< / CardsHeader >
2022-03-20 07:53:56 +01:00
< Body on:deletion = { updateDelete } on:update= { updateList } on:updateMembers = { updateList } bind:members bind:group bind:isPublic = { isPublic } / >
2021-12-18 20:10:02 +01:00
< / AccordionItem >
2021-12-19 14:24:21 +01:00
{ : else }
< AccordionItem >
2021-12-30 10:22:08 +01:00
< CardsHeader bind:item = { group } slot="header" >
2021-12-19 14:24:21 +01:00
< FaLock slot = "icon" / >
< / CardsHeader >
2022-03-20 07:53:56 +01:00
< Body on:deletion = { updateDelete } on:update= { updateList } on:updateMembers = { updateList } bind:members bind:group bind:isPublic = { isPublic } / >
2021-12-19 14:24:21 +01:00
< / AccordionItem >
{ /if }
2021-12-18 20:10:02 +01:00
{ /each }
< / Accordion >
2022-04-25 19:29:46 +02:00
{ :else if settings . accessibility . expandedcards }
{ #each slicedList as group , index ( group . id )}
{ #if ( ! isPublic && group . privacy . visibility === "public" ) || isPublic }
2022-04-25 20:07:22 +02:00
< Card class = "mb-3" >
2022-04-25 19:29:46 +02:00
< CardHeader >
< CardsHeader item = { group } >
< FaUsers slot = "icon" / >
< / CardsHeader >
< / CardHeader >
< CardBody >
< Body on:deletion = { updateDelete } on:update= { updateList } on:updateMembers = { updateList } isPublic= { isPublic } bind:members bind:group />
< / CardBody >
< / Card >
{ : else }
2022-04-25 20:07:22 +02:00
< Card class = "mb-3" >
2022-04-25 19:29:46 +02:00
< CardHeader >
< CardsHeader item = { group } >
< FaLock slot = "icon" / >
< / CardsHeader >
< / CardHeader >
< CardBody >
< Body on:deletion = { updateDelete } on:update= { updateList } on:updateMembers = { updateList } isPublic= { isPublic } bind:group bind:members />
< / CardBody >
< / Card >
{ /if }
{ /each }
{ : else }
2022-04-25 20:07:22 +02:00
< div class = "my-3" >
2022-04-25 19:29:46 +02:00
{ #each slicedList as group , index ( group . id )}
{ #if ( ! isPublic && group . privacy . visibility === "public" ) || isPublic }
< Card >
< Link class = "accordion-button collapsed" style = "text-decoration: none;" to = { ! isPublic ? `/dash/g/$ { group . id } ` : ` /profile/g / $ { group . id } ` } >
< CardsHeader bind:item = { group } >
< FaUsers slot = "icon" / >
< / CardsHeader >
< / Link >
< / Card >
{ : else }
< Card >
< Link class = "accordion-button collapsed" style = "text-decoration: none;" to = { ! isPublic ? `/dash/g/$ { group . id } ` : ` /profile/g / $ { group . id } ` } >
< CardsHeader bind:item = { group } >
< FaLock slot = "icon" / >
< / CardsHeader >
< / Link >
< / Card >
{ /if }
{ /each }
2022-04-25 20:07:22 +02:00
< / div >
2022-04-25 19:29:46 +02:00
{ /if }
2021-12-18 20:10:02 +01:00
< ListPagination bind:currentPage bind:pageAmount / >
{ /if }