1
0
Fork 0
mirror of https://github.com/seanmorley15/AdventureLog.git synced 2025-07-23 06:49:37 +02:00

feat: Add flag URL to Country type and update CountryCard component

This commit is contained in:
Sean Morley 2024-08-05 14:17:41 -04:00
parent 77c11fefea
commit d9e554ad42
14 changed files with 96 additions and 56 deletions

View file

@ -0,0 +1,18 @@
# Generated by Django 5.0.7 on 2024-08-05 17:51
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('adventures', '0018_note_links'),
]
operations = [
migrations.AddField(
model_name='collection',
name='updated_at',
field=models.DateTimeField(auto_now=True),
),
]

View file

@ -71,6 +71,8 @@ class Collection(models.Model):
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True)
start_date = models.DateField(blank=True, null=True) start_date = models.DateField(blank=True, null=True)
end_date = models.DateField(blank=True, null=True) end_date = models.DateField(blank=True, null=True)
updated_at = models.DateTimeField(auto_now=True)
# if connected adventures are private and collection is public, raise an error # if connected adventures are private and collection is public, raise an error
def clean(self): def clean(self):

View file

@ -98,4 +98,5 @@ class CollectionSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Collection model = Collection
# fields are all plus the adventures field # fields are all plus the adventures field
fields = ['id', 'description', 'user_id', 'name', 'is_public', 'adventures', 'created_at', 'start_date', 'end_date', 'transportations', 'notes'] fields = ['id', 'description', 'user_id', 'name', 'is_public', 'adventures', 'created_at', 'start_date', 'end_date', 'transportations', 'notes', 'updated_at']
read_only_fields = ['id', 'created_at', 'updated_at']

View file

@ -217,9 +217,9 @@ class CollectionViewSet(viewsets.ModelViewSet):
order_by = self.request.query_params.get('order_by', 'name') order_by = self.request.query_params.get('order_by', 'name')
order_direction = self.request.query_params.get('order_direction', 'asc') order_direction = self.request.query_params.get('order_direction', 'asc')
valid_order_by = ['name'] valid_order_by = ['name', 'upated_at']
if order_by not in valid_order_by: if order_by not in valid_order_by:
order_by = 'name' order_by = 'updated_at'
if order_direction not in ['asc', 'desc']: if order_direction not in ['asc', 'desc']:
order_direction = 'asc' order_direction = 'asc'
@ -228,13 +228,15 @@ class CollectionViewSet(viewsets.ModelViewSet):
if order_by == 'name': if order_by == 'name':
queryset = queryset.annotate(lower_name=Lower('name')) queryset = queryset.annotate(lower_name=Lower('name'))
ordering = 'lower_name' ordering = 'lower_name'
if order_direction == 'desc':
ordering = f'-{ordering}'
else: else:
ordering = order_by order_by == 'updated_at'
ordering = 'updated_at'
if order_direction == 'asc':
ordering = '-updated_at'
if order_direction == 'desc': #print(f"Ordering by: {ordering}") # For debugging
ordering = f'-{ordering}'
print(f"Ordering by: {ordering}") # For debugging
return queryset.order_by(ordering) return queryset.order_by(ordering)

View file

@ -1,10 +1,36 @@
# myapp/management/commands/seed.py # myapp/management/commands/seed.py
import os
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
import requests
from worldtravel.models import Country, Region from worldtravel.models import Country, Region
from django.db import transaction from django.db import transaction
from django.conf import settings
media_root = settings.MEDIA_ROOT
def saveCountryFlag(country_code):
flags_dir = os.path.join(media_root, 'flags')
# Check if the flags directory exists, if not, create it
if not os.path.exists(flags_dir):
os.makedirs(flags_dir)
# Check if the flag already exists in the media folder
flag_path = os.path.join(flags_dir, f'{country_code}.png')
if os.path.exists(flag_path):
print(f'Flag for {country_code} already exists')
return
res = requests.get(f'https://flagcdn.com/h240/{country_code}.png')
if res.status_code == 200:
with open(flag_path, 'wb') as f:
f.write(res.content)
print(f'Flag for {country_code} downloaded')
else:
print(f'Error downloading flag for {country_code}')
class Command(BaseCommand): class Command(BaseCommand):
help = 'Imports the world travel data' help = 'Imports the world travel data'
@ -528,8 +554,10 @@ class Command(BaseCommand):
defaults={'name': name, 'continent': continent} defaults={'name': name, 'continent': continent}
) )
if created: if created:
saveCountryFlag(country_code)
self.stdout.write(f'Inserted {name} into worldtravel countries') self.stdout.write(f'Inserted {name} into worldtravel countries')
else: else:
saveCountryFlag(country_code)
self.stdout.write(f'Updated {name} in worldtravel countries') self.stdout.write(f'Updated {name} in worldtravel countries')
def sync_regions(self, regions): def sync_regions(self, regions):

View file

@ -1,13 +1,24 @@
import os
from .models import Country, Region, VisitedRegion from .models import Country, Region, VisitedRegion
from rest_framework import serializers from rest_framework import serializers
class CountrySerializer(serializers.ModelSerializer): class CountrySerializer(serializers.ModelSerializer):
def get_public_url(self, obj):
return os.environ.get('PUBLIC_URL', 'http://127.0.0.1:8000').rstrip('/')
flag_url = serializers.SerializerMethodField()
def get_flag_url(self, obj):
public_url = self.get_public_url(obj)
return public_url + '/media/' + 'flags/' + obj.country_code + '.png'
class Meta: class Meta:
model = Country model = Country
fields = '__all__' # Serialize all fields of the Adventure model fields = '__all__' # Serialize all fields of the Adventure model
class RegionSerializer(serializers.ModelSerializer): class RegionSerializer(serializers.ModelSerializer):
flag_url = ''
class Meta: class Meta:
model = Region model = Region
fields = '__all__' # Serialize all fields of the Adventure model fields = '__all__' # Serialize all fields of the Adventure model

View file

@ -16,7 +16,7 @@ from django.contrib.staticfiles import finders
def regions_by_country(request, country_code): def regions_by_country(request, country_code):
# require authentication # require authentication
country = get_object_or_404(Country, country_code=country_code) country = get_object_or_404(Country, country_code=country_code)
regions = Region.objects.filter(country=country) regions = Region.objects.filter(country=country).order_by('name')
serializer = RegionSerializer(regions, many=True) serializer = RegionSerializer(regions, many=True)
return Response(serializer.data) return Response(serializer.data)

View file

@ -8,7 +8,6 @@ services:
- PUBLIC_SERVER_URL=http://server:8000 - PUBLIC_SERVER_URL=http://server:8000
- ORIGIN=http://localhost:8080 - ORIGIN=http://localhost:8080
- BODY_SIZE_LIMIT=Infinity - BODY_SIZE_LIMIT=Infinity
- ENABLE_ANALYTICS=false
ports: ports:
- "8080:3000" - "8080:3000"
depends_on: depends_on:

View file

@ -1,14 +1,14 @@
<script lang="ts"> <script lang="ts">
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { getFlag } from '$lib'; import { getFlag } from '$lib';
import type { Country } from '$lib/types';
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
export let countryName: string | undefined = undefined; export let country: Country;
export let countryCode: string;
async function nav() { async function nav() {
goto(`/worldtravel/${countryCode}`); goto(`/worldtravel/${country.country_code}`);
} }
</script> </script>
@ -17,15 +17,10 @@
> >
<figure> <figure>
<!-- svelte-ignore a11y-img-redundant-alt --> <!-- svelte-ignore a11y-img-redundant-alt -->
<img <img src={country.flag_url} alt="No image available" class="w-full h-48 object-cover" />
src={getFlag(240, countryCode) ||
'https://placehold.co/300?text=No%20Image%20Found&font=roboto'}
alt="No image available"
class="w-full h-48 object-cover"
/>
</figure> </figure>
<div class="card-body"> <div class="card-body">
<h2 class="card-title overflow-ellipsis">{countryName}</h2> <h2 class="card-title overflow-ellipsis">{country.name}</h2>
<div class="card-actions justify-end"> <div class="card-actions justify-end">
<!-- <button class="btn btn-info" on:click={moreInfo}>More Info</button> --> <!-- <button class="btn btn-info" on:click={moreInfo}>More Info</button> -->
<button class="btn btn-primary" on:click={nav}>Open</button> <button class="btn btn-primary" on:click={nav}>Open</button>

View file

@ -34,6 +34,7 @@ export type Country = {
name: string; name: string;
country_code: string; country_code: string;
continent: string; continent: string;
flag_url: string;
}; };
export type Region = { export type Region = {

View file

@ -249,7 +249,10 @@
{#if data.props.collection} {#if data.props.collection}
<div> <div>
<p class="text-sm text-muted-foreground">Collection</p> <p class="text-sm text-muted-foreground">Collection</p>
<p class="text-base font-medium">{data.props.collection.name}</p> <a
class="text-base font-medium link"
href="/collections/{data.props.collection.id}">{data.props.collection.name}</a
>
</div> </div>
{/if} {/if}
</div> </div>

View file

@ -17,7 +17,7 @@ export const load = (async (event) => {
let previous = null; let previous = null;
let count = 0; let count = 0;
let adventures: Adventure[] = []; let adventures: Adventure[] = [];
let initialFetch = await fetch(`${serverEndpoint}/api/collections/`, { let initialFetch = await fetch(`${serverEndpoint}/api/collections/?order_by=updated_at`, {
headers: { headers: {
Cookie: `${event.cookies.get('auth')}` Cookie: `${event.cookies.get('auth')}`
} }

View file

@ -23,8 +23,6 @@
let resultsPerPage: number = 25; let resultsPerPage: number = 25;
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;
@ -176,18 +174,18 @@
> >
{sidebarOpen ? 'Close Filters' : 'Open Filters'} {sidebarOpen ? 'Close Filters' : 'Open Filters'}
</button> </button>
{#if currentView == 'cards'}
<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 collections as collection} {#each collections as collection}
<CollectionCard <CollectionCard
type="" type=""
{collection} {collection}
on:delete={deleteCollection} on:delete={deleteCollection}
on:edit={editCollection} on:edit={editCollection}
/> />
{/each} {/each}
</div> </div>
{/if}
<div class="join flex items-center justify-center mt-4"> <div class="join flex items-center justify-center mt-4">
{#if next || previous} {#if next || previous}
<div class="join"> <div class="join">
@ -246,27 +244,9 @@
value="name" value="name"
hidden hidden
/> />
<button type="submit" class="btn btn-primary mt-4">Filter</button> <button type="submit" class="btn btn-success btn-primary mt-4">Filter</button>
</form> </form>
<div class="divider"></div> <div class="divider"></div>
<h3 class="text-center font-semibold text-lg mb-4">View</h3>
<div class="join">
<input
class="join-item btn-neutral btn"
type="radio"
name="options"
aria-label="Cards"
on:click={() => (currentView = 'cards')}
checked
/>
<input
class="join-item btn btn-neutral"
type="radio"
name="options"
aria-label="Table"
on:click={() => (currentView = 'table')}
/>
</div>
</div> </div>
</ul> </ul>
</div> </div>

View file

@ -13,7 +13,7 @@
<div class="flex flex-wrap gap-4 mr-4 ml-4 justify-center content-center"> <div class="flex flex-wrap gap-4 mr-4 ml-4 justify-center content-center">
{#each countries as country} {#each countries as country}
<CountryCard countryCode={country.country_code} countryName={country.name} /> <CountryCard {country} />
<!-- <p>Name: {item.name}, Continent: {item.continent}</p> --> <!-- <p>Name: {item.name}, Continent: {item.continent}</p> -->
{/each} {/each}
</div> </div>