@dssp/supervision 1.0.17 → 1.0.19

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 (28) hide show
  1. package/dist-client/pages/building-inspection/building-inspection-detail-ai-defect.js +7 -0
  2. package/dist-client/pages/building-inspection/building-inspection-detail-ai-defect.js.map +1 -1
  3. package/dist-client/pages/building-inspection/building-inspection-detail-ai-measurement.d.ts +7 -0
  4. package/dist-client/pages/building-inspection/building-inspection-detail-ai-measurement.js +101 -21
  5. package/dist-client/pages/building-inspection/building-inspection-detail-ai-measurement.js.map +1 -1
  6. package/dist-client/pages/building-inspection/building-inspection-detail-camera.js +6 -0
  7. package/dist-client/pages/building-inspection/building-inspection-detail-camera.js.map +1 -1
  8. package/dist-client/pages/building-inspection/building-inspection-detail-checklist.js +6 -0
  9. package/dist-client/pages/building-inspection/building-inspection-detail-checklist.js.map +1 -1
  10. package/dist-client/pages/building-inspection/building-inspection-detail-drawing.js +6 -0
  11. package/dist-client/pages/building-inspection/building-inspection-detail-drawing.js.map +1 -1
  12. package/dist-client/pages/building-inspection/component/building-inspection-detail-header.js +6 -6
  13. package/dist-client/pages/building-inspection/component/building-inspection-detail-header.js.map +1 -1
  14. package/dist-client/pages/building-inspection/inspection-create-popup.d.ts +1 -0
  15. package/dist-client/pages/building-inspection/inspection-create-popup.js +35 -8
  16. package/dist-client/pages/building-inspection/inspection-create-popup.js.map +1 -1
  17. package/dist-client/pages/building-inspection/inspection-part-picker.d.ts +31 -0
  18. package/dist-client/pages/building-inspection/inspection-part-picker.js +228 -0
  19. package/dist-client/pages/building-inspection/inspection-part-picker.js.map +1 -0
  20. package/dist-client/pages/building-inspection/inspection-with-checklist-create-popup.d.ts +1 -0
  21. package/dist-client/pages/building-inspection/inspection-with-checklist-create-popup.js +40 -1
  22. package/dist-client/pages/building-inspection/inspection-with-checklist-create-popup.js.map +1 -1
  23. package/dist-client/route.d.ts +1 -1
  24. package/dist-client/tsconfig.tsbuildinfo +1 -1
  25. package/dist-server/service/building-inspection/building-inspection-mutation.js +5 -1
  26. package/dist-server/service/building-inspection/building-inspection-mutation.js.map +1 -1
  27. package/dist-server/tsconfig.tsbuildinfo +1 -1
  28. package/package.json +3 -3
@@ -9,6 +9,7 @@ import { customElement, state } from 'lit/decorators.js';
9
9
  import { PageView } from '@operato/shell';
10
10
  import { CommonGristStyles, ScrollbarStyles } from '@operato/styles';
11
11
  import { client } from '@operato/graphql';
12
+ import { notify } from '@operato/layout';
12
13
  import './component/building-inspection-detail-header';
13
14
  let BuildingInspectionAiDefect = class BuildingInspectionAiDefect extends PageView {
14
15
  constructor() {
@@ -90,6 +91,12 @@ let BuildingInspectionAiDefect = class BuildingInspectionAiDefect extends PageVi
90
91
  if (response.errors)
91
92
  return;
92
93
  this.buildingInspection = response.data.buildingInspection;
94
+ // 삭제(soft delete)됐거나 다른 projectType의 검측이면 서버가 null을 준다.
95
+ // 그대로 진행하면 뒤 쿼리에 undefined가 실려 GraphQL 에러가 난다.
96
+ if (!this.buildingInspection) {
97
+ notify({ message: '삭제되었거나 접근할 수 없는 검측입니다.', level: 'warn' });
98
+ return;
99
+ }
93
100
  await this._getProjectByBuildingComplexId((_d = (_c = (_b = (_a = this.buildingInspection) === null || _a === void 0 ? void 0 : _a.buildingLevel) === null || _b === void 0 ? void 0 : _b.building) === null || _c === void 0 ? void 0 : _c.buildingComplex) === null || _d === void 0 ? void 0 : _d.id);
94
101
  }
95
102
  async _getProjectByBuildingComplexId(buildingComplexId) {
@@ -1 +1 @@
1
- {"version":3,"file":"building-inspection-detail-ai-defect.js","sourceRoot":"","sources":["../../../client/pages/building-inspection/building-inspection-detail-ai-defect.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@operato/input/ox-input-switch.js'\nimport '@operato/mini-map/ox-zoomable-image.js'\nimport '@material/web/progress/circular-progress.js'\n\nimport gql from 'graphql-tag'\nimport { css, html, PropertyValues } from 'lit'\nimport { customElement, state, query } from 'lit/decorators.js'\n\nimport { PageView } from '@operato/shell'\nimport { CommonGristStyles, ScrollbarStyles } from '@operato/styles'\nimport { PageLifecycle } from '@operato/shell/dist/src/app/pages/page-view'\nimport { client } from '@operato/graphql'\n\nimport './component/building-inspection-detail-header'\n\n@customElement('building-inspection-detail-ai-defect')\nexport class BuildingInspectionAiDefect extends PageView {\n static styles = [\n ScrollbarStyles,\n CommonGristStyles,\n css`\n :host {\n display: grid;\n grid-template-rows: 75px 1fr;\n color: #4e5055;\n width: 100%;\n height: 100%;\n background-color: #f7f7f7;\n }\n\n div[body] {\n display: flex;\n justify-items: center;\n gap: var(--spacing-medium);\n margin: var(--spacing-medium);\n min-height: 0;\n }\n\n div[preview] {\n flex: 1;\n border: 2px solid #ddd;\n border-radius: 10px;\n\n display: flex;\n justify-content: center;\n align-items: center;\n min-height: 0;\n overflow: hidden;\n }\n\n div[preview] video {\n flex: 1;\n max-width: 100%;\n max-height: 100%;\n object-fit: contain;\n }\n `\n ]\n\n @state() project: any = {}\n @state() buildingInspection: any = {}\n @state() buildingInspectionId: string = ''\n\n get context() {\n return {\n title: '검측 관리 상세 - AI 하자 분석'\n }\n }\n\n render() {\n return html`\n <building-inspection-detail-header\n .buildingInspectionId=${this.buildingInspection?.id}\n .buildingLevelId=${this.buildingInspection?.buildingLevel?.id}\n .projectName=${this.project.name}\n .buildingName=${this.buildingInspection?.buildingLevel?.building?.name}\n .buildingLevelFloor=${this.buildingInspection?.buildingLevel?.floor}\n .projectId=${this.project.id}\n ></building-inspection-detail-header>\n\n <div body>\n <div preview>\n <video src=\"/attachment/21e53688-5262-4fae-961a-591db7114b75.mp4\" controls playsinline></video>\n </div>\n </div>\n `\n }\n\n protected async updated(changes: PropertyValues): Promise<void> {}\n\n async pageUpdated(changes: any, lifecycle: PageLifecycle) {\n if (this.active) {\n this.buildingInspectionId = lifecycle.resourceId || ''\n await this.initBuildingInspection(this.buildingInspectionId)\n }\n }\n\n async initBuildingInspection(buildingInspectionId: string = '') {\n const response = await client.query({\n query: gql`\n query BuildingInspection($buildingInspectionId: String!) {\n buildingInspection(id: $buildingInspectionId) {\n id\n status\n requestDate\n drawingMarker\n checklist {\n id\n location\n inspectionDrawingType\n }\n buildingLevel {\n id\n floor\n mainDrawing {\n id\n name\n fullpath\n }\n mainDrawingImage\n building {\n id\n name\n buildingComplex {\n id\n }\n }\n }\n }\n }\n `,\n variables: {\n buildingInspectionId\n }\n })\n\n if (response.errors) return\n\n this.buildingInspection = response.data.buildingInspection\n\n await this._getProjectByBuildingComplexId(this.buildingInspection?.buildingLevel?.building?.buildingComplex?.id)\n }\n\n private async _getProjectByBuildingComplexId(buildingComplexId) {\n const response = await client.query({\n query: gql`\n query ProjectByBuildingComplexId($buildingComplexId: String!) {\n project: projectByBuildingComplexId(buildingComplexId: $buildingComplexId) {\n id\n name\n }\n }\n `,\n variables: {\n buildingComplexId\n }\n })\n\n if (response.errors) return\n\n this.project = response.data.project\n }\n}\n"]}
1
+ {"version":3,"file":"building-inspection-detail-ai-defect.js","sourceRoot":"","sources":["../../../client/pages/building-inspection/building-inspection-detail-ai-defect.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@operato/input/ox-input-switch.js'\nimport '@operato/mini-map/ox-zoomable-image.js'\nimport '@material/web/progress/circular-progress.js'\n\nimport gql from 'graphql-tag'\nimport { css, html, PropertyValues } from 'lit'\nimport { customElement, state, query } from 'lit/decorators.js'\n\nimport { PageView } from '@operato/shell'\nimport { CommonGristStyles, ScrollbarStyles } from '@operato/styles'\nimport { PageLifecycle } from '@operato/shell/dist/src/app/pages/page-view'\nimport { client } from '@operato/graphql'\nimport { notify } from '@operato/layout'\n\nimport './component/building-inspection-detail-header'\n\n@customElement('building-inspection-detail-ai-defect')\nexport class BuildingInspectionAiDefect extends PageView {\n static styles = [\n ScrollbarStyles,\n CommonGristStyles,\n css`\n :host {\n display: grid;\n grid-template-rows: 75px 1fr;\n color: #4e5055;\n width: 100%;\n height: 100%;\n background-color: #f7f7f7;\n }\n\n div[body] {\n display: flex;\n justify-items: center;\n gap: var(--spacing-medium);\n margin: var(--spacing-medium);\n min-height: 0;\n }\n\n div[preview] {\n flex: 1;\n border: 2px solid #ddd;\n border-radius: 10px;\n\n display: flex;\n justify-content: center;\n align-items: center;\n min-height: 0;\n overflow: hidden;\n }\n\n div[preview] video {\n flex: 1;\n max-width: 100%;\n max-height: 100%;\n object-fit: contain;\n }\n `\n ]\n\n @state() project: any = {}\n @state() buildingInspection: any = {}\n @state() buildingInspectionId: string = ''\n\n get context() {\n return {\n title: '검측 관리 상세 - AI 하자 분석'\n }\n }\n\n render() {\n return html`\n <building-inspection-detail-header\n .buildingInspectionId=${this.buildingInspection?.id}\n .buildingLevelId=${this.buildingInspection?.buildingLevel?.id}\n .projectName=${this.project.name}\n .buildingName=${this.buildingInspection?.buildingLevel?.building?.name}\n .buildingLevelFloor=${this.buildingInspection?.buildingLevel?.floor}\n .projectId=${this.project.id}\n ></building-inspection-detail-header>\n\n <div body>\n <div preview>\n <video src=\"/attachment/21e53688-5262-4fae-961a-591db7114b75.mp4\" controls playsinline></video>\n </div>\n </div>\n `\n }\n\n protected async updated(changes: PropertyValues): Promise<void> {}\n\n async pageUpdated(changes: any, lifecycle: PageLifecycle) {\n if (this.active) {\n this.buildingInspectionId = lifecycle.resourceId || ''\n await this.initBuildingInspection(this.buildingInspectionId)\n }\n }\n\n async initBuildingInspection(buildingInspectionId: string = '') {\n const response = await client.query({\n query: gql`\n query BuildingInspection($buildingInspectionId: String!) {\n buildingInspection(id: $buildingInspectionId) {\n id\n status\n requestDate\n drawingMarker\n checklist {\n id\n location\n inspectionDrawingType\n }\n buildingLevel {\n id\n floor\n mainDrawing {\n id\n name\n fullpath\n }\n mainDrawingImage\n building {\n id\n name\n buildingComplex {\n id\n }\n }\n }\n }\n }\n `,\n variables: {\n buildingInspectionId\n }\n })\n\n if (response.errors) return\n\n this.buildingInspection = response.data.buildingInspection\n\n // 삭제(soft delete)됐거나 다른 projectType의 검측이면 서버가 null을 준다.\n // 그대로 진행하면 뒤 쿼리에 undefined가 실려 GraphQL 에러가 난다.\n if (!this.buildingInspection) {\n notify({ message: '삭제되었거나 접근할 수 없는 검측입니다.', level: 'warn' })\n return\n }\n\n await this._getProjectByBuildingComplexId(this.buildingInspection?.buildingLevel?.building?.buildingComplex?.id)\n }\n\n private async _getProjectByBuildingComplexId(buildingComplexId) {\n const response = await client.query({\n query: gql`\n query ProjectByBuildingComplexId($buildingComplexId: String!) {\n project: projectByBuildingComplexId(buildingComplexId: $buildingComplexId) {\n id\n name\n }\n }\n `,\n variables: {\n buildingComplexId\n }\n })\n\n if (response.errors) return\n\n this.project = response.data.project\n }\n}\n"]}
@@ -41,6 +41,7 @@ export declare class BuildingInspectionAiMeasurement extends PageView {
41
41
  setPickerFor: number | null;
42
42
  recentSets: any[];
43
43
  recentLoading: boolean;
44
+ private autoAttachSince;
44
45
  expandedReportId: string | null;
45
46
  measureSetIndex: number;
46
47
  measureInputs: Record<string, string>;
@@ -66,7 +67,9 @@ export declare class BuildingInspectionAiMeasurement extends PageView {
66
67
  private setInputEl;
67
68
  private pendingSetIndex;
68
69
  private objectUrls;
70
+ private _onVisible;
69
71
  connectedCallback(): Promise<void>;
72
+ disconnectedCallback(): void;
70
73
  get context(): {
71
74
  title: string;
72
75
  };
@@ -96,6 +99,10 @@ export declare class BuildingInspectionAiMeasurement extends PageView {
96
99
  private _callMeasure;
97
100
  private _fetchMeasureImage;
98
101
  private _loadShotGuide;
102
+ private _shotTag;
103
+ private _shotTechnique;
104
+ private _shotAppLink;
105
+ private _autoAttachTaggedSets;
99
106
  private _renderSetChooser;
100
107
  private _loadRecentSets;
101
108
  private _openSetChooser;
@@ -82,6 +82,7 @@ let BuildingInspectionAiMeasurement = class BuildingInspectionAiMeasurement exte
82
82
  this.setPickerFor = null; // 세트 고르는 중인 촬영 번호 (-1 = 재촬영)
83
83
  this.recentSets = []; // 측정 서버에 올라온 최근 세트 (앱이 촬영 직후 자동 전송)
84
84
  this.recentLoading = false;
85
+ this.autoAttachSince = 0; // 이 시각(초) 이후 업로드된 세트만 자동 등록
85
86
  this.expandedReportId = null; // 종합 리포트에서 펼친 항목(아코디언)
86
87
  /* 측정 패널 상태 */
87
88
  this.measureSetIndex = 0;
@@ -101,6 +102,11 @@ let BuildingInspectionAiMeasurement = class BuildingInspectionAiMeasurement exte
101
102
  /* 세트 업로드 대상: 촬영 건 인덱스(가이드 화면) 또는 -1(재촬영 패널) */
102
103
  this.pendingSetIndex = null;
103
104
  this.objectUrls = []; // 페이지 이탈 시 정리할 blob URL들
105
+ /* 촬영 앱에 다녀온 뒤 화면이 다시 보이면 올라온 세트를 찾아 자동 등록한다 */
106
+ this._onVisible = () => {
107
+ if (document.visibilityState === 'visible' && this.guide && !this.result)
108
+ this._autoAttachTaggedSets();
109
+ };
104
110
  this._openReport = () => {
105
111
  this.panel = 'report';
106
112
  this.selectedItem = null;
@@ -129,9 +135,14 @@ let BuildingInspectionAiMeasurement = class BuildingInspectionAiMeasurement exte
129
135
  }
130
136
  async connectedCallback() {
131
137
  super.connectedCallback();
138
+ document.addEventListener('visibilitychange', this._onVisible);
132
139
  this.canMutate = await canDcspMutate();
133
140
  this.updateContext();
134
141
  }
142
+ disconnectedCallback() {
143
+ document.removeEventListener('visibilitychange', this._onVisible);
144
+ super.disconnectedCallback();
145
+ }
135
146
  get context() {
136
147
  return {
137
148
  title: '검측 관리 상세 - AI 자동 검측'
@@ -174,14 +185,10 @@ let BuildingInspectionAiMeasurement = class BuildingInspectionAiMeasurement exte
174
185
  ${this.result ? this._renderResult() : this._renderGuide()}
175
186
  </div>
176
187
 
177
- <!-- 세트 파일 선택: video.mov/poses.jsonl/meta.json 3개 멀티선택 또는 zip 1개 -->
178
- <input
179
- id="setInput"
180
- type="file"
181
- accept=".mov,.jsonl,.json,.zip,video/quicktime"
182
- multiple
183
- @change=${this._onSetFilesSelected}
184
- />
188
+ <!-- 세트 파일 선택: video.mov/poses.jsonl/meta.json 3개 멀티선택 또는 zip 1개.
189
+ accept을 두지 않는다 — iOS는 확장자를 UTI로 바꿔 거르는데 .jsonl은 매핑이 없어
190
+ 파일 앱에서 poses.jsonl이 비활성으로 보인다. 파일명 검증은 아래 핸들러가 한다. -->
191
+ <input id="setInput" type="file" multiple @change=${this._onSetFilesSelected} />
185
192
 
186
193
  ${this.loading
187
194
  ? html `
@@ -232,11 +239,6 @@ let BuildingInspectionAiMeasurement = class BuildingInspectionAiMeasurement exte
232
239
  <span>${uploaded} / ${total}</span>
233
240
  </div>
234
241
  <div class="progress-bar"><div style="width: ${total ? (uploaded / total) * 100 : 0}%"></div></div>
235
- <div class="app-links">
236
- <span>촬영 앱 열기</span>
237
- <a href=${appCaptureLink('cover')}>철근 배근 길이</a>
238
- <a href=${appCaptureLink('thick')}>벽·기둥 두께</a>
239
- </div>
240
242
  ${shots.map((s, i) => {
241
243
  const set = this.sets[i];
242
244
  return html `
@@ -245,9 +247,8 @@ let BuildingInspectionAiMeasurement = class BuildingInspectionAiMeasurement exte
245
247
  <div class="set-main">
246
248
  <b>촬영 ${s.shot_no || i + 1} · ${s.target}</b>
247
249
  ${set
248
- ? html `세트 업로드 완료${set.technique ? ` (${set.technique})` : ''}
249
- ${set.warnings.map(w => html `<div class="set-warn">⚠ ${w}</div>`)}`
250
- : html `탭해서 앱으로 촬영한 세트를 선택 (또는 파일 직접 선택)`}
250
+ ? html `세트 연결 완료 (${set.setId}) ${set.warnings.map(w => html `<div class="set-warn">⚠ ${w}</div>`)}`
251
+ : html `「앱으로 촬영」을 누르면 촬영 후 이 칸에 자동 등록됩니다 · 탭하면 직접 선택`}
251
252
  </div>
252
253
  ${set
253
254
  ? html `<button
@@ -259,7 +260,9 @@ let BuildingInspectionAiMeasurement = class BuildingInspectionAiMeasurement exte
259
260
  >
260
261
  교체
261
262
  </button>`
262
- : ''}
263
+ : html `<a class="shoot-link" href=${this._shotAppLink(s, i)} @click=${(e) => e.stopPropagation()}>
264
+ <md-icon>photo_camera</md-icon>앱으로 촬영
265
+ </a>`}
263
266
  </div>
264
267
  ${this.setPickerFor === i ? this._renderSetChooser(i) : ''}
265
268
  `;
@@ -504,9 +507,8 @@ let BuildingInspectionAiMeasurement = class BuildingInspectionAiMeasurement exte
504
507
  `
505
508
  : ''}
506
509
  <div class="app-links">
507
- <span>촬영 앱 열기</span>
508
- <a href=${appCaptureLink('cover')}>철근 배근 길이</a>
509
- <a href=${appCaptureLink('thick')}>벽·기둥 두께</a>
510
+ <span>앱으로 재촬영</span>
511
+ <a href=${`${appCaptureLink(this._shotTechnique(shot))}&set=${encodeURIComponent(this._shotTag(0))}`}> 촬영 시작 </a>
510
512
  </div>
511
513
  <div class="set-card" ?uploaded=${!!this.recaptureSet} @click=${() => this._openSetChooser(-1)}>
512
514
  <md-icon>${this.recaptureSet ? 'check_circle' : 'video_file'}</md-icon>
@@ -766,6 +768,12 @@ let BuildingInspectionAiMeasurement = class BuildingInspectionAiMeasurement exte
766
768
  if (response.errors)
767
769
  return;
768
770
  this.buildingInspection = response.data.buildingInspection;
771
+ // 삭제(soft delete)됐거나 다른 projectType의 검측이면 서버가 null을 준다.
772
+ // 그대로 진행하면 뒤 쿼리에 undefined가 실려 GraphQL 에러가 난다.
773
+ if (!this.buildingInspection) {
774
+ notify({ message: '삭제되었거나 접근할 수 없는 검측입니다.', level: 'warn' });
775
+ return;
776
+ }
769
777
  await this._getProjectByBuildingComplexId((_d = (_c = (_b = (_a = this.buildingInspection) === null || _a === void 0 ? void 0 : _a.buildingLevel) === null || _b === void 0 ? void 0 : _b.building) === null || _c === void 0 ? void 0 : _c.buildingComplex) === null || _d === void 0 ? void 0 : _d.id);
770
778
  }
771
779
  async _getProjectByBuildingComplexId(buildingComplexId) {
@@ -836,6 +844,9 @@ let BuildingInspectionAiMeasurement = class BuildingInspectionAiMeasurement exte
836
844
  this.loadingText = 'AI 촬영 가이드 생성 중...';
837
845
  this.guide = await this._callEngine({ action: 'plan-shots', checklist });
838
846
  this.sets = new Array((((_a = this.guide) === null || _a === void 0 ? void 0 : _a.shots) || []).length).fill(null);
847
+ // 자동 등록 기준 시각 — 이 시점 이후 올라온 세트만 자동으로 붙인다.
848
+ // (예전 테스트 세트가 태그만 같다고 채워지면 "찍지도 않았는데 완료"로 보인다)
849
+ this.autoAttachSince = Date.now() / 1000;
839
850
  }
840
851
  catch (error) {
841
852
  console.error('촬영 가이드 생성 실패:', error);
@@ -845,6 +856,48 @@ let BuildingInspectionAiMeasurement = class BuildingInspectionAiMeasurement exte
845
856
  this.loading = false;
846
857
  }
847
858
  }
859
+ /* 촬영 칸마다 고유 태그 — 앱에 부재 ID로 넘겨 두면, 올라온 세트의 member_id로
860
+ "어느 촬영 칸의 것인지" 되찾을 수 있다 (앱↔플랫폼 왕복에 식별자가 없어서 쓰는 방법) */
861
+ _shotTag(shotNo) {
862
+ return `${(this.buildingInspectionId || 'ins').replace(/-/g, '').slice(0, 6)}-${shotNo}`;
863
+ }
864
+ /* 촬영 지시문에서 기술 추론 — 앱의 촬영 안내만 달라진다(측정은 meta.technique를 안 봄) */
865
+ _shotTechnique(shot) {
866
+ const text = `${(shot === null || shot === void 0 ? void 0 : shot.target) || ''} ${(shot === null || shot === void 0 ? void 0 : shot.how) || ''}`;
867
+ if (/두께|콘크리트\s*면|거푸집|탈형|벽체\s*면/.test(text))
868
+ return 'thick';
869
+ return 'cover';
870
+ }
871
+ _shotAppLink(shot, index) {
872
+ const tag = this._shotTag((shot === null || shot === void 0 ? void 0 : shot.shot_no) || index + 1);
873
+ return `${appCaptureLink(this._shotTechnique(shot))}&set=${encodeURIComponent(tag)}`;
874
+ }
875
+ /* 앱에서 촬영하고 돌아오면 태그로 찾아 해당 촬영 칸에 자동 등록 */
876
+ async _autoAttachTaggedSets() {
877
+ var _a, _b;
878
+ if (!((_b = (_a = this.guide) === null || _a === void 0 ? void 0 : _a.shots) === null || _b === void 0 ? void 0 : _b.length))
879
+ return;
880
+ await this._loadRecentSets();
881
+ const used = new Set(this.uploadedSets.map(s => s.setId));
882
+ const attached = [];
883
+ const next = [...this.sets];
884
+ this.guide.shots.forEach((shot, i) => {
885
+ if (next[i])
886
+ return;
887
+ const tag = this._shotTag((shot === null || shot === void 0 ? void 0 : shot.shot_no) || i + 1);
888
+ // 이 화면을 연 뒤에 올라온 것만 — 예전 세트는 목록에서 직접 고르게 둔다
889
+ const hit = this.recentSets.find(s => s.member_id === tag && !used.has(s.set_id) && (s.uploaded_at || 0) >= this.autoAttachSince);
890
+ if (!hit)
891
+ return;
892
+ used.add(hit.set_id);
893
+ next[i] = { setId: hit.set_id, technique: hit.technique, label: `세트 ${hit.set_id}`, warnings: [] };
894
+ attached.push((shot === null || shot === void 0 ? void 0 : shot.shot_no) || i + 1);
895
+ });
896
+ if (attached.length) {
897
+ this.sets = next;
898
+ notify({ message: `촬영 ${attached.join('·')} 세트를 자동으로 등록했습니다.` });
899
+ }
900
+ }
848
901
  /* 세트 선택 목록 — 앱이 올린 최근 세트 + 파일 직접 선택 폴백 */
849
902
  _renderSetChooser(index) {
850
903
  return html `
@@ -861,7 +914,9 @@ let BuildingInspectionAiMeasurement = class BuildingInspectionAiMeasurement exte
861
914
  </span>
862
915
  </button>
863
916
  `)
864
- : html `<div class="chooser-empty">앱에서 올린 세트가 없습니다. 앱으로 촬영하거나 파일을 직접 선택하세요.</div>`}
917
+ : html `<div class="chooser-empty">
918
+ 올라온 세트가 없습니다. 카드의 「앱으로 촬영」으로 찍으면 여기에 나타납니다.
919
+ </div>`}
865
920
  <div class="chooser-actions">
866
921
  <button class="text-button" @click=${() => this._loadRecentSets()}>새로고침</button>
867
922
  <button class="text-button" @click=${() => this._openSetPicker(index)}>파일 직접 선택</button>
@@ -1543,6 +1598,26 @@ BuildingInspectionAiMeasurement.styles = [
1543
1598
  color: #777;
1544
1599
  }
1545
1600
 
1601
+ /* 촬영 칸의 「앱으로 촬영」 */
1602
+ .shoot-link {
1603
+ display: inline-flex;
1604
+ align-items: center;
1605
+ gap: 4px;
1606
+ flex-shrink: 0;
1607
+ padding: 7px 12px;
1608
+ border-radius: 999px;
1609
+ background: #7d2f2f;
1610
+ color: #fff;
1611
+ font-size: 12px;
1612
+ font-weight: 600;
1613
+ text-decoration: none;
1614
+ }
1615
+
1616
+ .shoot-link md-icon {
1617
+ --md-icon-size: 16px;
1618
+ color: #fff;
1619
+ }
1620
+
1546
1621
  .app-links a {
1547
1622
  display: inline-flex;
1548
1623
  align-items: center;
@@ -1604,6 +1679,11 @@ BuildingInspectionAiMeasurement.styles = [
1604
1679
  padding-top: 4px;
1605
1680
  }
1606
1681
 
1682
+ .set-chooser .app-links {
1683
+ border-top: 1px solid #f0f0f0;
1684
+ padding: 8px 4px 6px;
1685
+ }
1686
+
1607
1687
  /* 세트 업로드 카드 */
1608
1688
  .set-card {
1609
1689
  display: flex;