- 토큰 저장 키 통일 (access_token으로 일관성 확보) - 일일공수 페이지 API 스크립트 로딩 순서 수정 - 프로젝트 관리 페이지 비활성 프로젝트 표시 문제 해결 - 업로드 카테고리에 '기타' 항목 추가 (백엔드 schemas.py 포함) - 비밀번호 변경 기능 API 연동으로 수정 - 프로젝트 드롭다운 z-index 문제 해결 - CORS 설정 및 Nginx 구성 개선 - 비밀번호 해싱 방식 pbkdf2_sha256으로 변경 (bcrypt 72바이트 제한 해결)
324 lines
12 KiB
HTML
324 lines
12 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="ko">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
|
<title>모바일 프로젝트 문제 해결</title>
|
|
<style>
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
padding: 20px;
|
|
margin: 0;
|
|
background: #f5f5f5;
|
|
}
|
|
.container {
|
|
max-width: 100%;
|
|
background: white;
|
|
padding: 20px;
|
|
border-radius: 10px;
|
|
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
|
}
|
|
h1 {
|
|
color: #333;
|
|
font-size: 24px;
|
|
margin-bottom: 20px;
|
|
}
|
|
.section {
|
|
margin-bottom: 30px;
|
|
padding: 15px;
|
|
background: #f9f9f9;
|
|
border-radius: 8px;
|
|
border: 1px solid #e0e0e0;
|
|
}
|
|
.status {
|
|
padding: 10px;
|
|
margin: 10px 0;
|
|
border-radius: 5px;
|
|
font-size: 14px;
|
|
}
|
|
.success { background: #d4edda; color: #155724; }
|
|
.error { background: #f8d7da; color: #721c24; }
|
|
.info { background: #d1ecf1; color: #0c5460; }
|
|
.warning { background: #fff3cd; color: #856404; }
|
|
button {
|
|
background: #3b82f6;
|
|
color: white;
|
|
border: none;
|
|
padding: 12px 24px;
|
|
border-radius: 6px;
|
|
font-size: 16px;
|
|
margin: 5px;
|
|
cursor: pointer;
|
|
-webkit-tap-highlight-color: transparent;
|
|
}
|
|
button:active {
|
|
background: #2563eb;
|
|
}
|
|
select {
|
|
width: 100%;
|
|
padding: 12px;
|
|
font-size: 16px;
|
|
border: 2px solid #3b82f6;
|
|
border-radius: 8px;
|
|
background: white;
|
|
-webkit-appearance: none;
|
|
-moz-appearance: none;
|
|
appearance: none;
|
|
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
|
|
background-repeat: no-repeat;
|
|
background-position: right 10px center;
|
|
background-size: 20px;
|
|
}
|
|
pre {
|
|
background: #f4f4f4;
|
|
padding: 10px;
|
|
border-radius: 5px;
|
|
overflow-x: auto;
|
|
font-size: 12px;
|
|
}
|
|
.test-select {
|
|
margin: 20px 0;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<h1>🔧 모바일 프로젝트 문제 해결</h1>
|
|
|
|
<div class="section">
|
|
<h2>📱 디바이스 정보</h2>
|
|
<div id="deviceInfo"></div>
|
|
</div>
|
|
|
|
<div class="section">
|
|
<h2>💾 localStorage 상태</h2>
|
|
<div id="storageStatus"></div>
|
|
<button onclick="checkStorage()">localStorage 확인</button>
|
|
<button onclick="fixProjects()">프로젝트 복구</button>
|
|
</div>
|
|
|
|
<div class="section">
|
|
<h2>🧪 드롭다운 테스트</h2>
|
|
<div class="test-select">
|
|
<label>테스트 드롭다운:</label>
|
|
<select id="testSelect">
|
|
<option value="">선택하세요</option>
|
|
</select>
|
|
</div>
|
|
<button onclick="testDropdown()">드롭다운 테스트</button>
|
|
</div>
|
|
|
|
<div class="section">
|
|
<h2>📊 실제 프로젝트 드롭다운</h2>
|
|
<div class="test-select">
|
|
<label>프로젝트 선택:</label>
|
|
<select id="projectSelect">
|
|
<option value="">프로젝트를 선택하세요</option>
|
|
</select>
|
|
</div>
|
|
<button onclick="loadProjects()">프로젝트 로드</button>
|
|
</div>
|
|
|
|
<div class="section">
|
|
<h2>🔍 디버그 로그</h2>
|
|
<pre id="debugLog"></pre>
|
|
<button onclick="clearLog()">로그 지우기</button>
|
|
</div>
|
|
|
|
<div style="margin-top: 30px;">
|
|
<button onclick="location.href='index.html'">메인으로</button>
|
|
<button onclick="location.reload()">새로고침</button>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
let logContent = '';
|
|
|
|
function log(message) {
|
|
const time = new Date().toLocaleTimeString('ko-KR');
|
|
logContent += `[${time}] ${message}\n`;
|
|
document.getElementById('debugLog').textContent = logContent;
|
|
}
|
|
|
|
function clearLog() {
|
|
logContent = '';
|
|
document.getElementById('debugLog').textContent = '';
|
|
}
|
|
|
|
// 디바이스 정보
|
|
function showDeviceInfo() {
|
|
const info = `
|
|
<div class="status info">
|
|
<strong>화면 크기:</strong> ${window.innerWidth} x ${window.innerHeight}<br>
|
|
<strong>User Agent:</strong> ${navigator.userAgent}<br>
|
|
<strong>플랫폼:</strong> ${navigator.platform}<br>
|
|
<strong>모바일 여부:</strong> ${window.innerWidth <= 768 ? '예' : '아니오'}
|
|
</div>
|
|
`;
|
|
document.getElementById('deviceInfo').innerHTML = info;
|
|
log('디바이스 정보 표시 완료');
|
|
}
|
|
|
|
// localStorage 확인
|
|
function checkStorage() {
|
|
log('localStorage 확인 시작');
|
|
const statusDiv = document.getElementById('storageStatus');
|
|
|
|
try {
|
|
// 프로젝트 데이터 확인
|
|
const projectData = localStorage.getItem('work-report-projects');
|
|
if (projectData) {
|
|
const projects = JSON.parse(projectData);
|
|
statusDiv.innerHTML = `
|
|
<div class="status success">
|
|
✅ 프로젝트 데이터 있음: ${projects.length}개
|
|
</div>
|
|
<pre>${JSON.stringify(projects, null, 2)}</pre>
|
|
`;
|
|
log(`프로젝트 ${projects.length}개 발견`);
|
|
} else {
|
|
statusDiv.innerHTML = `
|
|
<div class="status warning">
|
|
⚠️ 프로젝트 데이터 없음
|
|
</div>
|
|
`;
|
|
log('프로젝트 데이터 없음');
|
|
}
|
|
|
|
// 사용자 데이터 확인
|
|
const userData = localStorage.getItem('currentUser');
|
|
if (userData) {
|
|
const user = JSON.parse(userData);
|
|
statusDiv.innerHTML += `
|
|
<div class="status success">
|
|
✅ 사용자: ${user.username} (${user.role})
|
|
</div>
|
|
`;
|
|
log(`사용자: ${user.username}`);
|
|
}
|
|
|
|
} catch (error) {
|
|
statusDiv.innerHTML = `
|
|
<div class="status error">
|
|
❌ 에러: ${error.message}
|
|
</div>
|
|
`;
|
|
log(`에러: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
// 프로젝트 복구
|
|
function fixProjects() {
|
|
log('프로젝트 복구 시작');
|
|
|
|
const projects = [
|
|
{
|
|
id: 1,
|
|
jobNo: 'TKR-25009R',
|
|
projectName: 'M Project',
|
|
isActive: true,
|
|
createdAt: new Date().toISOString(),
|
|
createdByName: '관리자'
|
|
},
|
|
{
|
|
id: 2,
|
|
jobNo: 'TKG-24011P',
|
|
projectName: 'TKG Project',
|
|
isActive: true,
|
|
createdAt: new Date().toISOString(),
|
|
createdByName: '관리자'
|
|
}
|
|
];
|
|
|
|
try {
|
|
localStorage.setItem('work-report-projects', JSON.stringify(projects));
|
|
log('프로젝트 데이터 저장 완료');
|
|
|
|
alert('프로젝트가 복구되었습니다!');
|
|
checkStorage();
|
|
loadProjects();
|
|
|
|
} catch (error) {
|
|
log(`복구 실패: ${error.message}`);
|
|
alert('복구 실패: ' + error.message);
|
|
}
|
|
}
|
|
|
|
// 드롭다운 테스트
|
|
function testDropdown() {
|
|
log('드롭다운 테스트 시작');
|
|
const select = document.getElementById('testSelect');
|
|
|
|
// 옵션 추가
|
|
select.innerHTML = '<option value="">선택하세요</option>';
|
|
for (let i = 1; i <= 5; i++) {
|
|
const option = document.createElement('option');
|
|
option.value = i;
|
|
option.textContent = `테스트 옵션 ${i}`;
|
|
select.appendChild(option);
|
|
}
|
|
|
|
log(`테스트 옵션 ${select.options.length - 1}개 추가됨`);
|
|
|
|
// 이벤트 리스너
|
|
select.onchange = function() {
|
|
log(`선택됨: ${this.value} - ${this.options[this.selectedIndex].text}`);
|
|
};
|
|
}
|
|
|
|
// 프로젝트 로드
|
|
function loadProjects() {
|
|
log('프로젝트 로드 시작');
|
|
const select = document.getElementById('projectSelect');
|
|
|
|
try {
|
|
const saved = localStorage.getItem('work-report-projects');
|
|
if (!saved) {
|
|
log('localStorage에 프로젝트 없음');
|
|
alert('프로젝트 데이터가 없습니다. "프로젝트 복구" 버튼을 눌러주세요.');
|
|
return;
|
|
}
|
|
|
|
const projects = JSON.parse(saved);
|
|
const activeProjects = projects.filter(p => p.isActive);
|
|
|
|
log(`활성 프로젝트 ${activeProjects.length}개 발견`);
|
|
|
|
// 드롭다운 초기화
|
|
select.innerHTML = '<option value="">프로젝트를 선택하세요</option>';
|
|
|
|
// 프로젝트 옵션 추가
|
|
activeProjects.forEach(project => {
|
|
const option = document.createElement('option');
|
|
option.value = project.id;
|
|
option.textContent = `${project.jobNo} - ${project.projectName}`;
|
|
select.appendChild(option);
|
|
log(`옵션 추가: ${project.jobNo} - ${project.projectName}`);
|
|
});
|
|
|
|
log(`드롭다운에 ${select.options.length - 1}개 프로젝트 표시됨`);
|
|
|
|
// 이벤트 리스너
|
|
select.onchange = function() {
|
|
log(`프로젝트 선택됨: ${this.value}`);
|
|
};
|
|
|
|
} catch (error) {
|
|
log(`프로젝트 로드 에러: ${error.message}`);
|
|
alert('프로젝트 로드 실패: ' + error.message);
|
|
}
|
|
}
|
|
|
|
// 페이지 로드 시 실행
|
|
window.onload = function() {
|
|
log('페이지 로드 완료');
|
|
showDeviceInfo();
|
|
checkStorage();
|
|
testDropdown();
|
|
loadProjects();
|
|
};
|
|
</script>
|
|
</body>
|
|
</html>
|
|
|