From 18ef919a116dd488cc09a5461229163913a894bd Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Mon, 5 Aug 2024 21:36:38 -0400 Subject: [PATCH] checklists ui --- backend/server/adventures/serializers.py | 52 +++- .../src/lib/components/ChecklistCard.svelte | 4 +- .../src/lib/components/ChecklistModal.svelte | 263 ++++++++++++++++++ frontend/src/lib/components/NoteCard.svelte | 2 +- frontend/src/lib/index.ts | 28 +- .../src/routes/collections/[id]/+page.svelte | 68 ++++- 6 files changed, 405 insertions(+), 12 deletions(-) create mode 100644 frontend/src/lib/components/ChecklistModal.svelte diff --git a/backend/server/adventures/serializers.py b/backend/server/adventures/serializers.py index c30a06f..517a55c 100644 --- a/backend/server/adventures/serializers.py +++ b/backend/server/adventures/serializers.py @@ -96,7 +96,7 @@ class ChecklistItemSerializer(serializers.ModelSerializer): fields = [ 'id', 'user_id', 'name', 'is_checked', 'checklist', 'created_at', 'updated_at' ] - read_only_fields = ['id', 'created_at', 'updated_at', 'user_id'] + read_only_fields = ['id', 'created_at', 'updated_at', 'user_id', 'checklist'] def validate(self, data): # Check if the checklist is public and the checklist item is not @@ -123,13 +123,56 @@ class ChecklistItemSerializer(serializers.ModelSerializer): class ChecklistSerializer(serializers.ModelSerializer): - items = ChecklistItemSerializer(many=True, read_only=True, source='checklistitem_set') + items = ChecklistItemSerializer(many=True, source='checklistitem_set') class Meta: model = Checklist fields = [ 'id', 'user_id', 'name', 'date', 'is_public', 'collection', 'created_at', 'updated_at', 'items' ] read_only_fields = ['id', 'created_at', 'updated_at', 'user_id'] + + def create(self, validated_data): + items_data = validated_data.pop('checklistitem_set') + checklist = Checklist.objects.create(**validated_data) + for item_data in items_data: + ChecklistItem.objects.create(checklist=checklist, **item_data) + return checklist + + def update(self, instance, validated_data): + items_data = validated_data.pop('checklistitem_set', []) + + # Update Checklist fields + for attr, value in validated_data.items(): + setattr(instance, attr, value) + instance.save() + + # Get current items + current_items = instance.checklistitem_set.all() + current_item_ids = set(current_items.values_list('id', flat=True)) + + # Update or create items + updated_item_ids = set() + for item_data in items_data: + item_id = item_data.get('id') + if item_id: + if item_id in current_item_ids: + item = current_items.get(id=item_id) + for attr, value in item_data.items(): + setattr(item, attr, value) + item.save() + updated_item_ids.add(item_id) + else: + # If ID is provided but doesn't exist, create new item + ChecklistItem.objects.create(checklist=instance, **item_data) + else: + # If no ID is provided, create new item + ChecklistItem.objects.create(checklist=instance, **item_data) + + # Delete items that are not in the updated data + items_to_delete = current_item_ids - updated_item_ids + instance.checklistitem_set.filter(id__in=items_to_delete).delete() + + return instance def validate(self, data): # Check if the collection is public and the checklist is not @@ -149,10 +192,7 @@ class ChecklistSerializer(serializers.ModelSerializer): return data - def create(self, validated_data): - # Set the user_id to the current user - validated_data['user_id'] = self.context['request'].user - return super().create(validated_data) + class CollectionSerializer(serializers.ModelSerializer): diff --git a/frontend/src/lib/components/ChecklistCard.svelte b/frontend/src/lib/components/ChecklistCard.svelte index f788b58..6df1d8b 100644 --- a/frontend/src/lib/components/ChecklistCard.svelte +++ b/frontend/src/lib/components/ChecklistCard.svelte @@ -11,7 +11,7 @@ export let checklist: Checklist; export let user: User | null = null; - function editNote() { + function editChecklist() { dispatch('edit', checklist); } @@ -51,7 +51,7 @@ - {#if checklist.user_id == user?.pk} diff --git a/frontend/src/lib/components/ChecklistModal.svelte b/frontend/src/lib/components/ChecklistModal.svelte new file mode 100644 index 0000000..d0e0895 --- /dev/null +++ b/frontend/src/lib/components/ChecklistModal.svelte @@ -0,0 +1,263 @@ + + + + + + + diff --git a/frontend/src/lib/components/NoteCard.svelte b/frontend/src/lib/components/NoteCard.svelte index b3a4913..71a8ea4 100644 --- a/frontend/src/lib/components/NoteCard.svelte +++ b/frontend/src/lib/components/NoteCard.svelte @@ -40,7 +40,7 @@
Note
{#if note.links && note.links.length > 0} -

{note.links.length} links

+

{note.links.length} {note.links.length > 1 ? 'Links' : 'Link'}

{/if} {#if note.date && note.date !== ''}
diff --git a/frontend/src/lib/index.ts b/frontend/src/lib/index.ts index 2a7db47..39196a9 100644 --- a/frontend/src/lib/index.ts +++ b/frontend/src/lib/index.ts @@ -1,5 +1,5 @@ import inspirationalQuotes from './json/quotes.json'; -import type { Adventure, Collection, Note, Transportation } from './types'; +import type { Adventure, Checklist, Collection, Note, Transportation } from './types'; export function getRandomQuote() { const quotes = inspirationalQuotes.quotes; @@ -152,3 +152,29 @@ export function groupNotesByDate( return groupedNotes; } + +export function groupChecklistsByDate( + checklists: Checklist[], + startDate: Date, + numberOfDays: number +): Record { + const groupedChecklists: Record = {}; + + for (let i = 0; i < numberOfDays; i++) { + const currentDate = new Date(startDate); + currentDate.setDate(startDate.getDate() + i); + const dateString = currentDate.toISOString().split('T')[0]; + groupedChecklists[dateString] = []; + } + + checklists.forEach((checklist) => { + if (checklist.date) { + const noteDate = new Date(checklist.date).toISOString().split('T')[0]; + if (groupedChecklists[noteDate]) { + groupedChecklists[noteDate].push(checklist); + } + } + }); + + return groupedChecklists; +} diff --git a/frontend/src/routes/collections/[id]/+page.svelte b/frontend/src/routes/collections/[id]/+page.svelte index 8e7d1a6..5d5c6d7 100644 --- a/frontend/src/routes/collections/[id]/+page.svelte +++ b/frontend/src/routes/collections/[id]/+page.svelte @@ -18,8 +18,14 @@ import NoteCard from '$lib/components/NoteCard.svelte'; import NoteModal from '$lib/components/NoteModal.svelte'; - import { groupAdventuresByDate, groupNotesByDate, groupTransportationsByDate } from '$lib'; + import { + groupAdventuresByDate, + groupNotesByDate, + groupTransportationsByDate, + groupChecklistsByDate + } from '$lib'; import ChecklistCard from '$lib/components/ChecklistCard.svelte'; + import ChecklistModal from '$lib/components/ChecklistModal.svelte'; export let data: PageData; console.log(data); @@ -46,6 +52,7 @@ let isShowingLinkModal: boolean = false; let isShowingCreateModal: boolean = false; let isShowingTransportationModal: boolean = false; + let isShowingChecklistModal: boolean = false; onMount(() => { if (data.props.adventure) { @@ -123,6 +130,7 @@ let isTransportationEditModalOpen: boolean = false; let isNoteModalOpen: boolean = false; let noteToEdit: Note | null; + let checklistToEdit: Checklist | null; let newType: string; @@ -205,6 +213,28 @@ /> {/if} +{#if isShowingChecklistModal} + (isShowingChecklistModal = false)} + on:create={(event) => { + checklists = [event.detail, ...checklists]; + isShowingChecklistModal = false; + }} + on:save={(event) => { + checklists = checklists.map((checklist) => { + if (checklist.id === event.detail.id) { + return event.detail; + } + return checklist; + }); + isShowingChecklistModal = false; + }} + /> +{/if} + {#if isShowingCreateModal} Note +