feat: TBM 모바일 시스템 + 작업 분할/이동 + 권한 통합

TBM 시스템:
- 4단계 워크플로우 (draft→세부편집→완료→작업보고)
- 모바일 전용 TBM 페이지 (tbm-mobile.html) + 3단계 생성 위자드
- 작업자 작업 분할 (work_hours + split_seq)
- 작업자 이동 보내기/빼오기 (tbm_transfers 테이블)
- 생성 시 중복 배정 방지 (당일 배정 현황 조회)
- 데스크탑 TBM 페이지 세부편집 기능 추가

작업보고서:
- 모바일 전용 작업보고서 페이지 (report-create-mobile.html)
- TBM에서 사전 등록된 work_hours 자동 반영

권한 시스템:
- tkuser user_page_permissions 테이블과 system1 페이지 접근 연동
- pageAccessRoutes를 userRoutes보다 먼저 등록 (라우트 우선순위 수정)
- TKUSER_DEFAULT_ACCESS 폴백 추가 (개인→부서→기본값 3단계)
- 권한 캐시키 갱신 (userPageAccess_v2)

기타:
- app-init.js 캐시 버스팅 (v=5)
- iOS Safari touch-action: manipulation 적용
- KST 타임존 날짜 버그 수정 (toISOString UTC 이슈)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-02-25 07:46:21 +09:00
parent d36303101e
commit 7637be33f3
65 changed files with 9470 additions and 240 deletions

View File

@@ -218,16 +218,36 @@
width: 100%;
}
/* 지도 영역 */
/* 작업장 카드: 엣지투엣지 */
.workplace-status-section .card {
border-radius: 0;
margin-left: -0.75rem;
margin-right: -0.75rem;
border-left: none;
border-right: none;
}
.workplace-status-section .card-body {
padding: 0 !important;
}
#workplaceMapContainer {
min-height: 300px !important;
min-height: auto !important;
}
/* 캔버스: 높이 제한 해제, 풀와이드 */
#workplaceMapCanvas {
max-height: 350px;
max-height: none;
border-radius: 0 !important;
border-left: none !important;
border-right: none !important;
}
/* 범례 숨기기 또는 축소 */
.workplace-status-section .card-header {
padding: 0.75rem !important;
}
/* 범례 숨기기 */
#mapLegend {
display: none;
}
@@ -756,6 +776,264 @@
}
}
/* ========== 모바일 대시보드 (작업장 리스트 뷰) ========== */
@media (max-width: 768px) {
/* 모바일 뷰가 활성화되면 데스크톱 섹션 숨김 */
.mobile-dashboard-view[style*="block"] ~ .workplace-status-section,
.mobile-dashboard-view[style*="block"] ~ .moved-equipment-section,
.mobile-dashboard-view[style*="block"] ~ .dashboard-footer {
display: none !important;
}
.mobile-dashboard-view {
padding-bottom: 8px;
}
/* --- 날짜 헤더 --- */
.md-date-header {
display: flex;
justify-content: space-between;
align-items: baseline;
margin-bottom: 14px;
padding: 0 2px;
}
.md-date-label {
font-size: var(--text-lg, 18px);
font-weight: 700;
color: var(--text-primary, #1a202c);
}
.md-date-value {
font-size: var(--text-sm, 14px);
color: var(--text-tertiary, #718096);
font-weight: 400;
}
/* --- 카테고리 탭 --- */
.md-category-tabs {
display: flex;
gap: 8px;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
padding: 0 2px 12px;
margin-bottom: 4px;
scrollbar-width: none;
}
.md-category-tabs::-webkit-scrollbar {
display: none;
}
.md-cat-tab {
flex-shrink: 0;
padding: 7px 16px;
border-radius: var(--radius-full, 9999px);
font-size: var(--text-sm, 14px);
font-weight: 500;
white-space: nowrap;
border: 1px solid var(--border-light, #e2e8f0);
background: var(--bg-primary, #fff);
color: var(--text-secondary, #4a5568);
cursor: pointer;
transition: all 0.15s;
min-height: 36px;
-webkit-tap-highlight-color: transparent;
}
.md-cat-tab:active {
transform: scale(0.97);
}
.md-cat-tab.active {
background: var(--primary-500, #3b82f6);
color: #fff;
border-color: var(--primary-500, #3b82f6);
}
/* --- 작업장 리스트 --- */
.md-workplace-list {
display: flex;
flex-direction: column;
gap: 10px;
}
/* --- 작업장 카드 --- */
.md-wp-card {
background: var(--bg-primary, #fff);
border: 1px solid var(--border-light, #e2e8f0);
border-radius: var(--radius-lg, 12px);
padding: 14px 16px;
}
.md-wp-name {
font-size: 15px;
font-weight: 600;
color: var(--text-primary, #1a202c);
margin: 0 0 8px;
}
.md-wp-stats {
display: flex;
flex-direction: column;
gap: 4px;
}
.md-wp-stat-row {
display: flex;
align-items: center;
gap: 6px;
font-size: var(--text-sm, 14px);
color: var(--text-secondary, #4a5568);
line-height: 1.6;
}
.md-wp-stat-icon {
flex-shrink: 0;
width: 18px;
text-align: center;
font-size: 13px;
}
.md-wp-stat-text {
font-variant-numeric: tabular-nums;
}
.md-wp-stat--warning {
color: var(--status-warning-text, #ca8a04);
}
/* --- 빈 상태 --- */
.md-wp-no-activity {
margin: 0;
color: var(--text-tertiary, #718096);
font-size: var(--text-sm, 14px);
}
.md-wp-empty-all {
text-align: center;
padding: 32px 0;
color: var(--text-tertiary, #718096);
font-size: var(--text-sm, 14px);
}
/* --- 카드 확장 상태 --- */
.md-wp-card.expanded {
border-color: var(--primary-300, #93c5fd);
box-shadow: 0 2px 8px rgba(59,130,246,0.1);
}
.md-wp-header {
cursor: pointer;
-webkit-tap-highlight-color: transparent;
}
.md-wp-toggle {
float: right;
color: var(--text-tertiary, #718096);
font-size: 12px;
transition: transform 0.2s;
}
.md-wp-card.expanded .md-wp-toggle {
transform: rotate(180deg);
}
/* --- 상세 영역 --- */
.md-wp-detail {
display: none;
border-top: 1px solid var(--border-light, #e2e8f0);
margin-top: 10px;
padding-top: 10px;
}
.md-wp-card.expanded .md-wp-detail {
display: block;
}
.md-wp-detail-section {
margin-bottom: 10px;
}
.md-wp-detail-section:last-child {
margin-bottom: 0;
}
.md-wp-detail-title {
font-size: 12px;
font-weight: 600;
color: var(--text-tertiary, #718096);
letter-spacing: 0.03em;
margin-bottom: 6px;
}
.md-wp-detail-item {
padding: 6px 0;
border-bottom: 1px solid var(--gray-100, #f5f5f5);
font-size: 14px;
}
.md-wp-detail-item:last-child {
border-bottom: none;
}
.md-wp-detail-main {
font-weight: 500;
color: var(--text-primary, #1a202c);
}
.md-wp-detail-sub {
font-size: 13px;
color: var(--text-tertiary, #718096);
margin-top: 1px;
}
/* 신고 상태 배지 */
.md-wp-issue-status {
display: inline-block;
font-size: 11px;
padding: 1px 6px;
border-radius: 4px;
font-weight: 500;
}
.md-wp-issue-status--reported {
background: var(--status-warning-bg, #fef3c7);
color: var(--status-warning-text, #ca8a04);
}
.md-wp-issue-status--received {
background: var(--status-info-bg, #dbeafe);
color: var(--status-info-text, #2563eb);
}
.md-wp-issue-status--in_progress {
background: var(--status-error-bg, #fee2e2);
color: var(--status-error-text, #dc2626);
}
/* --- 로딩 스켈레톤 --- */
.md-skeleton {
height: 48px;
border-radius: var(--radius-md, 8px);
background: linear-gradient(90deg, var(--gray-100, #f5f5f5) 25%, var(--gray-200, #eee) 50%, var(--gray-100, #f5f5f5) 75%);
background-size: 200% 100%;
animation: md-shimmer 1.5s infinite;
}
@keyframes md-shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
}
/* 데스크톱에서는 모바일 대시보드 절대 표시 안 함 */
@media (min-width: 769px) {
.mobile-dashboard-view {
display: none !important;
}
}
/* ========== 다크모드 지원 (선택적) ========== */
@media (max-width: 768px) and (prefers-color-scheme: dark) {
/* 다크모드 색상 조정 필요시 여기에 추가 */