- 실시간 작업장 현황을 지도로 시각화 - 작업장 관리 페이지에서 정의한 구역 정보 활용 - TBM 작업자 및 방문자 현황 표시 주요 변경사항: - dashboard.html: 작업장 현황 섹션 추가 (기존 작업 현황 테이블 제거) - workplace-status.js: 지도 렌더링 및 데이터 통합 로직 구현 - modern-dashboard.js: 삭제된 DOM 요소 조건부 체크 추가 시각화 방식: - 인원 없음: 회색 테두리 + 작업장 이름 - 내부 작업자: 파란색 영역 + 인원 수 - 외부 방문자: 보라색 영역 + 인원 수 - 둘 다: 초록색 영역 + 총 인원 수 기술 구현: - Canvas API 기반 사각형 영역 렌더링 - map-regions API를 통한 데이터 일관성 보장 - 클릭 이벤트로 상세 정보 모달 표시 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
88 lines
3.4 KiB
JavaScript
88 lines
3.4 KiB
JavaScript
/**
|
|
* vacation_balance_details 테이블 생성 및 데이터 마이그레이션
|
|
* - 작업자별, 휴가 유형별, 연도별 휴가 잔액 관리
|
|
* - 기존 worker_vacation_balance 데이터 이관
|
|
*/
|
|
|
|
exports.up = async function(knex) {
|
|
// vacation_balance_details 테이블 생성
|
|
await knex.schema.createTable('vacation_balance_details', (table) => {
|
|
table.increments('id').primary();
|
|
table.integer('worker_id').notNullable().comment('작업자 ID');
|
|
table.integer('vacation_type_id').unsigned().notNullable().comment('휴가 유형 ID');
|
|
table.integer('year').notNullable().comment('연도');
|
|
table.decimal('total_days', 4, 1).defaultTo(0).comment('총 발생 일수');
|
|
table.decimal('used_days', 4, 1).defaultTo(0).comment('사용 일수');
|
|
table.text('notes').nullable().comment('비고');
|
|
table.integer('created_by').notNullable().comment('생성자 ID');
|
|
table.timestamp('created_at').defaultTo(knex.fn.now());
|
|
table.timestamp('updated_at').defaultTo(knex.fn.now());
|
|
|
|
// 인덱스
|
|
table.unique(['worker_id', 'vacation_type_id', 'year'], 'unique_worker_vacation_year');
|
|
table.index(['worker_id', 'year'], 'idx_worker_year');
|
|
table.index('vacation_type_id', 'idx_vacation_type');
|
|
|
|
// 외래키
|
|
table.foreign('worker_id').references('worker_id').inTable('workers').onDelete('CASCADE');
|
|
table.foreign('vacation_type_id').references('id').inTable('vacation_types').onDelete('RESTRICT');
|
|
table.foreign('created_by').references('user_id').inTable('users');
|
|
});
|
|
|
|
// remaining_days를 generated column으로 추가 (Raw SQL)
|
|
await knex.raw(`
|
|
ALTER TABLE vacation_balance_details
|
|
ADD COLUMN remaining_days DECIMAL(4,1)
|
|
GENERATED ALWAYS AS (total_days - used_days) STORED
|
|
COMMENT '잔여 일수'
|
|
`);
|
|
|
|
console.log('✅ vacation_balance_details 테이블 생성 완료');
|
|
|
|
// 기존 worker_vacation_balance 데이터를 vacation_balance_details로 마이그레이션
|
|
const existingBalances = await knex('worker_vacation_balance').select('*');
|
|
|
|
if (existingBalances.length > 0) {
|
|
// ANNUAL 휴가 유형 ID 조회
|
|
const annualType = await knex('vacation_types')
|
|
.where('type_code', 'ANNUAL')
|
|
.first();
|
|
|
|
if (!annualType) {
|
|
throw new Error('ANNUAL 휴가 유형을 찾을 수 없습니다');
|
|
}
|
|
|
|
// 관리자 사용자 ID 조회 (created_by 용)
|
|
// role_id 1 = System Admin, 2 = Admin
|
|
const adminUser = await knex('users')
|
|
.whereIn('role_id', [1, 2])
|
|
.first();
|
|
|
|
const createdById = adminUser ? adminUser.user_id : 1;
|
|
|
|
// 데이터 변환 및 삽입
|
|
const balanceDetails = existingBalances.map(balance => ({
|
|
worker_id: balance.worker_id,
|
|
vacation_type_id: annualType.id,
|
|
year: balance.year,
|
|
total_days: balance.total_annual_leave || 0,
|
|
used_days: balance.used_annual_leave || 0,
|
|
notes: 'Migrated from worker_vacation_balance',
|
|
created_by: createdById,
|
|
created_at: balance.created_at,
|
|
updated_at: balance.updated_at
|
|
}));
|
|
|
|
await knex('vacation_balance_details').insert(balanceDetails);
|
|
|
|
console.log(`✅ ${balanceDetails.length}건의 기존 휴가 데이터 마이그레이션 완료`);
|
|
}
|
|
};
|
|
|
|
exports.down = async function(knex) {
|
|
// vacation_balance_details 테이블 삭제
|
|
await knex.schema.dropTableIfExists('vacation_balance_details');
|
|
|
|
console.log('✅ vacation_balance_details 테이블 롤백 완료');
|
|
};
|