mirror of
https://github.com/seanmorley15/AdventureLog.git
synced 2025-07-19 21:09:37 +02:00
is visited
This commit is contained in:
parent
9d42dbac98
commit
727daf0cfd
10 changed files with 284 additions and 281 deletions
|
@ -1,3 +1,4 @@
|
|||
from django.utils import timezone
|
||||
import os
|
||||
from .models import Adventure, AdventureImage, ChecklistItem, Collection, Note, Transportation, Checklist, Visit
|
||||
from rest_framework import serializers
|
||||
|
@ -19,6 +20,7 @@ class AdventureImageSerializer(serializers.ModelSerializer):
|
|||
return representation
|
||||
|
||||
class VisitSerializer(serializers.ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = Visit
|
||||
fields = ['id', 'start_date', 'end_date', 'notes']
|
||||
|
@ -27,10 +29,19 @@ class VisitSerializer(serializers.ModelSerializer):
|
|||
class AdventureSerializer(serializers.ModelSerializer):
|
||||
images = AdventureImageSerializer(many=True, read_only=True)
|
||||
visits = VisitSerializer(many=True, read_only=False)
|
||||
is_visited = serializers.SerializerMethodField()
|
||||
class Meta:
|
||||
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']
|
||||
read_only_fields = ['id', 'created_at', 'updated_at', 'user_id']
|
||||
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', '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):
|
||||
representation = super().to_representation(instance)
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
import CollectionLink from './CollectionLink.svelte';
|
||||
import DotsHorizontal from '~icons/mdi/dots-horizontal';
|
||||
import DeleteWarning from './DeleteWarning.svelte';
|
||||
import { isAdventureVisited } from '$lib';
|
||||
import CardCarousel from './CardCarousel.svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
|
@ -132,7 +131,7 @@
|
|||
<div>
|
||||
<div class="badge badge-primary">{$t(`adventures.activities.${adventure.type}`)}</div>
|
||||
<div class="badge badge-success">
|
||||
{isAdventureVisited(adventure) ? $t('adventures.visited') : $t('adventures.planned')}
|
||||
{adventure.is_visited ? $t('adventures.visited') : $t('adventures.planned')}
|
||||
</div>
|
||||
<div class="badge badge-secondary">
|
||||
{adventure.is_public ? $t('adventures.public') : $t('adventures.private')}
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
import ActivityComplete from './ActivityComplete.svelte';
|
||||
import { appVersion } from '$lib/config';
|
||||
import { ADVENTURE_TYPES } from '$lib';
|
||||
import RegionCard from './RegionCard.svelte';
|
||||
|
||||
let wikiError: string = '';
|
||||
|
||||
|
@ -629,7 +630,11 @@ it would also work to just use on:click on the MapLibre component itself. -->
|
|||
{#if reverseGeocodePlace}
|
||||
<div class="mt-2">
|
||||
<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>
|
||||
{#if !reverseGeocodePlace.is_visited}
|
||||
<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>
|
||||
</svg>
|
||||
<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}>
|
||||
Mark as Visited
|
||||
{$t('adventures.mark_visited')}
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
@ -253,28 +253,6 @@ export let ADVENTURE_TYPES = [
|
|||
{ 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() {
|
||||
const randomIndex = Math.floor(Math.random() * randomBackgrounds.backgrounds.length);
|
||||
return randomBackgrounds.backgrounds[randomIndex] as Background;
|
||||
|
|
|
@ -37,6 +37,7 @@ export type Adventure = {
|
|||
is_public: boolean;
|
||||
created_at?: string | null;
|
||||
updated_at?: string | null;
|
||||
is_visited?: boolean;
|
||||
};
|
||||
|
||||
export type Country = {
|
||||
|
|
|
@ -139,6 +139,7 @@
|
|||
"unarchive": "Unarchive",
|
||||
"archive": "Archive",
|
||||
"no_collections_found": "No collections found to add this adventure to.",
|
||||
"not_visited": "Not Visited",
|
||||
"archived_collection_message": "Collection archived successfully!",
|
||||
"unarchived_collection_message": "Collection unarchived successfully!",
|
||||
"delete_collection_success": "Collection deleted successfully!",
|
||||
|
@ -173,6 +174,8 @@
|
|||
"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!",
|
||||
"no_adventures_found": "No adventures found",
|
||||
"mark_region_as_visited": "Mark region {region}, {country} as visited?",
|
||||
"mark_visited": "Mark Visited",
|
||||
"activities": {
|
||||
"general": "General 🌍",
|
||||
"outdoor": "Outdoor 🏞️",
|
||||
|
|
|
@ -196,7 +196,10 @@
|
|||
"no_adventures_found": "No se encontraron aventuras",
|
||||
"no_collections_found": "No se encontraron colecciones para agregar esta aventura.",
|
||||
"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": {
|
||||
"all": "Todo",
|
||||
|
|
|
@ -20,8 +20,7 @@
|
|||
groupAdventuresByDate,
|
||||
groupNotesByDate,
|
||||
groupTransportationsByDate,
|
||||
groupChecklistsByDate,
|
||||
isAdventureVisited
|
||||
groupChecklistsByDate
|
||||
} from '$lib';
|
||||
import ChecklistCard from '$lib/components/ChecklistCard.svelte';
|
||||
import ChecklistModal from '$lib/components/ChecklistModal.svelte';
|
||||
|
@ -45,7 +44,7 @@
|
|||
|
||||
$: {
|
||||
numAdventures = adventures.length;
|
||||
numVisited = adventures.filter(isAdventureVisited).length;
|
||||
numVisited = adventures.filter((adventure) => adventure.is_visited).length;
|
||||
}
|
||||
|
||||
let notFound: boolean = false;
|
||||
|
|
|
@ -46,6 +46,7 @@ export const load = (async (event) => {
|
|||
name: adventure.name,
|
||||
visits: adventure.visits,
|
||||
type: adventure.type,
|
||||
is_visited: adventure.is_visited
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<script>
|
||||
// @ts-nocheck
|
||||
|
||||
import { isAdventureVisited } from '$lib';
|
||||
import AdventureModal from '$lib/components/AdventureModal.svelte';
|
||||
import {
|
||||
DefaultMarker,
|
||||
|
@ -25,8 +24,7 @@
|
|||
let showPlanned = true;
|
||||
|
||||
$: filteredMarkers = markers.filter(
|
||||
(marker) =>
|
||||
(showVisited && isAdventureVisited(marker)) || (showPlanned && !isAdventureVisited(marker))
|
||||
(marker) => (showVisited && marker.is_visited) || (showPlanned && !marker.is_visited)
|
||||
);
|
||||
|
||||
let newMarker = [];
|
||||
|
@ -145,7 +143,7 @@
|
|||
standardControls
|
||||
>
|
||||
{#each filteredMarkers as marker}
|
||||
{#if isAdventureVisited(marker)}
|
||||
{#if marker.is_visited}
|
||||
<Marker
|
||||
lngLat={marker.lngLat}
|
||||
on:click={() => (clickedName = marker.name)}
|
||||
|
@ -205,7 +203,7 @@
|
|||
<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">
|
||||
{$t(`adventures.activities.${marker.type}`)}}
|
||||
{$t(`adventures.activities.${marker.type}`)}
|
||||
</p>
|
||||
{#if marker.visits && marker.visits.length > 0}
|
||||
<p class="text-black text-sm">
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue