Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .jules/palette.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,6 @@
## 2024-07-13 - 일괄 μ—…λ‘œλ“œ 폼에 프리셋 λ²„νŠΌ 및 파일 크기 미리보기 μΆ”κ°€
**Learning:** 일괄 파일 μ—…λ‘œλ“œ νΌμ—μ„œ λŒ€μƒ λ°”μ΄νŠΈ(target_bytes) μž…λ ₯ ν•„λ“œλ§Œ μ œκ³΅ν•˜λ©΄ μ‚¬μš©μžκ°€ μ›ν•˜λŠ” μš©λŸ‰μ„ λ°”μ΄νŠΈ λ‹¨μœ„λ‘œ μ •ν™•νžˆ κ³„μ‚°ν•˜κΈ° μ–΄λ €μ›Œ μ‚¬μš©μ„±μ΄ λ–¨μ–΄μ§‘λ‹ˆλ‹€. μ‚¬μš©μžκ°€ μ—¬λŸ¬ νŒŒμΌμ„ μ—…λ‘œλ“œν•  λ•Œ 총 파일 크기λ₯Ό νŒŒμ•…ν•˜μ§€ λͺ»ν•΄ μ—…λ‘œλ“œ μ œν•œμ„ μ΄ˆκ³Όν•˜κ±°λ‚˜ 잘λͺ»λœ λŒ€μƒ λ°”μ΄νŠΈλ₯Ό μ„€μ •ν•  μœ„ν—˜μ΄ ν½λ‹ˆλ‹€.
**Action:** 일괄 파일 μ—…λ‘œλ“œ 폼에도 단일 파일 μ—…λ‘œλ“œ 폼과 λ™μΌν•˜κ²Œ λŒ€μƒ λ°”μ΄νŠΈλ₯Ό μ‰½κ²Œ 선택할 수 μžˆλŠ” λΉ λ₯Έ 프리셋 λ²„νŠΌμ„ μΆ”κ°€ν•˜κ³ , `onchange` 이벀트 λ°œμƒ μ‹œ μ„ νƒλœ λͺ¨λ“  파일의 크기λ₯Ό ν•©μ‚°ν•˜μ—¬ μ‚¬λžŒμ΄ 읽기 μ‰¬μš΄ λ‹¨μœ„(MiB, GiB λ“±)둜 미리보기λ₯Ό μ œκ³΅ν•˜λ„λ‘ JavaScript λ‘œμ§μ„ κ°œμ„ ν–ˆμŠ΅λ‹ˆλ‹€.
## 2025-02-28 - Dynamic File Size and Batch Limit Validation
**Learning:** When implementing client-side file size validation, do not hardcode the human-readable size limit; instead dynamically format the limit constant. For batch uploads, ensure client-side validation checks both the file count and total combined size against backend limits, providing immediate inline feedback with `setCustomValidity` and `aria-invalid`.
**Action:** Dynamically format limit constants in validation messages and validate aggregate metrics for batch uploads client-side.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
### Added
- 닀쀑 파일 μ—…λ‘œλ“œ 선택 μ‹œ 즉각적인 파일 개수 ν”Όλ“œλ°± 및 μ œν•œ 초과 κ²½κ³  λ©”μ‹œμ§€ μΆ”κ°€
- 일괄 μ—…λ‘œλ“œ 폼에 λŒ€μƒ λ°”μ΄νŠΈ 프리셋 λ²„νŠΌκ³Ό 총 파일 크기 미리보기λ₯Ό μΆ”κ°€ν•˜μ—¬ μ‚¬μš©μ„±μ„ κ°œμ„ ν–ˆμŠ΅λ‹ˆλ‹€.
* 🎨 Palette: [UX improvement] 배치 μ—…λ‘œλ“œ μ‹œ 전체 파일 크기 검증 μΆ”κ°€ 및 동적 μ—λŸ¬ λ©”μ‹œμ§€ ν¬λ§·νŒ… 적용
13 changes: 11 additions & 2 deletions saas_web.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,10 +229,11 @@ async def add_security_headers(request: Request, call_next):
return;
}
const text = formatBinaryBytes(file.size);
const limitText = formatBinaryBytes(MAX_UPLOAD_BYTES);
if (file.size > MAX_UPLOAD_BYTES) {
input.setCustomValidity('File exceeds 5 GiB limit.');
input.setCustomValidity('File exceeds ' + limitText + ' limit.');
input.setAttribute('aria-invalid', 'true');
preview.innerText = 'Selected file size: ' + text + ' (exceeds 5 GiB limit)';
preview.innerText = 'Selected file size: ' + text + ' (exceeds ' + limitText + ' limit)';
preview.style.color = '#dc3545';
return;
}
Expand Down Expand Up @@ -325,6 +326,14 @@ async def add_security_headers(request: Request, call_next):
preview.style.color = '#dc3545';
return;
}
const limitText = formatBinaryBytes(MAX_UPLOAD_BYTES);
if (totalSize > MAX_UPLOAD_BYTES) {
input.setCustomValidity('Total file size exceeds ' + limitText + ' limit.');
input.setAttribute('aria-invalid', 'true');
preview.innerText = 'Selected ' + files.length + ' file(s) (' + formatBinaryBytes(totalSize) + ', exceeds ' + limitText + ' limit)';
preview.style.color = '#dc3545';
return;
}
preview.innerText = 'Selected ' + files.length + ' file(s) (' + formatBinaryBytes(totalSize) + ')';
}

Expand Down
4 changes: 3 additions & 1 deletion tests/test_saas_web.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ def test_get_ui_includes_binary_file_size_validation(self):

self.assertIn("const MAX_UPLOAD_BYTES = 5 * 1024 * 1024 * 1024;", html)
self.assertIn("['B', 'KiB', 'MiB', 'GiB']", html)
self.assertIn("File exceeds 5 GiB limit.", html)
self.assertIn("const limitText = formatBinaryBytes(MAX_UPLOAD_BYTES);", html)
self.assertIn("input.setCustomValidity('File exceeds ' + limitText + ' limit.');", html)
self.assertIn("input.setCustomValidity('Total file size exceeds ' + limitText + ' limit.');", html)
self.assertIn("preview.style.color = '#0f6674';", html)
self.assertIn('onchange="updateFileSizePreview(this)"', html)

Expand Down
Loading