feat: 권한 탭 분리 + 부서 인원 표시 + 다수 시스템 개선
- tkuser: 권한 관리를 별도 탭으로 분리, 부서 클릭 시 소속 인원 목록 표시 - system1: 모바일 UI 개선, nginx 권한 보정, 신고 카테고리 타입 마이그레이션 - system2: 신고 상세/보고서 개선, 내 보고서 페이지 추가 - system3: 이슈 뷰/수신함/관리함 개선 - gateway: 포털 라우팅 수정 - user-management API: 부서별 권한 벌크 설정 추가 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* 마이그레이션: work_issue_reports에 category_type 컬럼 추가
|
||||
* - issue_report_categories ENUM에 'facility' 추가
|
||||
* - work_issue_reports에 category_type 직접 저장 (유형 이관 기능 지원)
|
||||
* - 기존 데이터 백필
|
||||
*/
|
||||
|
||||
exports.up = async function(knex) {
|
||||
// 1) issue_report_categories ENUM에 'facility' 추가
|
||||
await knex.raw(`
|
||||
ALTER TABLE issue_report_categories
|
||||
MODIFY COLUMN category_type ENUM('nonconformity', 'safety', 'facility') NOT NULL
|
||||
`);
|
||||
|
||||
// 2) work_issue_reports에 category_type 컬럼 추가
|
||||
await knex.raw(`
|
||||
ALTER TABLE work_issue_reports
|
||||
ADD COLUMN category_type ENUM('nonconformity', 'safety', 'facility') NULL AFTER issue_item_id
|
||||
`);
|
||||
|
||||
// 3) 기존 데이터 백필: issue_report_categories에서 category_type 복사
|
||||
await knex.raw(`
|
||||
UPDATE work_issue_reports wir
|
||||
INNER JOIN issue_report_categories irc ON wir.issue_category_id = irc.category_id
|
||||
SET wir.category_type = irc.category_type
|
||||
`);
|
||||
|
||||
// 4) NOT NULL로 변경
|
||||
await knex.raw(`
|
||||
ALTER TABLE work_issue_reports
|
||||
MODIFY COLUMN category_type ENUM('nonconformity', 'safety', 'facility') NOT NULL
|
||||
`);
|
||||
|
||||
// 5) 인덱스 추가
|
||||
await knex.raw(`
|
||||
ALTER TABLE work_issue_reports ADD INDEX idx_wir_category_type (category_type)
|
||||
`);
|
||||
|
||||
console.log('work_issue_reports에 category_type 컬럼 추가 완료');
|
||||
};
|
||||
|
||||
exports.down = async function(knex) {
|
||||
await knex.raw(`ALTER TABLE work_issue_reports DROP INDEX idx_wir_category_type`);
|
||||
await knex.raw(`ALTER TABLE work_issue_reports DROP COLUMN category_type`);
|
||||
await knex.raw(`
|
||||
ALTER TABLE issue_report_categories
|
||||
MODIFY COLUMN category_type ENUM('nonconformity', 'safety') NOT NULL
|
||||
`);
|
||||
|
||||
console.log('work_issue_reports에서 category_type 컬럼 제거 완료');
|
||||
};
|
||||
@@ -3,6 +3,9 @@ FROM nginx:alpine
|
||||
# 정적 파일 복사
|
||||
COPY . /usr/share/nginx/html/
|
||||
|
||||
# 디렉토리 권한 보정 (macOS에서 복사 시 700이 되는 문제 방지)
|
||||
RUN find /usr/share/nginx/html -type d -exec chmod 755 {} +
|
||||
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
@@ -120,18 +120,28 @@ body {
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
padding: 0 var(--space-4);
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
flex-shrink: 1;
|
||||
}
|
||||
|
||||
.header-left .brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-4);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.brand-logo {
|
||||
@@ -634,25 +644,41 @@ body {
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.dashboard-header {
|
||||
padding: var(--space-2) var(--space-3);
|
||||
height: 64px;
|
||||
padding: 0 0.5rem;
|
||||
height: 52px;
|
||||
}
|
||||
|
||||
body {
|
||||
padding-top: 64px;
|
||||
padding-top: 52px;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
padding: 0 var(--space-2);
|
||||
padding: 0;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.header-left {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.header-left .brand {
|
||||
gap: 0.5rem;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.brand-logo {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.brand-title {
|
||||
font-size: var(--text-base);
|
||||
font-size: 0.8125rem;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.brand-subtitle {
|
||||
@@ -663,13 +689,19 @@ body {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.header-right {
|
||||
gap: 0.25rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.dashboard-btn .btn-text,
|
||||
@@ -679,48 +711,52 @@ body {
|
||||
|
||||
.dashboard-btn,
|
||||
.report-btn {
|
||||
padding: var(--space-2);
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
padding: 0;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
justify-content: center;
|
||||
border: none;
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
|
||||
.dashboard-btn .btn-icon,
|
||||
.report-btn .btn-icon {
|
||||
margin: 0;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.notification-btn {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.notification-dropdown {
|
||||
position: fixed;
|
||||
top: 64px;
|
||||
left: var(--space-3);
|
||||
right: var(--space-3);
|
||||
top: 52px;
|
||||
left: 0.5rem;
|
||||
right: 0.5rem;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.header-right {
|
||||
gap: var(--space-2);
|
||||
}
|
||||
|
||||
.mobile-menu-btn {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin-right: var(--space-2);
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin-right: 0.25rem;
|
||||
font-size: 1.125rem;
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
border: none;
|
||||
}
|
||||
|
||||
.user-profile {
|
||||
padding: var(--space-1) var(--space-2);
|
||||
padding: 0.125rem 0.25rem;
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.profile-menu {
|
||||
position: fixed;
|
||||
top: 64px;
|
||||
right: var(--space-3);
|
||||
top: 52px;
|
||||
right: 0.5rem;
|
||||
left: auto;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
@@ -66,8 +66,11 @@
|
||||
<span class="nav-arrow">▾</span>
|
||||
</button>
|
||||
<div class="nav-category-items">
|
||||
<a href="#" class="nav-item cross-system-link" data-system="report" data-path="/pages/safety/report-status.html" data-page-key="safety.report_status">
|
||||
<span class="nav-text">안전신고 현황</span>
|
||||
<a href="#" class="nav-item cross-system-link" data-system="report" data-path="/pages/safety/my-reports.html" data-page-key="safety.my_reports">
|
||||
<span class="nav-text">내 신고 현황</span>
|
||||
</a>
|
||||
<a href="#" class="nav-item cross-system-link admin-only" data-system="report" data-path="/pages/safety/report-status.html" data-page-key="safety.report_status">
|
||||
<span class="nav-text">전체 신고 현황</span>
|
||||
</a>
|
||||
<a href="/pages/safety/visit-request.html" class="nav-item" data-page-key="safety.visit_request">
|
||||
<span class="nav-text">출입 신청</span>
|
||||
|
||||
@@ -3,48 +3,79 @@
|
||||
대시보드, TBM, 작업보고서, 출근 관리 페이지 최적화
|
||||
===================================================== */
|
||||
|
||||
/* ========== 모바일 헤더 간소화 ========== */
|
||||
/* ========== 모바일 헤더 (navbar.html 스타일과 동기화) ========== */
|
||||
@media (max-width: 768px) {
|
||||
.dashboard-header {
|
||||
height: 56px !important;
|
||||
padding: 0 0.75rem !important;
|
||||
height: 52px !important;
|
||||
padding: 0 0.5rem !important;
|
||||
}
|
||||
|
||||
body {
|
||||
padding-top: 56px !important;
|
||||
padding-top: 52px !important;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
padding: 0 !important;
|
||||
flex-wrap: nowrap !important;
|
||||
}
|
||||
|
||||
.header-left {
|
||||
flex: 1 !important;
|
||||
min-width: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
.brand-logo {
|
||||
width: 32px !important;
|
||||
height: 32px !important;
|
||||
width: 30px !important;
|
||||
height: 30px !important;
|
||||
}
|
||||
|
||||
.brand-text {
|
||||
.brand-subtitle {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.brand-title {
|
||||
font-size: 0.8125rem !important;
|
||||
white-space: nowrap !important;
|
||||
overflow: hidden !important;
|
||||
text-overflow: ellipsis !important;
|
||||
}
|
||||
|
||||
.header-right {
|
||||
gap: 0.5rem !important;
|
||||
gap: 0.25rem !important;
|
||||
flex-shrink: 0 !important;
|
||||
}
|
||||
|
||||
/* 버튼 아이콘만 표시 (숨기지 않음) */
|
||||
.dashboard-btn .btn-text,
|
||||
.report-btn .btn-text {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.dashboard-btn,
|
||||
.report-btn {
|
||||
display: none !important;
|
||||
padding: 0 !important;
|
||||
width: 32px !important;
|
||||
height: 32px !important;
|
||||
justify-content: center !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.notification-btn {
|
||||
width: 36px !important;
|
||||
height: 36px !important;
|
||||
width: 32px !important;
|
||||
height: 32px !important;
|
||||
}
|
||||
|
||||
.user-profile {
|
||||
padding: 0.25rem 0.5rem !important;
|
||||
padding: 0.125rem 0.25rem !important;
|
||||
background: transparent !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 32px !important;
|
||||
height: 32px !important;
|
||||
font-size: 0.875rem !important;
|
||||
width: 30px !important;
|
||||
height: 30px !important;
|
||||
font-size: 0.8rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1049,17 +1049,15 @@
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.dashboard-main {
|
||||
padding: 1rem;
|
||||
padding: 0.75rem;
|
||||
margin-left: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
|
||||
/* 헤더는 항상 가로 배치 유지 (navbar.html에서 관리) */
|
||||
.header-content {
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.header-center,
|
||||
.header-right {
|
||||
order: 3;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.grid-cols-4,
|
||||
|
||||
@@ -29,8 +29,8 @@ server {
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
# 업로드 파일 프록시
|
||||
location /uploads/ {
|
||||
# 업로드 파일 프록시 (^~ 로 regex location보다 우선 매칭)
|
||||
location ^~ /uploads/ {
|
||||
proxy_pass http://system1-api:3005/uploads/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
|
||||
Reference in New Issue
Block a user