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

feat: enhance adventure handling with user ID support in serializers and attachment view; refactor saveEdit function and clean up Avatar component

This commit is contained in:
Sean Morley 2025-01-22 08:36:02 -05:00
parent 3f30819d25
commit 0eb4bc706a
6 changed files with 199 additions and 186 deletions

View file

@ -8,8 +8,8 @@ from main.utils import CustomModelSerializer
class AdventureImageSerializer(CustomModelSerializer): class AdventureImageSerializer(CustomModelSerializer):
class Meta: class Meta:
model = AdventureImage model = AdventureImage
fields = ['id', 'image', 'adventure', 'is_primary'] fields = ['id', 'image', 'adventure', 'is_primary', 'user_id']
read_only_fields = ['id'] read_only_fields = ['id', 'user_id']
def to_representation(self, instance): def to_representation(self, instance):
representation = super().to_representation(instance) representation = super().to_representation(instance)
@ -25,8 +25,8 @@ class AttachmentSerializer(CustomModelSerializer):
extension = serializers.SerializerMethodField() extension = serializers.SerializerMethodField()
class Meta: class Meta:
model = Attachment model = Attachment
fields = ['id', 'file', 'adventure', 'extension', 'name'] fields = ['id', 'file', 'adventure', 'extension', 'name', 'user_id']
read_only_fields = ['id'] read_only_fields = ['id', 'user_id']
def get_extension(self, obj): def get_extension(self, obj):
return obj.file.name.split('.')[-1] return obj.file.name.split('.')[-1]

View file

@ -35,3 +35,6 @@ class AttachmentViewSet(viewsets.ModelViewSet):
return Response({"error": "User does not own this adventure"}, status=status.HTTP_403_FORBIDDEN) return Response({"error": "User does not own this adventure"}, status=status.HTTP_403_FORBIDDEN)
return super().create(request, *args, **kwargs) return super().create(request, *args, **kwargs)
def perform_create(self, serializer):
serializer.save(user_id=self.request.user)

View file

@ -173,16 +173,27 @@
} }
} }
let selectedFile: File | null = null;
function handleFileChange(event: Event) {
const input = event.target as HTMLInputElement;
if (input.files && input.files.length) {
selectedFile = input.files[0];
console.log('Selected file:', selectedFile);
}
}
async function uploadAttachment(event: Event) { async function uploadAttachment(event: Event) {
event.preventDefault(); event.preventDefault();
console.log('UPLOAD'); console.log('UPLOAD');
console.log(selectedFile);
if (!fileInput || !fileInput.files || fileInput.files.length === 0) { if (!selectedFile) {
console.error('No files selected'); console.error('No files selected');
return; return;
} }
const file = fileInput.files[0]; const file = selectedFile;
console.log(file); console.log(file);
const formData = new FormData(); const formData = new FormData();
@ -962,69 +973,6 @@ it would also work to just use on:click on the MapLibre component itself. -->
<ActivityComplete bind:activities={adventure.activity_types} /> <ActivityComplete bind:activities={adventure.activity_types} />
</div> </div>
</div> </div>
<div class="collapse collapse-plus bg-base-200 mb-4">
<input type="checkbox" />
<div class="collapse-title text-xl font-medium">
{$t('adventures.attachments')} ({adventure.attachments?.length || 0})
</div>
<div class="collapse-content">
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{#each adventure.attachments as attachment}
<AttachmentCard
{attachment}
on:delete={deleteAttachment}
allowEdit
on:edit={(e) => (attachmentToEdit = e.detail)}
/>
{/each}
</div>
<form
on:submit={(e) => {
e.preventDefault();
uploadAttachment(e);
}}
>
<div class="flex gap-2 m-4">
<input
type="file"
id="fileInput"
class="file-input file-input-bordered w-full max-w-xs"
accept="image/*,video/*,audio/*,application/pdf,.gpx"
bind:this={fileInput}
/>
<input
type="text"
class="input input-bordered w-full"
placeholder={$t('adventures.attachment_name')}
bind:value={attachmentName}
/>
<button type="submit" class="btn btn-neutral">{$t('adventures.upload')}</button>
</div>
</form>
{#if attachmentToEdit}
<form
on:submit={(e) => {
e.preventDefault();
editAttachment();
}}
>
<div class="flex gap-2 m-4">
<input
type="text"
class="input input-bordered w-full"
placeholder={$t('adventures.attachment_name')}
bind:value={attachmentToEdit.name}
/>
<button type="submit" class="btn btn-neutral"
>{$t('transportation.edit')}</button
>
</div>
</form>
{/if}
</div>
</div>
<div class="collapse collapse-plus bg-base-200 mb-4"> <div class="collapse collapse-plus bg-base-200 mb-4">
<input type="checkbox" /> <input type="checkbox" />
<div class="collapse-title text-xl font-medium"> <div class="collapse-title text-xl font-medium">
@ -1185,9 +1133,70 @@ it would also work to just use on:click on the MapLibre component itself. -->
</form> </form>
</div> </div>
{:else} {:else}
<p class="text-lg">{$t('adventures.upload_images_here')}</p> <div class="modal-action items-center">
<div class="collapse collapse-plus bg-base-200 mb-4">
<input type="checkbox" />
<div class="collapse-title text-xl font-medium">
{$t('adventures.attachments')} ({adventure.attachments?.length || 0})
</div>
<div class="collapse-content">
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{#each adventure.attachments as attachment}
<AttachmentCard
{attachment}
on:delete={deleteAttachment}
allowEdit
on:edit={(e) => (attachmentToEdit = e.detail)}
/>
{/each}
</div>
<div class="flex gap-2 m-4">
<input
type="file"
id="fileInput"
class="file-input file-input-bordered w-full max-w-xs"
accept="image/*,video/*,audio/*,application/pdf,.gpx"
on:change={handleFileChange}
/>
<div class="mb-4"> <input
type="text"
class="input input-bordered w-full"
placeholder={$t('adventures.attachment_name')}
bind:value={attachmentName}
/>
<button class="btn btn-neutral" on:click={uploadAttachment}>
{$t('adventures.upload')}
</button>
</div>
{#if attachmentToEdit}
<form
on:submit={(e) => {
e.preventDefault();
editAttachment();
}}
>
<div class="flex gap-2 m-4">
<input
type="text"
class="input input-bordered w-full"
placeholder={$t('adventures.attachment_name')}
bind:value={attachmentToEdit.name}
/>
<button type="submit" class="btn btn-neutral">{$t('transportation.edit')}</button>
</div>
</form>
{/if}
</div>
</div>
</div>
<div class="collapse collapse-plus bg-base-200 mb-4">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">
{$t('adventures.images')} ({adventure.images?.length || 0})
</div>
<div class="collapse-content">
<label for="image" class="block font-medium mb-2"> <label for="image" class="block font-medium mb-2">
{$t('adventures.image')} {$t('adventures.image')}
</label> </label>
@ -1203,11 +1212,7 @@ it would also work to just use on:click on the MapLibre component itself. -->
on:change={handleMultipleFiles} on:change={handleMultipleFiles}
/> />
<input type="hidden" name="adventure" value={adventure.id} id="adventure" /> <input type="hidden" name="adventure" value={adventure.id} id="adventure" />
<!-- <button class="btn btn-neutral w-full max-w-sm" type="submit">
{$t('adventures.upload_image')}
</button> -->
</form> </form>
</div>
<div class="mb-4"> <div class="mb-4">
<label for="url" class="block font-medium mb-2"> <label for="url" class="block font-medium mb-2">
@ -1299,8 +1304,9 @@ it would also work to just use on:click on the MapLibre component itself. -->
{:else} {:else}
<h1 class="font-semibold text-xl text-gray-500">{$t('adventures.no_images')}</h1> <h1 class="font-semibold text-xl text-gray-500">{$t('adventures.no_images')}</h1>
{/if} {/if}
</div>
<div class="mt-6"> </div>
<div class="mt-4">
<button type="button" class="btn btn-primary w-full max-w-sm" on:click={saveAndClose}> <button type="button" class="btn btn-primary w-full max-w-sm" on:click={saveAndClose}>
{$t('about.close')} {$t('about.close')}
</button> </button>

View file

@ -36,7 +36,6 @@
</p> </p>
<li><button on:click={() => goto('/profile')}>{$t('navbar.profile')}</button></li> <li><button on:click={() => goto('/profile')}>{$t('navbar.profile')}</button></li>
<li><button on:click={() => goto('/adventures')}>{$t('navbar.my_adventures')}</button></li> <li><button on:click={() => goto('/adventures')}>{$t('navbar.my_adventures')}</button></li>
<li><button on:click={() => goto('/activities')}>{$t('navbar.my_tags')}</button></li>
<li><button on:click={() => goto('/shared')}>{$t('navbar.shared_with_me')}</button></li> <li><button on:click={() => goto('/shared')}>{$t('navbar.shared_with_me')}</button></li>
<li><button on:click={() => goto('/settings')}>{$t('navbar.settings')}</button></li> <li><button on:click={() => goto('/settings')}>{$t('navbar.settings')}</button></li>
<form method="post"> <form method="post">

View file

@ -91,6 +91,9 @@ export const actions: Actions = {
body: formData body: formData
}); });
let data = await res.json(); let data = await res.json();
console.log(res);
console.log(data);
return data; return data;
} }
}; };

View file

@ -102,9 +102,11 @@
await getGpxFiles(); await getGpxFiles();
}); });
function saveEdit(event: CustomEvent<Adventure>) { async function saveEdit(event: CustomEvent<Adventure>) {
adventure = event.detail; adventure = event.detail;
isEditModalOpen = false; isEditModalOpen = false;
geojson = null;
await getGpxFiles();
} }
</script> </script>