@things-factory/scheduler 8.0.0-beta.9 → 8.0.2

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.
@@ -0,0 +1 @@
1
+ export default function bootstrap() {}
File without changes
@@ -0,0 +1,75 @@
1
+ import '@material/web/icon/icon.js'
2
+ import '@operato/input/ox-input-crontab.js'
3
+
4
+ import { css, html, LitElement } from 'lit'
5
+ import { customElement, property } from 'lit/decorators.js'
6
+
7
+ import { i18next } from '@operato/i18n'
8
+ import { closePopup } from '@operato/popup'
9
+ import { CommonHeaderStyles, ScrollbarStyles } from '@operato/styles'
10
+
11
+ @customElement('popup-crontab-input')
12
+ export class PopupCrontabInput extends LitElement {
13
+ static styles = [
14
+ CommonHeaderStyles,
15
+ ScrollbarStyles,
16
+ css`
17
+ :host {
18
+ display: flex;
19
+ flex-direction: column;
20
+
21
+ background-color: var(--md-sys-color-surface);
22
+
23
+ width: var(--overlay-center-normal-width, 50%);
24
+ height: var(--overlay-center-normal-height, 50%);
25
+ }
26
+
27
+ ox-input-crontab {
28
+ flex: 1;
29
+ overflow-y: auto;
30
+ }
31
+
32
+ span {
33
+ flex: 1;
34
+
35
+ display: flex;
36
+ align-items: center;
37
+ justify-content: center;
38
+
39
+ color: var(--md-sys-color-on-primary-container);
40
+ }
41
+ `
42
+ ]
43
+
44
+ @property({ type: Object }) value: any
45
+ @property({ type: Object }) confirmCallback!: (newval: any) => void
46
+
47
+ render() {
48
+ return html`
49
+ <ox-input-crontab .value=${this.value} @change=${this.onChange.bind(this)}> </ox-input-crontab>
50
+
51
+ <div class="footer">
52
+ <div filler></div>
53
+ <button @click=${this.onCancel.bind(this)} danger>
54
+ <md-icon>cancel</md-icon>${i18next.t('button.cancel')}
55
+ </button>
56
+ <button @click=${this.onConfirm.bind(this)} done><md-icon>done</md-icon>${i18next.t('button.confirm')}</button>
57
+ </div>
58
+ `
59
+ }
60
+
61
+ private onChange(e: CustomEvent) {
62
+ e.stopPropagation()
63
+
64
+ this.value = e.detail
65
+ }
66
+
67
+ private onCancel(e: Event) {
68
+ closePopup(this)
69
+ }
70
+
71
+ private onConfirm(e: Event) {
72
+ this.confirmCallback && this.confirmCallback(this.value)
73
+ closePopup(this)
74
+ }
75
+ }
@@ -0,0 +1,326 @@
1
+ import '@material/web/icon/icon.js'
2
+ import '@material/web/button/elevated-button.js'
3
+ import '@operato/data-grist/ox-grist.js'
4
+ import '@operato/data-grist/ox-filters-form.js'
5
+ import '@operato/data-grist/ox-record-creator.js'
6
+ import './schedule-popup.js'
7
+
8
+ import { CommonHeaderStyles, CommonGristStyles, ScrollbarStyles } from '@operato/styles'
9
+ import { PageView, store } from '@operato/shell'
10
+ import { css, html } from 'lit'
11
+ import { customElement, property, query } from 'lit/decorators.js'
12
+ import { DataGrist, FetchOption } from '@operato/data-grist'
13
+ import { client } from '@operato/graphql'
14
+ import { i18next, localize } from '@operato/i18n'
15
+ import { notify, openPopup } from '@operato/layout'
16
+ import { OxPrompt } from '@operato/popup'
17
+ import { isMobileDevice } from '@operato/utils'
18
+
19
+ import { connect } from 'pwa-helpers/connect-mixin'
20
+ import gql from 'graphql-tag'
21
+
22
+ @customElement('schedule-list-page')
23
+ export class SchedulerListPage extends connect(store)(localize(i18next)(PageView)) {
24
+ static styles = [
25
+ ScrollbarStyles,
26
+ CommonGristStyles,
27
+ CommonHeaderStyles,
28
+ css`
29
+ :host {
30
+ display: flex;
31
+
32
+ width: 100%;
33
+
34
+ --grid-record-emphasized-background-color: #8b0000;
35
+ --grid-record-emphasized-color: #ff6b6b;
36
+ }
37
+
38
+ ox-grist {
39
+ overflow-y: auto;
40
+ flex: 1;
41
+ }
42
+
43
+ ox-filters-form {
44
+ flex: 1;
45
+ }
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
+
54
+ get context() {
55
+ return {
56
+ title: i18next.t('title.schedule list'),
57
+ search: {
58
+ handler: (search: string) => {
59
+ this.grist.searchText = search
60
+ },
61
+ value: this.grist.searchText
62
+ },
63
+ filter: {
64
+ handler: () => {
65
+ this.grist.toggleHeadroom()
66
+ }
67
+ },
68
+ help: 'scheduler/scheduler'
69
+ }
70
+ }
71
+
72
+ render() {
73
+ const mode = this.mode || (isMobileDevice() ? 'CARD' : 'GRID')
74
+
75
+ return html`
76
+ <ox-grist .mode=${mode} .config=${this.gristConfig} .fetchHandler=${this.fetchHandler.bind(this)}>
77
+ <div slot="headroom" class="header">
78
+ <div class="filters">
79
+ <ox-filters-form autofocus without-search></ox-filters-form>
80
+
81
+ <div id="modes">
82
+ <md-icon @click=${() => (this.mode = 'GRID')} ?active=${mode == 'GRID'}>grid_on</md-icon>
83
+ <md-icon @click=${() => (this.mode = 'LIST')} ?active=${mode == 'LIST'}>format_list_bulleted</md-icon>
84
+ <md-icon @click=${() => (this.mode = 'CARD')} ?active=${mode == 'CARD'}>apps</md-icon>
85
+ </div>
86
+
87
+ <ox-record-creator id="add" .customPopupCallback=${this.popupCallback.bind(this)}>
88
+ <button>
89
+ <md-icon>add</md-icon>
90
+ </button>
91
+ <schedule-popup slot="popup"></schedule-popup>
92
+ </ox-record-creator>
93
+ </div>
94
+ </div>
95
+ </ox-grist>
96
+ `
97
+ }
98
+
99
+ async pageInitialized(lifecycle: any) {
100
+ this.gristConfig = {
101
+ list: {
102
+ fields: ['name', 'description'],
103
+ details: ['active', 'updatedAt']
104
+ },
105
+ columns: [
106
+ { type: 'gutter', gutterName: 'sequence' },
107
+ { type: 'gutter', gutterName: 'row-selector', multiple: true },
108
+ {
109
+ type: 'gutter',
110
+ gutterName: 'button',
111
+ fixed: true,
112
+ icon: record => (!record ? 'reorder' : record.id ? 'reorder' : ''),
113
+ iconOnly: false,
114
+ title: record => (!record ? i18next.t('button.detail') : record.id ? i18next.t('button.detail') : ''),
115
+ width: 72,
116
+ handlers: {
117
+ click: (columns, data, column, record, rowIndex) => {
118
+ if (!record.id) return
119
+ openPopup(
120
+ html`
121
+ <schedule-popup schedule-id=${record.id} @requestRefresh=${() => this.grist.fetch()}></schedule-popup>
122
+ `,
123
+ {
124
+ backdrop: true,
125
+ help: 'scheduler/scheduler',
126
+ size: 'large',
127
+ title: i18next.t('title.schedule detail')
128
+ }
129
+ )
130
+ }
131
+ }
132
+ },
133
+ {
134
+ type: 'gutter',
135
+ gutterName: 'button',
136
+ fixed: true,
137
+ icon: record => (!record ? 'delete' : record.id ? 'delete' : ''),
138
+ iconOnly: false,
139
+ title: record => (!record ? i18next.t('button.delete') : record.id ? i18next.t('button.delete') : ''),
140
+ width: 72,
141
+ handlers: {
142
+ click: (_columns, _data, _column, record, _rowIndex) => {
143
+ if (record) this.unregisterSchedule(record.id)
144
+ }
145
+ }
146
+ },
147
+ {
148
+ type: 'string',
149
+ name: 'name',
150
+ header: i18next.t('field.name'),
151
+ record: {
152
+ editable: false
153
+ },
154
+ filter: 'i_like' /* only i_like supported */,
155
+ sortable: true,
156
+ width: 150
157
+ },
158
+ {
159
+ type: 'string',
160
+ name: 'application',
161
+ header: i18next.t('field.schedule-application'),
162
+ record: {
163
+ renderer: (value, column, record, rowIndex, field) => record.client?.application
164
+ },
165
+ filter: 'i_like' /* only i_like supported */,
166
+ sortable: true,
167
+ width: 150
168
+ },
169
+ {
170
+ type: 'string',
171
+ name: 'group',
172
+ header: i18next.t('field.group'),
173
+ record: {
174
+ renderer: (value, column, record, rowIndex, field) => record.client?.group
175
+ },
176
+ filter: 'i_like' /* only i_like supported */,
177
+ sortable: true,
178
+ width: 150
179
+ },
180
+ {
181
+ type: 'string',
182
+ name: 'type',
183
+ header: i18next.t('field.type'),
184
+ record: {
185
+ renderer: (value, column, record, rowIndex, field) => record.client?.type
186
+ },
187
+ filter: 'i_like' /* only i_like supported */,
188
+ sortable: true,
189
+ width: 150
190
+ },
191
+ {
192
+ type: 'string',
193
+ name: 'key',
194
+ header: i18next.t('field.key'),
195
+ record: {
196
+ renderer: (value, column, record, rowIndex, field) => record.client?.key
197
+ },
198
+ filter: 'i_like' /* only i_like supported */,
199
+ sortable: true,
200
+ width: 150
201
+ },
202
+ {
203
+ type: 'string',
204
+ name: 'operation',
205
+ header: i18next.t('field.schedule-operation'),
206
+ record: {
207
+ renderer: (value, column, record, rowIndex, field) => record.client?.operation
208
+ },
209
+ filter: 'i_like' /* only i_like supported */,
210
+ sortable: true,
211
+ width: 150
212
+ },
213
+ {
214
+ type: 'datetime',
215
+ name: 'updatedAt',
216
+ header: i18next.t('field.updated_at'),
217
+ record: {
218
+ editable: false
219
+ },
220
+ sortable: true,
221
+ width: 180
222
+ }
223
+ ],
224
+ rows: {
225
+ appendable: false,
226
+ selectable: {
227
+ multiple: true
228
+ }
229
+ },
230
+ sorters: [
231
+ {
232
+ name: 'name'
233
+ }
234
+ ]
235
+ }
236
+ }
237
+
238
+ async pageUpdated(changes: any, lifecycle: any) {
239
+ if (this.active) {
240
+ this.grist.fetch()
241
+ }
242
+ }
243
+
244
+ async popupCallback(popup) {
245
+ popup.onclosed = () => {}
246
+ }
247
+
248
+ async fetchHandler({ page = 1, limit = 100, sortings = [], filters = [] }: FetchOption) {
249
+ const response = await client.query({
250
+ query: gql`
251
+ query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {
252
+ responses: schedules(filters: $filters, pagination: $pagination, sortings: $sortings) {
253
+ items {
254
+ id
255
+ name
256
+ client {
257
+ application
258
+ group
259
+ type
260
+ key
261
+ operation
262
+ }
263
+ type
264
+ schedule
265
+ timezone
266
+ task {
267
+ type
268
+ connection {
269
+ host
270
+ headers
271
+ topic
272
+ }
273
+ data
274
+ history_check
275
+ failed_policy
276
+ retry_wait
277
+ retry_count
278
+ max_retry_count
279
+ }
280
+ }
281
+ total
282
+ }
283
+ }
284
+ `,
285
+ variables: {
286
+ filters,
287
+ pagination: { page, limit },
288
+ sortings
289
+ }
290
+ })
291
+
292
+ return {
293
+ total: response.data.responses.total || 0,
294
+ records: response.data.responses.items || []
295
+ }
296
+ }
297
+
298
+ async unregisterSchedule(handle: string) {
299
+ if (
300
+ await OxPrompt.open({
301
+ title: i18next.t('text.are_you_sure'),
302
+ text: i18next.t('text.sure_to_x', { x: i18next.t('text.delete') }),
303
+ confirmButton: { text: i18next.t('button.confirm') },
304
+ cancelButton: { text: i18next.t('button.cancel') }
305
+ })
306
+ ) {
307
+ const response = await client.mutate({
308
+ mutation: gql`
309
+ mutation ($handle: ID!) {
310
+ unregisterSchedule(handle: $handle)
311
+ }
312
+ `,
313
+ variables: {
314
+ handle
315
+ }
316
+ })
317
+
318
+ if (!response.errors) {
319
+ this.grist.fetch()
320
+ notify({
321
+ message: i18next.t('text.info_x_successfully', { x: i18next.t('text.delete') })
322
+ })
323
+ }
324
+ }
325
+ }
326
+ }