@things-factory/calendar 6.0.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/assets/images/hatiolab-logo.png +0 -0
  3. package/client/bootstrap.ts +1 -0
  4. package/client/index.ts +0 -0
  5. package/client/pages/attendee/attendee-importer.ts +97 -0
  6. package/client/pages/attendee/attendee-list-page.ts +348 -0
  7. package/client/pages/calendar/calendar-importer.ts +97 -0
  8. package/client/pages/calendar/calendar-list-page.ts +348 -0
  9. package/client/pages/calendar/calendar-page.ts +128 -0
  10. package/client/pages/event/event-importer.ts +97 -0
  11. package/client/pages/event/event-list-page.ts +348 -0
  12. package/client/route.ts +19 -0
  13. package/client/themes/calendar-theme.css +54 -0
  14. package/client/tsconfig.json +11 -0
  15. package/concept.md +31 -0
  16. package/dist-client/bootstrap.d.ts +1 -0
  17. package/dist-client/bootstrap.js +2 -0
  18. package/dist-client/bootstrap.js.map +1 -0
  19. package/dist-client/index.d.ts +0 -0
  20. package/dist-client/index.js +2 -0
  21. package/dist-client/index.js.map +1 -0
  22. package/dist-client/pages/attendee/attendee-importer.d.ts +22 -0
  23. package/dist-client/pages/attendee/attendee-importer.js +100 -0
  24. package/dist-client/pages/attendee/attendee-importer.js.map +1 -0
  25. package/dist-client/pages/attendee/attendee-list-page.d.ts +62 -0
  26. package/dist-client/pages/attendee/attendee-list-page.js +326 -0
  27. package/dist-client/pages/attendee/attendee-list-page.js.map +1 -0
  28. package/dist-client/pages/calendar/calendar-importer.d.ts +22 -0
  29. package/dist-client/pages/calendar/calendar-importer.js +100 -0
  30. package/dist-client/pages/calendar/calendar-importer.js.map +1 -0
  31. package/dist-client/pages/calendar/calendar-list-page.d.ts +62 -0
  32. package/dist-client/pages/calendar/calendar-list-page.js +326 -0
  33. package/dist-client/pages/calendar/calendar-list-page.js.map +1 -0
  34. package/dist-client/pages/calendar/calendar-page.d.ts +26 -0
  35. package/dist-client/pages/calendar/calendar-page.js +128 -0
  36. package/dist-client/pages/calendar/calendar-page.js.map +1 -0
  37. package/dist-client/pages/event/event-importer.d.ts +22 -0
  38. package/dist-client/pages/event/event-importer.js +100 -0
  39. package/dist-client/pages/event/event-importer.js.map +1 -0
  40. package/dist-client/pages/event/event-list-page.d.ts +62 -0
  41. package/dist-client/pages/event/event-list-page.js +326 -0
  42. package/dist-client/pages/event/event-list-page.js.map +1 -0
  43. package/dist-client/route.d.ts +1 -0
  44. package/dist-client/route.js +17 -0
  45. package/dist-client/route.js.map +1 -0
  46. package/dist-client/tsconfig.tsbuildinfo +1 -0
  47. package/dist-server/controllers/index.js +1 -0
  48. package/dist-server/controllers/index.js.map +1 -0
  49. package/dist-server/index.js +7 -0
  50. package/dist-server/index.js.map +1 -0
  51. package/dist-server/middlewares/index.js +8 -0
  52. package/dist-server/middlewares/index.js.map +1 -0
  53. package/dist-server/migrations/index.js +12 -0
  54. package/dist-server/migrations/index.js.map +1 -0
  55. package/dist-server/routes.js +25 -0
  56. package/dist-server/routes.js.map +1 -0
  57. package/dist-server/service/attendee/attendee-mutation.js +124 -0
  58. package/dist-server/service/attendee/attendee-mutation.js.map +1 -0
  59. package/dist-server/service/attendee/attendee-query.js +47 -0
  60. package/dist-server/service/attendee/attendee-query.js.map +1 -0
  61. package/dist-server/service/attendee/attendee-type.js +65 -0
  62. package/dist-server/service/attendee/attendee-type.js.map +1 -0
  63. package/dist-server/service/attendee/attendee.js +46 -0
  64. package/dist-server/service/attendee/attendee.js.map +1 -0
  65. package/dist-server/service/attendee/index.js +10 -0
  66. package/dist-server/service/attendee/index.js.map +1 -0
  67. package/dist-server/service/calendar/calendar-mutation.js +168 -0
  68. package/dist-server/service/calendar/calendar-mutation.js.map +1 -0
  69. package/dist-server/service/calendar/calendar-query.js +97 -0
  70. package/dist-server/service/calendar/calendar-query.js.map +1 -0
  71. package/dist-server/service/calendar/calendar-type.js +86 -0
  72. package/dist-server/service/calendar/calendar-type.js.map +1 -0
  73. package/dist-server/service/calendar/calendar.js +93 -0
  74. package/dist-server/service/calendar/calendar.js.map +1 -0
  75. package/dist-server/service/calendar/index.js +10 -0
  76. package/dist-server/service/calendar/index.js.map +1 -0
  77. package/dist-server/service/event/event-mutation.js +124 -0
  78. package/dist-server/service/event/event-mutation.js.map +1 -0
  79. package/dist-server/service/event/event-query.js +66 -0
  80. package/dist-server/service/event/event-query.js.map +1 -0
  81. package/dist-server/service/event/event-type.js +86 -0
  82. package/dist-server/service/event/event-type.js.map +1 -0
  83. package/dist-server/service/event/event.js +94 -0
  84. package/dist-server/service/event/event.js.map +1 -0
  85. package/dist-server/service/event/index.js +10 -0
  86. package/dist-server/service/event/index.js.map +1 -0
  87. package/dist-server/service/index.js +33 -0
  88. package/dist-server/service/index.js.map +1 -0
  89. package/dist-server/tsconfig.tsbuildinfo +1 -0
  90. package/helps/calendar/attendee.md +160 -0
  91. package/helps/calendar/calendar.md +160 -0
  92. package/helps/calendar/event.md +160 -0
  93. package/package.json +36 -0
  94. package/server/controllers/index.ts +0 -0
  95. package/server/index.ts +4 -0
  96. package/server/middlewares/index.ts +3 -0
  97. package/server/migrations/index.ts +9 -0
  98. package/server/routes.ts +28 -0
  99. package/server/service/attendee/attendee-mutation.ts +122 -0
  100. package/server/service/attendee/attendee-query.ts +31 -0
  101. package/server/service/attendee/attendee-type.ts +44 -0
  102. package/server/service/attendee/attendee.ts +36 -0
  103. package/server/service/attendee/index.ts +7 -0
  104. package/server/service/calendar/calendar-mutation.ts +198 -0
  105. package/server/service/calendar/calendar-query.ts +62 -0
  106. package/server/service/calendar/calendar-type.ts +61 -0
  107. package/server/service/calendar/calendar.ts +82 -0
  108. package/server/service/calendar/index.ts +7 -0
  109. package/server/service/event/event-mutation.ts +125 -0
  110. package/server/service/event/event-query.ts +38 -0
  111. package/server/service/event/event-type.ts +61 -0
  112. package/server/service/event/event.ts +85 -0
  113. package/server/service/event/index.ts +7 -0
  114. package/server/service/index.ts +32 -0
  115. package/server/tsconfig.json +10 -0
  116. package/things-factory.config.js +25 -0
  117. package/translations/en.json +1 -0
  118. package/translations/ko.json +1 -0
  119. package/translations/ms.json +1 -0
  120. package/translations/zh.json +1 -0
@@ -0,0 +1,348 @@
1
+ import '@operato/data-grist'
2
+
3
+ import { CommonButtonStyles, CommonGristStyles, ScrollbarStyles } from '@operato/styles'
4
+ import { PageView, store } from '@operato/shell'
5
+ import { css, html } from 'lit'
6
+ import { customElement, property, query } from 'lit/decorators.js'
7
+ import { ScopedElementsMixin } from '@open-wc/scoped-elements'
8
+ import {
9
+ ColumnConfig,
10
+ DataGrist,
11
+ FetchOption,
12
+ SortersControl
13
+ } from '@operato/data-grist'
14
+ import { client } from '@operato/graphql'
15
+ import { i18next, localize } from '@operato/i18n'
16
+ import { notify, openPopup } from '@operato/layout'
17
+ import { OxPopup } from '@operato/popup'
18
+ import { isMobileDevice } from '@operato/utils'
19
+
20
+ import { connect } from 'pwa-helpers/connect-mixin'
21
+ import gql from 'graphql-tag'
22
+
23
+ import { CalendarImporter } from './calendar-importer'
24
+
25
+ @customElement('calendar-list-page')
26
+ export class CalendarListPage extends connect(store)(localize(i18next)(ScopedElementsMixin(PageView))) {
27
+
28
+ static styles = [
29
+ ScrollbarStyles,
30
+ CommonGristStyles,
31
+ css`
32
+ :host {
33
+ display: flex;
34
+
35
+ width: 100%;
36
+
37
+ --grid-record-emphasized-background-color: red;
38
+ --grid-record-emphasized-color: yellow;
39
+ }
40
+ `
41
+ ]
42
+
43
+ static get scopedElements() {
44
+ return {
45
+ 'calendar-importer': CalendarImporter
46
+ }
47
+ }
48
+
49
+ @property({ type: Object }) gristConfig: any
50
+ @property({ type: String }) mode: 'CARD' | 'GRID' | 'LIST' = isMobileDevice() ? 'CARD' : 'GRID'
51
+
52
+ @query('ox-grist') private grist!: DataGrist
53
+ @query('#sorter-control') private sortersControl!: OxPopup
54
+
55
+ get context() {
56
+ return {
57
+ search: {
58
+ handler: (search: string) => {
59
+ this.grist.searchText = search
60
+ },
61
+ placeholder: i18next.t('title.calendar list'),
62
+ value: this.grist.searchText
63
+ },
64
+ filter: {
65
+ handler: () => {
66
+ this.grist.toggleHeadroom()
67
+ }
68
+ },
69
+ help: 'calendar/calendar',
70
+ actions: [
71
+ {
72
+ title: i18next.t('button.save'),
73
+ action: this._updateCalendar.bind(this),
74
+ ...CommonButtonStyles.save
75
+ },
76
+ {
77
+ title: i18next.t('button.delete'),
78
+ action: this._deleteCalendar.bind(this),
79
+ ...CommonButtonStyles.delete
80
+ }
81
+ ],
82
+ exportable: {
83
+ name: i18next.t('title.calendar list'),
84
+ data: this.exportHandler.bind(this)
85
+ },
86
+ importable: {
87
+ handler: this.importHandler.bind(this)
88
+ }
89
+ }
90
+ }
91
+
92
+ render() {
93
+ const mode = this.mode || (isMobileDevice() ? 'CARD' : 'GRID')
94
+
95
+ return html`
96
+ <ox-grist
97
+ .mode=${mode}
98
+ .config=${this.gristConfig}
99
+ .fetchHandler=${this.fetchHandler.bind(this)}
100
+ >
101
+ <div slot="headroom">
102
+ <div id="filters">
103
+ <ox-filters-form autofocus></ox-filters-form>
104
+ </div>
105
+
106
+ <div id="sorters">
107
+ Sort
108
+ <mwc-icon
109
+ @click=${e => {
110
+ const target = e.currentTarget
111
+ this.sortersControl.open({
112
+ right: 0,
113
+ top: target.offsetTop + target.offsetHeight
114
+ })
115
+ }}
116
+ >expand_more</mwc-icon
117
+ >
118
+ <ox-popup id="sorter-control">
119
+ <ox-sorters-control> </ox-sorters-control>
120
+ </ox-popup>
121
+ </div>
122
+
123
+ <div id="modes">
124
+ <mwc-icon @click=${() => (this.mode = 'GRID')} ?active=${mode == 'GRID'}>grid_on</mwc-icon>
125
+ <mwc-icon @click=${() => (this.mode = 'LIST')} ?active=${mode == 'LIST'}>format_list_bulleted</mwc-icon>
126
+ <mwc-icon @click=${() => (this.mode = 'CARD')} ?active=${mode == 'CARD'}>apps</mwc-icon>
127
+ </div>
128
+ </div>
129
+ </ox-grist>
130
+ `
131
+ }
132
+
133
+ async pageInitialized(lifecycle: any) {
134
+ this.gristConfig = {
135
+ list: {
136
+ fields: ['name', 'description'],
137
+ details: ['active', 'updatedAt']
138
+ },
139
+ columns: [
140
+ { type: 'gutter', gutterName: 'sequence' },
141
+ { type: 'gutter', gutterName: 'row-selector', multiple: true },
142
+ {
143
+ type: 'string',
144
+ name: 'name',
145
+ header: i18next.t('field.name'),
146
+ record: {
147
+ editable: true
148
+ },
149
+ filter: 'search',
150
+ sortable: true,
151
+ width: 150
152
+ },
153
+ {
154
+ type: 'string',
155
+ name: 'description',
156
+ header: i18next.t('field.description'),
157
+ record: {
158
+ editable: true
159
+ },
160
+ filter: 'search',
161
+ width: 200
162
+ },
163
+ {
164
+ type: 'checkbox',
165
+ name: 'active',
166
+ label: true,
167
+ header: i18next.t('field.active'),
168
+ record: {
169
+ editable: true
170
+ },
171
+ filter: true,
172
+ sortable: true,
173
+ width: 60
174
+ },
175
+ {
176
+ type: 'resource-object',
177
+ name: 'updater',
178
+ header: i18next.t('field.updater'),
179
+ record: {
180
+ editable: false
181
+ },
182
+ sortable: true,
183
+ width: 120
184
+ },
185
+ {
186
+ type: 'datetime',
187
+ name: 'updatedAt',
188
+ header: i18next.t('field.updated_at'),
189
+ record: {
190
+ editable: false
191
+ },
192
+ sortable: true,
193
+ width: 180
194
+ }
195
+ ],
196
+ rows: {
197
+ selectable: {
198
+ multiple: true
199
+ }
200
+ },
201
+ sorters: [
202
+ {
203
+ name: 'name'
204
+ }
205
+ ]
206
+ }
207
+ }
208
+
209
+ async pageUpdated(changes: any, lifecycle: any) {
210
+ if (this.active) {
211
+ // do something here when this page just became as active
212
+ }
213
+ }
214
+
215
+ async fetchHandler({ page = 1, limit = 100, sortings = [], filters = [] }: FetchOption) {
216
+ const response = await client.query({
217
+ query: gql`
218
+ query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {
219
+ responses: calendars(filters: $filters, pagination: $pagination, sortings: $sortings) {
220
+ items {
221
+ id
222
+ name
223
+ description
224
+ active
225
+ updater {
226
+ id
227
+ name
228
+ }
229
+ updatedAt
230
+ }
231
+ total
232
+ }
233
+ }
234
+ `,
235
+ variables: {
236
+ filters,
237
+ pagination: { page, limit },
238
+ sortings
239
+ }
240
+ })
241
+
242
+ return {
243
+ total: response.data.responses.total || 0,
244
+ records: response.data.responses.items || []
245
+ }
246
+ }
247
+
248
+ async _deleteCalendar() {
249
+ if (confirm(i18next.t('text.sure_to_x', { x: i18next.t('text.delete') }))) {
250
+ const ids = this.grist.selected.map(record => record.id)
251
+ if (ids && ids.length > 0) {
252
+ const response = await client.mutate({
253
+ mutation: gql`
254
+ mutation ($ids: [String!]!) {
255
+ deleteCalendars(ids: $ids)
256
+ }
257
+ `,
258
+ variables: {
259
+ ids
260
+ }
261
+ })
262
+
263
+ if (!response.errors) {
264
+ this.grist.fetch()
265
+ notify({
266
+ message: i18next.t('text.info_x_successfully', { x: i18next.t('text.delete') })
267
+ })
268
+ }
269
+ }
270
+ }
271
+ }
272
+
273
+ async _updateCalendar() {
274
+ let patches = this.grist.dirtyRecords
275
+ if (patches && patches.length) {
276
+ patches = patches.map(patch => {
277
+ let patchField: any = patch.id ? { id: patch.id } : {}
278
+ const dirtyFields = patch.__dirtyfields__
279
+ for (let key in dirtyFields) {
280
+ patchField[key] = dirtyFields[key].after
281
+ }
282
+ patchField.cuFlag = patch.__dirty__
283
+
284
+ return patchField
285
+ })
286
+
287
+ const response = await client.mutate({
288
+ mutation: gql`
289
+ mutation ($patches: [CalendarPatch!]!) {
290
+ updateMultipleCalendar(patches: $patches) {
291
+ name
292
+ }
293
+ }
294
+ `,
295
+ variables: {
296
+ patches
297
+ }
298
+ })
299
+
300
+ if (!response.errors) {
301
+ this.grist.fetch()
302
+ }
303
+ }
304
+ }
305
+
306
+ async exportHandler() {
307
+ const exportTargets = this.grist.selected.length ? this.grist.selected : this.grist.dirtyData.records
308
+ const targetFieldSet = new Set([
309
+ 'id',
310
+ 'name',
311
+ 'description',
312
+ 'active'
313
+ ])
314
+
315
+ return exportTargets.map(calendar => {
316
+ let tempObj = {}
317
+ for (const field of targetFieldSet) {
318
+ tempObj[field] = calendar[field]
319
+ }
320
+
321
+ return tempObj
322
+ })
323
+ }
324
+
325
+ async importHandler(records) {
326
+ const popup = openPopup(
327
+ html`
328
+ <calendar-importer
329
+ .calendars=${records}
330
+ @imported=${() => {
331
+ history.back()
332
+ this.grist.fetch()
333
+ }}
334
+ ></calendar-importer>
335
+ `,
336
+ {
337
+ backdrop: true,
338
+ size: 'large',
339
+ title: i18next.t('title.import calendar')
340
+ }
341
+ )
342
+
343
+ popup.onclosed = () => {
344
+ this.grist.fetch()
345
+ }
346
+ }
347
+ }
348
+
@@ -0,0 +1,128 @@
1
+ import '@operato/event-view/ox-event-view.js'
2
+
3
+ import { PropertyValues, html, css } from 'lit'
4
+ import { customElement, property, query, state } from 'lit/decorators.js'
5
+ import { connect } from 'pwa-helpers/connect-mixin.js'
6
+ import { store, PageView } from '@operato/shell'
7
+ import { BizEvent, CALENDAR, EventProvider } from '@operato/event-view/types.js'
8
+ import { OxEventView } from '@operato/event-view'
9
+ import { ScrollbarStyles } from '@operato/styles'
10
+
11
+ @customElement('calendar-page')
12
+ export class CalendarPage extends connect(store)(PageView) {
13
+ static styles = [
14
+ ScrollbarStyles,
15
+ css`
16
+ :host {
17
+ display: flex;
18
+ }
19
+
20
+ ox-event-view {
21
+ flex: 1;
22
+ padding: 10px;
23
+ overflow: auto;
24
+ }
25
+ `
26
+ ]
27
+
28
+ @property({ type: String }) itemId?: string
29
+ @property({ type: Object }) params: any
30
+
31
+ @query('ox-event-view') eventView!: OxEventView
32
+
33
+ render() {
34
+ return html` <ox-event-view mode="monthly" .eventProvider=${this}></ox-event-view> `
35
+ }
36
+
37
+ updated(changes: PropertyValues<this>) {
38
+ /*
39
+ * If this page properties are changed, this callback will be invoked.
40
+ * This callback will be called back only when this page is activated.
41
+ */
42
+ if (changes.has('itemId') || changes.has('params')) {
43
+ /* do something */
44
+ }
45
+ }
46
+
47
+ fetchEventsForCalendar(calendar: CALENDAR): Map<Date, BizEvent[]> {
48
+ const eventMap = new Map<Date, BizEvent[]>()
49
+
50
+ calendar.forEach(({ date }) => {
51
+ eventMap.set(date, [])
52
+ })
53
+
54
+ return eventMap
55
+ }
56
+
57
+ stateChanged(state: any) {
58
+ /*
59
+ * application wide state changed
60
+ *
61
+ */
62
+ }
63
+
64
+ /*
65
+ * page lifecycle
66
+ *
67
+ * - pageInitialized(lifecycle)
68
+ * - pageUpdated(changes, lifecycle, changedBefore)
69
+ * - pageDisposed(lifecycle)
70
+ *
71
+ * lifecycle value has
72
+ * - active : this page is activated
73
+ * - page : first path of href
74
+ * - resourceId : second path of href
75
+ * - params : search params object of href
76
+ * - initialized : initialized state of this page
77
+ *
78
+ * you can update lifecycle values, or add custom values
79
+ * by calling this.pageUpdate({ ...values }, force)
80
+ * If lifecycle values changed by this.pageUpdate(...),
81
+ * this.pageUpdated(...) will be called back right after.
82
+ * If you want to invoke this.pageUpdated(...) callback,
83
+ * set force argument to true.
84
+ *
85
+ * you can re-initialize this page
86
+ * by calling this.pageReset().
87
+ * this.pageInitialized(...) followed by this.pageDisposed(...) will be invoked
88
+ * by calling this.pageReset().
89
+ *
90
+ * you can invoke this.pageDisposed()
91
+ * by calling this.pageDispose()
92
+ */
93
+
94
+ pageInitialized(lifecycle: any) {
95
+ /*
96
+ * This page is initialized.
97
+ * It's right time to configure of this page.
98
+ *
99
+ * - called before when this page activated first
100
+ * - called when i18next resource is updated (loaded, changed, ..)
101
+ * - called right after this.pageReset()
102
+ */
103
+ }
104
+
105
+ pageUpdated(changes: any, lifecycle: any, before: any) {
106
+ if (this.active) {
107
+ /*
108
+ * this page is activated
109
+ */
110
+ this.itemId = lifecycle.resourceId
111
+ this.params = lifecycle.params
112
+ } else {
113
+ /* this page is deactivated */
114
+ }
115
+ }
116
+
117
+ pageDisposed(lifecycle: any) {
118
+ /*
119
+ * This page is disposed.
120
+ * It's right time to release system resources.
121
+ *
122
+ * - called just before (re)pageInitialized
123
+ * - called right after when i18next resource updated (loaded, changed, ..)
124
+ * - called right after this.pageReset()
125
+ * - called right after this.pageDispose()
126
+ */
127
+ }
128
+ }
@@ -0,0 +1,97 @@
1
+ import '@operato/data-grist'
2
+
3
+ import gql from 'graphql-tag'
4
+ import { css, html, LitElement } from 'lit'
5
+ import { property } from 'lit/decorators.js'
6
+
7
+ import { client } from '@operato/graphql'
8
+ import { i18next } from '@operato/i18n'
9
+ import { isMobileDevice } from '@operato/utils'
10
+
11
+ export class EventImporter extends LitElement {
12
+ static styles = [
13
+ css`
14
+ :host {
15
+ display: flex;
16
+ flex-direction: column;
17
+
18
+ background-color: #fff;
19
+ }
20
+
21
+ ox-grist {
22
+ flex: 1;
23
+ }
24
+
25
+ .button-container {
26
+ display: flex;
27
+ margin-left: auto;
28
+ padding: var(--padding-default);
29
+ }
30
+
31
+ mwc-button {
32
+ margin-left: var(--margin-default);
33
+ }
34
+ `
35
+ ]
36
+
37
+ @property({ type: Array }) events: any[] = []
38
+ @property({ type: Object }) columns = {
39
+ list: { fields: ['name', 'description'] },
40
+ pagination: { infinite: true },
41
+ columns: [
42
+ {
43
+ type: 'string',
44
+ name: 'name',
45
+ header: i18next.t('field.name'),
46
+ width: 150
47
+ },
48
+ {
49
+ type: 'string',
50
+ name: 'description',
51
+ header: i18next.t('field.description'),
52
+ width: 200
53
+ },
54
+ {
55
+ type: 'checkbox',
56
+ name: 'active',
57
+ header: i18next.t('field.active'),
58
+ width: 60
59
+ }
60
+ ]
61
+ }
62
+
63
+
64
+ render() {
65
+ return html`
66
+ <ox-grist
67
+ .mode=${isMobileDevice() ? 'LIST' : 'GRID'}
68
+ .config=${this.columns}
69
+ .data=${
70
+ {
71
+ records: this.events
72
+ }
73
+ }
74
+ ></ox-grist>
75
+
76
+ <div class="button-container">
77
+ <mwc-button raised @click="${this.save.bind(this)}">${i18next.t('button.save')}</mwc-button>
78
+ </div>
79
+ `
80
+ }
81
+
82
+ async save() {
83
+ const response = await client.mutate({
84
+ mutation: gql`
85
+ mutation importEvents($events: [EventPatch!]!) {
86
+ importEvents(events: $events)
87
+ }
88
+ `,
89
+ variables: { events: this.events }
90
+ })
91
+
92
+ if (response.errors?.length) return
93
+
94
+ this.dispatchEvent(new CustomEvent('imported'))
95
+ }
96
+ }
97
+