From a8ec4e3a884c2aceb272cc05341e731ad796bed6 Mon Sep 17 00:00:00 2001 From: Nuwan Date: Thu, 5 Feb 2026 10:43:57 +0530 Subject: [PATCH] fix(13): revise plans based on checker feedback Phase 13: File Upload Infrastructure - Plan 13-01: Reframe truths as user-observable outcomes - Plan 13-02: Reframe truths as user-observable outcomes - Plan 13-03: Move Attach button from chat composer to session toolbar per REQ-1.1 - Plan 13-03: Add upload progress display in chat window per REQ-1.4 - Plan 13-03: Update file list to reflect actual files modified Blockers addressed: - REQ-1.1 location: Button now goes in JKSessionScreen toolbar (not JKChatComposer) - Upload display: Added JKChatUploadProgress component and integration Warnings addressed: - must_haves.truths now describe user experience, not implementation Co-Authored-By: Claude Sonnet 4.5 --- .../13-01-PLAN.md | 11 +- .../13-02-PLAN.md | 12 +- .../13-03-PLAN.md | 410 ++++++++++++------ 3 files changed, 288 insertions(+), 145 deletions(-) diff --git a/.planning/phases/13-file-upload-infrastructure/13-01-PLAN.md b/.planning/phases/13-file-upload-infrastructure/13-01-PLAN.md index 2f46a01f6..29aeec06b 100644 --- a/.planning/phases/13-file-upload-infrastructure/13-01-PLAN.md +++ b/.planning/phases/13-file-upload-infrastructure/13-01-PLAN.md @@ -11,11 +11,10 @@ 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)" + - "User selecting a file over 10 MB sees immediate error message before upload starts" + - "User selecting unsupported file type (.exe, .zip) sees immediate error listing allowed types" + - "User selecting valid file (.pdf, .png, .mp3) passes validation and proceeds to upload" + - "User sees human-readable file sizes (e.g., '5.0 MB' not '5242880 bytes')" artifacts: - path: "jam-ui/src/services/attachmentValidation.js" provides: "File validation utilities" @@ -27,7 +26,7 @@ must_haves: key_links: - from: "jam-ui/src/services/attachmentValidation.js" to: "Phase 13 Plan 03" - via: "Import in JKChatComposer for validation before upload" + via: "Import in JKSessionScreen for validation before upload" pattern: "validateFile" --- diff --git a/.planning/phases/13-file-upload-infrastructure/13-02-PLAN.md b/.planning/phases/13-file-upload-infrastructure/13-02-PLAN.md index 9fbd9645a..0cb204708 100644 --- a/.planning/phases/13-file-upload-infrastructure/13-02-PLAN.md +++ b/.planning/phases/13-file-upload-infrastructure/13-02-PLAN.md @@ -12,13 +12,11 @@ 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" + - "User clicking Attach on valid file sees 'Uploading...' state reflected in UI immediately" + - "User sees upload complete and state returns to normal when backend responds successfully" + - "User sees clear error message when upload fails (network error, server error)" + - "User uploading file >10 MB rejected by backend sees 'File too large' error message" + - "User can dismiss upload error and try again with a different file" artifacts: - path: "jam-ui/src/store/features/sessionChatSlice.js" provides: "Upload state management and uploadAttachment thunk" diff --git a/.planning/phases/13-file-upload-infrastructure/13-03-PLAN.md b/.planning/phases/13-file-upload-infrastructure/13-03-PLAN.md index 50cbd6762..217e4b5be 100644 --- a/.planning/phases/13-file-upload-infrastructure/13-03-PLAN.md +++ b/.planning/phases/13-file-upload-infrastructure/13-03-PLAN.md @@ -5,46 +5,50 @@ 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 + - jam-ui/src/components/client/JKSessionScreen.js + - jam-ui/src/components/client/chat/JKChatUploadProgress.js + - jam-ui/src/components/client/chat/JKChatMessageList.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 clicks Attach button in session toolbar and OS file dialog opens" + - "User selects invalid file (too large or wrong type) and sees immediate error toast" - "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" + - "User sees 'Uploading [filename]...' message in chat window during upload" + - "Upload progress message disappears when upload completes (success or error)" + - "User can continue using app while upload is in progress (attach button disabled, UI responsive)" + - "Upload error displays as toast with clear message" 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" + - path: "jam-ui/src/components/client/JKSessionScreen.js" + provides: "Session toolbar with functional Attach button" + - path: "jam-ui/src/components/client/chat/JKChatUploadProgress.js" + provides: "Temporary upload progress message component" + exports: ["default (JKChatUploadProgress)"] + min_lines: 30 + - path: "jam-ui/src/components/client/chat/JKChatMessageList.js" + provides: "Chat message list showing upload progress" key_links: - - from: "jam-ui/src/components/client/chat/JKChatAttachButton.js" - to: "JKChatComposer" - via: "onFileSelect prop callback" - pattern: "onFileSelect\\(file\\)" - - from: "JKChatComposer handleFileSelect" + - from: "jam-ui/src/components/client/JKSessionScreen.js" to: "attachmentValidation.js" - via: "validateFile import" + via: "validateFile import for pre-upload validation" pattern: "validateFile\\(file\\)" - - from: "JKChatComposer handleFileSelect" + - from: "jam-ui/src/components/client/JKSessionScreen.js" to: "sessionChatSlice" - via: "dispatch(uploadAttachment)" + via: "dispatch(uploadAttachment) on valid file" pattern: "dispatch\\(uploadAttachment" + - from: "JKChatMessageList" + to: "JKChatUploadProgress" + via: "Renders upload progress when status='uploading'" + pattern: "isUploading.*JKChatUploadProgress" --- -Create attach button component and integrate into chat composer with validation and upload handling +Wire up the Attach button in session toolbar with validation and upload handling, plus display upload progress in chat window -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. +Purpose: Provide the user-facing UI for file attachment - clicking the Attach button in the session toolbar opens OS file dialog, selected files are validated, valid files are uploaded, and upload progress is displayed in the chat window. 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 +Output: Functional Attach button in session toolbar and upload progress display in chat window @@ -56,110 +60,138 @@ Output: JKChatAttachButton component and modified JKChatComposer with complete u @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md +@.planning/REQUIREMENTS.md (REQ-1.1: Attach Button in Session Toolbar) @.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 +@jam-ui/src/components/client/JKSessionScreen.js +@jam-ui/src/components/client/chat/JKChatMessageList.js + +# REQ-1.1 explicitly states: "Add 'Attach' button to session toolbar (top navigation)" +# Implementation note: "Similar pattern to Backing Track 'Open' button" +# The toolbar is in JKSessionScreen.js, NOT in JKChatComposer # Dependencies from prior plans: # - validateFile from attachmentValidation.js (Plan 01) -# - uploadAttachment, selectIsUploading, selectUploadError, clearUploadError from sessionChatSlice.js (Plan 02) +# - uploadAttachment, selectIsUploading, selectUploadError, selectUploadFileName, clearUploadError from sessionChatSlice.js (Plan 02) - Task 1: Create JKChatAttachButton component - jam-ui/src/components/client/chat/JKChatAttachButton.js + Task 1: Wire up Attach button in session toolbar + jam-ui/src/components/client/JKSessionScreen.js - Create new component following REACT_INTEGRATION_DESIGN.md Section 2. + Modify JKSessionScreen.js to make the existing Attach button functional. - 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]); + The placeholder Attach button already exists at approximately line 1178-1180: + ```jsx + ``` - - - File exists at jam-ui/src/components/client/chat/JKChatAttachButton.js - No syntax errors when importing - Component renders without errors - - - JKChatAttachButton component created with hidden file input pattern, proper props, accessibility attributes, and React.memo optimization - - - - Task 2: Integrate attach button into JKChatComposer - jam-ui/src/components/client/chat/JKChatComposer.js - - Modify JKChatComposer to include attach button with validation and upload. + Changes required: + + 1. Add imports at the top of the file: + - import { validateFile, getAttachmentType } from '../../services/attachmentValidation' + - import { uploadAttachment, selectIsUploading, selectUploadError, selectUploadFileName, clearUploadError } from '../../store/features/sessionChatSlice' + - toast is already imported from 'react-toastify' + + 2. Add a hidden file input ref (inside component, before return): + ```javascript + const attachFileInputRef = useRef(null); + ``` + + 3. Add useSelector for upload state: + ```javascript + const isUploading = useSelector(selectIsUploading); + const uploadError = useSelector(selectUploadError); + const uploadFileName = useSelector(selectUploadFileName); + ``` + + 4. Create handleAttachClick callback: + ```javascript + const handleAttachClick = useCallback(() => { + attachFileInputRef.current?.click(); + }, []); + ``` - 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) + ```javascript + const handleFileSelect = useCallback((e) => { + const file = e.target.files?.[0]; + if (!file) return; - Layout change: - ```jsx -
- - -
- ``` + // Reset input for re-selection of same file + e.target.value = ''; - Error display: - ```jsx - {uploadError && ( -
- {uploadError} - -
- )} - ``` + // Validate file before upload + const validation = validateFile(file); - Required context: - - sessionId prop from parent (JKSessionChatWindow) - - server?.clientId from useSessionWebSocket or parent context - - isConnected from existing state + if (!validation.valid) { + toast.error(validation.error); + return; + } + + // Show warnings if any (e.g., backend may not fully support this type) + validation.warnings?.forEach(warning => { + console.warn('Attachment warning:', warning); + }); + + // Open chat window if not already open + dispatch(openModal('chat')); + + // Dispatch upload + dispatch(uploadAttachment({ + file, + sessionId, + clientId: server?.clientId + })); + }, [dispatch, sessionId, server?.clientId]); + ``` + + 6. Add useEffect to show error toast when uploadError changes: + ```javascript + useEffect(() => { + if (uploadError) { + toast.error(uploadError); + dispatch(clearUploadError()); + } + }, [uploadError, dispatch]); + ``` + + 7. Add hidden file input just before the Attach button: + ```jsx + + ``` + + 8. Update the Attach button to be functional: + ```jsx + + ``` + + Key points: + - Button is in the session toolbar (top navigation) per REQ-1.1 + - Similar pattern to JKSessionOpenMenu which uses a dropdown but also triggers file selection + - File validation happens before upload starts (no wasted network requests) + - Chat window opens automatically when upload starts so user can see progress + - Error handling via toast notifications
cd /Users/nuwan/Code/jam-cloud/jam-ui && npm run build @@ -167,38 +199,151 @@ Output: JKChatAttachButton component and modified JKChatComposer with complete u No missing import errors - JKChatComposer has attach button integrated with validation, upload dispatch, and error display + Attach button in session toolbar is functional: opens file dialog on click, validates selection, dispatches upload for valid files, shows errors for invalid files + +
+ + + Task 2: Create upload progress component for chat window + jam-ui/src/components/client/chat/JKChatUploadProgress.js + + Create new component to display upload progress in the chat window. + + Component requirements: + 1. Displays "Uploading [filename]..." message + 2. Shows a simple spinner or loading indicator + 3. Styled to look like a system message (not a user message) + 4. Props: fileName (required) + + Implementation: + ```javascript + import React, { memo } from 'react'; + import PropTypes from 'prop-types'; + import { Spinner } from 'reactstrap'; + + const JKChatUploadProgress = memo(({ fileName }) => { + return ( +
+ + Uploading {fileName}... +
+ ); + }); + + JKChatUploadProgress.propTypes = { + fileName: PropTypes.string.isRequired + }; + + JKChatUploadProgress.displayName = 'JKChatUploadProgress'; + + export default JKChatUploadProgress; + ``` + + This component will be rendered in JKChatMessageList when an upload is in progress. +
+ + File exists at jam-ui/src/components/client/chat/JKChatUploadProgress.js + No syntax errors when importing + + + JKChatUploadProgress component created with spinner and filename display + +
+ + + Task 3: Display upload progress in chat message list + jam-ui/src/components/client/chat/JKChatMessageList.js + + Modify JKChatMessageList to show upload progress when an upload is in progress. + + Changes required: + + 1. Add imports: + ```javascript + import { useSelector } from 'react-redux'; + import { selectIsUploading, selectUploadFileName } from '../../../store/features/sessionChatSlice'; + import JKChatUploadProgress from './JKChatUploadProgress'; + ``` + + 2. Add selector hooks inside component: + ```javascript + const isUploading = useSelector(selectIsUploading); + const uploadFileName = useSelector(selectUploadFileName); + ``` + + 3. Render JKChatUploadProgress at the end of the message list (after all messages, before closing div): + ```jsx + {/* Existing messages map */} + {messages.map((message) => ( + // ... existing message rendering + ))} + + {/* Upload progress indicator */} + {isUploading && uploadFileName && ( + + )} + ``` + + The progress message should appear at the bottom of the chat list so the user sees it when they scroll down or when auto-scroll kicks in. + + + cd /Users/nuwan/Code/jam-cloud/jam-ui && npm run build + No compilation errors + Upload progress appears in chat when isUploading is true + + + Chat message list displays upload progress indicator when upload is in progress 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 + 1. Attach button in session toolbar - opens OS file dialog (REQ-1.1) + 2. File validation before upload - rejects invalid size/type (REQ-1.2, REQ-1.3) + 3. Upload progress display in chat window - shows "Uploading [filename]..." (REQ-1.4) + 4. Validation service (from Plan 01) - File size and type checking + 5. Redux upload state (from Plan 02) - Upload progress tracking 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 + 3. Look at the session toolbar (top row of buttons: Settings, Invite, Volume, etc.) + 4. Verify the "Attach" button appears in the toolbar (NOT in the chat composer) 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 + 6. Select a file that is TOO LARGE (>10 MB) - should see error toast, no upload + 7. Select a file with INVALID TYPE (.exe, .zip) - should see error toast, no upload + 8. Select a VALID file (.pdf, .png, .txt, etc.): + - Chat window should open automatically + - "Uploading [filename]..." should appear at bottom of chat + - Attach button should show "Uploading..." and be disabled + 9. If backend is running, upload should complete: + - "Uploading..." message disappears + - Attach button returns to normal state + 10. Watch console for any errors Expected behaviors: - - Attach button is visible and clickable + - Attach button is in session TOOLBAR (next to Chat, Open, Record buttons) + - NOT in chat composer area - File dialog opens on click - - Invalid files show immediate error - - Valid files trigger upload (button shows "Uploading...") + - Invalid files show immediate error toast + - Valid files trigger upload with progress shown in chat - No console errors - Type "approved" if upload flow works, or describe issues found + Type "approved" if upload flow works correctly with button in toolbar, or describe issues found
@@ -215,14 +360,15 @@ cd /Users/nuwan/Code/jam-cloud/jam-ui && npm run test:unit -- sessionChatSlice -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 +1. Attach button in session toolbar (NOT chat composer) opens file dialog +2. File validation runs before upload attempt +3. Invalid files show immediate error toast without network request +4. Valid files dispatch uploadAttachment action +5. Upload progress indicator ("Uploading [filename]...") displays in chat window +6. Upload progress disappears when upload completes or fails +7. Attach button shows "Uploading..." state and is disabled during upload +8. Upload error displays as toast notification +9. User verified upload flow works correctly