diff --git a/src/components/CollectionEditor.vue b/src/components/CollectionEditor.vue index 6fac087..bd8ef67 100644 --- a/src/components/CollectionEditor.vue +++ b/src/components/CollectionEditor.vue @@ -2,7 +2,7 @@ import { ref, computed, watch } from 'vue' import { useServicesStore } from '@ChronoManager/stores/servicesStore' import { CollectionObject } from '@ChronoManager/models/collection' -import { ServiceObject } from '@ChronoManager/models/service' +import type { ServiceObject } from '@ChronoManager/models/service' // Store const servicesStore = useServicesStore() @@ -74,11 +74,11 @@ const onOpen = async () => { if (props.mode === 'edit') { // Edit mode - find the service editingCollectionService.value = services.value.find(s => - s.provider === props.collection!.provider && s.identifier === props.collection!.service + `${s.provider}:${s.identifier}` === String(props.collection!.service) ) || null } else { // Create mode - use first service that can create - editingCollectionService.value = services.value.filter(s => s.capabilities?.CollectionCreate)[0] || null + editingCollectionService.value = services.value.find(s => s.capable('CollectionCreate')) || null } } @@ -142,7 +142,7 @@ watch(() => props.modelValue, async (newValue) => { props.modelValue, async (newValue) => {
, - collectionsStore: ReturnType, -) { - function getEntityData(entity: EntityObject): Record { - const properties = entity.properties as ChronoEntityProperties - const now = new Date().toISOString() - - properties.modified = now as never - if (!entity.identifier) { - properties.created = now as never - } - - return properties.toJson() as unknown as Record - } - - function resolveCollection(entity: EntityObject, collection?: CollectionObject | null): CollectionObject { - if (collection instanceof CollectionObject) { - return collection - } - - const foundCollection = collectionsStore.collections.find((item) => item.identifier === entity.collection) - if (!foundCollection) { - throw new Error('Collection not found for entity operation') - } - - return foundCollection - } - - async function saveEntity(entity: EntityObject, collection: CollectionObject): Promise { - const data = getEntityData(entity) - - if (!entity.identifier) { - return entitiesStore.create( - collection.provider, - collection.service, - collection.identifier, - data, - ) - } - - return entitiesStore.update( - collection.provider, - collection.service, - collection.identifier, - entity.identifier, - data, - ) - } - - async function deleteEntity(entity: EntityObject, collection?: CollectionObject | null): Promise { - const resolvedCollection = resolveCollection(entity, collection) - - await entitiesStore.delete( - resolvedCollection.provider, - resolvedCollection.service, - resolvedCollection.identifier, - entity.identifier, - ) - } - - async function toggleTaskCompletion(entity: EntityObject, collection?: CollectionObject | null): Promise { - const resolvedCollection = resolveCollection(entity, collection) - const taskData = entity.properties as TaskObject - const isCompleted = taskData.status === 'completed' - - taskData.status = isCompleted ? 'needs-action' : 'completed' - taskData.completedOn = isCompleted ? null : new Date().toISOString() as never - taskData.progress = isCompleted ? null : 100 - taskData.modified = new Date().toISOString() as never - - return entitiesStore.update( - resolvedCollection.provider, - resolvedCollection.service, - resolvedCollection.identifier, - entity.identifier, - taskData.toJson(), - ) - } - - return { - saveEntity, - deleteEntity, - toggleTaskCompletion, - resolveCollection, - } -} diff --git a/src/pages/ChronoPage.vue b/src/pages/ChronoPage.vue index 57213b7..7644ae4 100644 --- a/src/pages/ChronoPage.vue +++ b/src/pages/ChronoPage.vue @@ -41,6 +41,7 @@ const { editingCollection, collectionEditorMode, collectionEditorType, + loading, calendars, taskLists, isTaskView, @@ -223,27 +224,31 @@ watch(() => route.fullPath, () => {
- +
+ - + + + +
@@ -322,6 +327,13 @@ watch(() => route.fullPath, () => { flex-direction: column; } +.chrono-wrapper { + display: flex; + flex: 1; + min-height: 0; + flex-direction: column; +} + .border-b { border-bottom: 1px solid rgb(var(--v-border-color)); } diff --git a/src/stores/chronoStore.ts b/src/stores/chronoStore.ts index 417da2d..ee5a695 100644 --- a/src/stores/chronoStore.ts +++ b/src/stores/chronoStore.ts @@ -5,10 +5,10 @@ import { CollectionObject } from '@ChronoManager/models/collection' import { EntityObject } from '@ChronoManager/models/entity' import { EventObject } from '@ChronoManager/models/event' import { TaskObject } from '@ChronoManager/models/task' -import { ServiceObject } from '@ChronoManager/models/service' +import type { ServiceObject } from '@ChronoManager/models/service' import { useCollectionsStore } from '@ChronoManager/stores/collectionsStore' import { useEntitiesStore } from '@ChronoManager/stores/entitiesStore' -import type { SourceSelector } from '@ChronoManager/types' +import { useServicesStore } from '@ChronoManager/stores/servicesStore' import type { CalendarView as CalendarViewType } from '@/types' import { AGENDA_VIEW_SPANS, @@ -16,15 +16,14 @@ import { type AgendaViewSpan, type DaysViewSpan, } from '@/types/spans' -import { useChronoEntityActions } from '@/composables/useChronoEntityActions' -type EntitySources = Record>> +type ChronoEntityProperties = EventObject | TaskObject export const useChronoStore = defineStore('chronoStore', () => { const userStore = useUserStore() + const servicesStore = useServicesStore() const collectionsStore = useCollectionsStore() const entitiesStore = useEntitiesStore() - const entityActions = useChronoEntityActions(entitiesStore, collectionsStore) const savedCalendarView = userStore.getSetting('chrono.calendarView') const savedDaysViewSpan = userStore.getSetting('chrono.daysViewSpan') @@ -95,22 +94,6 @@ export const useChronoStore = defineStore('chronoStore', () => { const filteredTasks = computed(() => tasks.value) - function buildSources(collectionItems: CollectionObject[]): EntitySources { - return collectionItems.reduce((acc, collection) => { - if (!acc[collection.provider]) { - acc[collection.provider] = {} - } - - const serviceKey = String(collection.service) - if (!acc[collection.provider][serviceKey]) { - acc[collection.provider][serviceKey] = {} - } - - acc[collection.provider][serviceKey][collection.identifier] = true - return acc - }, {}) - } - function setViewMode(mode: 'calendar' | 'tasks') { viewMode.value = mode } @@ -258,17 +241,32 @@ export const useChronoStore = defineStore('chronoStore', () => { showTaskEditor.value = false } + function prepareEntityProperties(entity: EntityObject): ChronoEntityProperties { + const properties = entity.properties as ChronoEntityProperties + const now = new Date().toISOString() + + properties.modified = now as never + if (!entity.identifier) { + properties.created = now as never + } + + return properties + } + + async function persistEntity(entity: EntityObject, collection: CollectionObject): Promise { + const properties = prepareEntityProperties(entity).toJson() + + return entity.identifier + ? entitiesStore.update(entity.identifier, properties) + : entitiesStore.create(collection.identifier, properties) + } + async function toggleCalendarVisibility(collection: CollectionObject) { const nextVisibility = collection.properties.visibility === false const updated = collection.clone() updated.properties.visibility = nextVisibility - await collectionsStore.update( - updated.provider, - updated.service, - updated.identifier, - updated.properties, - ) + await collectionsStore.update(updated.identifier, updated.properties) } async function saveEvent(entity: EntityObject, collection?: CollectionObject | null) { @@ -280,7 +278,7 @@ export const useChronoStore = defineStore('chronoStore', () => { throw new Error('No calendar collection selected') } - selectedEntity.value = await entityActions.saveEntity(entity, targetCollection) + selectedEntity.value = await persistEntity(entity, targetCollection) selectedCollection.value = targetCollection entityEditorMode.value = 'view' } @@ -294,18 +292,26 @@ export const useChronoStore = defineStore('chronoStore', () => { throw new Error('No task list selected') } - selectedEntity.value = await entityActions.saveEntity(entity, targetCollection) + selectedEntity.value = await persistEntity(entity, targetCollection) selectedCollection.value = targetCollection entityEditorMode.value = 'view' } async function deleteEvent(entity: EntityObject, collection?: CollectionObject | null) { - await entityActions.deleteEntity(entity, collection) + if (!(collection instanceof CollectionObject) && !selectedCollection.value) { + throw new Error('No calendar collection selected') + } + + await entitiesStore.delete([entity.identifier]) closeEntityEditor() } async function deleteTask(entity: EntityObject, collection?: CollectionObject | null) { - await entityActions.deleteEntity(entity, collection) + if (!(collection instanceof CollectionObject) && !selectedCollection.value) { + throw new Error('No task list selected') + } + + await entitiesStore.delete([entity.identifier]) closeEntityEditor() } @@ -315,7 +321,15 @@ export const useChronoStore = defineStore('chronoStore', () => { return } - await entityActions.toggleTaskCompletion(taskEntity) + const taskData = taskEntity.properties as TaskObject + const isCompleted = taskData.status === 'completed' + + taskData.status = isCompleted ? 'needs-action' : 'completed' + taskData.completedOn = isCompleted ? null : new Date().toISOString() as never + taskData.progress = isCompleted ? null : 100 + taskData.modified = new Date().toISOString() as never + + await entitiesStore.update(taskEntity.identifier, taskData.toJson()) } async function saveCollection(collection: CollectionObject, service: ServiceObject) { @@ -323,35 +337,35 @@ export const useChronoStore = defineStore('chronoStore', () => { await collectionsStore.create( service.provider, service.identifier || '', - null, collection.properties, ) } else { - await collectionsStore.update( - collection.provider, - collection.service, - collection.identifier, - collection.properties, - ) + await collectionsStore.update(collection.identifier, collection.properties) } + + showCollectionEditor.value = false } async function deleteCollection(collection: CollectionObject) { - await collectionsStore.delete(collection.provider, collection.service, collection.identifier) + await collectionsStore.delete(collection.identifier) if (selectedCollection.value?.identifier === collection.identifier) { selectedCollection.value = null + selectedEntity.value = null } + + showCollectionEditor.value = false } async function initialize() { loading.value = true try { + await servicesStore.list() await collectionsStore.list() - const sources = buildSources(collections.value) - if (Object.keys(sources).length > 0) { - await entitiesStore.list(sources as SourceSelector) + const sources = collections.value.map(collection => collection.identifier) + if (sources.length > 0) { + await entitiesStore.list(sources) } if (