모든 화면 크기에서 일관되고 안정적인 사용자 경험을 제공하도록 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>
153 lines
5.3 KiB
JavaScript
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);
|