2302 lines
96 KiB
Vue
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>
|
|
{{
|
|
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">
|
|
|
|
<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> {{
|
|
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>
|
|
{{
|
|
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>
|