feat: auto-select processed meeting to show transcript immediately#79
feat: auto-select processed meeting to show transcript immediately#79NotYuSheng merged 16 commits intomainfrom
Conversation
- Add CSS shadow variables for light and dark modes - Apply shadows to header card, main cards, and buttons - Add subtle hover animations with lift effect on buttons - Style transcript entries with shadows and rounded corners - Enhance visual hierarchy and modern design aesthetics 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Add subtle opacity reduction (50%) for disabled buttons - Include not-allowed cursor for better UX indication - Prevent hover animations on disabled state - Clean, minimal approach without color changes 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Slow down particle movement speed from 6 to 2 for subtler motion - Add organizational color variety to particles using brand palette - Keep original white connecting lines with full opacity unchanged - Particles now use: #2998D5, #265289, #75797C, #bba88e, #c42030, #FFFFFF 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Fixed sun icon visibility in light mode (☀️) - Corrected CSS logic for checked/unchecked states - Light mode: sun icon on left with blue gradient - Dark mode: moon icon on right with gray gradient - Smooth transitions between states with proper icon positioning 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Replace flat colors with subtle linear gradients for visual depth - Add gradient backgrounds to app container, cards, and components - Enhance button styling with gradient backgrounds and hover effects - Improve transcript entries with gradient backgrounds - Maintain organizational color palette while adding modern visual appeal - Keep accessibility and contrast standards intact 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Add professional styling to model selector dropdown - Implement proper focus and hover states with organizational colors - Use solid backgrounds for better cross-browser compatibility - Improve responsive layout with better spacing and alignment - Add consistent typography using Barlow font family - Support both light and dark theme modes 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Replace plain #bba88e borders with subtle gradient border effects - Use CSS pseudo-elements for sophisticated gradient border technique - Fix rectangular shadow issue by moving shadows to pseudo-elements - Apply proper border-radius calculations for clean rounded corners - Add theme-aware gradient variations for light and dark modes - Improve visual depth and premium appearance of all cards 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Remove ugly red color (#c42030) from particle palette - Increase particle opacity from 1.0 to 0.9 for much sharper appearance - Make connecting lines more prominent (opacity 0.8, width 2.5px) - Use darker shade of #bba88e (#8a7c6b) for connecting lines - Reduce app background opacity to 50-60% so particles show through - Improve particle backgrounds with more contrasting gradients - Clean color palette: #2998D5, #265289, #75797C, #bba88e, #FFFFFF 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Convert recording, upload, and transcription buttons to discrete red design - Use #F4393B for discrete buttons with icon-only interface and tooltips - Implement prominent transcription button (#D32F2F) when file is uploaded - Add "Process Audio" text to transcription button for better UX clarity - Fix CSS specificity issues with \!important to override gradient backgrounds - Ensure consistent button sizing (2.5rem width) across all states - Support both light and dark mode themes with proper red color scheme 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Replace confusing MicOff icon with Square icon for stop recording - Fix recording timer vertical alignment with action buttons - Move recording indicator inside button group for better layout - Add smooth hover effects to transcript/summary tabs - Implement tab-like appearance with rounded top corners - Add subtle blue background and border hints on tab hover - Improve overall controls container alignment and spacing 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Implement pause/resume recording functionality with MediaRecorder API - Add professional HTML5 audio player with timeline scrubbing and volume control - Enable manual control over when to process recorded audio (no auto-processing) - Add discard recording option for better user control - Improve button state management for different recording phases: * Default: [Mic] [Upload] [Process Audio] * Recording: [Pause] [Stop] [Process Audio] * Preview: [Trash] [Upload] [Process Audio] + Audio Player - Add visual feedback for paused recording state (orange dot, timer shows "(Paused)") - Style audio player to match app theme with custom controls - Handle proper cleanup of audio URLs to prevent memory leaks 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Refactored Dockerfile to use multi-stage build with nginx - Added nginx.conf with production-ready configuration including gzip, security headers, and API proxying - Updated docker-compose.yml port mapping from 3000:3000 to 80:80 - Removed unnecessary volume mounts for frontend container - This addresses HTTPS requirements for getUserMedia API and improves production deployment 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Remove invalid 'must-revalidate' value from gzip_proxied directive - Change frontend port mapping from 80:80 to 3000:80 - Comment out GPU deployment configuration 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Add audio player that displays when file is uploaded - Allow users to preview uploaded audio before processing - Add discard functionality for uploaded files - Integrate same audio controls as recording feature - Auto-clear uploaded file after processing - Update button states to handle upload workflow 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Replace emoji trash icon with Trash2 React icon component - Change button style from btn-danger to btn-discrete for consistency - Add tooltip for better user experience - Use btn-icon class for consistent icon styling - Match visual style of other action buttons (mic, upload) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Automatically select meeting after successful audio processing - Set selectedMeetingId in both uploadFile and processAudio functions - Ensures transcript appears immediately without manual selection - Improves user experience by eliminating extra click step 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Summary of Changes
Hello @NotYuSheng, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!
I've implemented a significant user experience enhancement that automatically selects a meeting after its audio has been successfully processed. This change ensures that the transcript and summary are displayed immediately, eliminating the need for manual selection and streamlining the workflow from audio upload/recording to viewing results. Additionally, I've refined the audio handling UI, optimized the Docker deployment for the frontend with Nginx, and updated the overall visual styling of the application.
Highlights
- User Experience Enhancement: I've implemented automatic selection of processed meetings to immediately display transcripts and summaries, significantly improving user workflow by removing the need for manual selection after audio processing.
- Enhanced Audio Handling UI/UX: The frontend's audio recording and upload interface has been overhauled, introducing pause/resume functionality for recordings, audio previews for both recorded and uploaded files, and clear options to discard audio, making the interaction more intuitive.
- Docker and Deployment Optimization: The frontend Docker setup has been migrated to a multi-stage build utilizing Nginx, which improves serving efficiency, reduces image size, and incorporates robust Nginx configurations for security headers, gzip compression, caching, and API proxying.
- Styling and Dependency Updates: Core frontend dependencies, including
react-router-dom, have been updated, and the application's visual styling has been refined with new linear gradients, subtle box shadows, and redesigned button components for a more modern look and feel.
Using Gemini Code Assist
The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.
Invoking Gemini
You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.
| Feature | Command | Description |
|---|---|---|
| Code Review | /gemini review |
Performs a code review for the current pull request in its current state. |
| Pull Request Summary | /gemini summary |
Provides a summary of the current pull request in its current state. |
| Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in issue comments and review comments. |
| Help | /gemini help |
Displays a list of available commands. |
Customization
To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.
Limitations & Feedback
Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments or fill out our survey to provide feedback.
You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.
Footnotes
-
Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩
There was a problem hiding this comment.
Code Review
This pull request introduces significant improvements to the frontend, including a major UI overhaul, a move to a production-ready multi-stage Docker build with Nginx, and the implementation of the core feature to auto-select a meeting after processing. The changes greatly enhance user experience and the application's architecture. My review includes feedback on a potential security issue in the Nginx configuration, CSS maintainability, and several opportunities to improve the robustness and readability of the React component's logic.
|
|
||
| # Handle preflight requests for CORS | ||
| if ($request_method = 'OPTIONS') { | ||
| add_header 'Access-Control-Allow-Origin' '*'; |
There was a problem hiding this comment.
| # deploy: | ||
| # resources: | ||
| # reservations: | ||
| # devices: | ||
| # - driver: nvidia | ||
| # count: all | ||
| # capabilities: [gpu] |
There was a problem hiding this comment.
The GPU resource allocation for the backend service has been commented out. If this service relies on a GPU for performance (e.g., for Whisper model inference), this change could significantly degrade performance in a production environment. Please clarify if this is an intentional change for all environments or just for local development without a GPU.
| .btn.btn-discrete { | ||
| background: var(--btn-discrete) !important; | ||
| color: #fff; | ||
| padding: 0.5rem; | ||
| width: 2.5rem; | ||
| justify-content: center; | ||
| flex-shrink: 0; | ||
| } | ||
|
|
||
| .btn.btn-discrete:hover { | ||
| background: var(--btn-discrete-hover) !important; | ||
| } |
There was a problem hiding this comment.
| // eslint-disable-next-line no-unused-vars | ||
| const [isPlayingRecording, setIsPlayingRecording] = useState(false); | ||
| // eslint-disable-next-line no-unused-vars | ||
| const [isPlayingUpload, setIsPlayingUpload] = useState(false); |
There was a problem hiding this comment.
The state variables isPlayingRecording and isPlayingUpload are set but their values are never used. The eslint-disable-next-line comments indicate this is known. If these are not needed for any logic, they and their corresponding onPlay/onPause/onEnded handlers on the <audio> elements (lines 818-820 and 832-834) should be removed to avoid dead code and simplify the component's state.
| <div className="button-group"> | ||
| <button | ||
| onClick={isRecording ? stopRecording : startRecording} | ||
| className={`btn ${isRecording ? "btn-danger" : "btn-primary"}`} | ||
| > | ||
| {isRecording ? ( | ||
| <MicOff className="btn-icon" /> | ||
| ) : ( | ||
| <Mic className="btn-icon" /> | ||
| )} | ||
| {isRecording ? "Stop Recording" : "Start Recording"} | ||
| </button> | ||
| {!isRecording && !recordedAudio && !selectedFile ? ( | ||
| <> | ||
| <button | ||
| onClick={startRecording} | ||
| className="btn btn-discrete" | ||
| title="Start Recording" | ||
| > | ||
| <Mic className="btn-icon" /> | ||
| </button> | ||
|
|
||
| <button | ||
| onClick={() => fileInputRef.current?.click()} | ||
| className="btn btn-secondary" | ||
| > | ||
| <Upload className="btn-icon" /> | ||
| {selectedFile ? "Change Audio File" : "Upload Audio File"} | ||
| </button> | ||
| <button | ||
| onClick={() => fileInputRef.current?.click()} | ||
| className="btn btn-discrete" | ||
| title="Upload Audio File" | ||
| > | ||
| <Upload className="btn-icon" /> | ||
| </button> | ||
| </> | ||
| ) : isRecording ? ( | ||
| <> | ||
| <button | ||
| onClick={isPaused ? resumeRecording : pauseRecording} | ||
| className="btn btn-discrete" | ||
| title={isPaused ? "Resume Recording" : "Pause Recording"} | ||
| > | ||
| {isPaused ? ( | ||
| <Play className="btn-icon" /> | ||
| ) : ( | ||
| <Pause className="btn-icon" /> | ||
| )} | ||
| </button> | ||
|
|
||
| <button | ||
| onClick={stopRecording} | ||
| className="btn btn-discrete" | ||
| title="Stop Recording" | ||
| > | ||
| <Square className="btn-icon" /> | ||
| </button> | ||
| </> | ||
| ) : recordedAudio ? ( | ||
| <> | ||
| <button | ||
| onClick={discardRecording} | ||
| className="btn btn-discrete" | ||
| title="Discard Recording" | ||
| > | ||
| <Trash2 className="btn-icon" /> | ||
| </button> | ||
|
|
||
| <button | ||
| onClick={() => fileInputRef.current?.click()} | ||
| className="btn btn-discrete" | ||
| title="Upload Audio File" | ||
| > | ||
| <Upload className="btn-icon" /> | ||
| </button> | ||
| </> | ||
| ) : selectedFile ? ( | ||
| <> | ||
| <button | ||
| onClick={discardUpload} | ||
| className="btn btn-discrete" | ||
| title="Discard Upload" | ||
| > | ||
| <Trash2 className="btn-icon" /> | ||
| </button> | ||
|
|
||
| <button | ||
| onClick={startRecording} | ||
| className="btn btn-discrete" | ||
| title="Start Recording" | ||
| > | ||
| <Mic className="btn-icon" /> | ||
| </button> | ||
| </> | ||
| ) : null} | ||
|
|
||
| <button | ||
| onClick={uploadFile} | ||
| disabled={!selectedFile || loading} | ||
| className={`btn ${selectedFile ? "btn-primary" : "btn-disabled"}`} | ||
| onClick={recordedAudio ? processRecordedAudio : uploadFile} | ||
| disabled={(!selectedFile && !recordedAudio) || loading} | ||
| className={`btn ${(selectedFile || recordedAudio) && !loading ? "btn-discrete-prominent" : "btn-discrete"}`} | ||
| title={loading ? "Processing..." : "Process Audio"} | ||
| > | ||
| <Send className="btn-icon" /> | ||
| {loading ? "Transcribing..." : "Start Transcription"} | ||
| {((selectedFile || recordedAudio) && !loading) ? "Process Audio" : ""} | ||
| </button> | ||
| </div> | ||
|
|
||
| {isRecording && ( | ||
| <div className="recording-indicator"> | ||
| <div className="recording-dot"></div> | ||
| <span className="recording-time"> | ||
| {formatTime(recordingTime)} | ||
| </span> | ||
| </div> | ||
| )} | ||
| {isRecording && ( | ||
| <div className="recording-indicator"> | ||
| <div className={`recording-dot ${isPaused ? 'paused' : ''}`}></div> | ||
| <span className="recording-time"> | ||
| {formatTime(recordingTime)} {isPaused ? '(Paused)' : ''} | ||
| </span> | ||
| </div> | ||
| )} | ||
| </div> | ||
| </div> |
There was a problem hiding this comment.
The logic for rendering the control buttons is complex and can lead to a confusing user experience. For instance, both recording and upload options are available simultaneously after an action, which could lead to accidental data loss (e.g., starting a new recording when a file is staged for upload). Consider simplifying this into a more linear workflow. Refactoring the large conditional rendering block into smaller, state-specific components (e.g., <InitialControls />, <RecordingControls />) would also greatly improve the code's readability and maintainability.
Summary
This PR enhances user experience by automatically selecting a meeting after successful audio processing, ensuring the transcript appears immediately without requiring manual selection.
Changes Made
Context / Rationale
Previously, users had to manually click on a meeting button after audio processing to view the transcript. This created an unnecessary friction point in the user experience. This enhancement:
Related Docs or References
General Checklist