603 lines
22 KiB
Vue
603 lines
22 KiB
Vue
<script setup>
|
|
import moment from "moment";
|
|
import { computed, onMounted, ref } from "vue";
|
|
import { useDisplay } from "vuetify";
|
|
import { useStore } from "vuex";
|
|
const router = useRouter();
|
|
const store = useStore();
|
|
const ordersList = ref([]);
|
|
const widgetData = ref([
|
|
{
|
|
title: "Pending Payment",
|
|
value: 56,
|
|
icon: "ri-calendar-2-line",
|
|
},
|
|
{
|
|
title: "Completed",
|
|
value: 12689,
|
|
icon: "ri-check-double-line",
|
|
},
|
|
{
|
|
title: "Refunded",
|
|
value: 124,
|
|
icon: "ri-wallet-3-line",
|
|
},
|
|
{
|
|
title: "Failed",
|
|
value: 32,
|
|
icon: "ri-error-warning-line",
|
|
},
|
|
]);
|
|
const searchQuery = ref("");
|
|
|
|
// Data table options
|
|
const itemsPerPage = ref(10);
|
|
const page = ref(1);
|
|
const sortBy = ref();
|
|
const orderBy = ref();
|
|
const { smAndDown } = useDisplay();
|
|
const options = ref({
|
|
page: 1,
|
|
itemsPerPage: 5,
|
|
sortBy: [""],
|
|
sortDesc: [false],
|
|
});
|
|
const isLoading = ref(true);
|
|
// Data table Headers
|
|
const headers = [
|
|
{
|
|
title: "Order",
|
|
key: "id",
|
|
},
|
|
// {
|
|
// title: 'Order Date',
|
|
// key: 'date',
|
|
// },
|
|
{
|
|
title: "Patient",
|
|
key: "patient_name",
|
|
},
|
|
{
|
|
title: "Meeting Date",
|
|
key: "appointment_date",
|
|
},
|
|
{
|
|
title: "Meeting Time",
|
|
key: "appointment_time",
|
|
},
|
|
{
|
|
title: "Perscrption Status",
|
|
key: "status",
|
|
},
|
|
{
|
|
title: "Meeting status",
|
|
key: "appointment_status",
|
|
},
|
|
{
|
|
title: "Actions",
|
|
key: "actions",
|
|
sortable: false,
|
|
},
|
|
];
|
|
|
|
const updateOptions = (options) => {
|
|
page.value = options.page;
|
|
sortBy.value = options.sortBy[0]?.key;
|
|
orderBy.value = options.sortBy[0]?.order;
|
|
};
|
|
|
|
const resolvePaymentStatus = (status) => {
|
|
if (status === 1)
|
|
return {
|
|
text: "Paid",
|
|
color: "success",
|
|
};
|
|
if (status === 2)
|
|
return {
|
|
text: "Pending",
|
|
color: "warning",
|
|
};
|
|
if (status === 3)
|
|
return {
|
|
text: "Cancelled",
|
|
color: "secondary",
|
|
};
|
|
if (status === 4)
|
|
return {
|
|
text: "Failed",
|
|
color: "error",
|
|
};
|
|
};
|
|
|
|
const resolveStatusVariant = (status) => {
|
|
switch (status.toLowerCase()) {
|
|
case "completed":
|
|
return { color: "success", text: "Completed" };
|
|
case "scheduled":
|
|
return { color: "primary", text: "Scheduled" };
|
|
case "cancelled":
|
|
return { color: "error", text: "Cancelled" };
|
|
default:
|
|
return { color: "warning", text: "Pending" };
|
|
}
|
|
};
|
|
const formatDateDa = (date) => {
|
|
const messageDate = new Date(date);
|
|
const options = {
|
|
year: "numeric",
|
|
month: "numeric",
|
|
day: "numeric",
|
|
hour: "numeric",
|
|
minute: "2-digit",
|
|
|
|
hour12: false,
|
|
};
|
|
const formattedDate = messageDate
|
|
.toLocaleString("en-US", options)
|
|
.replace(/\//g, "-")
|
|
.replace(",", "");
|
|
return `${formattedDate}`;
|
|
};
|
|
|
|
function totalCallDuration(start_time, end_time) {
|
|
console.log(start_time, end_time);
|
|
const startMoment = moment(start_time);
|
|
const endMoment = moment(end_time);
|
|
|
|
// Calculate the duration
|
|
const duration = moment.duration(endMoment.diff(startMoment));
|
|
const hours = duration.hours();
|
|
const thours = `${String(hours).padStart(2, "0")}`;
|
|
const minutes = duration.minutes();
|
|
const tminutes = `${String(minutes).padStart(2, "0")}`;
|
|
const seconds = duration.seconds();
|
|
const tsecond = `${String(seconds).padStart(2, "0")}`;
|
|
let durationText;
|
|
if (hours === 0 && minutes === 0) {
|
|
//for second
|
|
durationText = ` 00:00:${tsecond}`;
|
|
} else if (hours === 0 && minutes > 0) {
|
|
//for minutes
|
|
durationText = `00:${tminutes}:${tsecond}`;
|
|
} else if (hours > 0) {
|
|
//for hours
|
|
durationText = `${thours}:${tminutes}:${tsecond}`;
|
|
}
|
|
const totalDuration = durationText;
|
|
console.log("Duration:", durationText);
|
|
// You may need to adjust this function based on your actual data structure
|
|
// For example, if you have separate first name and last name properties in each appointment object
|
|
return totalDuration; // For now, just return the first name
|
|
}
|
|
const ordersGetList = computed(() => {
|
|
return ordersList.value.map((history) => ({
|
|
...history,
|
|
// appointment_date: getConvertedDate(convertUtcTime(history.appointment_time, history.appointment_date, history.timezone)),
|
|
//appointment_time: getConvertedTime(convertUtcTime(history.appointment_time, history.appointment_date, history.timezone)),
|
|
|
|
// appointment_date: changeFormat(history.appointment_date),
|
|
// appointment_time: convertUtcDateTimeToLocal(history.appointment_date, history.appointment_time, 'time'),
|
|
// start_time: changeDateFormat(history.start_time),
|
|
// end_time: changeDateFormat(history.end_time),
|
|
// duration: totalCallDuration(history.start_time, history.end_time),
|
|
}));
|
|
// isLoading.value - false
|
|
// ordersList.value.sort((a, b) => {
|
|
// return b.id - a.id;
|
|
// });
|
|
// return ordersList.value
|
|
});
|
|
onMounted(async () => {
|
|
store.dispatch("updateIsLoading", true);
|
|
await store.dispatch("orderAgentList");
|
|
ordersList.value = store.getters.getPatientOrderList;
|
|
console.log("ordersList", ordersList.value);
|
|
isLoading.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 formatDate = (date) => {
|
|
const messageDate = new Date(date);
|
|
const options = {
|
|
year: "numeric",
|
|
month: "numeric",
|
|
day: "numeric",
|
|
hour: "numeric", // Change from '2-digit' to 'numeric'
|
|
minute: "2-digit",
|
|
hour12: true, // Add hour12: true to get 12-hour format with AM/PM
|
|
};
|
|
const formattedDate = messageDate
|
|
.toLocaleString("en-US", options)
|
|
.replace(/\//g, "-")
|
|
.replace(",", ""); // Remove the comma
|
|
return formattedDate.trim();
|
|
};
|
|
const viewOrder = (orderId) => {
|
|
router.push({ name: "provider-order-detail", params: { id: orderId } });
|
|
};
|
|
function changeFormat(dateFormat) {
|
|
const dateParts = dateFormat.split("-"); // Assuming date is in yyyy-mm-dd format
|
|
const year = parseInt(dateParts[0]);
|
|
const month = String(dateParts[1]).padStart(2, "0"); // Pad single-digit months with leading zero
|
|
const day = String(dateParts[2]).padStart(2, "0"); // Pad single-digit days with leading zero
|
|
|
|
// Create a new Date object with the parsed values
|
|
const date = new Date(year, month - 1, day); // Month is zero-based in JavaScript Date object
|
|
|
|
// Format the date as mm-dd-yyyy
|
|
const formattedDate = month + "-" + day + "-" + date.getFullYear();
|
|
|
|
return formattedDate;
|
|
}
|
|
const getStatusColor = (status) => {
|
|
switch (status) {
|
|
case "pending":
|
|
return "orange";
|
|
case "Shipped":
|
|
return "blue";
|
|
case "Delivered":
|
|
return "green";
|
|
case "Cancelled":
|
|
return "red";
|
|
default:
|
|
return "gray";
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div>
|
|
<VDialog
|
|
v-model="store.getters.getIsLoading"
|
|
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>
|
|
<VCard class="mb-6" style="display: none">
|
|
<VCardText class="px-2">
|
|
<VRow>
|
|
<template v-for="(data, index) in widgetData" :key="index">
|
|
<VCol cols="12" sm="6" md="3" class="px-6">
|
|
<div
|
|
class="d-flex justify-space-between"
|
|
:class="
|
|
$vuetify.display.xs
|
|
? 'product-widget'
|
|
: $vuetify.display.sm
|
|
? index < 2
|
|
? 'product-widget'
|
|
: ''
|
|
: ''
|
|
"
|
|
>
|
|
<div class="d-flex flex-column gap-y-1">
|
|
<h4 class="text-h4">
|
|
{{ data.value }}
|
|
</h4>
|
|
<span class="text-base text-capitalize">
|
|
{{ data.title }}
|
|
</span>
|
|
</div>
|
|
|
|
<VAvatar variant="tonal" rounded size="42">
|
|
<VIcon :icon="data.icon" size="26" />
|
|
</VAvatar>
|
|
</div>
|
|
</VCol>
|
|
<VDivider
|
|
v-if="
|
|
$vuetify.display.mdAndUp
|
|
? index !== widgetData.length - 1
|
|
: $vuetify.display.smAndUp
|
|
? index % 2 === 0
|
|
: false
|
|
"
|
|
vertical
|
|
inset
|
|
length="100"
|
|
/>
|
|
</template>
|
|
</VRow>
|
|
</VCardText>
|
|
</VCard>
|
|
<VRow>
|
|
<VCol cols="12" md="12">
|
|
<VCard title="Prescriptions">
|
|
<VCardText>
|
|
<div
|
|
class="d-flex justify-sm-space-between align-center justify-start flex-wrap gap-4"
|
|
>
|
|
<VTextField
|
|
v-model="searchQuery"
|
|
placeholder="Search Order"
|
|
density="compact"
|
|
style="
|
|
max-inline-size: 200px;
|
|
min-inline-size: 200px;
|
|
"
|
|
/>
|
|
</div>
|
|
</VCardText>
|
|
<VCardText>
|
|
<v-data-table
|
|
:loading="isLoading"
|
|
:headers="headers"
|
|
:items="ordersGetList"
|
|
:search="searchQuery"
|
|
>
|
|
<!-- full name -->
|
|
<template #item.id="{ item }">
|
|
<RouterLink
|
|
:to="{
|
|
name: 'provider-order-detail',
|
|
params: { id: item.id },
|
|
}"
|
|
>
|
|
#{{ item.id }}
|
|
</RouterLink>
|
|
</template>
|
|
|
|
<template #item.patient_name="{ item }">
|
|
<div class="d-flex align-center">
|
|
<VAvatar
|
|
size="32"
|
|
:color="
|
|
item.profile_picture
|
|
? ''
|
|
: 'primary'
|
|
"
|
|
:class="
|
|
item.profile_picture
|
|
? ''
|
|
: 'v-avatar-light-bg primary--text'
|
|
"
|
|
:variant="
|
|
!item.profile_picture
|
|
? 'tonal'
|
|
: undefined
|
|
"
|
|
>
|
|
<VImg
|
|
v-if="item.profile_picture"
|
|
:src="item.profile_picture"
|
|
/>
|
|
<span v-else>{{
|
|
item.patient_name.charAt(0)
|
|
}}</span>
|
|
</VAvatar>
|
|
<div class="d-flex flex-column ms-3">
|
|
<RouterLink
|
|
:to="{
|
|
name: 'provider-patient-profile-detail',
|
|
params: { id: item.patient_id },
|
|
}"
|
|
>
|
|
<div class="font-weight-medium">
|
|
{{ item.patient_name }}
|
|
</div>
|
|
</RouterLink>
|
|
<small>{{ item.patient_email }}</small>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<template #item.appointment_date="{ item }">
|
|
{{
|
|
getConvertedDate(
|
|
convertUtcTime(
|
|
item.appointment_time,
|
|
item.appointment_date,
|
|
item.timezone
|
|
)
|
|
)
|
|
}}
|
|
</template>
|
|
<template #item.appointment_time="{ item }">
|
|
{{
|
|
getConvertedTime(
|
|
convertUtcTime(
|
|
item.appointment_time,
|
|
item.appointment_date,
|
|
item.timezone
|
|
)
|
|
)
|
|
}}
|
|
</template>
|
|
|
|
<template #item.status="{ item }">
|
|
<span>
|
|
<VChip
|
|
variant="tonal"
|
|
:color="getStatusColor(item.status)"
|
|
size="small"
|
|
>
|
|
{{ item.status }}
|
|
</VChip>
|
|
</span>
|
|
</template>
|
|
<!-- status -->
|
|
|
|
<template #item.appointment_status="{ item }">
|
|
<VChip
|
|
:color="
|
|
resolveStatusVariant(
|
|
item.appointment_status
|
|
).color
|
|
"
|
|
class="font-weight-medium"
|
|
size="small"
|
|
:class="{
|
|
'blink-status':
|
|
item.appointment_status.toLowerCase() !==
|
|
'completed',
|
|
}"
|
|
>
|
|
{{
|
|
resolveStatusVariant(
|
|
item.appointment_status
|
|
).text
|
|
}}
|
|
</VChip>
|
|
</template>
|
|
<template #item.actions="{ item }">
|
|
<IconBtn size="small">
|
|
<VIcon icon="ri-more-2-line" />
|
|
<VMenu activator="parent">
|
|
<VList>
|
|
<VListItem value="view">
|
|
<RouterLink
|
|
:to="{
|
|
name: 'provider-order-detail',
|
|
params: { id: item.id },
|
|
}"
|
|
class="text-high-emphasis"
|
|
>
|
|
View
|
|
</RouterLink>
|
|
</VListItem>
|
|
<VListItem
|
|
value="complete"
|
|
v-if="
|
|
item.appointment_status.toLowerCase() !==
|
|
'completed'
|
|
"
|
|
>
|
|
<span
|
|
@click="
|
|
markAsCompleted(item)
|
|
"
|
|
class="text-high-emphasis cursor-pointer"
|
|
>
|
|
Mark as Complete
|
|
</span>
|
|
</VListItem>
|
|
</VList>
|
|
</VMenu>
|
|
</IconBtn>
|
|
</template>
|
|
</v-data-table>
|
|
</VCardText>
|
|
</VCard>
|
|
</VCol>
|
|
</VRow>
|
|
</div>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
#customer-link {
|
|
&:hover {
|
|
color: "#000" !important;
|
|
}
|
|
}
|
|
|
|
.product-widget {
|
|
border-block-end: 1px solid
|
|
rgba(var(--v-theme-on-surface), var(--v-border-opacity));
|
|
padding-block-end: 1rem;
|
|
}
|
|
</style>
|