Styled Paper - Copy this React, Tailwind Component to your project
Import React, { useState, useEffect, useRef } from 'react'; import { Button, Grid, TextField, Typography, Paper, Box, Select, MenuItem, FormControl, InputLabel, Snackbar, Alert, CircularProgress, Switch, FormControlLabel, IconButton, } from '@mui/material'; import { Mic, MicOff, CallEnd, Call, Save, PlayArrow, Stop } from '@mui/icons material'; import $ from 'jquery'; // Import jQuery import PropTypes from 'prop types'; // Global JabarPhone instance let phone = null; const InCalling = ({ closeModal, phoneNumber }) => { const [remark, setRemark] = useState(''); const [callID, setCallID] = useState(null); const [callStatus, setCallStatus] = useState('Idle'); const [audioDevices, setAudioDevices] = useState([]); const [selectedDevice, setSelectedDevice] = useState(''); const [callHistory, setCallHistory] = useState([]); const [isMuted, setIsMuted] = useState(false); const [isOnHold, setIsOnHold] = useState(false); const [isRecording, setIsRecording] = useState(false); const [recordedChunks, setRecordedChunks] = useState([]); const [snackbarOpen, setSnackbarOpen] = useState(false); const [loading, setLoading] = useState(false); const [darkMode, setDarkMode] = useState(false); const remoteAudioRef = useRef(null); const mediaRecorderRef = useRef(null); // Load Jabarphone SDK useEffect(() => { const loadJabarphoneSDK = () => { const script = document.createElement('script'); script.src = 'https://jabartech.com/sipplugin/sdk.js'; script.async = true; script.onload = () => { initializeJabarphone(); }; document.body.appendChild(script); return () => { document.body.removeChild(script); }; }; loadJabarphoneSDK(); }, []); const initializeJabarphone = () => { phone = new Jabarphone({ sipUsername: '7001', sipPassword: '51ee29602746eea9f7d91faf5bcedcbc', sipServer: 'voice.nuke.co.in', wssServer: 'wss://voice.nuke.co.in:8089/ws', userAgentString: 'WebRTC Agent', earlyMedia: true, }); phone.on('registration', (name) => { console.log('Registered with SIP server:', name); setCallStatus('Ready'); }); phone.on('call_status', handleCallStatusChange); phone.on('stream', handleStream); phone.init(); }; useEffect(() => { const detectAudioDevices = async () => { try { const devices = await navigator.mediaDevices.enumerateDevices(); const outputDevices = devices.filter( (device) => device.kind === 'audiooutput' ); setAudioDevices(outputDevices); } catch (error) { console.error('Error detecting audio devices:', error); } }; detectAudioDevices(); }, []); useEffect(() => { const fetchCallHistory = async () => { try { const response = await fetch('http://182.71.43.77:5500/api/call history/history'); if (!response.ok) throw new Error('Failed to fetch call history'); const data = await response.json(); setCallHistory(data); } catch (error) { console.error('Error fetching call history:', error); } }; fetchCallHistory(); }, []); const handleCallStatusChange = (data) => { console.log('Call Status:', data); setCallStatus(data.message); if (data.message) { setCallHistory((prev) => [ ...prev, { timestamp: new Date().toLocaleString(), status: data.message, duration: data.duration || 'N/A', // Add duration if available }, ]); } }; const handleStream = (stream) => { startRecording(stream); remoteAudioRef.current.srcObject = stream; // Set the remote audio stream }; const selectAudioDevice = async (deviceId) => { try { if (remoteAudioRef.current && remoteAudioRef.current.setSinkId) { await remoteAudioRef.current.setSinkId(deviceId); setSelectedDevice(deviceId); console.log(`Audio output device set to: ${deviceId}`); } } catch (error) { console.error('Error selecting audio device:', error); } }; console.log("Phone Number in InCalling:", phoneNumber); // Debugging line const makeCall = async () => { // Log the type and value of phoneNumber for debugging console.log('Phone Number Type:', typeof phoneNumber); console.log('Phone Number Value:', phoneNumber); // More robust type checking and conversion const numberToCall = phoneNumber && typeof phoneNumber === 'string' ? phoneNumber.trim() : (phoneNumber?.toString() || ''); // Validate phone number if (!numberToCall) { console.error("Invalid phone number"); setSnackbarOpen(true); return; } setLoading(true); try { const newCallID = phone.getUniqueID(); setCallID(newCallID); await phone.dial(numberToCall, newCallID); setCallStatus('Dialing'); } catch (error) { console.error('Error making call:', error); setSnackbarOpen(true); } finally { setLoading(false); } }; const answerCall = async () => { if (callID) { setLoading(true); try { await phone.answerCall(callID); setCallStatus('Connected'); } catch (error) { console.error('Error answering call:', error); setSnackbarOpen(true); } finally { setLoading(false); } } }; const endCall = async () => { if (callID) { setLoading(true); try { await phone.endCall(callID); setCallStatus('Ended'); // Post call record to the server await fetch('http://182.71.43.77:5500/api/call history/history', { method: 'POST', headers: { 'Content Type': 'application/json' }, body: JSON.stringify({ status: 'Ended', duration: '5 minutes', // Add duration logic dynamically remark: remark || 'No remark', }), }); } catch (error) { console.error('Error ending call:', error); setSnackbarOpen(true); } finally { setLoading(false); } } }; const downloadRecording = () => { if (recordedChunks.length) { const blob = new Blob(recordedChunks, { type: 'audio/webm' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = 'call_recording.webm'; document.body.appendChild(link); link.click(); document.body.removeChild(link); } }; const toggleHold = async () => { if (callID) { setLoading(true); try { const action = isOnHold ? 'unhold' : 'hold'; await phone.toggleHold(callID, action); setIsOnHold(!isOnHold); setCallStatus(isOnHold ? 'On Call' : 'On Hold'); } catch (error) { console.error(`Error toggling hold (${action}):`, error); } finally { setLoading(false); } } }; const toggleMute = async () => { if (callID) { setLoading(true); try { const action = isMuted ? 'unmute' : 'mute'; await phone.toggleMute(callID, action); setIsMuted(!isMuted); setCallStatus(isMuted ? 'Muted' : 'On Call'); } catch (error) { console.error(`Error toggling mute (${action}):`, error); } finally { setLoading(false); } } }; const clearCallHistory = () => { setCallHistory([]); }; const saveRemark = () => { if (remark.trim()) { console.log('Remark saved:', remark); setRemark(''); // Clear remark after saving } }; const handleSnackbarClose = () => { setSnackbarOpen(false); }; const toggleDarkMode = () => { setDarkMode(!darkMode); }; // Start recording the audio stream const startRecording = (stream) => { if (mediaRecorderRef.current) { mediaRecorderRef.current.stop(); // Stop any previous recording } // Check if stream is valid if (!stream) { console.error('No stream available to record.'); return; } const recorder = new MediaRecorder(stream); mediaRecorderRef.current = recorder; recorder.ondataavailable = (event) => { setRecordedChunks((prev) => [...prev, event.data]); // Collect recorded chunks }; recorder.onstop = () => { const blob = new Blob(recordedChunks, { type: 'audio/webm' }); const url = URL.createObjectURL(blob); const audio = new Audio(url); // Set the recorded URL for playback audio.play(); setRecordedChunks([]); // Reset chunks after playback }; recorder.start(); setIsRecording(true); }; const stopRecording = () => { if (mediaRecorderRef.current) { mediaRecorderRef.current.stop(); setIsRecording(false); } }; return ( <Paper elevation={3} sx={{ padding: 4, maxWidth: '800px', margin: 'auto', backgroundColor: darkMode ? '#424242' : '#fff', color: darkMode ? '#fff' : '#000' }}> <Typography variant="h4" gutterBottom> In Call Management </Typography> <Box display="flex" justifyContent="space between" alignItems="center"> <Typography variant="h6" color="text.secondary" gutterBottom> Status: {callStatus} </Typography> <FormControlLabel control={<Switch checked={darkMode} onChange={toggleDarkMode} />} label="Dark Mode" /> </Box> <Grid container spacing={2} alignItems="center"> <Grid item xs={6} sm={4} md={3}> <Button variant="contained" color="primary" fullWidth onClick={makeCall} disabled={loading}> {loading ? <CircularProgress size={24} color="inherit" /> : <Call />} Make Call </Button> </Grid> <Grid item xs={6} sm={4} md={3}> <Button variant="contained" color="success" fullWidth onClick={answerCall} disabled={loading}> {loading ? <CircularProgress size={24} color="inherit" /> : <Call />} Answer Call </Button> </Grid> <Grid item xs={6} sm={4} md={3}> <Button variant="contained" color="secondary" fullWidth onClick={toggleHold} disabled={loading}> {loading ? <CircularProgress size={24} color="inherit" /> : isOnHold ? 'Resume Call' : 'Hold Call'} </Button> </Grid> <Grid item xs={6} sm={4} md={3}> <Button variant="contained" color="warning" fullWidth onClick={toggleMute} disabled={loading}> {loading ? <CircularProgress size={24} color="inherit" /> : isMuted ? <MicOff /> : <Mic />} {isMuted ? 'Unmute' : 'Mute'} </Button> </Grid> <Grid item xs={6} sm={4} md={3}> <Button variant="contained" color="info" fullWidth onClick={isRecording ? stopRecording : () => {}}> {isRecording ? <Stop /> : <PlayArrow />} {isRecording ? 'Stop Recording' : 'Start Recording'} </Button> </Grid> <Grid item xs={6} sm={4} md={3}> <Button variant="contained" color="error" fullWidth onClick={endCall} disabled={loading}> {loading ? <CircularProgress size={24} color="inherit" /> : <CallEnd />} End Call </Button> </Grid> </Grid> <Box sx={{ mt: 4 }}> <FormControl fullWidth> <InputLabel id="audio device label">Audio Device</InputLabel> <Select labelId="audio device label" value={selectedDevice} onChange={(e) => selectAudioDevice(e.target.value)} > {audioDevices.map((device) => ( <MenuItem key={device.deviceId} value={device.deviceId}> {device.label || 'Unknown Device'} </MenuItem> ))} </Select> </FormControl> </Box> <Box sx={{ mt: 4 }}> <TextField label="Remark" multiline rows={3} fullWidth value={remark} onChange={(e) => setRemark(e.target.value)} /> <Button variant="contained" sx={{ mt: 2 }} onClick={saveRemark}> <Save /> Save Remark </Button> <Button variant="contained" onClick={downloadRecording} disabled={!recordedChunks.length}> Download Recording </Button> </Box> {/* Audio Elements */} <audio ref={remoteAudioRef} autoPlay style={{ display: 'none' }} /> {/* Call History */} <Box sx={{ mt: 4, bgcolor: 'background.paper', borderRadius: '8px', p: 2 }}> <Typography variant="h6" gutterBottom> Call History </Typography> {callHistory.length === 0 ? ( <Typography variant="body2" color="text.secondary"> No call history available. </Typography> ) : ( callHistory.map((entry, index) => ( <Box key={index} display="flex" justifyContent="space between" py={1} borderBottom="1px solid #e0e0e0"> <Typography variant="body2" color="text.secondary">{entry.timestamp}</Typography> <Typography variant="body1">{entry.status} {entry.duration && `| Duration: ${entry.duration}`}</Typography> </Box> )) )} </Box> {/* Snackbar for error messages */} <Snackbar open={snackbarOpen} autoHideDuration={6000} onClose={handleSnackbarClose}> <Alert onClose={handleSnackbarClose} severity="error" sx={{ width: '100%' }}> An error occurred during the call! </Alert> </Snackbar> </Paper> ); }; InCalling.propTypes = { closeModal: PropTypes.func.isRequired, phoneNumber: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]).isRequired }; export default InCalling;
