fix(tkqc): 관리함 데스크톱 인라인 카드에도 사진 보충 UI 추가

첫 커밋(178155d)에서는 createModalContent(완료된 이슈 상세 모달)에만
사진 보충 input을 추가했는데, 데스크톱 관리함은 상세 모달이 아니라
createInProgressRow 로 렌더링되는 인라인 카드(저장/삭제/완료처리 버튼이
카드에 직접 있는 형태) 이었음. 사용자 스크린샷으로 확인.

- createInProgressRow: "업로드 사진" 섹션 아래 file input 추가
- saveIssueChanges: 저장 시 빈 슬롯에만 photo/photo2~5 base64 업로드

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-04-09 13:46:46 +09:00
parent 178155df6b
commit 56f626911a
2 changed files with 54 additions and 12 deletions

View File

@@ -349,6 +349,6 @@
<script src="/static/js/utils/photo-modal.js?v=2026031401"></script>
<script src="/static/js/utils/toast.js?v=2026031401"></script>
<script src="/static/js/components/mobile-bottom-nav.js?v=2026031401"></script>
<script src="/static/js/pages/issues-management.js?v=2026040901"></script>
<script src="/static/js/pages/issues-management.js?v=2026040902"></script>
</body>
</html>

View File

@@ -427,25 +427,37 @@ function createInProgressRow(issue, project) {
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">업로드 사진</label>
${(() => {
const photos = [
const photoSlots = [
issue.photo_path,
issue.photo_path2,
issue.photo_path3,
issue.photo_path4,
issue.photo_path5
].filter(p => p);
];
const filled = photoSlots.filter(p => p);
const emptyCount = 5 - filled.length;
if (photos.length === 0) {
return '<div class="w-20 h-20 bg-gray-100 rounded-lg flex items-center justify-center text-gray-400 text-xs border-2 border-dashed border-gray-300">사진 없음</div>';
}
const existing = filled.length === 0
? '<div class="w-20 h-20 bg-gray-100 rounded-lg flex items-center justify-center text-gray-400 text-xs border-2 border-dashed border-gray-300">사진 없음</div>'
: `<div class="flex flex-wrap gap-2">
${filled.map((path, idx) => `
<img src="${path}" class="w-20 h-20 object-cover rounded-lg cursor-pointer border-2 border-gray-200 hover:border-blue-400 transition-colors" onclick="openPhotoModal('${path}')" alt="업로드 사진 ${idx + 1}">
`).join('')}
</div>`;
return `
<div class="flex flex-wrap gap-2">
${photos.map((path, idx) => `
<img src="${path}" class="w-20 h-20 object-cover rounded-lg cursor-pointer border-2 border-gray-200 hover:border-blue-400 transition-colors" onclick="openPhotoModal('${path}')" alt="업로드 사진 ${idx + 1}">
`).join('')}
// 사진 보충 UI (빈 슬롯에만 채움, 저장 버튼 클릭 시 업로드)
const uploadUi = isPendingCompletion ? '' : (emptyCount > 0 ? `
<div class="mt-3 pt-3 border-t border-gray-200">
<label class="block text-xs font-medium text-gray-600 mb-1">
<i class="fas fa-plus-circle text-blue-500 mr-1"></i>사진 보충 (남은 슬롯: ${emptyCount}장)
</label>
<input type="file" id="original_photo_${issue.id}" accept="image/*" multiple
class="block w-full text-xs text-gray-500 file:mr-3 file:py-1.5 file:px-3 file:rounded-md file:border-0 file:text-xs file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100">
<p class="text-xs text-gray-400 mt-1">※ 비어있는 슬롯에만 자동으로 채워집니다 (기존 사진은 유지). 저장 버튼 클릭 시 업로드.</p>
</div>
`;
` : '<p class="text-xs text-gray-400 mt-2"><i class="fas fa-info-circle mr-1"></i>5장 모두 찬 상태입니다</p>');
return existing + uploadUi;
})()}
</div>
</div>
@@ -909,6 +921,36 @@ async function saveIssueChanges(issueId) {
}
}
// 원본 사진 보충 — 빈 슬롯에만 자동 채움 (기존 사진 유지)
const originalPhotoInput = document.getElementById(`original_photo_${issueId}`);
if (originalPhotoInput && originalPhotoInput.files && originalPhotoInput.files.length > 0) {
const currentIssue = issues.find(i => i.id === issueId);
if (currentIssue) {
const slotKeys = ['photo_path', 'photo_path2', 'photo_path3', 'photo_path4', 'photo_path5'];
const emptySlots = [];
slotKeys.forEach((key, idx) => {
if (!currentIssue[key]) emptySlots.push(idx + 1);
});
if (emptySlots.length === 0) {
alert('원본 사진 슬롯이 가득 찼습니다. 보충할 수 없습니다.');
return;
}
const files = Array.from(originalPhotoInput.files).slice(0, emptySlots.length);
if (originalPhotoInput.files.length > emptySlots.length) {
alert(`빈 슬롯은 ${emptySlots.length}개인데 ${originalPhotoInput.files.length}개를 선택하셨습니다. 처음 ${emptySlots.length}장만 업로드됩니다.`);
}
for (let i = 0; i < files.length; i++) {
const slotNum = emptySlots[i];
const base64 = await fileToBase64(files[i]);
const fieldName = slotNum === 1 ? 'photo' : `photo${slotNum}`;
updates[fieldName] = base64;
}
}
}
// API 호출
const response = await fetch(`/api/issues/${issueId}/management`, {
method: 'PUT',