Skip to content

Commit

Permalink
feat: Add Hotel model and integrate into admin panel; update related …
Browse files Browse the repository at this point in the history
…components
  • Loading branch information
seanmorley15 committed Feb 2, 2025
1 parent bdb17a3 commit 659c56f
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 17 deletions.
5 changes: 3 additions & 2 deletions backend/server/adventures/admin.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import os
from django.contrib import admin
from django.utils.html import mark_safe
from .models import Adventure, Checklist, ChecklistItem, Collection, Transportation, Note, AdventureImage, Visit, Category, Attachment
from worldtravel.models import Country, Region, VisitedRegion, City, VisitedCity
from .models import Adventure, Checklist, ChecklistItem, Collection, Transportation, Note, AdventureImage, Visit, Category, Attachment, Hotel
from worldtravel.models import Country, Region, VisitedRegion, City, VisitedCity
from allauth.account.decorators import secure_admin_login

admin.autodiscover()
Expand Down Expand Up @@ -140,6 +140,7 @@ def adventure_count(self, obj):
admin.site.register(City, CityAdmin)
admin.site.register(VisitedCity)
admin.site.register(Attachment)
admin.site.register(Hotel)

admin.site.site_header = 'AdventureLog Admin'
admin.site.site_title = 'AdventureLog Admin Site'
Expand Down
39 changes: 39 additions & 0 deletions backend/server/adventures/migrations/0022_hotel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Generated by Django 5.0.8 on 2025-02-02 15:36

import django.db.models.deletion
import uuid
from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('adventures', '0021_alter_attachment_name'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name='Hotel',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)),
('name', models.CharField(max_length=200)),
('description', models.TextField(blank=True, null=True)),
('rating', models.FloatField(blank=True, null=True)),
('link', models.URLField(blank=True, max_length=2083, null=True)),
('check_in', models.DateTimeField(blank=True, null=True)),
('check_out', models.DateTimeField(blank=True, null=True)),
('reservation_number', models.CharField(blank=True, max_length=100, null=True)),
('price', models.DecimalField(blank=True, decimal_places=2, max_digits=9, null=True)),
('latitude', models.DecimalField(blank=True, decimal_places=6, max_digits=9, null=True)),
('longitude', models.DecimalField(blank=True, decimal_places=6, max_digits=9, null=True)),
('location', models.CharField(blank=True, max_length=200, null=True)),
('is_public', models.BooleanField(default=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('collection', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='adventures.collection')),
('user_id', models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]
35 changes: 34 additions & 1 deletion backend/server/adventures/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,4 +318,37 @@ def clean(self) -> None:


def __str__(self):
return self.name + ' - ' + self.display_name + ' - ' + self.icon
return self.name + ' - ' + self.display_name + ' - ' + self.icon

class Hotel(models.Model):
id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True, primary_key=True)
user_id = models.ForeignKey(
User, on_delete=models.CASCADE, default=default_user_id)
name = models.CharField(max_length=200)
description = models.TextField(blank=True, null=True)
rating = models.FloatField(blank=True, null=True)
link = models.URLField(blank=True, null=True, max_length=2083)
check_in = models.DateTimeField(blank=True, null=True)
check_out = models.DateTimeField(blank=True, null=True)
reservation_number = models.CharField(max_length=100, blank=True, null=True)
price = models.DecimalField(max_digits=9, decimal_places=2, blank=True, null=True)
latitude = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True)
longitude = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True)
location = models.CharField(max_length=200, blank=True, null=True)
is_public = models.BooleanField(default=False)
collection = models.ForeignKey('Collection', on_delete=models.CASCADE, blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

def clean(self):
if self.date and self.end_date and self.date > self.end_date:
raise ValidationError('The start date must be before the end date. Start date: ' + str(self.date) + ' End date: ' + str(self.end_date))

if self.collection:
if self.collection.is_public and not self.is_public:
raise ValidationError('Hotels associated with a public collection must be public. Collection: ' + self.collection.name + ' Hotel: ' + self.name)
if self.user_id != self.collection.user_id:
raise ValidationError('Hotels must be associated with collections owned by the same user. Collection owner: ' + self.collection.user_id.username + ' Hotel owner: ' + self.user_id.username)

def __str__(self):
return self.name
29 changes: 17 additions & 12 deletions frontend/src/lib/components/ImmichSelect.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,22 @@
let loading = false;
export let adventure: Adventure | null = null;
const dispatch = createEventDispatcher();
let albums: ImmichAlbum[] = [];
let currentAlbum: string = '';
let selectedDate: string = (adventure as Adventure | null)?.visits.map(v => new Date(v.end_date || v.start_date)).sort((a,b) => +b - +a)[0]?.toISOString()?.split('T')[0] || '';
let selectedDate: string =
(adventure as Adventure | null)?.visits
.map((v) => new Date(v.end_date || v.start_date))
.sort((a, b) => +b - +a)[0]
?.toISOString()
?.split('T')[0] || '';
if (!selectedDate) {
selectedDate = new Date().toISOString().split('T')[0];
}
$: {
if (currentAlbum) {
immichImages = [];
Expand All @@ -33,7 +37,7 @@
searchImmich();
}
}
async function loadMoreImmich() {
// The next URL returned by our API is a absolute url to API, but we need to use the relative path, to use the frontend api proxy.
const url = new URL(immichNextURL);
Expand Down Expand Up @@ -70,7 +74,7 @@
}
}
async function fetchAlbumAssets(album_id: string,) {
async function fetchAlbumAssets(album_id: string) {
return fetchAssets(`/api/integrations/immich/albums/${album_id}`);
}
Expand All @@ -82,14 +86,13 @@
}
});
function buildQueryParams() {
let params = new URLSearchParams();
if (immichSearchValue && searchCategory === 'search') {
params.append('query', immichSearchValue);
} else if (selectedDate && searchCategory === 'date') {
params.append('date', selectedDate);
}
}
return params.toString();
}
Expand All @@ -98,9 +101,9 @@
}, 500); // Debounce the search function to avoid multiple requests on every key press
async function _searchImmich() {
immichImages = [];
return fetchAssets(`/api/integrations/immich/search/?${buildQueryParams()}`);
}
</script>

<div class="mb-4">
Expand Down Expand Up @@ -164,9 +167,11 @@
<p class="text-red-500">{immichError}</p>
<div class="flex flex-wrap gap-4 mr-4 mt-2">
{#if loading}
<div class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-[100] w-24 h-24">
<span class="loading loading-spinner w-24 h-24"></span>
</div>
<div
class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-[100] w-24 h-24"
>
<span class="loading loading-spinner w-24 h-24"></span>
</div>
{/if}

{#each immichImages as image}
Expand All @@ -178,7 +183,7 @@
class="h-24 w-24 object-cover rounded-md"
/>
<h4>
{image.fileCreatedAt?.split('T')[0] || "Unknown"}
{image.fileCreatedAt?.split('T')[0] || 'Unknown'}
</h4>
<button
type="button"
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/lib/components/UserCard.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
<!-- Card Actions -->
<div class="card-actions justify-center mt-6">
{#if !sharing}
<button class="btn btn-primary" on:click={() => goto(`/user/${user.uuid}`)}>
<button class="btn btn-primary" on:click={() => goto(`/profile/${user.username}`)}>
View Profile
</button>
{:else if shared_with && !shared_with.includes(user.uuid)}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export type Adventure = {
is_visited?: boolean;
category: Category | null;
attachments: Attachment[];
user: User
user?: User | null;
};

export type Country = {
Expand Down

0 comments on commit 659c56f

Please sign in to comment.