1
0
Fork 0
mirror of https://github.com/seanmorley15/AdventureLog.git synced 2025-08-05 05:05:17 +02:00

Merge pull request #136 from seanmorley15/development

Development
This commit is contained in:
Sean Morley 2024-07-22 10:46:44 -04:00 committed by GitHub
commit 58a40f1d09
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 171 additions and 68 deletions

View file

@ -57,15 +57,23 @@ class CustomUserAdmin(UserAdmin):
else: else:
return return
class CollectionAdmin(admin.ModelAdmin):
def adventure_count(self, obj):
return obj.adventure_set.count()
adventure_count.short_description = 'Adventure Count'
list_display = ('name', 'user_id', 'adventure_count', 'is_public')
admin.site.register(CustomUser, CustomUserAdmin) admin.site.register(CustomUser, CustomUserAdmin)
admin.site.register(Adventure, AdventureAdmin) admin.site.register(Adventure, AdventureAdmin)
admin.site.register(Collection, CollectionAdmin)
admin.site.register(Country, CountryAdmin) admin.site.register(Country, CountryAdmin)
admin.site.register(Region, RegionAdmin) admin.site.register(Region, RegionAdmin)
admin.site.register(VisitedRegion) admin.site.register(VisitedRegion)
admin.site.register(Collection)
admin.site.site_header = 'AdventureLog Admin' admin.site.site_header = 'AdventureLog Admin'
admin.site.site_title = 'AdventureLog Admin Site' admin.site.site_title = 'AdventureLog Admin Site'

View file

@ -157,8 +157,8 @@ class AdventureViewSet(viewsets.ModelViewSet):
(Q(user_id=request.user.id) | Q(is_public=True)) (Q(user_id=request.user.id) | Q(is_public=True))
) )
queryset = self.apply_sorting(queryset) queryset = self.apply_sorting(queryset)
adventures = self.paginate_and_respond(queryset, request) serializer = self.get_serializer(queryset, many=True)
return adventures return Response(serializer.data)
def paginate_and_respond(self, queryset, request): def paginate_and_respond(self, queryset, request):
paginator = self.pagination_class() paginator = self.pagination_class()

View file

@ -77,7 +77,7 @@ MIDDLEWARE = (
# For backwards compatibility for Django 1.8 # For backwards compatibility for Django 1.8
MIDDLEWARE_CLASSES = MIDDLEWARE MIDDLEWARE_CLASSES = MIDDLEWARE
ROOT_URLCONF = 'demo.urls' ROOT_URLCONF = 'main.urls'
# WSGI_APPLICATION = 'demo.wsgi.application' # WSGI_APPLICATION = 'demo.wsgi.application'

View file

@ -3,7 +3,7 @@ import os
import sys import sys
if __name__ == "__main__": if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "demo.settings") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "main.settings")
from django.core.management import execute_from_command_line from django.core.management import execute_from_command_line

View file

@ -83,6 +83,7 @@
}); });
if (res.ok) { if (res.ok) {
console.log('Adventure type changed'); console.log('Adventure type changed');
dispatch('typeChange', adventure.id);
addToast('info', 'Adventure type changed successfully!'); addToast('info', 'Adventure type changed successfully!');
adventure.type = newType; adventure.type = newType;
} else { } else {

View file

@ -83,6 +83,17 @@
<li> <li>
<button on:click={() => goto('/map')}>Map</button> <button on:click={() => goto('/map')}>Map</button>
</li> </li>
{/if}
{#if !data.user}
<li>
<button class="btn btn-primary" on:click={() => goto('/login')}>Login</button>
</li>
<li>
<button class="btn btn-primary" on:click={() => goto('/signup')}>Signup</button>
</li>
{/if}
<form class="flex gap-2"> <form class="flex gap-2">
<label class="input input-bordered flex items-center gap-2"> <label class="input input-bordered flex items-center gap-2">
<input type="text" bind:value={query} class="grow" placeholder="Search" /> <input type="text" bind:value={query} class="grow" placeholder="Search" />
@ -102,16 +113,6 @@
</label> </label>
<button on:click={searchGo} type="submit" class="btn btn-neutral">Search</button> <button on:click={searchGo} type="submit" class="btn btn-neutral">Search</button>
</form> </form>
{/if}
{#if !data.user}
<li>
<button class="btn btn-primary" on:click={() => goto('/login')}>Login</button>
</li>
<li>
<button class="btn btn-primary" on:click={() => goto('/signup')}>Signup</button>
</li>
{/if}
</ul> </ul>
</div> </div>
<a class="btn btn-ghost text-xl" href="/" <a class="btn btn-ghost text-xl" href="/"
@ -134,6 +135,17 @@
<li> <li>
<button class="btn btn-neutral" on:click={() => goto('/map')}>Map</button> <button class="btn btn-neutral" on:click={() => goto('/map')}>Map</button>
</li> </li>
{/if}
{#if !data.user}
<li>
<button class="btn btn-primary" on:click={() => goto('/login')}>Login</button>
</li>
<li>
<button class="btn btn-primary" on:click={() => goto('/signup')}>Signup</button>
</li>
{/if}
<form class="flex gap-2"> <form class="flex gap-2">
<label class="input input-bordered flex items-center gap-2"> <label class="input input-bordered flex items-center gap-2">
<input type="text" bind:value={query} class="grow" placeholder="Search" /> <input type="text" bind:value={query} class="grow" placeholder="Search" />
@ -153,16 +165,6 @@
</label> </label>
<button on:click={searchGo} type="submit" class="btn btn-neutral">Search</button> <button on:click={searchGo} type="submit" class="btn btn-neutral">Search</button>
</form> </form>
{/if}
{#if !data.user}
<li>
<button class="btn btn-primary" on:click={() => goto('/login')}>Login</button>
</li>
<li>
<button class="btn btn-primary" on:click={() => goto('/signup')}>Signup</button>
</li>
{/if}
</ul> </ul>
</div> </div>
<div class="navbar-end"> <div class="navbar-end">

View file

@ -27,7 +27,8 @@
user_id: NaN, user_id: NaN,
latitude: null, latitude: null,
longitude: null, longitude: null,
is_public: false is_public: false,
collection: null
}; };
let image: File; let image: File;

View file

@ -1,4 +1,4 @@
export let appVersion = 'Web v0.4.0'; export let appVersion = 'Web v0.4.1';
export let versionChangelog = 'https://github.com/seanmorley15/AdventureLog/releases/tag/v0.4.0'; export let versionChangelog = 'https://github.com/seanmorley15/AdventureLog/releases/tag/v0.4.1';
export let appTitle = 'AdventureLog'; export let appTitle = 'AdventureLog';
export let copyrightYear = '2024'; export let copyrightYear = '2024';

View file

@ -20,8 +20,6 @@
let resultsPerPage: number = 10; let resultsPerPage: number = 10;
let currentView: string = 'cards';
let next: string | null = data.props.next || null; let next: string | null = data.props.next || null;
let previous: string | null = data.props.previous || null; let previous: string | null = data.props.previous || null;
let count = data.props.count || 0; let count = data.props.count || 0;

View file

@ -9,13 +9,18 @@
import AdventureCard from '$lib/components/AdventureCard.svelte'; import AdventureCard from '$lib/components/AdventureCard.svelte';
import AdventureLink from '$lib/components/AdventureLink.svelte'; import AdventureLink from '$lib/components/AdventureLink.svelte';
import EditAdventure from '$lib/components/EditAdventure.svelte'; import EditAdventure from '$lib/components/EditAdventure.svelte';
import NotFound from '$lib/components/NotFound.svelte';
export let data: PageData; export let data: PageData;
let collection: Collection; let collection: Collection;
let adventures: Adventure[] = []; let adventures: Adventure[] = [];
let numVisited: number = adventures.filter((a) => a.type == 'visited').length; let numVisited: number = 0;
$: {
numVisited = adventures.filter((a) => a.type === 'visited').length;
}
let notFound: boolean = false; let notFound: boolean = false;
let isShowingCreateModal: boolean = false; let isShowingCreateModal: boolean = false;
@ -56,6 +61,19 @@
} }
} }
function changeType(event: CustomEvent<number>) {
adventures = adventures.map((adventure) => {
if (adventure.id == event.detail) {
if (adventure.type == 'visited') {
adventure.type = 'planned';
} else {
adventure.type = 'visited';
}
}
return adventure;
});
}
let adventureToEdit: Adventure; let adventureToEdit: Adventure;
let isEditModalOpen: boolean = false; let isEditModalOpen: boolean = false;
@ -157,7 +175,7 @@
<div class="flex items-center justify-center mb-4"> <div class="flex items-center justify-center mb-4">
<div class="stats shadow bg-base-300"> <div class="stats shadow bg-base-300">
<div class="stat"> <div class="stat">
<div class="stat-title">Region Stats</div> <div class="stat-title">Collection Stats</div>
<div class="stat-value">{numVisited}/{adventures.length} Visited</div> <div class="stat-value">{numVisited}/{adventures.length} Visited</div>
{#if numVisited === adventures.length} {#if numVisited === adventures.length}
<div class="stat-desc">You've completed this collection! 🎉!</div> <div class="stat-desc">You've completed this collection! 🎉!</div>
@ -169,6 +187,9 @@
</div> </div>
{/if} {/if}
<h1 class="text-center font-semibold text-2xl mt-4 mb-2">Linked Adventures</h1> <h1 class="text-center font-semibold text-2xl mt-4 mb-2">Linked Adventures</h1>
{#if adventures.length == 0}
<NotFound error={undefined} />
{/if}
<div class="flex flex-wrap gap-4 mr-4 justify-center content-center"> <div class="flex flex-wrap gap-4 mr-4 justify-center content-center">
{#each adventures as adventure} {#each adventures as adventure}
<AdventureCard <AdventureCard
@ -177,6 +198,7 @@
on:delete={deleteAdventure} on:delete={deleteAdventure}
type={adventure.type} type={adventure.type}
{adventure} {adventure}
on:typeChange={changeType}
/> />
{/each} {/each}
</div> </div>

View file

@ -25,7 +25,8 @@ export const load = (async (event) => {
.map((adventure) => { .map((adventure) => {
return { return {
lngLat: [adventure.longitude, adventure.latitude] as [number, number], lngLat: [adventure.longitude, adventure.latitude] as [number, number],
name: adventure.name name: adventure.name,
type: adventure.type
}; };
}); });
return { return {

View file

@ -1,9 +1,11 @@
<script> <script>
// @ts-nocheck // @ts-nocheck
import { DefaultMarker, MapEvents, MapLibre, Popup } from 'svelte-maplibre'; import { DefaultMarker, MapEvents, MapLibre, Popup, Marker } from 'svelte-maplibre';
export let data; export let data;
let clickedName = '';
let markers = data.props.markers; let markers = data.props.markers;
console.log(markers); console.log(markers);
</script> </script>
@ -13,14 +15,50 @@
class="relative aspect-[9/16] max-h-[70vh] w-full sm:aspect-video sm:max-h-full" class="relative aspect-[9/16] max-h-[70vh] w-full sm:aspect-video sm:max-h-full"
standardControls standardControls
> >
{#each data.props.markers as { lngLat, name }} {#each data.props.markers as { lngLat, name, type }}
<!-- Unlike the custom marker example, default markers do not have mouse events, {#if type == 'visited'}
and popups only support the default openOn="click" behavior --> <Marker
<DefaultMarker {lngLat}> {lngLat}
<Popup offset={[0, -10]}> on:click={() => (clickedName = name)}
class="grid h-8 w-8 place-items-center rounded-full border border-gray-200 bg-red-300 text-black shadow-2xl focus:outline-2 focus:outline-black"
>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="12" cy="12" r="10" stroke="red" stroke-width="2" fill="red" />
</svg>
<Popup openOn="click" offset={[0, -10]}>
<div class="text-lg font-bold">{name}</div> <div class="text-lg font-bold">{name}</div>
<p class="font-semibold text-md">Visited</p>
</Popup> </Popup>
</DefaultMarker> </Marker>
{/if}
{#if type == 'planned'}
<Marker
{lngLat}
on:click={() => (clickedName = name)}
class="grid h-8 w-8 place-items-center rounded-full border border-gray-200 bg-blue-300 text-black shadow-2xl focus:outline-2 focus:outline-black"
>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="12" cy="12" r="10" stroke="blue" stroke-width="2" fill="blue" />
</svg>
<Popup openOn="click" offset={[0, -10]}>
<div class="text-lg font-bold">{name}</div>
<p class="font-semibold text-md">Planned</p>
</Popup>
</Marker>
{/if}
{/each} {/each}
</MapLibre> </MapLibre>

View file

@ -25,10 +25,7 @@ export const load = (async (event) => {
return { return {
props: { props: {
adventures: data.results as Adventure[], adventures: data,
nextPage: data.next,
prevPage: data.previous,
total: data.count,
query query
} }
}; };

View file

@ -4,7 +4,7 @@
import type { Adventure, OpenStreetMapPlace } from '$lib/types'; import type { Adventure, OpenStreetMapPlace } from '$lib/types';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import type { PageData } from './$types'; import type { PageData } from './$types';
import { page } from '$app/stores'; import EditAdventure from '$lib/components/EditAdventure.svelte';
export let data: PageData; export let data: PageData;
@ -13,6 +13,7 @@
} }
let osmResults: OpenStreetMapPlace[] = []; let osmResults: OpenStreetMapPlace[] = [];
let adventures: Adventure[] = [];
let query: string | null = ''; let query: string | null = '';
@ -36,15 +37,44 @@
}); });
console.log(data); console.log(data);
let adventures: Adventure[] = [];
if (data.props) { if (data.props) {
adventures = data.props.adventures; adventures = data.props.adventures;
} }
let adventureToEdit: Adventure;
let isEditModalOpen: boolean = false;
let isShowingCreateModal: boolean = false;
function editAdventure(event: CustomEvent<Adventure>) {
adventureToEdit = event.detail;
isEditModalOpen = true;
}
function saveEdit(event: CustomEvent<Adventure>) {
adventures = adventures.map((adventure) => {
if (adventure.id === event.detail.id) {
return event.detail;
}
return adventure;
});
isEditModalOpen = false;
}
</script> </script>
{#if isEditModalOpen}
<EditAdventure
{adventureToEdit}
on:close={() => (isEditModalOpen = false)}
on:saveEdit={saveEdit}
/>
{/if}
{#if adventures.length === 0 && osmResults.length === 0} {#if adventures.length === 0 && osmResults.length === 0}
<NotFound error={data.error} /> <NotFound error={data.error} />
{:else} {/if}
{#if adventures.length > 0}
<h2 class="text-center font-bold text-2xl mb-4">AdventureLog Results</h2> <h2 class="text-center font-bold text-2xl mb-4">AdventureLog Results</h2>
<div class="flex flex-wrap gap-4 mr-4 justify-center content-center"> <div class="flex flex-wrap gap-4 mr-4 justify-center content-center">
{#each adventures as adventure} {#each adventures as adventure}
@ -53,10 +83,15 @@
type={adventure.type} type={adventure.type}
{adventure} {adventure}
on:delete={deleteAdventure} on:delete={deleteAdventure}
on:edit={editAdventure}
/> />
{/each} {/each}
</div> </div>
{/if}
{#if adventures.length > 0 && osmResults.length > 0}
<div class="divider"></div> <div class="divider"></div>
{/if}
{#if osmResults.length > 0}
<h2 class="text-center font-bold text-2xl mb-4">Online Results</h2> <h2 class="text-center font-bold text-2xl mb-4">Online Results</h2>
<div class="flex flex-wrap gap-4 mr-4 justify-center content-center"> <div class="flex flex-wrap gap-4 mr-4 justify-center content-center">
{#each osmResults as result} {#each osmResults as result}