1
0
Fork 0
mirror of https://github.com/seanmorley15/AdventureLog.git synced 2025-07-23 14:59:36 +02:00

is visited

This commit is contained in:
Sean Morley 2024-10-31 09:51:04 -04:00
parent 9d42dbac98
commit 727daf0cfd
10 changed files with 284 additions and 281 deletions

View file

@ -1,3 +1,4 @@
from django.utils import timezone
import os import os
from .models import Adventure, AdventureImage, ChecklistItem, Collection, Note, Transportation, Checklist, Visit from .models import Adventure, AdventureImage, ChecklistItem, Collection, Note, Transportation, Checklist, Visit
from rest_framework import serializers from rest_framework import serializers
@ -19,6 +20,7 @@ class AdventureImageSerializer(serializers.ModelSerializer):
return representation return representation
class VisitSerializer(serializers.ModelSerializer): class VisitSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Visit model = Visit
fields = ['id', 'start_date', 'end_date', 'notes'] fields = ['id', 'start_date', 'end_date', 'notes']
@ -27,10 +29,19 @@ class VisitSerializer(serializers.ModelSerializer):
class AdventureSerializer(serializers.ModelSerializer): class AdventureSerializer(serializers.ModelSerializer):
images = AdventureImageSerializer(many=True, read_only=True) images = AdventureImageSerializer(many=True, read_only=True)
visits = VisitSerializer(many=True, read_only=False) visits = VisitSerializer(many=True, read_only=False)
is_visited = serializers.SerializerMethodField()
class Meta: class Meta:
model = Adventure model = Adventure
fields = ['id', 'user_id', 'name', 'description', 'rating', 'activity_types', 'location', 'is_public', 'collection', 'created_at', 'updated_at', 'images', 'link', 'type', 'longitude', 'latitude', 'visits'] fields = ['id', 'user_id', 'name', 'description', 'rating', 'activity_types', 'location', 'is_public', 'collection', 'created_at', 'updated_at', 'images', 'link', 'type', 'longitude', 'latitude', 'visits', 'is_visited']
read_only_fields = ['id', 'created_at', 'updated_at', 'user_id'] read_only_fields = ['id', 'created_at', 'updated_at', 'user_id', 'is_visited']
def get_is_visited(self, obj):
current_date = timezone.now().date()
for visit in obj.visits.all():
if visit.start_date and visit.end_date and (visit.start_date <= current_date):
return True
return False
def to_representation(self, instance): def to_representation(self, instance):
representation = super().to_representation(instance) representation = super().to_representation(instance)

View file

@ -16,7 +16,6 @@
import CollectionLink from './CollectionLink.svelte'; import CollectionLink from './CollectionLink.svelte';
import DotsHorizontal from '~icons/mdi/dots-horizontal'; import DotsHorizontal from '~icons/mdi/dots-horizontal';
import DeleteWarning from './DeleteWarning.svelte'; import DeleteWarning from './DeleteWarning.svelte';
import { isAdventureVisited } from '$lib';
import CardCarousel from './CardCarousel.svelte'; import CardCarousel from './CardCarousel.svelte';
import { t } from 'svelte-i18n'; import { t } from 'svelte-i18n';
@ -132,7 +131,7 @@
<div> <div>
<div class="badge badge-primary">{$t(`adventures.activities.${adventure.type}`)}</div> <div class="badge badge-primary">{$t(`adventures.activities.${adventure.type}`)}</div>
<div class="badge badge-success"> <div class="badge badge-success">
{isAdventureVisited(adventure) ? $t('adventures.visited') : $t('adventures.planned')} {adventure.is_visited ? $t('adventures.visited') : $t('adventures.planned')}
</div> </div>
<div class="badge badge-secondary"> <div class="badge badge-secondary">
{adventure.is_public ? $t('adventures.public') : $t('adventures.private')} {adventure.is_public ? $t('adventures.public') : $t('adventures.private')}

View file

@ -28,6 +28,7 @@
import ActivityComplete from './ActivityComplete.svelte'; import ActivityComplete from './ActivityComplete.svelte';
import { appVersion } from '$lib/config'; import { appVersion } from '$lib/config';
import { ADVENTURE_TYPES } from '$lib'; import { ADVENTURE_TYPES } from '$lib';
import RegionCard from './RegionCard.svelte';
let wikiError: string = ''; let wikiError: string = '';
@ -629,7 +630,11 @@ it would also work to just use on:click on the MapLibre component itself. -->
{#if reverseGeocodePlace} {#if reverseGeocodePlace}
<div class="mt-2"> <div class="mt-2">
<p>{reverseGeocodePlace.region}, {reverseGeocodePlace.country}</p> <p>{reverseGeocodePlace.region}, {reverseGeocodePlace.country}</p>
<p>{reverseGeocodePlace.is_visited ? 'Visited' : 'Not Visited'}</p> <p>
{reverseGeocodePlace.is_visited
? $t('adventures.visited')
: $t('adventures.not_visited')}
</p>
</div> </div>
{#if !reverseGeocodePlace.is_visited} {#if !reverseGeocodePlace.is_visited}
<div role="alert" class="alert alert-info mt-2"> <div role="alert" class="alert alert-info mt-2">
@ -647,10 +652,15 @@ it would also work to just use on:click on the MapLibre component itself. -->
></path> ></path>
</svg> </svg>
<span <span
>Mark region {reverseGeocodePlace.region}, {reverseGeocodePlace.country} as visited?</span >{$t('adventures.mark_region_as_visited', {
values: {
region: reverseGeocodePlace.region,
country: reverseGeocodePlace.country
}
})}</span
> >
<button type="button" class="btn btn-neutral" on:click={markVisited}> <button type="button" class="btn btn-neutral" on:click={markVisited}>
Mark as Visited {$t('adventures.mark_visited')}
</button> </button>
</div> </div>
{/if} {/if}

View file

@ -253,28 +253,6 @@ export let ADVENTURE_TYPES = [
{ type: 'other', label: 'Other' } { type: 'other', label: 'Other' }
]; ];
/**
* Checks if an adventure has been visited.
*
* This function determines if the `adventure.visits` array contains at least one visit
* with a `start_date` that is before the current date.
*
* @param adventure - The adventure object to check.
* @returns `true` if the adventure has been visited, otherwise `false`.
*/
export function isAdventureVisited(adventure: Adventure) {
const currentTime = Date.now();
// Check if any visit's start_date is before the current time.
return (
adventure.visits &&
adventure.visits.some((visit) => {
const visitStartTime = new Date(visit.start_date).getTime();
return visit.start_date && visitStartTime <= currentTime;
})
);
}
export function getRandomBackground() { export function getRandomBackground() {
const randomIndex = Math.floor(Math.random() * randomBackgrounds.backgrounds.length); const randomIndex = Math.floor(Math.random() * randomBackgrounds.backgrounds.length);
return randomBackgrounds.backgrounds[randomIndex] as Background; return randomBackgrounds.backgrounds[randomIndex] as Background;

View file

@ -37,6 +37,7 @@ export type Adventure = {
is_public: boolean; is_public: boolean;
created_at?: string | null; created_at?: string | null;
updated_at?: string | null; updated_at?: string | null;
is_visited?: boolean;
}; };
export type Country = { export type Country = {

View file

@ -139,6 +139,7 @@
"unarchive": "Unarchive", "unarchive": "Unarchive",
"archive": "Archive", "archive": "Archive",
"no_collections_found": "No collections found to add this adventure to.", "no_collections_found": "No collections found to add this adventure to.",
"not_visited": "Not Visited",
"archived_collection_message": "Collection archived successfully!", "archived_collection_message": "Collection archived successfully!",
"unarchived_collection_message": "Collection unarchived successfully!", "unarchived_collection_message": "Collection unarchived successfully!",
"delete_collection_success": "Collection deleted successfully!", "delete_collection_success": "Collection deleted successfully!",
@ -173,6 +174,8 @@
"basic_information": "Basic Information", "basic_information": "Basic Information",
"adventure_not_found": "There are no adventures to display. Add some using the plus button at the bottom right or try changing filters!", "adventure_not_found": "There are no adventures to display. Add some using the plus button at the bottom right or try changing filters!",
"no_adventures_found": "No adventures found", "no_adventures_found": "No adventures found",
"mark_region_as_visited": "Mark region {region}, {country} as visited?",
"mark_visited": "Mark Visited",
"activities": { "activities": {
"general": "General 🌍", "general": "General 🌍",
"outdoor": "Outdoor 🏞️", "outdoor": "Outdoor 🏞️",

View file

@ -196,7 +196,10 @@
"no_adventures_found": "No se encontraron aventuras", "no_adventures_found": "No se encontraron aventuras",
"no_collections_found": "No se encontraron colecciones para agregar esta aventura.", "no_collections_found": "No se encontraron colecciones para agregar esta aventura.",
"my_adventures": "mis aventuras", "my_adventures": "mis aventuras",
"no_linkable_adventures": "No se encontraron aventuras que puedan vincularse a esta colección." "no_linkable_adventures": "No se encontraron aventuras que puedan vincularse a esta colección.",
"mark_region_as_visited": "¿Marcar región {region}, {country} como visitada?",
"mark_visited": "Marcos visitó",
"not_visited": "No visitado"
}, },
"worldtravel": { "worldtravel": {
"all": "Todo", "all": "Todo",

View file

@ -20,8 +20,7 @@
groupAdventuresByDate, groupAdventuresByDate,
groupNotesByDate, groupNotesByDate,
groupTransportationsByDate, groupTransportationsByDate,
groupChecklistsByDate, groupChecklistsByDate
isAdventureVisited
} from '$lib'; } from '$lib';
import ChecklistCard from '$lib/components/ChecklistCard.svelte'; import ChecklistCard from '$lib/components/ChecklistCard.svelte';
import ChecklistModal from '$lib/components/ChecklistModal.svelte'; import ChecklistModal from '$lib/components/ChecklistModal.svelte';
@ -45,7 +44,7 @@
$: { $: {
numAdventures = adventures.length; numAdventures = adventures.length;
numVisited = adventures.filter(isAdventureVisited).length; numVisited = adventures.filter((adventure) => adventure.is_visited).length;
} }
let notFound: boolean = false; let notFound: boolean = false;

View file

@ -46,6 +46,7 @@ export const load = (async (event) => {
name: adventure.name, name: adventure.name,
visits: adventure.visits, visits: adventure.visits,
type: adventure.type, type: adventure.type,
is_visited: adventure.is_visited
}; };
}); });

View file

@ -1,7 +1,6 @@
<script> <script>
// @ts-nocheck // @ts-nocheck
import { isAdventureVisited } from '$lib';
import AdventureModal from '$lib/components/AdventureModal.svelte'; import AdventureModal from '$lib/components/AdventureModal.svelte';
import { import {
DefaultMarker, DefaultMarker,
@ -25,8 +24,7 @@
let showPlanned = true; let showPlanned = true;
$: filteredMarkers = markers.filter( $: filteredMarkers = markers.filter(
(marker) => (marker) => (showVisited && marker.is_visited) || (showPlanned && !marker.is_visited)
(showVisited && isAdventureVisited(marker)) || (showPlanned && !isAdventureVisited(marker))
); );
let newMarker = []; let newMarker = [];
@ -145,7 +143,7 @@
standardControls standardControls
> >
{#each filteredMarkers as marker} {#each filteredMarkers as marker}
{#if isAdventureVisited(marker)} {#if marker.is_visited}
<Marker <Marker
lngLat={marker.lngLat} lngLat={marker.lngLat}
on:click={() => (clickedName = marker.name)} on:click={() => (clickedName = marker.name)}
@ -205,7 +203,7 @@
<div class="text-lg text-black font-bold">{marker.name}</div> <div class="text-lg text-black font-bold">{marker.name}</div>
<p class="font-semibold text-black text-md">Planned</p> <p class="font-semibold text-black text-md">Planned</p>
<p class="font-semibold text-black text-md"> <p class="font-semibold text-black text-md">
{$t(`adventures.activities.${marker.type}`)}} {$t(`adventures.activities.${marker.type}`)}
</p> </p>
{#if marker.visits && marker.visits.length > 0} {#if marker.visits && marker.visits.length > 0}
<p class="text-black text-sm"> <p class="text-black text-sm">