group pages!
This commit is contained in:
		| @@ -9,6 +9,8 @@ | ||||
|   import Main from "./pages/profiles/Main.svelte"; | ||||
|   import Status from './pages/status.svelte'; | ||||
|   import Member from './pages/Member.svelte'; | ||||
|   import Group from './pages/Group.svelte'; | ||||
|   import { Alert } from 'sveltestrap'; | ||||
|    | ||||
|   // theme cdns (I might make some myself too) | ||||
|   let light = "https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css"; | ||||
| @@ -51,21 +53,20 @@ | ||||
|     <Route path="/"><Home /></Route> | ||||
|     <Route path="dash"><Dash /></Route> | ||||
|     <Route path="dash/m/:id"><Member isPublic={falseBool}/></Route> | ||||
|     <Route path = "dash/g/:id"><Group isPublic={falseBool}/></Route> | ||||
|     <Route path="settings"><Settings /></Route> | ||||
|     <Route path="profile"><Public /></Route> | ||||
|     <Route path = "profile/s/:id"><Main /></Route> | ||||
|     <Route path = "s"> | ||||
|       hey please provide a system | ||||
|       <Alert color="danger">Please provide a system ID in the URL.</Alert> | ||||
|     </Route> | ||||
|     <Route path = "profile/m/:id"><Member/></Route> | ||||
|     <Route path = "profile/m"> | ||||
|       hey please provide a member | ||||
|     </Route> | ||||
|     <Route path = "profile/g/:id"> | ||||
|       group! | ||||
|       <Alert color="danger">Please provide a member ID in the URL.</Alert> | ||||
|     </Route> | ||||
|     <Route path = "profile/g/:id"><Group/></Route> | ||||
|     <Route path = "profile/g"> | ||||
|       hey please provide a group | ||||
|       <Alert color="danger">Please provide a group ID in the URL.</Alert> | ||||
|     </Route> | ||||
|     <Route path="status"><Status /></Route> | ||||
|   <Footer /> | ||||
|   | ||||
| @@ -6,6 +6,7 @@ | ||||
|     import twemoji from 'twemoji'; | ||||
|     import Privacy from './Privacy.svelte'; | ||||
|     import MemberEdit from './MemberEdit.svelte'; | ||||
|     import { navigate } from 'svelte-navigator'; | ||||
|  | ||||
|     import { Member, Group } from '../../api/types'; | ||||
|     | ||||
| @@ -15,6 +16,7 @@ | ||||
|     export let isPublic: boolean; | ||||
|     export let members: Member[] = []; | ||||
|     export let isMainDash = true; | ||||
|     export let isPage = false; | ||||
|  | ||||
|     let htmlDescription: string; | ||||
|     $: if (group.description) {  | ||||
| @@ -107,6 +109,11 @@ | ||||
| <Button style="flex: 0" color="primary" on:click={() => editMode = true}>Edit</Button>  | ||||
| {#if isMainDash}<Button style="flex: 0" color="secondary" on:click={() => memberMode = true}>Members</Button>{/if} | ||||
| {/if} | ||||
| {#if !isPage} | ||||
|     <Button style="flex: 0; {!isPublic && "float: right;"}" color="primary" on:click={() => navigate(isPublic ? `/profile/g/${group.id}` : `/dash/g/${group.id}`)}>View page</Button> | ||||
|     {:else if !isPublic} | ||||
|     <Button style="flex: 0; {!isPublic && "float: right;"}" color="primary" on:click={() => navigate("/dash?tab=groups")}>View system</Button> | ||||
|     {/if} | ||||
| {:else if editMode} | ||||
| <Edit on:deletion on:update bind:group bind:editMode /> | ||||
| {:else if memberMode} | ||||
|   | ||||
| @@ -16,6 +16,7 @@ | ||||
|     export let member: Member; | ||||
|     export let isPublic: boolean = false; | ||||
|     export let isPage: boolean = false; | ||||
|     export let isMainDash = true; | ||||
|  | ||||
|     let editMode: boolean = false; | ||||
|     let groupMode: boolean = false; | ||||
| @@ -145,7 +146,8 @@ | ||||
|     <img src={member.banner} alt="your system banner" class="w-100 mb-3 rounded" style="max-height: 17em; object-fit: cover"/> | ||||
|     {/if} | ||||
|     {#if !isPublic} | ||||
|     <Button style="flex: 0" color="primary" on:click={() => editMode = true}>Edit</Button> <Button style="flex: 0" color="secondary" on:click={() => groupMode = true}>Groups</Button> | ||||
|     <Button style="flex: 0" color="primary" on:click={() => editMode = true}>Edit</Button> | ||||
|     {#if isMainDash}<Button style="flex: 0" color="secondary" on:click={() => groupMode = true}>Groups</Button>{/if} | ||||
|     {/if} | ||||
|     {#if !isPage} | ||||
|     <Button style="flex: 0; {!isPublic && "float: right;"}" color="primary" on:click={() => navigate(isPublic ? `/profile/m/${member.id}` : `/dash/m/${member.id}`)}>View page</Button> | ||||
|   | ||||
							
								
								
									
										187
									
								
								src/pages/Group.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								src/pages/Group.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,187 @@ | ||||
| <script lang="ts"> | ||||
|     import { Container, Row, Col, Alert, Spinner, Card, CardHeader, CardBody, Accordion, AccordionItem, CardTitle } from "sveltestrap"; | ||||
|     import Body from '../lib/group/Body.svelte'; | ||||
|     import MemberBody from '../lib/member/Body.svelte'; | ||||
|     import { useParams, Link } from 'svelte-navigator'; | ||||
|     import { onMount } from 'svelte'; | ||||
|     import api from "../api"; | ||||
|     import { Member, Group } from "../api/types"; | ||||
|     import CardsHeader from "../lib/CardsHeader.svelte"; | ||||
|     import FaUsers from 'svelte-icons/fa/FaUsers.svelte'; | ||||
|     import FaList from 'svelte-icons/fa/FaList.svelte'; | ||||
|     import FaUserCircle from 'svelte-icons/fa/FaUserCircle.svelte'; | ||||
|     import ListPagination from '../lib/ListPagination.svelte'; | ||||
|     import FaLock from 'svelte-icons/fa/FaLock.svelte' | ||||
|  | ||||
|     let loading = true; | ||||
|     let memberLoading = false; | ||||
|     let params = useParams(); | ||||
|     let err = ""; | ||||
|     let memberErr = ""; | ||||
|     let group: Group; | ||||
|     let members: Member[] = []; | ||||
|     let systemMembers: Group[] = []; | ||||
|     let isMainDash = false; | ||||
|     let isDeleted = false; | ||||
|  | ||||
|     const isPage = true; | ||||
|     export let isPublic = true; | ||||
|     let settings = JSON.parse(localStorage.getItem("pk-settings")); | ||||
|  | ||||
|     let currentPage = 1; | ||||
|     let itemsPerPage = 10; | ||||
|  | ||||
|     $: indexOfLastItem = currentPage * itemsPerPage; | ||||
|     $: indexOfFirstItem = indexOfLastItem - itemsPerPage; | ||||
|     $: pageAmount = Math.ceil(members.length / itemsPerPage); | ||||
|  | ||||
|     $: orderedMembers = members.sort((a, b) => a.name.localeCompare(b.name)); | ||||
|     $: slicedMembers = orderedMembers.slice(indexOfFirstItem, indexOfLastItem); | ||||
|  | ||||
|     onMount(() => { | ||||
|         fetchGroup(); | ||||
|     }); | ||||
|  | ||||
|     async function fetchGroup() { | ||||
|         try { | ||||
|             group = await api().groups($params.id).get({auth: !isPublic}); | ||||
|             if (!isPublic && !group.privacy) throw new Error("This group does not belong to your system, did you mean to look up their public page?") | ||||
|             err = ""; | ||||
|             loading = false; | ||||
|             memberLoading = true; | ||||
|             await new Promise(resolve => setTimeout(resolve, 1000)); | ||||
|             fetchMembers(); | ||||
|         } catch (error) { | ||||
|             console.log(error); | ||||
|             err = error.message; | ||||
|             loading = false; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     async function fetchMembers() { | ||||
|         try { | ||||
|             members = await api().groups($params.id).members().get({auth: !isPublic}); | ||||
|             if (!isPublic) { | ||||
|                 await new Promise(resolve => setTimeout(resolve, 1000)); | ||||
|                 systemMembers = await api().systems("@me").members.get({ auth: true }); | ||||
|             } | ||||
|             memberErr = ""; | ||||
|             memberLoading = false; | ||||
|             // we can't use with_members from a group list from a member endpoint yet, but I'm leaving this in in case we do | ||||
|             // (this is needed for editing a group member list from the member page) | ||||
|             /* if (!isPublic) { | ||||
|                 await new Promise(resolve => setTimeout(resolve, 1000)); | ||||
|                 systemMembers = await api().systems("@me").members.get({auth: true}); | ||||
|             } */ | ||||
|         } catch (error) { | ||||
|             console.log(error); | ||||
|             memberErr = error.message; | ||||
|             memberLoading = false; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     async function updateMembers() { | ||||
|       memberLoading = true; | ||||
|       await new Promise(resolve => setTimeout(resolve, 500)); | ||||
|       fetchMembers(); | ||||
|     } | ||||
|  | ||||
|     function updateDelete() { | ||||
|         isDeleted = true; | ||||
|     } | ||||
|      | ||||
|     function updateMemberList(event: any) { | ||||
|         members = members.map(member => member.id !== event.detail.id ? member : event.detail); | ||||
|         systemMembers = systemMembers.map(member => member.id !== event.detail.id ? member : event.detail); | ||||
|     } | ||||
|  | ||||
|     function deleteMemberFromList(event: any) { | ||||
|         members = members.filter(member => member.id !== event.detail); | ||||
|         systemMembers = systemMembers.filter(member => member.id !== event.detail); | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| {#if settings && settings.appearance.color_background} | ||||
|     <div class="background" style="background-color: {group && `#${group.color}`}"></div> | ||||
| {/if} | ||||
| {#if group && group.banner && ((settings && settings.appearance.banner_top))} | ||||
| <div class="banner" style="background-image: url({group.banner})" /> | ||||
| {/if} | ||||
| <Container> | ||||
|     <Row> | ||||
|         <Col class="mx-auto" xs={12} lg={11} xl={10}> | ||||
|             {#if isDeleted} | ||||
|                 <Alert color="success">Group has been successfully deleted. <Link to="/dash">Return to dash</Link></Alert> | ||||
|             {:else} | ||||
|             {#if isPublic} | ||||
|                 <Alert color="info">You are currently <b>viewing</b> a group.</Alert> | ||||
|             {/if} | ||||
|             {#if err} | ||||
|                 <Alert color="danger">{err}</Alert> | ||||
|             {:else if loading} | ||||
|                 <Spinner/> | ||||
|             {:else if group && group.id} | ||||
|                 <Card class="mb-4"> | ||||
|                     <CardHeader> | ||||
|                         <CardsHeader bind:item={group}> | ||||
|                             <FaUsers slot="icon" /> | ||||
|                         </CardsHeader> | ||||
|                     </CardHeader> | ||||
|                     <CardBody> | ||||
|                         <Body on:deletion={updateDelete} on:updateGroups={updateMembers} bind:members={systemMembers} bind:group={group} isPage={isPage} isPublic={isPublic}/> | ||||
|                     </CardBody> | ||||
|                 </Card> | ||||
|             {/if} | ||||
|             {#if memberLoading} | ||||
|                 <Alert color="primary"><Spinner size="sm" /> Fetching members...</Alert> | ||||
|             {:else if memberErr} | ||||
|                 <Alert color="danger">{memberErr}</Alert> | ||||
|             {:else if members && members.length > 0} | ||||
|             <Card class="mb-2"> | ||||
|                 <CardHeader> | ||||
|                     <CardTitle style="margin-top: 8px; outline: none;"> | ||||
|                         <div class="icon d-inline-block"> | ||||
|                             <FaList /> | ||||
|                         </div> Member groups | ||||
|                     </CardTitle> | ||||
|                 </CardHeader> | ||||
|             </Card> | ||||
|             <ListPagination bind:currentPage bind:pageAmount /> | ||||
|             <Accordion class="mb-3" stayOpen> | ||||
|             {#each slicedMembers as member, index (member.id)} | ||||
|             {#if (!isPublic && member.privacy.visibility === "public") || isPublic} | ||||
|                 <AccordionItem> | ||||
|                     <CardsHeader bind:item={member} slot="header"> | ||||
|                         <FaUserCircle slot="icon" /> | ||||
|                     </CardsHeader> | ||||
|                     <MemberBody on:update={updateMemberList} isMainDash={isMainDash} on:deletion={deleteMemberFromList} bind:member bind:isPublic={isPublic}/> | ||||
|                 </AccordionItem> | ||||
|                 {:else} | ||||
|                 <AccordionItem> | ||||
|                     <CardsHeader bind:item={member} slot="header"> | ||||
|                         <FaLock slot="icon" /> | ||||
|                     </CardsHeader> | ||||
|                     <MemberBody on:update={updateMemberList} isMainDash={isMainDash} on:deletion={deleteMemberFromList} bind:member bind:isPublic={isPublic}/> | ||||
|                 </AccordionItem> | ||||
|                 {/if} | ||||
|             {/each} | ||||
|             </Accordion> | ||||
|             <ListPagination bind:currentPage bind:pageAmount /> | ||||
|             {/if} | ||||
|             {/if} | ||||
|         </Col> | ||||
|     </Row> | ||||
| </Container> | ||||
|  | ||||
| <style> | ||||
|     .background { | ||||
|         position: fixed; | ||||
|         top: 0; | ||||
|         left: 0; | ||||
|         width: 100%; | ||||
|         flex: 1; | ||||
|         min-height: 100%; | ||||
|         z-index: -30; | ||||
|     } | ||||
| </style> | ||||
|  | ||||
		Reference in New Issue
	
	Block a user