@things-factory/pdf 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.
Files changed (29) hide show
  1. package/client/bootstrap.js +6 -0
  2. package/client/pages/pdf-release/pdf-release-importer.ts +87 -0
  3. package/client/pages/pdf-release/pdf-release-list-page.ts +398 -0
  4. package/client/pages/pdf-template/pdf-template-importer.ts +87 -0
  5. package/client/pages/pdf-template/pdf-template-list-page.ts +491 -0
  6. package/client/route.ts +11 -0
  7. package/client/tsconfig.json +13 -0
  8. package/dist-client/tsconfig.tsbuildinfo +1 -1
  9. package/dist-server/tsconfig.tsbuildinfo +1 -1
  10. package/package.json +4 -4
  11. package/server/controller/pdf-service.ts +35 -0
  12. package/server/index.ts +3 -0
  13. package/server/routers/pdf-private-router.ts +85 -0
  14. package/server/routers/pdf-public-router.ts +80 -0
  15. package/server/routers/proxy-router.ts +9 -0
  16. package/server/routes.ts +19 -0
  17. package/server/service/index.ts +38 -0
  18. package/server/service/pdf-generate/pdf-generate-resolver.ts +81 -0
  19. package/server/service/pdf-release/index.ts +7 -0
  20. package/server/service/pdf-release/pdf-release-mutation.ts +138 -0
  21. package/server/service/pdf-release/pdf-release-query.ts +51 -0
  22. package/server/service/pdf-release/pdf-release-type.ts +55 -0
  23. package/server/service/pdf-release/pdf-release.ts +103 -0
  24. package/server/service/pdf-template/index.ts +7 -0
  25. package/server/service/pdf-template/pdf-template-mutation.ts +138 -0
  26. package/server/service/pdf-template/pdf-template-query.ts +51 -0
  27. package/server/service/pdf-template/pdf-template-type.ts +87 -0
  28. package/server/service/pdf-template/pdf-template.ts +108 -0
  29. package/server/tsconfig.json +10 -0
@@ -0,0 +1,491 @@
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
+
7
+ import { CommonButtonStyles, CommonHeaderStyles, CommonGristStyles, ScrollbarStyles } from '@operato/styles'
8
+ import { PageView, store } from '@operato/shell'
9
+ import { css, html } from 'lit'
10
+ import { customElement, property, query } from 'lit/decorators.js'
11
+ import { ScopedElementsMixin } from '@open-wc/scoped-elements'
12
+ import { ColumnConfig, 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 { OxPopup, 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
+ import { PDFTemplateImporter } from './pdf-template-importer'
23
+
24
+ @customElement('pdf-template-list-page')
25
+ export class PDFTemplateListPage extends connect(store)(localize(i18next)(ScopedElementsMixin(PageView))) {
26
+ static styles = [
27
+ ScrollbarStyles,
28
+ CommonGristStyles,
29
+ CommonHeaderStyles,
30
+ css`
31
+ :host {
32
+ display: flex;
33
+
34
+ width: 100%;
35
+
36
+ --grid-record-emphasized-background-color: #8b0000;
37
+ --grid-record-emphasized-color: #ff6b6b;
38
+ }
39
+
40
+ ox-grist {
41
+ overflow-y: auto;
42
+ flex: 1;
43
+ }
44
+
45
+ ox-filters-form {
46
+ flex: 1;
47
+ }
48
+ `
49
+ ]
50
+
51
+ static get scopedElements() {
52
+ return {
53
+ 'pdf-template-importer': PDFTemplateImporter
54
+ }
55
+ }
56
+
57
+ @property({ type: Object }) gristConfig: any
58
+ @property({ type: String }) mode: 'CARD' | 'GRID' | 'LIST' = isMobileDevice() ? 'CARD' : 'GRID'
59
+
60
+ @query('ox-grist') private grist!: DataGrist
61
+
62
+ get context() {
63
+ return {
64
+ title: i18next.t('title.pdf-template list'),
65
+ search: {
66
+ handler: (search: string) => {
67
+ this.grist.searchText = search
68
+ },
69
+ value: this.grist.searchText
70
+ },
71
+ filter: {
72
+ handler: () => {
73
+ this.grist.toggleHeadroom()
74
+ }
75
+ },
76
+ help: 'pdf/pdf-template',
77
+ actions: [
78
+ {
79
+ title: i18next.t('button.save'),
80
+ action: this._updatePDFTemplate.bind(this),
81
+ ...CommonButtonStyles.save
82
+ },
83
+ {
84
+ title: i18next.t('button.delete'),
85
+ action: this._deletePDFTemplate.bind(this),
86
+ ...CommonButtonStyles.delete
87
+ }
88
+ ],
89
+ exportable: {
90
+ name: i18next.t('title.pdf-template list'),
91
+ data: this.exportHandler.bind(this)
92
+ },
93
+ importable: {
94
+ handler: this.importHandler.bind(this)
95
+ }
96
+ }
97
+ }
98
+
99
+ render() {
100
+ const mode = this.mode || (isMobileDevice() ? 'CARD' : 'GRID')
101
+
102
+ return html`
103
+ <ox-grist .mode=${mode} .config=${this.gristConfig} .fetchHandler=${this.fetchHandler.bind(this)}>
104
+ <div slot="headroom" class="header">
105
+ <div class="filters">
106
+ <ox-filters-form autofocus without-search></ox-filters-form>
107
+
108
+ <div id="modes">
109
+ <md-icon @click=${() => (this.mode = 'GRID')} ?active=${mode == 'GRID'}>grid_on</md-icon>
110
+ <md-icon @click=${() => (this.mode = 'LIST')} ?active=${mode == 'LIST'}>format_list_bulleted</md-icon>
111
+ <md-icon @click=${() => (this.mode = 'CARD')} ?active=${mode == 'CARD'}>apps</md-icon>
112
+ </div>
113
+
114
+ <ox-record-creator id="add" .callback=${this.creationCallback.bind(this)}>
115
+ <button>
116
+ <md-icon>add</md-icon>
117
+ </button>
118
+ </ox-record-creator>
119
+ </div>
120
+ </div>
121
+ </ox-grist>
122
+ `
123
+ }
124
+
125
+ async pageInitialized(lifecycle: any) {
126
+ this.gristConfig = {
127
+ list: {
128
+ fields: ['name', 'description'],
129
+ details: ['active', 'updatedAt']
130
+ },
131
+ columns: [
132
+ { type: 'gutter', gutterName: 'sequence' },
133
+ { type: 'gutter', gutterName: 'row-selector', multiple: true },
134
+ {
135
+ type: 'string',
136
+ name: 'name',
137
+ header: i18next.t('field.name'),
138
+ record: {
139
+ editable: true
140
+ },
141
+ filter: 'search',
142
+ sortable: true,
143
+ width: 150
144
+ },
145
+ {
146
+ type: 'string',
147
+ name: 'description',
148
+ header: i18next.t('field.description'),
149
+ record: {
150
+ editable: true
151
+ },
152
+ filter: 'search',
153
+ width: 200
154
+ },
155
+ {
156
+ type: 'checkbox',
157
+ name: 'active',
158
+ label: true,
159
+ header: i18next.t('field.active'),
160
+ record: {
161
+ editable: true
162
+ },
163
+ filter: true,
164
+ sortable: true,
165
+ width: 60
166
+ },
167
+ {
168
+ type: 'string',
169
+ name: 'state',
170
+ label: true,
171
+ header: i18next.t('field.state'),
172
+ record: {
173
+ editable: true
174
+ },
175
+ width: 120
176
+ },
177
+ {
178
+ type: 'template',
179
+ name: 'content_template',
180
+ label: true,
181
+ header: i18next.t('field.content_template'),
182
+ record: {
183
+ editable: true,
184
+ options: {
185
+ language: 'html'
186
+ }
187
+ },
188
+ width: 120
189
+ },
190
+ {
191
+ type: 'template',
192
+ name: 'header_template',
193
+ label: true,
194
+ header: i18next.t('field.header_template'),
195
+ record: {
196
+ editable: true,
197
+ options: {
198
+ language: 'html'
199
+ }
200
+ },
201
+ width: 120
202
+ },
203
+ {
204
+ type: 'template',
205
+ name: 'footer_template',
206
+ label: true,
207
+ header: i18next.t('field.footer_template'),
208
+ record: {
209
+ editable: true,
210
+ options: {
211
+ language: 'html'
212
+ }
213
+ },
214
+ width: 120
215
+ },
216
+ {
217
+ type: 'template',
218
+ name: 'cover_template',
219
+ label: true,
220
+ header: i18next.t('field.cover_template'),
221
+ record: {
222
+ editable: true,
223
+ options: {
224
+ language: 'html'
225
+ }
226
+ },
227
+ width: 120
228
+ },
229
+ {
230
+ type: 'template',
231
+ name: 'last_template',
232
+ label: true,
233
+ header: i18next.t('field.last_template'),
234
+ record: {
235
+ editable: true,
236
+ options: {
237
+ language: 'html'
238
+ }
239
+ },
240
+ width: 120
241
+ },
242
+ {
243
+ type: 'select',
244
+ name: 'page_size',
245
+ label: true,
246
+ header: i18next.t('field.page_size'),
247
+ record: {
248
+ editable: true,
249
+ options: ['', 'A4', 'B4']
250
+ },
251
+ width: 120
252
+ },
253
+ {
254
+ type: 'string',
255
+ name: 'watermark',
256
+ label: true,
257
+ header: i18next.t('field.watermark'),
258
+ record: {
259
+ editable: true
260
+ },
261
+ width: 120
262
+ },
263
+
264
+ {
265
+ type: 'resource-object',
266
+ name: 'updater',
267
+ header: i18next.t('field.updater'),
268
+ record: {
269
+ editable: false
270
+ },
271
+ sortable: true,
272
+ width: 120
273
+ },
274
+ {
275
+ type: 'datetime',
276
+ name: 'updatedAt',
277
+ header: i18next.t('field.updated_at'),
278
+ record: {
279
+ editable: false
280
+ },
281
+ sortable: true,
282
+ width: 180
283
+ }
284
+ ],
285
+ rows: {
286
+ appendable: false,
287
+ selectable: {
288
+ multiple: true
289
+ }
290
+ },
291
+ sorters: [
292
+ {
293
+ name: 'name'
294
+ }
295
+ ]
296
+ }
297
+ }
298
+
299
+ async pageUpdated(changes: any, lifecycle: any) {
300
+ if (this.active) {
301
+ // do something here when this page just became as active
302
+ }
303
+ }
304
+
305
+ async fetchHandler({ page = 1, limit = 100, sortings = [], filters = [] }: FetchOption) {
306
+ const response = await client.query({
307
+ query: gql`
308
+ query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {
309
+ responses: PDFTemplates(filters: $filters, pagination: $pagination, sortings: $sortings) {
310
+ items {
311
+ id
312
+ name
313
+ description
314
+ active
315
+ state
316
+ content_template
317
+ header_template
318
+ footer_template
319
+ cover_template
320
+ last_template
321
+ page_size
322
+ watermark
323
+ updater {
324
+ id
325
+ name
326
+ }
327
+ updatedAt
328
+ }
329
+ total
330
+ }
331
+ }
332
+ `,
333
+ variables: {
334
+ filters,
335
+ pagination: { page, limit },
336
+ sortings
337
+ }
338
+ })
339
+
340
+ return {
341
+ total: response.data.responses.total || 0,
342
+ records: response.data.responses.items || []
343
+ }
344
+ }
345
+
346
+ async _deletePDFTemplate() {
347
+ if (
348
+ await OxPrompt.open({
349
+ title: i18next.t('text.are_you_sure'),
350
+ text: i18next.t('text.sure_to_x', { x: i18next.t('text.delete') }),
351
+ confirmButton: { text: i18next.t('button.confirm') },
352
+ cancelButton: { text: i18next.t('button.cancel') }
353
+ })
354
+ ) {
355
+ const ids = this.grist.selected.map(record => record.id)
356
+ if (ids && ids.length > 0) {
357
+ const response = await client.mutate({
358
+ mutation: gql`
359
+ mutation ($ids: [String!]!) {
360
+ deletePDFTemplates(ids: $ids)
361
+ }
362
+ `,
363
+ variables: {
364
+ ids
365
+ }
366
+ })
367
+
368
+ if (!response.errors) {
369
+ this.grist.fetch()
370
+ notify({
371
+ message: i18next.t('text.info_x_successfully', { x: i18next.t('text.delete') })
372
+ })
373
+ }
374
+ }
375
+ }
376
+ }
377
+
378
+ async _updatePDFTemplate() {
379
+ let patches = this.grist.dirtyRecords
380
+ if (patches && patches.length) {
381
+ patches = patches.map(patch => {
382
+ let patchField: any = patch.id ? { id: patch.id } : {}
383
+ const dirtyFields = patch.__dirtyfields__
384
+ for (let key in dirtyFields) {
385
+ patchField[key] = dirtyFields[key].after
386
+ }
387
+ patchField.cuFlag = patch.__dirty__
388
+
389
+ return patchField
390
+ })
391
+
392
+ const response = await client.mutate({
393
+ mutation: gql`
394
+ mutation ($patches: [PDFTemplatePatch!]!) {
395
+ updateMultiplePDFTemplate(patches: $patches) {
396
+ name
397
+ }
398
+ }
399
+ `,
400
+ variables: {
401
+ patches
402
+ }
403
+ })
404
+
405
+ if (!response.errors) {
406
+ this.grist.fetch()
407
+ }
408
+ }
409
+ }
410
+
411
+ async creationCallback(pdfTemplate) {
412
+ try {
413
+ const response = await client.query({
414
+ query: gql`
415
+ mutation ($pdfTemplate: NewPDFTemplate!) {
416
+ createPDFTemplate(pdfTemplate: $pdfTemplate) {
417
+ id
418
+ }
419
+ }
420
+ `,
421
+ variables: {
422
+ pdfTemplate
423
+ },
424
+ context: {
425
+ hasUpload: true
426
+ }
427
+ })
428
+
429
+ if (!response.errors) {
430
+ this.grist.fetch()
431
+ document.dispatchEvent(
432
+ new CustomEvent('notify', {
433
+ detail: {
434
+ message: i18next.t('text.data_created_successfully')
435
+ }
436
+ })
437
+ )
438
+ }
439
+
440
+ return true
441
+ } catch (ex) {
442
+ console.error(ex)
443
+ document.dispatchEvent(
444
+ new CustomEvent('notify', {
445
+ detail: {
446
+ type: 'error',
447
+ message: i18next.t('text.error')
448
+ }
449
+ })
450
+ )
451
+ return false
452
+ }
453
+ }
454
+
455
+ async exportHandler() {
456
+ const exportTargets = this.grist.selected.length ? this.grist.selected : this.grist.dirtyData.records
457
+ const targetFieldSet = new Set(['id', 'name', 'description', 'active'])
458
+
459
+ return exportTargets.map(pdfTemplate => {
460
+ let tempObj = {}
461
+ for (const field of targetFieldSet) {
462
+ tempObj[field] = pdfTemplate[field]
463
+ }
464
+
465
+ return tempObj
466
+ })
467
+ }
468
+
469
+ async importHandler(records) {
470
+ const popup = openPopup(
471
+ html`
472
+ <pdf-template-importer
473
+ .pdfTemplates=${records}
474
+ @imported=${() => {
475
+ history.back()
476
+ this.grist.fetch()
477
+ }}
478
+ ></pdf-template-importer>
479
+ `,
480
+ {
481
+ backdrop: true,
482
+ size: 'large',
483
+ title: i18next.t('title.import pdf-template')
484
+ }
485
+ )
486
+
487
+ popup.onclosed = () => {
488
+ this.grist.fetch()
489
+ }
490
+ }
491
+ }
@@ -0,0 +1,11 @@
1
+ export default function route(page: string) {
2
+ switch (page) {
3
+ case 'pdf-template-list-page':
4
+ import('./pages/pdf-template/pdf-template-list-page')
5
+ return page
6
+
7
+ case 'pdf-release-list':
8
+ import('./pages/pdf-release/pdf-release-list-page')
9
+ return page
10
+ }
11
+ }
@@ -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
+ }