mirror of
https://github.com/hcengineering/platform.git
synced 2025-05-10 01:15:03 +00:00
Select default meeting room in public schedule (#8732)
Signed-off-by: Nikolay Chunosov <Chunosov.N@gmail.com>
This commit is contained in:
parent
3d8c358845
commit
c31815e19f
@ -35,6 +35,7 @@ import {
|
||||
type Meeting,
|
||||
type MeetingMinutes,
|
||||
type MeetingStatus,
|
||||
type MeetingSchedule,
|
||||
type Office,
|
||||
type ParticipantInfo,
|
||||
type RequestStatus,
|
||||
@ -61,7 +62,7 @@ import {
|
||||
UX,
|
||||
TypeBoolean
|
||||
} from '@hcengineering/model'
|
||||
import calendar, { TEvent } from '@hcengineering/model-calendar'
|
||||
import calendar, { TEvent, TSchedule } from '@hcengineering/model-calendar'
|
||||
import core, { TAttachedDoc, TDoc } from '@hcengineering/model-core'
|
||||
import preference, { TPreference } from '@hcengineering/model-preference'
|
||||
import presentation from '@hcengineering/model-presentation'
|
||||
@ -252,6 +253,11 @@ export class TMeetingMinutes extends TAttachedDoc implements MeetingMinutes, Tod
|
||||
todos?: CollectionSize<ToDo>
|
||||
}
|
||||
|
||||
@Mixin(love.mixin.MeetingSchedule, calendar.class.Schedule)
|
||||
export class TMeetingSchedule extends TSchedule implements MeetingSchedule {
|
||||
room!: Ref<Room>
|
||||
}
|
||||
|
||||
export default love
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
@ -265,7 +271,8 @@ export function createModel (builder: Builder): void {
|
||||
TRoomInfo,
|
||||
TInvite,
|
||||
TMeeting,
|
||||
TMeetingMinutes
|
||||
TMeetingMinutes,
|
||||
TMeetingSchedule
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
@ -325,6 +332,19 @@ export function createModel (builder: Builder): void {
|
||||
component: love.component.EditMeetingData
|
||||
})
|
||||
|
||||
builder.createDoc(presentation.class.DocCreateExtension, core.space.Model, {
|
||||
ofClass: calendar.class.Schedule,
|
||||
apply: love.function.CreateMeetingSchedule,
|
||||
components: {
|
||||
body: love.component.MeetingScheduleData
|
||||
}
|
||||
})
|
||||
|
||||
builder.createDoc(presentation.class.ComponentPointExtension, core.space.Model, {
|
||||
extension: calendar.extensions.EditScheduleExtensions,
|
||||
component: love.component.EditMeetingScheduleData
|
||||
})
|
||||
|
||||
builder.createDoc(presentation.class.ComponentPointExtension, core.space.Model, {
|
||||
extension: media.extension.StateContext,
|
||||
component: love.component.MediaPopupItemExt
|
||||
|
@ -8,6 +8,8 @@
|
||||
import { Label, Modal, CheckBox, Spinner, Button, IconCopy, EditBox } from '@hcengineering/ui'
|
||||
import calendar from '@hcengineering/calendar'
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
import { slide } from 'svelte/transition'
|
||||
import { quintOut } from 'svelte/easing'
|
||||
import { getMetadata } from '@hcengineering/platform'
|
||||
import { getCurrentAccount, pickPrimarySocialId, SocialId, SocialIdType } from '@hcengineering/core'
|
||||
import { getAccountClient } from '../utils'
|
||||
@ -221,8 +223,12 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if !wasAccessEnabled}
|
||||
<div class="flex-col-stretch flex-gap-1-5" class:accessDisabled={!accessEnabled}>
|
||||
{#if !wasAccessEnabled && !loading}
|
||||
<div
|
||||
class="flex-col-stretch flex-gap-1-5"
|
||||
class:accessDisabled={!accessEnabled}
|
||||
transition:slide={{ duration: 300, easing: quintOut }}
|
||||
>
|
||||
<Label label={calendar.string.CalDavAccessPassword} />
|
||||
<div class="flex-row-center flex-gap-1">
|
||||
<EditBox bind:value={password} kind="ghost" format="password" fullSize focusable disabled={true} />
|
||||
@ -236,7 +242,9 @@
|
||||
/>
|
||||
</div>
|
||||
{#if canSave}
|
||||
<Label label={calendar.string.CalDavAccessPasswordWarning} />
|
||||
<div class:accessDisabled={!accessEnabled} transition:slide={{ duration: 300, easing: quintOut }}>
|
||||
<Label label={calendar.string.CalDavAccessPasswordWarning} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
@ -15,8 +15,14 @@
|
||||
//
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Data, generateUuid, Ref } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import core, { Data, generateId, generateUuid, Ref, Space } from '@hcengineering/core'
|
||||
import {
|
||||
ComponentExtensions,
|
||||
createQuery,
|
||||
DocCreateExtComponent,
|
||||
DocCreateExtensionManager,
|
||||
getClient
|
||||
} from '@hcengineering/presentation'
|
||||
import type { Schedule, ScheduleAvailability } from '@hcengineering/calendar'
|
||||
import ui, {
|
||||
Button,
|
||||
@ -51,12 +57,20 @@
|
||||
|
||||
export let schedule: Schedule | undefined
|
||||
|
||||
const docCreateManager = DocCreateExtensionManager.create(calendar.class.Schedule)
|
||||
|
||||
type EditableAvailability = Record<number, { start: Date, end: Date }[]>
|
||||
|
||||
const manager = createFocusManager()
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
|
||||
const spaceQ = createQuery()
|
||||
let space: Space | undefined = undefined
|
||||
spaceQ.query(core.class.Space, { _id: calendar.space.Calendar }, (res) => {
|
||||
space = res[0]
|
||||
})
|
||||
|
||||
let title = schedule?.title ?? ''
|
||||
let description = schedule?.description ?? ''
|
||||
let meetingDuration = schedule?.meetingDuration ?? 30 * 60_000
|
||||
@ -175,6 +189,7 @@
|
||||
|
||||
async function saveSchedule (): Promise<void> {
|
||||
if (schedule === undefined) {
|
||||
const _id = generateId<Schedule>()
|
||||
const currentUser = getCurrentEmployee()
|
||||
const data: Data<Schedule> = {
|
||||
owner: currentUser,
|
||||
@ -185,8 +200,10 @@
|
||||
availability: getStorableAvailability(),
|
||||
timeZone
|
||||
}
|
||||
const id = generateUuid() as Ref<Schedule>
|
||||
await client.createDoc(calendar.class.Schedule, calendar.space.Calendar, data, id)
|
||||
await client.createDoc(calendar.class.Schedule, calendar.space.Calendar, data, _id)
|
||||
if (space !== undefined) {
|
||||
await docCreateManager.commit(client, _id, space, {}, 'post')
|
||||
}
|
||||
} else {
|
||||
await client.update(schedule, {
|
||||
title,
|
||||
@ -299,7 +316,7 @@
|
||||
bind:content={description}
|
||||
/>
|
||||
</div>
|
||||
<div class="block rightCropPadding">
|
||||
<div class="block">
|
||||
<div class="flex-row-top flex-gap-1">
|
||||
<Icon icon={calendar.icon.Duration} size={'small'} />
|
||||
<div class="prop">
|
||||
@ -312,7 +329,7 @@
|
||||
on:click={showDurationVariants}
|
||||
/>
|
||||
</div>
|
||||
<div class="prop">
|
||||
<div class="prop" style="margin-left: 2rem; margin-right: 1rem">
|
||||
<Label label={calendar.string.MeetingInterval} />
|
||||
<Button
|
||||
focusIndex={10005}
|
||||
@ -324,18 +341,18 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="block rightCropPadding">
|
||||
<div class="block">
|
||||
<div class="flex-row-center flex-gap-1-5">
|
||||
<Icon icon={calendar.icon.Globe} size={'small'} />
|
||||
<TimeZoneSelector bind:timeZone />
|
||||
<TimeZoneSelector bind:timeZone flex="1" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="block rightCropPadding">
|
||||
<div class="block">
|
||||
<div class="flex-row-top flex-gap-1">
|
||||
<Icon icon={calendar.icon.Timer} size={'small'} />
|
||||
<div class="prop">
|
||||
<Label label={calendar.string.ScheduleAvailability} />
|
||||
{#each getWeekDayNames() as { weekDay, dayName }, i}
|
||||
{#each getWeekDayNames() as { weekDay, dayName }}
|
||||
<div class="flex-row-center flex-gap-1 availability">
|
||||
<span class="weekDay">
|
||||
{dayName}
|
||||
@ -375,6 +392,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="block">
|
||||
{#if schedule === undefined}
|
||||
<DocCreateExtComponent manager={docCreateManager} kind={'body'} />
|
||||
{:else}
|
||||
<ComponentExtensions extension={calendar.extensions.EditScheduleExtensions} props={{ value: schedule }} />
|
||||
{/if}
|
||||
</div>
|
||||
</Scroller>
|
||||
<div class="antiDivider noMargin" />
|
||||
<div class="flex-between p-5 flex-no-shrink">
|
||||
@ -410,18 +434,15 @@
|
||||
flex-shrink: 0;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
flex-direction: column;
|
||||
padding: 0.75rem 1rem 0.75rem 1.25rem;
|
||||
|
||||
&:not(:last-child) {
|
||||
border-bottom: 1px solid var(--theme-divider-color);
|
||||
}
|
||||
&:not(.rightCropPadding) {
|
||||
padding: 0.75rem 1.25rem;
|
||||
}
|
||||
&.rightCropPadding {
|
||||
padding: 0.75rem 1rem 0.75rem 1.25rem;
|
||||
}
|
||||
&.row {
|
||||
padding: 0 1.25rem 0.5rem;
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
|
||||
@ -429,10 +450,14 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
padding: 0rem 0.75rem 0rem 0.75rem;
|
||||
flex: 1;
|
||||
padding: 0rem 0rem 0rem 0.75rem;
|
||||
gap: 0.5rem;
|
||||
|
||||
.availability {
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
@ -441,7 +466,6 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
width: 11rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -453,7 +477,7 @@
|
||||
}
|
||||
|
||||
.weekDay {
|
||||
width: 3rem;
|
||||
width: 2.25rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -26,6 +26,7 @@
|
||||
|
||||
export let timeZone: string
|
||||
export let disabled: boolean = false
|
||||
export let flex: string | undefined = undefined
|
||||
|
||||
function open (e: MouseEvent) {
|
||||
if (disabled) {
|
||||
@ -56,7 +57,15 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Button {disabled} label={calendar.string.TimeZone} kind={'ghost'} padding={'0 .5rem'} justify={'left'} on:click={open}>
|
||||
<Button
|
||||
{disabled}
|
||||
label={calendar.string.TimeZone}
|
||||
kind={'ghost'}
|
||||
padding={'0 .5rem'}
|
||||
justify={'left'}
|
||||
{flex}
|
||||
on:click={open}
|
||||
>
|
||||
<svelte:fragment slot="content">
|
||||
<span class="ml-2 content-darker-color">
|
||||
{getTimeZoneName(timeZone)}
|
||||
|
@ -274,7 +274,8 @@ const calendarPlugin = plugin(calendarId, {
|
||||
CalDavServerURL: '' as Metadata<string>
|
||||
},
|
||||
extensions: {
|
||||
EditEventExtensions: '' as ComponentExtensionId
|
||||
EditEventExtensions: '' as ComponentExtensionId,
|
||||
EditScheduleExtensions: '' as ComponentExtensionId
|
||||
},
|
||||
ids: {
|
||||
ReminderNotification: '' as Ref<NotificationType>,
|
||||
|
@ -0,0 +1,40 @@
|
||||
<!--
|
||||
// Copyright © 2025 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Schedule } from '@hcengineering/calendar'
|
||||
import love from '../plugin'
|
||||
import RoomSelector from './RoomSelector.svelte'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { Room } from '@hcengineering/love'
|
||||
|
||||
export let value: Schedule
|
||||
|
||||
const client = getClient()
|
||||
|
||||
$: isMeetingSchedule = client.getHierarchy().hasMixin(value, love.mixin.MeetingSchedule)
|
||||
$: meetingSchedule = isMeetingSchedule ? client.getHierarchy().as(value, love.mixin.MeetingSchedule) : null
|
||||
|
||||
async function changeRoom (val: Ref<Room>): Promise<void> {
|
||||
const schedules = await client.findAll(value._class, { _id: value._id }, { projection: { _id: 1 } })
|
||||
for (const schedule of schedules) {
|
||||
await client.updateMixin(schedule._id, schedule._class, schedule.space, love.mixin.MeetingSchedule, { room: val })
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if isMeetingSchedule && meetingSchedule}
|
||||
<RoomSelector value={meetingSchedule?.room} on:change={(ev) => changeRoom(ev.detail)} />
|
||||
{/if}
|
@ -0,0 +1,33 @@
|
||||
<!--
|
||||
// Copyright © 2025 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { Room } from '@hcengineering/love'
|
||||
import { Writable } from 'svelte/store'
|
||||
import RoomSelector from './RoomSelector.svelte'
|
||||
|
||||
export let state: Writable<Record<string, any>>
|
||||
|
||||
function changeRoom (val: Ref<Room>): void {
|
||||
$state.room = val
|
||||
}
|
||||
</script>
|
||||
|
||||
<RoomSelector
|
||||
value={$state.room}
|
||||
on:change={(ev) => {
|
||||
changeRoom(ev.detail)
|
||||
}}
|
||||
/>
|
@ -26,10 +26,13 @@ import MeetingMinutesStatusPresenter from './components/MeetingMinutesStatusPres
|
||||
import RoomLanguageEditor from './components/RoomLanguageEditor.svelte'
|
||||
import MediaPopupItemExt from './components/MediaPopupItemExt.svelte'
|
||||
import SharingStateIndicator from './components/SharingStateIndicator.svelte'
|
||||
import MeetingScheduleData from './components/MeetingScheduleData.svelte'
|
||||
import EditMeetingScheduleData from './components/EditMeetingScheduleData.svelte'
|
||||
|
||||
import {
|
||||
copyGuestLink,
|
||||
createMeeting,
|
||||
createMeetingSchedule,
|
||||
showRoomSettings,
|
||||
startTranscription,
|
||||
stopTranscription,
|
||||
@ -66,10 +69,13 @@ export default async (): Promise<Resources> => ({
|
||||
MeetingMinutesStatusPresenter,
|
||||
RoomLanguageEditor,
|
||||
MediaPopupItemExt,
|
||||
SharingStateIndicator
|
||||
SharingStateIndicator,
|
||||
MeetingScheduleData,
|
||||
EditMeetingScheduleData
|
||||
},
|
||||
function: {
|
||||
CreateMeeting: createMeeting,
|
||||
CreateMeetingSchedule: createMeetingSchedule,
|
||||
CanShowRoomSettings: () => {
|
||||
if (!hasAccountRole(getCurrentAccount(), AccountRole.User)) {
|
||||
return
|
||||
|
@ -34,10 +34,13 @@ export default mergeIds(loveId, love, {
|
||||
FloorView: '' as AnyComponent,
|
||||
PanelControlBar: '' as AnyComponent,
|
||||
MeetingMinutesDocEditor: '' as AnyComponent,
|
||||
MeetingMinutesStatusPresenter: '' as AnyComponent
|
||||
MeetingMinutesStatusPresenter: '' as AnyComponent,
|
||||
MeetingScheduleData: '' as AnyComponent,
|
||||
EditMeetingScheduleData: '' as AnyComponent
|
||||
},
|
||||
function: {
|
||||
CreateMeeting: '' as Resource<DocCreateFunction>,
|
||||
CreateMeetingSchedule: '' as Resource<DocCreateFunction>,
|
||||
CanShowRoomSettings: '' as Resource<ViewActionAvailabilityFunction>,
|
||||
CanCopyGuestLink: '' as Resource<ViewActionAvailabilityFunction>
|
||||
},
|
||||
|
@ -1,7 +1,7 @@
|
||||
import aiBot from '@hcengineering/ai-bot'
|
||||
import { connectMeeting, disconnectMeeting } from '@hcengineering/ai-bot-resources'
|
||||
import { Analytics } from '@hcengineering/analytics'
|
||||
import calendar, { type Event, getAllEvents } from '@hcengineering/calendar'
|
||||
import calendar, { type Event, type Schedule, getAllEvents } from '@hcengineering/calendar'
|
||||
import chunter from '@hcengineering/chunter'
|
||||
import contact, { type Employee, getCurrentEmployee, getName, type Person } from '@hcengineering/contact'
|
||||
import { personByIdStore } from '@hcengineering/contact-resources'
|
||||
@ -32,6 +32,7 @@ import {
|
||||
loveId,
|
||||
type Meeting,
|
||||
type MeetingMinutes,
|
||||
type MeetingSchedule,
|
||||
MeetingStatus,
|
||||
type Office,
|
||||
type ParticipantInfo,
|
||||
@ -1112,6 +1113,32 @@ export async function createMeeting (
|
||||
}
|
||||
}
|
||||
|
||||
export async function createMeetingSchedule (
|
||||
client: TxOperations,
|
||||
_id: Ref<Schedule>,
|
||||
space: Space,
|
||||
data: Data<Schedule>,
|
||||
store: Record<string, any>,
|
||||
phase: DocCreatePhase
|
||||
): Promise<void> {
|
||||
console.log('createMeetingSchedule-0', _id)
|
||||
if (phase === 'post') {
|
||||
console.log('createMeetingSchedule-1', _id)
|
||||
const schedule = await client.findOne(calendar.class.Schedule, { _id })
|
||||
console.log('createMeetingSchedule-2', schedule)
|
||||
if (schedule === undefined) return
|
||||
await client.createMixin<Schedule, MeetingSchedule>(
|
||||
schedule._id,
|
||||
calendar.class.Schedule,
|
||||
space._id,
|
||||
love.mixin.MeetingSchedule,
|
||||
{
|
||||
room: store.room as Ref<Room>
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export function getLoveEndpoint (): string {
|
||||
const endpoint = getMetadata(love.metadata.ServiceEnpdoint)
|
||||
if (endpoint === undefined) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Event } from '@hcengineering/calendar'
|
||||
import { Event, Schedule } from '@hcengineering/calendar'
|
||||
import { Person } from '@hcengineering/contact'
|
||||
import { AttachedDoc, Class, MarkupBlobRef, Doc, Mixin, Ref, Timestamp } from '@hcengineering/core'
|
||||
import { Drive } from '@hcengineering/drive'
|
||||
@ -134,6 +134,10 @@ export interface Meeting extends Event {
|
||||
room: Ref<Room>
|
||||
}
|
||||
|
||||
export interface MeetingSchedule extends Schedule {
|
||||
room: Ref<Room>
|
||||
}
|
||||
|
||||
export enum RequestStatus {
|
||||
Pending,
|
||||
Approved,
|
||||
@ -192,7 +196,8 @@ const love = plugin(loveId, {
|
||||
MeetingMinutes: '' as Ref<Class<MeetingMinutes>>
|
||||
},
|
||||
mixin: {
|
||||
Meeting: '' as Ref<Mixin<Meeting>>
|
||||
Meeting: '' as Ref<Mixin<Meeting>>,
|
||||
MeetingSchedule: '' as Ref<Mixin<MeetingSchedule>>
|
||||
},
|
||||
action: {
|
||||
ToggleMic: '' as Ref<Action>,
|
||||
|
Loading…
Reference in New Issue
Block a user