Merge pull request #1225 from shariquerik/calendar-fixes
This commit is contained in:
commit
0653c2293c
@ -1,78 +1,70 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<!-- Combobox Input -->
|
||||||
class="flex items-center justify-between text-ink-gray-7 [&>div]:w-full"
|
<div class="flex items-center w-full text-ink-gray-8 [&>div]:w-full">
|
||||||
>
|
<ComboboxRoot
|
||||||
<Popover v-model:show="showOptions">
|
:model-value="tempSelection"
|
||||||
<template #target="{ togglePopover }">
|
:open="showOptions"
|
||||||
<TextInput
|
@update:open="(o) => (showOptions = o)"
|
||||||
|
@update:modelValue="onSelect"
|
||||||
|
:ignore-filter="true"
|
||||||
|
>
|
||||||
|
<ComboboxAnchor
|
||||||
|
class="flex w-full text-base items-center gap-1 rounded border border-outline-gray-2 bg-surface-white hover:border-outline-gray-3 focus:border-outline-gray-4 focus:ring-0 focus-visible:ring-2 focus-visible:ring-outline-gray-3 px-2 py-1"
|
||||||
|
:class="[size === 'sm' ? 'h-7' : 'h-8 ', inputClass]"
|
||||||
|
@click="showOptions = true"
|
||||||
|
>
|
||||||
|
<ComboboxInput
|
||||||
ref="search"
|
ref="search"
|
||||||
type="text"
|
autocomplete="off"
|
||||||
:size="size"
|
class="bg-transparent p-0 outline-none border-0 text-base text-ink-gray-8 h-full placeholder:text-ink-gray-4 w-full focus:outline-none focus:ring-0 focus:border-0"
|
||||||
class="w-full"
|
|
||||||
variant="outline"
|
|
||||||
v-model="query"
|
|
||||||
:debounce="300"
|
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
@click="togglePopover"
|
:value="query"
|
||||||
@keydown="onKeydown"
|
@input="onInput"
|
||||||
|
@keydown.enter.prevent="handleEnter"
|
||||||
|
@keydown.escape.stop="showOptions = false"
|
||||||
|
/>
|
||||||
|
<FeatherIcon
|
||||||
|
name="chevron-down"
|
||||||
|
class="h-4 text-ink-gray-5 cursor-pointer"
|
||||||
|
@click.stop="showOptions = !showOptions"
|
||||||
|
/>
|
||||||
|
</ComboboxAnchor>
|
||||||
|
<ComboboxPortal>
|
||||||
|
<ComboboxContent
|
||||||
|
class="z-10 mt-1 min-w-48 w-full max-w-md bg-surface-modal overflow-hidden rounded-lg shadow-2xl ring-1 ring-black ring-opacity-5"
|
||||||
|
position="popper"
|
||||||
|
:align="'start'"
|
||||||
|
@openAutoFocus.prevent
|
||||||
|
@closeAutoFocus.prevent
|
||||||
>
|
>
|
||||||
<template #suffix>
|
<ComboboxViewport class="max-h-60 overflow-auto p-1.5">
|
||||||
<FeatherIcon
|
<ComboboxEmpty
|
||||||
name="chevron-down"
|
|
||||||
class="h-4 text-ink-gray-5"
|
|
||||||
@click.stop="togglePopover()"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</TextInput>
|
|
||||||
</template>
|
|
||||||
<template #body="{ isOpen }">
|
|
||||||
<div v-show="isOpen">
|
|
||||||
<div
|
|
||||||
class="mt-1 rounded-lg bg-surface-modal shadow-2xl ring-1 ring-black ring-opacity-5 focus:outline-none"
|
|
||||||
>
|
|
||||||
<ul
|
|
||||||
v-if="options.length"
|
|
||||||
role="listbox"
|
|
||||||
class="p-1.5 max-h-[12rem] overflow-y-auto"
|
|
||||||
>
|
|
||||||
<li
|
|
||||||
v-for="(option, idx) in options"
|
|
||||||
:key="option.value"
|
|
||||||
role="option"
|
|
||||||
:aria-selected="idx === highlightIndex"
|
|
||||||
@click="selectOption(option)"
|
|
||||||
@mouseenter="highlightIndex = idx"
|
|
||||||
class="flex cursor-pointer items-center rounded px-2 py-1 text-base"
|
|
||||||
:class="{ 'bg-surface-gray-3': idx === highlightIndex }"
|
|
||||||
>
|
|
||||||
<UserAvatar class="mr-2" :user="option.value" size="lg" />
|
|
||||||
<div class="flex flex-col gap-1 p-1 text-ink-gray-8">
|
|
||||||
<div class="text-base font-medium">
|
|
||||||
{{ option.label }}
|
|
||||||
</div>
|
|
||||||
<div class="text-sm text-ink-gray-5">
|
|
||||||
{{ option.value }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div
|
|
||||||
v-else
|
|
||||||
class="flex gap-2 rounded px-2 py-1 text-base text-ink-gray-5"
|
class="flex gap-2 rounded px-2 py-1 text-base text-ink-gray-5"
|
||||||
>
|
>
|
||||||
<FeatherIcon v-if="fetchContacts" name="search" class="h-4" />
|
<FeatherIcon v-if="fetchContacts" name="search" class="h-4" />
|
||||||
{{
|
{{ emptyStateText }}
|
||||||
fetchContacts
|
</ComboboxEmpty>
|
||||||
? __('No results found')
|
<ComboboxItem
|
||||||
: __('Type an email address to add attendee')
|
v-for="option in options"
|
||||||
}}
|
:key="option.value"
|
||||||
</div>
|
:value="option.value"
|
||||||
</div>
|
class="text-base leading-none text-ink-gray-7 rounded flex items-center px-2 py-1 relative select-none data-[highlighted]:outline-none data-[highlighted]:bg-surface-gray-3 cursor-pointer"
|
||||||
</div>
|
@mousedown.prevent="onSelect(option.value, option)"
|
||||||
</template>
|
>
|
||||||
</Popover>
|
<UserAvatar class="mr-2" :user="option.value" size="lg" />
|
||||||
|
<div class="flex flex-col gap-1 p-1 text-ink-gray-8">
|
||||||
|
<div class="text-base font-medium">{{ option.label }}</div>
|
||||||
|
<div class="text-sm text-ink-gray-5">{{ option.value }}</div>
|
||||||
|
</div>
|
||||||
|
</ComboboxItem>
|
||||||
|
</ComboboxViewport>
|
||||||
|
</ComboboxContent>
|
||||||
|
</ComboboxPortal>
|
||||||
|
</ComboboxRoot>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Selected Attendees -->
|
||||||
<div
|
<div
|
||||||
v-if="values.length"
|
v-if="values.length"
|
||||||
class="flex flex-col gap-2 mt-2 max-h-[165px] overflow-y-auto"
|
class="flex flex-col gap-2 mt-2 max-h-[165px] overflow-y-auto"
|
||||||
@ -105,8 +97,18 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import UserAvatar from '@/components/UserAvatar.vue'
|
import UserAvatar from '@/components/UserAvatar.vue'
|
||||||
import { createResource, TextInput, Popover } from 'frappe-ui'
|
import { createResource } from 'frappe-ui'
|
||||||
import { ref, computed, nextTick, watch } from 'vue'
|
import {
|
||||||
|
ComboboxRoot,
|
||||||
|
ComboboxAnchor,
|
||||||
|
ComboboxInput,
|
||||||
|
ComboboxPortal,
|
||||||
|
ComboboxContent,
|
||||||
|
ComboboxViewport,
|
||||||
|
ComboboxItem,
|
||||||
|
ComboboxEmpty,
|
||||||
|
} from 'reka-ui'
|
||||||
|
import { ref, computed, nextTick } from 'vue'
|
||||||
import { watchDebounced } from '@vueuse/core'
|
import { watchDebounced } from '@vueuse/core'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -154,7 +156,7 @@ const query = ref('')
|
|||||||
const text = ref('')
|
const text = ref('')
|
||||||
const showOptions = ref(false)
|
const showOptions = ref(false)
|
||||||
const optionsRef = ref(null)
|
const optionsRef = ref(null)
|
||||||
const highlightIndex = ref(-1)
|
const tempSelection = ref(null)
|
||||||
|
|
||||||
const metaByEmail = computed(() => {
|
const metaByEmail = computed(() => {
|
||||||
const out = {}
|
const out = {}
|
||||||
@ -225,6 +227,12 @@ const options = computed(() => {
|
|||||||
return searchedContacts || []
|
return searchedContacts || []
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const emptyStateText = computed(() =>
|
||||||
|
props.fetchContacts
|
||||||
|
? __('No results found')
|
||||||
|
: __('Type an email address to add attendee'),
|
||||||
|
)
|
||||||
|
|
||||||
function reload(val) {
|
function reload(val) {
|
||||||
if (!props.fetchContacts) return
|
if (!props.fetchContacts) return
|
||||||
|
|
||||||
@ -234,34 +242,38 @@ function reload(val) {
|
|||||||
filterOptions.reload()
|
filterOptions.reload()
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
function onSelect(val, fullOption = null) {
|
||||||
() => options.value,
|
if (!val) return
|
||||||
() => {
|
const optionObj = fullOption ||
|
||||||
highlightIndex.value = options.value.length ? 0 : -1
|
options.value.find((o) => o.value === val) || {
|
||||||
},
|
name: 'new',
|
||||||
)
|
label: val,
|
||||||
|
value: val,
|
||||||
function selectOption(option) {
|
}
|
||||||
if (!option) return
|
addValue(optionObj)
|
||||||
addValue(option)
|
if (!error.value) {
|
||||||
!error.value && (query.value = '')
|
query.value = ''
|
||||||
showOptions.value = false
|
tempSelection.value = null
|
||||||
|
showOptions.value = false
|
||||||
|
nextTick(() => setFocus())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onKeydown(e) {
|
function handleEnter() {
|
||||||
if (e.key === 'Enter') {
|
if (query.value) {
|
||||||
if (highlightIndex.value >= 0 && options.value[highlightIndex.value]) {
|
onSelect(query.value, {
|
||||||
selectOption(options.value[highlightIndex.value])
|
name: 'new',
|
||||||
} else if (query.value) {
|
label: query.value,
|
||||||
// Add entered email directly
|
value: query.value,
|
||||||
selectOption({ name: 'new', label: query.value, value: query.value })
|
})
|
||||||
}
|
|
||||||
e.preventDefault()
|
|
||||||
} else if (e.key === 'Escape') {
|
|
||||||
showOptions.value = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onInput(e) {
|
||||||
|
query.value = e.target.value
|
||||||
|
showOptions.value = true
|
||||||
|
}
|
||||||
|
|
||||||
const addValue = (option) => {
|
const addValue = (option) => {
|
||||||
// Safeguard for falsy option
|
// Safeguard for falsy option
|
||||||
if (!option || !option.value) return
|
if (!option || !option.value) return
|
||||||
@ -315,7 +327,7 @@ const removeValue = (email) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setFocus() {
|
function setFocus() {
|
||||||
search.value.$el.focus()
|
search.value?.focus?.()
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({ setFocus })
|
defineExpose({ setFocus })
|
||||||
|
|||||||
@ -43,7 +43,10 @@
|
|||||||
|
|
||||||
<!-- Event Details -->
|
<!-- Event Details -->
|
||||||
<div v-if="mode == 'details'" class="flex flex-col overflow-y-auto">
|
<div v-if="mode == 'details'" class="flex flex-col overflow-y-auto">
|
||||||
<div class="flex items-start gap-2 px-4.5 py-3 pb-0">
|
<div
|
||||||
|
class="flex items-start gap-2 px-4.5 py-3 pb-0"
|
||||||
|
@dblclick="editDetails"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
class="mx-0.5 my-[5px] size-2.5 rounded-full cursor-pointer"
|
class="mx-0.5 my-[5px] size-2.5 rounded-full cursor-pointer"
|
||||||
:style="{
|
:style="{
|
||||||
@ -289,6 +292,9 @@
|
|||||||
class="w-[220px]"
|
class="w-[220px]"
|
||||||
v-model="_event.referenceDocname"
|
v-model="_event.referenceDocname"
|
||||||
:doctype="_event.referenceDoctype"
|
:doctype="_event.referenceDoctype"
|
||||||
|
:filters="
|
||||||
|
_event.referenceDoctype === 'CRM Lead' ? { converted: 0 } : {}
|
||||||
|
"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
@update:model-value="sync"
|
@update:model-value="sync"
|
||||||
/>
|
/>
|
||||||
@ -329,6 +335,9 @@
|
|||||||
variant="solid"
|
variant="solid"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
:disabled="!dirty"
|
:disabled="!dirty"
|
||||||
|
:loading="
|
||||||
|
mode === 'edit' ? events.setValue.loading : events.insert.loading
|
||||||
|
"
|
||||||
@click="saveEvent"
|
@click="saveEvent"
|
||||||
>
|
>
|
||||||
{{
|
{{
|
||||||
@ -376,7 +385,7 @@ import {
|
|||||||
createDocumentResource,
|
createDocumentResource,
|
||||||
} from 'frappe-ui'
|
} from 'frappe-ui'
|
||||||
import ShortcutTooltip from '@/components/ShortcutTooltip.vue'
|
import ShortcutTooltip from '@/components/ShortcutTooltip.vue'
|
||||||
import { ref, computed, watch, h } from 'vue'
|
import { ref, computed, watch, h, inject } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -402,6 +411,8 @@ const { $dialog } = globalStore()
|
|||||||
const show = defineModel()
|
const show = defineModel()
|
||||||
const event = defineModel('event')
|
const event = defineModel('event')
|
||||||
|
|
||||||
|
const events = inject('events')
|
||||||
|
|
||||||
const _event = ref({})
|
const _event = ref({})
|
||||||
|
|
||||||
const peoples = computed({
|
const peoples = computed({
|
||||||
@ -527,6 +538,8 @@ function updateTime(t, fromTime = false) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function saveEvent() {
|
function saveEvent() {
|
||||||
|
if (!dirty.value) return
|
||||||
|
|
||||||
error.value = null
|
error.value = null
|
||||||
if (!_event.value.title) {
|
if (!_event.value.title) {
|
||||||
error.value = __('Title is required')
|
error.value = __('Title is required')
|
||||||
|
|||||||
@ -56,7 +56,7 @@
|
|||||||
<Dropdown :options="actions(column)">
|
<Dropdown :options="actions(column)">
|
||||||
<template #default>
|
<template #default>
|
||||||
<Button
|
<Button
|
||||||
class="hidden group-hover:flex"
|
class="opacity-0 group-hover:opacity-100 pointer-events-none group-hover:pointer-events-auto transition-opacity"
|
||||||
icon="more-horizontal"
|
icon="more-horizontal"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -115,36 +115,6 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center">
|
|
||||||
<div class="text-base text-ink-gray-7 w-3/12">
|
|
||||||
{{ __('Link') }}
|
|
||||||
</div>
|
|
||||||
<div class="flex gap-2 w-9/12">
|
|
||||||
<FormControl
|
|
||||||
:class="_event.referenceDoctype ? 'w-20' : 'w-full'"
|
|
||||||
type="select"
|
|
||||||
:options="linkDoctypeOptions"
|
|
||||||
v-model="_event.referenceDoctype"
|
|
||||||
variant="outline"
|
|
||||||
:placeholder="__('Add Lead or Deal')"
|
|
||||||
@change="() => (_event.referenceDocname = '')"
|
|
||||||
/>
|
|
||||||
<Link
|
|
||||||
v-if="_event.referenceDoctype"
|
|
||||||
class="w-full"
|
|
||||||
v-model="_event.referenceDocname"
|
|
||||||
:doctype="_event.referenceDoctype"
|
|
||||||
variant="outline"
|
|
||||||
:placeholder="
|
|
||||||
__('Select {0}', [
|
|
||||||
_event.referenceDoctype == 'CRM Lead'
|
|
||||||
? __('Lead')
|
|
||||||
: __('Deal'),
|
|
||||||
])
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-start">
|
<div class="flex items-start">
|
||||||
<div class="text-base text-ink-gray-7 mt-1.5 w-3/12">
|
<div class="text-base text-ink-gray-7 mt-1.5 w-3/12">
|
||||||
{{ __('Attendees') }}
|
{{ __('Attendees') }}
|
||||||
@ -188,6 +158,7 @@
|
|||||||
? __('Duplicate')
|
? __('Duplicate')
|
||||||
: __('Create')
|
: __('Create')
|
||||||
"
|
"
|
||||||
|
:disabled="!dirty"
|
||||||
:loading="
|
:loading="
|
||||||
mode === 'edit'
|
mode === 'edit'
|
||||||
? eventsResource.setValue.loading
|
? eventsResource.setValue.loading
|
||||||
@ -200,7 +171,6 @@
|
|||||||
</Dialog>
|
</Dialog>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import Link from '@/components/Controls/Link.vue'
|
|
||||||
import Attendee from '@/components/Calendar/Attendee.vue'
|
import Attendee from '@/components/Calendar/Attendee.vue'
|
||||||
import {
|
import {
|
||||||
Switch,
|
Switch,
|
||||||
@ -211,7 +181,6 @@ import {
|
|||||||
TimePicker,
|
TimePicker,
|
||||||
dayjs,
|
dayjs,
|
||||||
Dropdown,
|
Dropdown,
|
||||||
FormControl,
|
|
||||||
} from 'frappe-ui'
|
} from 'frappe-ui'
|
||||||
import { globalStore } from '@/stores/global'
|
import { globalStore } from '@/stores/global'
|
||||||
import { validateEmail } from '@/utils'
|
import { validateEmail } from '@/utils'
|
||||||
@ -256,6 +225,7 @@ const mode = computed(() => {
|
|||||||
: 'create'
|
: 'create'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const oldEvent = ref({})
|
||||||
const _event = ref({
|
const _event = ref({
|
||||||
title: '',
|
title: '',
|
||||||
description: '',
|
description: '',
|
||||||
@ -271,6 +241,10 @@ const _event = ref({
|
|||||||
event_participants: [],
|
event_participants: [],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const dirty = computed(() => {
|
||||||
|
return JSON.stringify(_event.value) !== JSON.stringify(oldEvent.value)
|
||||||
|
})
|
||||||
|
|
||||||
const peoples = computed({
|
const peoples = computed({
|
||||||
get() {
|
get() {
|
||||||
return _event.value.event_participants || []
|
return _event.value.event_participants || []
|
||||||
@ -306,6 +280,8 @@ onMounted(() => {
|
|||||||
event_participants: props.event.event_participants || [],
|
event_participants: props.event.event_participants || [],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
oldEvent.value = JSON.parse(JSON.stringify(_event.value))
|
||||||
|
|
||||||
setTimeout(() => title.value?.el?.focus(), 100)
|
setTimeout(() => title.value?.el?.focus(), 100)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -376,8 +352,6 @@ function createEvent() {
|
|||||||
color: _event.value.color,
|
color: _event.value.color,
|
||||||
reference_doctype: props.doctype,
|
reference_doctype: props.doctype,
|
||||||
reference_docname: props.docname,
|
reference_docname: props.docname,
|
||||||
reference_doctype: _event.value.referenceDoctype || props.doctype,
|
|
||||||
reference_docname: _event.value.referenceDocname || props.docname,
|
|
||||||
event_participants: _event.value.event_participants,
|
event_participants: _event.value.event_participants,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -407,8 +381,6 @@ function updateEvent() {
|
|||||||
color: _event.value.color,
|
color: _event.value.color,
|
||||||
reference_doctype: props.doctype,
|
reference_doctype: props.doctype,
|
||||||
reference_docname: props.docname,
|
reference_docname: props.docname,
|
||||||
reference_doctype: _event.value.referenceDoctype || props.doctype,
|
|
||||||
reference_docname: _event.value.referenceDocname || props.docname,
|
|
||||||
event_participants: _event.value.event_participants,
|
event_participants: _event.value.event_participants,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -455,12 +427,6 @@ function deleteEvent() {
|
|||||||
|
|
||||||
const toOptions = computed(() => buildEndTimeOptions(_event.value.fromTime))
|
const toOptions = computed(() => buildEndTimeOptions(_event.value.fromTime))
|
||||||
|
|
||||||
const linkDoctypeOptions = [
|
|
||||||
{ label: '', value: '' },
|
|
||||||
{ label: __('Lead'), value: 'CRM Lead' },
|
|
||||||
{ label: __('Deal'), value: 'CRM Deal' },
|
|
||||||
]
|
|
||||||
|
|
||||||
const colors = Object.keys(colorMap).map((c) => ({
|
const colors = Object.keys(colorMap).map((c) => ({
|
||||||
label: c.charAt(0).toUpperCase() + c.slice(1),
|
label: c.charAt(0).toUpperCase() + c.slice(1),
|
||||||
value: colorMap[c].color,
|
value: colorMap[c].color,
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { onMounted, onBeforeUnmount, unref } from 'vue'
|
import { onMounted, onBeforeUnmount, unref } from 'vue'
|
||||||
|
import { isDialogOpen } from '@/utils/dialogs'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic global keyboard shortcuts composable.
|
* Generic global keyboard shortcuts composable.
|
||||||
@ -20,6 +21,7 @@ export function useKeyboardShortcuts(options) {
|
|||||||
shortcuts = [],
|
shortcuts = [],
|
||||||
ignoreTyping = true,
|
ignoreTyping = true,
|
||||||
target = typeof window !== 'undefined' ? window : null,
|
target = typeof window !== 'undefined' ? window : null,
|
||||||
|
skipWhenDialogOpen = true,
|
||||||
} = options || {}
|
} = options || {}
|
||||||
|
|
||||||
function isTypingEvent(e) {
|
function isTypingEvent(e) {
|
||||||
@ -49,6 +51,7 @@ export function useKeyboardShortcuts(options) {
|
|||||||
const isActive = typeof active === 'function' ? active() : unref(active)
|
const isActive = typeof active === 'function' ? active() : unref(active)
|
||||||
if (!isActive) return
|
if (!isActive) return
|
||||||
if (isTypingEvent(e)) return
|
if (isTypingEvent(e)) return
|
||||||
|
if (skipWhenDialogOpen && isDialogOpen()) return
|
||||||
|
|
||||||
for (const def of shortcuts) {
|
for (const def of shortcuts) {
|
||||||
if (!def) continue
|
if (!def) continue
|
||||||
|
|||||||
@ -18,7 +18,6 @@
|
|||||||
</LayoutHeader>
|
</LayoutHeader>
|
||||||
<div class="flex h-screen overflow-hidden">
|
<div class="flex h-screen overflow-hidden">
|
||||||
<Calendar
|
<Calendar
|
||||||
v-if="events.data?.length"
|
|
||||||
class="flex-1 overflow-hidden"
|
class="flex-1 overflow-hidden"
|
||||||
ref="calendar"
|
ref="calendar"
|
||||||
:config="{
|
:config="{
|
||||||
@ -93,11 +92,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #daily-header="{ parseDateWithDay, currentDate }">
|
|
||||||
<p class="ml-4 pb-2 text-base text-ink-gray-6">
|
|
||||||
{{ parseDateWithDay(currentDate) }}
|
|
||||||
</p>
|
|
||||||
</template>
|
|
||||||
</Calendar>
|
</Calendar>
|
||||||
|
|
||||||
<!-- Event Panel Container -->
|
<!-- Event Panel Container -->
|
||||||
@ -144,7 +138,7 @@ import {
|
|||||||
CalendarActiveEvent as activeEvent,
|
CalendarActiveEvent as activeEvent,
|
||||||
call,
|
call,
|
||||||
} from 'frappe-ui'
|
} from 'frappe-ui'
|
||||||
import { onMounted, ref, computed } from 'vue'
|
import { onMounted, ref, computed, provide } from 'vue'
|
||||||
|
|
||||||
const { user } = sessionStore()
|
const { user } = sessionStore()
|
||||||
const { $dialog } = globalStore()
|
const { $dialog } = globalStore()
|
||||||
@ -188,6 +182,8 @@ const events = createListResource({
|
|||||||
})),
|
})),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
provide('events', events)
|
||||||
|
|
||||||
const eventPanel = ref(null)
|
const eventPanel = ref(null)
|
||||||
const showEventPanel = ref(false)
|
const showEventPanel = ref(false)
|
||||||
const event = ref({})
|
const event = ref({})
|
||||||
@ -248,6 +244,9 @@ function createEvent(_event) {
|
|||||||
async function updateEvent(_event, afterDrag = false) {
|
async function updateEvent(_event, afterDrag = false) {
|
||||||
if (!_event.id) return
|
if (!_event.id) return
|
||||||
|
|
||||||
|
_event.fromTime = dayjs(_event.fromTime, 'HH:mm').format('HH:mm')
|
||||||
|
_event.toTime = dayjs(_event.toTime, 'HH:mm').format('HH:mm')
|
||||||
|
|
||||||
if (
|
if (
|
||||||
['duplicate', 'new'].includes(mode.value) &&
|
['duplicate', 'new'].includes(mode.value) &&
|
||||||
!['duplicate-event', 'new-event'].includes(_event.id) &&
|
!['duplicate-event', 'new-event'].includes(_event.id) &&
|
||||||
|
|||||||
@ -3,6 +3,10 @@ import { reactive, ref } from 'vue'
|
|||||||
|
|
||||||
let dialogs = ref([])
|
let dialogs = ref([])
|
||||||
|
|
||||||
|
export function isDialogOpen() {
|
||||||
|
return dialogs.value.some((d) => d.show)
|
||||||
|
}
|
||||||
|
|
||||||
export let Dialogs = {
|
export let Dialogs = {
|
||||||
name: 'Dialogs',
|
name: 'Dialogs',
|
||||||
render() {
|
render() {
|
||||||
@ -18,9 +22,7 @@ export let Dialogs = {
|
|||||||
dialog.message && (
|
dialog.message && (
|
||||||
<p class="text-p-base text-ink-gray-7">{dialog.message}</p>
|
<p class="text-p-base text-ink-gray-7">{dialog.message}</p>
|
||||||
),
|
),
|
||||||
dialog.html && (
|
dialog.html && <div v-html={dialog.html} />,
|
||||||
<div v-html={dialog.html} />
|
|
||||||
),
|
|
||||||
<ErrorMessage class="mt-2" message={dialog.error} />,
|
<ErrorMessage class="mt-2" message={dialog.error} />,
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user