Files
TK-FB-Project/web-ui/js/modules/calendar/CalendarAPI.js
Hyungi Ahn d810a8b339 refactor(web-ui): 전체 UI 반응형 디자인 개선
모든 화면 크기에서 일관되고 안정적인 사용자 경험을 제공하도록
UI 컴포넌트를 전면 개선했습니다.

주요 변경사항:
- 네비게이션 바: flex-wrap, rem 단위, sticky positioning 적용
- 사용자 정보 영역: max-width로 크기 제한, 텍스트 overflow 처리
- 공통 헤더: clamp()로 반응형 폰트, 반응형 패딩 적용
- 모든 관리 페이지: ES6 모듈 로딩 통일 (type="module")
- 반응형 breakpoint: 1200px, 768px, 640px, 480px

개선 효과:
 모든 페이지에서 일관된 헤더 표시
 사용자 정보 영역 늘어나는 문제 해결
 모든 화면 크기에서 최적화된 레이아웃
 rem 단위 사용으로 접근성 개선

수정된 파일:
- web-ui/components/navbar.html: 전면 리팩토링
- web-ui/css/common.css: 반응형 스타일 추가
- web-ui/pages/**/*.html: 모듈 로딩 및 버전 업데이트 (13개 파일)
- web-ui/js/*.js: 모듈 시스템 개선

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-19 08:54:44 +09:00

153 lines
5.3 KiB
JavaScript

// web-ui/js/modules/calendar/CalendarAPI.js
/**
* 캘린더와 관련된 모든 API 호출을 관리하는 전역 객체입니다.
*/
(function(window) {
'use strict';
const CalendarAPI = {};
/**
* 활성화된 모든 작업자 목록을 가져옵니다.
* @returns {Promise<Array>} 작업자 객체 배열
*/
CalendarAPI.getWorkers = async function() {
try {
// api-helper.js 에 정의된 전역 apiGet 함수를 사용합니다.
const response = await window.apiGet('/workers');
if (response.success && Array.isArray(response.data)) {
// 활성화된 작업자만 필터링
const activeWorkers = response.data.filter(worker =>
worker.status === 'active' || worker.is_active === 1 || worker.is_active === true
);
return activeWorkers;
}
console.warn('API 응답 형식이 올바르지 않거나 데이터가 없습니다:', response);
return [];
} catch (error) {
console.error('작업자 데이터 로딩 중 API 오류 발생:', error);
// 에러를 다시 던져서 호출부에서 처리할 수 있도록 함
throw new Error('작업자 데이터를 불러오는 데 실패했습니다.');
}
};
/**
* 월별 작업 데이터 로드 (집계 테이블 사용으로 최적화)
* @param {number} year
* @param {number} month (0-indexed)
* @returns {Promise<object>}
*/
CalendarAPI.getMonthlyCalendarData = async function(year, month) {
const monthKey = `${year}-${String(month + 1).padStart(2, '0')}`;
try {
const response = await window.apiGet(`/monthly-status/calendar?year=${year}&month=${month + 1}`);
if (response.success) {
return response.data;
} else {
throw new Error(response.message || '집계 데이터 조회 실패');
}
} catch (error) {
console.error(`${monthKey} 집계 데이터 로딩 오류:`, error);
console.log(`📋 폴백: ${monthKey} 기존 방식 로딩 시작...`);
return await _getMonthlyWorkDataFallback(year, month);
}
};
/**
* 일일 상세 데이터 조회 (모달용)
* @param {string} dateStr (YYYY-MM-DD)
* @returns {Promise<object>}
*/
CalendarAPI.getDailyDetails = async function(dateStr) {
try {
const response = await window.apiGet(`/monthly-status/daily-details?date=${dateStr}`);
if (response.success) {
return response.data;
}
// Fallback to old API if new one fails
const fallbackResponse = await window.apiGet(`/daily-work-reports?date=${dateStr}&view_all=true`);
return {
workers: fallbackResponse.data, // Assuming structure is different
summary: {} // No summary in fallback
};
} catch (error) {
console.error('일일 작업 데이터 로딩 오류:', error);
throw new Error('해당 날짜의 작업 데이터를 불러오는 데 실패했습니다.');
}
};
/**
* 특정 작업자의 하루치 작업을 모두 삭제합니다.
* @param {number} workerId
* @param {string} date (YYYY-MM-DD)
* @returns {Promise<object>}
*/
CalendarAPI.deleteWorkerDayWork = async function(workerId, date) {
return await window.apiDelete(`/daily-work-reports/date/${date}/worker/${workerId}`);
};
/**
* 폴백: 순차적 로딩 (지연 시간 포함) - Private helper
* @param {number} year
* @param {number} month (0-indexed)
* @returns {Promise<object>}
*/
async function _getMonthlyWorkDataFallback(year, month) {
const monthKey = `${year}-${String(month + 1).padStart(2, '0')}`;
const monthData = {};
try {
const firstDay = new Date(year, month, 1);
const lastDay = new Date(year, month + 1, 0);
const currentDay = new Date(firstDay);
console.log(`📅 폴백: ${monthKey} 순차 로딩 시작 (rate limit 방지)`);
// 순차적으로 요청하되 작은 배치로 나눔 (5개씩)
const BATCH_SIZE = 5;
const DELAY_BETWEEN_BATCHES = 100; // 100ms
let day = 1;
while (currentDay <= lastDay) {
const batch = [];
// 배치 생성
for (let i = 0; i < BATCH_SIZE && currentDay <= lastDay; i++) {
const dateStr = currentDay.toISOString().split('T')[0];
batch.push({
date: dateStr,
promise: window.apiGet(`/daily-work-reports?date=${dateStr}&view_all=true`)
});
currentDay.setDate(currentDay.getDate() + 1);
}
// 배치 실행
const results = await Promise.all(batch.map(b => b.promise));
// 결과 저장
batch.forEach((item, index) => {
const result = results[index];
monthData[item.date] = result.success && Array.isArray(result.data) ? result.data : [];
});
// 다음 배치 전 잠시 대기 (rate limit 방지)
if (currentDay <= lastDay) {
await new Promise(resolve => setTimeout(resolve, DELAY_BETWEEN_BATCHES));
}
}
console.log(`✅ 폴백: ${monthKey} 순차 로딩 완료`);
return monthData;
} catch (error) {
console.error(`${monthKey} 순차 로딩 오류:`, error);
throw new Error('작업 데이터를 불러오는 데 실패했습니다.');
}
}
// 전역 스코프에 CalendarAPI 객체 할당
window.CalendarAPI = CalendarAPI;
})(window);