@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,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 { TaskResourceImporter } from './task-resource-importer'
25
-
26
- @customElement('task-resource-list-page')
27
- export class TaskResourceListPage 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
- 'task-resource-importer': TaskResourceImporter
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.task-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/task-resource',
71
- actions: [
72
- {
73
- title: i18next.t('button.save'),
74
- action: this.updateTaskResource.bind(this),
75
- ...CommonButtonStyles.save
76
- },
77
- {
78
- title: i18next.t('button.delete'),
79
- action: this.deleteTaskResource.bind(this),
80
- ...CommonButtonStyles.delete
81
- }
82
- ],
83
- exportable: {
84
- name: i18next.t('title.task-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: taskResources(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 deleteTaskResource() {
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
- deleteTaskResources(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 updateTaskResource() {
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: [TaskResourcePatch!]!) {
298
- updateMultipleTaskResource(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(taskResource => {
324
- let tempObj = {}
325
- for (const field of targetFieldSet) {
326
- tempObj[field] = taskResource[field]
327
- }
328
-
329
- return tempObj
330
- })
331
- }
332
-
333
- private async importHandler(records) {
334
- const popup = openPopup(
335
- html`
336
- <task-resource-importer
337
- .taskResources=${records}
338
- @imported=${() => {
339
- history.back()
340
- this.grist.fetch()
341
- }}
342
- ></task-resource-importer>
343
- `,
344
- {
345
- backdrop: true,
346
- size: 'large',
347
- title: i18next.t('title.import task-resource')
348
- }
349
- )
350
-
351
- popup.onclosed = () => {
352
- this.grist.fetch()
353
- }
354
- }
355
- }
356
-
package/client/route.ts DELETED
@@ -1,55 +0,0 @@
1
- export default function route(page: string) {
2
- switch (page) {
3
- case 'project-list':
4
- import('./pages/project/project-list')
5
- return page
6
-
7
- case 'project-detail':
8
- import('./pages/project/project-detail')
9
- return page
10
-
11
- case 'project-completed-list':
12
- import('./pages/project/project-completed-list')
13
- return page
14
-
15
- case 'project-schedule-list':
16
- import('./pages/project/project-schedule-list')
17
- return page
18
-
19
- case 'project-schedule':
20
- import('./pages/project/project-schedule')
21
- return page
22
-
23
- case 'project-setting-list':
24
- import('./pages/project/project-setting-list')
25
- return page
26
-
27
- case 'project-update':
28
- import('./pages/project/project-update')
29
- return page
30
-
31
- case 'project-plan-management':
32
- import('./pages/project/project-plan-management')
33
- return page
34
-
35
- case 'worker-type-management':
36
- import('./pages/resource/worker-type-management')
37
- return page
38
-
39
- case 'construction-type-management':
40
- import('./pages/resource/construction-type-management')
41
- return page
42
-
43
- case 'inspection-drawing-type-management':
44
- import('./pages/resource/inspection-drawing-type-management')
45
- return page
46
-
47
- case 'resource-list':
48
- import('./pages/resource/resource-list-page')
49
- return page
50
-
51
- case 'task-resource-list':
52
- import('./pages/task-resource/task-resource-list-page')
53
- return page
54
- }
55
- }
@@ -1,11 +0,0 @@
1
- {
2
- "extends": "../../tsconfig-base.json",
3
- "compilerOptions": {
4
- "strict": true,
5
- "declaration": true,
6
- "module": "esnext",
7
- "outDir": "../dist-client",
8
- "baseUrl": "./"
9
- },
10
- "include": ["./**/*"]
11
- }
@@ -1,40 +0,0 @@
1
- import { Workbook, Worksheet } from 'exceljs'
2
-
3
- export interface Task {
4
- name: string
5
- startDate: Date
6
- endDate: Date
7
- subtasks?: Task[]
8
- }
9
-
10
- function createGanttChart(tasks: Task[], worksheet: Worksheet, level: number = 0) {
11
- tasks.forEach(task => {
12
- const row = worksheet.addRow([task.name, task.startDate, task.endDate])
13
-
14
- row.outlineLevel = level
15
-
16
- if (task.subtasks && task.subtasks.length > 0) {
17
- createGanttChart(task.subtasks, worksheet, level + 1)
18
- }
19
- })
20
- }
21
-
22
- export async function generateExcel(tasks: Task[]) {
23
- const workbook = new Workbook()
24
- const worksheet = workbook.addWorksheet('Gantt Chart')
25
-
26
- worksheet.properties.outlineProperties = {
27
- summaryBelow: false,
28
- summaryRight: false
29
- }
30
-
31
- worksheet.columns = [
32
- { header: 'Task Name', key: 'name', width: 30 },
33
- { header: 'Start Date', key: 'startDate', width: 20 },
34
- { header: 'End Date', key: 'endDate', width: 20 }
35
- ]
36
-
37
- createGanttChart(tasks, worksheet)
38
-
39
- return await workbook.xlsx.writeBuffer()
40
- }
@@ -1,134 +0,0 @@
1
- import { getRepository } from '@things-factory/shell'
2
-
3
- import { Project } from '../service/project/project'
4
- import { Task, TaskType } from '../service/task/task'
5
- import { TaskResource } from '../service/task-resource/task-resource'
6
- import { Resource } from '../service/resource/resource'
7
- import { RawTask } from './types'
8
-
9
- function excelSerialToJSDate(serial) {
10
- const excelEpoch = new Date(1899, 11, 30) // Excel epoch (30th December 1899)
11
- const days = Math.floor(serial) // Get the number of days
12
- const milliseconds = (serial - days) * 86400 * 1000 // Convert the fractional day part to milliseconds
13
-
14
- const jsDate = new Date(excelEpoch.getTime() + days * 86400 * 1000 + milliseconds)
15
- return jsDate
16
- }
17
-
18
- export async function importTasks(project: Project, tasks: RawTask[], context: ResolverContext) {
19
- const { domain, user, tx } = context.state
20
-
21
- const taskRepository = getRepository(Task, tx)
22
- const resourceRepository = getRepository(Resource, tx)
23
- const taskResourceRepository = getRepository(TaskResource, tx)
24
-
25
- // 1. 기존 태스크와 리소스를 Soft Delete
26
- await taskRepository.softDelete({ project: { id: project.id } })
27
-
28
- // 2. 태스크 임포트
29
- const importTaskData = async (rawTask: RawTask, parent?: Task) => {
30
- if (rawTask.children && rawTask.children.length > 0) {
31
- rawTask.type = TaskType.GROUP
32
- } else {
33
- rawTask.type = TaskType.TASK
34
- }
35
-
36
- // 유효성 검사
37
- if (!rawTask.title || !rawTask.code || (rawTask.type == TaskType.TASK && (rawTask.duration ?? null) === null)) {
38
- throw new Error(`Task '${rawTask.code}' is missing required fields.`)
39
- }
40
-
41
- if (rawTask.type == TaskType.TASK) {
42
- // 시작일, 종료일 계산
43
- var startDate: Date = new Date(rawTask.startDate)
44
-
45
- var endDate
46
- if (!startDate && rawTask.dependsOn) {
47
- const dependsOnTask = await taskRepository.findOne({ where: { code: rawTask.dependsOn, project: { id: project.id } } })
48
- if (dependsOnTask && dependsOnTask.endDate) {
49
- startDate = new Date(dependsOnTask.endDate)
50
- startDate.setDate(startDate.getDate() + 1)
51
- } else {
52
- // TODO handler error
53
- // throw new Error(`Task '${rawTask.code}' depends on a task '${rawTask.dependsOn}' that doesn't have a valid end date.`)
54
- }
55
- }
56
-
57
- if (!startDate) {
58
- throw new Error(`Task '${rawTask.code}' must have either a start date or a valid dependency.`)
59
- }
60
-
61
- const duration = rawTask.duration
62
- endDate = new Date(startDate)
63
- endDate.setDate(startDate.getDate() + duration - 1)
64
- }
65
-
66
- // 태스크 생성 및 저장
67
- var task: Task = await taskRepository.save({
68
- code: rawTask.code,
69
- name: rawTask.title,
70
- type: rawTask.type,
71
- startDate,
72
- endDate,
73
- project,
74
- parent,
75
- duration: rawTask.duration,
76
- dependsOn: rawTask.dependsOn,
77
- progress: rawTask.progress,
78
- tags: rawTask.tags,
79
- style: rawTask.style,
80
- updater: user,
81
- creator: user
82
- })
83
-
84
- // 리소스 생성 및 저장
85
- if (rawTask.resources) {
86
- for (const resource of rawTask.resources) {
87
- const resourceType = await resourceRepository.findOne({ where: { domain: { id: domain.id }, name: resource.type } })
88
- if (resourceType) {
89
- await taskResourceRepository.save({
90
- task,
91
- resource: resourceType,
92
- quantity: resource.allocated
93
- })
94
- } else {
95
- throw new Error(`unknown resource type: ${resource.type}`)
96
- }
97
- }
98
- }
99
-
100
- // 자식 태스크 처리
101
- if (rawTask.children && rawTask.children.length > 0) {
102
- let lastEndDate = null
103
- let lastStartDate = null
104
- for (const childTask of rawTask.children) {
105
- const subtask = await importTaskData(childTask, task)
106
-
107
- lastEndDate = !lastEndDate ? subtask.endDate : lastEndDate > subtask.endDate ? lastEndDate : subtask.endDate
108
- lastStartDate = !lastStartDate ? subtask.startDate : lastStartDate < subtask.startDate ? lastStartDate : subtask.startDate
109
- }
110
-
111
- // 그룹 태스크의 기간(duration)을 계산합니다.
112
- const calculatedDuration =
113
- lastEndDate && lastStartDate
114
- ? Math.ceil((lastEndDate.getTime() - lastStartDate.getTime()) / (1000 * 60 * 60 * 24)) + 1
115
- : 0
116
-
117
- task = await taskRepository.findOne({ where: { id: task.id } })
118
-
119
- return await taskRepository.save({
120
- ...task,
121
- startDate: lastStartDate,
122
- endDate: lastEndDate,
123
- duration: calculatedDuration
124
- })
125
- }
126
-
127
- return task
128
- }
129
-
130
- // 루트 태스크들 임포트
131
- for (const rootTask of tasks) {
132
- await importTaskData(rootTask)
133
- }
134
- }
File without changes
@@ -1,86 +0,0 @@
1
- import ExcelJS from 'exceljs'
2
- import { RawTask } from './types'
3
- import { Project } from '../service/project/project'
4
- import { importTasks } from './import-task'
5
-
6
- export async function parseExcelAndImportTasks(buffer: Buffer, project: Project, context: ResolverContext) {
7
- // 1. 엑셀 파일을 읽어들입니다.
8
- const workbook = new ExcelJS.Workbook()
9
- await workbook.xlsx.load(buffer)
10
-
11
- // 2. 첫 번째 워크시트를 가져옵니다.
12
- const worksheet = workbook.getWorksheet(1) // Index or sheet name can be used
13
-
14
- // 3. 첫 번째 row를 header로 사용합니다.
15
- const headers: string[] = []
16
- let taskCodeColumnIndex = -1
17
-
18
- const headerRow = worksheet.getRow(1)
19
- headerRow.eachCell((cell, colNumber) => {
20
- const headerText = cell.text.toString()
21
- headers[colNumber - 1] = headerText // Store headers in an array
22
-
23
- if (headerText === '작업코드') {
24
- taskCodeColumnIndex = colNumber // Store the column index for "작업코드"
25
- }
26
- })
27
-
28
- if (taskCodeColumnIndex === -1) {
29
- throw new Error('작업코드 column not found')
30
- }
31
-
32
- // 4. 엑셀 데이터를 RawTask 형식으로 변환합니다.
33
- const tasks: RawTask[] = []
34
-
35
- // Start processing from the second row onward to skip the header
36
- for (let rowIndex = 2; rowIndex <= worksheet.rowCount; rowIndex++) {
37
- const row = worksheet.getRow(rowIndex)
38
- const taskData: any = {}
39
-
40
- row.eachCell((cell, colNumber) => {
41
- const header = headers[colNumber - 1]
42
-
43
- // Check if the cell has a formula(or sharedFormula) and use the formula result
44
- let cellValue: any = cell.value
45
- if (cellValue && typeof cellValue === 'object' && ('formula' in cellValue || 'sharedFormula' in cellValue)) {
46
- // Cell contains a formula, use the calculated result if available
47
- cellValue = cellValue.result ?? cellValue.value // Use the result, or fallback to value if result is not calculated
48
- }
49
-
50
- taskData[header] = cellValue
51
- })
52
-
53
- const taskCodeCell = row.getCell(taskCodeColumnIndex)
54
- let bgColor = '#FFFFFF'
55
- const fill = taskCodeCell.style.fill
56
-
57
- if (fill && fill.type === 'pattern' && fill.pattern === 'solid') {
58
- const fgColor = fill.fgColor
59
- if (fgColor && fgColor.argb) {
60
- // ARGB is a color in the format AARRGGBB, remove the alpha channel (first two characters)
61
- bgColor = `#${fgColor.argb.slice(2)}`
62
- }
63
- }
64
-
65
- const task: RawTask = {
66
- code: taskData['작업코드'],
67
- title: taskData['작업명'],
68
- type: taskData['세부공종'],
69
- duration: taskData['기간'],
70
- startDate: taskData['시작일'],
71
- dependsOn: taskData['선행작업코드'],
72
- progress: taskData['진척율'],
73
- tags: taskData['Tags'] ? taskData['Tags'].split(',') : [],
74
- resources: taskData['Resources'] ? JSON.parse(taskData['Resources']) : [],
75
- style: bgColor,
76
- children: []
77
- }
78
-
79
- if (task.code && task.type) {
80
- tasks.push(task)
81
- }
82
- }
83
-
84
- // 5. 변환된 데이터를 importTasks 함수로 전달합니다.
85
- await importTasks(project, tasks, context)
86
- }
@@ -1,20 +0,0 @@
1
- import { Task, TaskType } from '../service/task/task'
2
-
3
- export interface RawResource {
4
- type: string
5
- allocated: number
6
- }
7
-
8
- export interface RawTask {
9
- code: string
10
- title: string
11
- type?: TaskType
12
- duration?: number
13
- startDate?: Date | string
14
- dependsOn?: string
15
- progress?: number
16
- tags?: string[]
17
- style?: string
18
- resources?: RawResource[]
19
- children?: RawTask[]
20
- }