28
28
<div v-if =" loadingIndicator" class =" loading-indicator" >
29
29
<div class =" icon-loading" />
30
30
</div >
31
- <h2 >Find a time</h2 >
32
31
<div class =" modal__content__header" >
33
- <div class =" modal__content__header__title" >
34
- <h3 >{{ formattedCurrentDate }}</h3 >
32
+ <h2 >Find a time</h2 >
33
+ <h3 >{{ eventTitle }}</h3 >
34
+ <div class =" modal__content__header__attendees" >
35
+ with:
36
+ <NcUserBubble :display-name =" organizer.commonName" />
37
+ <NcUserBubble v-for =" attendee in attendees"
38
+ :key =" attendee.id"
39
+ class =" modal__content__header__attendees__user-bubble"
40
+ :display-name =" attendee.commonName" >
41
+ <template #name >
42
+ <a href =" #"
43
+ title =" Remove user"
44
+ class =" icon-close"
45
+ @click =" removeAttendee(attendee)" />
46
+ </template >
47
+ </NcUserBubble >
35
48
</div >
36
- <div class =" modal__content__header__actions" >
49
+ </div >
50
+ <div class =" modal__content__actions" >
51
+ <InviteesListSearch class =" modal__content__actions__select"
52
+ :already-invited-emails =" alreadyInvitedEmails"
53
+ :organizer =" organizer"
54
+ @add-attendee =" addAttendee" />
55
+ <div class =" modal__content__actions__date" >
37
56
<NcButton type =" secondary"
38
57
@click =" handleActions('today')" >
39
58
{{ $t('calendar', 'Today') }}
51
70
</template >
52
71
</NcButton >
53
72
54
- <NcDatetimePicker :value =" currentDate"
73
+ <NcDateTimePicker :value =" currentDate"
55
74
confirm
56
75
@confirm =" setCurrentDate" />
76
+ <NcPopover >
77
+ <template #trigger >
78
+ <NcButton type =" tertiary-no-background" >
79
+ <template #icon >
80
+ <HelpCircleIcon :size =" 20" />
81
+ </template >
82
+ </NcButton >
83
+ </template >
84
+ <template >
85
+ <div class =" freebusy-caption" >
86
+ <div class =" freebusy-caption__calendar-user-types" />
87
+ <div class =" freebusy-caption__colors" >
88
+ <div v-for =" color in colorCaption" :key =" color.color" class =" freebusy-caption-item" >
89
+ <div class =" freebusy-caption-item__color" :style =" { 'background-color': color.color }" />
90
+ <div class =" freebusy-caption-item__label" >
91
+ {{ color.label }}
92
+ </div >
93
+ </div >
94
+ </div >
95
+ </div >
96
+ </template >
97
+ </NcPopover >
57
98
</div >
58
99
</div >
59
100
<FullCalendar ref =" freeBusyFullCalendar"
60
101
:options =" options" />
61
- <div class =" freebusy-caption " >
62
- <div class =" freebusy-caption__calendar-user-types " / >
63
- < div class = " freebusy-caption__colors " >
64
- < div v-for = " color in colorCaption " :key = " color.color " class = " freebusy-caption-item " >
65
- < div class = " freebusy-caption-item__color " :style = " { 'background-color': color.color } " / >
66
- < div class = " freebusy-caption-item__label " >
67
- {{ color.label }}
68
- </ div >
69
- </div >
70
- </div >
102
+ <div class =" modal__content__footer " >
103
+ <h3 class =" modal__content__footer__title " >
104
+ {{ formattedCurrentDate }}
105
+ </ h3 >
106
+ < NcButton >
107
+ {{ $t('calendar', 'Done') }}
108
+ < template # icon >
109
+ <CheckIcon :size = " 20 " / >
110
+ </template >
111
+ </NcButton >
71
112
</div >
72
113
</div >
73
114
</Modal >
77
118
// Import FullCalendar itself
78
119
import FullCalendar from ' @fullcalendar/vue'
79
120
import resourceTimelinePlugin from ' @fullcalendar/resource-timeline'
80
- import NcDatetimePicker from ' @nextcloud/vue/dist/Components/NcDatetimePicker.js'
121
+ import interactionPlugin from ' @fullcalendar/interaction'
122
+
123
+ import NcDateTimePicker from ' @nextcloud/vue/dist/Components/NcDateTimePicker.js'
81
124
import NcButton from ' @nextcloud/vue/dist/Components/NcButton.js'
125
+ import NcPopover from ' @nextcloud/vue/dist/Components/NcPopover.js'
126
+ import NcUserBubble from ' @nextcloud/vue/dist/Components/NcUserBubble.js'
82
127
// Import event sources
83
128
import freeBusyBlockedForAllEventSource from ' ../../../fullcalendar/eventSources/freeBusyBlockedForAllEventSource.js'
84
129
import freeBusyFakeBlockingEventSource from ' ../../../fullcalendar/eventSources/freeBusyFakeBlockingEventSource.js'
@@ -99,22 +144,27 @@ import {
99
144
import { NcModal as Modal } from ' @nextcloud/vue'
100
145
import ChevronRightIcon from ' vue-material-design-icons/ChevronRight.vue'
101
146
import ChevronLeftIcon from ' vue-material-design-icons/ChevronLeft.vue'
102
- import Vue from ' vue'
103
- import OrganizerListItem from ' ../Invitees/OrganizerListItem.vue'
104
- import InviteesListItem from ' ../Invitees/InviteesListItem.vue'
147
+ import CheckIcon from ' vue-material-design-icons/Check.vue'
148
+ import HelpCircleIcon from ' vue-material-design-icons/HelpCircle.vue'
149
+
150
+ import InviteesListSearch from ' ../Invitees/InviteesListSearch.vue'
105
151
106
152
import { getColorForFBType } from ' ../../../utils/freebusy.js'
107
153
108
154
export default {
109
155
name: ' FreeBusy' ,
110
156
components: {
111
157
FullCalendar,
112
- NcDatetimePicker,
158
+ InviteesListSearch,
159
+ NcDateTimePicker,
113
160
Modal,
114
161
NcButton,
162
+ NcPopover,
163
+ NcUserBubble,
115
164
ChevronRightIcon,
116
165
ChevronLeftIcon,
117
-
166
+ CheckIcon,
167
+ HelpCircleIcon,
118
168
},
119
169
props: {
120
170
/**
@@ -147,6 +197,15 @@ export default {
147
197
type: Date ,
148
198
required: true ,
149
199
},
200
+ eventTitle: {
201
+ type: String ,
202
+ required: true ,
203
+
204
+ },
205
+ alreadyInvitedEmails: {
206
+ type: Array ,
207
+ required: true ,
208
+ },
150
209
},
151
210
data () {
152
211
return {
@@ -175,6 +234,7 @@ export default {
175
234
resourceTimelinePlugin,
176
235
momentPluginFactory (this .$store ),
177
236
VTimezoneNamedTimezone,
237
+ interactionPlugin,
178
238
]
179
239
},
180
240
formattedCurrentDate () {
@@ -282,15 +342,15 @@ export default {
282
342
plugins: this .plugins ,
283
343
// Interaction:
284
344
editable: false ,
285
- selectable: false ,
345
+ selectable: true ,
346
+ select: this .handleSelect ,
286
347
// Localization:
287
348
... getDateFormattingConfig (),
288
349
... getFullCalendarLocale (),
289
350
// Rendering
290
351
height: ' auto' ,
291
352
loading: this .loading ,
292
353
headerToolbar: false ,
293
- // resourceLabelContent: this.customResourceRender,
294
354
resourceAreaColumns: [
295
355
{
296
356
field: ' title' ,
@@ -312,6 +372,13 @@ export default {
312
372
},
313
373
},
314
374
methods: {
375
+
376
+ addAttendee (attendee ) {
377
+ this .$emit (' add-attendee' , attendee)
378
+ },
379
+ removeAttendee (attendee ) {
380
+ this .$emit (' remove-attendee' , attendee)
381
+ },
315
382
loading (isLoading ) {
316
383
this .loadingIndicator = isLoading
317
384
},
@@ -336,43 +403,28 @@ export default {
336
403
break
337
404
}
338
405
},
339
- customResourceRender (arg ) {
340
- const attendee = [this .organizer , ... this .attendees ].find ((a ) => a .uri === arg .resource .id )
341
- let component = null
342
- if (attendee === this .organizer ) {
343
- component = new Vue ({
344
- render : h => h (OrganizerListItem, { props: { organizer: attendee, isReadOnly: false } }),
345
- }).$mount ()
346
- } else {
347
- component = new Vue ({
348
- render : h => h (InviteesListItem, { props: { attendee, organizerDisplayName: this .organizer .commonName , isReadOnly: false } }),
349
- }).$mount ()
350
-
351
- }
352
-
353
- const html = document .createElement (' div' )
354
- html .style .position = ' absolute'
355
- html .innerText (component .$el .outerHTML )
356
-
357
- return {
358
- html,
359
- }
360
- },
361
406
},
362
407
}
363
408
</script >
364
409
365
410
<style lang='scss' scoped>
411
+ .icon-close {
412
+ display : block ;
413
+ height : 100% ;
414
+ }
366
415
.modal__content {
367
416
padding : 50px ;
368
417
// when the calendar is open, it's cut at the bottom, adding a margin fixes it
369
418
margin-bottom : 95px ;
370
- & __header {
419
+ & __actions {
371
420
display : flex ;
372
421
justify-content : space-between ;
373
422
align-items : center ;
374
423
margin-bottom : 20px ;
375
- & __actions {
424
+ & __select {
425
+ width : 260px ;
426
+ }
427
+ & __date {
376
428
display : flex ;
377
429
justify-content : space-between ;
378
430
align-items : center ;
@@ -381,6 +433,26 @@ export default {
381
433
}
382
434
}
383
435
}
436
+ & __header {
437
+ h3 {
438
+ font-weight : 500 ;
439
+ }
440
+ margin-bottom : 20px ;
441
+ & __attendees {
442
+ & __user-bubble {
443
+ margin-right : 5px ;
444
+ }
445
+ }
446
+ }
447
+ & __footer {
448
+ display : flex ;
449
+ justify-content : space-between ;
450
+ align-items : center ;
451
+ margin-top : 20px ;
452
+ & __title {
453
+ font-weight : 500 ;
454
+ }
455
+ }
384
456
}
385
457
386
458
::v-deep .mx-input {
0 commit comments