feat: use module store

Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
2026-02-17 19:11:12 -05:00
parent 6a7b28b86d
commit b663efbb3e
10 changed files with 883 additions and 612 deletions
+86 -444
View File
@@ -1,17 +1,11 @@
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue';
import { computed, onMounted, watch } from 'vue';
import { storeToRefs } from 'pinia';
import { useRoute } from 'vue-router';
import { useDisplay } from 'vuetify';
import { useModuleStore } from '@KTXC/stores/moduleStore';
import { useCollectionsStore } from '@ChronoManager/stores/collectionsStore';
import { useEntitiesStore } from '@ChronoManager/stores/entitiesStore';
import { useServicesStore } from '@ChronoManager/stores/servicesStore';
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 { CalendarView as CalendarViewType } from '@/types';
import { useChronoStore } from '@/stores/chronoStore';
import CollectionList from '@/components/CollectionList.vue';
import CollectionEditor from '@/components/CollectionEditor.vue';
import CalendarView from '@/components/CalendarView.vue';
@@ -29,423 +23,82 @@ const isChronoManagerAvailable = computed(() => {
return moduleStore.has('chrono_manager') || moduleStore.has('ChronoManager')
});
// Stores
const collectionsStore = useCollectionsStore();
const entitiesStore = useEntitiesStore();
const servicesStore = useServicesStore();
const chronoStore = useChronoStore();
const route = useRoute();
// View state
const viewMode = ref<'calendar' | 'tasks'>('calendar');
const calendarView = ref<CalendarViewType>('days');
const currentDate = ref(new Date());
const sidebarVisible = ref(true);
const {
calendarView,
daysViewSpan,
agendaViewSpan,
currentDate,
sidebarVisible,
selectedCollection,
showEventEditor,
showTaskEditor,
showCollectionEditor,
selectedEntity,
entityEditorMode,
editingCollection,
collectionEditorMode,
collectionEditorType,
calendars,
taskLists,
isTaskView,
filteredEvents,
filteredTasks,
collections,
} = storeToRefs(chronoStore);
// Data - using manager objects directly
const collections = ref<CollectionObject[]>([]);
const entities = ref<EntityObject[]>([]);
const selectedCollection = ref<CollectionObject | null>(null);
const {
initialize,
setViewMode,
setCalendarView,
setDaysViewSpan,
setAgendaViewSpan,
selectCalendar,
openEditCalendar,
toggleCalendarVisibility,
selectTaskList,
openEditTaskList,
createEventFromDate,
startEditingSelectedEntity,
cancelEditingSelectedEntity,
closeEntityEditor,
editEvent,
editTask,
toggleTaskComplete,
saveEvent,
deleteEvent,
saveTask,
deleteTask,
saveCollection,
deleteCollection,
openCreateCalendar,
openCreateTaskList,
} = chronoStore;
// Computed - filter collections and entities
const calendars = computed(() => {
return collections.value.filter(col => col.properties.contents?.event);
});
const taskLists = computed(() => {
return collections.value.filter(col => col.properties.contents?.task);
});
const events = computed(() => {
return entities.value.filter(entity => entity.properties && (entity.properties as any).type === 'event');
});
const tasks = computed(() => {
return entities.value.filter(entity => entity.properties && (entity.properties as any).type === 'task');
});
// Dialog state
const showEventEditor = ref(false);
const showTaskEditor = ref(false);
const showCollectionEditor = ref(false);
const selectedEntity = ref<EntityObject | null>(null);
const entityEditorMode = ref<'edit' | 'view'>('view');
const editingCollection = ref<CollectionObject | null>(null);
const collectionEditorMode = ref<'create' | 'edit'>('create');
const collectionEditorType = ref<'calendar' | 'tasklist'>('calendar');
// Computed
const isTaskView = computed(() => viewMode.value === 'tasks');
const filteredEvents = computed(() => {
const visibleCalendarIds = calendars.value
.filter(cal => cal.properties.visibility !== false)
.map(cal => cal.identifier);
return events.value.filter(event =>
visibleCalendarIds.includes(event.collection)
);
});
const filteredTasks = computed(() => {
return tasks.value;
});
// Methods
function selectCalendar(calendar: CollectionObject) {
selectedCollection.value = calendar;
console.log('[Chrono] - Selected calendar:', calendar);
function syncViewModeFromRoute() {
const routePath = route.path;
const mode = routePath.endsWith('/tasks') ? 'tasks' : 'calendar';
setViewMode(mode);
}
function createCalendar() {
editingCollection.value = new CollectionObject();
editingCollection.value.properties.contents = { event: true };
collectionEditorMode.value = 'create';
collectionEditorType.value = 'calendar';
showCollectionEditor.value = true;
function handleCalendarViewChange(view: 'days' | 'month' | 'agenda') {
setCalendarView(view);
}
function editCalendar(collection: CollectionObject) {
editingCollection.value = collection;
collectionEditorMode.value = 'edit';
collectionEditorType.value = 'calendar';
showCollectionEditor.value = true;
}
async function toggleCalendarVisibility(collection: CollectionObject) {
try {
await collectionsStore.update(
collection.provider,
collection.service,
collection.identifier,
collection.properties
);
console.log('[Chrono] - Toggled calendar visibility:', collection);
} catch (error) {
console.error('[Chrono] - Failed to toggle calendar visibility:', error);
}
}
function selectTaskList(list: CollectionObject) {
selectedCollection.value = list;
console.log('[Chrono] - Selected task list:', list);
}
function createTaskList() {
editingCollection.value = new CollectionObject();
editingCollection.value.properties.contents = { task: true };
collectionEditorMode.value = 'create';
collectionEditorType.value = 'tasklist';
showCollectionEditor.value = true;
}
function editTaskList(collection: CollectionObject) {
editingCollection.value = collection;
collectionEditorMode.value = 'edit';
collectionEditorType.value = 'tasklist';
showCollectionEditor.value = true;
}
function createEvent() {
// Select first calendar collection or use selected
if (!selectedCollection.value && calendars.value.length > 0) {
selectedCollection.value = calendars.value[0];
}
if (!selectedCollection.value) {
console.warn('[Chrono] - No calendar collection available');
return;
}
selectedEntity.value = new EntityObject();
selectedEntity.value.properties = new EventObject();
entityEditorMode.value = 'edit';
showEventEditor.value = true;
}
function editEvent(entity: EntityObject) {
selectedEntity.value = entity;
entityEditorMode.value = 'view';
showEventEditor.value = true;
}
async function saveEvent(entity: EntityObject, collection?: CollectionObject | null) {
try {
if (!(collection instanceof CollectionObject)) {
collection = selectedCollection.value;
}
if (!collection) {
console.error('[Chrono] - No collection selected');
return;
}
const eventData = entity.properties as EventObject;
eventData.modified = new Date().toISOString();
if (!entity.identifier) {
eventData.created = new Date().toISOString();
selectedEntity.value = await entitiesStore.create(
collection.provider,
collection.service,
collection.identifier,
eventData.toJson()
);
} else {
selectedEntity.value = await entitiesStore.update(
collection.provider,
collection.service,
collection.identifier,
entity.identifier,
eventData.toJson()
);
}
entityEditorMode.value = 'view';
} catch (error) {
console.error('[Chrono] - Failed to save event:', error);
}
}
async function deleteEvent(entity: EntityObject, collection?: CollectionObject | null) {
try {
if (!(collection instanceof CollectionObject)) {
collection = collections.value.find(c => c.identifier === entity.collection);
}
if (!collection) {
console.error('[Chrono] - No collection found');
return;
}
await entitiesStore.delete(collection.provider, collection.service, collection.identifier, entity.identifier);
selectedEntity.value = null;
entityEditorMode.value = 'view';
showEventEditor.value = false;
} catch (error) {
console.error('[Chrono] - Failed to delete event:', error);
}
}
function handleDateClick(date: Date) {
// Select first calendar collection or use selected
if (!selectedCollection.value && calendars.value.length > 0) {
selectedCollection.value = calendars.value[0];
}
if (!selectedCollection.value) {
console.warn('[Chrono] - No calendar collection available');
return;
}
selectedEntity.value = new EntityObject();
selectedEntity.value.properties = new EventObject();
const eventData = selectedEntity.value.properties as EventObject;
eventData.startsOn = date.toISOString();
eventData.endsOn = new Date(date.getTime() + 60 * 60 * 1000).toISOString();
entityEditorMode.value = 'edit';
showEventEditor.value = true;
}
function handleEditorEdit() {
console.log('[Chrono] - Editor editing started');
entityEditorMode.value = 'edit';
}
function handleEditorCancel() {
console.log('[Chrono] - Editor editing cancelled');
entityEditorMode.value = 'view';
}
function handleEditorClose() {
console.log('[Chrono] - Editor closed');
selectedEntity.value = null;
entityEditorMode.value = 'view';
showEventEditor.value = false;
showTaskEditor.value = false;
}
function createTask() {
// Select first task list collection or use selected
if (!selectedCollection.value && taskLists.value.length > 0) {
selectedCollection.value = taskLists.value[0];
}
if (!selectedCollection.value) {
console.warn('[Chrono] - No task list available');
return;
}
selectedEntity.value = new EntityObject();
selectedEntity.value.properties = new TaskObject();
entityEditorMode.value = 'edit';
showTaskEditor.value = true;
}
function editTask(entity: EntityObject) {
selectedEntity.value = entity;
entityEditorMode.value = 'view';
showTaskEditor.value = true;
}
async function saveTask(entity: EntityObject, collection?: CollectionObject | null) {
try {
if (!(collection instanceof CollectionObject)) {
collection = selectedCollection.value;
}
if (!collection) {
console.error('[Chrono] - No collection selected');
return;
}
const taskData = entity.properties as TaskObject;
taskData.modified = new Date().toISOString();
if (!entity.identifier) {
taskData.created = new Date().toISOString();
selectedEntity.value = await entitiesStore.create(
collection.provider,
collection.service,
collection.identifier,
taskData.toJson()
);
} else {
selectedEntity.value = await entitiesStore.update(
collection.provider,
collection.service,
collection.identifier,
entity.identifier,
taskData.toJson()
);
}
entityEditorMode.value = 'view';
} catch (error) {
console.error('[Chrono] - Failed to save task:', error);
}
}
async function deleteTask(entity: EntityObject, collection?: CollectionObject | null) {
try {
if (!(collection instanceof CollectionObject)) {
collection = collections.value.find(c => c.identifier === entity.collection);
}
if (!collection) {
console.error('[Chrono] - No collection found');
return;
}
await entitiesStore.delete(collection.provider, collection.service, collection.identifier, entity.identifier);
selectedEntity.value = null;
entityEditorMode.value = 'view';
showTaskEditor.value = false;
} catch (error) {
console.error('[Chrono] - Failed to delete task:', error);
}
}
async function toggleTaskComplete(taskId: string | number) {
try {
const entity = entities.value.find(e => e.identifier === taskId);
if (!entity || !entity.properties) return;
const collection = collections.value.find(c => c.identifier === entity.collection);
if (!collection) return;
const taskData = entity.properties as TaskObject;
const isCompleted = taskData.status === 'completed';
taskData.status = isCompleted ? 'needs-action' : 'completed';
taskData.completedOn = isCompleted ? null : new Date().toISOString();
taskData.progress = isCompleted ? null : 100;
taskData.modified = new Date().toISOString();
await entitiesStore.update(
collection.provider,
collection.service,
collection.identifier,
entity.identifier,
taskData.toJson()
);
} catch (error) {
console.error('[Chrono] - Failed to toggle task completion:', error);
}
}
async function saveCollection(collection: CollectionObject, service: ServiceObject) {
try {
if (collectionEditorMode.value === 'create') {
await collectionsStore.create(
service.provider,
service.identifier || '',
null,
collection.properties
);
console.log('[Chrono] - Created collection:', collection);
} else {
await collectionsStore.update(
collection.provider,
collection.service,
collection.identifier,
collection.properties
);
console.log('[Chrono] - Modified collection:', collection);
}
// Reload collections
collections.value = Object.values(await collectionsStore.list());
} catch (error) {
console.error('[Chrono] - Failed to save collection:', error);
}
}
async function deleteCollection(collection: CollectionObject) {
try {
await collectionsStore.delete(collection.provider, collection.service, collection.identifier);
console.log('[Chrono] - Deleted collection:', collection);
// Reload collections
collections.value = Object.values(await collectionsStore.list());
if (selectedCollection.value?.identifier === collection.identifier) {
selectedCollection.value = null;
}
} catch (error) {
console.error('[Chrono] - Failed to delete collection:', error);
}
}
// Initialize data from stores
onMounted(async () => {
try {
// Load collections (calendars and task lists)
collections.value = Object.values(await collectionsStore.list());
// Load entities (events and tasks) for available collection sources only
const sources = collections.value.reduce<Record<string, Record<string, Record<string | number, true>>>>((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;
}, {});
if (Object.keys(sources).length > 0) {
entities.value = Object.values(await entitiesStore.list(sources));
} else {
entities.value = [];
}
console.log('[Chrono] - Loaded data from ChronoManager:', {
collections: collections.value.length,
calendars: calendars.value.length,
events: events.value.length,
tasks: tasks.value.length,
taskLists: taskLists.value.length,
});
syncViewModeFromRoute();
await initialize();
} catch (error) {
console.error('[Chrono] - Failed to load data from ChronoManager:', error);
}
});
watch(() => route.fullPath, () => {
syncViewModeFromRoute();
});
</script>
<template>
@@ -467,33 +120,16 @@ onMounted(async () => {
<v-spacer></v-spacer>
<!-- View Toggle -->
<v-btn-toggle
v-model="viewMode"
color="primary"
variant="outlined"
density="compact"
class="mr-2"
>
<v-btn value="calendar" size="small">
<v-icon>mdi-calendar</v-icon>
<span class="ml-1 d-none d-sm-inline">Calendar</span>
</v-btn>
<v-btn value="tasks" size="small">
<v-icon>mdi-checkbox-marked-outline</v-icon>
<span class="ml-1 d-none d-sm-inline">Tasks</span>
</v-btn>
</v-btn-toggle>
<!-- View Switcher for Calendar -->
<v-btn-toggle
v-if="!isTaskView"
v-model="calendarView"
:model-value="calendarView"
color="primary"
variant="outlined"
density="compact"
mandatory
class="mr-2"
@update:model-value="handleCalendarViewChange"
>
<v-btn value="days" size="small">Days</v-btn>
<v-btn value="month" size="small">Month</v-btn>
@@ -514,18 +150,20 @@ onMounted(async () => {
<div class="pa-4">
<CollectionList
v-if="!isTaskView"
:collections="collections"
:selected-collection="selectedCollection"
type="calendar"
@select="selectCalendar"
@edit="editCalendar"
@edit="openEditCalendar"
@toggle-visibility="toggleCalendarVisibility"
/>
<CollectionList
v-else
:collections="collections"
:selected-collection="selectedCollection"
type="tasklist"
@select="selectTaskList"
@edit="editTaskList"
@edit="openEditTaskList"
/>
<v-btn
@@ -534,7 +172,7 @@ onMounted(async () => {
color="primary"
block
class="mt-3"
@click="createCalendar"
@click="openCreateCalendar"
>
<v-icon start>mdi-plus</v-icon>
New Calendar
@@ -545,7 +183,7 @@ onMounted(async () => {
color="primary"
block
class="mt-3"
@click="createTaskList"
@click="openCreateTaskList"
>
<v-icon start>mdi-plus</v-icon>
New Task List
@@ -591,8 +229,12 @@ onMounted(async () => {
:current-date="currentDate"
:events="filteredEvents"
:calendars="calendars"
:initial-days-span="daysViewSpan"
:initial-agenda-view-span="agendaViewSpan"
@event-click="editEvent"
@date-click="handleDateClick"
@date-click="createEventFromDate"
@update:days-span="setDaysViewSpan"
@update:agenda-span="setAgendaViewSpan"
/>
<TaskView
@@ -615,9 +257,9 @@ onMounted(async () => {
:calendars="calendars"
@save="saveEvent"
@delete="deleteEvent"
@edit="handleEditorEdit"
@cancel="handleEditorCancel"
@close="handleEditorClose"
@edit="startEditingSelectedEntity"
@cancel="cancelEditingSelectedEntity"
@close="closeEntityEditor"
/>
</v-dialog>
@@ -631,9 +273,9 @@ onMounted(async () => {
:lists="taskLists"
@save="saveTask"
@delete="deleteTask"
@edit="handleEditorEdit"
@cancel="handleEditorCancel"
@close="handleEditorClose"
@edit="startEditingSelectedEntity"
@cancel="cancelEditingSelectedEntity"
@close="closeEntityEditor"
/>
</v-dialog>