Files
VoiceStudio/frontend/src/components/NotificationPanel.jsx
T
debpalash a7b7e1f897 feat: flush dropdown, credentials tab, whisper model selector, reactive transcriptions
Flush Dropdown:
- Flush button now opens a dropdown showing all loaded models
- Each model shows device, VRAM usage, and individual Unload button
- Backend endpoints: GET /model/loaded, POST /model/unload/{id}
- Bottom actions: Flush caches, Unload all + flush

Credentials Tab:
- New Settings > Credentials tab with HF_TOKEN and TRANSLATE_API_KEY
- Session-scoped via POST /system/set-env (no ElevenLabs — we ARE the alternative)
- Shows 'Set' / 'Not set' badge for HF token

Notification Panel:
- Moved from header dropdown to footer status bar (4th tab: Notifications)
- Bell icon in header dispatches event to open footer tab
- Click notification → navigates to relevant page (e.g., Settings for HF token)
- No inline inputs — notifications are purely informational + navigational

Whisper Model Selector:
- Capture widget now has quality preset picker: tiny → large-v3
- Persisted in localStorage; sent to backend as 'model' form field
- Backend passes chosen model to ASR backend

Reactive Transcriptions:
- Custom window event (omni:transcription-added) bridges CaptureButton → TranscriptionsPage
- Page updates in realtime when new dictation completes
2026-04-28 12:33:10 +05:30

56 lines
1.6 KiB
React

/**
* NotificationPanel — bell icon in the header that opens the
* Notifications tab in the footer status bar.
*/
import React, { useState, useEffect, useCallback } from 'react';
import { Bell } from 'lucide-react';
import { API } from '../api/client';
import './NotificationPanel.css';
export default function NotificationPanel() {
const [count, setCount] = useState(0);
const [hasErrors, setHasErrors] = useState(false);
const [hasWarns, setHasWarns] = useState(false);
const fetchCount = useCallback(async () => {
try {
const res = await fetch(`${API}/system/notifications`);
if (res.ok) {
const data = await res.json();
const notifs = data.notifications || [];
setCount(notifs.length);
setHasErrors(notifs.some(n => n.level === 'error'));
setHasWarns(notifs.some(n => n.level === 'warn'));
}
} catch {
// Backend not ready
}
}, []);
useEffect(() => {
fetchCount();
const iv = setInterval(fetchCount, 30000);
return () => clearInterval(iv);
}, [fetchCount]);
const openNotifications = () => {
window.dispatchEvent(new CustomEvent('omni:open-notifications'));
};
return (
<button
className={`notif-trigger ${count > 0 ? 'notif-trigger--has-items' : ''}`}
onClick={openNotifications}
aria-label={`Notifications (${count})`}
title="Notifications"
>
<Bell size={14} />
{count > 0 && (
<span className={`notif-badge ${hasErrors ? '' : hasWarns ? 'notif-badge--warn' : ''}`}>
{count}
</span>
)}
</button>
);
}