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
4 changes: 4 additions & 0 deletions .jules/palette.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2024-08-10 - λ“œλ‘­μ‘΄ 클릭 이벀트 지원
**Learning:** 파일 μž…λ ₯ λ²„νŠΌ λŒ€μ‹  넓은 λ“œλ‘­μ‘΄ μ˜μ—­μ„ 클릭해도 파일 선택 창이 열리도둝 ν•˜λ©΄ λͺ¨λ°”일 ν™˜κ²½μ΄λ‚˜ 마우슀 μ‚¬μš©μž λͺ¨λ‘μ—κ²Œ 더 넓은 νƒ€κ²Ÿ μ˜μ—­μ„ μ œκ³΅ν•΄ μ ‘κ·Όμ„± 및 μ‚¬μš©μ„±μ΄ ν–₯μƒλ©λ‹ˆλ‹€. λ˜ν•œ λ‚΄λΆ€ μš”μ†Œ 클릭은 λ¬΄μ‹œν•˜λ„λ‘ ν•˜μ—¬ 폼 μΈν„°λž™μ…˜ μΆ©λŒμ„ λ§‰λŠ” 것이 μ€‘μš”ν•©λ‹ˆλ‹€.
**Action:** 폼의 루트 μ»¨ν…Œμ΄λ„ˆ(λ“œλ‘­μ‘΄)에 클릭 이벀트 λ¦¬μŠ€λ„ˆλ₯Ό μΆ”κ°€ν•˜μ—¬ λ‚΄λΆ€μ˜ hidden input을 `.click()`으둜 νŠΈλ¦¬κ±°ν•˜λ˜, λŒ€ν™”ν˜• μžμ‹ μš”μ†Œ(input, button, label λ“±)λ₯Ό λͺ…μ‹œμ μœΌλ‘œ μ œμ™Έν•˜λŠ” 쑰건을 μž‘μ„±ν•˜μ‹­μ‹œμ˜€.

## 2024-07-15 - Dynamic Size formatting and Total Size Validation
**Learning:** Hardcoding human-readable sizes (like '5 GiB') in validation error messages is error-prone when the underlying constant changes. Moreover, failing to validate total upload size against backend limits (e.g., MAX_UPLOAD_BYTES) in batch file uploads frustrates users who wait for a large upload to finish only to get a server-side 413 Payload Too Large error.
**Action:** Always format backend byte limit constants dynamically (e.g., `formatBinaryBytes(MAX_UPLOAD_BYTES)`) on the client side to display accurate error messages. For multiple file inputs, ensure both the file count and the combined file size are validated against backend limits, giving immediate inline feedback via `setCustomValidity` and `aria-invalid`.
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@

### Fixed
- 단일·일괄 λŒ€μƒ 크기 μž…λ ₯을 비웠을 λ•Œ 이전 custom validity와 `aria-invalid` μƒνƒœλ₯Ό μ¦‰μ‹œ μ΄ˆκΈ°ν™”ν•΄ ν˜„μž¬ ν•„μˆ˜ μž…λ ₯ μƒνƒœλ₯Ό μ •ν™•νžˆ μ „λ‹¬ν•©λ‹ˆλ‹€.
- λ“œλ‘­μ‘΄ μ˜μ—­μ„ ν΄λ¦­ν•˜μ—¬ 파일 탐색기λ₯Ό μ—΄ 수 μžˆλ„λ‘ μ ‘κ·Όμ„± 및 νŽΈμ˜μ„± κ°œμ„ 
22 changes: 22 additions & 0 deletions plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
1. **λ“œλ‘­μ‘΄ 클릭 μ‹œ 파일 탐색기 μ—΄κΈ° κΈ°λŠ₯ μΆ”κ°€ (`saas_web.py`)**
- μ‚¬μš©μž νŽΈμ˜μ„± ν–₯상을 μœ„ν•΄ 전체 λ“œλ‘­μ‘΄ μ˜μ—­(`dropZone`, `batchDropZone`)을 ν΄λ¦­ν•˜λ©΄ ν•΄λ‹Ήν•˜λŠ” 파일 μž…λ ₯(input type="file") ν•„λ“œλ₯Ό ν”„λ‘œκ·Έλž˜λ° λ°©μ‹μœΌλ‘œ ν΄λ¦­ν•˜μ—¬ 파일 탐색기가 열리도둝 κ°œμ„ ν•©λ‹ˆλ‹€.
- 폼 λ‚΄λΆ€μ˜ μž…λ ₯ μš”μ†Œ(INPUT), λ²„νŠΌ(BUTTON), 라벨(LABEL)을 클릭할 λ•ŒλŠ” μ΄λ²€νŠΈκ°€ μ „νŒŒλ˜μ–΄ 파일 탐색기가 열리지 μ•Šλ„λ‘ `['INPUT', 'BUTTON', 'LABEL'].includes(e.target.tagName)` 쑰건을 μΆ”κ°€ν•˜μ—¬ λ°©μ–΄ν•©λ‹ˆλ‹€.
- `saas_web.py` λ‚΄μ˜ HTML `script` μ˜μ—­μ— `replace_with_git_merge_diff`λ₯Ό μ‚¬μš©ν•˜μ—¬ μ½”λ“œλ₯Ό μ μš©ν•©λ‹ˆλ‹€.

2. **UI ν…ŒμŠ€νŠΈ μ—…λ°μ΄νŠΈ (`tests/test_saas_web.py`)**
- μƒˆλ‘­κ²Œ μΆ”κ°€λœ κΈ°λŠ₯에 λŒ€ν•΄ `test_get_ui_includes_binary_file_size_validation` ν…ŒμŠ€νŠΈ 내에 `assertIn` 단언문을 μΆ”κ°€ν•©λ‹ˆλ‹€.
- `assertIn("dropZone.addEventListener('click'", html)`
- `replace_with_git_merge_diff`λ₯Ό μ‚¬μš©ν•˜μ—¬ `tests/test_saas_web.py` μ½”λ“œλ₯Ό μ—…λ°μ΄νŠΈν•©λ‹ˆλ‹€.

3. **CHANGELOG 및 Palette 저널 기둝**
- `CHANGELOG.md`의 끝에 λ³€κ²½ 사항을 `cat << 'EOF' >> CHANGELOG.md` λͺ…λ Ήμ–΄λ‘œ μΆ”κ°€ν•©λ‹ˆλ‹€.
- `.jules/palette.md` μ΅œμƒλ‹¨μ— μƒˆλ‘œμš΄ 배움(ν•™μŠ΅)κ³Ό 쑰치λ₯Ό μΆ”κ°€ν•©λ‹ˆλ‹€. (λ“œλ‘­μ‘΄ 전체 μ˜μ—­μ„ 파일 μ—…λ‘œλ“œ νƒ€κ²ŸμœΌλ‘œ λ§Œλ“€μ–΄ μ‚¬μš©μž κ²½ν—˜ ν–₯상). `bash`의 μž„μ‹œ νŒŒμΌμ„ μ‚¬μš©ν•˜μ—¬ 맨 μ•žμ— μΆ”κ°€ν•©λ‹ˆλ‹€.

4. **100% ν…ŒμŠ€νŠΈ 컀버리지 확인**
- `python3 -m pip install coverage && python3 -m coverage run -m unittest discover -s tests && python3 -m coverage report -m` λͺ…령을 μ‹€ν–‰ν•˜μ—¬ λͺ¨λ“  ν…ŒμŠ€νŠΈκ°€ μ„±κ³΅ν•˜κ³  컀버리지가 100%인지 ν™•μΈν•©λ‹ˆλ‹€.

5. **사전 컀밋 단계 μ™„λ£Œ**
- Complete pre-commit steps to ensure proper testing, verification, review, and reflection are done.

6. **제좜(Submit)**
- "🎨 Palette: λ“œλ‘­μ‘΄ ν΄λ¦­ν•˜μ—¬ 파일 선택 κΈ°λŠ₯ μΆ”κ°€" ν˜•μ‹μ˜ PR 제λͺ©κ³Ό μ μ ˆν•œ μ„€λͺ…을 ν¬ν•¨ν•˜μ—¬ 컀밋 및 μ œμΆœν•©λ‹ˆλ‹€. PR μ„€λͺ…μ—λŠ” 무엇을, μ™œ, 이전/이후(변경사항), 접근성에 λŒ€ν•œ μ„€λͺ…이 ν¬ν•¨λ˜μ–΄μ•Ό ν•©λ‹ˆλ‹€.
12 changes: 12 additions & 0 deletions saas_web.py
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,18 @@ async def add_security_headers(request: Request, call_next):
}
}, false);
}

dropZone.addEventListener('click', (e) => {
if (['INPUT', 'BUTTON', 'LABEL'].includes(e.target.tagName)) return;
fileInput.click();
});

if (batchDropZone) {
batchDropZone.addEventListener('click', (e) => {
if (['INPUT', 'BUTTON', 'LABEL'].includes(e.target.tagName)) return;
batchFileInput.click();
});
}
</script>
</div>
<div class="box" id="batch-drop-zone" style="margin-top: 20px;">
Expand Down
2 changes: 2 additions & 0 deletions tests/test_saas_web.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ def test_get_ui_includes_binary_file_size_validation(self):
self.assertIn("Total file size exceeds ' + limitText + ' limit.", html)
self.assertIn("preview.style.color = '#0f6674';", html)
self.assertIn('onchange="updateFileSizePreview(this)"', html)
self.assertIn("dropZone.addEventListener('click'", html)
self.assertIn("if (['INPUT', 'BUTTON', 'LABEL'].includes(e.target.tagName)) return;", html)

def test_security_headers_present_without_plain_http_hsts(self):
response = client.get("/")
Expand Down
Loading