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:
@@ -349,6 +349,6 @@
|
|||||||
<script src="/static/js/utils/photo-modal.js?v=2026031401"></script>
|
<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/utils/toast.js?v=2026031401"></script>
|
||||||
<script src="/static/js/components/mobile-bottom-nav.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>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -427,25 +427,37 @@ function createInProgressRow(issue, project) {
|
|||||||
<div>
|
<div>
|
||||||
<label class="block text-sm font-medium text-gray-700 mb-2">업로드 사진</label>
|
<label class="block text-sm font-medium text-gray-700 mb-2">업로드 사진</label>
|
||||||
${(() => {
|
${(() => {
|
||||||
const photos = [
|
const photoSlots = [
|
||||||
issue.photo_path,
|
issue.photo_path,
|
||||||
issue.photo_path2,
|
issue.photo_path2,
|
||||||
issue.photo_path3,
|
issue.photo_path3,
|
||||||
issue.photo_path4,
|
issue.photo_path4,
|
||||||
issue.photo_path5
|
issue.photo_path5
|
||||||
].filter(p => p);
|
];
|
||||||
|
const filled = photoSlots.filter(p => p);
|
||||||
|
const emptyCount = 5 - filled.length;
|
||||||
|
|
||||||
if (photos.length === 0) {
|
const existing = filled.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>';
|
? '<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) => `
|
||||||
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}">
|
<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('')}
|
`).join('')}
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
// 사진 보충 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>
|
</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>
|
||||||
</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 호출
|
// API 호출
|
||||||
const response = await fetch(`/api/issues/${issueId}/management`, {
|
const response = await fetch(`/api/issues/${issueId}/management`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
|
|||||||
Reference in New Issue
Block a user