docs(13): create phase plan for File Upload Infrastructure
Phase 13: File Upload Infrastructure - 3 plan(s) in 2 wave(s) - 2 parallel (13-01, 13-02 in Wave 1), 1 sequential (13-03 in Wave 2) - Ready for execution Plan structure: - 13-01: TDD - Attachment validation service (validateFileSize, validateFileType, getAttachmentType) - 13-02: TDD - Redux upload state and REST helpers (uploadAttachment thunk, uploadMusicNotation) - 13-03: Attach button integration (JKChatAttachButton + JKChatComposer + human verification) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
15f0e45668
commit
451138bbb8
|
|
@ -236,9 +236,9 @@ Plans:
|
|||
9. User can continue using app while upload is in progress (non-blocking)
|
||||
|
||||
Plans:
|
||||
- [ ] 13-01: Attach button, file dialog, and client-side validation (type, size)
|
||||
- [ ] 13-02: S3 upload integration with progress tracking
|
||||
- [ ] 13-03: Error handling and success feedback
|
||||
- [ ] 13-01-PLAN.md — TDD: Attachment validation service (validateFileSize, validateFileType, getAttachmentType)
|
||||
- [ ] 13-02-PLAN.md — TDD: Redux upload state and REST helpers (uploadAttachment thunk, uploadMusicNotation)
|
||||
- [ ] 13-03-PLAN.md — Attach button integration (JKChatAttachButton + JKChatComposer integration)
|
||||
|
||||
#### Phase 14: Chat Integration & Display
|
||||
**Goal**: Attachments display as messages in chat window with metadata and clickable links
|
||||
|
|
|
|||
|
|
@ -0,0 +1,146 @@
|
|||
---
|
||||
phase: 13-file-upload-infrastructure
|
||||
plan: 01
|
||||
type: tdd
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- jam-ui/src/services/attachmentValidation.js
|
||||
- jam-ui/src/services/__tests__/attachmentValidation.test.js
|
||||
autonomous: true
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "validateFileSize rejects files over 10 MB with error message"
|
||||
- "validateFileType accepts only whitelisted extensions (.pdf, .xml, .mxl, .txt, .png, .jpg, .jpeg, .gif, .mp3, .wav)"
|
||||
- "getAttachmentType returns 'audio' for audio files and 'notation' for everything else"
|
||||
- "validateFile combines size and type validation with warnings for backend-unsupported types"
|
||||
- "formatFileSize displays human-readable sizes (KB, MB)"
|
||||
artifacts:
|
||||
- path: "jam-ui/src/services/attachmentValidation.js"
|
||||
provides: "File validation utilities"
|
||||
exports: ["validateFileSize", "validateFileType", "getAttachmentType", "validateFile", "formatFileSize", "MAX_FILE_SIZE", "ALLOWED_EXTENSIONS"]
|
||||
min_lines: 60
|
||||
- path: "jam-ui/src/services/__tests__/attachmentValidation.test.js"
|
||||
provides: "Unit tests for validation service"
|
||||
min_lines: 80
|
||||
key_links:
|
||||
- from: "jam-ui/src/services/attachmentValidation.js"
|
||||
to: "Phase 13 Plan 03"
|
||||
via: "Import in JKChatComposer for validation before upload"
|
||||
pattern: "validateFile"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Create file validation service with TDD methodology for client-side attachment validation
|
||||
|
||||
Purpose: Validate file size (10 MB limit) and type (extension whitelist) before upload attempt, preventing wasted uploads and providing immediate user feedback. This is a foundational service used by the upload flow in Plan 03.
|
||||
|
||||
Output: `attachmentValidation.js` service with exported validation functions and comprehensive test suite
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@/Users/nuwan/.claude/get-shit-done/workflows/execute-plan.md
|
||||
@/Users/nuwan/.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/STATE.md
|
||||
@.planning/phases/12-attachment-research-&-backend-validation/docs/REACT_INTEGRATION_DESIGN.md
|
||||
|
||||
# Validation requirements from research
|
||||
# - 10 MB max file size (matches legacy AttachmentStore)
|
||||
# - Extension whitelist: .pdf, .xml, .mxl, .txt, .png, .jpg, .jpeg, .gif, .mp3, .wav
|
||||
# - Attachment type: 'audio' for .mp3, .wav, .flac, .ogg, .aiff, .aifc, .au; 'notation' for others
|
||||
# - Backend-supported extensions differ slightly (no .mp3 yet) - validation should warn
|
||||
</context>
|
||||
|
||||
<feature>
|
||||
<name>Attachment Validation Service</name>
|
||||
<files>
|
||||
jam-ui/src/services/attachmentValidation.js
|
||||
jam-ui/src/services/__tests__/attachmentValidation.test.js
|
||||
</files>
|
||||
<behavior>
|
||||
## validateFileSize(file, maxSizeBytes?)
|
||||
- Input: File object, optional max size (default 10 MB)
|
||||
- Output: { valid: boolean, error: string|null }
|
||||
- Cases:
|
||||
- 5 MB file -> { valid: true, error: null }
|
||||
- 15 MB file -> { valid: false, error: "File exceeds 10 MB limit" }
|
||||
- Exactly 10 MB -> { valid: true, error: null }
|
||||
|
||||
## validateFileType(file, allowedTypes?)
|
||||
- Input: File object, optional allowed extensions array
|
||||
- Output: { valid: boolean, error: string|null }
|
||||
- Cases:
|
||||
- "document.pdf" -> { valid: true, error: null }
|
||||
- "music.mp3" -> { valid: true, error: null }
|
||||
- "virus.exe" -> { valid: false, error: "File type not allowed..." }
|
||||
- "DOCUMENT.PDF" (uppercase) -> { valid: true, error: null }
|
||||
|
||||
## getAttachmentType(filename)
|
||||
- Input: filename string
|
||||
- Output: 'audio' | 'notation'
|
||||
- Cases:
|
||||
- "song.mp3" -> 'audio'
|
||||
- "track.wav" -> 'audio'
|
||||
- "sheet.pdf" -> 'notation'
|
||||
- "image.png" -> 'notation'
|
||||
|
||||
## validateFile(file)
|
||||
- Input: File object
|
||||
- Output: { valid: boolean, error: string|null, warnings: string[] }
|
||||
- Combines size + type validation
|
||||
- Adds warning if extension not in backend-supported list (e.g., .mp3)
|
||||
|
||||
## formatFileSize(bytes)
|
||||
- Input: number (bytes)
|
||||
- Output: string
|
||||
- Cases:
|
||||
- 500 -> "500 B"
|
||||
- 2048 -> "2.0 KB"
|
||||
- 5242880 -> "5.0 MB"
|
||||
</behavior>
|
||||
<implementation>
|
||||
Follow REACT_INTEGRATION_DESIGN.md Section 7 "File Validation Service" exactly.
|
||||
|
||||
Constants to export:
|
||||
- MAX_FILE_SIZE = 10 * 1024 * 1024 (10 MB in bytes)
|
||||
- ALLOWED_EXTENSIONS = ['.pdf', '.xml', '.mxl', '.txt', '.png', '.jpg', '.jpeg', '.gif', '.mp3', '.wav']
|
||||
- BACKEND_SUPPORTED_EXTENSIONS = ['.pdf', '.xml', '.mxl', '.txt', '.png', '.jpg', '.jpeg', '.gif', '.wav', '.flac', '.ogg', '.aiff', '.aifc', '.au']
|
||||
- AUDIO_EXTENSIONS = ['.mp3', '.wav', '.flac', '.ogg', '.aiff', '.aifc', '.au']
|
||||
|
||||
TDD Cycle:
|
||||
1. RED: Write test for validateFileSize -> test fails
|
||||
2. GREEN: Implement validateFileSize -> test passes
|
||||
3. RED: Write test for validateFileType -> test fails
|
||||
4. GREEN: Implement validateFileType -> test passes
|
||||
5. (continue for each function)
|
||||
6. REFACTOR: Clean up if needed
|
||||
</implementation>
|
||||
</feature>
|
||||
|
||||
<verification>
|
||||
```bash
|
||||
# Run validation service tests
|
||||
cd /Users/nuwan/Code/jam-cloud/jam-ui && npm run test:unit -- attachmentValidation
|
||||
|
||||
# Expected: All tests pass (15+ test cases)
|
||||
# Coverage: 100% for attachmentValidation.js
|
||||
```
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
1. All 5 exported functions implemented and tested
|
||||
2. Test file has 15+ test cases covering edge cases
|
||||
3. All tests pass with `npm run test:unit -- attachmentValidation`
|
||||
4. No console errors or warnings
|
||||
5. File matches structure from REACT_INTEGRATION_DESIGN.md Section 7
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/13-file-upload-infrastructure/13-01-SUMMARY.md`
|
||||
</output>
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
---
|
||||
phase: 13-file-upload-infrastructure
|
||||
plan: 02
|
||||
type: tdd
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- jam-ui/src/store/features/sessionChatSlice.js
|
||||
- jam-ui/src/store/features/__tests__/sessionChatSlice.test.js
|
||||
- jam-ui/src/helpers/rest.js
|
||||
autonomous: true
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "uploadState tracks upload status (idle, uploading, error), progress, error message, and fileName"
|
||||
- "uploadAttachment thunk sends FormData to /api/music_notations with correct fields"
|
||||
- "Upload pending state sets status='uploading' and stores fileName"
|
||||
- "Upload success resets uploadState to idle"
|
||||
- "Upload failure sets status='error' with user-friendly error message"
|
||||
- "HTTP 413 error shows 'File too large' message"
|
||||
- "clearUploadError action resets error state"
|
||||
artifacts:
|
||||
- path: "jam-ui/src/store/features/sessionChatSlice.js"
|
||||
provides: "Upload state management and uploadAttachment thunk"
|
||||
exports: ["uploadAttachment", "setUploadStatus", "clearUploadError", "selectUploadStatus", "selectUploadError", "selectIsUploading"]
|
||||
- path: "jam-ui/src/helpers/rest.js"
|
||||
provides: "REST API helpers for file upload"
|
||||
exports: ["uploadMusicNotation", "getMusicNotationUrl"]
|
||||
- path: "jam-ui/src/store/features/__tests__/sessionChatSlice.test.js"
|
||||
provides: "Unit tests for upload reducers and thunk"
|
||||
key_links:
|
||||
- from: "jam-ui/src/store/features/sessionChatSlice.js"
|
||||
to: "jam-ui/src/helpers/rest.js"
|
||||
via: "uploadAttachment calls uploadMusicNotation"
|
||||
pattern: "uploadMusicNotation\\(formData\\)"
|
||||
- from: "uploadMusicNotation"
|
||||
to: "/api/music_notations"
|
||||
via: "native fetch with FormData (NOT apiFetch)"
|
||||
pattern: "fetch.*music_notations"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Extend sessionChatSlice with upload state management and create REST helpers for file upload
|
||||
|
||||
Purpose: Track upload progress in Redux and provide the API layer for uploading files to S3 via backend. Uses native fetch (NOT apiFetch) because FormData requires browser-generated Content-Type with multipart boundary.
|
||||
|
||||
Output: Extended sessionChatSlice with upload state/thunk and rest.js helpers for upload/download
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@/Users/nuwan/.claude/get-shit-done/workflows/execute-plan.md
|
||||
@/Users/nuwan/.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/STATE.md
|
||||
@.planning/phases/12-attachment-research-&-backend-validation/docs/REACT_INTEGRATION_DESIGN.md
|
||||
@jam-ui/src/store/features/sessionChatSlice.js
|
||||
@jam-ui/src/helpers/rest.js
|
||||
|
||||
# Critical pattern from research:
|
||||
# - Use native fetch() for FormData, NOT apiFetch
|
||||
# - Do NOT set Content-Type header - browser must set it with multipart boundary
|
||||
# - FormData fields: files[] (File), session_id (string), attachment_type ('notation'|'audio')
|
||||
</context>
|
||||
|
||||
<feature>
|
||||
<name>Redux Upload State and REST Helpers</name>
|
||||
<files>
|
||||
jam-ui/src/store/features/sessionChatSlice.js
|
||||
jam-ui/src/store/features/__tests__/sessionChatSlice.test.js
|
||||
jam-ui/src/helpers/rest.js
|
||||
</files>
|
||||
<behavior>
|
||||
## initialState.uploadState
|
||||
- status: 'idle' | 'uploading' | 'error'
|
||||
- progress: 0-100 (future enhancement, start at 0)
|
||||
- error: string | null
|
||||
- fileName: string | null
|
||||
|
||||
## Reducers
|
||||
- setUploadStatus({ status, progress?, error?, fileName? }) -> updates uploadState fields
|
||||
- clearUploadError() -> resets error to null, status to 'idle'
|
||||
|
||||
## uploadAttachment Thunk
|
||||
- Input: { file: File, sessionId: string, clientId?: string }
|
||||
- Builds FormData with files[], session_id, attachment_type
|
||||
- Calls uploadMusicNotation(formData)
|
||||
- On success: returns response data
|
||||
- On 413: rejects with "File too large - maximum 10 MB"
|
||||
- On 422: rejects with "Invalid file type or format"
|
||||
- On other error: rejects with error.message or "Upload failed"
|
||||
|
||||
## Selectors
|
||||
- selectUploadStatus(state) -> 'idle' | 'uploading' | 'error'
|
||||
- selectUploadError(state) -> string | null
|
||||
- selectUploadProgress(state) -> number
|
||||
- selectUploadFileName(state) -> string | null
|
||||
- selectIsUploading(state) -> boolean
|
||||
|
||||
## uploadMusicNotation(formData) in rest.js
|
||||
- Uses native fetch (NOT apiFetch)
|
||||
- POST to /api/music_notations
|
||||
- credentials: 'include' (session cookie)
|
||||
- NO Content-Type header (browser sets it)
|
||||
- Returns Promise<response.json()>
|
||||
- Throws error with status property on failure
|
||||
|
||||
## getMusicNotationUrl(id) in rest.js
|
||||
- Uses apiFetch (JSON response)
|
||||
- GET /music_notations/:id
|
||||
- Returns Promise<{ url: string }>
|
||||
</behavior>
|
||||
<implementation>
|
||||
Follow REACT_INTEGRATION_DESIGN.md Sections 3 and 4.
|
||||
|
||||
TDD Cycle:
|
||||
1. RED: Write test for uploadState initial state
|
||||
2. GREEN: Add uploadState to initialState
|
||||
3. RED: Write test for setUploadStatus reducer
|
||||
4. GREEN: Implement setUploadStatus
|
||||
5. RED: Write test for clearUploadError reducer
|
||||
6. GREEN: Implement clearUploadError
|
||||
7. RED: Write test for uploadAttachment.pending
|
||||
8. GREEN: Add extraReducer for pending
|
||||
9. (continue for fulfilled/rejected)
|
||||
10. Write REST helper tests (mock fetch)
|
||||
11. Implement REST helpers
|
||||
|
||||
CRITICAL: uploadMusicNotation must use native fetch, not apiFetch.
|
||||
Reason: apiFetch sets Content-Type: application/json, which breaks multipart FormData.
|
||||
Browser must auto-generate Content-Type with boundary parameter.
|
||||
</implementation>
|
||||
</feature>
|
||||
|
||||
<verification>
|
||||
```bash
|
||||
# Run sessionChatSlice tests
|
||||
cd /Users/nuwan/Code/jam-cloud/jam-ui && npm run test:unit -- sessionChatSlice
|
||||
|
||||
# Expected: All existing tests pass + 15+ new upload tests
|
||||
```
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
1. uploadState added to sessionChatSlice initial state
|
||||
2. setUploadStatus and clearUploadError reducers working
|
||||
3. uploadAttachment async thunk implemented with extraReducers
|
||||
4. 5 selectors exported for upload state
|
||||
5. uploadMusicNotation uses native fetch (verify no Content-Type header)
|
||||
6. getMusicNotationUrl uses apiFetch
|
||||
7. All existing sessionChatSlice tests still pass
|
||||
8. New tests for upload functionality pass
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/13-file-upload-infrastructure/13-02-SUMMARY.md`
|
||||
</output>
|
||||
|
|
@ -0,0 +1,230 @@
|
|||
---
|
||||
phase: 13-file-upload-infrastructure
|
||||
plan: 03
|
||||
type: execute
|
||||
wave: 2
|
||||
depends_on: ["13-01", "13-02"]
|
||||
files_modified:
|
||||
- jam-ui/src/components/client/chat/JKChatAttachButton.js
|
||||
- jam-ui/src/components/client/chat/JKChatComposer.js
|
||||
autonomous: false
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "User clicks Attach button and OS file dialog opens"
|
||||
- "User selects invalid file (too large or wrong type) and sees immediate error"
|
||||
- "User selects valid file and upload starts automatically"
|
||||
- "Upload progress indicator shows 'Uploading...' state during upload"
|
||||
- "User can continue using app while upload is in progress (button disabled, UI responsive)"
|
||||
- "Upload error displays with clear message and option to dismiss"
|
||||
artifacts:
|
||||
- path: "jam-ui/src/components/client/chat/JKChatAttachButton.js"
|
||||
provides: "Hidden file input + visible button trigger component"
|
||||
exports: ["default (JKChatAttachButton)"]
|
||||
min_lines: 50
|
||||
- path: "jam-ui/src/components/client/chat/JKChatComposer.js"
|
||||
provides: "Chat composer with integrated attach button"
|
||||
key_links:
|
||||
- from: "jam-ui/src/components/client/chat/JKChatAttachButton.js"
|
||||
to: "JKChatComposer"
|
||||
via: "onFileSelect prop callback"
|
||||
pattern: "onFileSelect\\(file\\)"
|
||||
- from: "JKChatComposer handleFileSelect"
|
||||
to: "attachmentValidation.js"
|
||||
via: "validateFile import"
|
||||
pattern: "validateFile\\(file\\)"
|
||||
- from: "JKChatComposer handleFileSelect"
|
||||
to: "sessionChatSlice"
|
||||
via: "dispatch(uploadAttachment)"
|
||||
pattern: "dispatch\\(uploadAttachment"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Create attach button component and integrate into chat composer with validation and upload handling
|
||||
|
||||
Purpose: Provide the user-facing UI for file attachment - clicking the attach button opens OS file dialog, selected files are validated, and valid files are uploaded. This is the final plan that wires together the validation service (Plan 01) and upload state (Plan 02) into the user interface.
|
||||
|
||||
Output: JKChatAttachButton component and modified JKChatComposer with complete upload flow
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@/Users/nuwan/.claude/get-shit-done/workflows/execute-plan.md
|
||||
@/Users/nuwan/.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/STATE.md
|
||||
@.planning/phases/12-attachment-research-&-backend-validation/docs/REACT_INTEGRATION_DESIGN.md
|
||||
@.planning/phases/13-file-upload-infrastructure/13-01-SUMMARY.md
|
||||
@.planning/phases/13-file-upload-infrastructure/13-02-SUMMARY.md
|
||||
@jam-ui/src/components/client/chat/JKChatComposer.js
|
||||
|
||||
# Dependencies from prior plans:
|
||||
# - validateFile from attachmentValidation.js (Plan 01)
|
||||
# - uploadAttachment, selectIsUploading, selectUploadError, clearUploadError from sessionChatSlice.js (Plan 02)
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Create JKChatAttachButton component</name>
|
||||
<files>jam-ui/src/components/client/chat/JKChatAttachButton.js</files>
|
||||
<action>
|
||||
Create new component following REACT_INTEGRATION_DESIGN.md Section 2.
|
||||
|
||||
Component requirements:
|
||||
1. Hidden file input triggered by visible button (useRef pattern)
|
||||
2. Props: onFileSelect (required), disabled (optional), isUploading (optional)
|
||||
3. Accept attribute: ".pdf,.xml,.mxl,.txt,.png,.jpg,.jpeg,.gif,.mp3,.wav"
|
||||
4. Button shows "Uploading..." when isUploading=true, "Attach" otherwise
|
||||
5. Reset input value after file selection (allows re-selecting same file)
|
||||
6. Inline styles for MVP (gray background, white text, disabled cursor when disabled)
|
||||
7. aria-label="Attach file" for accessibility
|
||||
8. React.memo for performance optimization
|
||||
9. PropTypes for all props
|
||||
|
||||
Pattern (from research design):
|
||||
```javascript
|
||||
const fileInputRef = useRef(null);
|
||||
const handleButtonClick = useCallback(() => {
|
||||
fileInputRef.current?.click();
|
||||
}, []);
|
||||
const handleFileChange = useCallback((e) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (file) {
|
||||
onFileSelect(file);
|
||||
e.target.value = ''; // Reset for re-selection
|
||||
}
|
||||
}, [onFileSelect]);
|
||||
```
|
||||
</action>
|
||||
<verify>
|
||||
File exists at jam-ui/src/components/client/chat/JKChatAttachButton.js
|
||||
No syntax errors when importing
|
||||
Component renders without errors
|
||||
</verify>
|
||||
<done>
|
||||
JKChatAttachButton component created with hidden file input pattern, proper props, accessibility attributes, and React.memo optimization
|
||||
</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Integrate attach button into JKChatComposer</name>
|
||||
<files>jam-ui/src/components/client/chat/JKChatComposer.js</files>
|
||||
<action>
|
||||
Modify JKChatComposer to include attach button with validation and upload.
|
||||
|
||||
Changes:
|
||||
1. Import JKChatAttachButton from './JKChatAttachButton'
|
||||
2. Import validateFile from '../../../services/attachmentValidation'
|
||||
3. Import uploadAttachment, selectIsUploading, selectUploadError, clearUploadError from Redux
|
||||
4. Add useSelector for isUploading and uploadError
|
||||
5. Create handleFileSelect callback:
|
||||
- Call validateFile(file)
|
||||
- If invalid: show error (alert for MVP, can enhance later)
|
||||
- If valid with warnings: console.warn
|
||||
- If valid: dispatch(uploadAttachment({ file, sessionId, clientId }))
|
||||
6. Add JKChatAttachButton to render, positioned before Send button
|
||||
7. Pass props: onFileSelect={handleFileSelect}, disabled={!isConnected || isUploading}, isUploading={isUploading}
|
||||
8. Display uploadError below composer if present (with dismiss button)
|
||||
|
||||
Layout change:
|
||||
```jsx
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<JKChatAttachButton
|
||||
onFileSelect={handleFileSelect}
|
||||
disabled={!isConnected || isUploading}
|
||||
isUploading={isUploading}
|
||||
/>
|
||||
<button onClick={handleSend} disabled={!canSend}>
|
||||
{isSending ? 'Sending...' : 'Send'}
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
Error display:
|
||||
```jsx
|
||||
{uploadError && (
|
||||
<div style={{ color: '#dc3545', fontSize: '12px', marginTop: '4px' }}>
|
||||
{uploadError}
|
||||
<button onClick={() => dispatch(clearUploadError())} style={{ marginLeft: '8px' }}>
|
||||
Dismiss
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
```
|
||||
|
||||
Required context:
|
||||
- sessionId prop from parent (JKSessionChatWindow)
|
||||
- server?.clientId from useSessionWebSocket or parent context
|
||||
- isConnected from existing state
|
||||
</action>
|
||||
<verify>
|
||||
cd /Users/nuwan/Code/jam-cloud/jam-ui && npm run build
|
||||
No compilation errors
|
||||
No missing import errors
|
||||
</verify>
|
||||
<done>
|
||||
JKChatComposer has attach button integrated with validation, upload dispatch, and error display
|
||||
</done>
|
||||
</task>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking">
|
||||
<what-built>
|
||||
Complete file upload infrastructure:
|
||||
1. JKChatAttachButton - Hidden file input with visible button trigger
|
||||
2. JKChatComposer - Integrated attach button with validation and upload
|
||||
3. Validation service (from Plan 01) - File size and type checking
|
||||
4. Redux upload state (from Plan 02) - Upload progress tracking
|
||||
</what-built>
|
||||
<how-to-verify>
|
||||
1. Start jam-ui dev server: cd /Users/nuwan/Code/jam-cloud/jam-ui && npm run start
|
||||
2. Navigate to a session at http://beta.jamkazam.local:4000/
|
||||
3. Open the chat window by clicking the Chat button
|
||||
4. Verify the "Attach" button appears next to the Send button
|
||||
5. Click "Attach" - OS file dialog should open
|
||||
6. Select a file that is TOO LARGE (>10 MB) - should see error, no upload
|
||||
7. Select a file with INVALID TYPE (.exe, .zip) - should see error, no upload
|
||||
8. Select a VALID file (.pdf, .png, .txt, etc.) - should see "Uploading..." state
|
||||
9. Watch console for any errors
|
||||
10. If backend is running, upload should complete and reset to normal state
|
||||
|
||||
Expected behaviors:
|
||||
- Attach button is visible and clickable
|
||||
- File dialog opens on click
|
||||
- Invalid files show immediate error
|
||||
- Valid files trigger upload (button shows "Uploading...")
|
||||
- No console errors
|
||||
</how-to-verify>
|
||||
<resume-signal>Type "approved" if upload flow works, or describe issues found</resume-signal>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
```bash
|
||||
# Build verification
|
||||
cd /Users/nuwan/Code/jam-cloud/jam-ui && npm run build
|
||||
|
||||
# Run related tests
|
||||
cd /Users/nuwan/Code/jam-cloud/jam-ui && npm run test:unit -- attachmentValidation
|
||||
cd /Users/nuwan/Code/jam-cloud/jam-ui && npm run test:unit -- sessionChatSlice
|
||||
```
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
1. JKChatAttachButton component exists and exports correctly
|
||||
2. JKChatComposer imports and uses JKChatAttachButton
|
||||
3. File validation runs before upload attempt
|
||||
4. Invalid files show immediate error without network request
|
||||
5. Valid files dispatch uploadAttachment action
|
||||
6. Upload progress indicator visible during upload
|
||||
7. Upload error displays with dismiss option
|
||||
8. User verified upload flow works correctly
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/13-file-upload-infrastructure/13-03-SUMMARY.md`
|
||||
</output>
|
||||
Loading…
Reference in New Issue