@dssp/project 1.0.0-alpha.0 → 1.0.0-alpha.1

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 (97) hide show
  1. package/dist-client/tsconfig.tsbuildinfo +1 -1
  2. package/package.json +3 -3
  3. package/client/bootstrap.ts +0 -0
  4. package/client/index.ts +0 -0
  5. package/client/pages/lib/select2-component.ts +0 -175
  6. package/client/pages/lib/waether.ts +0 -159
  7. package/client/pages/project/component/project-update-header.ts +0 -88
  8. package/client/pages/project/popup/popup-plan-upload.ts +0 -138
  9. package/client/pages/project/popup/popup-project-create.ts +0 -147
  10. package/client/pages/project/popup/popup-schedule-upload.ts +0 -102
  11. package/client/pages/project/project-completed-list.ts +0 -281
  12. package/client/pages/project/project-detail.ts +0 -738
  13. package/client/pages/project/project-list.ts +0 -418
  14. package/client/pages/project/project-plan-management.ts +0 -476
  15. package/client/pages/project/project-schedule-list.ts +0 -294
  16. package/client/pages/project/project-schedule.ts +0 -393
  17. package/client/pages/project/project-setting-list.ts +0 -393
  18. package/client/pages/project/project-update.ts +0 -876
  19. package/client/pages/resource/construction-detail-type-popup.ts +0 -201
  20. package/client/pages/resource/construction-type-management.ts +0 -212
  21. package/client/pages/resource/inspection-drawing-type-management.ts +0 -245
  22. package/client/pages/resource/inspection-part-popup.ts +0 -201
  23. package/client/pages/resource/resource-importer.ts +0 -97
  24. package/client/pages/resource/resource-list-page.ts +0 -356
  25. package/client/pages/resource/worker-type-management.ts +0 -192
  26. package/client/pages/task/task-importer.ts +0 -94
  27. package/client/pages/task/task-list-page.ts +0 -340
  28. package/client/pages/task-resource/task-resource-importer.ts +0 -97
  29. package/client/pages/task-resource/task-resource-list-page.ts +0 -356
  30. package/client/route.ts +0 -55
  31. package/client/tsconfig.json +0 -11
  32. package/server/controllers/export-tasks.ts +0 -40
  33. package/server/controllers/import-task.ts +0 -134
  34. package/server/controllers/index.ts +0 -0
  35. package/server/controllers/parse-excel.ts +0 -86
  36. package/server/controllers/types.ts +0 -20
  37. package/server/index.ts +0 -4
  38. package/server/middlewares/index.ts +0 -3
  39. package/server/migrations/1723861466413-seed-roles.ts +0 -128
  40. package/server/migrations/1723861466414-seed-codes.ts +0 -157
  41. package/server/migrations/1723861476419-seed-resources.ts +0 -62
  42. package/server/migrations/1723861478420-seed-/bsample-project.ts +0 -87
  43. package/server/migrations/1723861478421-seed-/bsample-tasks.ts +0 -194
  44. package/server/migrations/index.ts +0 -9
  45. package/server/routes.ts +0 -108
  46. package/server/service/construction-detail-type/construction-detail-type-mutation.ts +0 -57
  47. package/server/service/construction-detail-type/construction-detail-type-query.ts +0 -31
  48. package/server/service/construction-detail-type/construction-detail-type-type.ts +0 -26
  49. package/server/service/construction-detail-type/construction-detail-type.ts +0 -52
  50. package/server/service/construction-detail-type/index.ts +0 -6
  51. package/server/service/construction-type/construction-type-mutation.ts +0 -66
  52. package/server/service/construction-type/construction-type-query.ts +0 -56
  53. package/server/service/construction-type/construction-type-type.ts +0 -26
  54. package/server/service/construction-type/construction-type.ts +0 -74
  55. package/server/service/construction-type/index.ts +0 -6
  56. package/server/service/index.ts +0 -56
  57. package/server/service/inspection-drawing-type/index.ts +0 -6
  58. package/server/service/inspection-drawing-type/inspection-drawing-type-mutation.ts +0 -69
  59. package/server/service/inspection-drawing-type/inspection-drawing-type-query.ts +0 -55
  60. package/server/service/inspection-drawing-type/inspection-drawing-type-type.ts +0 -23
  61. package/server/service/inspection-drawing-type/inspection-drawing-type.ts +0 -68
  62. package/server/service/inspection-part/index.ts +0 -6
  63. package/server/service/inspection-part/inspection-part-mutation.ts +0 -52
  64. package/server/service/inspection-part/inspection-part-query.ts +0 -41
  65. package/server/service/inspection-part/inspection-part-type.ts +0 -26
  66. package/server/service/inspection-part/inspection-part.ts +0 -51
  67. package/server/service/manager/index.ts +0 -6
  68. package/server/service/manager/manager-mutation.ts +0 -42
  69. package/server/service/manager/manager-query.ts +0 -28
  70. package/server/service/manager/manager-type.ts +0 -40
  71. package/server/service/manager/manager.ts +0 -29
  72. package/server/service/project/index.ts +0 -6
  73. package/server/service/project/project-mutation.ts +0 -255
  74. package/server/service/project/project-query.ts +0 -105
  75. package/server/service/project/project-type.ts +0 -72
  76. package/server/service/project/project.ts +0 -134
  77. package/server/service/resource/index.ts +0 -7
  78. package/server/service/resource/resource-mutation.ts +0 -137
  79. package/server/service/resource/resource-query.ts +0 -50
  80. package/server/service/resource/resource-type.ts +0 -41
  81. package/server/service/resource/resource.ts +0 -82
  82. package/server/service/task/index.ts +0 -6
  83. package/server/service/task/task-mutation.ts +0 -135
  84. package/server/service/task/task-query.ts +0 -169
  85. package/server/service/task/task-type.ts +0 -75
  86. package/server/service/task/task.ts +0 -130
  87. package/server/service/task-resource/index.ts +0 -7
  88. package/server/service/task-resource/task-resource-mutation.ts +0 -140
  89. package/server/service/task-resource/task-resource-query.ts +0 -36
  90. package/server/service/task-resource/task-resource-type.ts +0 -41
  91. package/server/service/task-resource/task-resource.ts +0 -51
  92. package/server/service/worker-type/index.ts +0 -6
  93. package/server/service/worker-type/worker-type-mutation.ts +0 -66
  94. package/server/service/worker-type/worker-type-query.ts +0 -47
  95. package/server/service/worker-type/worker-type-type.ts +0 -26
  96. package/server/service/worker-type/worker-type.ts +0 -68
  97. package/server/tsconfig.json +0 -10
@@ -1,201 +0,0 @@
1
- import '@material/web/icon/icon.js'
2
- import '@operato/data-grist/ox-grist.js'
3
-
4
- import gql from 'graphql-tag'
5
- import { css, html, LitElement } from 'lit'
6
- import { customElement, property, query } from 'lit/decorators.js'
7
-
8
- import { DataGrist } from '@operato/data-grist/ox-grist.js'
9
- import { client } from '@operato/graphql'
10
- import { ButtonContainerStyles } from '@operato/styles'
11
- import { FetchOption } from '@operato/data-grist'
12
- import { notify } from '@operato/layout'
13
-
14
- @customElement('inspection-part-popup')
15
- class InspectionPartPopup extends LitElement {
16
- static styles = [
17
- ButtonContainerStyles,
18
- css`
19
- :host {
20
- display: flex;
21
- flex-direction: column;
22
-
23
- background-color: var(--md-sys-color-surface);
24
- }
25
-
26
- ox-grist {
27
- flex: 1;
28
- }
29
- `
30
- ]
31
-
32
- @property({ type: Object }) inspectionDrawingType: any
33
- @property({ type: Object }) gristConfig: any
34
-
35
- @query('ox-grist') grist!: DataGrist
36
-
37
- render() {
38
- return html`
39
- <ox-grist .mode=${'GRID'} .config=${this.gristConfig} .fetchHandler=${this.fetchHandler.bind(this)}></ox-grist>
40
- <div class="button-container">
41
- <button danger @click=${this._deleteInspectionPart.bind(this)}><md-icon>delete</md-icon>삭제</button>
42
- <button @click=${this._updateInspectionPart.bind(this)}><md-icon>save</md-icon>저장</button>
43
- </div>
44
- `
45
- }
46
-
47
- async firstUpdated() {
48
- this.gristConfig = {
49
- columns: [
50
- { type: 'gutter', gutterName: 'row-selector', multiple: true },
51
- {
52
- type: 'gutter',
53
- gutterName: 'button',
54
- fixed: true,
55
- icon: 'add',
56
- handlers: {
57
- click: 'record-copy'
58
- }
59
- },
60
- { type: 'gutter', gutterName: 'sequence' },
61
- {
62
- type: 'gutter',
63
- gutterName: 'button',
64
- icon: 'arrow_upward',
65
- handlers: {
66
- click: 'move-up'
67
- }
68
- },
69
- {
70
- type: 'gutter',
71
- gutterName: 'button',
72
- icon: 'arrow_downward',
73
- handlers: {
74
- click: 'move-down'
75
- }
76
- },
77
- {
78
- type: 'number',
79
- name: 'sequence',
80
- hidden: true
81
- },
82
- {
83
- type: 'string',
84
- name: 'id',
85
- hidden: true
86
- },
87
- {
88
- type: 'string',
89
- name: 'name',
90
- header: '검측 부위',
91
- record: {
92
- editable: true
93
- },
94
- width: 300
95
- }
96
- ],
97
- rows: {
98
- selectable: {
99
- multiple: true
100
- }
101
- },
102
- pagination: {
103
- infinite: true
104
- },
105
- sorters: [{ name: 'sequence' }]
106
- }
107
- }
108
-
109
- async fetchHandler({ page, limit, sorters = [] }: FetchOption) {
110
- const response = await client.query({
111
- query: gql`
112
- query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {
113
- inspectionParts(filters: $filters, pagination: $pagination, sortings: $sortings) {
114
- items {
115
- id
116
- sequence
117
- name
118
- }
119
- }
120
- }
121
- `,
122
- variables: {
123
- filters: {
124
- name: 'inspectionDrawingTypeId',
125
- value: this.inspectionDrawingType.id,
126
- operator: 'eq'
127
- },
128
- sortings: [{ name: 'sequence' }]
129
- }
130
- })
131
-
132
- return {
133
- total: response.data.inspectionParts.total || 0,
134
- records: response.data.inspectionParts.items || []
135
- }
136
- }
137
-
138
- private async _deleteInspectionPart() {
139
- if (confirm('삭제하시겠습니까?')) {
140
- const ids = this.grist.selected.map(record => record.id)
141
- if (ids && ids.length > 0) {
142
- const response = await client.mutate({
143
- mutation: gql`
144
- mutation ($ids: [String!]!) {
145
- deleteInspectionParts(ids: $ids)
146
- }
147
- `,
148
- variables: {
149
- ids
150
- }
151
- })
152
-
153
- if (!response.errors) {
154
- this.grist.fetch()
155
- notify({ message: '삭제되었습니다.' })
156
- }
157
- }
158
- }
159
- }
160
-
161
- async _updateInspectionPart() {
162
- let patches = this.grist.dirtyData.records
163
- if (patches) {
164
- patches = patches.map(patch => {
165
- const { __origin__: { __typename, ...patchField } = {}, __dirtyfields__ } = patch
166
-
167
- for (let key in __dirtyfields__) {
168
- patchField[key] = __dirtyfields__[key].after
169
- }
170
-
171
- return patchField
172
- })
173
-
174
- const response = await client.mutate({
175
- mutation: gql`
176
- mutation UpdateMultipleInspectionPart($inspectionDrawingTypeId: String!, $patches: [InspectionPartPatch!]!) {
177
- updateMultipleInspectionPart(inspectionDrawingTypeId: $inspectionDrawingTypeId, patches: $patches) {
178
- id
179
- }
180
- }
181
- `,
182
- variables: {
183
- inspectionDrawingTypeId: this.inspectionDrawingType.id,
184
- patches
185
- }
186
- })
187
-
188
- if (!response.errors) {
189
- this.grist.fetch()
190
- notify({ message: '저장되었습니다.' })
191
- this.requestRefresh()
192
- } else {
193
- notify({ message: '저장에 실패하였습니다.', level: 'error' })
194
- }
195
- }
196
- }
197
-
198
- requestRefresh() {
199
- this.dispatchEvent(new CustomEvent('requestRefresh'))
200
- }
201
- }
@@ -1,97 +0,0 @@
1
- import '@material/web/button/elevated-button.js'
2
- import '@operato/data-grist'
3
-
4
- import gql from 'graphql-tag'
5
- import { css, html, LitElement } from 'lit'
6
- import { property, state } from 'lit/decorators.js'
7
-
8
- import { client } from '@operato/graphql'
9
- import { i18next } from '@operato/i18n'
10
- import { isMobileDevice } from '@operato/utils'
11
-
12
- export class ResourceImporter extends LitElement {
13
- static styles = [
14
- css`
15
- :host {
16
- display: flex;
17
- flex-direction: column;
18
-
19
- background-color: #fff;
20
- }
21
-
22
- ox-grist {
23
- flex: 1;
24
- }
25
-
26
- .button-container {
27
- display: flex;
28
- margin-left: auto;
29
- padding: var(--padding-default);
30
- }
31
-
32
- md-elevated-button {
33
- margin-left: var(--margin-default);
34
- }
35
- `
36
- ]
37
-
38
- @state() private resources: any[] = []
39
- @state() private columns = {
40
- list: { fields: ['name', 'description'] },
41
- pagination: { infinite: true },
42
- columns: [
43
- {
44
- type: 'string',
45
- name: 'name',
46
- header: i18next.t('field.name'),
47
- width: 150
48
- },
49
- {
50
- type: 'string',
51
- name: 'description',
52
- header: i18next.t('field.description'),
53
- width: 200
54
- },
55
- {
56
- type: 'checkbox',
57
- name: 'active',
58
- header: i18next.t('field.active'),
59
- width: 60
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.resources
72
- }
73
- }
74
- ></ox-grist>
75
-
76
- <div class="button-container">
77
- <md-elevated-button raised @click="${this.save.bind(this)}">${i18next.t('button.save')}</md-elevated-button>
78
- </div>
79
- `
80
- }
81
-
82
- private async save() {
83
- const response = await client.mutate({
84
- mutation: gql`
85
- mutation importResources($resources: [ResourcePatch!]!) {
86
- importResources(resources: $resources)
87
- }
88
- `,
89
- variables: { resources: this.resources }
90
- })
91
-
92
- if (response.errors?.length) return
93
-
94
- this.dispatchEvent(new CustomEvent('imported'))
95
- }
96
- }
97
-
@@ -1,356 +0,0 @@
1
- import '@material/web/icon/icon.js'
2
- import '@operato/data-grist'
3
-
4
- import { CommonButtonStyles, CommonGristStyles, ScrollbarStyles } from '@operato/styles'
5
- import { PageView, store } from '@operato/shell'
6
- import { css, html } from 'lit'
7
- import { customElement, property, query, state } from 'lit/decorators.js'
8
- import { ScopedElementsMixin } from '@open-wc/scoped-elements'
9
- import {
10
- ColumnConfig,
11
- DataGrist,
12
- FetchOption,
13
- SortersControl
14
- } from '@operato/data-grist'
15
- import { client } from '@operato/graphql'
16
- import { i18next, localize } from '@operato/i18n'
17
- import { notify, openPopup } from '@operato/layout'
18
- import { OxPopup, OxPrompt } from '@operato/popup'
19
- import { isMobileDevice } from '@operato/utils'
20
-
21
- import { connect } from 'pwa-helpers/connect-mixin'
22
- import gql from 'graphql-tag'
23
-
24
- import { ResourceImporter } from './resource-importer'
25
-
26
- @customElement('resource-list-page')
27
- export class ResourceListPage extends connect(store)(localize(i18next)(ScopedElementsMixin(PageView))) {
28
-
29
- static styles = [
30
- ScrollbarStyles,
31
- CommonGristStyles,
32
- css`
33
- :host {
34
- display: flex;
35
-
36
- width: 100%;
37
-
38
- --grid-record-emphasized-background-color: red;
39
- --grid-record-emphasized-color: yellow;
40
- }
41
- `
42
- ]
43
-
44
- static get scopedElements() {
45
- return {
46
- 'resource-importer': ResourceImporter
47
- }
48
- }
49
-
50
- @state() private gristConfig: any
51
- @state() private mode: 'CARD' | 'GRID' | 'LIST' = isMobileDevice() ? 'CARD' : 'GRID'
52
-
53
- @query('ox-grist') private grist!: DataGrist
54
- @query('#sorter-control') private sortersControl!: OxPopup
55
-
56
- get context() {
57
- return {
58
- title: i18next.t('title.resource list'),
59
- search: {
60
- handler: (search: string) => {
61
- this.grist.searchText = search
62
- },
63
- value: this.grist.searchText
64
- },
65
- filter: {
66
- handler: () => {
67
- this.grist.toggleHeadroom()
68
- }
69
- },
70
- help: 'project/resource',
71
- actions: [
72
- {
73
- title: i18next.t('button.save'),
74
- action: this.updateResource.bind(this),
75
- ...CommonButtonStyles.save
76
- },
77
- {
78
- title: i18next.t('button.delete'),
79
- action: this.deleteResource.bind(this),
80
- ...CommonButtonStyles.delete
81
- }
82
- ],
83
- exportable: {
84
- name: i18next.t('title.resource list'),
85
- data: this.exportHandler.bind(this)
86
- },
87
- importable: {
88
- handler: this.importHandler.bind(this)
89
- }
90
- }
91
- }
92
-
93
- render() {
94
- const mode = this.mode || (isMobileDevice() ? 'CARD' : 'GRID')
95
-
96
- return html`
97
- <ox-grist
98
- .mode=${mode}
99
- .config=${this.gristConfig}
100
- .fetchHandler=${this.fetchHandler.bind(this)}
101
- >
102
- <div slot="headroom">
103
- <div id="filters">
104
- <ox-filters-form autofocus></ox-filters-form>
105
- </div>
106
-
107
- <div id="sorters">
108
- Sort
109
- <md-icon
110
- @click=${e => {
111
- const target = e.currentTarget
112
- this.sortersControl.open({
113
- right: 0,
114
- top: target.offsetTop + target.offsetHeight
115
- })
116
- }}
117
- >expand_more</md-icon
118
- >
119
- <ox-popup id="sorter-control">
120
- <ox-sorters-control> </ox-sorters-control>
121
- </ox-popup>
122
- </div>
123
-
124
- <div id="modes">
125
- <md-icon @click=${() => (this.mode = 'GRID')} ?active=${mode == 'GRID'}>grid_on</md-icon>
126
- <md-icon @click=${() => (this.mode = 'LIST')} ?active=${mode == 'LIST'}>format_list_bulleted</md-icon>
127
- <md-icon @click=${() => (this.mode = 'CARD')} ?active=${mode == 'CARD'}>apps</md-icon>
128
- </div>
129
- </div>
130
- </ox-grist>
131
- `
132
- }
133
-
134
- async pageInitialized(lifecycle: any) {
135
- this.gristConfig = {
136
- list: {
137
- fields: ['name', 'description'],
138
- details: ['active', 'updatedAt']
139
- },
140
- columns: [
141
- { type: 'gutter', gutterName: 'sequence' },
142
- { type: 'gutter', gutterName: 'row-selector', multiple: true },
143
- {
144
- type: 'string',
145
- name: 'name',
146
- header: i18next.t('field.name'),
147
- record: {
148
- editable: true
149
- },
150
- filter: 'search',
151
- sortable: true,
152
- width: 150
153
- },
154
- {
155
- type: 'string',
156
- name: 'description',
157
- header: i18next.t('field.description'),
158
- record: {
159
- editable: true
160
- },
161
- filter: 'search',
162
- width: 200
163
- },
164
- {
165
- type: 'checkbox',
166
- name: 'active',
167
- label: true,
168
- header: i18next.t('field.active'),
169
- record: {
170
- editable: true
171
- },
172
- filter: true,
173
- sortable: true,
174
- width: 60
175
- },
176
- {
177
- type: 'resource-object',
178
- name: 'updater',
179
- header: i18next.t('field.updater'),
180
- record: {
181
- editable: false
182
- },
183
- sortable: true,
184
- width: 120
185
- },
186
- {
187
- type: 'datetime',
188
- name: 'updatedAt',
189
- header: i18next.t('field.updated_at'),
190
- record: {
191
- editable: false
192
- },
193
- sortable: true,
194
- width: 180
195
- }
196
- ],
197
- rows: {
198
- selectable: {
199
- multiple: true
200
- }
201
- },
202
- sorters: [
203
- {
204
- name: 'name'
205
- }
206
- ]
207
- }
208
- }
209
-
210
- async pageUpdated(changes: any, lifecycle: any) {
211
- if (this.active) {
212
- // do something here when this page just became as active
213
- }
214
- }
215
-
216
- async fetchHandler({ page = 1, limit = 100, sortings = [], filters = [] }: FetchOption) {
217
- const response = await client.query({
218
- query: gql`
219
- query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {
220
- responses: resources(filters: $filters, pagination: $pagination, sortings: $sortings) {
221
- items {
222
- id
223
- name
224
- description
225
- active
226
- updater {
227
- id
228
- name
229
- }
230
- updatedAt
231
- }
232
- total
233
- }
234
- }
235
- `,
236
- variables: {
237
- filters,
238
- pagination: { page, limit },
239
- sortings
240
- }
241
- })
242
-
243
- return {
244
- total: response.data.responses.total || 0,
245
- records: response.data.responses.items || []
246
- }
247
- }
248
-
249
- private async deleteResource() {
250
- if (
251
- await OxPrompt.open({
252
- title: i18next.t('text.are_you_sure'),
253
- text: i18next.t('text.sure_to_x', { x: i18next.t('text.delete') }),
254
- confirmButton: { text: i18next.t('button.confirm') },
255
- cancelButton: { text: i18next.t('button.cancel') }
256
- })
257
- ) {
258
- const ids = this.grist.selected.map(record => record.id)
259
- if (ids && ids.length > 0) {
260
- const response = await client.mutate({
261
- mutation: gql`
262
- mutation ($ids: [String!]!) {
263
- deleteResources(ids: $ids)
264
- }
265
- `,
266
- variables: {
267
- ids
268
- }
269
- })
270
-
271
- if (!response.errors) {
272
- this.grist.fetch()
273
- notify({
274
- message: i18next.t('text.info_x_successfully', { x: i18next.t('text.delete') })
275
- })
276
- }
277
- }
278
- }
279
- }
280
-
281
- private async updateResource() {
282
- let patches = this.grist.dirtyRecords
283
- if (patches && patches.length) {
284
- patches = patches.map(patch => {
285
- let patchField: any = patch.id ? { id: patch.id } : {}
286
- const dirtyFields = patch.__dirtyfields__
287
- for (let key in dirtyFields) {
288
- patchField[key] = dirtyFields[key].after
289
- }
290
- patchField.cuFlag = patch.__dirty__
291
-
292
- return patchField
293
- })
294
-
295
- const response = await client.mutate({
296
- mutation: gql`
297
- mutation ($patches: [ResourcePatch!]!) {
298
- updateMultipleResource(patches: $patches) {
299
- name
300
- }
301
- }
302
- `,
303
- variables: {
304
- patches
305
- }
306
- })
307
-
308
- if (!response.errors) {
309
- this.grist.fetch()
310
- }
311
- }
312
- }
313
-
314
- private async exportHandler() {
315
- const exportTargets = this.grist.selected.length ? this.grist.selected : this.grist.dirtyData.records
316
- const targetFieldSet = new Set([
317
- 'id',
318
- 'name',
319
- 'description',
320
- 'active'
321
- ])
322
-
323
- return exportTargets.map(resource => {
324
- let tempObj = {}
325
- for (const field of targetFieldSet) {
326
- tempObj[field] = resource[field]
327
- }
328
-
329
- return tempObj
330
- })
331
- }
332
-
333
- private async importHandler(records) {
334
- const popup = openPopup(
335
- html`
336
- <resource-importer
337
- .resources=${records}
338
- @imported=${() => {
339
- history.back()
340
- this.grist.fetch()
341
- }}
342
- ></resource-importer>
343
- `,
344
- {
345
- backdrop: true,
346
- size: 'large',
347
- title: i18next.t('title.import resource')
348
- }
349
- )
350
-
351
- popup.onclosed = () => {
352
- this.grist.fetch()
353
- }
354
- }
355
- }
356
-