@dssp/supervision 1.0.0-alpha.4 → 1.0.0-alpha.7

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 (91) hide show
  1. package/dist-client/pages/building-inspection/building-inspection-list.js +2 -1
  2. package/dist-client/pages/building-inspection/building-inspection-list.js.map +1 -1
  3. package/dist-client/pages/building-inspection/component/inspection-event-provider.js +3 -3
  4. package/dist-client/pages/building-inspection/component/inspection-event-provider.js.map +1 -1
  5. package/dist-client/pages/building-inspection/inspection-create-popup.js +5 -4
  6. package/dist-client/pages/building-inspection/inspection-create-popup.js.map +1 -1
  7. package/dist-client/pages/building-inspection-grid/building-inspection-grid-detail.d.ts +46 -0
  8. package/dist-client/pages/building-inspection-grid/building-inspection-grid-detail.js +930 -0
  9. package/dist-client/pages/building-inspection-grid/building-inspection-grid-detail.js.map +1 -0
  10. package/dist-client/pages/building-inspection-grid/component/grid-inspection-create-popup.d.ts +2 -0
  11. package/dist-client/pages/building-inspection-grid/component/grid-inspection-create-popup.js +734 -0
  12. package/dist-client/pages/building-inspection-grid/component/grid-inspection-create-popup.js.map +1 -0
  13. package/dist-client/pages/building-inspection-grid/component/notice-comment-popup.d.ts +1 -0
  14. package/dist-client/pages/building-inspection-grid/component/notice-comment-popup.js +304 -0
  15. package/dist-client/pages/building-inspection-grid/component/notice-comment-popup.js.map +1 -0
  16. package/dist-client/pages/building-inspection-grid/component/plan-preview-popup.d.ts +15 -0
  17. package/dist-client/pages/building-inspection-grid/component/plan-preview-popup.js +430 -0
  18. package/dist-client/pages/building-inspection-grid/component/plan-preview-popup.js.map +1 -0
  19. package/dist-client/pages/checklist/checklist-view.d.ts +2 -0
  20. package/dist-client/pages/checklist/checklist-view.js +286 -185
  21. package/dist-client/pages/checklist/checklist-view.js.map +1 -1
  22. package/dist-client/pages/checklist-template/checklist-template-item.js +8 -3
  23. package/dist-client/pages/checklist-template/checklist-template-item.js.map +1 -1
  24. package/dist-client/pages/checklist-template/checklist-template-list.js +1 -1
  25. package/dist-client/pages/checklist-template/checklist-template-list.js.map +1 -1
  26. package/dist-client/route.d.ts +1 -1
  27. package/dist-client/route.js +3 -0
  28. package/dist-client/route.js.map +1 -1
  29. package/dist-client/stt/speech-to-text.d.ts +36 -0
  30. package/dist-client/stt/speech-to-text.js +89 -0
  31. package/dist-client/stt/speech-to-text.js.map +1 -0
  32. package/dist-client/stt/stt-utils.d.ts +28 -0
  33. package/dist-client/stt/stt-utils.js +146 -0
  34. package/dist-client/stt/stt-utils.js.map +1 -0
  35. package/dist-client/tsconfig.tsbuildinfo +1 -1
  36. package/dist-server/service/action-plan/index.d.ts +0 -1
  37. package/dist-server/service/action-plan/index.js +1 -2
  38. package/dist-server/service/action-plan/index.js.map +1 -1
  39. package/dist-server/service/building-inspection/building-inspection-mutation.d.ts +1 -0
  40. package/dist-server/service/building-inspection/building-inspection-mutation.js +83 -10
  41. package/dist-server/service/building-inspection/building-inspection-mutation.js.map +1 -1
  42. package/dist-server/service/building-inspection/building-inspection-query.d.ts +2 -1
  43. package/dist-server/service/building-inspection/building-inspection-query.js +31 -2
  44. package/dist-server/service/building-inspection/building-inspection-query.js.map +1 -1
  45. package/dist-server/service/building-inspection/building-inspection-type.d.ts +5 -2
  46. package/dist-server/service/building-inspection/building-inspection-type.js +16 -4
  47. package/dist-server/service/building-inspection/building-inspection-type.js.map +1 -1
  48. package/dist-server/service/building-inspection/building-inspection.d.ts +9 -0
  49. package/dist-server/service/building-inspection/building-inspection.js +40 -2
  50. package/dist-server/service/building-inspection/building-inspection.js.map +1 -1
  51. package/dist-server/service/building-inspection/index.d.ts +2 -4
  52. package/dist-server/service/building-inspection/index.js +2 -4
  53. package/dist-server/service/building-inspection/index.js.map +1 -1
  54. package/dist-server/service/checklist/checklist-history.d.ts +8 -0
  55. package/dist-server/service/checklist/checklist-history.js +36 -0
  56. package/dist-server/service/checklist/checklist-history.js.map +1 -1
  57. package/dist-server/service/checklist/checklist.d.ts +10 -0
  58. package/dist-server/service/checklist/checklist.js +45 -3
  59. package/dist-server/service/checklist/checklist.js.map +1 -1
  60. package/dist-server/service/checklist/index.d.ts +0 -2
  61. package/dist-server/service/checklist/index.js +1 -3
  62. package/dist-server/service/checklist/index.js.map +1 -1
  63. package/dist-server/service/checklist-item-comment/index.d.ts +0 -2
  64. package/dist-server/service/checklist-item-comment/index.js +1 -3
  65. package/dist-server/service/checklist-item-comment/index.js.map +1 -1
  66. package/dist-server/service/checklist-template-item/checklist-template-item.js +2 -4
  67. package/dist-server/service/checklist-template-item/checklist-template-item.js.map +1 -1
  68. package/dist-server/service/index.d.ts +0 -1
  69. package/dist-server/service/index.js +1 -11
  70. package/dist-server/service/index.js.map +1 -1
  71. package/dist-server/service/issue/index.d.ts +0 -1
  72. package/dist-server/service/issue/index.js +1 -2
  73. package/dist-server/service/issue/index.js.map +1 -1
  74. package/dist-server/service/project-report/index.d.ts +0 -1
  75. package/dist-server/service/project-report/index.js +1 -2
  76. package/dist-server/service/project-report/index.js.map +1 -1
  77. package/dist-server/service/supervisor/index.d.ts +0 -1
  78. package/dist-server/service/supervisor/index.js +1 -2
  79. package/dist-server/service/supervisor/index.js.map +1 -1
  80. package/dist-server/tsconfig.tsbuildinfo +1 -1
  81. package/package.json +13 -13
  82. package/things-factory.config.js +3 -1
  83. package/dist-server/service/building-inspection/event-subscriber.d.ts +0 -7
  84. package/dist-server/service/building-inspection/event-subscriber.js +0 -21
  85. package/dist-server/service/building-inspection/event-subscriber.js.map +0 -1
  86. package/dist-server/service/checklist/event-subscriber.d.ts +0 -7
  87. package/dist-server/service/checklist/event-subscriber.js +0 -21
  88. package/dist-server/service/checklist/event-subscriber.js.map +0 -1
  89. package/dist-server/service/checklist-item-comment/event-subscriber.d.ts +0 -7
  90. package/dist-server/service/checklist-item-comment/event-subscriber.js +0 -21
  91. package/dist-server/service/checklist-item-comment/event-subscriber.js.map +0 -1
@@ -0,0 +1,430 @@
1
+ import { __decorate, __metadata } from "tslib";
2
+ import { css, html, LitElement } from 'lit';
3
+ import { customElement, property } from 'lit/decorators.js';
4
+ import { ifDefined } from 'lit/directives/if-defined.js';
5
+ import { openPopup } from '@operato/layout';
6
+ import { client } from '@operato/graphql';
7
+ import gql from 'graphql-tag';
8
+ let PlanPreviewPopup = class PlanPreviewPopup extends LitElement {
9
+ constructor() {
10
+ super(...arguments);
11
+ this.buildingLevelId = '';
12
+ this.buildingLevel = {};
13
+ }
14
+ async connectedCallback() {
15
+ super.connectedCallback();
16
+ await this._loadBuildingLevel();
17
+ }
18
+ render() {
19
+ const { mainDrawing, elevationDrawing, rebarDistributionDrawing, etcDrawings } = this.buildingLevel;
20
+ // 메인 도면 파일들
21
+ const mainFiles = [];
22
+ if (mainDrawing === null || mainDrawing === void 0 ? void 0 : mainDrawing.id) {
23
+ mainFiles.push(Object.assign(Object.assign({}, mainDrawing), { type: '평면도' }));
24
+ }
25
+ if (elevationDrawing === null || elevationDrawing === void 0 ? void 0 : elevationDrawing.id) {
26
+ mainFiles.push(Object.assign(Object.assign({}, elevationDrawing), { type: '입면도' }));
27
+ }
28
+ if (rebarDistributionDrawing === null || rebarDistributionDrawing === void 0 ? void 0 : rebarDistributionDrawing.id) {
29
+ mainFiles.push(Object.assign(Object.assign({}, rebarDistributionDrawing), { type: '철근배근도' }));
30
+ }
31
+ // 기타 도면 파일들
32
+ const etcFiles = (etcDrawings === null || etcDrawings === void 0 ? void 0 : etcDrawings.map(drawing => (Object.assign(Object.assign({}, drawing), { type: '기타 도면' })))) || [];
33
+ return html `
34
+ <div body>
35
+ <div file-list-container>
36
+ ${mainFiles.length > 0
37
+ ? html `
38
+ <div section-title>주요 도면</div>
39
+ ${mainFiles.map(file => html `
40
+ <div file-item @click=${() => this._openPreview(file)}>
41
+ <div file-info>
42
+ <md-icon>description</md-icon>
43
+ <span file-name>${file.name || ''}</span>
44
+ <span file-type>${file.type}</span>
45
+ </div>
46
+ <md-icon slot="action">visibility</md-icon>
47
+ </div>
48
+ `)}
49
+ `
50
+ : ''}
51
+ ${etcFiles.length > 0
52
+ ? html `
53
+ <div section-title>기타 도면</div>
54
+ ${etcFiles.map(file => html `
55
+ <div file-item @click=${() => this._openPreview(file)}>
56
+ <div file-info>
57
+ <md-icon>attachment</md-icon>
58
+ <span file-name>${file.name || ''}</span>
59
+ <span file-type>${file.type}</span>
60
+ </div>
61
+ <md-icon slot="action">visibility</md-icon>
62
+ </div>
63
+ `)}
64
+ `
65
+ : ''}
66
+ ${mainFiles.length === 0 && etcFiles.length === 0
67
+ ? html `
68
+ <div empty-state>
69
+ <md-icon>insert_drive_file</md-icon>
70
+ <div>미리보기할 도면 파일이 없습니다.</div>
71
+ </div>
72
+ `
73
+ : ''}
74
+ </div>
75
+
76
+ <div button-container>
77
+ <md-outlined-button @click=${this._close}><md-icon slot="icon">cancel</md-icon>닫기</md-outlined-button>
78
+ </div>
79
+ </div>
80
+ `;
81
+ }
82
+ async _loadBuildingLevel() {
83
+ var _a;
84
+ try {
85
+ const response = await client.query({
86
+ query: gql `
87
+ query BuildingLevel($buildingLevelId: String!) {
88
+ buildingLevel(id: $buildingLevelId) {
89
+ id
90
+ floor
91
+ building {
92
+ id
93
+ name
94
+ }
95
+ mainDrawing {
96
+ id
97
+ name
98
+ fullpath
99
+ }
100
+ elevationDrawing {
101
+ id
102
+ name
103
+ fullpath
104
+ }
105
+ rebarDistributionDrawing {
106
+ id
107
+ name
108
+ fullpath
109
+ }
110
+ etcDrawings {
111
+ id
112
+ name
113
+ fullpath
114
+ }
115
+ }
116
+ }
117
+ `,
118
+ variables: {
119
+ buildingLevelId: this.buildingLevelId
120
+ }
121
+ });
122
+ this.buildingLevel = ((_a = response.data) === null || _a === void 0 ? void 0 : _a.buildingLevel) || {};
123
+ }
124
+ catch (error) {
125
+ console.error('Building level 정보를 불러오는 중 오류 발생:', error);
126
+ }
127
+ }
128
+ _openPreview(file) {
129
+ if (!file.fullpath) {
130
+ return;
131
+ }
132
+ // 파일 확장자로 타입 판단
133
+ const fileExtension = file.fullpath.toLowerCase().split('.').pop();
134
+ const isImage = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg'].includes(fileExtension || '');
135
+ const isPdf = fileExtension === 'pdf';
136
+ if (isImage) {
137
+ this._openImagePreview(file);
138
+ }
139
+ else if (isPdf) {
140
+ this._openPdfPreview(file);
141
+ }
142
+ else {
143
+ // 기타 파일은 다운로드 또는 기본 뷰어
144
+ this._openGenericPreview(file);
145
+ }
146
+ }
147
+ _openImagePreview(file) {
148
+ openPopup(html `
149
+ <div style="display: flex; flex-direction: column;">
150
+ <div style="flex: 1; overflow: hidden; position: relative; touch-action: none;">
151
+ <img
152
+ id="zoomable-image"
153
+ src="${ifDefined(file.fullpath)}"
154
+ alt="${ifDefined(file.name)}"
155
+ style="
156
+ width: 100%;
157
+ height: 100%;
158
+ object-fit: contain;
159
+ transform-origin: center center;
160
+ transition: transform 0.1s ease-out;
161
+ user-select: none;
162
+ -webkit-user-select: none;
163
+ "
164
+ @load=${this._initImageZoom}
165
+ @error=${(e) => {
166
+ const target = e.target;
167
+ target.style.display = 'none';
168
+ target.parentElement.innerHTML +=
169
+ '<div style="padding: 40px; text-align: center; color: #666;">이미지를 불러올 수 없습니다.</div>';
170
+ }}
171
+ />
172
+ </div>
173
+ </div>
174
+ `, { backdrop: true, size: 'large', title: `[이미지 미리보기] ${file.name} (${file.type})` });
175
+ }
176
+ _openPdfPreview(file) {
177
+ openPopup(html `
178
+ <div style="display: flex; flex-direction: column;">
179
+ <div style="flex: 1; overflow: hidden; touch-action: pan-x pan-y;">
180
+ <iframe
181
+ src="${file.fullpath}#toolbar=1&navpanes=0&scrollbar=1&zoom=page-fit"
182
+ style="
183
+ width: 100%;
184
+ height: 100%;
185
+ border: none;
186
+ touch-action: pan-x pan-y;
187
+ "
188
+ @error=${() => {
189
+ // PDF 로드 실패시 새 탭에서 열기
190
+ window.open(file.fullpath, '_blank');
191
+ }}
192
+ ></iframe>
193
+ </div>
194
+ </div>
195
+ `, { backdrop: true, size: 'large', title: `[PDF 미리보기] ${file.name} (${file.type})` });
196
+ }
197
+ // 기타 파일 타입은 새 탭에서 열기
198
+ _openGenericPreview(file) {
199
+ window.open(file.fullpath, '_blank');
200
+ }
201
+ _close() {
202
+ history.back();
203
+ }
204
+ _initImageZoom(e) {
205
+ const img = e.target;
206
+ let scale = 1;
207
+ let translateX = 0;
208
+ let translateY = 0;
209
+ let isDragging = false;
210
+ let lastTouchDistance = 0;
211
+ let lastTouchX = 0;
212
+ let lastTouchY = 0;
213
+ const updateTransform = () => {
214
+ img.style.transform = `scale(${scale}) translate(${translateX}px, ${translateY}px)`;
215
+ };
216
+ const resetTransform = () => {
217
+ scale = 1;
218
+ translateX = 0;
219
+ translateY = 0;
220
+ updateTransform();
221
+ };
222
+ // 터치 시작
223
+ img.addEventListener('touchstart', event => {
224
+ event.preventDefault();
225
+ if (event.touches.length === 1) {
226
+ // 단일 터치 - 드래그 시작
227
+ isDragging = true;
228
+ lastTouchX = event.touches[0].clientX;
229
+ lastTouchY = event.touches[0].clientY;
230
+ }
231
+ else if (event.touches.length === 2) {
232
+ // 멀티 터치 - 핀치 줌 시작
233
+ isDragging = false;
234
+ const touch1 = event.touches[0];
235
+ const touch2 = event.touches[1];
236
+ lastTouchDistance = Math.sqrt(Math.pow(touch2.clientX - touch1.clientX, 2) + Math.pow(touch2.clientY - touch1.clientY, 2));
237
+ }
238
+ });
239
+ // 터치 이동
240
+ img.addEventListener('touchmove', event => {
241
+ event.preventDefault();
242
+ if (event.touches.length === 1 && isDragging && scale > 1) {
243
+ // 드래그
244
+ const currentX = event.touches[0].clientX;
245
+ const currentY = event.touches[0].clientY;
246
+ const deltaX = currentX - lastTouchX;
247
+ const deltaY = currentY - lastTouchY;
248
+ translateX += deltaX / scale;
249
+ translateY += deltaY / scale;
250
+ lastTouchX = currentX;
251
+ lastTouchY = currentY;
252
+ updateTransform();
253
+ }
254
+ else if (event.touches.length === 2) {
255
+ // 핀치 줌
256
+ const touch1 = event.touches[0];
257
+ const touch2 = event.touches[1];
258
+ const currentDistance = Math.sqrt(Math.pow(touch2.clientX - touch1.clientX, 2) + Math.pow(touch2.clientY - touch1.clientY, 2));
259
+ if (lastTouchDistance > 0) {
260
+ const scaleChange = currentDistance / lastTouchDistance;
261
+ const newScale = scale * scaleChange;
262
+ // 최소/최대 배율 제한
263
+ if (newScale >= 0.5 && newScale <= 5) {
264
+ scale = newScale;
265
+ updateTransform();
266
+ }
267
+ }
268
+ lastTouchDistance = currentDistance;
269
+ }
270
+ });
271
+ // 터치 끝
272
+ img.addEventListener('touchend', event => {
273
+ event.preventDefault();
274
+ isDragging = false;
275
+ if (event.touches.length === 0) {
276
+ // 모든 터치가 끝났을 때 - 경계 체크 및 보정
277
+ if (scale < 1) {
278
+ resetTransform();
279
+ }
280
+ }
281
+ });
282
+ // 더블 탭으로 줌 토글
283
+ let lastTapTime = 0;
284
+ img.addEventListener('touchend', event => {
285
+ const currentTime = new Date().getTime();
286
+ const tapLength = currentTime - lastTapTime;
287
+ if (tapLength < 500 && tapLength > 0 && event.touches.length === 0) {
288
+ if (scale === 1) {
289
+ scale = 2;
290
+ updateTransform();
291
+ }
292
+ else {
293
+ resetTransform();
294
+ }
295
+ }
296
+ lastTapTime = currentTime;
297
+ });
298
+ // 마우스 휠 지원 (데스크톱)
299
+ img.addEventListener('wheel', event => {
300
+ event.preventDefault();
301
+ const delta = event.deltaY > 0 ? 0.9 : 1.1;
302
+ const newScale = scale * delta;
303
+ if (newScale >= 0.5 && newScale <= 5) {
304
+ scale = newScale;
305
+ updateTransform();
306
+ }
307
+ });
308
+ }
309
+ };
310
+ PlanPreviewPopup.styles = [
311
+ css `
312
+ :host {
313
+ display: flex;
314
+ flex-direction: column;
315
+ background-color: #fff;
316
+ width: 100%;
317
+ max-height: 80vh;
318
+ }
319
+
320
+ div[body] {
321
+ flex: 1;
322
+ overflow-y: auto;
323
+ padding: 20px;
324
+
325
+ div[file-list-container] {
326
+ display: flex;
327
+ flex-direction: column;
328
+ gap: 15px;
329
+ background-color: #f7f7f7;
330
+ padding: 20px;
331
+ border-radius: 8px;
332
+ margin-bottom: 20px;
333
+
334
+ div[file-item] {
335
+ display: flex;
336
+ align-items: center;
337
+ gap: 12px;
338
+ padding: 12px;
339
+ background-color: #fff;
340
+ border-radius: 6px;
341
+ border: 1px solid #ddd;
342
+ cursor: pointer;
343
+ transition: all 0.2s ease;
344
+
345
+ &:hover {
346
+ background-color: #e3f2fd;
347
+ border-color: #2196f3;
348
+ transform: translateY(-1px);
349
+ box-shadow: 0 2px 8px rgba(33, 150, 243, 0.15);
350
+ }
351
+
352
+ div[file-info] {
353
+ flex: 1;
354
+ display: flex;
355
+ align-items: center;
356
+ gap: 10px;
357
+
358
+ md-icon {
359
+ --md-icon-size: 24px;
360
+ color: #666;
361
+ }
362
+
363
+ span[file-name] {
364
+ font-size: 14px;
365
+ font-weight: 500;
366
+ color: #333;
367
+ }
368
+
369
+ span[file-type] {
370
+ font-size: 12px;
371
+ color: #666;
372
+ background-color: #f0f0f0;
373
+ padding: 2px 6px;
374
+ border-radius: 4px;
375
+ }
376
+ }
377
+
378
+ md-icon[slot='action'] {
379
+ --md-icon-size: 20px;
380
+ color: #2196f3;
381
+ opacity: 0.7;
382
+ }
383
+ }
384
+
385
+ div[section-title] {
386
+ font-size: 16px;
387
+ font-weight: 600;
388
+ color: #333;
389
+ margin-top: 10px;
390
+ margin-bottom: 5px;
391
+ border-bottom: 2px solid #ddd;
392
+ padding-bottom: 5px;
393
+ }
394
+ }
395
+
396
+ div[empty-state] {
397
+ text-align: center;
398
+ color: #666;
399
+ padding: 40px;
400
+
401
+ md-icon {
402
+ --md-icon-size: 48px;
403
+ color: #ccc;
404
+ margin-bottom: 16px;
405
+ }
406
+ }
407
+
408
+ div[button-container] {
409
+ display: flex;
410
+ align-items: center;
411
+ justify-content: center;
412
+ gap: 10px;
413
+ margin-top: 20px;
414
+ }
415
+ }
416
+ `
417
+ ];
418
+ __decorate([
419
+ property({ type: String }),
420
+ __metadata("design:type", String)
421
+ ], PlanPreviewPopup.prototype, "buildingLevelId", void 0);
422
+ __decorate([
423
+ property({ type: Object }),
424
+ __metadata("design:type", Object)
425
+ ], PlanPreviewPopup.prototype, "buildingLevel", void 0);
426
+ PlanPreviewPopup = __decorate([
427
+ customElement('plan-preview-popup')
428
+ ], PlanPreviewPopup);
429
+ export { PlanPreviewPopup };
430
+ //# sourceMappingURL=plan-preview-popup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan-preview-popup.js","sourceRoot":"","sources":["../../../../client/pages/building-inspection-grid/component/plan-preview-popup.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, LitElement } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport { ifDefined } from 'lit/directives/if-defined.js'\nimport { openPopup } from '@operato/layout'\nimport { client } from '@operato/graphql'\nimport gql from 'graphql-tag'\n\ninterface FileItem {\n id?: string\n name?: string\n type: string\n fullpath?: string\n}\n\ninterface BuildingLevel {\n id?: string\n floor?: string\n building?: {\n id?: string\n name?: string\n }\n mainDrawing?: {\n id?: string\n name?: string\n fullpath?: string\n }\n elevationDrawing?: {\n id?: string\n name?: string\n fullpath?: string\n }\n rebarDistributionDrawing?: {\n id?: string\n name?: string\n fullpath?: string\n }\n etcDrawings?: Array<{\n id?: string\n name?: string\n fullpath?: string\n }>\n}\n\n@customElement('plan-preview-popup')\nexport class PlanPreviewPopup extends LitElement {\n static styles = [\n css`\n :host {\n display: flex;\n flex-direction: column;\n background-color: #fff;\n width: 100%;\n max-height: 80vh;\n }\n\n div[body] {\n flex: 1;\n overflow-y: auto;\n padding: 20px;\n\n div[file-list-container] {\n display: flex;\n flex-direction: column;\n gap: 15px;\n background-color: #f7f7f7;\n padding: 20px;\n border-radius: 8px;\n margin-bottom: 20px;\n\n div[file-item] {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px;\n background-color: #fff;\n border-radius: 6px;\n border: 1px solid #ddd;\n cursor: pointer;\n transition: all 0.2s ease;\n\n &:hover {\n background-color: #e3f2fd;\n border-color: #2196f3;\n transform: translateY(-1px);\n box-shadow: 0 2px 8px rgba(33, 150, 243, 0.15);\n }\n\n div[file-info] {\n flex: 1;\n display: flex;\n align-items: center;\n gap: 10px;\n\n md-icon {\n --md-icon-size: 24px;\n color: #666;\n }\n\n span[file-name] {\n font-size: 14px;\n font-weight: 500;\n color: #333;\n }\n\n span[file-type] {\n font-size: 12px;\n color: #666;\n background-color: #f0f0f0;\n padding: 2px 6px;\n border-radius: 4px;\n }\n }\n\n md-icon[slot='action'] {\n --md-icon-size: 20px;\n color: #2196f3;\n opacity: 0.7;\n }\n }\n\n div[section-title] {\n font-size: 16px;\n font-weight: 600;\n color: #333;\n margin-top: 10px;\n margin-bottom: 5px;\n border-bottom: 2px solid #ddd;\n padding-bottom: 5px;\n }\n }\n\n div[empty-state] {\n text-align: center;\n color: #666;\n padding: 40px;\n\n md-icon {\n --md-icon-size: 48px;\n color: #ccc;\n margin-bottom: 16px;\n }\n }\n\n div[button-container] {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 10px;\n margin-top: 20px;\n }\n }\n `\n ]\n\n @property({ type: String }) buildingLevelId: string = ''\n @property({ type: Object }) private buildingLevel: BuildingLevel = {}\n\n async connectedCallback() {\n super.connectedCallback()\n await this._loadBuildingLevel()\n }\n\n render() {\n const { mainDrawing, elevationDrawing, rebarDistributionDrawing, etcDrawings } = this.buildingLevel\n\n // 메인 도면 파일들\n const mainFiles: FileItem[] = []\n if (mainDrawing?.id) {\n mainFiles.push({ ...mainDrawing, type: '평면도' })\n }\n if (elevationDrawing?.id) {\n mainFiles.push({ ...elevationDrawing, type: '입면도' })\n }\n if (rebarDistributionDrawing?.id) {\n mainFiles.push({ ...rebarDistributionDrawing, type: '철근배근도' })\n }\n\n // 기타 도면 파일들\n const etcFiles: FileItem[] = etcDrawings?.map(drawing => ({ ...drawing, type: '기타 도면' })) || []\n\n return html`\n <div body>\n <div file-list-container>\n ${mainFiles.length > 0\n ? html`\n <div section-title>주요 도면</div>\n ${mainFiles.map(\n file => html`\n <div file-item @click=${() => this._openPreview(file)}>\n <div file-info>\n <md-icon>description</md-icon>\n <span file-name>${file.name || ''}</span>\n <span file-type>${file.type}</span>\n </div>\n <md-icon slot=\"action\">visibility</md-icon>\n </div>\n `\n )}\n `\n : ''}\n ${etcFiles.length > 0\n ? html`\n <div section-title>기타 도면</div>\n ${etcFiles.map(\n file => html`\n <div file-item @click=${() => this._openPreview(file)}>\n <div file-info>\n <md-icon>attachment</md-icon>\n <span file-name>${file.name || ''}</span>\n <span file-type>${file.type}</span>\n </div>\n <md-icon slot=\"action\">visibility</md-icon>\n </div>\n `\n )}\n `\n : ''}\n ${mainFiles.length === 0 && etcFiles.length === 0\n ? html`\n <div empty-state>\n <md-icon>insert_drive_file</md-icon>\n <div>미리보기할 도면 파일이 없습니다.</div>\n </div>\n `\n : ''}\n </div>\n\n <div button-container>\n <md-outlined-button @click=${this._close}><md-icon slot=\"icon\">cancel</md-icon>닫기</md-outlined-button>\n </div>\n </div>\n `\n }\n\n private async _loadBuildingLevel() {\n try {\n const response = await client.query({\n query: gql`\n query BuildingLevel($buildingLevelId: String!) {\n buildingLevel(id: $buildingLevelId) {\n id\n floor\n building {\n id\n name\n }\n mainDrawing {\n id\n name\n fullpath\n }\n elevationDrawing {\n id\n name\n fullpath\n }\n rebarDistributionDrawing {\n id\n name\n fullpath\n }\n etcDrawings {\n id\n name\n fullpath\n }\n }\n }\n `,\n variables: {\n buildingLevelId: this.buildingLevelId\n }\n })\n\n this.buildingLevel = response.data?.buildingLevel || {}\n } catch (error) {\n console.error('Building level 정보를 불러오는 중 오류 발생:', error)\n }\n }\n\n private _openPreview(file: FileItem) {\n if (!file.fullpath) {\n return\n }\n\n // 파일 확장자로 타입 판단\n const fileExtension = file.fullpath.toLowerCase().split('.').pop()\n const isImage = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg'].includes(fileExtension || '')\n const isPdf = fileExtension === 'pdf'\n\n if (isImage) {\n this._openImagePreview(file)\n } else if (isPdf) {\n this._openPdfPreview(file)\n } else {\n // 기타 파일은 다운로드 또는 기본 뷰어\n this._openGenericPreview(file)\n }\n }\n\n private _openImagePreview(file: FileItem) {\n openPopup(\n html`\n <div style=\"display: flex; flex-direction: column;\">\n <div style=\"flex: 1; overflow: hidden; position: relative; touch-action: none;\">\n <img\n id=\"zoomable-image\"\n src=\"${ifDefined(file.fullpath)}\"\n alt=\"${ifDefined(file.name)}\"\n style=\"\n width: 100%; \n height: 100%; \n object-fit: contain; \n transform-origin: center center;\n transition: transform 0.1s ease-out;\n user-select: none;\n -webkit-user-select: none;\n \"\n @load=${this._initImageZoom}\n @error=${(e: Event) => {\n const target = e.target as HTMLImageElement\n target.style.display = 'none'\n target.parentElement!.innerHTML +=\n '<div style=\"padding: 40px; text-align: center; color: #666;\">이미지를 불러올 수 없습니다.</div>'\n }}\n />\n </div>\n </div>\n `,\n { backdrop: true, size: 'large', title: `[이미지 미리보기] ${file.name} (${file.type})` }\n )\n }\n\n private _openPdfPreview(file: FileItem) {\n openPopup(\n html`\n <div style=\"display: flex; flex-direction: column;\">\n <div style=\"flex: 1; overflow: hidden; touch-action: pan-x pan-y;\">\n <iframe\n src=\"${file.fullpath}#toolbar=1&navpanes=0&scrollbar=1&zoom=page-fit\"\n style=\"\n width: 100%; \n height: 100%; \n border: none; \n touch-action: pan-x pan-y;\n \"\n @error=${() => {\n // PDF 로드 실패시 새 탭에서 열기\n window.open(file.fullpath, '_blank')\n }}\n ></iframe>\n </div>\n </div>\n `,\n { backdrop: true, size: 'large', title: `[PDF 미리보기] ${file.name} (${file.type})` }\n )\n }\n\n // 기타 파일 타입은 새 탭에서 열기\n private _openGenericPreview(file: FileItem) {\n window.open(file.fullpath, '_blank')\n }\n\n private _close() {\n history.back()\n }\n\n private _initImageZoom(e: Event) {\n const img = e.target as HTMLImageElement\n let scale = 1\n let translateX = 0\n let translateY = 0\n let isDragging = false\n let lastTouchDistance = 0\n let lastTouchX = 0\n let lastTouchY = 0\n\n const updateTransform = () => {\n img.style.transform = `scale(${scale}) translate(${translateX}px, ${translateY}px)`\n }\n\n const resetTransform = () => {\n scale = 1\n translateX = 0\n translateY = 0\n updateTransform()\n }\n\n // 터치 시작\n img.addEventListener('touchstart', event => {\n event.preventDefault()\n\n if (event.touches.length === 1) {\n // 단일 터치 - 드래그 시작\n isDragging = true\n lastTouchX = event.touches[0].clientX\n lastTouchY = event.touches[0].clientY\n } else if (event.touches.length === 2) {\n // 멀티 터치 - 핀치 줌 시작\n isDragging = false\n const touch1 = event.touches[0]\n const touch2 = event.touches[1]\n lastTouchDistance = Math.sqrt(Math.pow(touch2.clientX - touch1.clientX, 2) + Math.pow(touch2.clientY - touch1.clientY, 2))\n }\n })\n\n // 터치 이동\n img.addEventListener('touchmove', event => {\n event.preventDefault()\n\n if (event.touches.length === 1 && isDragging && scale > 1) {\n // 드래그\n const currentX = event.touches[0].clientX\n const currentY = event.touches[0].clientY\n const deltaX = currentX - lastTouchX\n const deltaY = currentY - lastTouchY\n\n translateX += deltaX / scale\n translateY += deltaY / scale\n\n lastTouchX = currentX\n lastTouchY = currentY\n updateTransform()\n } else if (event.touches.length === 2) {\n // 핀치 줌\n const touch1 = event.touches[0]\n const touch2 = event.touches[1]\n const currentDistance = Math.sqrt(\n Math.pow(touch2.clientX - touch1.clientX, 2) + Math.pow(touch2.clientY - touch1.clientY, 2)\n )\n\n if (lastTouchDistance > 0) {\n const scaleChange = currentDistance / lastTouchDistance\n const newScale = scale * scaleChange\n\n // 최소/최대 배율 제한\n if (newScale >= 0.5 && newScale <= 5) {\n scale = newScale\n updateTransform()\n }\n }\n\n lastTouchDistance = currentDistance\n }\n })\n\n // 터치 끝\n img.addEventListener('touchend', event => {\n event.preventDefault()\n isDragging = false\n\n if (event.touches.length === 0) {\n // 모든 터치가 끝났을 때 - 경계 체크 및 보정\n if (scale < 1) {\n resetTransform()\n }\n }\n })\n\n // 더블 탭으로 줌 토글\n let lastTapTime = 0\n img.addEventListener('touchend', event => {\n const currentTime = new Date().getTime()\n const tapLength = currentTime - lastTapTime\n\n if (tapLength < 500 && tapLength > 0 && event.touches.length === 0) {\n if (scale === 1) {\n scale = 2\n updateTransform()\n } else {\n resetTransform()\n }\n }\n\n lastTapTime = currentTime\n })\n\n // 마우스 휠 지원 (데스크톱)\n img.addEventListener('wheel', event => {\n event.preventDefault()\n const delta = event.deltaY > 0 ? 0.9 : 1.1\n const newScale = scale * delta\n\n if (newScale >= 0.5 && newScale <= 5) {\n scale = newScale\n updateTransform()\n }\n })\n }\n}\n"]}
@@ -1,7 +1,9 @@
1
+ import '@material/web/fab/fab.js';
1
2
  import '@material/web/icon/icon.js';
2
3
  import '@operato/input/ox-input-signature.js';
3
4
  import './comment-list-popup';
4
5
  import './attachment-list-popup';
6
+ import '../../stt/speech-to-text';
5
7
  export declare const enum ChecklistMode {
6
8
  VIEWER = "VIEWER",
7
9
  EDITOR = "EDITOR"