Feat: Add Corpus Audio Recording and Chunked Upload to Volunteer KYP
Feature Summary
Enable volunteers to record audio in KYP and upload it to Corpus with metadata, location, chunked transfer, retry logic, progress feedback, and finalization. In the same KYP section, add Corpus credential login (phone + password) verified against Corpus API and use that token for upload.
Problem Statement
- KYP currently captures patient responses but does not support Corpus audio capture/upload in the same workflow.
- Volunteers must use external/manual processes for voice collection.
- This increases friction and can cause missing or delayed audio records.
Proposed Solution
- Add
AudioRecordercontrols (start/stop/clear + preview) inside KYP. - Add
UploadFormfor title, description, location, Corpus login fields, and upload actions. - Implement Corpus upload service with:
- chunk upload (
/records/upload/chunk) - exponential retry for transient failures
- finalize upload (
/records/upload) - upload progress and stage tracking
- Add Corpus login verification using
/auth/loginwith{ phone, password }on Corpus API. - Persist Corpus token + Corpus user id in local storage and use them for upload authorization.
- Keep eHRS auth only as base app authentication; Corpus upload auth comes from Corpus login session.
- Add i18n keys for Corpus recording/upload messages.
- Add unit tests for chunk splitting, retry behavior, unauthorized handling, and finalize payload fields.
Test-Driven Development
Acceptance Criteria (Given-When-Then)
Primary User Flow
Scenario 1: Record and Upload Audio Successfully
Given volunteer is on KYP page with a selected patient
And volunteer is authenticated in eHRS
And volunteer has logged into Corpus with valid phone and password
And volunteer has recorded audio, entered title/description, and captured location
When volunteer clicks Upload Audio
Then audio is uploaded to Corpus in chunks
And upload progress is shown
And upload is finalized successfully
And success message is displayed
Alternative Flows
Scenario 2: Retry Works for Transient Chunk Failure
Given chunk upload fails due to temporary network error
When upload retry logic runs
Then chunk upload is retried with exponential backoff
And upload continues once retry succeeds
Edge Cases
Scenario 3: Empty Audio State
Given volunteer has not recorded audio
When volunteer attempts upload
Then upload is blocked
And guidance message is shown
Scenario 4: Missing Corpus Credentials
Given volunteer has not logged into Corpus
When volunteer attempts upload
Then upload is blocked
And a Corpus login required message is shown
Scenario 5: File Size Validation Error
Given recorded audio exceeds 10MB
When volunteer attempts upload
Then upload is blocked
And validation error is shown
Scenario 6: Upload Unauthorized
Given Corpus token is missing or expired
When volunteer attempts upload
Then upload is blocked
And user is prompted to login to Corpus again
Scenario 7: Corpus API Error State
Given Corpus API returns an error during upload/finalize
When volunteer attempts upload
Then error is handled gracefully
And user-friendly error message is shown
Unit Test Requirements
Components/Functions to Create:
| File Path | Component/Function | Test Coverage Required |
|---|---|---|
| src/features/corpus/components/AudioRecorder.tsx | Recording controls + preview | Render, control states, disabled behavior |
| src/features/corpus/components/UploadForm.tsx | Metadata/location/upload UI + Corpus login fields | Render, validation, login/upload interactions |
| src/features/corpus/hooks/useRecorder.ts | Browser recording hook | Start/stop/clear and error flows |
| src/features/corpus/hooks/useCorpusUpload.ts | Upload + Corpus login session hook | Login/logout, stage transitions, progress, error handling |
| src/features/corpus/services/corpusApi.ts | Corpus upload/login API helpers | Login payload, token parsing, chunking, retry, finalize payload |
| tests/features/corpus/corpusApi.test.ts | API helper tests | Chunk split, retry, unauthorized, finalize fields |
Test Assertions Required:
-
Component renders without crashing -
Component renders with correct initial state -
User interactions trigger correct handlers -
Form validation works correctly -
API calls made with correct payload -
Loading states display correctly -
Success states display correctly -
Error states display correctly -
State updates propagate correctly -
Child components receive correct props -
Callbacks are called with correct arguments
Technical Specification
Files to Create/Modify
| File Path | Action | Purpose |
|---|---|---|
| src/features/corpus/components/AudioRecorder.tsx | Create | Recording UI and browser support/error messaging |
| src/features/corpus/components/UploadForm.tsx | Create | Corpus upload form with metadata, location, upload status |
| src/features/corpus/hooks/useRecorder.ts | Create | MediaRecorder lifecycle and blob handling |
| src/features/corpus/hooks/useCorpusUpload.ts | Create/Modify | Upload orchestration plus Corpus credential login session management |
| src/features/corpus/services/corpusApi.ts | Create/Modify | Chunk upload, retry, finalize, and Corpus login API integration |
| src/features/corpus/types.ts | Create | Shared Corpus upload/session types |
| src/pages/volunteer/KYPPage.tsx | Modify | Integrate recorder + upload flow in KYP |
| src/contexts/AuthContext.tsx | Modify | Expose authToken in context value |
| .env.example | Modify | Add VITE_CORPUS_SERVER_URL for Corpus API base URL |
| src/locales/en/translations.json | Modify | Add Corpus recording/upload strings |
| src/locales/hi/translations.json | Modify | Add Corpus recording/upload strings |
| src/locales/te/translations.json | Modify | Add Corpus recording/upload strings |
| tests/features/corpus/corpusApi.test.ts | Create | Unit tests for corpus API helpers |
API Requirements
| Endpoint | Method | Request Body | Response | Status |
|---|---|---|---|---|
| /api/v1/auth/login | POST | { phone, password } | { access_token, user fields/claims } | [x] Exists [ ] Needs Backend |
| /api/v1/records/upload/chunk | POST | multipart/form-data | upload chunk ack | [x] Exists [ ] Needs Backend |
| /api/v1/records/upload | POST | multipart/form-data | upload result | [x] Exists [ ] Needs Backend |
API Types to Add:
interface CorpusUploadPayload {
title: string;
description: string;
audioFile: File;
location: { latitude: number; longitude: number };
}
interface CorpusLoginRequest {
phone: string;
password: string;
}
interface CorpusAuthSession {
token: string;
userId: string | null;
}
type CorpusUploadStep =
| 'idle'
| 'recording'
| 'ready'
| 'uploading'
| 'finalizing'
| 'success'
| 'error';
UI/UX Specification
Design Requirements
- Add recorder controls (start/stop/clear) with disabled states.
- Show audio preview when recording exists.
- Keep upload metadata fields (title, description, location).
- Add Corpus login inputs (phone, password) and login/logout action.
- Show upload states: idle/recording/ready/uploading/finalizing/success/error.
- Show retry action and inline validation/error/success messages.
Mockups/Wireframes:
- Reuse existing KYP card layout; no new design dependency required.
Responsive Behavior
| Viewport | Behavior |
|---|---|
| Mobile (<576px) | Recorder and upload form stack vertically with wrapped actions |
| Tablet (576px-991px) | Form fields and controls keep spacing and readability |
| Desktop (≥992px) | Recorder + upload controls shown in single card with full form width |
i18n Requirements
| String Key | Default Value (English) | Context |
|---|---|---|
| common.corpusAudioRecordingUpload | Corpus Audio Recording & Upload | KYP section title |
| corpus.login.phone | Phone Number | Corpus login input label |
| corpus.login.password | Password | Corpus login input label |
| corpus.login.required | Corpus login is required before uploading. | Upload prerequisite hint |
| common.recordAudioToEnableUpload | Record audio to enable upload. | Upload prerequisite hint |
| common.captureYourLocationBeforeUpload | Capture your location before upload. | Location prerequisite hint |
| messages.signInIsRequiredBeforeUploading | Sign in is required before uploading. | Session prerequisite hint |
| messages.uploadingChunks | Uploading chunks: | Progress text |
| nav.loginSessionIsRequiredToEnableRecording | Login session is required to enable recording. | Recorder disabled hint |
Definition of Done
Development
-
All acceptance criteria (Given-When-Then) pass -
All test cases executed and passing -
Unit tests written with minimum 80% coverage -
Integration tests written (if applicable) -
Code follows project conventions -
No TypeScript errors or any types -
No ESLint/Prettier errors -
No console.log() or debugger statements
Testing
-
Manual testing completed on all browsers -
Responsive testing on mobile, tablet, desktop -
Accessibility testing completed -
Performance testing (no significant slowdowns) -
Cross-browser testing (Chrome, Firefox, Safari, Edge)
Documentation
-
README.md updated (if setup changed) -
.env.example updated (if new env vars needed) -
Feature documented for end users (if applicable)
Code Review
-
Code reviewed and approved by at least 1 team member -
Product owner approved (if applicable)
Additional Context
Open Questions
- Should Corpus login success/failure labels be fully moved to i18n keys (currently some UI text is hardcoded)?
- Is
ENABLE_VOLUNTEER_CAMP_GATING = falseintended for this branch release? - Should Corpus user id always come from API response, or keep JWT-claim fallback extraction?
References
- API docs: https://api.corpus.swecha.org/docs
- Login endpoint: https://api.corpus.swecha.org/api/v1/auth/login
- Chunk upload endpoint: https://api.corpus.swecha.org/api/v1/records/upload/chunk
- Finalize endpoint: https://api.corpus.swecha.org/api/v1/records/upload
- KYP page: src/pages/volunteer/KYPPage.tsx
- Upload form: src/features/corpus/components/UploadForm.tsx