refactor: split uni store to per responsiblity store
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
+37
-16
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
})
|
||||
@@ -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,
|
||||
}
|
||||
})
|
||||
@@ -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,
|
||||
}
|
||||
})
|
||||
@@ -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,
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user