jeongsoo commited on
Commit
c9321b7
Β·
1 Parent(s): 458fff2
Files changed (2) hide show
  1. app/static/js/app-device.js +105 -100
  2. app/static/js/app.js +18 -0
app/static/js/app-device.js CHANGED
@@ -19,16 +19,74 @@ const programsLoading = document.getElementById('programsLoading');
19
  const programsList = document.getElementById('programsList');
20
  const noProgramsMessage = document.getElementById('noProgramsMessage');
21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  // νŽ˜μ΄μ§€ λ‘œλ“œ μ‹œ μ΄ˆκΈ°ν™”
23
  document.addEventListener('DOMContentLoaded', () => {
24
  console.log("μž₯치 관리 λͺ¨λ“ˆ μ΄ˆκΈ°ν™”");
25
 
26
- // νƒ­ μ „ν™˜ 이벀트 λ¦¬μŠ€λ„ˆ μΆ”κ°€
27
- deviceTab.addEventListener('click', () => {
28
- console.log("μž₯치 관리 νƒ­ 클릭");
29
- switchTab('device');
30
- checkDeviceStatus(); // νƒ­ μ „ν™˜ μ‹œ μžλ™μœΌλ‘œ μƒνƒœ 확인
31
- });
 
 
 
 
32
 
33
  // μž₯치 μƒνƒœ 확인 λ²„νŠΌ 이벀트 λ¦¬μŠ€λ„ˆ
34
  checkDeviceStatusButton.addEventListener('click', () => {
@@ -49,48 +107,6 @@ document.addEventListener('DOMContentLoaded', () => {
49
  });
50
  });
51
 
52
- /**
53
- * νƒ­ μ „ν™˜ ν•¨μˆ˜ (κΈ°μ‘΄ app.js의 ν•¨μˆ˜ ν™•μž₯)
54
- * @param {string} tabName - ν™œμ„±ν™”ν•  νƒ­ 이름 ('chat', 'docs', 'device')
55
- */
56
- function switchTab(tabName) {
57
- // app.js에 이미 μ •μ˜λœ ν•¨μˆ˜λ₯Ό μ°Έμ‘°
58
- if (typeof window.switchTab === 'function') {
59
- // κΈ°μ‘΄ switchTab ν•¨μˆ˜ 호좜
60
- window.switchTab(tabName);
61
- return;
62
- }
63
-
64
- // κΈ°μ‘΄ ν•¨μˆ˜κ°€ μ—†λŠ” 경우λ₯Ό λŒ€λΉ„ν•œ κΈ°λ³Έ κ΅¬ν˜„
65
- const chatTab = document.getElementById('chatTab');
66
- const docsTab = document.getElementById('docsTab');
67
- const chatSection = document.getElementById('chatSection');
68
- const docsSection = document.getElementById('docsSection');
69
-
70
- if (tabName === 'chat') {
71
- chatTab.classList.add('active');
72
- docsTab.classList.remove('active');
73
- deviceTab.classList.remove('active');
74
- chatSection.classList.add('active');
75
- docsSection.classList.remove('active');
76
- deviceSection.classList.remove('active');
77
- } else if (tabName === 'docs') {
78
- chatTab.classList.remove('active');
79
- docsTab.classList.add('active');
80
- deviceTab.classList.remove('active');
81
- chatSection.classList.remove('active');
82
- docsSection.classList.add('active');
83
- deviceSection.classList.remove('active');
84
- } else if (tabName === 'device') {
85
- chatTab.classList.remove('active');
86
- docsTab.classList.remove('active');
87
- deviceTab.classList.add('active');
88
- chatSection.classList.remove('active');
89
- docsSection.classList.remove('active');
90
- deviceSection.classList.add('active');
91
- }
92
- }
93
-
94
  /**
95
  * μ—λŸ¬ 처리 헬퍼 ν•¨μˆ˜
96
  * @param {Error} error - λ°œμƒν•œ 였λ₯˜
@@ -126,7 +142,7 @@ function escapeHtml(unsafe) {
126
  }
127
 
128
  /**
129
- * μž₯치 관리 μ„œλ²„ μƒνƒœ 확인 ν•¨μˆ˜
130
  */
131
  async function checkDeviceStatus() {
132
  console.log("μž₯치 μƒνƒœ 확인 쀑...");
@@ -140,8 +156,8 @@ async function checkDeviceStatus() {
140
  const controller = new AbortController();
141
  const timeoutId = setTimeout(() => controller.abort(), 5000); // 5초 νƒ€μž„μ•„μ›ƒ
142
 
143
- // API μš”μ²­
144
- const response = await fetch('/api/device/status', {
145
  signal: controller.signal
146
  });
147
 
@@ -159,10 +175,10 @@ async function checkDeviceStatus() {
159
  deviceStatusLoading.classList.add('hidden');
160
  deviceStatusResult.classList.remove('hidden');
161
 
162
- if (data.success) {
163
  // 온라인 μƒνƒœμΈ 경우
164
  statusIcon.innerHTML = '<i class="fas fa-circle online"></i>';
165
- statusText.textContent = `μ„œλ²„ μƒνƒœ: ${data.server_status || '정상'}`;
166
 
167
  // μžλ™μœΌλ‘œ μž₯치 λͺ©λ‘ λ‘œλ“œ
168
  loadDevices();
@@ -199,8 +215,8 @@ async function loadDevices() {
199
  const controller = new AbortController();
200
  const timeoutId = setTimeout(() => controller.abort(), 5000); // 5초 νƒ€μž„μ•„μ›ƒ
201
 
202
- // API μš”μ²­
203
- const response = await fetch('/api/device/list', {
204
  signal: controller.signal
205
  });
206
 
@@ -217,19 +233,15 @@ async function loadDevices() {
217
  // UI μ—…λ°μ΄νŠΈ
218
  devicesLoading.classList.add('hidden');
219
 
220
- if (data.success && data.devices && data.devices.length > 0) {
221
- // μž₯μΉ˜κ°€ μžˆλŠ” 경우
222
  data.devices.forEach(device => {
223
- const deviceItem = createDeviceItem(device);
224
- deviceList.appendChild(deviceItem);
225
  });
226
  } else {
227
- // μž₯μΉ˜κ°€ μ—†λŠ” 경우
228
  noDevicesMessage.classList.remove('hidden');
229
-
230
- if (data.error) {
231
- noDevicesMessage.querySelector('p').textContent = `였λ₯˜: ${data.error}`;
232
- }
233
  }
234
  } catch (error) {
235
  console.error("μž₯치 λͺ©λ‘ λ‘œλ“œ 였λ₯˜:", error);
@@ -237,7 +249,7 @@ async function loadDevices() {
237
  // UI μ—…λ°μ΄νŠΈ
238
  devicesLoading.classList.add('hidden');
239
  noDevicesMessage.classList.remove('hidden');
240
- noDevicesMessage.querySelector('p').textContent = handleError(error);
241
  }
242
  }
243
 
@@ -334,8 +346,8 @@ async function loadPrograms() {
334
  const controller = new AbortController();
335
  const timeoutId = setTimeout(() => controller.abort(), 5000); // 5초 νƒ€μž„μ•„μ›ƒ
336
 
337
- // API μš”μ²­
338
- const response = await fetch('/api/device/programs', {
339
  signal: controller.signal
340
  });
341
 
@@ -352,20 +364,15 @@ async function loadPrograms() {
352
  // UI μ—…λ°μ΄νŠΈ
353
  programsLoading.classList.add('hidden');
354
 
355
- if (data.success && data.programs && data.programs.length > 0) {
356
- // ν”„λ‘œκ·Έλž¨μ΄ μžˆλŠ” 경우
357
  data.programs.forEach(program => {
358
- const programItem = createProgramItem(program);
359
- programsList.appendChild(programItem);
360
  });
361
- noProgramsMessage.classList.add('hidden');
362
  } else {
363
- // ν”„λ‘œκ·Έλž¨μ΄ μ—†λŠ” 경우
364
  noProgramsMessage.classList.remove('hidden');
365
-
366
- if (data.error) {
367
- noProgramsMessage.querySelector('p').textContent = `였λ₯˜: ${data.error}`;
368
- }
369
  }
370
  } catch (error) {
371
  console.error("ν”„λ‘œκ·Έλž¨ λͺ©λ‘ λ‘œλ“œ 였λ₯˜:", error);
@@ -373,7 +380,7 @@ async function loadPrograms() {
373
  // UI μ—…λ°μ΄νŠΈ
374
  programsLoading.classList.add('hidden');
375
  noProgramsMessage.classList.remove('hidden');
376
- noProgramsMessage.querySelector('p').textContent = handleError(error);
377
  }
378
  }
379
 
@@ -421,52 +428,50 @@ function createProgramItem(program) {
421
 
422
  /**
423
  * ν”„λ‘œκ·Έλž¨ μ‹€ν–‰ ν•¨μˆ˜
424
- * @param {string} programId - ν”„λ‘œκ·Έλž¨ ID
425
- * @param {string} programName - ν”„λ‘œκ·Έλž¨ 이름
426
  */
427
  async function executeProgram(programId, programName) {
428
- console.log(`ν”„λ‘œκ·Έλž¨ μ‹€ν–‰: ${programName} (ID: ${programId})`);
 
 
429
 
430
  // μ‹€ν–‰ 확인
431
  if (!confirm(`'${programName}' ν”„λ‘œκ·Έλž¨μ„ μ‹€ν–‰ν•˜μ‹œκ² μŠ΅λ‹ˆκΉŒ?`)) {
 
432
  return;
433
  }
434
 
435
  try {
436
- // νƒ€μž„μ•„μ›ƒ 섀정을 μœ„ν•œ 컨트둀러
437
- const controller = new AbortController();
438
- const timeoutId = setTimeout(() => controller.abort(), 10000); // 10초 νƒ€μž„μ•„μ›ƒ (μ‹€ν–‰μ—λŠ” 더 κΈ΄ μ‹œκ°„ λΆ€μ—¬)
439
 
440
- // API μš”μ²­
441
- const response = await fetch(`/api/device/programs/${programId}/execute`, {
442
  method: 'POST',
443
  headers: {
444
  'Content-Type': 'application/json'
445
  },
446
- body: JSON.stringify({}),
447
- signal: controller.signal
448
  });
449
 
450
- clearTimeout(timeoutId); // νƒ€μž„μ•„μ›ƒ ν•΄μ œ
451
-
452
  // 응닡 처리
453
  if (!response.ok) {
454
- const errorData = await response.json();
455
- throw new Error(errorData.error || `HTTP 였λ₯˜: ${response.status}`);
456
  }
457
 
458
  const data = await response.json();
459
- console.log("ν”„λ‘œκ·Έλž¨ μ‹€ν–‰ 응닡:", data);
460
 
461
- // 성곡 λ©”μ‹œμ§€
462
  if (data.success) {
463
- showNotification(`'${programName}' 싀행이 μš”μ²­λ˜μ—ˆμŠ΅λ‹ˆλ‹€.`, 'success');
464
  } else {
465
- showNotification(`'${programName}' μ‹€ν–‰ μš”μ²­ μ‹€νŒ¨: ${data.error || 'μ•Œ 수 μ—†λŠ” 였λ₯˜'}`, 'error');
466
  }
467
  } catch (error) {
468
- console.error(`ν”„λ‘œκ·Έλž¨ μ‹€ν–‰ 였λ₯˜ (${programName}):`, error);
469
- showNotification(`'${programName}' μ‹€ν–‰ 쀑 였λ₯˜ λ°œμƒ: ${handleError(error)}`, 'error');
470
  }
471
  }
472
 
@@ -508,5 +513,5 @@ function showNotification(message, type = 'info') {
508
  }, 5000); // 5초 ν›„ 사라짐
509
  }
510
 
511
- // μ•± μ „μ—­μ—μ„œ switchTab ν•¨μˆ˜κ°€ μ‚¬μš© κ°€λŠ₯ν•˜λ„λ‘ export
512
- window.deviceSwitchTab = switchTab;
 
19
  const programsList = document.getElementById('programsList');
20
  const noProgramsMessage = document.getElementById('noProgramsMessage');
21
 
22
+ // μž₯치 μ„œλ²„ URL μ„€μ •
23
+ // 직접 μ ‘κ·Ό μ„€μ •: 빈 λ¬Έμžμ—΄μ΄λ©΄ ν˜„μž¬ 호슀트의 5050 포트λ₯Ό μ‚¬μš©
24
+ let DEVICE_SERVER_URL = ''; // μ„œλ²„μ—μ„œ μ „λ‹¬λœ URL이 있으면 μ΄ˆκΈ°ν™” μ‹œ 섀정됨
25
+
26
+ /**
27
+ * μž₯치 μ„œλ²„ API 경둜 생성 ν•¨μˆ˜
28
+ * @param {string} endpoint - API μ—”λ“œν¬μΈνŠΈ 경둜
29
+ * @returns {string} - μ™„μ „ν•œ API URL
30
+ */
31
+ function getDeviceApiUrl(endpoint) {
32
+ // 직접 μ ‘κ·Ό λͺ¨λ“œμΈ 경우 (별도 μ„œλ²„μ— 직접 μš”μ²­)
33
+ if (DEVICE_SERVER_URL) {
34
+ return `${DEVICE_SERVER_URL}${endpoint}`;
35
+ }
36
+
37
+ // ν”„λ‘μ‹œ λͺ¨λ“œμΈ 경우 (λ‚΄λΆ€ API 경둜 μ‚¬μš©)
38
+ return endpoint;
39
+ }
40
+
41
+ /**
42
+ * μž₯치 μ„œλ²„ μ„€μ • μ΄ˆκΈ°ν™” (νŽ˜μ΄μ§€ λ‘œλ“œ μ‹œ 호좜)
43
+ */
44
+ function initDeviceServerSettings() {
45
+ console.log("μž₯치 μ„œλ²„ μ„€μ • μ΄ˆκΈ°ν™” μ‹œμž‘");
46
+ // μ„œλ²„ URL μ„€μ • (μ›Ή μ„œλ²„λ‘œλΆ€ν„° μ„€μ • 뢈러였기)
47
+ fetch('/api/device/settings')
48
+ .then(response => {
49
+ if (!response.ok) {
50
+ throw new Error('섀정을 κ°€μ Έμ˜¬ 수 μ—†μŠ΅λ‹ˆλ‹€');
51
+ }
52
+ return response.json();
53
+ })
54
+ .then(data => {
55
+ if (data.server_url) {
56
+ console.log(`μž₯치 μ„œλ²„ URL 섀정됨: ${data.server_url}`);
57
+ DEVICE_SERVER_URL = data.server_url;
58
+ } else {
59
+ // μ„€μ • μ—†μŒ - μžλ™ 생성 (ν˜„μž¬ 호슀트 + 포트 5050)
60
+ const currentHost = window.location.hostname;
61
+ const protocol = window.location.protocol;
62
+ DEVICE_SERVER_URL = `${protocol}//${currentHost}:5050`;
63
+ console.log(`μž₯치 μ„œλ²„ URL μžλ™ μ„€μ •: ${DEVICE_SERVER_URL}`);
64
+ }
65
+ })
66
+ .catch(error => {
67
+ console.error('μž₯치 μ„œλ²„ μ„€μ • μ΄ˆκΈ°ν™” 였λ₯˜:', error);
68
+ // κΈ°λ³Έκ°’μœΌλ‘œ μ„€μ • (ν˜„μž¬ 호슀트 + 포트 5050)
69
+ const currentHost = window.location.hostname;
70
+ const protocol = window.location.protocol;
71
+ DEVICE_SERVER_URL = `${protocol}//${currentHost}:5050`;
72
+ console.log(`μž₯치 μ„œλ²„ URL κΈ°λ³Έκ°’ μ„€μ •: ${DEVICE_SERVER_URL}`);
73
+ });
74
+ }
75
+
76
  // νŽ˜μ΄μ§€ λ‘œλ“œ μ‹œ μ΄ˆκΈ°ν™”
77
  document.addEventListener('DOMContentLoaded', () => {
78
  console.log("μž₯치 관리 λͺ¨λ“ˆ μ΄ˆκΈ°ν™”");
79
 
80
+ // μž₯치 μ„œλ²„ μ„€μ • μ΄ˆκΈ°ν™”
81
+ initDeviceServerSettings();
82
+
83
+ // νƒ­ μ „ν™˜ 이벀트 λ¦¬μŠ€λ„ˆλŠ” 이미 app.jsμ—μ„œ λ“±λ‘λ˜μ–΄ μžˆμœΌλ―€λ‘œ μ—¬κΈ°μ„œλŠ” λ“±λ‘ν•˜μ§€ μ•ŠμŒ
84
+ // λŒ€μ‹  μ „μ—­ ν•¨μˆ˜κ°€ μ˜¬λ°”λ₯΄κ²Œ μ„€μ •λ˜μ–΄ μžˆλŠ”μ§€ 확인
85
+ if (typeof window.switchTab !== 'function') {
86
+ console.log("window.switchTab ν•¨μˆ˜κ°€ μ •μ˜λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€. λ‚΄λΆ€ κ΅¬ν˜„μ„ μ‚¬μš©ν•©λ‹ˆλ‹€.");
87
+ } else {
88
+ console.log("window.switchTab ν•¨μˆ˜ μ‚¬μš© κ°€λŠ₯ν•©λ‹ˆλ‹€.");
89
+ }
90
 
91
  // μž₯치 μƒνƒœ 확인 λ²„νŠΌ 이벀트 λ¦¬μŠ€λ„ˆ
92
  checkDeviceStatusButton.addEventListener('click', () => {
 
107
  });
108
  });
109
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  /**
111
  * μ—λŸ¬ 처리 헬퍼 ν•¨μˆ˜
112
  * @param {Error} error - λ°œμƒν•œ 였λ₯˜
 
142
  }
143
 
144
  /**
145
+ * μž₯치 관리 μ„œλ²„ μƒνƒœ 확인 ν•¨μˆ˜ - μ „μ—­ ν•¨μˆ˜λ‘œ export
146
  */
147
  async function checkDeviceStatus() {
148
  console.log("μž₯치 μƒνƒœ 확인 쀑...");
 
156
  const controller = new AbortController();
157
  const timeoutId = setTimeout(() => controller.abort(), 5000); // 5초 νƒ€μž„μ•„μ›ƒ
158
 
159
+ // API μš”μ²­ - μˆ˜μ •λœ URL ꡬ성 μ‚¬μš©
160
+ const response = await fetch(getDeviceApiUrl('/api/status'), {
161
  signal: controller.signal
162
  });
163
 
 
175
  deviceStatusLoading.classList.add('hidden');
176
  deviceStatusResult.classList.remove('hidden');
177
 
178
+ if (data.status === "online") {
179
  // 온라인 μƒνƒœμΈ 경우
180
  statusIcon.innerHTML = '<i class="fas fa-circle online"></i>';
181
+ statusText.textContent = `μ„œλ²„ μƒνƒœ: ${data.status || '정상'}`;
182
 
183
  // μžλ™μœΌλ‘œ μž₯치 λͺ©λ‘ λ‘œλ“œ
184
  loadDevices();
 
215
  const controller = new AbortController();
216
  const timeoutId = setTimeout(() => controller.abort(), 5000); // 5초 νƒ€μž„μ•„μ›ƒ
217
 
218
+ // API μš”μ²­ - μˆ˜μ •λœ URL ꡬ성 μ‚¬μš©
219
+ const response = await fetch(getDeviceApiUrl('/api/devices'), {
220
  signal: controller.signal
221
  });
222
 
 
233
  // UI μ—…λ°μ΄νŠΈ
234
  devicesLoading.classList.add('hidden');
235
 
236
+ if (data.devices && data.devices.length > 0) {
237
+ // μž₯치 λͺ©λ‘ ν‘œμ‹œ
238
  data.devices.forEach(device => {
239
+ const deviceElement = createDeviceItem(device);
240
+ deviceList.appendChild(deviceElement);
241
  });
242
  } else {
243
+ // μž₯치 μ—†μŒ λ©”μ‹œμ§€ ν‘œμ‹œ
244
  noDevicesMessage.classList.remove('hidden');
 
 
 
 
245
  }
246
  } catch (error) {
247
  console.error("μž₯치 λͺ©λ‘ λ‘œλ“œ 였λ₯˜:", error);
 
249
  // UI μ—…λ°μ΄νŠΈ
250
  devicesLoading.classList.add('hidden');
251
  noDevicesMessage.classList.remove('hidden');
252
+ noDevicesMessage.textContent = handleError(error);
253
  }
254
  }
255
 
 
346
  const controller = new AbortController();
347
  const timeoutId = setTimeout(() => controller.abort(), 5000); // 5초 νƒ€μž„μ•„μ›ƒ
348
 
349
+ // API μš”μ²­ - μˆ˜μ •λœ URL ꡬ성 μ‚¬μš©
350
+ const response = await fetch(getDeviceApiUrl('/api/programs'), {
351
  signal: controller.signal
352
  });
353
 
 
364
  // UI μ—…λ°μ΄νŠΈ
365
  programsLoading.classList.add('hidden');
366
 
367
+ if (data.programs && data.programs.length > 0) {
368
+ // ν”„λ‘œκ·Έλž¨ λͺ©λ‘ ν‘œμ‹œ
369
  data.programs.forEach(program => {
370
+ const programElement = createProgramItem(program);
371
+ programsList.appendChild(programElement);
372
  });
 
373
  } else {
374
+ // ν”„λ‘œκ·Έλž¨ μ—†μŒ λ©”μ‹œμ§€ ν‘œμ‹œ
375
  noProgramsMessage.classList.remove('hidden');
 
 
 
 
376
  }
377
  } catch (error) {
378
  console.error("ν”„λ‘œκ·Έλž¨ λͺ©λ‘ λ‘œλ“œ 였λ₯˜:", error);
 
380
  // UI μ—…λ°μ΄νŠΈ
381
  programsLoading.classList.add('hidden');
382
  noProgramsMessage.classList.remove('hidden');
383
+ noProgramsMessage.textContent = handleError(error);
384
  }
385
  }
386
 
 
428
 
429
  /**
430
  * ν”„λ‘œκ·Έλž¨ μ‹€ν–‰ ν•¨μˆ˜
431
+ * @param {string} programId - μ‹€ν–‰ν•  ν”„λ‘œκ·Έλž¨ ID
432
+ * @param {string} programName - ν”„λ‘œκ·Έλž¨ 이름 (μ•Œλ¦Όμš©)
433
  */
434
  async function executeProgram(programId, programName) {
435
+ if (!programId) return;
436
+
437
+ console.log(`ν”„λ‘œκ·Έλž¨ μ‹€ν–‰ μš”μ²­: ${programId} (${programName})`);
438
 
439
  // μ‹€ν–‰ 확인
440
  if (!confirm(`'${programName}' ν”„λ‘œκ·Έλž¨μ„ μ‹€ν–‰ν•˜μ‹œκ² μŠ΅λ‹ˆκΉŒ?`)) {
441
+ console.log('ν”„λ‘œκ·Έλž¨ μ‹€ν–‰ μ·¨μ†Œλ¨');
442
  return;
443
  }
444
 
445
  try {
446
+ // λ‘œλ”© μ•Œλ¦Ό ν‘œμ‹œ
447
+ showNotification(`'${programName}' μ‹€ν–‰ 쀑...`, 'info');
 
448
 
449
+ // API μš”μ²­ - μˆ˜μ •λœ URL ꡬ성 μ‚¬μš©
450
+ const response = await fetch(getDeviceApiUrl(`/api/programs/${programId}/execute`), {
451
  method: 'POST',
452
  headers: {
453
  'Content-Type': 'application/json'
454
  },
455
+ body: JSON.stringify({}) // ν•„μš” μ‹œ μΆ”κ°€ νŒŒλΌλ―Έν„° 전달
 
456
  });
457
 
 
 
458
  // 응닡 처리
459
  if (!response.ok) {
460
+ throw new Error(`HTTP 였λ₯˜: ${response.status}`);
 
461
  }
462
 
463
  const data = await response.json();
464
+ console.log(`ν”„λ‘œκ·Έλž¨ μ‹€ν–‰ 응닡:`, data);
465
 
466
+ // 결과 처리
467
  if (data.success) {
468
+ showNotification(`'${programName}' μ‹€ν–‰ 성곡: ${data.message}`, 'success');
469
  } else {
470
+ showNotification(`'${programName}' μ‹€ν–‰ μ‹€νŒ¨: ${data.message || 'μ•Œ 수 μ—†λŠ” 였λ₯˜'}`, 'error');
471
  }
472
  } catch (error) {
473
+ console.error(`ν”„λ‘œκ·Έλž¨ μ‹€ν–‰ 였λ₯˜ (${programId}):`, error);
474
+ showNotification(`'${programName}' μ‹€ν–‰ 였λ₯˜: ${handleError(error)}`, 'error');
475
  }
476
  }
477
 
 
513
  }, 5000); // 5초 ν›„ 사라짐
514
  }
515
 
516
+ // checkDeviceStatus ν•¨μˆ˜λ₯Ό μ „μ—­μœΌλ‘œ λ…ΈμΆœ
517
+ window.checkDeviceStatus = checkDeviceStatus;
app/static/js/app.js CHANGED
@@ -174,14 +174,29 @@ document.addEventListener('DOMContentLoaded', () => {
174
 
175
  // νƒ­ μ „ν™˜ 이벀트 λ¦¬μŠ€λ„ˆ
176
  chatTab.addEventListener('click', () => {
 
177
  switchTab('chat');
178
  });
179
 
180
  docsTab.addEventListener('click', () => {
 
181
  switchTab('docs');
182
  loadDocuments();
183
  });
184
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
  // LLM 선택 이벀트 λ¦¬μŠ€λ„ˆ
186
  llmSelect.addEventListener('change', (event) => {
187
  changeLLM(event.target.value);
@@ -231,6 +246,8 @@ document.addEventListener('DOMContentLoaded', () => {
231
  * @param {string} tabName - ν™œμ„±ν™”ν•  νƒ­ 이름 ('chat', 'docs', 'device')
232
  */
233
  function switchTab(tabName) {
 
 
234
  if (tabName === 'chat') {
235
  chatTab.classList.add('active');
236
  docsTab.classList.remove('active');
@@ -252,6 +269,7 @@ function switchTab(tabName) {
252
  chatSection.classList.remove('active');
253
  docsSection.classList.remove('active');
254
  deviceSection.classList.add('active');
 
255
  }
256
  }
257
 
 
174
 
175
  // νƒ­ μ „ν™˜ 이벀트 λ¦¬μŠ€λ„ˆ
176
  chatTab.addEventListener('click', () => {
177
+ console.log("λŒ€ν™” νƒ­ 클릭");
178
  switchTab('chat');
179
  });
180
 
181
  docsTab.addEventListener('click', () => {
182
+ console.log("λ¬Έμ„œκ΄€λ¦¬ νƒ­ 클릭");
183
  switchTab('docs');
184
  loadDocuments();
185
  });
186
 
187
+ // μž₯μΉ˜κ΄€λ¦¬ νƒ­ 이벀트 λ¦¬μŠ€λ„ˆ μΆ”κ°€
188
+ deviceTab.addEventListener('click', () => {
189
+ console.log("μž₯μΉ˜κ΄€λ¦¬ νƒ­ 클릭");
190
+ switchTab('device');
191
+ // μž₯μΉ˜κ΄€λ¦¬ νƒ­μœΌλ‘œ μ „ν™˜ μ‹œ ν•„μš”ν•œ μ΄ˆκΈ°ν™” μž‘μ—…μ΄ μžˆλ‹€λ©΄ μ—¬κΈ°μ„œ 호좜
192
+ if (typeof checkDeviceStatus === 'function') {
193
+ console.log("μž₯치 μƒνƒœ 확인 ν•¨μˆ˜ 호좜");
194
+ checkDeviceStatus();
195
+ } else {
196
+ console.log("checkDeviceStatus ν•¨μˆ˜κ°€ μ •μ˜λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€");
197
+ }
198
+ });
199
+
200
  // LLM 선택 이벀트 λ¦¬μŠ€λ„ˆ
201
  llmSelect.addEventListener('change', (event) => {
202
  changeLLM(event.target.value);
 
246
  * @param {string} tabName - ν™œμ„±ν™”ν•  νƒ­ 이름 ('chat', 'docs', 'device')
247
  */
248
  function switchTab(tabName) {
249
+ console.log(`switchTab ν•¨μˆ˜ 호좜: ${tabName}`);
250
+
251
  if (tabName === 'chat') {
252
  chatTab.classList.add('active');
253
  docsTab.classList.remove('active');
 
269
  chatSection.classList.remove('active');
270
  docsSection.classList.remove('active');
271
  deviceSection.classList.add('active');
272
+ console.log("μž₯μΉ˜κ΄€λ¦¬ νƒ­μœΌλ‘œ μ „ν™˜ μ™„λ£Œ");
273
  }
274
  }
275