purityselect/resources/js/layouts/components/QueueComponent.vue
2024-10-25 01:05:27 +05:00

2302 lines
96 KiB
Vue

<script setup>
import PatientLabKit from '@/pages/patient-lab-kit-order.vue';
import PatientPrescription from '@/pages/PrescriptionPatient.vue';
import axios from '@axios';
import img from '@images/avatars/avatar-1577909_1280.png';
import upgradeBannerDark from '@images/pro/upgrade-banner-dark.png';
import upgradeBannerLight from '@images/pro/upgrade-banner-light.png';
import Echo from 'laravel-echo';
import moment from 'moment-timezone';
import Pusher from 'pusher-js';
import { defineEmits, defineExpose } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useTheme } from 'vuetify';
import { useStore } from 'vuex';
const router = useRouter()
const route = useRoute()
const store = useStore()
const queueUsers = ref([]);
const usertime = ref(null);
const isMuted = ref(false);
const isMicrophoneAllowed = ref(null);
const profileName = ref();
const isCameraAllowed = ref(false);
const toggelUserValue = ref(null)
const isMobile = ref(window.innerWidth <= 768);
const isLoadingVisible = ref(false);
const dialogNote = ref(false)
const messageNote = ref('')
const dialogLab = ref(false)
const labNote = ref('')
const dialogDocument = ref(false)
const documentNote = ref('')
const labOrderModel = ref(false)
const prescriptionModel = ref(false)
const prescriptionModelForm = ref(false)
const selectedMedicines = ref([]);
const medicines = ref('')
const brand = ref('')
const from = ref('')
const dosage = ref('')
const quantity = ref('')
const direction_quantity = ref('')
const direction_one = ref('')
const direction_two = ref('')
const refil_quantity = ref('')
const dont_substitute = ref('')
const comments = ref('')
const search = ref('');
const loading = ref(true);
const page = ref(1);
const itemsPerPage = ref(10);
const pageCount = ref(0);
const itemsPrescriptions = ref([]);
const prescription_id = ref([]);
const OrderDetail = ref();
const filteredOrders = ref([]);
const ordersItems = ref([]);
const patientOrderId = ref();
const isPrescriptionModel = ref();
const permissionModel = ref(false)
const propsCom = defineProps({
isVisable: {
type: Boolean,
required: true,
},
});
const headerOrders = [
{
title: "Product",
key: "title",
},
{
title: "Price",
key: "price",
},
{
title: "Quantity",
key: "quantity",
},
{
title: "Total",
key: "total",
sortable: false,
},
{
title: "status",
key: "status",
},
];
const headers = [
{ key: 'name', title: 'Name' },
{ key: 'brand', title: 'Brand' },
{ key: 'from', title: 'From' },
{ key: 'direction_quantity', title: 'Direction Quantity' },
{ key: 'dosage', title: 'Dosage' },
{ key: 'quantity', title: 'Quantity' },
{ key: 'refill_quantity', title: 'Refill Quantity' },
{ key: 'actions', title: 'Action' },
];
const testKits = computed(async () => {
let Calluser = localStorage.getItem('call_user')
let patient = JSON.parse(Calluser)
await store.dispatch('getLabKitProductList', {})
console.log(store.getters.getLabOrderProductList)
state.testKits = store.getters.getLabOrderProductList
});
const state = reactive({
addLabOrder: false,
selectedTestKitId: null,
valid: false,
testKits: [],
});
const labTests = ref(['GLP-1', 'TRT', 'Cholesterol', 'Glucose', 'Vitamin D'])
const documentRules = [
v => !!v || 'Document Note is required',
];
const labRules = [
v => !!v || 'Lab Request Note is required',
];
const messageRules = [
v => !!v || 'Notes is required',
];
const valid = ref(false);
const form = ref(null);
// Components
import { computed } from 'vue';
const vuetifyTheme = useTheme()
const userRole = localStorage.getItem('user_role'); // Fetch user role from local storage
const isPatient = computed(() => userRole.toLowerCase() === 'patient');
const isAgent = computed(() => userRole.toLowerCase() === 'agent');
const isConferncePage = computed(() => store.getters.getCurrentPage === '/provider/telehealth');
const isConferncePageForPatient = computed(() => store.getters.getCurrentPage === '/queue');
//const currentRout = computed(() => localStorage.getItem('getCurrentPage'));queue');
const isVisable = computed(() => propsCom.isVisable);
const getUserId = ref(null)
const upgradeBanner = computed(() => {
return vuetifyTheme.global.name.value === 'light' ? upgradeBannerLight : upgradeBannerDark
})
const isCameraPermissionGranted = ref(false);
const isMicrophonePermissionGranted = ref(false);
const getIsTonalSnackbarVisible = ref(false);
const errorMessage = ref(null);
const appointmentUsers = ref([]);
const getFieldRules = (fieldName, errorMessage) => {
if (fieldName) {
return [
v => !!v || `${errorMessage}`,
// Add more validation rules as needed
];
}
};
// const checkPermissions = async () => {
// try {
// const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
// if (stream) {
// isCameraPermissionGranted.value = true;
// isMicrophonePermissionGranted.value = true;
// }
// } catch (error) {
// getIsTonalSnackbarVisible.value = true;
// errorMessage.value = "We can't access your camera and microphone.";
// console.error('Error accessing media devices:', error);
// }
// };
const emit = defineEmits(['formSubmitted', 'addPrescription']);
const checkMicrophonePermission = async () => {
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
try {
// Ask for camera permission
const videoPermissionResponse = await navigator.mediaDevices.getUserMedia({ video: true });
if (videoPermissionResponse) {
isCameraPermissionGranted.value = true;
store.dispatch('updateIsCamEnabled', true);
console.log('Camera permission granted.');
// Immediately stop using the camera to avoid actually turning it on
videoPermissionResponse.getVideoTracks().forEach(track => track.stop());
}
} catch (error) {
console.error('Error requesting camera permissions:', error);
if (error.name === 'NotAllowedError') {
console.log('Camera permissions were not granted.');
}
}
try {
// Ask for microphone permission
const audioPermissionResponse = await navigator.mediaDevices.getUserMedia({ audio: true });
if (audioPermissionResponse) {
isMicrophonePermissionGranted.value = true;
store.dispatch('updateIsMicEnabled', true);
store.dispatch("updateIsTonalSnackbar", false)
store.dispatch("updateErrorMessage", null)
permissionModel.value = false
console.log('Microphone permission granted.');
// Immediately stop using the microphone to avoid actually turning it on
audioPermissionResponse.getAudioTracks().forEach(track => track.stop());
}
} catch (error) {
console.error('Error requesting microphone permissions:', error);
if (error.name === 'NotAllowedError') {
console.log('Microphone permissions were not granted.');
}
}
} else {
console.error('navigator.mediaDevices is not supported in this browser.');
}
if (!store.getters.getIsMicEnabled && !store.getters.getIsCamEnabled) {
store.dispatch("updateIsTonalSnackbar", true)
store.dispatch("updateErrorMessage", "To proceed, please enable camera and microphone permissions in your browser's settings.")
permissionModel.value = true
return
}
// try {
// const permissionStatus = await navigator.permissions.query({ name: 'microphone' });
// console.log('permissionStatus', permissionStatus)
// isMicrophoneAllowed.value = permissionStatus.state === 'granted';
// // Check camera permission
// const cameraPermission = await navigator.permissions.query({ name: 'camera' });
// console.log('Camera permission status:', cameraPermission.state);
// isCameraAllowed.value = cameraPermission.state === 'granted';
// if (permissionStatus.state === 'granted') {
// isMuted.value = true
// }
// if (cameraPermission.state === 'granted') {
// isCameraAllowed.valu = true
// }
// } catch (error) {
// console.error('Error checking microphone permission:', error);
// }
};
const enablePermission = async () => {
await checkMicrophonePermission();
await nextTick()
if (store.getters.getIsMicEnabled && store.getters.getIsCamEnabled) {
store.dispatch("updateIsTonalSnackbar", false)
store.dispatch("updateErrorMessage", null)
permissionModel.value = false
}
};
const drawer = ref(null);
const toggleDrawer = (user) => {
toggelUserValue.value = user
console.log(toggelUserValue.value)
drawer.value = !drawer.value;
};
const isPrescriptionClick = () => {
isPrescriptionModel.value = true;
}
const closeDrawer = () => {
drawer.value = false;
};
const states = ref([
{ name: 'Howland Island', abbreviation: 'UM-84' },
{ name: 'Delaware', abbreviation: 'DE' },
{ name: 'Maryland', abbreviation: 'MD' },
{ name: 'Baker Island', abbreviation: 'UM-81' },
{ name: 'Kingman Reef', abbreviation: 'UM-89' },
{ name: 'New Hampshire', abbreviation: 'NH' },
{ name: 'Wake Island', abbreviation: 'UM-79' },
{ name: 'Kansas', abbreviation: 'KS' },
{ name: 'Texas', abbreviation: 'TX' },
{ name: 'Nebraska', abbreviation: 'NE' },
{ name: 'Vermont', abbreviation: 'VT' },
{ name: 'Jarvis Island', abbreviation: 'UM-86' },
{ name: 'Hawaii', abbreviation: 'HI' },
{ name: 'Guam', abbreviation: 'GU' },
{ name: 'United States Virgin Islands', abbreviation: 'VI' },
{ name: 'Utah', abbreviation: 'UT' },
{ name: 'Oregon', abbreviation: 'OR' },
{ name: 'California', abbreviation: 'CA' },
{ name: 'New Jersey', abbreviation: 'NJ' },
{ name: 'North Dakota', abbreviation: 'ND' },
{ name: 'Kentucky', abbreviation: 'KY' },
{ name: 'Minnesota', abbreviation: 'MN' },
{ name: 'Oklahoma', abbreviation: 'OK' },
{ name: 'Pennsylvania', abbreviation: 'PA' },
{ name: 'New Mexico', abbreviation: 'NM' },
{ name: 'American Samoa', abbreviation: 'AS' },
{ name: 'Illinois', abbreviation: 'IL' },
{ name: 'Michigan', abbreviation: 'MI' },
{ name: 'Virginia', abbreviation: 'VA' },
{ name: 'Johnston Atoll', abbreviation: 'UM-67' },
{ name: 'West Virginia', abbreviation: 'WV' },
{ name: 'Mississippi', abbreviation: 'MS' },
{ name: 'Northern Mariana Islands', abbreviation: 'MP' },
{ name: 'United States Minor Outlying Islands', abbreviation: 'UM' },
{ name: 'Massachusetts', abbreviation: 'MA' },
{ name: 'Connecticut', abbreviation: 'CT' },
{ name: 'Florida', abbreviation: 'FL' },
{ name: 'District of Columbia', abbreviation: 'DC' },
{ name: 'Midway Atoll', abbreviation: 'UM-71' },
{ name: 'Navassa Island', abbreviation: 'UM-76' },
{ name: 'Indiana', abbreviation: 'IN' },
{ name: 'Wisconsin', abbreviation: 'WI' },
{ name: 'Wyoming', abbreviation: 'WY' },
{ name: 'South Carolina', abbreviation: 'SC' },
{ name: 'Arkansas', abbreviation: 'AR' },
{ name: 'South Dakota', abbreviation: 'SD' },
{ name: 'Montana', abbreviation: 'MT' },
{ name: 'North Carolina', abbreviation: 'NC' },
{ name: 'Palmyra Atoll', abbreviation: 'UM-95' },
{ name: 'Puerto Rico', abbreviation: 'PR' },
{ name: 'Colorado', abbreviation: 'CO' },
{ name: 'Missouri', abbreviation: 'MO' },
{ name: 'New York', abbreviation: 'NY' },
{ name: 'Maine', abbreviation: 'ME' },
{ name: 'Tennessee', abbreviation: 'TN' },
{ name: 'Georgia', abbreviation: 'GA' },
{ name: 'Louisiana', abbreviation: 'LA' },
{ name: 'Nevada', abbreviation: 'NV' },
{ name: 'Iowa', abbreviation: 'IA' },
{ name: 'Idaho', abbreviation: 'ID' },
{ name: 'Rhode Island', abbreviation: 'RI' },
{ name: 'Washington', abbreviation: 'WA' },
{ name: 'Ohio', abbreviation: 'OH' },
// ... (add the rest of the states)
]);
const registrationTimeAgo = (time) => {
const currentDate = new Date();
const registrationDate = new Date(time * 1000);
const timeDifference = currentDate - registrationDate;
const seconds = Math.floor(timeDifference / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
if (seconds < 60) {
return `less than minute ago`;
} else if (minutes < 60) {
return `${minutes} minute${minutes !== 1 ? 's' : ''} ago`;
} else if (hours < 24) {
return `${hours} hour${hours !== 1 ? 's' : ''} ago`;
} else if (days < 7) {
return `${days} day${days !== 1 ? 's' : ''} ago`;
}
}
const selectedStateName = (stateName) => {
console.log('queueUsers', stateName);
const selectedState = states.value.find(s => s.abbreviation === stateName);
return selectedState ? selectedState.name : '';
};
const relativeTimeFromDate = (dateString, timeString) => {
// Ensure the input is treated as local time explicitly
const eventDateTime = new Date(dateString + "T" + timeString);
// Get the current date and time
const now = new Date();
// Calculate the difference in milliseconds between the event time and now
const diffMs = eventDateTime - now;
// Convert the difference to an absolute value
const absDiffMs = Math.abs(diffMs);
// Calculate differences in days, hours, and minutes
const minutes = Math.floor(absDiffMs / 60000) % 60;
const hours = Math.floor(absDiffMs / 3600000) % 24;
const days = Math.floor(absDiffMs / 86400000);
// Determine the appropriate suffix based on whether the date is in the future or past
const suffix = diffMs < 0 ? " ago" : " left";
// Result formulation based on the above logic
if (days > 0) {
return `${days} day${days > 1 ? 's' : ''}${suffix}`;
} else {
// Compose hours and minutes
let result = [];
if (hours > 0) {
result.push(`${hours} Hr${hours > 1 ? 's' : ''}`);
}
if (minutes > 0) {
result.push(`${minutes} Min${minutes > 1 ? 's' : ''}`);
}
if (result.length === 0) {
return 'just now';
}
return result.join(' ') + suffix;
}
}
const updateTimeAgoForQueueUsers = () => {
console.log('update time')
queueUsers.value.forEach(user => {
let appointmentDate = convertUtcDateTimeToLocal(user.appointment.appointment_date, user.appointment.appointment_time, 'date')
let appointmentTime = convertUtcDateTimeToLocal(user.appointment.appointment_date, user.appointment.appointment_time, 'time')
user.waitingTime = relativeTimeFromDate(appointmentDate, appointmentTime);
});
console.log('queueUsers update time', queueUsers.value)
}
const updateStatusById = (users, id, newStatus, appointment_date, appointment_time, time, waitingTime, mic, cam) => {
console.log('---->>>>', appointment_date, appointment_time)
users.forEach(user => {
if (user.id === id) {
user.status = newStatus;
if (appointment_date) { user.appointment_date = appointment_date; }
if (appointment_time) { user.appointment_time = appointment_time; }
if (time) { user.time = time; }
if (waitingTime) {
// user.waitingTime = waitingTime
// let appointmentDate = convertUtcDateTimeToLocal(appointment_date, appointment_time, 'date')
// let appointmentTime = convertUtcDateTimeToLocal(appointment_date, appointment_time, 'time')
// user.waitingTime = relativeTimeFromDate(appointmentDate, appointmentTime);
}
if (mic) { user.mic = mic; }
if (cam) { user.cam = cam; }
}
});
};
const convertUtcDateTimeToLocal = (utcDate, utcTime, type) => {
const utcDateTime = `${utcDate}T${utcTime}Z`; // Use Z to denote UTC timezone explicitly
const momentObj = moment.utc(utcDateTime).local(); // Convert UTC to local time
if (type === 'date') {
return momentObj.format('YYYY-MM-DD'); // Return local date
} else if (type === 'time') {
return momentObj.format('HH:mm:ss'); // Return local time
} else {
throw new Error("Invalid type specified. Use 'date' or 'time'.");
}
};
onMounted(async () => {
// console.log("router", store.getters.getCurrentPage)
if (userRole === 'agent') {
store.dispatch("updateIsLoading", true);
await getprescription()
await getAppointmentUsers();
// await store.dispatch("orderDetailAgent", {
// id: route.params.id,
// });
// filteredOrders.value = store.getters.getPatientOrderDetail;
// await store.dispatch("orderAgentList");
// filteredOrders.value = store.getters.getPatientOrderList;
// console.log("orders Call", filteredOrders.value);
// store.dispatch("updateIsLoading", false);
}
window.addEventListener('resize', checkMobile);
window.Pusher = Pusher;
const key = 'bc8bffbbbc49cfa39818';
const cluster = 'mt1';
let echo = new Echo({
broadcaster: 'pusher',
key: key,
cluster: cluster,
forceTLS: true,
auth: {
headers: {
Authorization: 'Bearer ' + localStorage.getItem('access_token'),
},
},
});
//presence channel ((user bio show))
if (userRole === 'agent') {
if (appointmentUsers.value && appointmentUsers.value.length > 0) {
queueUsers.value = appointmentUsers.value
queueUsers.value.forEach((appUser, index) => {
let appointmentDate = convertUtcDateTimeToLocal(appUser.appointment.appointment_date, appUser.appointment.appointment_time, 'date')
let appointmentTime = convertUtcDateTimeToLocal(appUser.appointment.appointment_date, appUser.appointment.appointment_time, 'time')
appUser.status = 'inactive'
appUser.waitingTime = relativeTimeFromDate(appointmentDate, appointmentTime);
appUser.state = selectedStateName(appUser.state);
appUser.mic = false;
appUser.cam = false;
})
}
const presenceChannel = echo.join(`dhkjkiplqe84sdaqf17nqg`)
window.presenceChannel = presenceChannel
presenceChannel.here((users) => { //existing user/ including current user
users.forEach((user, index) => {
if (user.type === 'patient') {
presenceChannel.whisper('getPermission-' + user.id, { user_id: localStorage.getItem('agent_id') })
console.log('---User Data', user)
usertime.value = user.time;
let appointmentDate = convertUtcDateTimeToLocal(user.appointment.appointment_date, user.appointment.appointment_time, 'date')
let appointmentTime = convertUtcDateTimeToLocal(user.appointment.appointment_date, user.appointment.appointment_time, 'time')
user.waitingTime = relativeTimeFromDate(appointmentDate, appointmentTime);
user.state = selectedStateName(user.state);
user.status = 'active'
}
});
let channelUsers = users.filter(user => user.type == "patient");
console.log('channelUsers', channelUsers)
channelUsers.forEach((chanUser, index) => {
let existUser = queueUsers.value.filter(appUser => appUser.id == chanUser.id)
if (!existUser) {
queueUsers.value.push(chanUser)
} else {
let appointmentDate = convertUtcDateTimeToLocal(chanUser.appointment.appointment_date, chanUser.appointment.appointment_time, 'date')
let appointmentTime = convertUtcDateTimeToLocal(chanUser.appointment.appointment_date, chanUser.appointment.appointment_time, 'time')
updateStatusById(queueUsers.value, chanUser.id, "active", appointmentDate, appointmentTime, chanUser.time, relativeTimeFromDate(appointmentDate, appointmentTime), false, false);
}
})
sortQueueUsers();
console.log("-----------alluser", queueUsers.value);
})
.joining((user) => {
//new user
console.log('joining', user);
console.log('Happy');
if (user.type !== "patient")
return
usertime.value = user.time;
let appointmentDate = convertUtcDateTimeToLocal(user.appointment.appointment_date, user.appointment.appointment_time, 'date')
let appointmentTime = convertUtcDateTimeToLocal(user.appointment.appointment_date, user.appointment.appointment_time, 'time')
user.waitingTime = relativeTimeFromDate(appointmentDate, appointmentTime);
user.state = selectedStateName(user.state);
user.status = 'active'
let channelUser = []
channelUser.push(user);
// console.log('---channelUser---', user)
// console.log('---channelUserArray---', channelUser, queueUsers.value)
channelUser.forEach((chanUser, index) => {
let existUser = queueUsers.value.filter(appUser => appUser.id == chanUser.id)
console.log('---existUser---', existUser, existUser.length)
if (!existUser || existUser.length == 0) {
console.log('if', chanUser)
queueUsers.value.push(user);
} else {
console.log('else', chanUser)
let appointmentDate = convertUtcDateTimeToLocal(chanUser.appointment.appointment_date, chanUser.appointment.appointment_time, 'date')
let appointmentTime = convertUtcDateTimeToLocal(chanUser.appointment.appointment_date, chanUser.appointment.appointment_time, 'time')
updateStatusById(queueUsers.value, chanUser.id, "active", appointmentDate, appointmentTime, chanUser.time, relativeTimeFromDate(appointmentDate, appointmentTime), false, false);
}
})
console.log('---JoiningFinalUser---', queueUsers.value)
sortQueueUsers();
})
.leaving((user) => {
console.log('leaving', user.name);
queueUsers.value = queueUsers.value.filter(u => u.id !== user.id);
console.log("CurrentUser", queueUsers);
if (user.type != "patient")
return
})
.listenForWhisper('DeviceCurrentStatus', (e, data) => {
if (e && data) {
let micVal = e.mic
let camVal = e.cam
let userIdVal = data.user_id
console.log('my custom data ====', e, data.user_id, userIdVal)
let filteredUsers = queueUsers.value.filter(user =>
user.id == userIdVal
)
.map(user => ({
...user,
status: 'active',
mic: micVal, // dynamically setting mic status
cam: camVal // dynamically setting cam status
}));
// queueUsers.value = []
filteredUsers.forEach((chanUser, index) => {
let existUser = queueUsers.value.filter(appUser => appUser.id == chanUser.id)
if (!existUser) {
console.log('if', chanUser)
queueUsers.value.push(chanUser)
} else {
console.log('else', chanUser)
let appointmentDate = convertUtcDateTimeToLocal(chanUser.appointment.appointment_date, chanUser.appointment.appointment_time, 'date')
let appointmentTime = convertUtcDateTimeToLocal(chanUser.appointment.appointment_date, chanUser.appointment.appointment_time, 'time')
updateStatusById(queueUsers.value, chanUser.id, "active", appointmentDate, appointmentTime, chanUser.time, relativeTimeFromDate(appointmentDate, appointmentTime), micVal, camVal);
}
})
sortQueueUsers();
// queueUsers.value = filteredUsers
console.log('-------filteredUsers For', filteredUsers);
}
})
.listenForWhisper('DeviceCurrentStatus-' + localStorage.getItem('agent_id'), (e, data) => {
if (e && data) {
let micVal = e.mic
let camVal = e.cam
let userIdVal = data.user_id
console.log('my custom data ====', e, data.user_id, userIdVal, queueUsers.value)
let filteredUsers = queueUsers.value.filter(user =>
user.id == userIdVal
)
.map(user => ({
...user,
status: 'active',
mic: micVal, // dynamically setting mic status
cam: camVal // dynamically setting cam status
}));
console.log('-------filteredUsers', filteredUsers);
// queueUsers.value = []
filteredUsers.forEach((chanUser, index) => {
let existUser = queueUsers.value.filter(appUser => appUser.id == chanUser.id)
if (!existUser) {
console.log('if', chanUser)
queueUsers.value.push(chanUser)
} else {
console.log('else', chanUser)
let appointmentDate = convertUtcDateTimeToLocal(chanUser.appointment.appointment_date, chanUser.appointment.appointment_time, 'date')
let appointmentTime = convertUtcDateTimeToLocal(chanUser.appointment.appointment_date, chanUser.appointment.appointment_time, 'time')
updateStatusById(queueUsers.value, chanUser.id, "active", chanUser.appointment_date, chanUser.appointment_time, chanUser.time, relativeTimeFromDate(appointmentDate, appointmentTime), micVal, camVal);
}
})
sortQueueUsers();
// queueUsers.value = filteredUsers
console.log('-------finalUsers', queueUsers.value);
}
})
.error((error) => {
console.error(error);
});
setInterval(updateTimeAgoForQueueUsers, 60000);
}
const access_token = localStorage.getItem('access_token');
if (userRole == 'agent') {
await axios.post('/agent/api/agent-profile', {
headers: {
'Authorization': `Bearer ${access_token}`,
}
})
.then(response => {
console.log('ResponseTest:', response.data);
if (response.data) {
let patientData = response.data.ai_switch;
profileName.value = patientData.name;
// console.log("profileName", profileName.value);
} else {
isLoadingVisible.value = false;
}
})
.catch(error => {
console.error('Error:', error);
});
}
if (isAgent.value)
await checkMicrophonePermission();
});
const sortQueueUsers = () => {
queueUsers.value.sort((a, b) => {
// First, compare by status (active users come first)
if (a.status === 'active' && b.status !== 'active') {
return -1;
} else if (a.status !== 'active' && b.status === 'active') {
return 1;
}
// Then, if statuses are the same, compare by id (for ascending order)
return a.id - b.id;
});
};
const joinCall = async (user, type) => {
console.log('Join call');
isLoadingVisible.value = true;
let patient_id = user.id
localStorage.setItem('call_user', JSON.stringify(user));
localStorage.setItem('patient_id', patient_id);
let appointment_id = user.appointment.id
const agent_id = localStorage.getItem('agent_id');
await checkMicrophonePermission()
console.log(isCameraPermissionGranted.value, isMicrophonePermissionGranted.value)
if (!store.getters.getIsMicEnabled && !store.getters.getIsCamEnabled) {
store.dispatch("updateIsTonalSnackbar", true)
store.dispatch("updateErrorMessage", "To proceed, please enable camera and microphone permissions in your browser's settings.")
permissionModel.value = true
return
}
if (!isCameraPermissionGranted.value && !isMicrophonePermissionGranted.value) {
isLoadingVisible.value = false;
getIsTonalSnackbarVisible.value = true;
errorMessage.value = "We can't access your camera and microphone.";
} else {
isLoadingVisible.value = true;
localStorage.setItem('call_type', type);
const access_token = localStorage.getItem('access_token');
console.log(access_token);
await axios.post('/agent/api/start-call/' + patient_id + '/' + agent_id + '/' + appointment_id, {
call_type: type,
}, {
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${access_token}`
}
})
.then(response => {
console.log("Start Call", response.data);
localStorage.setItem('agent_meeting_id', response.data.meeting_id);
localStorage.setItem('patient_appiontment_id', response.data.appointment_id);
console.log('Aegnt_meetingId', response.data.meeting_id);
store.dispatch('updateCurrentCallPatient', patient_id)
store.dispatch('updateCallToken', response.data.meeting_id)
store.dispatch('updateCallType', type)
store.dispatch('updateCallStarted', true)
store.dispatch('updateCurrentPage', '/provider/telehealth')
router.push('/provider/telehealth');
isLoadingVisible.value = false;
})
.catch(error => {
console.error(error.response.data);
isLoadingVisible.value = false;
});
}
}
const convertUtcTime = (time, date, timezone) => {
const timezones = {
"EST": "America/New_York",
"CST": "America/Chicago",
"MST": "America/Denver",
"PST": "America/Los_Angeles",
// Add more mappings as needed
};
// Get the IANA timezone identifier from the abbreviation
const ianaTimeZone = timezones[timezone];
if (!ianaTimeZone) {
throw new Error(`Unknown timezone abbreviation: ${timezone}`);
}
// Combine date and time into a single string
const dateTimeString = `${date}T${time}Z`; // Assuming the input date and time are in UTC
// Create a Date object from the combined string
const dateObj = new Date(dateTimeString);
// Options for the formatter
const options = {
timeZone: ianaTimeZone,
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false,
};
// Create the formatter
const formatter = new Intl.DateTimeFormat('en-US', options);
// Format the date
const convertedDateTime = formatter.format(dateObj);
return convertedDateTime;
};
const getConvertedTime = (inputDate) => {
// Split the input date string into date and time components
const [datePart, timePart] = inputDate.split(', ');
// Split the time component into hours, minutes, and seconds
let [hours, minutes, seconds] = timePart.split(':');
// Convert the hours to an integer
hours = parseInt(hours);
// Determine the period (AM/PM) and adjust the hours if necessary
const period = hours >= 12 ? 'PM' : 'AM';
hours = hours % 12 || 12; // Convert 0 and 12 to 12, and other hours to 1-11
// Format the time as desired
const formattedTime = `${hours.toString().padStart(2, '0')}:${minutes}${period}`;
return formattedTime;
}
const getConvertedDate = (inputDate) => {
// Split the input date string into date and time components
const [datePart, timePart] = inputDate.split(', ');
// Split the date component into month, day, and year
const [month, day, year] = datePart.split('/');
// Create a new Date object from the parsed components
const dateObject = new Date(`${year}-${month}-${day}T${timePart}`);
// Define an array of month names
const monthNames = [
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
];
// Format the date as desired
const formattedDate = `${monthNames[dateObject.getMonth()]} ${day}, ${year}`;
return formattedDate;
};
const formatDateDate = (date) => {
const messageDate = new Date(date);
const options = {
year: 'numeric',
month: 'long',
day: 'numeric',
};
return messageDate.toLocaleDateString('en-US', options).replace(/\//g, '-');
};
const timeFormate = (timeData) => {
let timeZone = '';
const time = timeData;
return moment.tz(time, "HH:mm:ss", timeZone).format("hh:mm A");
}
const checkMobile = () => {
isMobile.value = window.innerWidth <= 768;
};
const openDialog = (user, type) => {
console.log('userId', user)
toggelUserValue.value = user
getUserId.value = user.id
let patient_id = user.id
localStorage.setItem('call_user', JSON.stringify(user));
localStorage.setItem('patient_id', patient_id);
if (type == 'Notes') {
dialogNote.value = true
}
if (type == 'Request For Documents') {
dialogDocument.value = true
}
if (type == 'Request For Lab') {
dialogLab.value = true
}
if (type == "Lab Order") {
console.log('enter ne value')
labOrderModel.value = true
}
if (type == "Prescription") {
console.log('enter ne value')
prescriptionModel.value = true
}
if (type == "Prescription Form") {
console.log('enter ne value')
prescriptionModelForm.value = true
}
if (type == "Add Lab Order") {
console.log('enter ne value')
state.addLabOrder = true
}
};
const submitFormNote = async () => {
if (form.value.validate()) {
// Submit form data
valid.value = true
await store.dispatch('submitFormNote', {
note: messageNote.value,
note_type: "Notes",
patient_id: getUserId.value,
appointment_id: toggelUserValue.value.appointment.id
})
await store.dispatch('getHistoryPatientNotes', {
patient_id: getUserId.value,
appointment_id: toggelUserValue.value.appointment.id,
})
console.log('Form submitted!', { note: messageNote.value });
dialogNote.value = false; // Close the modal
messageNote.value = ''
valid.value = false
emit('formSubmitted', {
notes: store.getters.getPatientNotes,
});
}
};
const submitFormDocumentNote = async () => {
if (form.value.validate()) {
await store.dispatch('submitFormNote', {
note: documentNote.value,
note_type: "Request For Documents",
patient_id: getUserId.value,
appointment_id: toggelUserValue.value.appointment.id
})
console.log('Form submitted!', { note: documentNote.value });
dialogDocument.value = false; // Close the modal
documentNote.value = ''
}
};
const submitFormLabNote = async () => {
if (form.value.validate()) {
await store.dispatch('submitFormNote', {
note: labNote.value,
note_type: "Request For Lab",
patient_id: getUserId.value
})
console.log('Form submitted!', { note: labNote.value });
dialogLab.value = false; // Close the modal
labNote.value = ''
}
};
const prescriptionForm = async () => {
console.log('toggelUserValue.value.', prescription_id.value)
// isLoadingVisible.value = true
if (form.value.validate()) {
// Get the values of all the form fields
isLoadingVisible.value = true
const formData = {
medicines: medicines.value,
patient_id: getUserId.value,
prescription_id: prescription_id.value,
appointment_id: toggelUserValue.value.appointment.id,
brand: brand.value,
from: from.value,
dosage: dosage.value,
quantity: quantity.value,
direction_quantity: direction_quantity.value,
direction_one: direction_one.value,
direction_two: direction_two.value,
refill_quantity: refil_quantity.value,
dont_substitute: dont_substitute.value,
comments: comments.value
};
await axios.post('/agent/api/store-patient-prescriptions', formData, {
headers: {
'Authorization': `Bearer ${localStorage.getItem('access_token')}`,
}
}).then(response => {
isLoadingVisible.value = false
prescriptionModel.value = false
medicines.value = ''
prescription_id.value = ''
brand.value = ""
from.value = ""
dosage.value = ""
quantity.value = ""
direction_quantity.value = ""
direction_one.value = ""
direction_two.value = ""
refil_quantity.value = ""
dont_substitute.value = ""
comments.value = ""
console.log('Response:', response.data);
})
.catch(error => {
isLoadingVisible.value = false
console.error('Error:', error);
});
console.log('Form data:', toggelUserValue.value.appointment.id);
await store.dispatch('getPrescriptions', {
patient_id: getUserId.value,
appointment_id: toggelUserValue.value.appointment.id
})
emit('addPrescription', {
getPrescriptionList: store.getters.getPrescriptionList,
});
}
};
const storeTestKit = async () => {
let Calluser = localStorage.getItem('call_user')
let patient = JSON.parse(Calluser)
await store.dispatch('saveLabOrderProductList', {
labkit: state.selectedTestKitId,
patient_id: patient.id
})
console.log('Selected Test Kit:', state.selectedTestKitId);
state.addLabOrder = false;
state.selectedTestKitId = ''
};
const getStatusColor = (status) => {
switch (status) {
case "pending":
return "orange";
case "Shipped":
return "blue";
case "Delivered":
return "green";
case "Cancelled":
return "red";
default:
return "gray";
}
};
const selectedItem = async (item) => {
console.log(item)
medicines.value = item.name
brand.value = item.brand
dosage.value = item.dosage
dosage.value = item.dosage
from.value = item.from
quantity.value = item.quantity
direction_quantity.value = item.direction_quantity
refil_quantity.value = item.refill_quantity
prescription_id.value = item.id
prescriptionModelForm.value = false
}
const getprescription = async () => {
await axios.post('/agent/api/get-prescriptions', {}, {
headers: {
'Authorization': `Bearer ${localStorage.getItem('access_token')}`,
}
}).then(response => {
// console.log('Response prescriptions:', response.data);
for (let data of response.data) {
let dataObject = {}
dataObject.name = data.name
dataObject.brand = data.brand
dataObject.from = data.from
dataObject.direction_quantity = data.direction_quantity
dataObject.dosage = data.dosage
dataObject.quantity = data.quantity
dataObject.refill_quantity = data.refill_quantity
dataObject.actions = ''
dataObject.id = data.id
itemsPrescriptions.value.push(dataObject)
}
//itemsPrescriptions.value = response.data
//itemsPrescriptions.value.push(dataObject)
loading.value = false;
})
.catch(error => {
loading.value = false;
console.error('Error:', error);
});
};
const getAppointmentUsers = async () => {
await axios.post('/agent/api/pending-appointment', {
headers: {
'Authorization': `Bearer ${localStorage.getItem('access_token')}`,
}
})
.then(response => {
console.log('appointments:', response.data);
appointmentUsers.value = Object.values(response.data);;
console.log("appointmentUsers.value", appointmentUsers.value);
})
.catch(error => {
console.error('Error:', error);
});
};
const logout = () => {
// const currentUser = localStorage.getItem('user_role');
localStorage.removeItem('isLogin');
localStorage.removeItem('patient_id');
localStorage.removeItem('user_role');
localStorage.removeItem('access_token');
localStorage.removeItem('userAbilities');
localStorage.removeItem('agent_id');
localStorage.removeItem('appiontment-id');
localStorage.removeItem('patient_appointment_id');
localStorage.removeItem('cominguser');
localStorage.removeItem('profileCompleted');
localStorage.removeItem('category_name');
localStorage.removeItem('selectedSidebarMenu');
localStorage.removeItem('plan_name');
localStorage.removeItem('plan_price');
router.push('/login');
};
const openProfile = () => {
// Resolve the path to the desired route
const routeData = router.resolve({ path: '/provider/patient-profile' });
// Open the resolved route in a new tab
window.open(routeData.href, '_blank');
}
defineExpose({ openDialog });
const getOrderDetail = async (order_id) => {
filteredOrders.value = [];
console.log("order_id", order_id);
await store.dispatch("orderDetailAgent", {
id: order_id,
});
filteredOrders.value = store.getters.getPatientOrderDetail;
console.log("queue order", filteredOrders.value);
OrderDetail.value = true;
}
const formatTotalCurrency = (amount) => {
let formattedAmount = amount.toString();
// Remove '.00' if present
// if (formattedAmount.includes('.00')) {
// formattedAmount = formattedAmount.replace('.00', '');
// }
// Split into parts for integer and decimal
let parts = formattedAmount.split('.');
// Format integer part with commas
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
// Return formatted number
return parts.join('.');
}
</script>
<template>
<VDialog v-model="isLoadingVisible" width="110" height="150" color="primary">
<VCardText class="" style="color: white !important;">
<div class="demo-space-x">
<VProgressCircular :size="40" color="primary" indeterminate />
</div>
</VCardText>
</VDialog>
<VSnackbar v-model="store.getters.getIsTonalSnackbarVisible" :timeout="5000" location="top end" variant="flat"
color="red">
{{ store.getters.getErrorMessage }}
</VSnackbar>
<VDialog v-model="permissionModel" refs="myDialog" width="500">
<!-- <template v-slot:default="{ isActive }"> -->
<v-card>
<v-card-text>
<div class="mt-2 mb-2">
<h4 class="text-center">Enable your Camera & Microphone permission from your browser</h4>
</div>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn text="Grant Pesmission" @click="enablePermission()"></v-btn>
</v-card-actions>
</v-card>
<!-- </template> -->
</VDialog>
<div v-if="isVisable" class="queue">
<div v-if="!isMobile">
<VList density="compact" v-if="isAgent">
<div class="px-4">
<span class="ml-3" v-if="isAgent">Patient Queue</span>
<VDivider v-if="isAgent" style="color: #fff;"></VDivider>
</div>
<div class="scroll-container">
<div class="px-4 border text-center" v-if="queueUsers.length === 0">
<span class="text-center">
<p class="mt-3 fs-0">No data available</p>
</span>
</div>
<div class="px-4" v-else v-for="(user, index) of queueUsers" :key="index">
<VListItem :value="user.id" class="py-3 list-item-hover">
<div class="user-info" v-if="!isMobile">
<VListItemTitle class="user-name">
<VIcon
:icon="user.status === 'active' ? 'mdi-checkbox-blank-circle' : 'mdi-checkbox-blank-circle-outline'"
:color="user.status === 'active' ? 'green' : 'red'"
:title="user.status === 'active' ? 'Active' : 'Inactive'" class="mr-2"
style="font-size: 10px;margin-right: 0px !important;">
</VIcon>
{{ user.name }}
</VListItemTitle>
<VListItemSubtitle class="waiting-time">{{ user.waitingTime }}</VListItemSubtitle>
</div>
<template #append v-if="!isMobile">
<VListItemAction end>
<VBtn color="error" class="start-call-btn mt-1"
@click.stop="joinCall(user, 'video')"
:disabled="user.status === 'inactive' || !user.mic || !user.cam">
Start Call
</VBtn>
<VMenu location="right" :close-on-content-click="true" :close-on-click="true"
v-if="!isMobile" content-class="custom-menu1">
<template v-slot:activator="{ props }">
<VIcon icon="mdi-dots-vertical" v-bind="props"></VIcon>
</template>
<v-card class="pop_card">
<v-row>
<VCol cols="12" md="4">
<VImg :src="user.url ? user.url : img" style="cursor: pointer;">
</VImg>
</VCol>
<VCol cols="12" md="8">
<v-row>
<VCol cols="12" md="8">
<h3>{{ user.name }} (Order #{{ user.order_id
}})</h3>
</VCol>
<VCol cols="12" md="1">
<VIcon
:icon="user.mic ? 'mdi-microphone' : 'mdi-microphone-off'"
:color="user.mic ? 'green' : 'red'" class="mr-2">
</VIcon>
</VCol>
<VCol cols="12" md="1">
<VIcon :icon="user.cam ? 'mdi-video' : 'mdi-video-off'"
:color="user.cam ? 'green' : 'red'" class="mr-2">
</VIcon>
</VCol>
</v-row>
<!-- next row -->
<v-row>
<VCol cols="12" md="12">
<VIcon
:icon="user.status === 'active' ? 'mdi-checkbox-blank-circle' : 'mdi-checkbox-blank-circle-outline'"
:color="user.status === 'active' ? 'green' : 'red'"
:title="user.status === 'active' ? 'Active' : 'Inactive'"
class="mr-2" style="font-size: 14px;">
</VIcon><span
style="color: black; font-weight: bold;">Appointment:</span>
&nbsp;{{
getConvertedDate(convertUtcTime(user.appointment.appointment_time,
user.appointment.appointment_date,
user.appointment.timezone))
}} at
{{
getConvertedTime(convertUtcTime(user.appointment.appointment_time,
user.appointment.appointment_date,
user.appointment.timezone))
}}
</VCol>
<VCol cols="12" md="12">
<strong>From:</strong> <span v-if="user.city">{{ user.city
}},{{ user.state
}}</span>
</VCol>
</v-row>
</VCol>
</v-row>
<v-row>
<VCol cols="12" md="2" class="pr-0">
<v-btn class="button_margin" color="primary"> <v-icon
start>mdi-message</v-icon>Chat</v-btn>
</VCol>
<VCol cols="12" md="2" class="pr-0">
<v-btn class="button_margin" color="primary"
@click.stop="joinCall(user, 'audio')">
<v-icon start>mdi-phone</v-icon>Audio</v-btn>
</VCol>
<VCol cols="12" md="2" class="pr-0">
<VMenu>
<template v-slot:activator="{ props }">
<v-btn class="button_margin" color="primary" v-bind="props">
<v-icon start icon="tabler-menu-2"></v-icon>More</v-btn>
</template>
<VCard>
<VList class="more">
<VListItem @click.stop="getOrderDetail(user.order_id)">
<VListItemTitle>
<v-icon start>mdi-shopping-cart</v-icon>
Order Detail
</VListItemTitle>
</VListItem>
<VListItem @click.stop="openDialog(user, 'Notes')">
<VListItemTitle>
<v-icon start>mdi-note-plus</v-icon>
Note
</VListItemTitle>
</VListItem>
<VListItem
@click.stop="openDialog(user, 'Request For Documents')">
<VListItemTitle>
<VIcon icon="mdi-file-document-multiple"
class="mr-2"></VIcon>
Request For Documents
</VListItemTitle>
</VListItem>
<!-- <VListItem @click.stop="openDialog(user, 'Request For Lab')">
<VListItemTitle>
<VIcon icon="mdi-flask" class="mr-2"></VIcon>
Request For Lab
</VListItemTitle>
</VListItem> -->
</VList>
</VCard>
</VMenu>
</VCol>
<VCol cols="12" md="2" class="pr-0 px-0">
<v-btn class="button_margin" color="primary"
@click.stop="openDialog(user, 'Lab Order')">
<VIcon icon="mdi-flask" class="mr-2"></VIcon>Lab
Order
</v-btn>
</VCol>
<VCol cols="12" md="4" class="pr-0">
<v-btn class="button_margin" color="primary"
@click.stop="isPrescriptionClick()">
<VIcon icon="mdi-flask" class="mr-2"></VIcon>Prescription
</v-btn>
</VCol>
</v-row>
</v-card>
</VMenu>
</VListItemAction>
</template>
<div class="userinfo" v-if="isMobile">
<VListItemTitle class="user-name">
<VIcon
:icon="user.status === 'active' ? 'mdi-checkbox-blank-circle' : 'mdi-checkbox-blank-circle-outline'"
:color="user.status === 'active' ? 'green' : 'red'"
:title="user.status === 'active' ? 'Active' : 'Inactive'" class="mr-2"
style="font-size: 10px;margin-right: 0px !important;">
</VIcon>
{{ user.name }}
</VListItemTitle>
<VListItemSubtitle class="waiting-time">{{ user.waitingTime }}</VListItemSubtitle>
</div>
<template #append v-if="isMobile">
<VListItemAction end>
<VIcon icon="mdi-dots-vertical" @click.stop="toggleDrawer(user)" v-if="isMobile">
</VIcon>
</VListItemAction>
</template>
</VListItem>
<VDivider></VDivider>
</div>
</div>
</VList>
</div>
</div>
<v-navigation-drawer v-model="drawer" app temporary v-if="isMobile">
<div class="pop_card" v-if="toggelUserValue">
<v-row class="pt-4 pb-4">
<VIcon icon="tabler-chevron-left" class="mt-1" @click="closeDrawer">
</VIcon>
<h3>{{ toggelUserValue.name }} (Order #{{ toggelUserValue.order_id
}})</h3>
<div class="icon_mobile">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<VIcon :icon="toggelUserValue.mic ? 'mdi-microphone' : 'mdi-microphone-off'"
:color="toggelUserValue.mic ? 'green' : 'red'" class="mr-2">
</VIcon>
<VIcon :icon="toggelUserValue.cam ? 'mdi-video' : 'mdi-video-off'"
:color="toggelUserValue.cam ? 'green' : 'red'" class="mr-2">
</VIcon>
</div>
<VDivider></VDivider>
<div class="container_img">
<div class="image">
<VImg :src="toggelUserValue.url ? toggelUserValue.url : img" style="cursor: pointer;"
width="50px" height="50px">
</VImg>
</div>
<div class="text">
<VIcon
:icon="toggelUserValue.status === 'active' ? 'mdi-checkbox-blank-circle' : 'mdi-checkbox-blank-circle-outline'"
:color="toggelUserValue.status === 'active' ? 'green' : 'red'"
:title="toggelUserValue.status === 'active' ? 'Active' : 'Inactive'" class="mr-2"
style="font-size: 14px;">
</VIcon>
<span style="color: black; font-weight: bold;">Appointment:</span>&nbsp;{{
getConvertedDate(convertUtcTime(toggelUserValue.appointment.appointment_time,
toggelUserValue.appointment.appointment_date, toggelUserValue.appointment.timezone))
}} at {{ getConvertedTime(convertUtcTime(toggelUserValue.appointment.appointment_time,
toggelUserValue.appointment.appointment_date, toggelUserValue.appointment.timezone)) }}
</div>
</div>
<VCol cols="12" md="12">
from <span v-if="toggelUserValue.country">{{ toggelUserValue.country }},{{ toggelUserValue.state
}}</span>
</VCol>
</v-row>
<v-row>
<VCol cols="12" md="12">
<v-btn class="button_margin_mobile" color="primary" @click.stop="joinCall(toggelUserValue, 'video')"
:disabled="toggelUserValue.status === 'inactive' || !user.mic || !user.cam">
<v-icon start>mdi-video</v-icon>Start Video
Call</v-btn>
</VCol>
</v-row>
<v-row>
<VCol cols="12" md="12">
<v-btn class="button_margin_mobile" color="primary"> <v-icon start>mdi-message</v-icon>Chat</v-btn>
</VCol>
<VCol cols="12" md="12">
<v-btn class="button_margin_mobile" color="primary" @click.stop="joinCall(user, 'audio')">
<v-icon start>mdi-phone</v-icon>Audio</v-btn>
</VCol>
</v-row>
<v-row>
<VCol cols="12" md="12">
<VMenu>
<template v-slot:activator="{ props }">
<v-btn class="button_margin_mobile" color="primary" v-bind="props"> <v-icon start
icon="tabler-menu-2"></v-icon>More</v-btn>
</template>
<VCard>
<VList class="more">
<VListItem @click.stop="getOrderDetail(toggelUserValue.order_id)">
<VListItemTitle>
<v-icon start>mdi-shopping-cart</v-icon>
Order Detail
</VListItemTitle>
</VListItem>
<VListItem @click.stop="openDialog(toggelUserValue, 'Notes')">
<VListItemTitle>
<v-icon start>mdi-note-plus</v-icon>
Note
</VListItemTitle>
</VListItem>
<VListItem @click.stop="openDialog(toggelUserValue, 'Request For Documents')">
<VListItemTitle>
<VIcon icon="mdi-file-document-multiple" class="mr-2"></VIcon>
Request For Documents
</VListItemTitle>
</VListItem>
<!-- <VListItem @click.stop="openDialog(toggelUserValue, 'Request For Lab')">
<VListItemTitle>
<VIcon icon="mdi-flask" class="mr-2"></VIcon>
Request For Lab
</VListItemTitle>
</VListItem> -->
</VList>
</VCard>
</VMenu>
</VCol>
<VCol cols="12" md="12">
<v-btn class="button_margin_mobile" color="primary"
@click.stop="openDialog(toggelUserValue, 'Lab Order')">
<VIcon icon="mdi-flask" class="mr-2"></VIcon>Lab
Order
</v-btn>
</VCol>
<VCol cols="12" md="12">
<v-btn class="button_margin_mobile" color="primary"
@click.stop="isPrescriptionClick()">
<VIcon icon="mdi-flask" class="mr-2"></VIcon>Prescription
</v-btn>
</VCol>
</v-row>
</div>
</v-navigation-drawer>
<!-- open note model -->
<v-dialog v-model="dialogNote" max-width="500" :class="isMobile ? 'dialog_padding_mobile' : 'dialog_padding'"
:style="isMobile ? 'top: -300px;' : ''">
<v-card class="pa-3">
<v-form ref="form" v-model="valid">
<v-textarea v-model="messageNote" :rules="messageRules" label="Notes" required></v-textarea>
<small class="float-right">By {{ profileName }}</small>
</v-form>
<v-card-actions class="pr-0 pl-0">
<v-btn color="primary" class="btn btn-primary" @click.stop="submitFormNote" :disabled="!valid">Add
Note</v-btn>
<v-btn color="primary" @click="dialogNote = false">Close</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- open Document model -->
<v-dialog v-model="dialogDocument" max-width="500" :class="isMobile ? 'dialog_padding_mobile' : 'dialog_padding'"
:style="isMobile ? 'top: -300px;' : ''">
<v-card class="pa-3">
<v-form ref="form" v-model="valid">
<v-textarea v-model="documentNote" :rules="documentRules" label="Request For Documents"
required></v-textarea>
</v-form>
<v-card-actions>
<v-btn color="primary" class="btn btn-primary" @click="submitFormDocumentNote"
:disabled="!valid">Submit</v-btn>
<v-btn color="primary" @click="dialogDocument = false">Close</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- open Request Lab model -->
<v-dialog v-model="dialogLab" max-width="500" :class="isMobile ? 'dialog_padding_mobile' : 'dialog_padding'"
:style="isMobile ? 'top: -300px;' : ''">
<v-card class="pa-3">
<v-form ref="form" v-model="valid">
<v-textarea v-model="labNote" :rules="labRules" label="Request For Lab" required></v-textarea>
</v-form>
<v-card-actions>
<v-btn color="primary" class="btn btn-primary" @click="submitFormLabNote"
:disabled="!valid">Submit</v-btn>
<v-btn color="primary" @click="dialogLab = false">Close</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- open Lab Order -->
<v-dialog v-model="OrderDetail" :class="isMobile ? 'dialog_padding_mobile' : 'dialog_padding'"
:style="isMobile ? 'top: -300px;' : ''">
<v-card class="pa-3">
<v-row>
<v-col cols="6" class="text-right cross">
<h5 class="text-h5 text-left">Order #{{ filteredOrders.order_details.id }}</h5>
</v-col>
<v-col cols="6" class="text-right cross">
<v-btn icon color="transparent" small @click="OrderDetail = false">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-col>
<VDataTable :headers="headerOrders" :items="filteredOrders.order_items.items" item-value="id"
class="text-no-wrap ">
<template #item.title="{ item }">
<div class="d-flex gap-x-3">
<!-- <VAvatar size="34" variant="tonal" :image="item.image_url" rounded /> -->
<div class="d-flex flex-column text-left">
<h5 style="margin-bottom: 0px;">
{{ item.title }}
</h5>
<span class=" text-start align-self-start">
{{ item.list_sub_title }}
</span>
</div>
</div>
</template>
<template #item.price="{ item }">
<span>${{ item.price }}</span>
</template>
<template #item.quantity="{ item }">
<span>{{ item.quantity }}</span>
</template>
<template #item.status="{ item }">
<span>
<VChip variant="tonal" :color="getStatusColor(item.status)" size="small">
{{ item.status }}
</VChip>
</span>
</template>
<template #item.total="{ item }">
<span> ${{ item.price * item.quantity }} </span>
</template>
<template #bottom />
</VDataTable>
<VDivider />
<VCardText>
<div class="d-flex align-end flex-column">
<table class="text-high-emphasis">
<tbody>
<tr>
<td width="200px">Subtotal:</td>
<td class="font-weight-medium">
${{
formatTotalCurrency(parseFloat(filteredOrders.order_items.total_amount).toFixed(2))
}}
</td>
</tr>
<tr>
<td>Shipping fee:</td>
<td class="font-weight-medium">
${{
parseFloat(
filteredOrders.order_items.total_shipping_cost
).toFixed(2)
}}
</td>
</tr>
<tr>
<td class="font-weight-medium">
Total:
</td>
<td class="font-weight-medium">
${{
formatTotalCurrency(parseFloat(
filteredOrders.order_items.total
).toFixed(2))
}}
</td>
</tr>
</tbody>
</table>
</div>
</VCardText>
</v-row>
</v-card>
</v-dialog>
<v-dialog v-model="labOrderModel" :class="isMobile ? 'dialog_padding_mobile' : 'dialog_padding'"
:style="isMobile ? 'top: -300px;' : ''">
<v-card class="pa-3">
<v-row>
<v-col cols="12" class="text-right cross">
<v-btn icon color="transparent" small @click="labOrderModel = false">
<v-icon>mdi-close</v-icon>
</v-btn>
<VDivider class="mt-2"></VDivider>
</v-col>
</v-row>
<v-row class="text-center">
<!-- <VCol cols="12" md="4">
<v-btn color="primary" size="small" @click.stop="openDialog(toggelUserValue, 'Add Lab Order')"
class="mr-2">
<v-icon>mdi-plus</v-icon> Add Lab Order
</v-btn>
</VCol>
<VCol cols="12" md="4">
<v-btn color="primary" size="small"><v-icon start icon="tabler-report-medical"></v-icon> GLP-1 Lab
Order</v-btn>
</VCol>
<VCol cols="12" md="4">
<v-btn color="primary" size="small"><v-icon start icon="tabler-report-medical"></v-icon> TRT Lab
order</v-btn>
</VCol> -->
</v-row>
<PatientLabKit />
<v-card-actions>
</v-card-actions>
</v-card>
</v-dialog>
<v-dialog v-model="state.addLabOrder" max-width="400">
<v-card>
<v-card-title>Add Lab Kit</v-card-title>
<v-card-text>
<v-form ref="form" v-model="state.valid" class="mt-1">
<v-row v-if="testKits">
<v-col cols="12" md="12">
<v-autocomplete label="Test Kit" v-model="state.selectedTestKitId" style="column-gap: 0px;"
:items="state.testKits" item-title="name" item-value="id"
:rules="getFieldRules('Test Kit', 'Test Kit is required')"></v-autocomplete>
</v-col>
</v-row>
</v-form>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="primary" text @click="state.addLabOrder = false">Cancel</v-btn>
<v-btn color="primary" @click="storeTestKit" :disabled="!state.valid">Save</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- open Lab Order -->
<v-dialog v-model="prescriptionModel" max-width="1200"
:class="isMobile ? 'dialog_padding_mobile' : 'dialog_padding'">
<v-card class="pa-3">
<v-row>
<v-col cols="12" class="text-right cross">
<v-btn icon color="transparent" small @click="prescriptionModel = false">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-col>
</v-row>
<v-form ref="form" v-model="valid" class="mt-6">
<v-row>
<v-col cols="12" md="2" v-if="isMobile">
<v-btn color="primary" class="btn" style="height: 54px;"
@click.stop="openDialog(toggelUserValue, 'Prescription Form')">
Prescription </v-btn>
</v-col>
<v-col cols="12" md="10">
<v-text-field label="Medicine" :rules="getFieldRules('Medicine', 'Medicine is required')"
v-model="medicines" required></v-text-field>
</v-col>
<v-col cols="12" md="2" v-if="!isMobile">
<v-btn color="primary" class="btn" style="height: 54px;"
@click.stop="openDialog(toggelUserValue, 'Prescription Form')">
Prescription </v-btn>
</v-col>
</v-row>
<v-row>
<v-col cols="12" md="6">
<v-text-field label="Brand" :rules="getFieldRules('brand', 'Brand is required')" v-model="brand"
required></v-text-field>
</v-col>
<v-col cols="12" md="6">
<v-text-field label="From" :rules="getFieldRules('from', 'From is required')" v-model="from"
required></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col cols="12" md="6">
<v-text-field label="Dosage" :rules="getFieldRules('dosage', 'Dosage is required')"
v-model="dosage" required></v-text-field>
</v-col>
<v-col cols="12" md="6">
<v-text-field label="Quantity" :rules="getFieldRules('quantity', 'Quantity is required')"
v-model="quantity" required></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col cols="12" md="6">
<v-text-field label="Direction Quantity"
:rules="getFieldRules('direction quantity', 'Direction Quantity is required')"
v-model="direction_quantity" required></v-text-field>
</v-col>
<v-col cols="12" md="6">
<v-text-field label="Refil Quantity"
:rules="getFieldRules('Refil Quantity', 'Refil Quantity one is required')"
v-model="refil_quantity" required></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col cols="12" md="6">
<v-text-field label="Direction one" :rules="getFieldRules('', 'Direction one is required')"
v-model="direction_one" required></v-text-field>
</v-col>
<v-col cols="12" md="6">
<v-text-field label="Direction Two" :rules="getFieldRules('', 'Direction Two is required')"
v-model="direction_two" required></v-text-field>
</v-col>
</v-row>
<v-row style="margin-bottom: 5px;">
<v-col cols="12" md="12">
<!-- <v-text-field label="Comments" :rules="getFieldRules('', 'Comments is required')" v-model="comments"
required></v-text-field> -->
<v-textarea label="Comments" :rules="getFieldRules('', 'Comments is required')"
v-model="comments" required></v-textarea>
</v-col>
</v-row>
</v-form>
<v-row class="text-center">
<v-col cols="12" md="12">
<v-btn color="primary" @click="prescriptionForm" :disabled="!valid">Select</v-btn>
</v-col>
</v-row>
</v-card>
</v-dialog>
<v-dialog v-model="isPrescriptionModel" max-width="1200">
<v-card class="pa-3">
<VRow>
<v-col cols="12" class="text-right cross">
<v-btn icon color="transparent" small @click="isPrescriptionModel = false">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-col>
</VRow>
<PatientPrescription />
</v-card>
</v-dialog>
<v-dialog v-model="prescriptionModelForm" max-width="1200">
<v-card class="pa-3">
<v-row>
<v-col cols="12" class="text-right cross">
<v-btn icon color="transparent" small @click="prescriptionModelForm = false">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-col>
</v-row>
<v-row>
<v-col cols="12" md="12">
<v-data-table :headers="headers" :items="itemsPrescriptions" :search="search" :loading="loading"
:page.sync="page" :items-per-page.sync="itemsPerPage" @page-count="pageCount = $event"
class="elevation-1">
<template v-slot:top>
<v-toolbar flat :height="30">
<v-toolbar-title>Prescriptions</v-toolbar-title>
<v-divider class="mx-4" inset vertical></v-divider>
<v-spacer></v-spacer>
<v-text-field v-model="search" label="Search" single-line hide-details></v-text-field>
</v-toolbar>
</template>
<template v-slot:item.actions="{ item }">
<v-btn color="primary" small @click="selectedItem(item)">Select</v-btn>
</template>
</v-data-table>
</v-col>
</v-row>
</v-card>
</v-dialog>
<div v-if="isVisable" class="queue">
<div v-if="isMobile">
<VList density="compact" v-if="isAgent">
<div class="px-4">
<span class="ml-3" v-if="isAgent">Patient Queue</span>
<VDivider v-if="isAgent"></VDivider>
</div>
<div class="scroll-container">
<div class="px-4 border text-center" v-if="queueUsers.length === 0">
<span class="text-center">
<p class="mt-3 fs-0">No data available</p>
</span>
</div>
<div class="px-4" v-else v-for="(user, index) of queueUsers" :key="index">
<VListItem :value="user.id" class="py-3 list-item-hover">
<div class="user-info" v-if="!isMobile">
<VListItemTitle class="user-name">
<VIcon
:icon="user.status === 'active' ? 'mdi-checkbox-blank-circle' : 'mdi-checkbox-blank-circle-outline'"
:color="user.status === 'active' ? 'green' : 'red'"
:title="user.status === 'active' ? 'Active' : 'Inactive'" class="mr-2"
style="font-size: 10px;margin-right: 0px !important;">
</VIcon>
{{ user.name }}
</VListItemTitle>
<VListItemSubtitle class="waiting-time">{{ user.waitingTime }}</VListItemSubtitle>
</div>
<template #append v-if="!isMobile">
<VListItemAction end>
<VBtn color="error" class="start-call-btn mt-1"
@click.stop="joinCall(user, 'video')"
:disabled="user.status === 'inactive' || !user.mic || !user.cam">
Start Call
</VBtn>
<VMenu location="right" :close-on-content-click="true" :close-on-click="true"
v-if="!isMobile" content-class="custom-menu1">
<template v-slot:activator="{ props }">
<VIcon icon="mdi-dots-vertical" v-bind="props"></VIcon>
</template>
<v-card class="pop_card">
<v-row>
<VCol cols="12" md="4">
<VImg :src="user.url ? user.url : img" style="cursor: pointer;">
</VImg>
</VCol>
<VCol cols="12" md="8">
<v-row>
<VCol cols="12" md="8">
<h3>{{ user.name }}</h3>
</VCol>
<VCol cols="12" md="1">
<VIcon
:icon="user.mic ? 'mdi-microphone' : 'mdi-microphone-off'"
:color="user.mic ? 'green' : 'red'" class="mr-2">
</VIcon>
</VCol>
<VCol cols="12" md="1">
<VIcon :icon="user.cam ? 'mdi-video' : 'mdi-video-off'"
:color="user.cam ? 'green' : 'red'" class="mr-2">
</VIcon>
</VCol>
</v-row>
<!-- next row -->
<v-row>
<VCol cols="12" md="12">
<VIcon
:icon="user.status === 'active' ? 'mdi-checkbox-blank-circle' : 'mdi-checkbox-blank-circle-outline'"
:color="user.status === 'active' ? 'green' : 'red'"
:title="user.status === 'active' ? 'Active' : 'Inactive'"
class="mr-2" style="font-size: 14px;">
</VIcon><span
style="color: black; font-weight: bold;">Appointment:</span>
&nbsp;{{
getConvertedDate(convertUtcTime(user.appointment.appointment_time,
user.appointment.appointment_date,
user.appointment.timezone)) }} at {{
getConvertedTime(convertUtcTime(user.appointment.appointment_time,
user.appointment.appointment_date,
user.appointment.timezone)) }}
</VCol>
<VCol cols="12" md="12">
<strong>From:</strong> <span v-if="user.city">{{ user.city
}},{{ user.state
}}</span>
</VCol>
</v-row>
<v-row>
<VCol cols="12" md="4">
<v-btn class="button_margin" color="primary"> <v-icon
start>mdi-message</v-icon>Chat</v-btn>
</VCol>
<VCol cols="12" md="4">
<v-btn class="button_margin" color="primary"
@click.stop="joinCall(user, 'audio')">
<v-icon start>mdi-phone</v-icon>Audio</v-btn>
</VCol>
<VCol cols="12" md="4">
<VMenu>
<template v-slot:activator="{ props }">
<v-btn class="button_margin" color="primary"
v-bind="props"> <v-icon start
icon="tabler-menu-2"></v-icon>More</v-btn>
</template>
<VCard>
<VList class="more">
<VListItem
@click.stop="openDialog(user, 'Notes')">
<VListItemTitle>
<v-icon start>mdi-note-plus</v-icon>
Note
</VListItemTitle>
</VListItem>
<VListItem
@click.stop="openDialog(user, 'Request For Documents')">
<VListItemTitle>
<VIcon icon="mdi-file-document-multiple"
class="mr-2">
</VIcon>
Request For Documents
</VListItemTitle>
</VListItem>
<!-- <VListItem @click.stop="openDialog(user, 'Request For Lab')">
<VListItemTitle>
<VIcon icon="mdi-flask" class="mr-2"></VIcon>
Request For Lab
</VListItemTitle>
</VListItem> -->
</VList>
</VCard>
</VMenu>
</VCol>
</v-row>
</VCol>
</v-row>
</v-card>
</VMenu>
</VListItemAction>
</template>
<div class="userinfo" v-if="isMobile">
<VListItemTitle class="user-name">
<VIcon
:icon="user.status === 'active' ? 'mdi-checkbox-blank-circle' : 'mdi-checkbox-blank-circle-outline'"
:color="user.status === 'active' ? 'green' : 'red'"
:title="user.status === 'active' ? 'Active' : 'Inactive'" class="mr-2"
style="font-size: 10px;margin-right: 0px !important;">
</VIcon>
{{ user.name }}
</VListItemTitle>
<VListItemSubtitle class="waiting-time">{{ user.waitingTime }}</VListItemSubtitle>
</div>
<template #append v-if="isMobile">
<VListItemAction end>
<VIcon icon="mdi-dots-vertical" @click.stop="toggleDrawer(user)" v-if="isMobile">
</VIcon>
</VListItemAction>
</template>
</VListItem>
<VDivider></VDivider>
</div>
</div>
</VList>
</div>
</div>
</template>
<style lang="scss" scoped>
hr {
margin: 0 !important;
}
.meta-key {
border: thin solid rgba(var(--v-border-color), var(--v-border-opacity));
border-radius: 6px;
block-size: 1.5625rem;
line-height: 1.3125rem;
padding-block: 0.125rem;
padding-inline: 0.25rem;
}
::v-deep .custom-menu {
position: relative;
}
::v-deep .custom-menu::before {
content: "" !important;
position: absolute !important;
transform: translateY(-50%);
top: 50% !important;
left: -8px !important;
border-left: 8px solid transparent !important;
border-right: 8px solid transparent !important;
border-bottom: 8px solid #fff !important;
}
// Styles for the VList component
.more .v-list-item-title {
color: rgb(106 109 255);
}
.more .menu-item:hover {
cursor: pointer;
}
.slide-enter-active,
.slide-leave-active {
transition: transform 0.3s ease;
}
.slide-enter,
.slide-leave-to {
transform: translateX(-100%);
}
.start-call-btn {
opacity: 0;
display: none;
transition: opacity 0.3s ease;
background-color: #212121;
}
.button_margin {
margin: 2px;
}
.dialog_padding {
padding: 5px;
}
.custom-menu .v-menu__content {
background-color: #333;
color: #fff;
border-radius: 4px;
padding: 8px 0;
}
.user-info {
display: flex;
flex-direction: column;
transition: opacity 0.3s ease;
}
.list-item-hover {
transition: background-color 0.3s ease;
&:hover {
background-color: rgba(var(--v-theme-primary), 0.1);
.start-call-btn {
opacity: 1;
display: block;
position: relative;
left: -35px;
}
.user-info {
opacity: 0;
display: none;
}
}
}
.queue .v-list {
background-color: rgb(var(--v-theme-yellow)) !important;
color: rgb(var(--v-theme-yellow-theme-button)) !important;
}
.pop_card {
overflow: hidden !important;
padding: 10px;
}
.waiting-time {
color: rgb(var(--v-theme-yellow-theme-button)) !important;
}
.v-overlay__content {
max-height: 706.4px;
max-width: 941.6px;
min-width: 24px;
--v-overlay-anchor-origin: bottom left;
transform-origin: left top;
top: 154.4px !important;
left: 204px !important;
}
.button_margin {
margin-top: 10px;
font-size: 10px;
}
/* Responsive Styles */
@media screen and (max-width: 768px) {
.pop_card {
max-width: 100%;
margin: 0 auto;
}
}
.container_img {
display: flex;
flex-direction: column;
width: 100%;
align-items: center;
}
.image {
order: 2;
/* Change the order to 2 in mobile view */
}
.text {
order: 1;
/* Change the order to 1 in mobile view */
}
/* Media query for mobile view */
@media (max-width: 768px) {
.container_img {
flex-direction: row;
margin-top: 10px;
}
.button_margin_mobile {
width: 100%;
}
.image {
width: 20%;
padding: 0 10px;
}
.text {
width: 80%;
/* Each takes 50% width */
padding: 0 10px;
/* Optional padding */
}
}
::-webkit-scrollbar {
width: 10px;
/* Width of the scrollbar */
}
/* Track */
::-webkit-scrollbar-track {
background: #f1f1f1;
/* Color of the track */
}
/* Handle */
::-webkit-scrollbar-thumb {
background: #888;
/* Color of the handle */
border-radius: 5px;
/* Roundness of the handle */
}
/* Handle on hover */
::-webkit-scrollbar-thumb:hover {
background: #555;
/* Color of the handle on hover */
}
/* Container for the content */
.scroll-container {
max-height: 191px;
/* Maximum height of the scrollable content */
overflow-y: scroll;
/* Enable vertical scrolling */
}
/* Content within the scroll container */
.scroll-content {
padding: 20px;
}
/* Example of additional styling for content */
.scroll-content p {
margin-bottom: 20px;
}
.cross button {
padding: 0px;
margin: 0px;
/* font-size: 10px; */
background: none;
border: none;
box-shadow: none;
height: 23px;
}
.v-data-table-header {
display: table-header-group;
}
</style>