Files
chrono/src/pages/Main.vue
T
2026-07-14 17:40:15 -04:00

333 lines
8.9 KiB
Vue

<script setup lang="ts">
import { computed, onMounted, watch } from 'vue';
import { storeToRefs } from 'pinia';
import { useRoute } from 'vue-router';
import { useModuleStore } from '@KTXC/stores/moduleStore';
import { useChronoOperationsStore } from '@/stores/chronoOperationsStore';
import { useChronoSettingsStore } from '@/stores/chronoSettingsStore';
import { useChronoUiStore } from '@/stores/chronoUiStore';
import type { CalendarView as CalendarViewType } from '@/types';
import { spanToDays, type AgendaViewSpan, type DaysViewSpan } from '@/types/spans';
import { daysVisibleRange, monthGridVisibleRange } from '@/utils/date';
import Sidebar from '@/components/Sidebar.vue';
import CollectionEditor from '@/components/CollectionEditor.vue';
import CalendarView from '@/components/CalendarView.vue';
import TaskView from '@/components/TaskView.vue';
import EventEditor from '@/components/EventEditor.vue';
import TaskEditor from '@/components/TaskEditor.vue';
import ImportDialog from '@/components/ImportDialog.vue';
import { EpochSpan } from '@/types/date';
// Check if chrono manager is available
const moduleStore = useModuleStore();
const isChronoManagerAvailable = computed(() => {
return moduleStore.has('chrono_manager') || moduleStore.has('ChronoManager')
});
const chronoOperationsStore = useChronoOperationsStore();
const chronoSettingsStore = useChronoSettingsStore();
const chronoUiStore = useChronoUiStore();
const route = useRoute();
const {
calendarView,
daysViewSpan,
agendaViewSpan,
} = storeToRefs(chronoSettingsStore);
const {
loading,
calendars,
taskLists,
filteredTasks,
collections,
} = storeToRefs(chronoOperationsStore);
const {
currentDate,
sidebarVisible,
selectedCollection,
showEventEditor,
showTaskEditor,
showCollectionEditor,
showImportDialog,
selectedEntity,
entityEditorMode,
editingCollection,
collectionEditorMode,
collectionEditorType,
isTasksSection,
} = storeToRefs(chronoUiStore);
const { loadVisibleRange: loadVisibleRangeOperations } = chronoOperationsStore;
const {
initialize,
selectSection,
selectDate,
createEventFromDate,
startEditingSelectedEntity,
cancelEditingSelectedEntity,
closeEntityEditor,
editEvent,
editTask,
saveEvent,
deleteEvent,
saveTask,
deleteTask,
saveCollection,
deleteCollection,
closeImport,
} = chronoUiStore;
const {
toggleTaskComplete,
ensureTasksLoaded,
} = chronoOperationsStore;
watch(() => route.fullPath, async () => {
selectSectionFromRoute();
if (isTasksSection.value) {
await ensureTasksLoaded();
}
});
watch(
[calendarView, currentDate, daysViewSpan, agendaViewSpan],
() => { loadVisibleRangeOperations(computeVisibleRange()); },
{ immediate: true },
);
onMounted(async () => {
try {
selectSectionFromRoute();
await initialize(isTasksSection.value ? 'tasks' : 'calendar');
} catch (error) {
console.error('[Chrono] - Failed to load data from Chrono Manager:', error);
}
});
function handleCalendarViewChange(view: CalendarViewType) {
chronoSettingsStore.calendarView = view;
}
function handleCalendarDaysViewSpanChange(span: DaysViewSpan) {
chronoSettingsStore.daysViewSpan = span;
}
function handleCalendarAgendaViewSpanChange(span: AgendaViewSpan) {
chronoSettingsStore.agendaViewSpan = span;
}
function computeVisibleRange(): EpochSpan {
if (calendarView.value === 'month') {
return monthGridVisibleRange(currentDate.value);
}
const span = calendarView.value === 'agenda'
? agendaViewSpan.value
: daysViewSpan.value;
return daysVisibleRange(currentDate.value, spanToDays(span));
}
function selectSectionFromRoute() {
const routePath = route.path;
const section = routePath.endsWith('/tasks') ? 'tasks' : 'calendar';
selectSection(section);
}
</script>
<template>
<div class="chrono-container">
<!-- Top Navigation Bar -->
<v-toolbar elevation="0" density="compact" class="chrono-toolbar">
<template #prepend>
<v-btn
icon="mdi-menu"
variant="text"
@click="sidebarVisible = !sidebarVisible"
></v-btn>
</template>
<v-toolbar-title class="d-flex align-center">
<v-icon size="24" color="primary" class="mr-2">mdi-calendar-month</v-icon>
<span class="text-subtitle-1 font-weight-bold">Chrono</span>
</v-toolbar-title>
<v-spacer></v-spacer>
<!-- View Switcher for Calendar -->
<v-btn-toggle
v-if="!isTasksSection"
: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>
<v-btn value="agenda" size="small">Agenda</v-btn>
</v-btn-toggle>
</v-toolbar>
<!-- Main Content Area -->
<div class="chrono-content">
<!-- Sidebar Navigation Drawer -->
<Sidebar />
<!-- Main Calendar/Task View -->
<div class="chrono-main">
<v-alert
v-if="!isChronoManagerAvailable"
type="warning"
variant="tonal"
closable
class="mb-4"
>
<v-alert-title class="d-flex align-center">
<v-icon icon="mdi-alert-circle" class="mr-2" />
Chrono Manager Not Available
</v-alert-title>
<div class="mt-2">
<p>
The Chrono Manager module is not installed or enabled.
This module requires the <strong>chrono_manager</strong> module to function properly.
</p>
<p class="mt-2 mb-0">
Please contact your system administrator to install and enable the
<code>chrono_manager</code> module.
</p>
</div>
</v-alert>
<div v-if="isChronoManagerAvailable" class="chrono-wrapper">
<v-progress-linear v-if="loading" indeterminate color="primary" />
<CalendarView
v-if="!isTasksSection"
:view="calendarView"
:current-date="currentDate"
:calendars="calendars"
:initial-days-span="daysViewSpan"
:initial-agenda-view-span="agendaViewSpan"
@event-click="editEvent"
@date-click="createEventFromDate"
@date-range-select="createEventFromDate($event.start, $event.end)"
@update:current-date="selectDate"
@update:days-span="handleCalendarDaysViewSpanChange"
@update:agenda-span="handleCalendarAgendaViewSpanChange"
/>
<TaskView
v-else
:tasks="filteredTasks"
:lists="taskLists"
@task-click="editTask"
@toggle-complete="toggleTaskComplete"
/>
</div>
</div>
</div>
<!-- Event Editor Dialog -->
<v-dialog v-model="showEventEditor" max-width="800px" scrollable>
<EventEditor
v-if="showEventEditor"
:mode="entityEditorMode"
:entity="selectedEntity"
:collection="selectedCollection"
:calendars="calendars"
@save="saveEvent"
@delete="deleteEvent"
@edit="startEditingSelectedEntity"
@cancel="cancelEditingSelectedEntity"
@close="closeEntityEditor"
/>
</v-dialog>
<!-- Task Editor Dialog -->
<v-dialog v-model="showTaskEditor" max-width="700px" scrollable>
<TaskEditor
v-if="showTaskEditor"
:mode="entityEditorMode"
:entity="selectedEntity"
:collection="selectedCollection"
:lists="taskLists"
@save="saveTask"
@delete="deleteTask"
@edit="startEditingSelectedEntity"
@cancel="cancelEditingSelectedEntity"
@close="closeEntityEditor"
/>
</v-dialog>
<!-- Collection Editor Dialog -->
<CollectionEditor
v-model="showCollectionEditor"
:collection="editingCollection"
:mode="collectionEditorMode"
:type="collectionEditorType"
@save="saveCollection"
@delete="deleteCollection"
/>
<ImportDialog v-model="showImportDialog" :collections="collections" @close="closeImport" />
</div>
</template>
<style scoped>
.chrono-container {
display: flex;
flex-direction: column;
height: 100%;
min-height: 0;
isolation: isolate; /* Create stacking context to prevent style leakage */
}
.chrono-toolbar {
flex-shrink: 0;
border-bottom: 1px solid rgb(var(--v-border-color)) !important;
}
.chrono-content {
display: flex;
flex: 1;
min-height: 0;
overflow: hidden;
position: relative;
}
.chrono-main {
flex: 1;
min-height: 0;
min-width: 0;
overflow: hidden;
display: flex;
flex-direction: column;
}
.chrono-wrapper {
display: flex;
flex: 1;
min-height: 0;
flex-direction: column;
}
/* Responsive adjustments */
@media (max-width: 960px) {
.chrono-main {
padding: 16px;
}
}
@media (max-width: 600px) {
.chrono-main {
padding: 12px;
}
}
</style>