fix: Fix 3 critical bugs - voice tracking, session persistence, and status updates
BUG-1: Voice tracking not saving activities - Fix activity data format to match backend CreateActivityDto - Change 'timestamp' to 'startedAt' and 'data' to 'metadata' - Remove duplicate voice button from mobile TabBar BUG-2: Session persistence after revocation - Add logout() call when revoking all sessions - Add logout() call when removing all devices - Ensures user is logged out after session/device revocation - Clears tokens and redirects to login BUG-3: Voice modal status not updating - Set identifiedActivity before saving to show tracker name - Display "Adding to [tracker] tracker..." during save - Improves UX by showing which tracker is being updated 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -110,34 +110,7 @@ export const TabBar = () => {
|
||||
</BottomNavigation>
|
||||
</Paper>
|
||||
|
||||
{/* Voice Command Floating Button - Mobile Only */}
|
||||
<Fab
|
||||
color="secondary"
|
||||
aria-label="voice command"
|
||||
onClick={() => {
|
||||
// Trigger voice command - will integrate with existing VoiceFloatingButton
|
||||
const voiceButton = document.querySelector('[aria-label="voice input"]') as HTMLButtonElement;
|
||||
if (voiceButton) {
|
||||
voiceButton.click();
|
||||
}
|
||||
}}
|
||||
sx={{
|
||||
display: { xs: 'flex', md: 'none' },
|
||||
position: 'fixed',
|
||||
bottom: 40,
|
||||
left: '50%',
|
||||
transform: 'translateX(-50%)',
|
||||
zIndex: 1100,
|
||||
width: 56,
|
||||
height: 56,
|
||||
bgcolor: '#FF69B4',
|
||||
'&:hover': {
|
||||
bgcolor: '#FF1493',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Mic />
|
||||
</Fab>
|
||||
{/* Voice Command Floating Button is now centralized in layout.tsx - removed duplicate */}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -36,8 +36,10 @@ import {
|
||||
import { devicesApi, type DeviceInfo } from '@/lib/api/devices';
|
||||
import { motion } from 'framer-motion';
|
||||
import { useLocalizedDate } from '@/hooks/useLocalizedDate';
|
||||
import { useAuth } from '@/lib/auth/AuthContext';
|
||||
|
||||
export function DeviceTrustManagement() {
|
||||
const { logout } = useAuth();
|
||||
const { formatDistanceToNow } = useLocalizedDate();
|
||||
const [devices, setDevices] = useState<DeviceInfo[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
@@ -112,13 +114,16 @@ export function DeviceTrustManagement() {
|
||||
const response = await devicesApi.removeAllDevices();
|
||||
setSuccessMessage(`${response.removedCount} device(s) removed successfully`);
|
||||
setRemoveAllDialogOpen(false);
|
||||
await loadDevices();
|
||||
|
||||
// Logout the current user since all devices are removed
|
||||
// This terminates all sessions and redirects to login
|
||||
await logout();
|
||||
} catch (err: any) {
|
||||
console.error('Failed to remove all devices:', err);
|
||||
setError(err.response?.data?.message || 'Failed to remove devices');
|
||||
} finally {
|
||||
setIsRemovingAll(false);
|
||||
}
|
||||
// Note: Don't set isRemovingAll to false here, as we're logging out
|
||||
};
|
||||
|
||||
const getPlatformIcon = (platform?: string) => {
|
||||
|
||||
@@ -32,9 +32,11 @@ import {
|
||||
import { sessionsApi, type SessionInfo } from '@/lib/api/sessions';
|
||||
import { motion } from 'framer-motion';
|
||||
import { useLocalizedDate } from '@/hooks/useLocalizedDate';
|
||||
import { useAuth } from '@/lib/auth/AuthContext';
|
||||
|
||||
export function SessionsManagement() {
|
||||
const { formatDistanceToNow } = useLocalizedDate();
|
||||
const { logout } = useAuth();
|
||||
const [sessions, setSessions] = useState<SessionInfo[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@@ -91,13 +93,16 @@ export function SessionsManagement() {
|
||||
const response = await sessionsApi.revokeAllSessions();
|
||||
setSuccessMessage(`${response.revokedCount} session(s) revoked successfully`);
|
||||
setRevokeAllDialogOpen(false);
|
||||
await loadSessions();
|
||||
|
||||
// Logout the current user since all sessions are revoked
|
||||
// This clears tokens and redirects to login
|
||||
await logout();
|
||||
} catch (err: any) {
|
||||
console.error('Failed to revoke all sessions:', err);
|
||||
setError(err.response?.data?.message || 'Failed to revoke sessions');
|
||||
} finally {
|
||||
setIsRevokingAll(false);
|
||||
}
|
||||
// Note: Don't set isRevokingAll to false here, as we're logging out
|
||||
};
|
||||
|
||||
const getPlatformIcon = (platform?: string) => {
|
||||
|
||||
@@ -173,11 +173,12 @@ export function VoiceFloatingButton() {
|
||||
const childId = children[0].id;
|
||||
console.log('[Voice] Using child ID:', childId);
|
||||
|
||||
// Create the activity
|
||||
// Create the activity - match backend CreateActivityDto format
|
||||
const activityData = {
|
||||
type: activityType,
|
||||
timestamp: activityTimestamp || new Date().toISOString(),
|
||||
data: activityDetails,
|
||||
startedAt: activityTimestamp ? new Date(activityTimestamp).toISOString() : new Date().toISOString(),
|
||||
endedAt: activityDetails.endedAt || undefined,
|
||||
metadata: activityDetails,
|
||||
notes: activityDetails.notes || undefined,
|
||||
};
|
||||
|
||||
@@ -213,6 +214,7 @@ export function VoiceFloatingButton() {
|
||||
const handleApprove = async (data: any) => {
|
||||
try {
|
||||
setIsProcessing(true);
|
||||
setIdentifiedActivity(data.type); // Set the activity type for display
|
||||
setProcessingStatus('saving');
|
||||
setShowReview(false);
|
||||
|
||||
@@ -249,6 +251,7 @@ export function VoiceFloatingButton() {
|
||||
const handleEdit = async (editedData: any) => {
|
||||
try {
|
||||
setIsProcessing(true);
|
||||
setIdentifiedActivity(editedData.type); // Set the activity type for display
|
||||
setProcessingStatus('saving');
|
||||
setShowReview(false);
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user