From 09f8cd4a8c13db5ae486a50860aa7fbd6e6fe9e4 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Mon, 22 Dec 2025 11:54:34 -0500 Subject: [PATCH] feat: add itinerary removal functionality to various cards and update UI components - Implemented `removeFromItinerary` function in `LodgingCard`, `NoteCard`, and `TransportationCard` to allow users to remove items from their itinerary. - Replaced the trash icon with a calendar remove icon in `LocationCard`, `LodgingCard`, `NoteCard`, and `TransportationCard` for better visual representation. - Updated the dropdown menus in `LodgingCard`, `NoteCard`, and `TransportationCard` to include the new remove from itinerary option. - Enhanced `CollectionItineraryPlanner` to pass itinerary items to the respective cards. - Removed `PointSelectionModal.svelte` as it is no longer needed. - Refactored `LocationMedia.svelte` to integrate `ImageManagement` component and clean up unused code related to image handling. --- .../server/adventures/views/itinerary_view.py | 22 +- documentation/.vitepress/config.mts | 4 + .../src/lib/components/ChecklistCard.svelte | 40 +- .../src/lib/components/ImageManagement.svelte | 492 ++++++++++++++++++ .../src/lib/components/ImmichSelect.svelte | 284 +++++----- .../lib/components/ItineraryLinkModal.svelte | 70 +-- .../src/lib/components/LocationCard.svelte | 3 +- .../src/lib/components/LodgingCard.svelte | 40 +- frontend/src/lib/components/NoteCard.svelte | 40 +- .../lib/components/PointSelectionModal.svelte | 157 ------ .../lib/components/TransportationCard.svelte | 40 +- .../CollectionItineraryPlanner.svelte | 34 +- .../components/locations/LocationMedia.svelte | 470 +---------------- 13 files changed, 855 insertions(+), 841 deletions(-) create mode 100644 frontend/src/lib/components/ImageManagement.svelte delete mode 100644 frontend/src/lib/components/PointSelectionModal.svelte diff --git a/backend/server/adventures/views/itinerary_view.py b/backend/server/adventures/views/itinerary_view.py index 1004b69c..28f8eb43 100644 --- a/backend/server/adventures/views/itinerary_view.py +++ b/backend/server/adventures/views/itinerary_view.py @@ -1,7 +1,8 @@ -from adventures.models import Location, Collection, CollectionItineraryItem, Transportation, Note, Lodging, Visit +from adventures.models import Location, Collection, CollectionItineraryItem, Transportation, Note, Lodging, Visit, Checklist, Note import datetime from django.utils.dateparse import parse_date, parse_datetime from django.contrib.contenttypes.models import ContentType +from django.db import models from adventures.serializers import CollectionItineraryItemSerializer from adventures.utils.itinerary import reorder_itinerary_items from adventures.utils.autogenerate_itinerary import auto_generate_itinerary @@ -66,6 +67,8 @@ class ItineraryViewSet(viewsets.ModelViewSet): 'note': Note, 'lodging': Lodging, 'visit': Visit, + 'checklist': Checklist, + 'note': Note, } if content_type_val not in content_map: @@ -171,6 +174,23 @@ class ItineraryViewSet(viewsets.ModelViewSet): setattr(content_object, date_field, clean_date) content_object.save(update_fields=[date_field]) + # Ensure order is unique for this collection+date combination + collection_id = data.get('collection') + item_date = data.get('date') + item_order = data.get('order', 0) + + if collection_id and item_date: + # Find the maximum order for this collection+date + existing_max = CollectionItineraryItem.objects.filter( + collection_id=collection_id, + date=item_date + ).aggregate(max_order=models.Max('order'))['max_order'] + + # Check if the requested order conflicts with existing items + if existing_max is not None and item_order <= existing_max: + # Assign next available order + data['order'] = existing_max + 1 + # Proceed with normal serializer flow using modified data serializer = self.get_serializer(data=data) serializer.is_valid(raise_exception=True) diff --git a/documentation/.vitepress/config.mts b/documentation/.vitepress/config.mts index 3eea14eb..f0441c8d 100644 --- a/documentation/.vitepress/config.mts +++ b/documentation/.vitepress/config.mts @@ -203,6 +203,10 @@ export default defineConfig({ text: "Authelia", link: "https://www.authelia.com/integration/openid-connect/adventure-log/", }, + { + text: "Pocket ID", + link: "/docs/configuration/social_auth/pocket_id", + }, { text: "Open ID Connect", link: "/docs/configuration/social_auth/oidc", diff --git a/frontend/src/lib/components/ChecklistCard.svelte b/frontend/src/lib/components/ChecklistCard.svelte index d6073db5..469d52c0 100644 --- a/frontend/src/lib/components/ChecklistCard.svelte +++ b/frontend/src/lib/components/ChecklistCard.svelte @@ -13,11 +13,14 @@ import FileDocumentEdit from '~icons/mdi/file-document-edit'; import CheckCircle from '~icons/mdi/check-circle'; import CheckboxBlankCircleOutline from '~icons/mdi/checkbox-blank-circle-outline'; + import CalendarRemove from '~icons/mdi/calendar-remove'; + import type { CollectionItineraryItem } from '$lib/types'; export let checklist: Checklist; export let user: User | null = null; export let collection: Collection; export let readOnly: boolean = false; + export let itineraryItem: CollectionItineraryItem | null = null; let isWarningModalOpen: boolean = false; @@ -37,6 +40,19 @@ addToast($t('checklist.checklist_delete_error'), 'error'); } } + + async function removeFromItinerary() { + let itineraryItemId = itineraryItem?.id; + let res = await fetch(`/api/itineraries/${itineraryItemId}`, { + method: 'DELETE' + }); + if (res.ok) { + addToast('info', $t('itinerary.item_remove_success')); + dispatch('removeFromItinerary', itineraryItem); + } else { + addToast('error', $t('itinerary.item_remove_error')); + } + } {#if isWarningModalOpen} @@ -64,14 +80,12 @@ {#if !readOnly && (checklist.user == user?.uuid || (collection && user && collection.shared_with?.includes(user.uuid)))} - + {/if} diff --git a/frontend/src/lib/components/ImageManagement.svelte b/frontend/src/lib/components/ImageManagement.svelte new file mode 100644 index 00000000..65a3c193 --- /dev/null +++ b/frontend/src/lib/components/ImageManagement.svelte @@ -0,0 +1,492 @@ + + +
+
+
+
+ +
+

{$t('adventures.image_management')}

+
+ + +
+ +
+

+ {$t('adventures.upload_from_device')} +

+ +
+ + +
+

+ {$t('adventures.upload_from_url')} +

+
+ + +
+ {#if imageError} +
+ {imageError} +
+ {/if} +
+ + +
+

+ {$t('adventures.wikipedia')} +

+
+ + +
+ {#if wikiImageError} +
+ {wikiImageError} +
+ {/if} + + + {#if wikiImageResults.length > 0} +
+
+ + {$t('adventures.wiki_results_found', { + values: { count: wikiImageResults.length, query: imageSearch } + })} + + +
+
+ {#each wikiImageResults as result (result.source)} + + {/each} +
+
+ {/if} +
+ + + {#if immichIntegration} +
+

+ {$t('immich.immich')} +

+ { + url = e.detail; + handleUrlUpload(); + }} + on:remoteImmichSaved={handleImmichImageSaved} + /> +
+ {/if} +
+ + + {#if images.length > 0} +
Current Images
+
+ {#each images as image (image.id)} +
+
+ Uploaded content +
+ + + +
+ {#if !image.is_primary} + + {/if} + + +
+ + + {#if image.is_primary} +
+ +
+ {/if} +
+ {/each} +
+ {:else} +
+
{$t('adventures.no_images_uploaded_yet')}
+
+ {$t('adventures.upload_first_image')} +
+
+ {/if} +
+
diff --git a/frontend/src/lib/components/ImmichSelect.svelte b/frontend/src/lib/components/ImmichSelect.svelte index 73150064..3ff2a86c 100644 --- a/frontend/src/lib/components/ImmichSelect.svelte +++ b/frontend/src/lib/components/ImmichSelect.svelte @@ -1,8 +1,8 @@ -
- -
-

- {$t('immich.immich')} -

- Immich Logo -
+ +
+ + + +
- -
+ +{#if searchCategory === 'search'} +
+ - -
- - -
- {#if searchCategory === 'search'} -
- - -
- {:else if searchCategory === 'date'} -
- - -
- {:else if searchCategory === 'album'} -
- - -
- {/if} +{:else if searchCategory === 'date'} +
+
+{:else if searchCategory === 'album'} + +{/if} - - {#if immichError} -
-
+ {immichError} +
+{/if} + + +{#if immichImages.length > 0} +
+
+ + {immichImages.length} + {immichImages.length === 1 ? 'image' : 'images'} found + +
- {/if} - - -
- - {#if loading} -
-
- - {$t('immich.loading')} -
-
- {/if} - - - {#if immichImages.length > 0} -
- {#each immichImages as image (image.id)} +
+ {#each immichImages as image (image.id)} + +
+ + {$t('adventures.select')}
- {/each} -
- {:else if !loading && searchCategory !== 'search'} -
-
{$t('immich.no_images')}
-
- {#if searchCategory === 'date'} - {$t('immich.try_different_date')} - {:else if searchCategory === 'album'} - {$t('immich.select_album_first')} - {/if} -
-
- {/if} + + {/each} +
- {#if immichNextURL && !loading} -
-
{/if}
-
+{/if} diff --git a/frontend/src/lib/components/ItineraryLinkModal.svelte b/frontend/src/lib/components/ItineraryLinkModal.svelte index 424a5f90..139c549a 100644 --- a/frontend/src/lib/components/ItineraryLinkModal.svelte +++ b/frontend/src/lib/components/ItineraryLinkModal.svelte @@ -114,7 +114,9 @@ scheduledOnThisDay.push({ type: 'note', item: note, dates }); else scheduledOtherDays.push({ type: 'note', item: note, dates }); } else { - otherDays.push({ type: 'note', item: note }); + const itemDate = note.date ? note.date.split('T')[0] : null; + if (itemDate === targetDate) onThisDay.push({ type: 'note', item: note }); + else otherDays.push({ type: 'note', item: note }); } }); @@ -126,7 +128,9 @@ scheduledOnThisDay.push({ type: 'checklist', item: checklist, dates }); else scheduledOtherDays.push({ type: 'checklist', item: checklist, dates }); } else { - otherDays.push({ type: 'checklist', item: checklist }); + const itemDate = checklist.date ? checklist.date.split('T')[0] : null; + if (itemDate === targetDate) onThisDay.push({ type: 'checklist', item: checklist }); + else otherDays.push({ type: 'checklist', item: checklist }); } }); @@ -171,14 +175,14 @@ >
-
- +
+
-

+

Link Items to {displayDate}

-

+

{groupedItems.scheduledOnThisDay.length + groupedItems.onThisDay.length + groupedItems.scheduledOtherDays.length + @@ -195,18 +199,18 @@

-
+
{#if groupedItems.onThisDay.length > 0} -
+

Items on this day

-
+
{#each groupedItems.onThisDay as { type, item }}
-
+
{#if type === 'location'}
@@ -348,16 +352,16 @@ {#if groupedItems.otherDays.length > 0} -
+

Items on other days

These items have different dates. You can add them and optionally update their date to match.

-
+
{#each groupedItems.otherDays as { type, item }}
-
+
{#if type === 'location'}
diff --git a/frontend/src/lib/components/LodgingCard.svelte b/frontend/src/lib/components/LodgingCard.svelte index 3e61c952..51b2a151 100644 --- a/frontend/src/lib/components/LodgingCard.svelte +++ b/frontend/src/lib/components/LodgingCard.svelte @@ -17,6 +17,8 @@ import StarOutline from '~icons/mdi/star-outline'; import MapMarker from '~icons/mdi/map-marker'; import DotsHorizontal from '~icons/mdi/dots-horizontal'; + import CalendarRemove from '~icons/mdi/calendar-remove'; + import type { CollectionItineraryItem } from '$lib/types'; const dispatch = createEventDispatcher(); @@ -40,6 +42,7 @@ export let user: User | null = null; export let collection: Collection | null = null; export let readOnly: boolean = false; + export let itineraryItem: CollectionItineraryItem | null = null; let isWarningModalOpen: boolean = false; @@ -62,6 +65,19 @@ dispatch('delete', lodging.id); } } + + async function removeFromItinerary() { + let itineraryItemId = itineraryItem?.id; + let res = await fetch(`/api/itineraries/${itineraryItemId}`, { + method: 'DELETE' + }); + if (res.ok) { + addToast('info', $t('itinerary.item_remove_success')); + dispatch('removeFromItinerary', itineraryItem); + } else { + addToast('error', $t('itinerary.item_remove_error')); + } + } {#if isWarningModalOpen} @@ -119,14 +135,12 @@

{lodging.name}

{#if !readOnly && (lodging.user == user?.uuid || (collection && user && collection.shared_with?.includes(user.uuid)))} - + {/if}
diff --git a/frontend/src/lib/components/NoteCard.svelte b/frontend/src/lib/components/NoteCard.svelte index 19c27ffa..0b497fac 100644 --- a/frontend/src/lib/components/NoteCard.svelte +++ b/frontend/src/lib/components/NoteCard.svelte @@ -17,11 +17,14 @@ import DotsHorizontal from '~icons/mdi/dots-horizontal'; import FileDocumentEdit from '~icons/mdi/file-document-edit'; import LinkVariant from '~icons/mdi/link-variant'; + import CalendarRemove from '~icons/mdi/calendar-remove'; + import type { CollectionItineraryItem } from '$lib/types'; export let note: Note; export let user: User | null = null; export let collection: Collection | null = null; export let readOnly: boolean = false; + export let itineraryItem: CollectionItineraryItem | null = null; let isWarningModalOpen: boolean = false; @@ -41,6 +44,19 @@ addToast($t('notes.note_delete_error'), 'error'); } } + + async function removeFromItinerary() { + let itineraryItemId = itineraryItem?.id; + let res = await fetch(`/api/itineraries/${itineraryItemId}`, { + method: 'DELETE' + }); + if (res.ok) { + addToast('info', $t('itinerary.item_remove_success')); + dispatch('removeFromItinerary', itineraryItem); + } else { + addToast('error', $t('itinerary.item_remove_error')); + } + } {#if isWarningModalOpen} @@ -69,14 +85,12 @@
{#if !readOnly && (note.user == user?.uuid || (collection && user && collection.shared_with?.includes(user.uuid)))} - + {/if}
diff --git a/frontend/src/lib/components/PointSelectionModal.svelte b/frontend/src/lib/components/PointSelectionModal.svelte deleted file mode 100644 index b1bdcc8a..00000000 --- a/frontend/src/lib/components/PointSelectionModal.svelte +++ /dev/null @@ -1,157 +0,0 @@ - - - - - - - diff --git a/frontend/src/lib/components/TransportationCard.svelte b/frontend/src/lib/components/TransportationCard.svelte index d8e59ec9..76882ed0 100644 --- a/frontend/src/lib/components/TransportationCard.svelte +++ b/frontend/src/lib/components/TransportationCard.svelte @@ -17,6 +17,8 @@ import Star from '~icons/mdi/star'; import StarOutline from '~icons/mdi/star-outline'; import DotsHorizontal from '~icons/mdi/dots-horizontal'; + import CalendarRemove from '~icons/mdi/calendar-remove'; + import type { CollectionItineraryItem } from '$lib/types'; function getTransportationIcon(type: string) { if (type in TRANSPORTATION_TYPES_ICONS) { @@ -40,6 +42,7 @@ export let user: User | null = null; export let collection: Collection | null = null; export let readOnly: boolean = false; + export let itineraryItem: CollectionItineraryItem | null = null; const toMiles = (km: any) => (Number(km) * 0.621371).toFixed(1); @@ -64,6 +67,19 @@ dispatch('delete', transportation.id); } } + + async function removeFromItinerary() { + let itineraryItemId = itineraryItem?.id; + let res = await fetch(`/api/itineraries/${itineraryItemId}`, { + method: 'DELETE' + }); + if (res.ok) { + addToast('info', $t('itinerary.item_remove_success')); + dispatch('removeFromItinerary', itineraryItem); + } else { + addToast('error', $t('itinerary.item_remove_error')); + } + } {#if isWarningModalOpen} @@ -126,14 +142,12 @@

{transportation.name}

{#if !readOnly && (transportation.user === user?.uuid || (collection && user && collection.shared_with?.includes(user.uuid)))} - + {/if}
diff --git a/frontend/src/lib/components/locations/CollectionItineraryPlanner.svelte b/frontend/src/lib/components/locations/CollectionItineraryPlanner.svelte index f1c32cc1..71695f59 100644 --- a/frontend/src/lib/components/locations/CollectionItineraryPlanner.svelte +++ b/frontend/src/lib/components/locations/CollectionItineraryPlanner.svelte @@ -824,7 +824,7 @@
{#if resolvedObj} @@ -906,15 +906,39 @@ compact={true} /> {:else if objectType === 'transportation'} - + {:else if objectType === 'lodging'} - + {:else if objectType === 'note'} - + {:else if objectType === 'checklist'} - + {/if}
{:else} diff --git a/frontend/src/lib/components/locations/LocationMedia.svelte b/frontend/src/lib/components/locations/LocationMedia.svelte index 346e3ba2..acb05799 100644 --- a/frontend/src/lib/components/locations/LocationMedia.svelte +++ b/frontend/src/lib/components/locations/LocationMedia.svelte @@ -5,17 +5,15 @@ import { deserialize } from '$app/forms'; // Icons - import Star from '~icons/mdi/star'; - import Crown from '~icons/mdi/crown'; import SaveIcon from '~icons/mdi/content-save'; import ArrowLeftIcon from '~icons/mdi/arrow-left'; import TrashIcon from '~icons/mdi/delete'; import EditIcon from '~icons/mdi/pencil'; import FileIcon from '~icons/mdi/file-document'; + import AttachmentIcon from '~icons/mdi/attachment'; import CheckIcon from '~icons/mdi/check'; import CloseIcon from '~icons/mdi/close'; - import ImageIcon from '~icons/mdi/image'; - import AttachmentIcon from '~icons/mdi/attachment'; + import Star from '~icons/mdi/star'; import SwapHorizontalVariantIcon from '~icons/mdi/swap-horizontal-variant'; import LinkIcon from '~icons/mdi/link'; import PlusIcon from '~icons/mdi/plus'; @@ -27,7 +25,7 @@ import Camera from '~icons/mdi/camera'; import { addToast } from '$lib/toasts'; - import ImmichSelect from '../ImmichSelect.svelte'; + import ImageManagement from '../ImageManagement.svelte'; import WandererCard from '../WandererCard.svelte'; // Props @@ -39,16 +37,10 @@ export let measurementSystem: 'metric' | 'imperial' = 'metric'; export let userIsOwner: boolean = false; // Component state - let fileInput: HTMLInputElement; let attachmentFileInput: HTMLInputElement; - let url: string = ''; - let imageSearch: string = ''; - let imageError: string = ''; - let wikiImageError: string = ''; let attachmentError: string = ''; let immichIntegration: boolean = false; let copyImmichLocally: boolean = false; - let isLoading: boolean = false; let isAttachmentLoading: boolean = false; // Attachment state @@ -75,15 +67,6 @@ let wandererFetchedTrails: WandererTrail[] = []; - // Wikipedia image selection - let wikiImageResults: Array<{ - source: string; - width: number; - height: number; - title: string; - type: string; - }> = []; - // Allowed file types for attachments const allowedFileTypes = [ '.gpx', @@ -103,23 +86,6 @@ const dispatch = createEventDispatcher(); // Helper functions - function createImageFromData(data: { - id: string; - image: string; - immich_id?: string | null; - }): ContentImage { - return { - id: data.id, - image: data.image, - is_primary: false, - immich_id: data.immich_id || null - }; - } - - function updateImagesList(newImage: ContentImage) { - images = [...images, newImage]; - } - function updateAttachmentsList(newAttachment: Attachment) { attachments = [...attachments, newAttachment]; } @@ -128,191 +94,6 @@ trails = [...trails, newTrail]; } - // API calls - async function uploadImageToServer(file: File) { - const formData = new FormData(); - formData.append('image', file); - formData.append('object_id', itemId); - formData.append('content_type', 'location'); - - try { - const res = await fetch(`/locations?/image`, { - method: 'POST', - body: formData - }); - - if (res.ok) { - const newData = deserialize(await res.text()) as { data: { id: string; image: string } }; - return createImageFromData(newData.data); - } else { - throw new Error('Upload failed'); - } - } catch (error) { - console.error('Upload error:', error); - return null; - } - } - - async function fetchImageFromUrl(imageUrl: string): Promise { - try { - const res = await fetch(imageUrl); - if (!res.ok) throw new Error('Failed to fetch image'); - return await res.blob(); - } catch (error) { - console.error('Fetch error:', error); - return null; - } - } - - // Image event handlers - async function handleMultipleFiles(event: Event) { - const files = (event.target as HTMLInputElement).files; - if (!files) return; - - isLoading = true; - imageError = ''; - - try { - for (const file of files) { - const newImage = await uploadImageToServer(file); - if (newImage) { - updateImagesList(newImage); - } - } - addToast('success', $t('adventures.image_upload_success')); - } catch (error) { - addToast('error', $t('adventures.image_upload_error')); - imageError = $t('adventures.image_upload_error'); - } finally { - isLoading = false; - if (fileInput) fileInput.value = ''; - } - } - - async function handleUrlUpload() { - if (!url.trim()) return; - - isLoading = true; - imageError = ''; - - try { - const blob = await fetchImageFromUrl(url); - if (!blob) { - imageError = $t('adventures.no_image_url'); - return; - } - - const file = new File([blob], 'image.jpg', { type: 'image/jpeg' }); - const newImage = await uploadImageToServer(file); - - if (newImage) { - updateImagesList(newImage); - addToast('success', $t('adventures.image_upload_success')); - url = ''; - } else { - throw new Error('Upload failed'); - } - } catch (error) { - imageError = $t('adventures.image_fetch_failed'); - addToast('error', $t('adventures.image_upload_error')); - } finally { - isLoading = false; - } - } - - async function handleWikiImageSearch() { - if (!imageSearch.trim()) return; - - isLoading = true; - wikiImageError = ''; - - try { - const res = await fetch(`/api/generate/img/?name=${encodeURIComponent(imageSearch)}`); - const data = await res.json(); - - if (!res.ok || !data.images || data.images.length === 0) { - wikiImageError = $t('adventures.image_fetch_failed'); - return; - } - - // Store results to display inline - wikiImageResults = data.images; - } catch (error) { - wikiImageError = $t('adventures.wiki_image_error'); - addToast('error', $t('adventures.image_upload_error')); - } finally { - isLoading = false; - } - } - - async function selectWikiImage(imageUrl: string) { - isLoading = true; - - try { - const blob = await fetchImageFromUrl(imageUrl); - if (!blob) { - wikiImageError = $t('adventures.image_fetch_failed'); - isLoading = false; - return; - } - - const file = new File([blob], `${imageSearch}.jpg`, { type: 'image/jpeg' }); - const newImage = await uploadImageToServer(file); - - if (newImage) { - updateImagesList(newImage); - addToast('success', $t('adventures.image_upload_success')); - // Keep results open to allow adding multiple images - } else { - throw new Error('Upload failed'); - } - } catch (error) { - wikiImageError = $t('adventures.wiki_image_error'); - addToast('error', $t('adventures.image_upload_error')); - } finally { - isLoading = false; - } - } - - async function makePrimaryImage(imageId: string) { - try { - const res = await fetch(`/api/images/${imageId}/toggle_primary`, { - method: 'POST' - }); - - if (res.ok) { - images = images.map((image) => ({ - ...image, - is_primary: image.id === imageId - })); - addToast('success', 'Primary image updated'); - } else { - throw new Error('Failed to update primary image'); - } - } catch (error) { - console.error('Error in makePrimaryImage:', error); - addToast('error', 'Failed to update primary image'); - } - } - - async function removeImage(imageId: string) { - try { - const res = await fetch(`/api/images/${imageId}/image_delete`, { - method: 'POST' - }); - - if (res.status === 204) { - images = images.filter((image) => image.id !== imageId); - addToast('success', 'Image removed'); - } else { - throw new Error('Failed to remove image'); - } - } catch (error) { - console.error('Error removing image:', error); - addToast('error', 'Failed to remove image'); - } - } - // Attachment event handlers function handleAttachmentFileChange(event: Event) { const files = (event.target as HTMLInputElement).files; @@ -677,10 +458,8 @@ dispatch('next'); } - function handleImmichImageSaved(event: CustomEvent) { - const newImage = createImageFromData(event.detail); - updateImagesList(newImage); - addToast('success', $t('adventures.image_upload_success')); + function handleImagesUpdated(event: CustomEvent) { + images = event.detail; } // Lifecycle @@ -709,242 +488,21 @@ } catch (error) { console.error('Error checking integrations:', error); } - if (itemName) { - imageSearch = itemName; - } });
-
-
-
-
- -
-

{$t('adventures.image_management')}

-
- - -
- -
-

- {$t('adventures.upload_from_device')} -

- -
- - -
-

- {$t('adventures.upload_from_url')} -

-
- - -
- {#if imageError} -
- {imageError} -
- {/if} -
- - - {#if immichIntegration} -
-

Immich Integration

- { - url = e.detail; - handleUrlUpload(); - }} - on:remoteImmichSaved={handleImmichImageSaved} - /> -
- {/if} -
- - -
-

- {$t('adventures.wikipedia')} -

-
- - -
- {#if wikiImageError} -
- {wikiImageError} -
- {/if} - - - {#if wikiImageResults.length > 0} -
-
- - {$t('adventures.wiki_results_found', { - values: { count: wikiImageResults.length, query: imageSearch } - })} - - -
-
- {#each wikiImageResults as result (result.source)} - - {/each} -
-
- {/if} -
- - - {#if images.length > 0} -
Current Images
-
- {#each images as image (image.id)} -
-
- Uploaded content -
- - -
- {#if !image.is_primary} - - {/if} - - -
- - - {#if image.is_primary} -
- -
- {/if} -
- {/each} -
- {:else} -
-
{$t('adventures.no_images_uploaded_yet')}
-
- {$t('adventures.upload_first_image')} -
-
- {/if} -
-
+