Initial commit

This commit is contained in:
root
2025-12-21 09:59:39 -05:00
committed by Sebastian Krupinski
commit cc4e467cef
36 changed files with 7133 additions and 0 deletions
+277
View File
@@ -0,0 +1,277 @@
<script setup lang="ts">
import { ref, computed } from 'vue'
import type { CollectionObject } from '@ChronoManager/models/collection'
import type { EntityObject } from '@ChronoManager/models/entity'
import type { EventObject } from '@ChronoManager/models/event'
import EventEditorLabel from './editors/EventEditorLabel.vue'
import EventEditorDescription from './editors/EventEditorDescription.vue'
import EventEditorDates from './editors/EventEditorDates.vue'
import EventEditorLocations from './editors/EventEditorLocations.vue'
import EventEditorParticipants from './editors/EventEditorParticipants.vue'
import EventEditorNotifications from './editors/EventEditorNotifications.vue'
import EventEditorOccurrence from './editors/EventEditorOccurrence.vue'
import EventEditorTags from './editors/EventEditorTags.vue'
// Props - wrapped entity with id, data properties
const props = defineProps<{
mode: 'edit' | 'view'
entity: EntityObject | null
collection?: CollectionObject | null
calendars?: CollectionObject[]
}>()
// Emits
const emit = defineEmits<{
'edit': []
'cancel': []
'close': []
'save': [entity: any, collection?: any]
'delete': [entity: any, collection?: any]
}>()
// State
const loading = ref(false)
const saving = ref(false)
const selectedCollectionId = ref(props.entity?.in || props.collection?.id || props.calendars?.[0]?.id || '')
// Computed
const mode = computed(() => props.mode)
const entity = computed(() => props.entity || null)
const entityObject = computed(() => entity.value?.data as EventObject ?? null)
const entityFresh = computed(() => entity.value?.id === null || entity.value?.id === undefined)
const calendarOptions = computed(() =>
(props.calendars || []).map(cal => ({
title: cal.label || 'Unnamed Calendar',
value: cal.id,
}))
)
// Permissions
const isExternalEvent = computed(() => {
return false
})
const canEdit = computed(() => {
return true
})
const canDelete = computed(() => {
return !isExternalEvent.value
})
// UI helpers
const entityIcon = computed(() => 'mdi-calendar')
// Functions
const startEdit = () => {
emit('edit')
}
const cancelEdit = () => {
emit('cancel')
}
const saveEntity = async () => {
saving.value = true
const targetCollection = (props.calendars || []).find(cal => cal.id === selectedCollectionId.value)
emit('save', entity.value!, targetCollection || props.collection)
saving.value = false
}
// Location management
const addLocationPhysical = () => {
(entityObject.value as any)?.addLocationPhysical()
}
const removeLocationPhysical = (key: string) => {
(entityObject.value as any)?.removeLocationPhysical(key)
}
const addLocationVirtual = () => {
(entityObject.value as any)?.addLocationVirtual()
}
const removeLocationVirtual = (key: string) => {
(entityObject.value as any)?.removeLocationVirtual(key)
}
// Participant management
const addParticipant = () => {
(entityObject.value as any)?.addParticipant()
}
const removeParticipant = (key: string) => {
(entityObject.value as any)?.removeParticipant(key)
}
// Notification management
const addNotification = () => {
(entityObject.value as any)?.addNotification()
}
const removeNotification = (key: string) => {
(entityObject.value as any)?.removeNotification(key)
}
</script>
<template>
<v-sheet class="pa-2 text-start overflow-auto" min-height="84vh" max-height="84vh">
<div class="event-editor-header d-flex justify-space-between pa-4">
<div>
<v-icon :icon="entityIcon" class="mr-2" />
{{ entityObject?.label || 'Nothing Selected' }}
</div>
<div v-if="!loading && entity">
<v-btn
v-if="mode === 'view' && canEdit"
icon="mdi-pencil"
size="small"
variant="text"
@click="startEdit"
/>
<v-btn
v-if="!entityFresh && canDelete"
icon="mdi-delete"
size="small"
variant="text"
color="error"
@click="emit('delete', entity!, collection)"
/>
<v-btn
icon="mdi-close"
size="small"
variant="text"
@click="$emit('close')"
/>
</div>
</div>
<div class="event-editor-fields">
<v-divider />
<v-progress-linear v-if="loading" indeterminate color="primary" />
<div v-if="!entity" class="text-center pa-8">
<v-icon icon="mdi-calendar-blank" size="64" color="grey" class="mb-4" />
<p class="text-h6 text-grey">Select an event to view details</p>
</div>
<div v-else class="pa-4">
<v-select
v-if="mode === 'edit' && !entity.id"
v-model="selectedCollectionId"
:items="calendarOptions"
label="Calendar"
variant="outlined"
density="compact"
class="mb-4"
/>
<EventEditorLabel
:mode="mode"
:label="entityObject!.label"
@update:label="entityObject!.label = $event"
/>
<EventEditorDescription
:mode="mode"
:description="entityObject!.description"
@update:description="entityObject!.description = $event"
/>
<EventEditorDates
:mode="mode"
:starts-on="entityObject!.startsOn"
:ends-on="entityObject!.endsOn"
:timeless="entityObject!.timeless"
:time-zone="entityObject!.timeZone"
@update:starts-on="entityObject!.startsOn = $event"
@update:ends-on="entityObject!.endsOn = $event"
@update:timeless="entityObject!.timeless = $event"
@update:time-zone="entityObject!.timeZone = $event"
/>
<EventEditorLocations
:mode="mode"
:locations-physical="entityObject!.locationsPhysical || {}"
:locations-virtual="entityObject!.locationsVirtual || {}"
@add-location-physical="addLocationPhysical"
@remove-location-physical="removeLocationPhysical"
@add-location-virtual="addLocationVirtual"
@remove-location-virtual="removeLocationVirtual"
/>
<EventEditorParticipants
:mode="mode"
:organizer="entityObject!.organizer"
:participants="entityObject!.participants || {}"
@update:organizer="entityObject!.organizer = $event"
@add-participant="addParticipant"
@remove-participant="removeParticipant"
/>
<EventEditorNotifications
:mode="mode"
:notifications="entityObject!.notifications || {}"
@add-notification="addNotification"
@remove-notification="removeNotification"
/>
<EventEditorOccurrence
:mode="mode"
:pattern="entityObject!.pattern"
@update:pattern="entityObject!.pattern = $event"
/>
<EventEditorTags
:mode="mode"
:tags="entityObject!.tags"
@update:tags="entityObject!.tags = $event"
/>
<!-- Metadata for view mode -->
<div v-if="mode === 'view' && entityObject!.created" class="event-editor-section">
<v-divider class="mb-4"></v-divider>
<div class="text-caption text-medium-emphasis">
<div>Created: {{ new Date(entityObject!.created).toLocaleString() }}</div>
<div v-if="entityObject!.modified">Modified: {{ new Date(entityObject!.modified).toLocaleString() }}</div>
<div v-if="isExternalEvent" class="mt-2">
<v-chip size="small" color="info" variant="tonal">
<v-icon start size="small">mdi-account-arrow-right</v-icon>
External Event
</v-chip>
</div>
</div>
</div>
</div>
<v-divider/>
</div>
<div class="event-editor-footer d-flex text-end justify-space-between pa-4">
<v-btn v-if="mode === 'edit'"
variant="text"
@click="cancelEdit"
:disabled="saving">
Cancel
</v-btn>
<v-btn v-if="mode === 'edit'"
color="primary"
variant="elevated"
@click="saveEntity"
:loading="saving">
Save
</v-btn>
</div>
</v-sheet>
</template>
<style scoped>
.event-editor-section {
margin-bottom: 1.5rem;
}
</style>