@things-factory/calendar 8.0.0-beta.8 → 8.0.0

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 (36) hide show
  1. package/client/bootstrap.ts +1 -0
  2. package/client/index.ts +0 -0
  3. package/client/pages/attendee/attendee-importer.ts +87 -0
  4. package/client/pages/attendee/attendee-list-page.ts +324 -0
  5. package/client/pages/calendar/calendar-importer.ts +87 -0
  6. package/client/pages/calendar/calendar-list-page.ts +325 -0
  7. package/client/pages/calendar/calendar-page.ts +128 -0
  8. package/client/pages/event/event-importer.ts +87 -0
  9. package/client/pages/event/event-list-page.ts +324 -0
  10. package/client/route.ts +19 -0
  11. package/client/tsconfig.json +13 -0
  12. package/dist-client/tsconfig.tsbuildinfo +1 -1
  13. package/dist-server/tsconfig.tsbuildinfo +1 -1
  14. package/package.json +7 -7
  15. package/server/controllers/index.ts +0 -0
  16. package/server/index.ts +4 -0
  17. package/server/middlewares/index.ts +3 -0
  18. package/server/migrations/index.ts +9 -0
  19. package/server/routes.ts +28 -0
  20. package/server/service/attendee/attendee-mutation.ts +122 -0
  21. package/server/service/attendee/attendee-query.ts +31 -0
  22. package/server/service/attendee/attendee-type.ts +44 -0
  23. package/server/service/attendee/attendee.ts +37 -0
  24. package/server/service/attendee/index.ts +7 -0
  25. package/server/service/calendar/calendar-mutation.ts +133 -0
  26. package/server/service/calendar/calendar-query.ts +48 -0
  27. package/server/service/calendar/calendar-type.ts +55 -0
  28. package/server/service/calendar/calendar.ts +82 -0
  29. package/server/service/calendar/index.ts +7 -0
  30. package/server/service/event/event-mutation.ts +125 -0
  31. package/server/service/event/event-query.ts +38 -0
  32. package/server/service/event/event-type.ts +61 -0
  33. package/server/service/event/event.ts +85 -0
  34. package/server/service/event/index.ts +7 -0
  35. package/server/service/index.ts +32 -0
  36. package/server/tsconfig.json +10 -0
@@ -0,0 +1,324 @@
1
+ import '@operato/data-grist'
2
+
3
+ import { CommonButtonStyles, CommonHeaderStyles, 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 { ColumnConfig, DataGrist, FetchOption } from '@operato/data-grist'
9
+ import { client } from '@operato/graphql'
10
+ import { i18next, localize } from '@operato/i18n'
11
+ import { notify, openPopup } from '@operato/layout'
12
+ import { isMobileDevice } from '@operato/utils'
13
+
14
+ import { connect } from 'pwa-helpers/connect-mixin'
15
+ import gql from 'graphql-tag'
16
+
17
+ import { EventImporter } from './event-importer'
18
+
19
+ @customElement('event-list-page')
20
+ export class EventListPage extends connect(store)(localize(i18next)(ScopedElementsMixin(PageView))) {
21
+ static styles = [
22
+ ScrollbarStyles,
23
+ CommonGristStyles,
24
+ CommonHeaderStyles,
25
+ css`
26
+ :host {
27
+ display: flex;
28
+
29
+ width: 100%;
30
+
31
+ --grid-record-emphasized-background-color: #8b0000;
32
+ --grid-record-emphasized-color: #ff6b6b;
33
+ }
34
+
35
+ ox-grist {
36
+ overflow-y: auto;
37
+ flex: 1;
38
+ }
39
+
40
+ ox-filters-form {
41
+ flex: 1;
42
+ }
43
+ `
44
+ ]
45
+
46
+ static get scopedElements() {
47
+ return {
48
+ 'event-importer': EventImporter
49
+ }
50
+ }
51
+
52
+ @property({ type: Object }) gristConfig: any
53
+ @property({ type: String }) mode: 'CARD' | 'GRID' | 'LIST' = isMobileDevice() ? 'CARD' : 'GRID'
54
+
55
+ @query('ox-grist') private grist!: DataGrist
56
+
57
+ get context() {
58
+ return {
59
+ title: i18next.t('title.event list'),
60
+ search: {
61
+ handler: (search: string) => {
62
+ this.grist.searchText = search
63
+ },
64
+ value: this.grist?.searchText || '',
65
+ autofocus: true
66
+ },
67
+ filter: {
68
+ handler: () => {
69
+ this.grist.toggleHeadroom()
70
+ }
71
+ },
72
+ help: 'calendar/event',
73
+ actions: [
74
+ {
75
+ title: i18next.t('button.save'),
76
+ action: this._updateEvent.bind(this),
77
+ ...CommonButtonStyles.save
78
+ },
79
+ {
80
+ title: i18next.t('button.delete'),
81
+ action: this._deleteEvent.bind(this),
82
+ ...CommonButtonStyles.delete
83
+ }
84
+ ],
85
+ exportable: {
86
+ name: i18next.t('title.event list'),
87
+ data: this.exportHandler.bind(this)
88
+ },
89
+ importable: {
90
+ handler: this.importHandler.bind(this)
91
+ }
92
+ }
93
+ }
94
+
95
+ render() {
96
+ const mode = this.mode || (isMobileDevice() ? 'CARD' : 'GRID')
97
+
98
+ return html`
99
+ <ox-grist .mode=${mode} .config=${this.gristConfig} .fetchHandler=${this.fetchHandler.bind(this)}>
100
+ <div slot="headroom" class="header">
101
+ <div class="filters">
102
+ <ox-filters-form autofocus></ox-filters-form>
103
+
104
+ <div id="modes">
105
+ <md-icon @click=${() => (this.mode = 'GRID')} ?active=${mode == 'GRID'}>grid_on</md-icon>
106
+ <md-icon @click=${() => (this.mode = 'LIST')} ?active=${mode == 'LIST'}>format_list_bulleted</md-icon>
107
+ <md-icon @click=${() => (this.mode = 'CARD')} ?active=${mode == 'CARD'}>apps</md-icon>
108
+ </div>
109
+ </div>
110
+ </div>
111
+ </ox-grist>
112
+ `
113
+ }
114
+
115
+ async pageInitialized(lifecycle: any) {
116
+ this.gristConfig = {
117
+ list: {
118
+ fields: ['name', 'description'],
119
+ details: ['active', 'updatedAt']
120
+ },
121
+ columns: [
122
+ { type: 'gutter', gutterName: 'sequence' },
123
+ { type: 'gutter', gutterName: 'row-selector', multiple: true },
124
+ {
125
+ type: 'string',
126
+ name: 'name',
127
+ header: i18next.t('field.name'),
128
+ record: {
129
+ editable: true
130
+ },
131
+ filter: 'search',
132
+ sortable: true,
133
+ width: 150
134
+ },
135
+ {
136
+ type: 'string',
137
+ name: 'description',
138
+ header: i18next.t('field.description'),
139
+ record: {
140
+ editable: true
141
+ },
142
+ filter: 'search',
143
+ width: 200
144
+ },
145
+ {
146
+ type: 'checkbox',
147
+ name: 'active',
148
+ label: true,
149
+ header: i18next.t('field.active'),
150
+ record: {
151
+ editable: true
152
+ },
153
+ filter: true,
154
+ sortable: true,
155
+ width: 60
156
+ },
157
+ {
158
+ type: 'resource-object',
159
+ name: 'updater',
160
+ header: i18next.t('field.updater'),
161
+ record: {
162
+ editable: false
163
+ },
164
+ sortable: true,
165
+ width: 120
166
+ },
167
+ {
168
+ type: 'datetime',
169
+ name: 'updatedAt',
170
+ header: i18next.t('field.updated_at'),
171
+ record: {
172
+ editable: false
173
+ },
174
+ sortable: true,
175
+ width: 180
176
+ }
177
+ ],
178
+ rows: {
179
+ selectable: {
180
+ multiple: true
181
+ }
182
+ },
183
+ sorters: [
184
+ {
185
+ name: 'name'
186
+ }
187
+ ]
188
+ }
189
+ }
190
+
191
+ async pageUpdated(changes: any, lifecycle: any) {
192
+ if (this.active) {
193
+ // do something here when this page just became as active
194
+ }
195
+ }
196
+
197
+ async fetchHandler({ page = 1, limit = 100, sortings = [], filters = [] }: FetchOption) {
198
+ const response = await client.query({
199
+ query: gql`
200
+ query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {
201
+ responses: events(filters: $filters, pagination: $pagination, sortings: $sortings) {
202
+ items {
203
+ id
204
+ name
205
+ description
206
+ active
207
+ updater {
208
+ id
209
+ name
210
+ }
211
+ updatedAt
212
+ }
213
+ total
214
+ }
215
+ }
216
+ `,
217
+ variables: {
218
+ filters,
219
+ pagination: { page, limit },
220
+ sortings
221
+ }
222
+ })
223
+
224
+ return {
225
+ total: response.data.responses.total || 0,
226
+ records: response.data.responses.items || []
227
+ }
228
+ }
229
+
230
+ async _deleteEvent() {
231
+ if (confirm(i18next.t('text.sure_to_x', { x: i18next.t('text.delete') }))) {
232
+ const ids = this.grist.selected.map(record => record.id)
233
+ if (ids && ids.length > 0) {
234
+ const response = await client.mutate({
235
+ mutation: gql`
236
+ mutation ($ids: [String!]!) {
237
+ deleteEvents(ids: $ids)
238
+ }
239
+ `,
240
+ variables: {
241
+ ids
242
+ }
243
+ })
244
+
245
+ if (!response.errors) {
246
+ this.grist.fetch()
247
+ notify({
248
+ message: i18next.t('text.info_x_successfully', { x: i18next.t('text.delete') })
249
+ })
250
+ }
251
+ }
252
+ }
253
+ }
254
+
255
+ async _updateEvent() {
256
+ let patches = this.grist.dirtyRecords
257
+ if (patches && patches.length) {
258
+ patches = patches.map(patch => {
259
+ let patchField: any = patch.id ? { id: patch.id } : {}
260
+ const dirtyFields = patch.__dirtyfields__
261
+ for (let key in dirtyFields) {
262
+ patchField[key] = dirtyFields[key].after
263
+ }
264
+ patchField.cuFlag = patch.__dirty__
265
+
266
+ return patchField
267
+ })
268
+
269
+ const response = await client.mutate({
270
+ mutation: gql`
271
+ mutation ($patches: [EventPatch!]!) {
272
+ updateMultipleEvent(patches: $patches) {
273
+ name
274
+ }
275
+ }
276
+ `,
277
+ variables: {
278
+ patches
279
+ }
280
+ })
281
+
282
+ if (!response.errors) {
283
+ this.grist.fetch()
284
+ }
285
+ }
286
+ }
287
+
288
+ async exportHandler() {
289
+ const exportTargets = this.grist.selected.length ? this.grist.selected : this.grist.dirtyData.records
290
+ const targetFieldSet = new Set(['id', 'name', 'description', 'active'])
291
+
292
+ return exportTargets.map(event => {
293
+ let tempObj = {}
294
+ for (const field of targetFieldSet) {
295
+ tempObj[field] = event[field]
296
+ }
297
+
298
+ return tempObj
299
+ })
300
+ }
301
+
302
+ async importHandler(records) {
303
+ const popup = openPopup(
304
+ html`
305
+ <event-importer
306
+ .events=${records}
307
+ @imported=${() => {
308
+ history.back()
309
+ this.grist.fetch()
310
+ }}
311
+ ></event-importer>
312
+ `,
313
+ {
314
+ backdrop: true,
315
+ size: 'large',
316
+ title: i18next.t('title.import event')
317
+ }
318
+ )
319
+
320
+ popup.onclosed = () => {
321
+ this.grist.fetch()
322
+ }
323
+ }
324
+ }
@@ -0,0 +1,19 @@
1
+ export default function route(page: string) {
2
+ switch (page) {
3
+ case 'calendar':
4
+ import('./pages/calendar/calendar-page')
5
+ return page
6
+
7
+ case 'calendar-list':
8
+ import('./pages/calendar/calendar-list-page')
9
+ return page
10
+
11
+ case 'event-list':
12
+ import('./pages/event/event-list-page')
13
+ return page
14
+
15
+ case 'attendee-list':
16
+ import('./pages/attendee/attendee-list-page')
17
+ return page
18
+ }
19
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "extends": "../../tsconfig-base.json",
3
+ "compilerOptions": {
4
+ "experimentalDecorators": true,
5
+ "skipLibCheck": true,
6
+ "strict": true,
7
+ "declaration": true,
8
+ "module": "esnext",
9
+ "outDir": "../dist-client",
10
+ "baseUrl": "./"
11
+ },
12
+ "include": ["./**/*"]
13
+ }