refactor: split uni store to per responsiblity store

Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
2026-06-29 20:41:01 -04:00
parent 460c31e224
commit 31d20448be
5 changed files with 508 additions and 487 deletions
+37 -16
View File
@@ -5,7 +5,11 @@ import { storeToRefs } from 'pinia';
import { useRoute } from 'vue-router';
import { useDisplay } from 'vuetify';
import { useModuleStore } from '@KTXC/stores/moduleStore';
import { useChronoStore } from '@/stores/chronoStore';
import { useChronoOperationsStore } from '@/stores/chronoOperationsStore';
import { useChronoSettingsStore } from '@/stores/chronoSettingsStore';
import { useChronoUiStore } from '@/stores/chronoUiStore';
import type { CalendarView as CalendarViewType } from '@/types';
import type { AgendaViewSpan, DaysViewSpan } from '@/types/spans';
import CollectionList from '@/components/CollectionList.vue';
import CollectionEditor from '@/components/CollectionEditor.vue';
import CalendarView from '@/components/CalendarView.vue';
@@ -24,13 +28,27 @@ const isChronoManagerAvailable = computed(() => {
return moduleStore.has('chrono_manager') || moduleStore.has('ChronoManager')
});
const chronoStore = useChronoStore();
const chronoOperationsStore = useChronoOperationsStore();
const chronoSettingsStore = useChronoSettingsStore();
const chronoUiStore = useChronoUiStore();
const route = useRoute();
const {
calendarView,
daysViewSpan,
agendaViewSpan,
} = storeToRefs(chronoSettingsStore);
const {
loading,
calendars,
taskLists,
filteredEvents,
filteredTasks,
collections,
} = storeToRefs(chronoOperationsStore);
const {
currentDate,
sidebarVisible,
selectedCollection,
@@ -43,24 +61,14 @@ const {
editingCollection,
collectionEditorMode,
collectionEditorType,
loading,
calendars,
taskLists,
isTaskView,
filteredEvents,
filteredTasks,
collections,
} = storeToRefs(chronoStore);
} = storeToRefs(chronoUiStore);
const {
initialize,
setViewMode,
setCalendarView,
setDaysViewSpan,
setAgendaViewSpan,
selectCalendar,
openEditCalendar,
toggleCalendarVisibility,
selectTaskList,
openEditTaskList,
createEventFromDate,
@@ -69,7 +77,6 @@ const {
closeEntityEditor,
editEvent,
editTask,
toggleTaskComplete,
saveEvent,
deleteEvent,
saveTask,
@@ -80,7 +87,21 @@ const {
openCreateTaskList,
openImport,
closeImport,
} = chronoStore;
} = chronoUiStore;
const { toggleCalendarVisibility, toggleTaskComplete } = chronoOperationsStore;
function setCalendarView(view: CalendarViewType) {
chronoSettingsStore.calendarView = view;
}
function setDaysViewSpan(span: DaysViewSpan) {
chronoSettingsStore.daysViewSpan = span;
}
function setAgendaViewSpan(span: AgendaViewSpan) {
chronoSettingsStore.agendaViewSpan = span;
}
const fileInputRef = ref<HTMLInputElement | null>(null)
function triggerFileInput() { fileInputRef.value?.click() }
@@ -97,7 +118,7 @@ function syncViewModeFromRoute() {
setViewMode(mode);
}
function handleCalendarViewChange(view: 'days' | 'month' | 'agenda') {
function handleCalendarViewChange(view: CalendarViewType) {
setCalendarView(view);
}
+175
View File
@@ -0,0 +1,175 @@
import { computed, ref } from 'vue'
import { defineStore } from 'pinia'
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 type { ServiceObject } from '@ChronoManager/models/service'
import { useCollectionsStore } from '@ChronoManager/stores/collectionsStore'
import { useEntitiesStore } from '@ChronoManager/stores/entitiesStore'
import { useServicesStore } from '@ChronoManager/stores/servicesStore'
import { useImportStore } from '@ChronoManager/stores/importStore'
type ChronoEntityProperties = EventObject | TaskObject
export const useChronoOperationsStore = defineStore('chronoOperationsStore', () => {
const servicesStore = useServicesStore()
const collectionsStore = useCollectionsStore()
const entitiesStore = useEntitiesStore()
const importStore = useImportStore()
const loading = ref(false)
const collections = computed(() => collectionsStore.collections)
const entities = computed(() => entitiesStore.entities)
const calendars = computed(() => collections.value.filter(
collection => collection.properties.content?.includes('event'),
))
const taskLists = computed(() => collections.value.filter(
collection => collection.properties.content?.includes('task'),
))
const events = computed(() => entities.value.filter(
entity => entity.properties instanceof EventObject,
))
const tasks = computed(() => entities.value.filter(
entity => entity.properties instanceof TaskObject,
))
const filteredEvents = computed(() => {
const visibleCalendarIds = calendars.value
.filter(calendar => calendar.properties.visibility !== false)
.map(calendar => calendar.identifier)
return events.value.filter(event => visibleCalendarIds.includes(event.collection))
})
const filteredTasks = computed(() => tasks.value)
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 saveEntity(entity: EntityObject, collection: CollectionObject): Promise<EntityObject> {
const properties = prepareEntityProperties(entity).toJson()
return entity.identifier
? entitiesStore.update(entity.identifier, properties)
: entitiesStore.create(collection.identifier, properties)
}
async function deleteEntity(entity: EntityObject) {
await entitiesStore.delete([entity.identifier])
}
async function toggleCalendarVisibility(collection: CollectionObject) {
const updated = collection.clone()
updated.properties.visibility = collection.properties.visibility === false
await collectionsStore.update(updated.identifier, updated.properties)
}
async function toggleTaskComplete(taskId: string | number) {
const taskEntity = tasks.value.find(task => task.identifier === taskId)
if (!taskEntity) return
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,
mode: 'create' | 'edit',
) {
if (mode === 'create') {
await collectionsStore.create(service.provider, service.identifier || '', collection.properties)
return
}
await collectionsStore.update(collection.identifier, collection.properties)
}
async function deleteCollection(collection: CollectionObject) {
await collectionsStore.delete(collection.identifier)
}
async function prepareImport(files: File[], selectedCollection: CollectionObject | null) {
importStore.removeAllFiles()
importStore.reset()
for (const file of files) {
const id = importStore.addFile({
name: file.name,
contents: await file.text(),
size: file.size,
type: file.type,
})
if (selectedCollection && collections.value.some(
collection => collection.identifier === selectedCollection.identifier,
)) {
importStore.setCollectionForFile(id, selectedCollection.identifier)
}
}
importStore.stage = 'selecting'
}
async function finishImport(refresh = false) {
if (refresh) {
const targets = new Set<CollectionObject['identifier']>()
for (const id of importStore.order) {
const target = importStore.sessions[id]?.targetIdentifier
if (target) targets.add(target)
}
for (const target of targets) await entitiesStore.list([target])
}
importStore.removeAllFiles()
importStore.reset()
}
async function initialize() {
loading.value = true
try {
await servicesStore.list()
await collectionsStore.list()
const sources = collections.value.map(collection => collection.identifier)
if (sources.length > 0) await entitiesStore.list(sources)
} finally {
loading.value = false
}
}
return {
loading,
collections,
entities,
calendars,
taskLists,
events,
tasks,
filteredEvents,
filteredTasks,
saveEntity,
deleteEntity,
toggleCalendarVisibility,
toggleTaskComplete,
saveCollection,
deleteCollection,
prepareImport,
finishImport,
initialize,
}
})
+55
View File
@@ -0,0 +1,55 @@
import { computed } from 'vue'
import { defineStore } from 'pinia'
import { useUserStore } from '@KTXC/stores/userStore'
import type { CalendarView } from '@/types'
import {
AGENDA_VIEW_SPANS,
DAYS_VIEW_SPANS,
type AgendaViewSpan,
type DaysViewSpan,
} from '@/types/spans'
const CALENDAR_VIEW_KEY = 'chrono.calendarView'
const DAYS_VIEW_SPAN_KEY = 'chrono.daysViewSpan'
const AGENDA_VIEW_SPAN_KEY = 'chrono.agendaViewSpan'
function normalizeCalendarView(value: unknown): CalendarView {
return value === 'month' || value === 'agenda' || value === 'days' ? value : 'days'
}
function normalizeDaysViewSpan(value: unknown): DaysViewSpan {
return typeof value === 'string' && DAYS_VIEW_SPANS.includes(value as DaysViewSpan)
? value as DaysViewSpan
: '7d'
}
function normalizeAgendaViewSpan(value: unknown): AgendaViewSpan {
return typeof value === 'string' && AGENDA_VIEW_SPANS.includes(value as AgendaViewSpan)
? value as AgendaViewSpan
: '1w'
}
export const useChronoSettingsStore = defineStore('chronoSettingsStore', () => {
const userStore = useUserStore()
const calendarView = computed({
get: () => normalizeCalendarView(userStore.getSetting(CALENDAR_VIEW_KEY)),
set: (value: CalendarView) => userStore.setSetting(CALENDAR_VIEW_KEY, normalizeCalendarView(value)),
})
const daysViewSpan = computed({
get: () => normalizeDaysViewSpan(userStore.getSetting(DAYS_VIEW_SPAN_KEY)),
set: (value: DaysViewSpan) => userStore.setSetting(DAYS_VIEW_SPAN_KEY, normalizeDaysViewSpan(value)),
})
const agendaViewSpan = computed({
get: () => normalizeAgendaViewSpan(userStore.getSetting(AGENDA_VIEW_SPAN_KEY)),
set: (value: AgendaViewSpan) => userStore.setSetting(AGENDA_VIEW_SPAN_KEY, normalizeAgendaViewSpan(value)),
})
return {
calendarView,
daysViewSpan,
agendaViewSpan,
}
})
-471
View File
@@ -1,471 +0,0 @@
import { computed, ref, shallowRef } from 'vue'
import { defineStore } from 'pinia'
import { useUserStore } from '@KTXC/stores/userStore'
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 type { ServiceObject } from '@ChronoManager/models/service'
import { useCollectionsStore } from '@ChronoManager/stores/collectionsStore'
import { useEntitiesStore } from '@ChronoManager/stores/entitiesStore'
import { useServicesStore } from '@ChronoManager/stores/servicesStore'
import { useImportStore } from '@ChronoManager/stores/importStore'
import type { CalendarView as CalendarViewType } from '@/types'
import {
AGENDA_VIEW_SPANS,
DAYS_VIEW_SPANS,
type AgendaViewSpan,
type DaysViewSpan,
} from '@/types/spans'
type ChronoEntityProperties = EventObject | TaskObject
export const useChronoStore = defineStore('chronoStore', () => {
const userStore = useUserStore()
const servicesStore = useServicesStore()
const collectionsStore = useCollectionsStore()
const entitiesStore = useEntitiesStore()
const importStore = useImportStore()
const savedCalendarView = userStore.getSetting('chrono.calendarView')
const savedDaysViewSpan = userStore.getSetting('chrono.daysViewSpan')
const savedAgendaViewSpan = userStore.getSetting('chrono.agendaViewSpan')
const viewMode = ref<'calendar' | 'tasks'>('calendar')
const calendarView = ref<CalendarViewType>(
savedCalendarView === 'month' || savedCalendarView === 'agenda' || savedCalendarView === 'days'
? savedCalendarView
: 'days',
)
const daysViewSpan = ref<DaysViewSpan>(
typeof savedDaysViewSpan === 'string' && DAYS_VIEW_SPANS.includes(savedDaysViewSpan as DaysViewSpan)
? (savedDaysViewSpan as DaysViewSpan)
: '7d',
)
const agendaViewSpan = ref<AgendaViewSpan>(
typeof savedAgendaViewSpan === 'string' && AGENDA_VIEW_SPANS.includes(savedAgendaViewSpan as AgendaViewSpan)
? (savedAgendaViewSpan as AgendaViewSpan)
: '1w',
)
const currentDate = ref(new Date())
const sidebarVisible = ref(true)
const selectedCollection = shallowRef<CollectionObject | null>(null)
const selectedEntity = shallowRef<EntityObject | null>(null)
const showEventEditor = ref(false)
const showTaskEditor = ref(false)
const showCollectionEditor = ref(false)
const showImportDialog = ref(false)
const entityEditorMode = ref<'edit' | 'view'>('view')
const editingCollection = shallowRef<CollectionObject | null>(null)
const collectionEditorMode = ref<'create' | 'edit'>('create')
const collectionEditorType = ref<'calendar' | 'tasklist'>('calendar')
const loading = ref(false)
const collections = computed(() => collectionsStore.collections)
const entities = computed(() => entitiesStore.entities)
const calendars = computed(() => {
return collectionsStore.collections.filter(col => col.properties.content?.includes('event'))
})
const taskLists = computed(() => {
return collectionsStore.collections.filter(col => col.properties.content?.includes('task'))
})
const events = computed(() => {
return entities.value.filter(entity => entity.properties instanceof EventObject)
})
const tasks = computed(() => {
return entities.value.filter(entity => entity.properties instanceof TaskObject)
})
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(() => tasks.value)
function setViewMode(mode: 'calendar' | 'tasks') {
viewMode.value = mode
}
function setCalendarView(view: CalendarViewType) {
calendarView.value = view
userStore.setSetting('chrono.calendarView', view)
}
function setDaysViewSpan(span: DaysViewSpan) {
if (!DAYS_VIEW_SPANS.includes(span)) {
return
}
daysViewSpan.value = span
userStore.setSetting('chrono.daysViewSpan', span)
}
function setAgendaViewSpan(span: AgendaViewSpan) {
agendaViewSpan.value = span
userStore.setSetting('chrono.agendaViewSpan', span)
}
function selectCalendar(calendar: CollectionObject) {
selectedCollection.value = calendar
}
function selectTaskList(list: CollectionObject) {
selectedCollection.value = list
}
function openCreateCalendar() {
editingCollection.value = new CollectionObject()
editingCollection.value.properties.fromJson({
...editingCollection.value.properties.toJson(),
content: ['event'],
})
collectionEditorMode.value = 'create'
collectionEditorType.value = 'calendar'
showCollectionEditor.value = true
}
function openCreateTaskList() {
editingCollection.value = new CollectionObject()
editingCollection.value.properties.fromJson({
...editingCollection.value.properties.toJson(),
content: ['task'],
})
collectionEditorMode.value = 'create'
collectionEditorType.value = 'tasklist'
showCollectionEditor.value = true
}
function openEditCalendar(collection: CollectionObject) {
editingCollection.value = collection
collectionEditorMode.value = 'edit'
collectionEditorType.value = 'calendar'
showCollectionEditor.value = true
}
function openEditTaskList(collection: CollectionObject) {
editingCollection.value = collection
collectionEditorMode.value = 'edit'
collectionEditorType.value = 'tasklist'
showCollectionEditor.value = true
}
function ensureSelectedCollection(targetType: 'calendar' | 'tasklist'): CollectionObject | null {
const list = targetType === 'calendar' ? calendars.value : taskLists.value
if (!selectedCollection.value && list.length > 0) {
selectedCollection.value = list[0]
}
return selectedCollection.value
}
function createEvent() {
const collection = ensureSelectedCollection('calendar')
if (!collection) {
return
}
const entity = new EntityObject()
entity.properties = new EventObject()
selectedEntity.value = entity
entityEditorMode.value = 'edit'
showEventEditor.value = true
}
function editEvent(entity: EntityObject) {
selectedEntity.value = entity
entityEditorMode.value = 'view'
showEventEditor.value = true
}
function createTask() {
const collection = ensureSelectedCollection('tasklist')
if (!collection) {
return
}
const entity = new EntityObject()
entity.properties = new TaskObject()
selectedEntity.value = entity
entityEditorMode.value = 'edit'
showTaskEditor.value = true
}
function editTask(entity: EntityObject) {
selectedEntity.value = entity
entityEditorMode.value = 'view'
showTaskEditor.value = true
}
function createEventFromDate(date: Date) {
const collection = ensureSelectedCollection('calendar')
if (!collection) {
return
}
const entity = new EntityObject()
entity.properties = new EventObject()
const eventData = entity.properties as EventObject
eventData.startsOn = date.toISOString()
eventData.endsOn = new Date(date.getTime() + 60 * 60 * 1000).toISOString()
selectedEntity.value = entity
entityEditorMode.value = 'edit'
showEventEditor.value = true
}
function startEditingSelectedEntity() {
entityEditorMode.value = 'edit'
}
function cancelEditingSelectedEntity() {
entityEditorMode.value = 'view'
}
function closeEntityEditor() {
selectedEntity.value = null
entityEditorMode.value = 'view'
showEventEditor.value = false
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<EntityObject> {
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.identifier, updated.properties)
}
async function saveEvent(entity: EntityObject, collection?: CollectionObject | null) {
const targetCollection = collection instanceof CollectionObject
? collection
: selectedCollection.value
if (!targetCollection) {
throw new Error('No calendar collection selected')
}
selectedEntity.value = await persistEntity(entity, targetCollection)
selectedCollection.value = targetCollection
entityEditorMode.value = 'view'
}
async function saveTask(entity: EntityObject, collection?: CollectionObject | null) {
const targetCollection = collection instanceof CollectionObject
? collection
: selectedCollection.value
if (!targetCollection) {
throw new Error('No task list selected')
}
selectedEntity.value = await persistEntity(entity, targetCollection)
selectedCollection.value = targetCollection
entityEditorMode.value = 'view'
}
async function deleteEvent(entity: EntityObject, collection?: CollectionObject | null) {
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) {
if (!(collection instanceof CollectionObject) && !selectedCollection.value) {
throw new Error('No task list selected')
}
await entitiesStore.delete([entity.identifier])
closeEntityEditor()
}
async function toggleTaskComplete(taskId: string | number) {
const taskEntity = tasks.value.find(task => task.identifier === taskId)
if (!taskEntity) {
return
}
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) {
if (collectionEditorMode.value === 'create') {
await collectionsStore.create(
service.provider,
service.identifier || '',
collection.properties,
)
} else {
await collectionsStore.update(collection.identifier, collection.properties)
}
showCollectionEditor.value = false
}
async function deleteCollection(collection: CollectionObject) {
await collectionsStore.delete(collection.identifier)
if (selectedCollection.value?.identifier === collection.identifier) {
selectedCollection.value = null
selectedEntity.value = null
}
showCollectionEditor.value = false
}
async function openImport(files: File[]) {
importStore.removeAllFiles()
importStore.reset()
for (const file of files) {
const id = importStore.addFile({ name: file.name, contents: await file.text(), size: file.size, type: file.type })
if (selectedCollection.value && collections.value.some(collection => collection.identifier === selectedCollection.value?.identifier)) {
importStore.setCollectionForFile(id, selectedCollection.value.identifier)
}
}
importStore.stage = 'selecting'
showImportDialog.value = true
}
async function closeImport(refresh = false) {
showImportDialog.value = false
if (refresh) {
const targets = new Set<CollectionObject['identifier']>()
for (const id of importStore.order) {
const target = importStore.sessions[id]?.targetIdentifier
if (target) targets.add(target)
}
for (const target of targets) await entitiesStore.list([target])
}
importStore.removeAllFiles()
importStore.reset()
}
async function initialize() {
loading.value = true
try {
await servicesStore.list()
await collectionsStore.list()
const sources = collections.value.map(collection => collection.identifier)
if (sources.length > 0) {
await entitiesStore.list(sources)
}
if (
selectedCollection.value
&& !collections.value.find(collection => collection.identifier === selectedCollection.value?.identifier)
) {
selectedCollection.value = null
}
} finally {
loading.value = false
}
}
return {
viewMode,
calendarView,
daysViewSpan,
agendaViewSpan,
currentDate,
sidebarVisible,
selectedCollection,
selectedEntity,
showEventEditor,
showTaskEditor,
showCollectionEditor,
showImportDialog,
entityEditorMode,
editingCollection,
collectionEditorMode,
collectionEditorType,
loading,
collections,
entities,
calendars,
taskLists,
events,
tasks,
isTaskView,
filteredEvents,
filteredTasks,
setViewMode,
setCalendarView,
setDaysViewSpan,
setAgendaViewSpan,
selectCalendar,
selectTaskList,
openCreateCalendar,
openCreateTaskList,
openEditCalendar,
openEditTaskList,
createEvent,
editEvent,
createTask,
editTask,
createEventFromDate,
startEditingSelectedEntity,
cancelEditingSelectedEntity,
closeEntityEditor,
toggleCalendarVisibility,
toggleTaskComplete,
saveEvent,
deleteEvent,
saveTask,
deleteTask,
saveCollection,
deleteCollection,
openImport,
closeImport,
initialize,
}
})
+241
View File
@@ -0,0 +1,241 @@
import { computed, ref, shallowRef } from 'vue'
import { defineStore } from 'pinia'
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 type { ServiceObject } from '@ChronoManager/models/service'
import { useChronoOperationsStore } from '@/stores/chronoOperationsStore'
export const useChronoUiStore = defineStore('chronoUiStore', () => {
const operationsStore = useChronoOperationsStore()
const viewMode = ref<'calendar' | 'tasks'>('calendar')
const currentDate = ref(new Date())
const sidebarVisible = ref(true)
const selectedCollection = shallowRef<CollectionObject | null>(null)
const selectedEntity = shallowRef<EntityObject | null>(null)
const showEventEditor = ref(false)
const showTaskEditor = ref(false)
const showCollectionEditor = ref(false)
const showImportDialog = ref(false)
const entityEditorMode = ref<'edit' | 'view'>('view')
const editingCollection = shallowRef<CollectionObject | null>(null)
const collectionEditorMode = ref<'create' | 'edit'>('create')
const collectionEditorType = ref<'calendar' | 'tasklist'>('calendar')
const isTaskView = computed(() => viewMode.value === 'tasks')
function setViewMode(mode: 'calendar' | 'tasks') {
viewMode.value = mode
}
function selectCalendar(calendar: CollectionObject) {
selectedCollection.value = calendar
}
function selectTaskList(list: CollectionObject) {
selectedCollection.value = list
}
function openCollectionEditor(type: 'calendar' | 'tasklist') {
const collection = new CollectionObject()
collection.properties.fromJson({
...collection.properties.toJson(),
content: [type === 'calendar' ? 'event' : 'task'],
})
editingCollection.value = collection
collectionEditorMode.value = 'create'
collectionEditorType.value = type
showCollectionEditor.value = true
}
function openCreateCalendar() {
openCollectionEditor('calendar')
}
function openCreateTaskList() {
openCollectionEditor('tasklist')
}
function openEditCalendar(collection: CollectionObject) {
editingCollection.value = collection
collectionEditorMode.value = 'edit'
collectionEditorType.value = 'calendar'
showCollectionEditor.value = true
}
function openEditTaskList(collection: CollectionObject) {
editingCollection.value = collection
collectionEditorMode.value = 'edit'
collectionEditorType.value = 'tasklist'
showCollectionEditor.value = true
}
function ensureSelectedCollection(type: 'calendar' | 'tasklist'): CollectionObject | null {
const candidates = type === 'calendar' ? operationsStore.calendars : operationsStore.taskLists
if (!selectedCollection.value && candidates.length > 0) selectedCollection.value = candidates[0]
return selectedCollection.value
}
function openEntityEditor(entity: EntityObject, type: 'event' | 'task', mode: 'edit' | 'view') {
selectedEntity.value = entity
entityEditorMode.value = mode
showEventEditor.value = type === 'event'
showTaskEditor.value = type === 'task'
}
function createEventFromDate(date?: Date) {
if (!ensureSelectedCollection('calendar')) return
const entity = new EntityObject()
entity.properties = new EventObject()
if (date) {
entity.properties.startsOn = date.toISOString()
entity.properties.endsOn = new Date(date.getTime() + 60 * 60 * 1000).toISOString()
}
openEntityEditor(entity, 'event', 'edit')
}
function createEvent() {
createEventFromDate()
}
function editEvent(entity: EntityObject) {
openEntityEditor(entity, 'event', 'view')
}
function createTask() {
if (!ensureSelectedCollection('tasklist')) return
const entity = new EntityObject()
entity.properties = new TaskObject()
openEntityEditor(entity, 'task', 'edit')
}
function editTask(entity: EntityObject) {
openEntityEditor(entity, 'task', 'view')
}
function startEditingSelectedEntity() {
entityEditorMode.value = 'edit'
}
function cancelEditingSelectedEntity() {
entityEditorMode.value = 'view'
}
function closeEntityEditor() {
selectedEntity.value = null
entityEditorMode.value = 'view'
showEventEditor.value = false
showTaskEditor.value = false
}
async function saveSelectedEntity(entity: EntityObject, collection: CollectionObject | null | undefined, kind: string) {
const targetCollection = collection instanceof CollectionObject ? collection : selectedCollection.value
if (!targetCollection) throw new Error(`No ${kind} collection selected`)
selectedEntity.value = await operationsStore.saveEntity(entity, targetCollection)
selectedCollection.value = targetCollection
entityEditorMode.value = 'view'
}
async function saveEvent(entity: EntityObject, collection?: CollectionObject | null) {
await saveSelectedEntity(entity, collection, 'calendar')
}
async function saveTask(entity: EntityObject, collection?: CollectionObject | null) {
await saveSelectedEntity(entity, collection, 'task list')
}
async function deleteSelectedEntity(entity: EntityObject, collection: CollectionObject | null | undefined, kind: string) {
if (!(collection instanceof CollectionObject) && !selectedCollection.value) {
throw new Error(`No ${kind} collection selected`)
}
await operationsStore.deleteEntity(entity)
closeEntityEditor()
}
async function deleteEvent(entity: EntityObject, collection?: CollectionObject | null) {
await deleteSelectedEntity(entity, collection, 'calendar')
}
async function deleteTask(entity: EntityObject, collection?: CollectionObject | null) {
await deleteSelectedEntity(entity, collection, 'task list')
}
async function saveCollection(collection: CollectionObject, service: ServiceObject) {
await operationsStore.saveCollection(collection, service, collectionEditorMode.value)
showCollectionEditor.value = false
}
async function deleteCollection(collection: CollectionObject) {
await operationsStore.deleteCollection(collection)
if (selectedCollection.value?.identifier === collection.identifier) {
selectedCollection.value = null
selectedEntity.value = null
}
showCollectionEditor.value = false
}
async function openImport(files: File[]) {
await operationsStore.prepareImport(files, selectedCollection.value)
showImportDialog.value = true
}
async function closeImport(refresh = false) {
showImportDialog.value = false
await operationsStore.finishImport(refresh)
}
async function initialize() {
await operationsStore.initialize()
if (selectedCollection.value && !operationsStore.collections.some(
collection => collection.identifier === selectedCollection.value?.identifier,
)) {
selectedCollection.value = null
}
}
return {
viewMode,
currentDate,
sidebarVisible,
selectedCollection,
selectedEntity,
showEventEditor,
showTaskEditor,
showCollectionEditor,
showImportDialog,
entityEditorMode,
editingCollection,
collectionEditorMode,
collectionEditorType,
isTaskView,
setViewMode,
selectCalendar,
selectTaskList,
openCreateCalendar,
openCreateTaskList,
openEditCalendar,
openEditTaskList,
createEvent,
editEvent,
createTask,
editTask,
createEventFromDate,
startEditingSelectedEntity,
cancelEditingSelectedEntity,
closeEntityEditor,
saveEvent,
deleteEvent,
saveTask,
deleteTask,
saveCollection,
deleteCollection,
openImport,
closeImport,
initialize,
}
})