@dssp/project 0.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 (195) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/assets/images/hatiolab-logo.png +0 -0
  3. package/assets/images/o-mark.png +0 -0
  4. package/assets/images/x-mark.png +0 -0
  5. package/client/bootstrap.ts +0 -0
  6. package/client/index.ts +0 -0
  7. package/client/pages/checklist/checklist-importer.ts +96 -0
  8. package/client/pages/checklist/checklist-list-page.ts +355 -0
  9. package/client/pages/lib/waether.ts +159 -0
  10. package/client/pages/project/ox-progress-circle.ts +133 -0
  11. package/client/pages/project/project-create-popup.ts +148 -0
  12. package/client/pages/project/project-detail.ts +618 -0
  13. package/client/pages/project/project-list.ts +354 -0
  14. package/client/pages/project/project-plan-management.ts +437 -0
  15. package/client/pages/project/project-schedule-list.ts +280 -0
  16. package/client/pages/project/project-schedule.ts +248 -0
  17. package/client/pages/project/project-setting-list.ts +298 -0
  18. package/client/pages/project/project-update.ts +797 -0
  19. package/client/pages/resource/resource-importer.ts +96 -0
  20. package/client/pages/resource/resource-list-page.ts +355 -0
  21. package/client/pages/task/task-importer.ts +96 -0
  22. package/client/pages/task/task-list-page.ts +355 -0
  23. package/client/route.ts +39 -0
  24. package/client/tsconfig.json +11 -0
  25. package/dist-client/bootstrap.d.ts +0 -0
  26. package/dist-client/bootstrap.js +2 -0
  27. package/dist-client/bootstrap.js.map +1 -0
  28. package/dist-client/index.d.ts +0 -0
  29. package/dist-client/index.js +2 -0
  30. package/dist-client/index.js.map +1 -0
  31. package/dist-client/pages/checklist/checklist-importer.d.ts +9 -0
  32. package/dist-client/pages/checklist/checklist-importer.js +100 -0
  33. package/dist-client/pages/checklist/checklist-importer.js.map +1 -0
  34. package/dist-client/pages/checklist/checklist-list-page.d.ts +62 -0
  35. package/dist-client/pages/checklist/checklist-list-page.js +331 -0
  36. package/dist-client/pages/checklist/checklist-list-page.js.map +1 -0
  37. package/dist-client/pages/lib/waether.d.ts +6 -0
  38. package/dist-client/pages/lib/waether.js +152 -0
  39. package/dist-client/pages/lib/waether.js.map +1 -0
  40. package/dist-client/pages/project/ox-progress-circle.d.ts +41 -0
  41. package/dist-client/pages/project/ox-progress-circle.js +182 -0
  42. package/dist-client/pages/project/ox-progress-circle.js.map +1 -0
  43. package/dist-client/pages/project/project-create-popup.d.ts +14 -0
  44. package/dist-client/pages/project/project-create-popup.js +153 -0
  45. package/dist-client/pages/project/project-create-popup.js.map +1 -0
  46. package/dist-client/pages/project/project-detail.d.ts +35 -0
  47. package/dist-client/pages/project/project-detail.js +617 -0
  48. package/dist-client/pages/project/project-detail.js.map +1 -0
  49. package/dist-client/pages/project/project-list.d.ts +88 -0
  50. package/dist-client/pages/project/project-list.js +303 -0
  51. package/dist-client/pages/project/project-list.js.map +1 -0
  52. package/dist-client/pages/project/project-plan-management.d.ts +27 -0
  53. package/dist-client/pages/project/project-plan-management.js +438 -0
  54. package/dist-client/pages/project/project-plan-management.js.map +1 -0
  55. package/dist-client/pages/project/project-schedule-list.d.ts +18 -0
  56. package/dist-client/pages/project/project-schedule-list.js +292 -0
  57. package/dist-client/pages/project/project-schedule-list.js.map +1 -0
  58. package/dist-client/pages/project/project-schedule.d.ts +23 -0
  59. package/dist-client/pages/project/project-schedule.js +256 -0
  60. package/dist-client/pages/project/project-schedule.js.map +1 -0
  61. package/dist-client/pages/project/project-setting-list.d.ts +23 -0
  62. package/dist-client/pages/project/project-setting-list.js +307 -0
  63. package/dist-client/pages/project/project-setting-list.js.map +1 -0
  64. package/dist-client/pages/project/project-update.d.ts +24 -0
  65. package/dist-client/pages/project/project-update.js +797 -0
  66. package/dist-client/pages/project/project-update.js.map +1 -0
  67. package/dist-client/pages/resource/resource-importer.d.ts +9 -0
  68. package/dist-client/pages/resource/resource-importer.js +100 -0
  69. package/dist-client/pages/resource/resource-importer.js.map +1 -0
  70. package/dist-client/pages/resource/resource-list-page.d.ts +62 -0
  71. package/dist-client/pages/resource/resource-list-page.js +331 -0
  72. package/dist-client/pages/resource/resource-list-page.js.map +1 -0
  73. package/dist-client/pages/task/task-importer.d.ts +9 -0
  74. package/dist-client/pages/task/task-importer.js +100 -0
  75. package/dist-client/pages/task/task-importer.js.map +1 -0
  76. package/dist-client/pages/task/task-list-page.d.ts +62 -0
  77. package/dist-client/pages/task/task-list-page.js +331 -0
  78. package/dist-client/pages/task/task-list-page.js.map +1 -0
  79. package/dist-client/route.d.ts +1 -0
  80. package/dist-client/route.js +32 -0
  81. package/dist-client/route.js.map +1 -0
  82. package/dist-client/tsconfig.tsbuildinfo +1 -0
  83. package/dist-server/controllers/index.js +1 -0
  84. package/dist-server/controllers/index.js.map +1 -0
  85. package/dist-server/controllers/project-to-excel.js +30 -0
  86. package/dist-server/controllers/project-to-excel.js.map +1 -0
  87. package/dist-server/index.js +6 -0
  88. package/dist-server/index.js.map +1 -0
  89. package/dist-server/middlewares/index.js +8 -0
  90. package/dist-server/middlewares/index.js.map +1 -0
  91. package/dist-server/routes.js +105 -0
  92. package/dist-server/routes.js.map +1 -0
  93. package/dist-server/service/check-item/check-item-mutation.js +129 -0
  94. package/dist-server/service/check-item/check-item-mutation.js.map +1 -0
  95. package/dist-server/service/check-item/check-item-query.js +86 -0
  96. package/dist-server/service/check-item/check-item-query.js.map +1 -0
  97. package/dist-server/service/check-item/check-item-type.js +69 -0
  98. package/dist-server/service/check-item/check-item-type.js.map +1 -0
  99. package/dist-server/service/check-item/check-item.js +84 -0
  100. package/dist-server/service/check-item/check-item.js.map +1 -0
  101. package/dist-server/service/check-item/index.js +9 -0
  102. package/dist-server/service/check-item/index.js.map +1 -0
  103. package/dist-server/service/checklist/checklist-mutation.js +129 -0
  104. package/dist-server/service/checklist/checklist-mutation.js.map +1 -0
  105. package/dist-server/service/checklist/checklist-query.js +86 -0
  106. package/dist-server/service/checklist/checklist-query.js.map +1 -0
  107. package/dist-server/service/checklist/checklist-type.js +69 -0
  108. package/dist-server/service/checklist/checklist-type.js.map +1 -0
  109. package/dist-server/service/checklist/checklist.js +120 -0
  110. package/dist-server/service/checklist/checklist.js.map +1 -0
  111. package/dist-server/service/checklist/index.js +9 -0
  112. package/dist-server/service/checklist/index.js.map +1 -0
  113. package/dist-server/service/index.js +36 -0
  114. package/dist-server/service/index.js.map +1 -0
  115. package/dist-server/service/project/index.js +11 -0
  116. package/dist-server/service/project/index.js.map +1 -0
  117. package/dist-server/service/project/project-mutation.js +190 -0
  118. package/dist-server/service/project/project-mutation.js.map +1 -0
  119. package/dist-server/service/project/project-query.js +143 -0
  120. package/dist-server/service/project/project-query.js.map +1 -0
  121. package/dist-server/service/project/project-type.js +106 -0
  122. package/dist-server/service/project/project-type.js.map +1 -0
  123. package/dist-server/service/project/project.js +146 -0
  124. package/dist-server/service/project/project.js.map +1 -0
  125. package/dist-server/service/resource/index.js +9 -0
  126. package/dist-server/service/resource/index.js.map +1 -0
  127. package/dist-server/service/resource/resource-mutation.js +129 -0
  128. package/dist-server/service/resource/resource-mutation.js.map +1 -0
  129. package/dist-server/service/resource/resource-query.js +86 -0
  130. package/dist-server/service/resource/resource-query.js.map +1 -0
  131. package/dist-server/service/resource/resource-type.js +69 -0
  132. package/dist-server/service/resource/resource-type.js.map +1 -0
  133. package/dist-server/service/resource/resource.js +62 -0
  134. package/dist-server/service/resource/resource.js.map +1 -0
  135. package/dist-server/service/task/index.js +9 -0
  136. package/dist-server/service/task/index.js.map +1 -0
  137. package/dist-server/service/task/task-mutation.js +129 -0
  138. package/dist-server/service/task/task-mutation.js.map +1 -0
  139. package/dist-server/service/task/task-query.js +86 -0
  140. package/dist-server/service/task/task-query.js.map +1 -0
  141. package/dist-server/service/task/task-type.js +69 -0
  142. package/dist-server/service/task/task-type.js.map +1 -0
  143. package/dist-server/service/task/task.js +106 -0
  144. package/dist-server/service/task/task.js.map +1 -0
  145. package/dist-server/tsconfig.tsbuildinfo +1 -0
  146. package/helps/management/alarm.md +160 -0
  147. package/helps/management/alter-video.md +160 -0
  148. package/helps/management/cctv.md +160 -0
  149. package/helps/management/device.md +160 -0
  150. package/helps/management/parking.md +160 -0
  151. package/helps/management/site.md +160 -0
  152. package/helps/management/sun-pnl.md +160 -0
  153. package/helps/project/checklist.md +160 -0
  154. package/helps/project/project.md +160 -0
  155. package/helps/project/resource.md +160 -0
  156. package/helps/project/task.md +160 -0
  157. package/package.json +38 -0
  158. package/server/controllers/index.ts +0 -0
  159. package/server/controllers/project-to-excel.ts +40 -0
  160. package/server/index.ts +3 -0
  161. package/server/middlewares/index.ts +3 -0
  162. package/server/routes.ts +108 -0
  163. package/server/service/check-item/check-item-mutation.ts +142 -0
  164. package/server/service/check-item/check-item-query.ts +56 -0
  165. package/server/service/check-item/check-item-type.ts +49 -0
  166. package/server/service/check-item/check-item.ts +74 -0
  167. package/server/service/check-item/index.ts +6 -0
  168. package/server/service/checklist/checklist-mutation.ts +142 -0
  169. package/server/service/checklist/checklist-query.ts +56 -0
  170. package/server/service/checklist/checklist-type.ts +49 -0
  171. package/server/service/checklist/checklist.ts +105 -0
  172. package/server/service/checklist/index.ts +6 -0
  173. package/server/service/index.ts +35 -0
  174. package/server/service/project/index.ts +8 -0
  175. package/server/service/project/project-mutation.ts +201 -0
  176. package/server/service/project/project-query.ts +103 -0
  177. package/server/service/project/project-type.ts +71 -0
  178. package/server/service/project/project.ts +131 -0
  179. package/server/service/resource/index.ts +6 -0
  180. package/server/service/resource/resource-mutation.ts +139 -0
  181. package/server/service/resource/resource-query.ts +56 -0
  182. package/server/service/resource/resource-type.ts +49 -0
  183. package/server/service/resource/resource.ts +56 -0
  184. package/server/service/task/index.ts +6 -0
  185. package/server/service/task/task-mutation.ts +136 -0
  186. package/server/service/task/task-query.ts +56 -0
  187. package/server/service/task/task-type.ts +49 -0
  188. package/server/service/task/task.ts +97 -0
  189. package/server/tsconfig.json +10 -0
  190. package/things-factory.config.js +20 -0
  191. package/translations/en.json +1 -0
  192. package/translations/ja.json +1 -0
  193. package/translations/ko.json +9 -0
  194. package/translations/ms.json +1 -0
  195. package/translations/zh.json +1 -0
@@ -0,0 +1,35 @@
1
+ /* EXPORT ENTITY TYPES */
2
+ export * from './resource/resource'
3
+ export * from './check-item/check-item'
4
+ export * from './checklist/checklist'
5
+ export * from './task/task'
6
+ export * from './project/project'
7
+
8
+ /* IMPORT ENTITIES AND RESOLVERS */
9
+ import { entities as ResourceEntities, resolvers as ResourceResolvers } from './resource'
10
+ import { entities as CheckItemEntities, resolvers as CheckItemResolvers } from './check-item'
11
+ import { entities as ChecklistEntities, resolvers as ChecklistResolvers } from './checklist'
12
+ import { entities as TaskEntities, resolvers as TaskResolvers } from './task'
13
+ import { entities as ProjectEntities, resolvers as ProjectResolvers, types as ProjectTypes } from './project'
14
+
15
+ export const entities = [
16
+ /* ENTITIES */
17
+ ...ResourceEntities,
18
+ ...CheckItemEntities,
19
+ ...ChecklistEntities,
20
+ ...TaskEntities,
21
+ ...ProjectEntities
22
+ ]
23
+
24
+ export const schema = {
25
+ resolverClasses: [
26
+ /* RESOLVER CLASSES */
27
+ ...ResourceResolvers,
28
+ ...CheckItemResolvers,
29
+ ...ChecklistResolvers,
30
+ ...TaskResolvers,
31
+ ...ProjectResolvers
32
+ ]
33
+ }
34
+
35
+ export const types = [...ProjectTypes]
@@ -0,0 +1,8 @@
1
+ import { Project } from './project'
2
+ import { ProjectQuery } from './project-query'
3
+ import { ProjectMutation } from './project-mutation'
4
+ import { InspectionSummary } from './project-type'
5
+
6
+ export const entities = [Project]
7
+ export const resolvers = [ProjectQuery, ProjectMutation]
8
+ export const types = [InspectionSummary]
@@ -0,0 +1,201 @@
1
+ import { Resolver, Mutation, Arg, Args, Ctx, Directive } from 'type-graphql'
2
+ import { In, Not } from 'typeorm'
3
+
4
+ import { createAttachment, deleteAttachmentsByRef } from '@things-factory/attachment-base'
5
+
6
+ import { Project } from './project'
7
+ import { NewProject, ProjectPatch } from './project-type'
8
+ import { BuildingComplex, Building, BuildingLevel } from '@dssp/building-complex'
9
+
10
+ @Resolver(Project)
11
+ export class ProjectMutation {
12
+ @Directive('@transaction')
13
+ @Mutation(returns => Project, { description: '프로젝트 생성' })
14
+ async createProject(@Arg('project') project: NewProject, @Ctx() context: ResolverContext): Promise<Project> {
15
+ const { domain, user, tx } = context.state
16
+ const projectRepo = tx.getRepository(Project)
17
+ const buildingComplexRepo = tx.getRepository(BuildingComplex)
18
+
19
+ const newBuildingComplex = await buildingComplexRepo.save({
20
+ domain,
21
+ creator: user,
22
+ updater: user
23
+ })
24
+
25
+ const result = await projectRepo.save({
26
+ name: project.name,
27
+ buildingComplex: newBuildingComplex,
28
+ domain,
29
+ creator: user,
30
+ updater: user
31
+ })
32
+
33
+ return result
34
+ }
35
+
36
+ @Directive('@transaction')
37
+ @Mutation(returns => Project, { description: '프로젝트 업데이트' })
38
+ async updateProject(@Arg('project') project: ProjectPatch, @Ctx() context: ResolverContext): Promise<Project> {
39
+ const { user, tx } = context.state
40
+ const projectRepo = tx.getRepository(Project)
41
+ const buildingComplexRepo = tx.getRepository(BuildingComplex)
42
+ const buildingRepo = tx.getRepository(Building)
43
+ const buildingLevelRepo = tx.getRepository(BuildingLevel)
44
+
45
+ const buildingComplex = project.buildingComplex
46
+ const buildings = project.buildingComplex?.buildings || []
47
+
48
+ // 1. 프로젝트 수정
49
+ const projectResult = await projectRepo.save({ ...project, updater: user })
50
+
51
+ // 2. 단지 정보 수정
52
+ await buildingComplexRepo.save({ ...buildingComplex, updater: user })
53
+
54
+ // 2-1. 프로젝트 메인 이미지 첨부파일 나머지 삭제 후 저장 (null로 오면 삭제만)
55
+ if (project.mainPhotoUpload !== undefined) {
56
+ await deleteAttachmentsByRef(null, { refBys: [project.id] }, context)
57
+
58
+ if (project.mainPhotoUpload) {
59
+ await createAttachment(
60
+ null,
61
+ {
62
+ attachment: {
63
+ file: project.mainPhotoUpload,
64
+ refType: Project.name,
65
+ refBy: project.id
66
+ }
67
+ },
68
+ context
69
+ )
70
+ }
71
+ }
72
+
73
+ // 2-2. 단지 BIM 이미지 첨부파일 나머지 삭제 후 저장 (null로 오면 삭제만)
74
+ if (buildingComplex.drawingUpload !== undefined) {
75
+ await deleteAttachmentsByRef(null, { refBys: [buildingComplex.id] }, context)
76
+
77
+ if (buildingComplex.drawingUpload) {
78
+ await createAttachment(
79
+ null,
80
+ {
81
+ attachment: {
82
+ file: buildingComplex.drawingUpload,
83
+ refType: BuildingComplex.name,
84
+ refBy: buildingComplex.id
85
+ }
86
+ },
87
+ context
88
+ )
89
+ }
90
+ }
91
+
92
+ // 3. 동의 층 정보가 바뀌었으면 층 초기화 후 다시 생성
93
+ const originBuilding = await buildingRepo.findBy({ buildingComplex: { id: buildingComplex.id } }) // 이전 동 정보 가져오기
94
+ const afterBuilding = buildings.reduce((acc, building) => ({ ...acc, [building.name]: building.floorCount }), {}) // 비교용으로 수정된 동 정보 데이터 파싱
95
+ const isBuidlingChanged = originBuilding.some(building => afterBuilding[building.name] !== building.floorCount) // 층 개수가 달라진 동이 있는지 확인
96
+
97
+ // 동의 층 개수가 달라지면 모든 층의 데이터 제거 후 생성
98
+ if (isBuidlingChanged || originBuilding.length !== buildings.length) {
99
+ // 3-1. 기존 동/층 첨부파일 및 데이터 제거
100
+ const buildingIds = originBuilding.map((building: Building) => building.id)
101
+ const buildingLevels = await buildingLevelRepo.findBy({ building: { id: In(buildingIds) } })
102
+ const buildingLevelIds = buildingLevels.map((buildingLevel: BuildingLevel) => buildingLevel.id)
103
+
104
+ await buildingLevelRepo.softDelete({ building: { id: In(buildingIds) } })
105
+ await buildingRepo.softDelete({ id: In(buildingIds) })
106
+ await deleteAttachmentsByRef(null, { refBys: [...buildingIds, ...buildingLevelIds] }, context)
107
+
108
+ buildings.forEach(async (building: Building) => {
109
+ // 3-2. 단지 내 동 정보들 생성
110
+ const newBuilding = await buildingRepo.save({
111
+ buildingComplex: buildingComplex,
112
+ name: building.name,
113
+ floorCount: building.floorCount,
114
+ creator: user
115
+ })
116
+
117
+ // 3-3. 동별로 for문 돌면서 층 데이터 개수대로 생성
118
+ for (let i = 1; i <= building.floorCount; i++) {
119
+ await buildingLevelRepo.save({ building: newBuilding, floor: i, creator: user })
120
+ }
121
+ })
122
+ }
123
+
124
+ return projectResult
125
+ }
126
+
127
+ @Directive('@transaction')
128
+ @Mutation(returns => Project, { description: '프로젝트 도면 업데이트' })
129
+ async updateProjectPlan(@Arg('project') project: ProjectPatch, @Ctx() context: ResolverContext): Promise<Project> {
130
+ const { user, tx } = context.state
131
+ const projectRepo = tx.getRepository(Project)
132
+ const buildingComplexRepo = tx.getRepository(BuildingComplex)
133
+ const buildingComplex = project.buildingComplex
134
+ const buildings: Building[] = project.buildingComplex?.buildings || []
135
+
136
+ // 1. 프로젝트 수정 시간 업데이트
137
+ const projectResult = await projectRepo.save({ ...project, updater: user })
138
+
139
+ // 2. 단지 축척 정보 수정
140
+ await buildingComplexRepo.save({ ...buildingComplex, updater: user })
141
+
142
+ for (let buildingKey in buildings) {
143
+ const building = buildings[buildingKey]
144
+
145
+ for (let buildingLevelKey in building.buildingLevels) {
146
+ const buildingLevel = building.buildingLevels[buildingLevelKey]
147
+
148
+ // 3. 층별 도면 이미지 저장 (null로 오면 삭제만)
149
+ if (buildingLevel?.mainDrawingUpload !== undefined) {
150
+ await deleteAttachmentsByRef(null, { refBys: [buildingLevel.id] }, context)
151
+
152
+ if (buildingLevel?.mainDrawingUpload) {
153
+ await createAttachment(
154
+ null,
155
+ {
156
+ attachment: {
157
+ file: buildingLevel.mainDrawingUpload,
158
+ refType: BuildingLevel.name,
159
+ refBy: buildingLevel.id
160
+ }
161
+ },
162
+ context
163
+ )
164
+ }
165
+ }
166
+ }
167
+
168
+ // 4. 동별 도면 이미지 저장 (null로 오면 삭제만)
169
+ if (building?.drawingUpload !== undefined) {
170
+ await deleteAttachmentsByRef(null, { refBys: [building.id] }, context)
171
+
172
+ if (building?.drawingUpload) {
173
+ await createAttachment(
174
+ null,
175
+ {
176
+ attachment: {
177
+ file: building.drawingUpload,
178
+ refType: Building.name,
179
+ refBy: building.id
180
+ }
181
+ },
182
+ context
183
+ )
184
+ }
185
+ }
186
+ }
187
+
188
+ return projectResult
189
+ }
190
+
191
+ @Directive('@transaction')
192
+ @Mutation(returns => Boolean, { description: 'To delete Project' })
193
+ async deleteProject(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<boolean> {
194
+ const { domain, tx } = context.state
195
+
196
+ await tx.getRepository(Project).delete({ domain: { id: domain.id }, id })
197
+ await deleteAttachmentsByRef(null, { refBys: [id] }, context)
198
+
199
+ return true
200
+ }
201
+ }
@@ -0,0 +1,103 @@
1
+ import { Resolver, Query, FieldResolver, Root, Arg, Ctx } from 'type-graphql'
2
+ import { Domain, getRepository } from '@things-factory/shell'
3
+ import { User } from '@things-factory/auth-base'
4
+ import { Project } from './project'
5
+ import { InspectionSummary, ProjectList } from './project-type'
6
+ import { BuildingComplex, InspectionType } from '@dssp/building-complex'
7
+ import { Attachment } from '@things-factory/attachment-base'
8
+
9
+ @Resolver(Project)
10
+ export class ProjectQuery {
11
+ @Query(returns => Project!, { nullable: true, description: 'To fetch a Project' })
12
+ async project(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<Project> {
13
+ const { domain } = context.state
14
+
15
+ return await getRepository(Project).findOne({
16
+ where: { domain: { id: domain.id }, id }
17
+ })
18
+ }
19
+
20
+ @Query(returns => ProjectList, { description: '프로젝트 리스트' })
21
+ async projects(@Arg('projectName') projectName: string, @Ctx() context: ResolverContext): Promise<ProjectList> {
22
+ const { domain } = context.state
23
+ // const { page = 1, limit = 0 } = params.pagination || {}
24
+
25
+ const queryBuilder = await getRepository(Project)
26
+ .createQueryBuilder('p')
27
+ .innerJoinAndSelect('p.buildingComplex', 'bc')
28
+ .where('p.domain = :domain', { domain: domain.id })
29
+ .orderBy('p.created_at', 'DESC')
30
+ // .offset((page - 1) * limit)
31
+ // .limit(limit)
32
+
33
+ if (projectName) {
34
+ projectName = `%${projectName}%`
35
+ queryBuilder.andWhere('p.name LIKE :projectName', { projectName })
36
+ }
37
+
38
+ const [items, total] = await queryBuilder.getManyAndCount()
39
+
40
+ return { items, total }
41
+ }
42
+
43
+ @Query(returns => InspectionSummary, { description: '프로젝트의 검측상태 별 카운트' })
44
+ async inspectionSummary(
45
+ @Arg('projectId') projectId: string,
46
+ @Ctx() context: ResolverContext
47
+ ): Promise<InspectionSummary> {
48
+ const { domain } = context.state
49
+
50
+ const queryBuilder = getRepository(Project)
51
+ .createQueryBuilder('p')
52
+ .select(`COUNT(CASE WHEN bi.type="${InspectionType.REQUEST}" THEN 1 ELSE NULL END) AS request`)
53
+ .addSelect(`COUNT(CASE WHEN bi.type="${InspectionType.PASS}" THEN 1 ELSE NULL END) AS pass`)
54
+ .addSelect(`COUNT(CASE WHEN bi.type="${InspectionType.FAIL}" THEN 1 ELSE NULL END) AS fail`)
55
+ .innerJoin('p.buildingComplex', 'bc')
56
+ .innerJoin('bc.buildings', 'b')
57
+ .innerJoin('b.buildingLevels', 'bl')
58
+ .innerJoin('bl.buildingInspections', 'bi')
59
+ .where('p.domain = :domain', { domain: domain.id })
60
+ .andWhere('p.id = :projectId', { projectId })
61
+ .groupBy('p.id')
62
+
63
+ const result = (await queryBuilder.getRawOne()) || {}
64
+ return {
65
+ request: result.request || 0,
66
+ pass: result.pass || 0,
67
+ fail: result.fail || 0
68
+ }
69
+ }
70
+
71
+ @FieldResolver(type => Attachment)
72
+ async mainPhoto(@Root() project: Project): Promise<string | Attachment> {
73
+ const attachment: Attachment = await getRepository(Attachment).findOne({
74
+ where: {
75
+ domain: { id: project.domainId },
76
+ refBy: project.id
77
+ },
78
+ order: { createdAt: 'ASC' }
79
+ })
80
+
81
+ return attachment
82
+ }
83
+
84
+ @FieldResolver(type => BuildingComplex)
85
+ async buildingComplex(@Root() project: Project): Promise<BuildingComplex> {
86
+ return await getRepository(BuildingComplex).findOneBy({ id: project.buildingComplexId })
87
+ }
88
+
89
+ @FieldResolver(type => Domain)
90
+ async domain(@Root() project: Project): Promise<Domain> {
91
+ return await getRepository(Domain).findOneBy({ id: project.domainId })
92
+ }
93
+
94
+ @FieldResolver(type => User)
95
+ async updater(@Root() project: Project): Promise<User> {
96
+ return await getRepository(User).findOneBy({ id: project.updaterId })
97
+ }
98
+
99
+ @FieldResolver(type => User)
100
+ async creator(@Root() project: Project): Promise<User> {
101
+ return await getRepository(User).findOneBy({ id: project.creatorId })
102
+ }
103
+ }
@@ -0,0 +1,71 @@
1
+ import { ObjectType, Field, InputType, Int, Float } from 'type-graphql'
2
+ import { Project } from './project'
3
+ import { BuildingComplexPatch } from '@dssp/building-complex'
4
+ import type { FileUpload } from 'graphql-upload/GraphQLUpload.js'
5
+ import GraphQLUpload from 'graphql-upload/GraphQLUpload.js'
6
+
7
+ @InputType()
8
+ export class NewProject {
9
+ @Field({ nullable: false })
10
+ name: string
11
+ }
12
+
13
+ @InputType()
14
+ export class ProjectPatch {
15
+ @Field({ nullable: false })
16
+ id: string
17
+
18
+ @Field({ nullable: false })
19
+ name: string
20
+
21
+ @Field({ nullable: true })
22
+ startDate?: string
23
+
24
+ @Field({ nullable: true })
25
+ endDate?: string
26
+
27
+ @Field(type => GraphQLUpload, { nullable: true })
28
+ mainPhotoUpload?: FileUpload
29
+
30
+ @Field(type => Float, { nullable: true })
31
+ totalProgress?: number
32
+
33
+ @Field(type => Float, { nullable: true })
34
+ weeklyProgress?: number
35
+
36
+ @Field(type => Float, { nullable: true })
37
+ kpi?: number
38
+
39
+ @Field(type => Float, { nullable: true })
40
+ inspPassRate?: number
41
+
42
+ @Field(type => Float, { nullable: true })
43
+ robotProgressRate?: number
44
+
45
+ @Field(type => Float, { nullable: true })
46
+ structuralSafetyRate?: number
47
+
48
+ @Field({ nullable: true })
49
+ buildingComplex?: BuildingComplexPatch
50
+ }
51
+
52
+ @ObjectType()
53
+ export class ProjectList {
54
+ @Field(type => [Project])
55
+ items: Project[]
56
+
57
+ @Field(type => Int)
58
+ total: number
59
+ }
60
+
61
+ @ObjectType()
62
+ export class InspectionSummary {
63
+ @Field(type => Int)
64
+ request: number
65
+
66
+ @Field(type => Int)
67
+ pass: number
68
+
69
+ @Field(type => Int)
70
+ fail: number
71
+ }
@@ -0,0 +1,131 @@
1
+ import {
2
+ CreateDateColumn,
3
+ UpdateDateColumn,
4
+ DeleteDateColumn,
5
+ Entity,
6
+ Index,
7
+ Column,
8
+ RelationId,
9
+ ManyToOne,
10
+ OneToOne,
11
+ OneToMany,
12
+ JoinColumn,
13
+ PrimaryGeneratedColumn
14
+ } from 'typeorm'
15
+ import { ObjectType, Field, ID, registerEnumType } from 'type-graphql'
16
+
17
+ import { Domain, roundTransformer } from '@things-factory/shell'
18
+ import { User } from '@things-factory/auth-base'
19
+ import { Task } from '../task/task'
20
+ import { BuildingComplex } from '@dssp/building-complex'
21
+ import { Attachment } from '@things-factory/attachment-base'
22
+
23
+ export enum ProjectStatus {
24
+ 'PROCEEDING' = '10',
25
+ 'COMPLICATED' = '20'
26
+ }
27
+
28
+ registerEnumType(ProjectStatus, {
29
+ name: 'ProjectStatus',
30
+ description: '프로젝트 상태'
31
+ })
32
+
33
+ @Entity()
34
+ @Index('ix_project_0', (project: Project) => [project.buildingComplex], { unique: true, where: '"deleted_at" IS NULL' })
35
+ @ObjectType({ description: '프로젝트' })
36
+ export class Project {
37
+ @PrimaryGeneratedColumn('uuid')
38
+ @Field(type => ID)
39
+ readonly id: string
40
+
41
+ @ManyToOne(type => Domain)
42
+ @Field({ nullable: true })
43
+ domain?: Domain
44
+
45
+ @RelationId((project: Project) => project.domain)
46
+ domainId?: string
47
+
48
+ @Column({ nullable: false, comment: '프로젝트 이름' })
49
+ @Field({ nullable: false })
50
+ name?: string
51
+
52
+ @Column({ nullable: false, default: ProjectStatus.PROCEEDING, comment: '프로젝트 상태 (10: 진행중, 20: 완료)' })
53
+ @Field({ nullable: false })
54
+ state?: ProjectStatus
55
+
56
+ @Column({ type: 'date', nullable: true, comment: '착공일정' })
57
+ @Field({ nullable: true })
58
+ startDate?: string
59
+
60
+ @Column({ type: 'date', nullable: true, comment: '준공일정' })
61
+ @Field({ nullable: true })
62
+ endDate?: string
63
+
64
+ // 대표 사진
65
+ @Field(type => Attachment, { nullable: true })
66
+ mainPhoto?: Attachment
67
+
68
+ @Column({ type: 'float', nullable: true, default: 0, transformer: roundTransformer, comment: '전체 진행현황' })
69
+ @Field({ nullable: true })
70
+ totalProgress?: number
71
+
72
+ @Column({ type: 'float', nullable: true, default: 0, transformer: roundTransformer, comment: '주간 진행현황' })
73
+ @Field({ nullable: true })
74
+ weeklyProgress?: number
75
+
76
+ @Column({ type: 'float', nullable: true, default: 0, transformer: roundTransformer, comment: 'KPI' })
77
+ @Field({ nullable: true })
78
+ kpi?: number
79
+
80
+ @Column({ type: 'float', nullable: true, default: 0, transformer: roundTransformer, comment: '검측/통과 비율' })
81
+ @Field({ nullable: true })
82
+ inspPassRate?: number
83
+
84
+ @Column({ type: 'float', nullable: true, default: 0, transformer: roundTransformer, comment: '로봇 작업 진행율' })
85
+ @Field({ nullable: true })
86
+ robotProgressRate?: number
87
+
88
+ @Column({ type: 'float', nullable: true, default: 0, transformer: roundTransformer, comment: '구조 안전도' })
89
+ @Field({ nullable: true })
90
+ structuralSafetyRate?: number
91
+
92
+ // 단지 정보 (1:1 테이블 참조)
93
+ @OneToOne(type => BuildingComplex)
94
+ @JoinColumn()
95
+ @Field({ nullable: true })
96
+ buildingComplex?: BuildingComplex
97
+
98
+ @RelationId((project: Project) => project.buildingComplex)
99
+ buildingComplexId?: string
100
+
101
+ // 작업 정보 (하위 테이블 참조)
102
+ @Field(() => [Task], { nullable: true })
103
+ @OneToMany(() => Task, task => task.project)
104
+ tasks?: Task[]
105
+
106
+ @CreateDateColumn()
107
+ @Field({ nullable: true })
108
+ createdAt?: Date
109
+
110
+ @UpdateDateColumn()
111
+ @Field({ nullable: true })
112
+ updatedAt?: Date
113
+
114
+ @DeleteDateColumn()
115
+ @Field({ nullable: true })
116
+ deletedAt?: Date
117
+
118
+ @ManyToOne(type => User, { nullable: true })
119
+ @Field(type => User, { nullable: true })
120
+ creator?: User
121
+
122
+ @RelationId((project: Project) => project.creator)
123
+ creatorId?: string
124
+
125
+ @ManyToOne(type => User, { nullable: true })
126
+ @Field(type => User, { nullable: true })
127
+ updater?: User
128
+
129
+ @RelationId((project: Project) => project.updater)
130
+ updaterId?: string
131
+ }
@@ -0,0 +1,6 @@
1
+ import { Resource } from './resource'
2
+ import { ResourceQuery } from './resource-query'
3
+ import { ResourceMutation } from './resource-mutation'
4
+
5
+ export const entities = [Resource]
6
+ export const resolvers = [ResourceQuery, ResourceMutation]
@@ -0,0 +1,139 @@
1
+ import { Resolver, Mutation, Arg, Ctx, Directive } from 'type-graphql'
2
+ import { In } from 'typeorm'
3
+
4
+ import { createAttachment, deleteAttachmentsByRef } from '@things-factory/attachment-base'
5
+
6
+ import { Resource } from './resource'
7
+ import { NewResource, ResourcePatch } from './resource-type'
8
+
9
+ @Resolver(Resource)
10
+ export class ResourceMutation {
11
+ @Directive('@transaction')
12
+ @Mutation(returns => Resource, { description: 'To create new Resource' })
13
+ async createResource(@Arg('resource') resource: NewResource, @Ctx() context: ResolverContext): Promise<Resource> {
14
+ const { domain, user, tx } = context.state
15
+
16
+ const result = await tx.getRepository(Resource).save({
17
+ ...resource,
18
+ domain,
19
+ creator: user,
20
+ updater: user
21
+ })
22
+
23
+ return result
24
+ }
25
+
26
+ @Directive('@transaction')
27
+ @Mutation(returns => Resource, { description: 'To modify Resource information' })
28
+ async updateResource(
29
+ @Arg('id') id: string,
30
+ @Arg('patch') patch: ResourcePatch,
31
+ @Ctx() context: ResolverContext
32
+ ): Promise<Resource> {
33
+ const { domain, user, tx } = context.state
34
+
35
+ const repository = tx.getRepository(Resource)
36
+ const resource = await repository.findOne({
37
+ where: { id }
38
+ })
39
+
40
+ const result = await repository.save({
41
+ ...resource,
42
+ ...patch,
43
+ updater: user
44
+ })
45
+
46
+ return result
47
+ }
48
+
49
+ @Directive('@transaction')
50
+ @Mutation(returns => [Resource], { description: "To modify multiple Resources' information" })
51
+ async updateMultipleResource(
52
+ @Arg('patches', type => [ResourcePatch]) patches: ResourcePatch[],
53
+ @Ctx() context: ResolverContext
54
+ ): Promise<Resource[]> {
55
+ const { domain, user, tx } = context.state
56
+
57
+ let results = []
58
+ const _createRecords = patches.filter((patch: any) => patch.cuFlag.toUpperCase() === '+')
59
+ const _updateRecords = patches.filter((patch: any) => patch.cuFlag.toUpperCase() === 'M')
60
+ const resourceRepo = tx.getRepository(Resource)
61
+
62
+ if (_createRecords.length > 0) {
63
+ for (let i = 0; i < _createRecords.length; i++) {
64
+ const newRecord = _createRecords[i]
65
+
66
+ const result = await resourceRepo.save({
67
+ ...newRecord,
68
+ domain,
69
+ creator: user,
70
+ updater: user
71
+ })
72
+
73
+ results.push({ ...result, cuFlag: '+' })
74
+ }
75
+ }
76
+
77
+ if (_updateRecords.length > 0) {
78
+ for (let i = 0; i < _updateRecords.length; i++) {
79
+ const updateRecord = _updateRecords[i]
80
+ const resource = await resourceRepo.findOneBy({ id: updateRecord.id })
81
+
82
+ const result = await resourceRepo.save({
83
+ ...resource,
84
+ ...updateRecord,
85
+ updater: user
86
+ })
87
+
88
+ results.push({ ...result, cuFlag: 'M' })
89
+ }
90
+ }
91
+
92
+ return results
93
+ }
94
+
95
+ @Directive('@transaction')
96
+ @Mutation(returns => Boolean, { description: 'To delete Resource' })
97
+ async deleteResource(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<boolean> {
98
+ const { domain, tx } = context.state
99
+
100
+ await tx.getRepository(Resource).delete({ id })
101
+ await deleteAttachmentsByRef(null, { refBys: [id] }, context)
102
+
103
+ return true
104
+ }
105
+
106
+ @Directive('@transaction')
107
+ @Mutation(returns => Boolean, { description: 'To delete multiple Resources' })
108
+ async deleteResources(
109
+ @Arg('ids', type => [String]) ids: string[],
110
+ @Ctx() context: ResolverContext
111
+ ): Promise<boolean> {
112
+ const { domain, tx } = context.state
113
+
114
+ await tx.getRepository(Resource).delete({
115
+ id: In(ids)
116
+ })
117
+
118
+ await deleteAttachmentsByRef(null, { refBys: ids }, context)
119
+
120
+ return true
121
+ }
122
+
123
+ @Directive('@transaction')
124
+ @Mutation(returns => Boolean, { description: 'To import multiple Resources' })
125
+ async importResources(
126
+ @Arg('resources', type => [ResourcePatch]) resources: ResourcePatch[],
127
+ @Ctx() context: ResolverContext
128
+ ): Promise<boolean> {
129
+ const { domain, tx } = context.state
130
+
131
+ await Promise.all(
132
+ resources.map(async (resource: ResourcePatch) => {
133
+ const createdResource: Resource = await tx.getRepository(Resource).save({ domain, ...resource })
134
+ })
135
+ )
136
+
137
+ return true
138
+ }
139
+ }