mirror of
https://github.com/seanmorley15/AdventureLog.git
synced 2025-07-25 15:59:38 +02:00
Allow for Sharing of Collections to other Public Users
This commit is contained in:
commit
4a293798eb
47 changed files with 1368 additions and 311 deletions
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import type { Adventure, User } from '$lib/types';
|
||||
import type { Adventure, Collection, User } from '$lib/types';
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
import Launch from '~icons/mdi/launch';
|
||||
|
@ -21,8 +21,8 @@
|
|||
import ImageDisplayModal from './ImageDisplayModal.svelte';
|
||||
|
||||
export let type: string;
|
||||
|
||||
export let user: User | null;
|
||||
export let collection: Collection | null = null;
|
||||
|
||||
let isCollectionModalOpen: boolean = false;
|
||||
let isWarningModalOpen: boolean = false;
|
||||
|
@ -161,7 +161,7 @@
|
|||
{/if}
|
||||
|
||||
<div
|
||||
class="card w-full max-w-xs sm:max-w-sm md:max-w-md lg:max-w-md xl:max-w-md bg-primary-content shadow-xl text-base-content"
|
||||
class="card w-full max-w-xs sm:max-w-sm md:max-w-md lg:max-w-md xl:max-w-md bg-neutral text-neutral-content shadow-xl"
|
||||
>
|
||||
<figure>
|
||||
{#if adventure.images && adventure.images.length > 0}
|
||||
|
@ -209,19 +209,17 @@
|
|||
</button>
|
||||
</div>
|
||||
<div>
|
||||
{#if adventure.type == 'visited' && user?.pk == adventure.user_id}
|
||||
{#if adventure.type == 'visited'}
|
||||
<div class="badge badge-primary">Visited</div>
|
||||
{:else if user?.pk == adventure.user_id && adventure.type == 'planned'}
|
||||
{:else if adventure.type == 'planned'}
|
||||
<div class="badge badge-secondary">Planned</div>
|
||||
{:else if (user?.pk !== adventure.user_id && adventure.type == 'planned') || adventure.type == 'visited'}
|
||||
<div class="badge badge-secondary">Adventure</div>
|
||||
{:else if user?.pk == adventure.user_id && adventure.type == 'lodging'}
|
||||
{:else if adventure.type == 'lodging'}
|
||||
<div class="badge badge-success">Lodging</div>
|
||||
{:else if adventure.type == 'dining'}
|
||||
<div class="badge badge-accent">Dining</div>
|
||||
{/if}
|
||||
|
||||
<div class="badge badge-neutral">{adventure.is_public ? 'Public' : 'Private'}</div>
|
||||
<div class="badge badge-secondary">{adventure.is_public ? 'Public' : 'Private'}</div>
|
||||
</div>
|
||||
{#if adventure.location && adventure.location !== ''}
|
||||
<div class="inline-flex items-center">
|
||||
|
@ -254,9 +252,9 @@
|
|||
<div class="card-actions justify-end mt-2">
|
||||
<!-- action options dropdown -->
|
||||
{#if type != 'link'}
|
||||
{#if user?.pk == adventure.user_id}
|
||||
{#if adventure.user_id == user?.pk || (collection && user && collection.shared_with.includes(user.uuid))}
|
||||
<div class="dropdown dropdown-end">
|
||||
<div tabindex="0" role="button" class="btn btn-neutral">
|
||||
<div tabindex="0" role="button" class="btn btn-neutral-200">
|
||||
<DotsHorizontal class="w-6 h-6" />
|
||||
</div>
|
||||
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||
|
@ -272,18 +270,18 @@
|
|||
<button class="btn btn-neutral mb-2" on:click={editAdventure}>
|
||||
<FileDocumentEdit class="w-6 h-6" />Edit {keyword}
|
||||
</button>
|
||||
{#if adventure.type == 'visited'}
|
||||
{#if adventure.type == 'visited' && user?.pk == adventure.user_id}
|
||||
<button class="btn btn-neutral mb-2" on:click={changeType('planned')}
|
||||
><FormatListBulletedSquare class="w-6 h-6" />Change to Plan</button
|
||||
>
|
||||
{/if}
|
||||
{#if adventure.type == 'planned'}
|
||||
{#if adventure.type == 'planned' && user?.pk == adventure.user_id}
|
||||
<button class="btn btn-neutral mb-2" on:click={changeType('visited')}
|
||||
><CheckBold class="w-6 h-6" />Mark Visited</button
|
||||
>
|
||||
{/if}
|
||||
<!-- remove from adventure -->
|
||||
{#if adventure.collection && (adventure.type == 'visited' || adventure.type == 'planned')}
|
||||
{#if adventure.collection && (adventure.type == 'visited' || adventure.type == 'planned') && user?.pk == adventure.user_id}
|
||||
<button class="btn btn-neutral mb-2" on:click={removeFromCollection}
|
||||
><LinkVariantRemove class="w-6 h-6" />Remove from Collection</button
|
||||
>
|
||||
|
@ -309,8 +307,9 @@
|
|||
</ul>
|
||||
</div>
|
||||
{:else}
|
||||
<button class="btn btn-neutral mb-2" on:click={() => goto(`/adventures/${adventure.id}`)}
|
||||
><Launch class="w-6 h-6" /></button
|
||||
<button
|
||||
class="btn btn-neutral-200 mb-2"
|
||||
on:click={() => goto(`/adventures/${adventure.id}`)}><Launch class="w-6 h-6" /></button
|
||||
>
|
||||
{/if}
|
||||
{/if}
|
||||
|
|
|
@ -627,22 +627,24 @@
|
|||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
<div>
|
||||
<div class="mt-2">
|
||||
<div>
|
||||
<label for="is_public"
|
||||
>Public <Earth class="inline-block -mt-1 mb-1 w-6 h-6" /></label
|
||||
><br />
|
||||
<input
|
||||
type="checkbox"
|
||||
class="toggle toggle-primary"
|
||||
id="is_public"
|
||||
name="is_public"
|
||||
bind:checked={adventure.is_public}
|
||||
/>
|
||||
{#if !collection_id}
|
||||
<div>
|
||||
<div class="mt-2">
|
||||
<div>
|
||||
<label for="is_public"
|
||||
>Public <Earth class="inline-block -mt-1 mb-1 w-6 h-6" /></label
|
||||
><br />
|
||||
<input
|
||||
type="checkbox"
|
||||
class="toggle toggle-primary"
|
||||
id="is_public"
|
||||
name="is_public"
|
||||
bind:checked={adventure.is_public}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
<h2 class="text-2xl font-semibold mb-2 mt-2">Location Information</h2>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
<div class="dropdown dropdown-bottom dropdown-end" tabindex="0" role="button">
|
||||
<div class="avatar placeholder">
|
||||
<div class="bg-neutral text-neutral-content rounded-full w-10 ml-4">
|
||||
<div class="bg-neutral rounded-full text-neutral-200 w-10 ml-4">
|
||||
{#if user.profile_pic}
|
||||
<img src={user.profile_pic} alt="User Profile" />
|
||||
{:else}
|
||||
|
@ -24,7 +24,7 @@
|
|||
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||
<ul
|
||||
tabindex="0"
|
||||
class="dropdown-content z-[1] menu p-2 shadow bg-primary-content mt-2 rounded-box w-52"
|
||||
class="dropdown-content z-[1] text-neutral-200 menu p-2 shadow bg-neutral mt-2 rounded-box w-52"
|
||||
>
|
||||
<!-- svelte-ignore a11y-missing-attribute -->
|
||||
<!-- svelte-ignore a11y-missing-attribute -->
|
||||
|
@ -32,6 +32,7 @@
|
|||
<li><button on:click={() => goto('/profile')}>Profile</button></li>
|
||||
<li><button on:click={() => goto('/adventures')}>My Adventures</button></li>
|
||||
<li><button on:click={() => goto('/activities')}>My Activities</button></li>
|
||||
<li><button on:click={() => goto('/shared')}>Shared With Me</button></li>
|
||||
<li><button on:click={() => goto('/settings')}>User Settings</button></li>
|
||||
<form method="post">
|
||||
<li><button formaction="/?/logout">Logout</button></li>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts">
|
||||
import { addToast } from '$lib/toasts';
|
||||
import type { Checklist, User } from '$lib/types';
|
||||
import type { Checklist, Collection, User } from '$lib/types';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
|||
|
||||
export let checklist: Checklist;
|
||||
export let user: User | null = null;
|
||||
export let collection: Collection | null = null;
|
||||
|
||||
function editChecklist() {
|
||||
dispatch('edit', checklist);
|
||||
|
@ -29,7 +30,7 @@
|
|||
</script>
|
||||
|
||||
<div
|
||||
class="card w-full max-w-xs sm:max-w-sm md:max-w-md lg:max-w-md xl:max-w-md bg-primary-content shadow-xl overflow-hidden text-base-content"
|
||||
class="card w-full max-w-xs sm:max-w-sm md:max-w-md lg:max-w-md xl:max-w-md bg-neutral text-neutral-content shadow-xl overflow-hidden"
|
||||
>
|
||||
<div class="card-body">
|
||||
<div class="flex justify-between">
|
||||
|
@ -51,10 +52,10 @@
|
|||
<!-- <button class="btn btn-neutral mb-2" on:click={() => goto(`/notes/${note.id}`)}
|
||||
><Launch class="w-6 h-6" />Open Details</button
|
||||
> -->
|
||||
<button class="btn btn-neutral mb-2" on:click={editChecklist}>
|
||||
<button class="btn btn-neutral-200 mb-2" on:click={editChecklist}>
|
||||
<Launch class="w-6 h-6" />Open
|
||||
</button>
|
||||
{#if checklist.user_id == user?.pk}
|
||||
{#if checklist.user_id == user?.pk || (collection && user && collection.shared_with.includes(user.uuid))}
|
||||
<button
|
||||
id="delete_adventure"
|
||||
data-umami-event="Delete Checklist"
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
{
|
||||
name: newItem,
|
||||
is_checked: newStatus,
|
||||
id: 0,
|
||||
id: '',
|
||||
user_id: 0,
|
||||
checklist: 0,
|
||||
created_at: '',
|
||||
|
@ -135,7 +135,7 @@
|
|||
<p class="font-semibold text-md mb-2">Editing checklist {initialName}</p>
|
||||
{/if}
|
||||
|
||||
{#if (checklist && user?.pk == checklist?.user_id) || !checklist}
|
||||
{#if (checklist && user?.pk == checklist?.user_id) || (user && collection && collection.shared_with.includes(user.uuid)) || !checklist}
|
||||
<form on:submit|preventDefault>
|
||||
<div class="form-control mb-2">
|
||||
<label for="name">Name</label>
|
||||
|
|
|
@ -16,10 +16,12 @@
|
|||
import DotsHorizontal from '~icons/mdi/dots-horizontal';
|
||||
import TrashCan from '~icons/mdi/trashcan';
|
||||
import DeleteWarning from './DeleteWarning.svelte';
|
||||
import ShareModal from './ShareModal.svelte';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let type: String | undefined | null;
|
||||
let isShareModalOpen: boolean = false;
|
||||
|
||||
// export let type: String;
|
||||
|
||||
|
@ -77,8 +79,12 @@
|
|||
/>
|
||||
{/if}
|
||||
|
||||
{#if isShareModalOpen}
|
||||
<ShareModal {collection} on:close={() => (isShareModalOpen = false)} />
|
||||
{/if}
|
||||
|
||||
<div
|
||||
class="card min-w-max lg:w-96 md:w-80 sm:w-60 xs:w-40 bg-primary-content shadow-xl text-base-content"
|
||||
class="card min-w-max lg:w-96 md:w-80 sm:w-60 xs:w-40 bg-neutral text-neutral-content shadow-xl"
|
||||
>
|
||||
<div class="card-body">
|
||||
<div class="flex justify-between">
|
||||
|
@ -90,7 +96,7 @@
|
|||
</button>
|
||||
</div>
|
||||
<div class="inline-flex gap-2 mb-2">
|
||||
<div class="badge badge-neutral">{collection.is_public ? 'Public' : 'Private'}</div>
|
||||
<div class="badge badge-secondary">{collection.is_public ? 'Public' : 'Private'}</div>
|
||||
{#if collection.is_archived}
|
||||
<div class="badge badge-warning">Archived</div>
|
||||
{/if}
|
||||
|
@ -117,7 +123,7 @@
|
|||
</button>
|
||||
{:else}
|
||||
<div class="dropdown dropdown-end">
|
||||
<div tabindex="0" role="button" class="btn btn-neutral">
|
||||
<div tabindex="0" role="button" class="btn btn-neutral-200">
|
||||
<DotsHorizontal class="w-6 h-6" />
|
||||
</div>
|
||||
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||
|
@ -125,7 +131,7 @@
|
|||
tabindex="0"
|
||||
class="dropdown-content menu bg-base-100 rounded-box z-[1] w-52 p-2 shadow"
|
||||
>
|
||||
{#if type != 'link'}
|
||||
{#if type != 'link' && type != 'viewonly'}
|
||||
<button
|
||||
class="btn btn-neutral mb-2"
|
||||
on:click={() => goto(`/collections/${collection.id}`)}
|
||||
|
@ -135,6 +141,9 @@
|
|||
<button class="btn btn-neutral mb-2" on:click={editAdventure}>
|
||||
<FileDocumentEdit class="w-6 h-6" />Edit Collection
|
||||
</button>
|
||||
<button class="btn btn-neutral mb-2" on:click={() => (isShareModalOpen = true)}>
|
||||
<FileDocumentEdit class="w-6 h-6" />Share
|
||||
</button>
|
||||
{/if}
|
||||
{#if collection.is_archived}
|
||||
<button class="btn btn-neutral mb-2" on:click={() => archiveCollection(false)}>
|
||||
|
@ -153,6 +162,13 @@
|
|||
><TrashCan class="w-6 h-6" />Delete</button
|
||||
>
|
||||
{/if}
|
||||
{#if type == 'viewonly'}
|
||||
<button
|
||||
class="btn btn-neutral mb-2"
|
||||
on:click={() => goto(`/collections/${collection.id}`)}
|
||||
><Launch class="w-5 h-5 mr-1" />Open Details</button
|
||||
>
|
||||
{/if}
|
||||
</ul>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { getFlag } from '$lib';
|
||||
import { continentCodeToString, getFlag } from '$lib';
|
||||
import type { Country } from '$lib/types';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
const dispatch = createEventDispatcher();
|
||||
|
@ -13,7 +13,7 @@
|
|||
</script>
|
||||
|
||||
<div
|
||||
class="card w-full max-w-xs sm:max-w-sm md:max-w-md lg:max-w-md xl:max-w-md bg-primary-content shadow-xl overflow-hidden text-base-content"
|
||||
class="card w-full max-w-xs sm:max-w-sm md:max-w-md lg:max-w-md xl:max-w-md bg-neutral text-neutral-content shadow-xl overflow-hidden"
|
||||
>
|
||||
<figure>
|
||||
<!-- svelte-ignore a11y-img-redundant-alt -->
|
||||
|
@ -21,6 +21,7 @@
|
|||
</figure>
|
||||
<div class="card-body">
|
||||
<h2 class="card-title overflow-ellipsis">{country.name}</h2>
|
||||
<div class="badge badge-primary">{continentCodeToString(country.continent)}</div>
|
||||
<div class="card-actions justify-end">
|
||||
<!-- <button class="btn btn-info" on:click={moreInfo}>More Info</button> -->
|
||||
<button class="btn btn-primary" on:click={nav}>Open</button>
|
||||
|
|
|
@ -11,7 +11,9 @@
|
|||
import Flower from '~icons/mdi/flower';
|
||||
import Water from '~icons/mdi/water';
|
||||
import AboutModal from './AboutModal.svelte';
|
||||
import AccountMultiple from '~icons/mdi/account-multiple';
|
||||
import Avatar from './Avatar.svelte';
|
||||
import PaletteOutline from '~icons/mdi/palette-outline';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
let query: string = '';
|
||||
|
@ -81,6 +83,9 @@
|
|||
<li>
|
||||
<button on:click={() => goto('/map')}>Map</button>
|
||||
</li>
|
||||
<li>
|
||||
<button on:click={() => goto('/users')}>Users</button>
|
||||
</li>
|
||||
{/if}
|
||||
|
||||
{#if !data.user}
|
||||
|
@ -133,6 +138,11 @@
|
|||
<li>
|
||||
<button class="btn btn-neutral" on:click={() => goto('/map')}>Map</button>
|
||||
</li>
|
||||
<li>
|
||||
<button class="btn btn-neutral" on:click={() => goto('/users')}
|
||||
><AccountMultiple /></button
|
||||
>
|
||||
</li>
|
||||
{/if}
|
||||
|
||||
{#if !data.user}
|
||||
|
@ -184,6 +194,10 @@
|
|||
on:click={() => (window.location.href = 'https://docs.adventurelog.app/')}
|
||||
>Documentation</button
|
||||
>
|
||||
<button
|
||||
class="btn btn-sm mt-2"
|
||||
on:click={() => (window.location.href = 'https://discord.gg/wRbQ9Egr8C')}>Discord</button
|
||||
>
|
||||
<p class="font-bold m-4 text-lg">Theme Selection</p>
|
||||
<form method="POST" use:enhance={submitUpdateTheme}>
|
||||
<li>
|
||||
|
@ -202,7 +216,12 @@
|
|||
</li>
|
||||
<li>
|
||||
<button formaction="/?/setTheme&theme=forest">Forest<Forest class="w-6 h-6" /></button>
|
||||
<button formaction="/?/setTheme&theme=garden">Garden<Flower class="w-6 h-6" /></button>
|
||||
<button formaction="/?/setTheme&theme=aestheticLight"
|
||||
>Aesthetic Light<PaletteOutline class="w-6 h-6" /></button
|
||||
>
|
||||
<button formaction="/?/setTheme&theme=aestheticDark"
|
||||
>Aesthetic Dark<PaletteOutline class="w-6 h-6" /></button
|
||||
>
|
||||
<button formaction="/?/setTheme&theme=aqua">Aqua<Water class="w-6 h-6" /></button>
|
||||
</li>
|
||||
</form>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { addToast } from '$lib/toasts';
|
||||
import type { Note, User } from '$lib/types';
|
||||
import type { Collection, Note, User } from '$lib/types';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
|||
|
||||
export let note: Note;
|
||||
export let user: User | null = null;
|
||||
export let collection: Collection | null = null;
|
||||
|
||||
function editNote() {
|
||||
dispatch('edit', note);
|
||||
|
@ -30,7 +31,7 @@
|
|||
</script>
|
||||
|
||||
<div
|
||||
class="card w-full max-w-xs sm:max-w-sm md:max-w-md lg:max-w-md xl:max-w-md bg-primary-content shadow-xl overflow-hidden text-base-content"
|
||||
class="card w-full max-w-xs sm:max-w-sm md:max-w-md lg:max-w-md xl:max-w-md overflow-hidden bg-neutral text-neutral-content shadow-xl"
|
||||
>
|
||||
<div class="card-body">
|
||||
<div class="flex justify-between">
|
||||
|
@ -52,10 +53,10 @@
|
|||
<!-- <button class="btn btn-neutral mb-2" on:click={() => goto(`/notes/${note.id}`)}
|
||||
><Launch class="w-6 h-6" />Open Details</button
|
||||
> -->
|
||||
<button class="btn btn-neutral mb-2" on:click={editNote}>
|
||||
<button class="btn btn-neutral-200 mb-2" on:click={editNote}>
|
||||
<Launch class="w-6 h-6" />Open
|
||||
</button>
|
||||
{#if note.user_id == user?.pk}
|
||||
{#if note.user_id == user?.pk || (collection && user && collection.shared_with.includes(user.uuid))}
|
||||
<button
|
||||
id="delete_adventure"
|
||||
data-umami-event="Delete Adventure"
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
import { createEventDispatcher } from 'svelte';
|
||||
const dispatch = createEventDispatcher();
|
||||
import { onMount } from 'svelte';
|
||||
import ShareModal from './ShareModal.svelte';
|
||||
let modal: HTMLDialogElement;
|
||||
|
||||
export let note: Note | null = null;
|
||||
|
@ -113,7 +114,7 @@
|
|||
<p class="font-semibold text-md mb-2">Editing note {initialName}</p>
|
||||
{/if}
|
||||
|
||||
{#if (note && user?.pk == note?.user_id) || !note}
|
||||
{#if (note && user?.pk == note?.user_id) || (collection && user && collection.shared_with.includes(user.uuid)) || !note}
|
||||
<form on:submit|preventDefault>
|
||||
<div class="form-control mb-2">
|
||||
<label for="name">Name</label>
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
</script>
|
||||
|
||||
<div
|
||||
class="card w-full max-w-xs sm:max-w-sm md:max-w-md lg:max-w-md xl:max-w-md bg-primary-content shadow-xl overflow-hidden text-base-content"
|
||||
class="card w-full max-w-xs sm:max-w-sm md:max-w-md lg:max-w-md xl:max-w-md bg-neutral text-neutral-content shadow-xl overflow-hidden"
|
||||
>
|
||||
<div class="card-body">
|
||||
{#if region.name_en && region.name !== region.name_en}
|
||||
|
|
118
frontend/src/lib/components/ShareModal.svelte
Normal file
118
frontend/src/lib/components/ShareModal.svelte
Normal file
|
@ -0,0 +1,118 @@
|
|||
<script lang="ts">
|
||||
import type { Collection, User } from '$lib/types';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
const dispatch = createEventDispatcher();
|
||||
import { onMount } from 'svelte';
|
||||
import UserCard from './UserCard.svelte';
|
||||
import { addToast } from '$lib/toasts';
|
||||
let modal: HTMLDialogElement;
|
||||
|
||||
export let collection: Collection;
|
||||
|
||||
let allUsers: User[] = [];
|
||||
|
||||
let sharedWithUsers: User[] = [];
|
||||
let notSharedWithUsers: User[] = [];
|
||||
|
||||
async function share(user: User) {
|
||||
let res = await fetch(`/api/collections/${collection.id}/share/${user.uuid}/`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
if (res.ok) {
|
||||
sharedWithUsers = sharedWithUsers.concat(user);
|
||||
collection.shared_with.push(user.uuid);
|
||||
notSharedWithUsers = notSharedWithUsers.filter((u) => u.uuid !== user.uuid);
|
||||
addToast('success', `Shared ${collection.name} with ${user.first_name} ${user.last_name}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function unshare(user: User) {
|
||||
let res = await fetch(`/api/collections/${collection.id}/unshare/${user.uuid}/`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
if (res.ok) {
|
||||
notSharedWithUsers = notSharedWithUsers.concat(user);
|
||||
collection.shared_with = collection.shared_with.filter((u) => u !== user.uuid);
|
||||
sharedWithUsers = sharedWithUsers.filter((u) => u.uuid !== user.uuid);
|
||||
addToast('success', `Unshared ${collection.name} with ${user.first_name} ${user.last_name}`);
|
||||
}
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
modal = document.getElementById('my_modal_1') as HTMLDialogElement;
|
||||
if (modal) {
|
||||
modal.showModal();
|
||||
}
|
||||
let res = await fetch(`/auth/users/`);
|
||||
if (res.ok) {
|
||||
let data = await res.json();
|
||||
allUsers = data;
|
||||
sharedWithUsers = allUsers.filter((user) => collection.shared_with.includes(user.uuid));
|
||||
notSharedWithUsers = allUsers.filter((user) => !collection.shared_with.includes(user.uuid));
|
||||
console.log(sharedWithUsers);
|
||||
console.log(notSharedWithUsers);
|
||||
}
|
||||
});
|
||||
|
||||
function close() {
|
||||
dispatch('close');
|
||||
}
|
||||
|
||||
function handleKeydown(event: KeyboardEvent) {
|
||||
if (event.key === 'Escape') {
|
||||
dispatch('close');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<dialog id="my_modal_1" class="modal">
|
||||
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
|
||||
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||
<div class="modal-box w-11/12 max-w-5xl" role="dialog" on:keydown={handleKeydown} tabindex="0">
|
||||
<h3 class="font-bold text-lg">Share {collection.name}</h3>
|
||||
<p class="py-1">Share this collection with other users.</p>
|
||||
<div class="divider"></div>
|
||||
<h3 class="font-bold text-md">Shared With</h3>
|
||||
<ul>
|
||||
{#each sharedWithUsers as user}
|
||||
<div class="flex flex-wrap gap-4 mr-4 justify-center content-center">
|
||||
<UserCard
|
||||
{user}
|
||||
shared_with={collection.shared_with}
|
||||
sharing={true}
|
||||
on:share={(event) => share(event.detail)}
|
||||
on:unshare={(event) => unshare(event.detail)}
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
{#if sharedWithUsers.length === 0}
|
||||
<p class="text-neutral-content">No users shared with</p>
|
||||
{/if}
|
||||
</ul>
|
||||
<div class="divider"></div>
|
||||
<h3 class="font-bold text-md">Not Shared With</h3>
|
||||
<ul>
|
||||
{#each notSharedWithUsers as user}
|
||||
<div class="flex flex-wrap gap-4 mr-4 justify-center content-center">
|
||||
<UserCard
|
||||
{user}
|
||||
shared_with={collection.shared_with}
|
||||
sharing={true}
|
||||
on:share={(event) => share(event.detail)}
|
||||
on:unshare={(event) => unshare(event.detail)}
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
{#if notSharedWithUsers.length === 0}
|
||||
<p class="text-neutral-content">No users not shared with</p>
|
||||
{/if}
|
||||
</ul>
|
||||
<button class="btn btn-primary mt-4" on:click={close}>Close</button>
|
||||
</div>
|
||||
</dialog>
|
|
@ -1,22 +1,17 @@
|
|||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
import Launch from '~icons/mdi/launch';
|
||||
import TrashCanOutline from '~icons/mdi/trash-can-outline';
|
||||
|
||||
import FileDocumentEdit from '~icons/mdi/file-document-edit';
|
||||
|
||||
import { goto } from '$app/navigation';
|
||||
import type { Collection, Transportation, User } from '$lib/types';
|
||||
import { addToast } from '$lib/toasts';
|
||||
|
||||
import Plus from '~icons/mdi/plus';
|
||||
import ArrowDownThick from '~icons/mdi/arrow-down-thick';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let transportation: Transportation;
|
||||
export let user: User | null = null;
|
||||
export let collection: Collection | null = null;
|
||||
|
||||
function editTransportation() {
|
||||
dispatch('edit', transportation);
|
||||
|
@ -40,7 +35,7 @@
|
|||
</script>
|
||||
|
||||
<div
|
||||
class="card w-full max-w-xs sm:max-w-sm md:max-w-md lg:max-w-md xl:max-w-md bg-primary-content shadow-xl text-base-content"
|
||||
class="card w-full max-w-xs sm:max-w-sm md:max-w-md lg:max-w-md xl:max-w-md bg-neutral text-neutral-content shadow-xl"
|
||||
>
|
||||
<div class="card-body">
|
||||
<h2 class="card-title overflow-ellipsis">{transportation.name}</h2>
|
||||
|
@ -64,7 +59,7 @@
|
|||
{/if}
|
||||
</div>
|
||||
|
||||
{#if user?.pk === transportation.user_id}
|
||||
{#if transportation.user_id == user?.pk || (collection && user && collection.shared_with.includes(user.uuid))}
|
||||
<div class="card-actions justify-end">
|
||||
<button on:click={deleteTransportation} class="btn btn-secondary"
|
||||
><TrashCanOutline class="w-5 h-5 mr-1" /></button
|
||||
|
|
51
frontend/src/lib/components/UserCard.svelte
Normal file
51
frontend/src/lib/components/UserCard.svelte
Normal file
|
@ -0,0 +1,51 @@
|
|||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { continentCodeToString, getFlag } from '$lib';
|
||||
import type { User } from '$lib/types';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
import Calendar from '~icons/mdi/calendar';
|
||||
|
||||
export let sharing: boolean = false;
|
||||
export let shared_with: string[] | undefined = undefined;
|
||||
|
||||
export let user: User;
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="card w-full max-w-xs sm:max-w-sm md:max-w-md lg:max-w-md xl:max-w-md bg-neutral text-neutral-content shadow-xl"
|
||||
>
|
||||
<div class="card-body">
|
||||
<div>
|
||||
{#if user.profile_pic}
|
||||
<div class="avatar">
|
||||
<div class="w-24 rounded-full">
|
||||
<img src={user.profile_pic} alt={user.username} />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<h2 class="card-title overflow-ellipsis">{user.first_name} {user.last_name}</h2>
|
||||
</div>
|
||||
<p class="text-sm text-neutral-content">{user.username}</p>
|
||||
{#if user.is_staff}
|
||||
<div class="badge badge-primary">Admin</div>
|
||||
{/if}
|
||||
<!-- member since -->
|
||||
<div class="flex items-center space-x-2">
|
||||
<Calendar class="w-4 h-4 mr-1" />
|
||||
<p class="text-sm text-neutral-content">
|
||||
{user.date_joined ? 'Joined ' + new Date(user.date_joined).toLocaleDateString() : ''}
|
||||
</p>
|
||||
</div>
|
||||
<div class="card-actions justify-end">
|
||||
{#if !sharing}
|
||||
<button class="btn btn-primary" on:click={() => goto(`/user/${user.uuid}`)}>View</button>
|
||||
{:else if shared_with && !shared_with.includes(user.uuid)}
|
||||
<button class="btn btn-primary" on:click={() => dispatch('share', user)}>Share</button>
|
||||
{:else}
|
||||
<button class="btn btn-primary" on:click={() => dispatch('unshare', user)}>Unshare</button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
Loading…
Add table
Add a link
Reference in a new issue