Initial commit
This commit is contained in:
@@ -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>
|
||||
Reference in New Issue
Block a user