@dssp/project 0.0.31 → 0.0.33
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.
- package/client/pages/lib/select2-component.ts +21 -16
- package/client/pages/project/component/project-update-header.ts +16 -13
- package/client/pages/project/project-detail.ts +74 -60
- package/client/pages/project/project-plan-management.ts +39 -29
- package/client/pages/project/project-schedule.ts +4 -3
- package/client/pages/project/project-setting-list.ts +9 -9
- package/client/pages/project/project-update.ts +53 -29
- package/dist-client/pages/lib/select2-component.js +21 -16
- package/dist-client/pages/lib/select2-component.js.map +1 -1
- package/dist-client/pages/project/component/project-update-header.js +16 -13
- package/dist-client/pages/project/component/project-update-header.js.map +1 -1
- package/dist-client/pages/project/project-detail.js +74 -60
- package/dist-client/pages/project/project-detail.js.map +1 -1
- package/dist-client/pages/project/project-plan-management.js +39 -29
- package/dist-client/pages/project/project-plan-management.js.map +1 -1
- package/dist-client/pages/project/project-schedule.js +4 -3
- package/dist-client/pages/project/project-schedule.js.map +1 -1
- package/dist-client/pages/project/project-setting-list.js +9 -9
- package/dist-client/pages/project/project-setting-list.js.map +1 -1
- package/dist-client/pages/project/project-update.js +53 -29
- package/dist-client/pages/project/project-update.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/controllers/{project-to-excel.js → export-tasks.js} +1 -1
- package/dist-server/controllers/export-tasks.js.map +1 -0
- package/dist-server/controllers/import-task.d.ts +1 -17
- package/dist-server/controllers/import-task.js +24 -14
- package/dist-server/controllers/import-task.js.map +1 -1
- package/dist-server/controllers/parse-excel.d.ts +4 -0
- package/dist-server/controllers/parse-excel.js +75 -0
- package/dist-server/controllers/parse-excel.js.map +1 -0
- package/dist-server/controllers/types.d.ts +18 -0
- package/dist-server/controllers/types.js +3 -0
- package/dist-server/controllers/types.js.map +1 -0
- package/dist-server/routes.js +2 -2
- package/dist-server/routes.js.map +1 -1
- package/dist-server/service/project/project-mutation.d.ts +2 -1
- package/dist-server/service/project/project-mutation.js +52 -24
- package/dist-server/service/project/project-mutation.js.map +1 -1
- package/dist-server/service/task/task.d.ts +1 -0
- package/dist-server/service/task/task.js +5 -0
- package/dist-server/service/task/task.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/server/controllers/import-task.ts +25 -31
- package/server/controllers/parse-excel.ts +86 -0
- package/server/controllers/types.ts +20 -0
- package/server/routes.ts +1 -1
- package/server/service/project/project-mutation.ts +40 -24
- package/server/service/task/task.ts +4 -0
- package/dist-server/controllers/project-to-excel.js.map +0 -1
- /package/dist-server/controllers/{project-to-excel.d.ts → export-tasks.d.ts} +0 -0
- /package/server/controllers/{project-to-excel.ts → export-tasks.ts} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dssp/project",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.33",
|
|
4
4
|
"main": "dist-server/index.js",
|
|
5
5
|
"browser": "dist-client/index.js",
|
|
6
6
|
"things-factory": true,
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"migration:create": "node ../../node_modules/typeorm/cli.js migration:create -d ./server/migrations"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@dssp/building-complex": "^0.0.
|
|
30
|
+
"@dssp/building-complex": "^0.0.33",
|
|
31
31
|
"@operato/graphql": "^8.0.0-alpha",
|
|
32
32
|
"@operato/shell": "^8.0.0-alpha",
|
|
33
33
|
"@things-factory/auth-base": "^8.0.0-alpha",
|
|
@@ -38,5 +38,5 @@
|
|
|
38
38
|
"@things-factory/shell": "^8.0.0-alpha",
|
|
39
39
|
"exceljs": "^4.4.0"
|
|
40
40
|
},
|
|
41
|
-
"gitHead": "
|
|
41
|
+
"gitHead": "65cbb3d3b4aafcaeb8d7b0da22ae250345ac0f51"
|
|
42
42
|
}
|
|
@@ -4,23 +4,15 @@ import { Project } from '../service/project/project'
|
|
|
4
4
|
import { Task, TaskType } from '../service/task/task'
|
|
5
5
|
import { TaskResource } from '../service/task-resource/task-resource'
|
|
6
6
|
import { Resource } from '../service/resource/resource'
|
|
7
|
+
import { RawTask } from './types'
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
title: string
|
|
16
|
-
type?: TaskType
|
|
17
|
-
duration?: number
|
|
18
|
-
startDate?: string /* YYYY-MM-DD */
|
|
19
|
-
dependsOn?: string
|
|
20
|
-
progress?: number
|
|
21
|
-
tags?: string[]
|
|
22
|
-
resources?: RawResource[]
|
|
23
|
-
children?: RawTask[]
|
|
14
|
+
const jsDate = new Date(excelEpoch.getTime() + days * 86400 * 1000 + milliseconds)
|
|
15
|
+
return jsDate
|
|
24
16
|
}
|
|
25
17
|
|
|
26
18
|
export async function importTasks(project: Project, tasks: RawTask[], context: ResolverContext) {
|
|
@@ -34,7 +26,6 @@ export async function importTasks(project: Project, tasks: RawTask[], context: R
|
|
|
34
26
|
await taskRepository.softDelete({ project: { id: project.id } })
|
|
35
27
|
|
|
36
28
|
// 2. 태스크 임포트
|
|
37
|
-
|
|
38
29
|
const importTaskData = async (rawTask: RawTask, parent?: Task) => {
|
|
39
30
|
if (rawTask.children && rawTask.children.length > 0) {
|
|
40
31
|
rawTask.type = TaskType.GROUP
|
|
@@ -49,27 +40,31 @@ export async function importTasks(project: Project, tasks: RawTask[], context: R
|
|
|
49
40
|
|
|
50
41
|
if (rawTask.type == TaskType.TASK) {
|
|
51
42
|
// 시작일, 종료일 계산
|
|
52
|
-
var startDate: Date
|
|
43
|
+
var startDate: Date = new Date(rawTask.startDate)
|
|
44
|
+
|
|
45
|
+
var endDate
|
|
53
46
|
if (!startDate && rawTask.dependsOn) {
|
|
54
47
|
const dependsOnTask = await taskRepository.findOne({ where: { code: rawTask.dependsOn, project: { id: project.id } } })
|
|
55
|
-
if (
|
|
56
|
-
|
|
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.`)
|
|
57
54
|
}
|
|
58
|
-
startDate = new Date(dependsOnTask.endDate)
|
|
59
|
-
startDate.setDate(startDate.getDate() + 1)
|
|
60
55
|
}
|
|
61
56
|
|
|
62
57
|
if (!startDate) {
|
|
63
58
|
throw new Error(`Task '${rawTask.code}' must have either a start date or a valid dependency.`)
|
|
64
59
|
}
|
|
65
60
|
|
|
66
|
-
|
|
67
|
-
|
|
61
|
+
const duration = rawTask.duration
|
|
62
|
+
endDate = new Date(startDate)
|
|
68
63
|
endDate.setDate(startDate.getDate() + duration - 1)
|
|
69
64
|
}
|
|
70
65
|
|
|
71
66
|
// 태스크 생성 및 저장
|
|
72
|
-
var task = await taskRepository.save({
|
|
67
|
+
var task: Task = await taskRepository.save({
|
|
73
68
|
code: rawTask.code,
|
|
74
69
|
name: rawTask.title,
|
|
75
70
|
type: rawTask.type,
|
|
@@ -77,10 +72,11 @@ export async function importTasks(project: Project, tasks: RawTask[], context: R
|
|
|
77
72
|
endDate,
|
|
78
73
|
project,
|
|
79
74
|
parent,
|
|
80
|
-
duration,
|
|
75
|
+
duration: rawTask.duration,
|
|
81
76
|
dependsOn: rawTask.dependsOn,
|
|
82
77
|
progress: rawTask.progress,
|
|
83
78
|
tags: rawTask.tags,
|
|
79
|
+
style: rawTask.style,
|
|
84
80
|
updater: user,
|
|
85
81
|
creator: user
|
|
86
82
|
})
|
|
@@ -102,9 +98,9 @@ export async function importTasks(project: Project, tasks: RawTask[], context: R
|
|
|
102
98
|
}
|
|
103
99
|
|
|
104
100
|
// 자식 태스크 처리
|
|
105
|
-
if (rawTask.children) {
|
|
106
|
-
|
|
107
|
-
|
|
101
|
+
if (rawTask.children && rawTask.children.length > 0) {
|
|
102
|
+
let lastEndDate = null
|
|
103
|
+
let lastStartDate = null
|
|
108
104
|
for (const childTask of rawTask.children) {
|
|
109
105
|
const subtask = await importTaskData(childTask, task)
|
|
110
106
|
|
|
@@ -118,9 +114,7 @@ export async function importTasks(project: Project, tasks: RawTask[], context: R
|
|
|
118
114
|
? Math.ceil((lastEndDate.getTime() - lastStartDate.getTime()) / (1000 * 60 * 60 * 24)) + 1
|
|
119
115
|
: 0
|
|
120
116
|
|
|
121
|
-
task =
|
|
122
|
-
where: { id: task.id }
|
|
123
|
-
})) as any
|
|
117
|
+
task = await taskRepository.findOne({ where: { id: task.id } })
|
|
124
118
|
|
|
125
119
|
return await taskRepository.save({
|
|
126
120
|
...task,
|
|
@@ -0,0 +1,86 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
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
|
+
}
|
package/server/routes.ts
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import { Resolver, Mutation, Arg, Ctx, Directive } from 'type-graphql'
|
|
2
2
|
import { In } from 'typeorm'
|
|
3
|
-
import {
|
|
3
|
+
import { getRepository } from '@things-factory/shell'
|
|
4
|
+
import { Attachment, createAttachment, deleteAttachmentsByRef, ATTACHMENT_PATH } from '@things-factory/attachment-base'
|
|
4
5
|
import { Project, ProjectState } from './project'
|
|
5
6
|
import { NewProject, ProjectPatch, UploadProjectScheduleTable } from './project-type'
|
|
6
7
|
import { BuildingComplex, Building, BuildingLevel } from '@dssp/building-complex'
|
|
7
8
|
import { pdfToImage } from '@things-factory/board-service/dist-server/controllers/headless-pdf-to-image'
|
|
8
9
|
|
|
10
|
+
import { parseExcelAndImportTasks } from '../../controllers/parse-excel'
|
|
9
11
|
@Resolver(Project)
|
|
10
12
|
export class ProjectMutation {
|
|
11
13
|
@Directive('@transaction')
|
|
12
14
|
@Mutation(returns => Project, { description: '프로젝트 생성' })
|
|
13
15
|
async createProject(@Arg('project') project: NewProject, @Ctx() context: ResolverContext): Promise<Project> {
|
|
14
16
|
const { domain, user, tx } = context.state
|
|
15
|
-
const projectRepo =
|
|
16
|
-
const buildingComplexRepo =
|
|
17
|
+
const projectRepo = getRepository(Project, tx)
|
|
18
|
+
const buildingComplexRepo = getRepository(BuildingComplex, tx)
|
|
17
19
|
|
|
18
20
|
const newBuildingComplex = await buildingComplexRepo.save({
|
|
19
21
|
domain,
|
|
@@ -36,10 +38,10 @@ export class ProjectMutation {
|
|
|
36
38
|
@Mutation(returns => Project, { description: '프로젝트 업데이트' })
|
|
37
39
|
async updateProject(@Arg('project') project: ProjectPatch, @Ctx() context: ResolverContext): Promise<Project> {
|
|
38
40
|
const { user, tx } = context.state
|
|
39
|
-
const projectRepo =
|
|
40
|
-
const buildingComplexRepo =
|
|
41
|
-
const buildingRepo =
|
|
42
|
-
const buildingLevelRepo =
|
|
41
|
+
const projectRepo = getRepository(Project, tx)
|
|
42
|
+
const buildingComplexRepo = getRepository(BuildingComplex, tx)
|
|
43
|
+
const buildingRepo = getRepository(Building, tx)
|
|
44
|
+
const buildingLevelRepo = getRepository(BuildingLevel, tx)
|
|
43
45
|
|
|
44
46
|
const buildingComplex = project.buildingComplex
|
|
45
47
|
const buildings = project.buildingComplex?.buildings || []
|
|
@@ -97,10 +99,10 @@ export class ProjectMutation {
|
|
|
97
99
|
@Mutation(returns => Project, { description: '프로젝트 도면 업데이트' })
|
|
98
100
|
async updateProjectPlan(@Arg('project') project: ProjectPatch, @Ctx() context: ResolverContext): Promise<Project> {
|
|
99
101
|
const { user, tx, domain } = context.state
|
|
100
|
-
const projectRepo =
|
|
101
|
-
const buildingComplexRepo =
|
|
102
|
-
const buildingRepo =
|
|
103
|
-
const buildingLevelRepo =
|
|
102
|
+
const projectRepo = getRepository(Project, tx)
|
|
103
|
+
const buildingComplexRepo = getRepository(BuildingComplex, tx)
|
|
104
|
+
const buildingRepo = getRepository(Building, tx)
|
|
105
|
+
const buildingLevelRepo = getRepository(BuildingLevel, tx)
|
|
104
106
|
const buildingComplex = project.buildingComplex
|
|
105
107
|
const buildings = project.buildingComplex?.buildings || []
|
|
106
108
|
|
|
@@ -126,7 +128,7 @@ export class ProjectMutation {
|
|
|
126
128
|
// 첨부된 PDF가 있으면 PDF 파일대로 썸네일 생성
|
|
127
129
|
if (mainDrawingAttatchment) {
|
|
128
130
|
const mainDrawingUpload = await buildingLevel.mainDrawingUpload
|
|
129
|
-
const pdfPath = `/${ATTACHMENT_PATH}/${mainDrawingAttatchment.path}`
|
|
131
|
+
const pdfPath = `/${ATTACHMENT_PATH}/${mainDrawingAttatchment.path}` // TODO ATTACHMENT_PATH 제거, mainDrawingAttachment.fullpath 로 해도 될 것 같은데...
|
|
130
132
|
const fileName = mainDrawingUpload.filename.replace('.pdf', '')
|
|
131
133
|
const pngFile = await pdfToImage({ pdfPath, fileName })
|
|
132
134
|
await createAttachmentAfterDelete(context, pngFile, buildingLevel.id, BuildingLevel.name + '_mainDrawing_image')
|
|
@@ -200,11 +202,27 @@ export class ProjectMutation {
|
|
|
200
202
|
@Arg('param') param: UploadProjectScheduleTable,
|
|
201
203
|
@Ctx() context: ResolverContext
|
|
202
204
|
): Promise<boolean> {
|
|
203
|
-
const { user, tx } = context.state
|
|
205
|
+
const { domain, user, tx } = context.state
|
|
204
206
|
const { projectId, scheduleTable } = param
|
|
205
207
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
+
const projectRepo = getRepository(Project, tx)
|
|
209
|
+
const project = await projectRepo.findOne({
|
|
210
|
+
where: { domain: { id: domain.id }, id: projectId }
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
const { createReadStream, filename, mimetype } = await scheduleTable
|
|
214
|
+
|
|
215
|
+
const stream = createReadStream()
|
|
216
|
+
|
|
217
|
+
const chunks = []
|
|
218
|
+
for await (const chunk of stream) {
|
|
219
|
+
chunks.push(chunk)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const buffer = Buffer.concat(chunks)
|
|
223
|
+
|
|
224
|
+
await parseExcelAndImportTasks(buffer, project, context)
|
|
225
|
+
// await parseExcelAndImportTasks(attachment.fullpath, project, context)
|
|
208
226
|
|
|
209
227
|
return true
|
|
210
228
|
}
|
|
@@ -214,7 +232,7 @@ export class ProjectMutation {
|
|
|
214
232
|
async deleteProject(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<boolean> {
|
|
215
233
|
const { domain, tx } = context.state
|
|
216
234
|
|
|
217
|
-
await
|
|
235
|
+
await getRepository(Project, tx).delete({ domain: { id: domain.id }, id })
|
|
218
236
|
await deleteAttachmentsByRef(null, { refBys: [id] }, context)
|
|
219
237
|
|
|
220
238
|
return true
|
|
@@ -222,18 +240,16 @@ export class ProjectMutation {
|
|
|
222
240
|
}
|
|
223
241
|
|
|
224
242
|
export async function createAttachmentAfterDelete(context: ResolverContext, file: any, refBy: any, refType: any) {
|
|
225
|
-
|
|
243
|
+
if (file === undefined) {
|
|
244
|
+
return null
|
|
245
|
+
}
|
|
226
246
|
|
|
227
|
-
|
|
228
|
-
if (file === undefined) return result
|
|
247
|
+
const { tx } = context.state
|
|
229
248
|
|
|
230
249
|
// 기존 첨부 파일이 있으면 삭제
|
|
231
250
|
await deleteAttachmentsByRef(null, { refBys: [refBy], refType }, context)
|
|
232
251
|
|
|
233
|
-
|
|
234
|
-
if (file) {
|
|
235
|
-
result = await createAttachment(null, { attachment: { file, refType, refBy } }, context)
|
|
236
|
-
}
|
|
252
|
+
let result = await createAttachment(null, { attachment: { file, refType, refBy } }, context)
|
|
237
253
|
|
|
238
|
-
return result
|
|
254
|
+
return await getRepository(Attachment, tx).findOne({ where: { id: result.id } })
|
|
239
255
|
}
|
|
@@ -90,6 +90,10 @@ export class Task {
|
|
|
90
90
|
@Field({ nullable: true })
|
|
91
91
|
progress?: number
|
|
92
92
|
|
|
93
|
+
@Column({ nullable: true, comment: '스타일' })
|
|
94
|
+
@Field({ nullable: true })
|
|
95
|
+
style?: string
|
|
96
|
+
|
|
93
97
|
// @OneToMany(type => Checklist, checklist => checklist.task, { nullable: true })
|
|
94
98
|
// @Field(type => [Checklist], { nullable: true })
|
|
95
99
|
// checklists?: Checklist[]
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"project-to-excel.js","sourceRoot":"","sources":["../../server/controllers/project-to-excel.ts"],"names":[],"mappings":";;;AAAA,qCAA6C;AAS7C,SAAS,gBAAgB,CAAC,KAAa,EAAE,SAAoB,EAAE,QAAgB,CAAC;IAC9E,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;QACnB,MAAM,GAAG,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAA;QAEvE,GAAG,CAAC,YAAY,GAAG,KAAK,CAAA;QAExB,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE;YAC7C,gBAAgB,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,EAAE,KAAK,GAAG,CAAC,CAAC,CAAA;SACtD;IACH,CAAC,CAAC,CAAA;AACJ,CAAC;AAEM,KAAK,UAAU,aAAa,CAAC,KAAa;IAC/C,MAAM,QAAQ,GAAG,IAAI,kBAAQ,EAAE,CAAA;IAC/B,MAAM,SAAS,GAAG,QAAQ,CAAC,YAAY,CAAC,aAAa,CAAC,CAAA;IAEtD,SAAS,CAAC,UAAU,CAAC,iBAAiB,GAAG;QACvC,YAAY,EAAE,KAAK;QACnB,YAAY,EAAE,KAAK;KACpB,CAAA;IAED,SAAS,CAAC,OAAO,GAAG;QAClB,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE;QAC/C,EAAE,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE,EAAE;QACrD,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE;KAClD,CAAA;IAED,gBAAgB,CAAC,KAAK,EAAE,SAAS,CAAC,CAAA;IAElC,OAAO,MAAM,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,CAAA;AAC1C,CAAC;AAlBD,sCAkBC","sourcesContent":["import { Workbook, Worksheet } from 'exceljs'\n\nexport interface Task {\n name: string\n startDate: Date\n endDate: Date\n subtasks?: Task[]\n}\n\nfunction createGanttChart(tasks: Task[], worksheet: Worksheet, level: number = 0) {\n tasks.forEach(task => {\n const row = worksheet.addRow([task.name, task.startDate, task.endDate])\n\n row.outlineLevel = level\n\n if (task.subtasks && task.subtasks.length > 0) {\n createGanttChart(task.subtasks, worksheet, level + 1)\n }\n })\n}\n\nexport async function generateExcel(tasks: Task[]) {\n const workbook = new Workbook()\n const worksheet = workbook.addWorksheet('Gantt Chart')\n\n worksheet.properties.outlineProperties = {\n summaryBelow: false,\n summaryRight: false\n }\n\n worksheet.columns = [\n { header: 'Task Name', key: 'name', width: 30 },\n { header: 'Start Date', key: 'startDate', width: 20 },\n { header: 'End Date', key: 'endDate', width: 20 }\n ]\n\n createGanttChart(tasks, worksheet)\n\n return await workbook.xlsx.writeBuffer()\n}\n"]}
|
|
File without changes
|
|
File without changes
|