initial commit
This commit is contained in:
320
resources/js/pages/patient/AccountSettingsAccount.vue
Normal file
320
resources/js/pages/patient/AccountSettingsAccount.vue
Normal file
@@ -0,0 +1,320 @@
|
||||
<script setup>
|
||||
import avatar1 from "@images/avatars/avatar-1.png";
|
||||
import { ref, reactive, computed, onMounted } from "vue";
|
||||
import {
|
||||
emailValidator,
|
||||
requiredEmail,
|
||||
requiredName,
|
||||
requiredPhone,
|
||||
validUSAPhone,
|
||||
} from "@validators";
|
||||
import { useStore } from "vuex";
|
||||
|
||||
const store = useStore();
|
||||
let imageBlob = null;
|
||||
const profileData = ref([]);
|
||||
const errors = ref({
|
||||
name: undefined,
|
||||
email: undefined,
|
||||
phone_no: undefined,
|
||||
});
|
||||
const accountData = {
|
||||
avatarImg: avatar1,
|
||||
first_name: "",
|
||||
last_name: "",
|
||||
email: "",
|
||||
phone_no: "",
|
||||
};
|
||||
|
||||
const refVForm = ref();
|
||||
const refInputEl = ref();
|
||||
const isConfirmDialogOpen = ref(false);
|
||||
const accountDataLocal = ref(structuredClone(accountData));
|
||||
const isAccountDeactivated = ref(false);
|
||||
const validateAccountDeactivation = [
|
||||
(v) => !!v || "Please confirm account deactivation",
|
||||
];
|
||||
const getIsTonalSnackbarVisible = ref(false);
|
||||
const ImageBase64 = ref();
|
||||
const resetForm = () => {
|
||||
accountDataLocal.value = structuredClone(accountData);
|
||||
};
|
||||
|
||||
const changeAvatar = async (file) => {
|
||||
const fileReader = new FileReader();
|
||||
const { files } = file.target;
|
||||
if (files && files.length) {
|
||||
fileReader.readAsDataURL(files[0]);
|
||||
fileReader.onload = () => {
|
||||
if (typeof fileReader.result === "string") {
|
||||
accountDataLocal.value.avatarImg = fileReader.result;
|
||||
}
|
||||
ImageBase64.value = fileReader.result.split(",")[1];
|
||||
store.dispatch("profilePicPatient", {
|
||||
image: ImageBase64.value, //ecelData,
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
onMounted(async () => {
|
||||
await store.dispatch("patientDetial");
|
||||
let list = await store.getters.getPatientDetail;
|
||||
console.log("list", list);
|
||||
accountDataLocal.value.email = list.email;
|
||||
accountDataLocal.value.first_name = list.first_name;
|
||||
accountDataLocal.value.last_name = list.last_name;
|
||||
accountDataLocal.value.phone_no = list.phone_no;
|
||||
if (!list.profile_picture) {
|
||||
accountDataLocal.value.avatarImg = avatar1;
|
||||
} else {
|
||||
accountDataLocal.value.avatarImg = list.profile_picture;
|
||||
}
|
||||
});
|
||||
// reset avatar image
|
||||
const resetAvatar = () => {
|
||||
accountDataLocal.value.avatarImg = accountData.avatarImg;
|
||||
};
|
||||
|
||||
const timezones = [
|
||||
"(GMT-11:00) International Date Line West",
|
||||
"(GMT-11:00) Midway Island",
|
||||
"(GMT-10:00) Hawaii",
|
||||
"(GMT-09:00) Alaska",
|
||||
"(GMT-08:00) Pacific Time (US & Canada)",
|
||||
"(GMT-08:00) Tijuana",
|
||||
"(GMT-07:00) Arizona",
|
||||
"(GMT-07:00) Chihuahua",
|
||||
"(GMT-07:00) La Paz",
|
||||
"(GMT-07:00) Mazatlan",
|
||||
"(GMT-07:00) Mountain Time (US & Canada)",
|
||||
"(GMT-06:00) Central America",
|
||||
"(GMT-06:00) Central Time (US & Canada)",
|
||||
"(GMT-06:00) Guadalajara",
|
||||
"(GMT-06:00) Mexico City",
|
||||
"(GMT-06:00) Monterrey",
|
||||
"(GMT-06:00) Saskatchewan",
|
||||
"(GMT-05:00) Bogota",
|
||||
"(GMT-05:00) Eastern Time (US & Canada)",
|
||||
"(GMT-05:00) Indiana (East)",
|
||||
"(GMT-05:00) Lima",
|
||||
"(GMT-05:00) Quito",
|
||||
"(GMT-04:00) Atlantic Time (Canada)",
|
||||
"(GMT-04:00) Caracas",
|
||||
"(GMT-04:00) La Paz",
|
||||
"(GMT-04:00) Santiago",
|
||||
"(GMT-03:30) Newfoundland",
|
||||
"(GMT-03:00) Brasilia",
|
||||
"(GMT-03:00) Buenos Aires",
|
||||
"(GMT-03:00) Georgetown",
|
||||
"(GMT-03:00) Greenland",
|
||||
"(GMT-02:00) Mid-Atlantic",
|
||||
"(GMT-01:00) Azores",
|
||||
"(GMT-01:00) Cape Verde Is.",
|
||||
"(GMT+00:00) Casablanca",
|
||||
"(GMT+00:00) Dublin",
|
||||
"(GMT+00:00) Edinburgh",
|
||||
"(GMT+00:00) Lisbon",
|
||||
"(GMT+00:00) London",
|
||||
];
|
||||
|
||||
const currencies = [
|
||||
"USD",
|
||||
"EUR",
|
||||
"GBP",
|
||||
"AUD",
|
||||
"BRL",
|
||||
"CAD",
|
||||
"CNY",
|
||||
"CZK",
|
||||
"DKK",
|
||||
"HKD",
|
||||
"HUF",
|
||||
"INR",
|
||||
];
|
||||
|
||||
const onSubmit = async () => {
|
||||
const { valid } = await refVForm.value.validate();
|
||||
console.log(valid);
|
||||
if (valid) {
|
||||
try {
|
||||
console.log(ImageBase64.value);
|
||||
await store.dispatch("profileUpdatePatient", {
|
||||
first_name: accountDataLocal.value.first_name,
|
||||
last_name: accountDataLocal.value.last_name,
|
||||
phone_no: accountDataLocal.value.phone_no,
|
||||
image: ImageBase64.value, //ecelData,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
// await store.dispatch("patientDetial");
|
||||
//let list = await store.getters.getPatientDetail;
|
||||
//console.log("list", list);
|
||||
//accountDataLocal.value.avatarImg = list.profile_picture;
|
||||
// accountDataLocal.value.name = list.name;
|
||||
//accountDataLocal.value.last_name = list.last_name;
|
||||
// accountDataLocal.value.phone_no = list.phone_no;
|
||||
}
|
||||
};
|
||||
|
||||
const formatPhoneNumber = () => {
|
||||
// Remove non-numeric characters from the input
|
||||
const numericValue = accountDataLocal.value.phone_no.replace(/\D/g, "");
|
||||
|
||||
// Apply formatting logic
|
||||
if (numericValue.length <= 10) {
|
||||
accountDataLocal.value.phone_no = numericValue.replace(
|
||||
/(\d{3})(\d{3})(\d{4})/,
|
||||
"($1) $2-$3"
|
||||
);
|
||||
} else {
|
||||
// Limit the input to a maximum of 14 characters
|
||||
const truncatedValue = numericValue.slice(0, 10);
|
||||
accountDataLocal.value.phone_no = truncatedValue.replace(
|
||||
/(\d{3})(\d{3})(\d{4})/,
|
||||
"($1) $2-$3"
|
||||
);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<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>
|
||||
<VRow>
|
||||
<VCol cols="12">
|
||||
<VCard>
|
||||
<VCardText>
|
||||
<div class="d-flex mb-10">
|
||||
<VSnackbar
|
||||
v-model="getIsTonalSnackbarVisible"
|
||||
:timeout="5000"
|
||||
location="top end"
|
||||
variant="flat"
|
||||
color="success"
|
||||
>
|
||||
<VIcon class="ri-success-line success-icon" />
|
||||
Profile Update Successfully
|
||||
</VSnackbar>
|
||||
<!-- 👉 Avatar -->
|
||||
<VAvatar
|
||||
rounded
|
||||
size="100"
|
||||
class="me-6"
|
||||
:image="accountDataLocal.avatarImg"
|
||||
/>
|
||||
|
||||
<!-- 👉 Upload Photo -->
|
||||
<form class="d-flex flex-column justify-center gap-4">
|
||||
<div class="d-flex flex-wrap gap-4">
|
||||
<VBtn
|
||||
color="primary"
|
||||
@click="refInputEl?.click()"
|
||||
>
|
||||
<VIcon
|
||||
icon="ri-upload-cloud-line"
|
||||
class="d-sm-none"
|
||||
/>
|
||||
<span class="d-none d-sm-block"
|
||||
>Upload Profile Image</span
|
||||
>
|
||||
</VBtn>
|
||||
<input
|
||||
ref="refInputEl"
|
||||
type="file"
|
||||
name="file"
|
||||
accept=".jpeg,.png,.jpg,GIF"
|
||||
hidden
|
||||
@input="changeAvatar"
|
||||
/>
|
||||
</div>
|
||||
<p class="text-body-1 mb-0">
|
||||
Allowed JPG, GIF or PNG. Max size of 800K
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- 👉 Form -->
|
||||
<VForm ref="refVForm">
|
||||
<VRow>
|
||||
<!-- 👉 First Name -->
|
||||
<VCol md="6" cols="12">
|
||||
<VTextField
|
||||
v-model="accountDataLocal.first_name"
|
||||
label="Name"
|
||||
:rules="[requiredName]"
|
||||
:error-messages="errors.name"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Last Name -->
|
||||
<VCol md="6" cols="12">
|
||||
<VTextField
|
||||
v-model="accountDataLocal.last_name"
|
||||
placeholder="Doe"
|
||||
label="Last Name"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Email -->
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
readonly
|
||||
v-model="accountDataLocal.email"
|
||||
label="E-mail"
|
||||
placeholder="johndoe@gmail.com"
|
||||
type="email"
|
||||
:rules="[requiredEmail, emailValidator]"
|
||||
:error-messages="errors.email"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Organization -->
|
||||
|
||||
<!-- 👉 Phone -->
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="accountDataLocal.phone_no"
|
||||
label="Phone Number"
|
||||
placeholder="+1 (917) 543-9876"
|
||||
:rules="[requiredPhone, validUSAPhone]"
|
||||
:error-messages="errors.phone"
|
||||
@input="formatPhoneNumber"
|
||||
max="14"
|
||||
pattern="^\(\d{3}\) \d{3}-\d{4}$"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Form Actions -->
|
||||
<VCol cols="12" class="d-flex flex-wrap gap-4">
|
||||
<VBtn @click.prevent="onSubmit"
|
||||
>Save changes</VBtn
|
||||
>
|
||||
</VCol>
|
||||
</VRow>
|
||||
</VForm>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
</VCol>
|
||||
</VRow>
|
||||
|
||||
<!-- Confirm Dialog -->
|
||||
<!-- <ConfirmDialog
|
||||
v-model:isDialogVisible="isConfirmDialogOpen"
|
||||
confirmation-question="Are you sure you want to deactivate your account?"
|
||||
confirm-title="Deactivated!"
|
||||
confirm-msg="Your account has been deactivated successfully."
|
||||
cancel-title="Cancelled"
|
||||
cancel-msg="Account Deactivation Cancelled!"
|
||||
/> -->
|
||||
</template>
|
262
resources/js/pages/patient/AccountSettingsSecurity.vue
Normal file
262
resources/js/pages/patient/AccountSettingsSecurity.vue
Normal file
@@ -0,0 +1,262 @@
|
||||
<script setup>
|
||||
|
||||
import { confirmedValidator, passwordValidator } from "@validators";
|
||||
import { computed, ref } from "vue";
|
||||
import { useStore } from "vuex";
|
||||
const store = useStore();
|
||||
const refVForm = ref(null);
|
||||
const isCurrentPasswordVisible = ref(false);
|
||||
const isNewPasswordVisible = ref(false);
|
||||
const isConfirmPasswordVisible = ref(false);
|
||||
const currentPassword = ref("");
|
||||
const newPassword = ref("");
|
||||
const confirmPassword = ref("");
|
||||
const passwordConfirmationTarget = computed(() => newPassword.value);
|
||||
const passwordRequirements = [
|
||||
"Minimum 8 characters long - the more, the better",
|
||||
"At least one lowercase character",
|
||||
"At least one number, symbol, or whitespace character",
|
||||
];
|
||||
|
||||
const serverKeys = [
|
||||
{
|
||||
name: "Server Key 1",
|
||||
key: "23eaf7f0-f4f7-495e-8b86-fad3261282ac",
|
||||
createdOn: "28 Apr 2021, 18:20 GTM+4:10",
|
||||
permission: "Full Access",
|
||||
},
|
||||
{
|
||||
name: "Server Key 2",
|
||||
key: "bb98e571-a2e2-4de8-90a9-2e231b5e99",
|
||||
createdOn: "12 Feb 2021, 10:30 GTM+2:30",
|
||||
permission: "Read Only",
|
||||
},
|
||||
{
|
||||
name: "Server Key 3",
|
||||
key: "2e915e59-3105-47f2-8838-6e46bf83b711",
|
||||
createdOn: "28 Dec 2020, 12:21 GTM+4:10",
|
||||
permission: "Full Access",
|
||||
},
|
||||
];
|
||||
|
||||
const recentDevicesHeaders = [
|
||||
{
|
||||
title: "BROWSER",
|
||||
key: "browser",
|
||||
},
|
||||
{
|
||||
title: "DEVICE",
|
||||
key: "device",
|
||||
},
|
||||
{
|
||||
title: "LOCATION",
|
||||
key: "location",
|
||||
},
|
||||
{
|
||||
title: "RECENT ACTIVITY",
|
||||
key: "recentActivity",
|
||||
},
|
||||
];
|
||||
|
||||
const recentDevices = [
|
||||
{
|
||||
browser: "Chrome on Windows",
|
||||
device: "HP Spectre 360",
|
||||
location: "New York, NY",
|
||||
recentActivity: "28 Apr 2022, 18:20",
|
||||
deviceIcon: {
|
||||
icon: "ri-macbook-line",
|
||||
color: "primary",
|
||||
},
|
||||
},
|
||||
{
|
||||
browser: "Chrome on iPhone",
|
||||
device: "iPhone 12x",
|
||||
location: "Los Angeles, CA",
|
||||
recentActivity: "20 Apr 2022, 10:20",
|
||||
deviceIcon: {
|
||||
icon: "ri-android-line",
|
||||
color: "error",
|
||||
},
|
||||
},
|
||||
{
|
||||
browser: "Chrome on Android",
|
||||
device: "Oneplus 9 Pro",
|
||||
location: "San Francisco, CA",
|
||||
recentActivity: "16 Apr 2022, 04:20",
|
||||
deviceIcon: {
|
||||
icon: "ri-smartphone-line",
|
||||
color: "success",
|
||||
},
|
||||
},
|
||||
{
|
||||
browser: "Chrome on macOS",
|
||||
device: "Apple iMac",
|
||||
location: "New York, NY",
|
||||
recentActivity: "28 Apr 2022, 18:20",
|
||||
deviceIcon: {
|
||||
icon: "ri-mac-line",
|
||||
color: "secondary",
|
||||
},
|
||||
},
|
||||
{
|
||||
browser: "Chrome on Windows",
|
||||
device: "HP Spectre 360",
|
||||
location: "Los Angeles, CA",
|
||||
recentActivity: "20 Apr 2022, 10:20",
|
||||
deviceIcon: {
|
||||
icon: "ri-macbook-line",
|
||||
color: "primary",
|
||||
},
|
||||
},
|
||||
{
|
||||
browser: "Chrome on Android",
|
||||
device: "Oneplus 9 Pro",
|
||||
location: "San Francisco, CA",
|
||||
recentActivity: "16 Apr 2022, 04:20",
|
||||
deviceIcon: {
|
||||
icon: "ri-android-line",
|
||||
color: "success",
|
||||
},
|
||||
},
|
||||
];
|
||||
const save = async () => {
|
||||
const { valid } = await refVForm.value.validate();
|
||||
console.log(valid);
|
||||
if (valid) {
|
||||
try {
|
||||
await store.dispatch("patientPasswordupdate", {
|
||||
password: currentPassword.value,
|
||||
new_password: newPassword.value,
|
||||
confirm_password: confirmPassword.value,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
currentPassword.value = null;
|
||||
newPassword.value = null;
|
||||
confirmPassword.value = null;
|
||||
}
|
||||
};
|
||||
|
||||
const isOneTimePasswordDialogVisible = ref(false);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VRow>
|
||||
<!-- SECTION: Change Password -->
|
||||
<VCol cols="12">
|
||||
<VCard>
|
||||
<VCardItem class="pb-6">
|
||||
<VCardTitle>Change Password</VCardTitle>
|
||||
</VCardItem>
|
||||
<VForm ref="refVForm">
|
||||
<VCardText class="pt-0">
|
||||
<!-- 👉 Current Password -->
|
||||
<VRow>
|
||||
<VCol cols="12" md="6">
|
||||
<!-- 👉 current password -->
|
||||
<VTextField v-model="currentPassword" :type="isCurrentPasswordVisible
|
||||
? 'text'
|
||||
: 'password'
|
||||
" :append-inner-icon="isCurrentPasswordVisible
|
||||
? 'ri-eye-off-line'
|
||||
: 'ri-eye-line'
|
||||
" autocomplete="on" label="Current Password" placeholder="············"
|
||||
@click:append-inner="
|
||||
isCurrentPasswordVisible =
|
||||
!isCurrentPasswordVisible
|
||||
" />
|
||||
</VCol>
|
||||
</VRow>
|
||||
|
||||
<!-- 👉 New Password -->
|
||||
<VRow>
|
||||
<VCol cols="12" md="6">
|
||||
<!-- 👉 new password -->
|
||||
<VTextField v-model="newPassword" :type="isNewPasswordVisible
|
||||
? 'text'
|
||||
: 'password'
|
||||
" :append-inner-icon="isNewPasswordVisible
|
||||
? 'ri-eye-off-line'
|
||||
: 'ri-eye-line'
|
||||
" label="New Password" autocomplete="on" placeholder="············"
|
||||
@click:append-inner="
|
||||
isNewPasswordVisible =
|
||||
!isNewPasswordVisible
|
||||
" :rules="[passwordValidator]" />
|
||||
</VCol>
|
||||
|
||||
<VCol cols="12" md="6">
|
||||
<!-- 👉 confirm password -->
|
||||
<VTextField v-model="confirmPassword" :type="isConfirmPasswordVisible
|
||||
? 'text'
|
||||
: 'password'
|
||||
" :append-inner-icon="isConfirmPasswordVisible
|
||||
? 'ri-eye-off-line'
|
||||
: 'ri-eye-line'
|
||||
" autocomplete="on" label="Confirm New Password" placeholder="············"
|
||||
@click:append-inner="
|
||||
isConfirmPasswordVisible =
|
||||
!isConfirmPasswordVisible
|
||||
" :rules="[
|
||||
(value) =>
|
||||
confirmedValidator(
|
||||
value,
|
||||
passwordConfirmationTarget
|
||||
),
|
||||
]" />
|
||||
</VCol>
|
||||
</VRow>
|
||||
</VCardText>
|
||||
|
||||
<!-- 👉 Password Requirements -->
|
||||
<VCardText>
|
||||
<h6 class="text-h6 text-medium-emphasis mt-1">
|
||||
Password Requirements:
|
||||
</h6>
|
||||
|
||||
<VList>
|
||||
<VListItem v-for="(item, index) in passwordRequirements" :key="index" class="px-0">
|
||||
<template #prepend>
|
||||
<VIcon size="8" icon="ri-circle-fill"
|
||||
color="rgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity))" />
|
||||
</template>
|
||||
<VListItemTitle class="text-medium-emphasis text-wrap">
|
||||
{{ item }}
|
||||
</VListItemTitle>
|
||||
</VListItem>
|
||||
</VList>
|
||||
|
||||
<!-- 👉 Action Buttons -->
|
||||
<div class="d-flex flex-wrap gap-4">
|
||||
<VBtn @click="save">Save changes</VBtn>
|
||||
|
||||
<!-- <VBtn
|
||||
type="reset"
|
||||
color="secondary"
|
||||
variant="outlined"
|
||||
>
|
||||
Reset
|
||||
</VBtn>-->
|
||||
</div>
|
||||
</VCardText>
|
||||
</VForm>
|
||||
</VCard>
|
||||
</VCol>
|
||||
<!-- !SECTION -->
|
||||
|
||||
<!-- SECTION Two-steps verification -->
|
||||
|
||||
<!-- !SECTION -->
|
||||
|
||||
<!-- SECTION Recent Devices -->
|
||||
|
||||
<!-- !SECTION -->
|
||||
</VRow>
|
||||
|
||||
<!-- SECTION Enable One time password -->
|
||||
|
||||
<!-- !SECTION -->
|
||||
</template>
|
205
resources/js/pages/patient/AddEditAddressDialog.vue
Normal file
205
resources/js/pages/patient/AddEditAddressDialog.vue
Normal file
@@ -0,0 +1,205 @@
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
billingAddress: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => ({
|
||||
firstName: "",
|
||||
lastName: "",
|
||||
selectedCountry: null,
|
||||
addressLine1: "",
|
||||
addressLine2: "",
|
||||
landmark: "",
|
||||
contact: "",
|
||||
country: null,
|
||||
state: "",
|
||||
zipCode: null,
|
||||
}),
|
||||
},
|
||||
isDialogVisible: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(["update:isDialogVisible", "submit"]);
|
||||
|
||||
const billingAddress = ref(structuredClone(toRaw(props.billingAddress)));
|
||||
|
||||
const resetForm = () => {
|
||||
emit("update:isDialogVisible", false);
|
||||
billingAddress.value = structuredClone(toRaw(props.billingAddress));
|
||||
};
|
||||
|
||||
const onFormSubmit = () => {
|
||||
emit("update:isDialogVisible", false);
|
||||
emit("submit", billingAddress.value);
|
||||
};
|
||||
|
||||
const selectedAddress = ref("Home");
|
||||
|
||||
const addressTypes = [
|
||||
{
|
||||
title: "Home",
|
||||
desc: "Delivery Time (7am - 9pm)",
|
||||
value: "Home",
|
||||
icon: "ri-home-smile-2-line",
|
||||
},
|
||||
{
|
||||
title: "Office",
|
||||
desc: "Delivery Time (10am - 6pm)",
|
||||
value: "Office",
|
||||
icon: "ri-building-line",
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VDialog
|
||||
:width="$vuetify.display.smAndDown ? 'auto' : 900"
|
||||
:model-value="props.isDialogVisible"
|
||||
@update:model-value="(val) => $emit('update:isDialogVisible', val)"
|
||||
>
|
||||
<VCard v-if="props.billingAddress" class="pa-sm-11 pa-3">
|
||||
<VCardText class="pt-5">
|
||||
<!-- 👉 dialog close btn -->
|
||||
|
||||
<!-- 👉 Title -->
|
||||
<div class="text-center mb-6">
|
||||
<h4 class="text-h4 mb-2">
|
||||
{{
|
||||
props.billingAddress.firstName ? "Edit" : "Add New"
|
||||
}}
|
||||
Address
|
||||
</h4>
|
||||
|
||||
<p class="text-body-1">Add Address for future billing</p>
|
||||
</div>
|
||||
|
||||
<CustomRadios
|
||||
v-model:selected-radio="selectedAddress"
|
||||
:radio-content="addressTypes"
|
||||
:grid-column="{ sm: '6', cols: '12' }"
|
||||
class="mb-5"
|
||||
>
|
||||
<template #default="items">
|
||||
<div class="d-flex flex-column">
|
||||
<div class="d-flex mb-2 align-center gap-x-1">
|
||||
<VIcon :icon="items.item.icon" size="20" />
|
||||
<div
|
||||
class="text-body-1 font-weight-medium text-high-emphasis"
|
||||
>
|
||||
{{ items.item.title }}
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-body-2 mb-0">
|
||||
{{ items.item.desc }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
</CustomRadios>
|
||||
<!-- 👉 Form -->
|
||||
<VForm @submit.prevent="onFormSubmit">
|
||||
<VRow>
|
||||
<!-- 👉 First Name -->
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="billingAddress.firstName"
|
||||
label="First Name"
|
||||
placeholder="John"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Last Name -->
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="billingAddress.lastName"
|
||||
label="Last Name"
|
||||
placeholder="Doe"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Select country -->
|
||||
|
||||
<VCol cols="12">
|
||||
<VSelect
|
||||
v-model="billingAddress.selectedCountry"
|
||||
label="Select Country"
|
||||
placeholder="Select Country"
|
||||
:items="['USA', 'Canada', 'NZ', 'Aus']"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Address Line 1 -->
|
||||
|
||||
<VCol cols="12">
|
||||
<VTextField
|
||||
v-model="billingAddress.addressLine1"
|
||||
label="Address Line 1"
|
||||
placeholder="1, New Street"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Address Line 2 -->
|
||||
|
||||
<VCol cols="12">
|
||||
<VTextField
|
||||
v-model="billingAddress.addressLine2"
|
||||
label="Address Line 2"
|
||||
placeholder="Near hospital"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Landmark -->
|
||||
|
||||
<VCol cols="12">
|
||||
<VTextField
|
||||
v-model="billingAddress.landmark"
|
||||
label="Landmark & City"
|
||||
placeholder="Near hospital, New York"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 State -->
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="billingAddress.state"
|
||||
label="State/Province"
|
||||
placeholder="New York"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Zip Code -->
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="billingAddress.zipCode"
|
||||
label="Zip Code"
|
||||
placeholder="123123"
|
||||
type="number"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<VCol cols="12">
|
||||
<VSwitch
|
||||
label="Make this default shipping address"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Submit and Cancel button -->
|
||||
<VCol cols="12" class="text-center">
|
||||
<VBtn type="submit" class="me-3"> submit </VBtn>
|
||||
|
||||
<VBtn
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
@click="resetForm"
|
||||
>
|
||||
Cancel
|
||||
</VBtn>
|
||||
</VCol>
|
||||
</VRow>
|
||||
</VForm>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
</VDialog>
|
||||
</template>
|
138
resources/js/pages/patient/ConfirmDialog.vue
Normal file
138
resources/js/pages/patient/ConfirmDialog.vue
Normal file
@@ -0,0 +1,138 @@
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
confirmationQuestion: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
isDialogVisible: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
confirmTitle: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
confirmMsg: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
cancelTitle: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
cancelMsg: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(["update:isDialogVisible", "confirm"]);
|
||||
|
||||
const unsubscribed = ref(false);
|
||||
const cancelled = ref(false);
|
||||
|
||||
const updateModelValue = (val) => {
|
||||
emit("update:isDialogVisible", val);
|
||||
};
|
||||
|
||||
const onConfirmation = () => {
|
||||
emit("confirm", true);
|
||||
updateModelValue(false);
|
||||
unsubscribed.value = true;
|
||||
};
|
||||
|
||||
const onCancel = () => {
|
||||
emit("confirm", false);
|
||||
emit("update:isDialogVisible", false);
|
||||
cancelled.value = true;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 👉 Confirm Dialog -->
|
||||
<VDialog
|
||||
max-width="500"
|
||||
:model-value="props.isDialogVisible"
|
||||
@update:model-value="updateModelValue"
|
||||
>
|
||||
<VCard class="text-center px-10 py-6">
|
||||
<VCardText>
|
||||
<VBtn
|
||||
icon
|
||||
variant="outlined"
|
||||
color="warning"
|
||||
class="my-4"
|
||||
size="x-large"
|
||||
>
|
||||
<span class="text-4xl">!</span>
|
||||
</VBtn>
|
||||
|
||||
<h6 class="text-lg font-weight-medium">
|
||||
{{ props.confirmationQuestion }}
|
||||
</h6>
|
||||
</VCardText>
|
||||
|
||||
<VCardText class="d-flex align-center justify-center gap-4">
|
||||
<VBtn variant="elevated" @click="onConfirmation">
|
||||
Confirm
|
||||
</VBtn>
|
||||
|
||||
<VBtn color="secondary" variant="outlined" @click="onCancel">
|
||||
Cancel
|
||||
</VBtn>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
</VDialog>
|
||||
|
||||
<!-- Unsubscribed -->
|
||||
<VDialog v-model="unsubscribed" max-width="500">
|
||||
<VCard>
|
||||
<VCardText class="text-center px-10 py-6">
|
||||
<VBtn
|
||||
icon
|
||||
variant="outlined"
|
||||
color="success"
|
||||
class="my-4"
|
||||
size="x-large"
|
||||
>
|
||||
<span class="text-xl">
|
||||
<VIcon icon="ri-check-line" />
|
||||
</span>
|
||||
</VBtn>
|
||||
|
||||
<h1 class="text-h4 mb-4">
|
||||
{{ props.confirmTitle }}
|
||||
</h1>
|
||||
|
||||
<p>{{ props.confirmMsg }}</p>
|
||||
|
||||
<VBtn color="success" @click="unsubscribed = false"> Ok </VBtn>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
</VDialog>
|
||||
|
||||
<!-- Cancelled -->
|
||||
<VDialog v-model="cancelled" max-width="500">
|
||||
<VCard>
|
||||
<VCardText class="text-center px-10 py-6">
|
||||
<VBtn
|
||||
icon
|
||||
variant="outlined"
|
||||
color="error"
|
||||
class="my-4"
|
||||
size="x-large"
|
||||
>
|
||||
<span class="text-2xl font-weight-light">X</span>
|
||||
</VBtn>
|
||||
|
||||
<h1 class="text-h4 mb-4">
|
||||
{{ props.cancelTitle }}
|
||||
</h1>
|
||||
|
||||
<p>{{ props.cancelMsg }}</p>
|
||||
|
||||
<VBtn color="success" @click="cancelled = false"> Ok </VBtn>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
</VDialog>
|
||||
</template>
|
126
resources/js/pages/patient/OrderDetailNotes.vue
Normal file
126
resources/js/pages/patient/OrderDetailNotes.vue
Normal file
@@ -0,0 +1,126 @@
|
||||
<script setup>
|
||||
import store from '@/store';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const props = defineProps({
|
||||
orderData: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const notes = ref([]);
|
||||
const historyNotes = computed(async () => {
|
||||
|
||||
let notesData = props.orderData.appointment_notes;
|
||||
console.log("notesData", notesData);
|
||||
for (let data of notesData) {
|
||||
if (data.note_type == 'Notes') {
|
||||
let dataObject = {}
|
||||
dataObject.note = data.note
|
||||
dataObject.doctor = props.orderData.appointment_details.provider_name;
|
||||
dataObject.date = formatDateDate(data.created_at)
|
||||
dataObject.id = data.id
|
||||
//notes.value.push(dataObject)
|
||||
}
|
||||
}
|
||||
notes.value.sort((a, b) => {
|
||||
return b.id - a.id;
|
||||
});
|
||||
console.log("getNotes", notes.value);
|
||||
store.dispatch('updateIsLoading', false)
|
||||
return notes.value
|
||||
});
|
||||
const formatDateDate = (date) => {
|
||||
const messageDate = new Date(date);
|
||||
const options = {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
};
|
||||
return messageDate.toLocaleDateString('en-US', options).replace(/\//g, '-');
|
||||
};
|
||||
onMounted(async () => {
|
||||
let notesData = props.orderData.appointment_notes;
|
||||
console.log("notesData", notesData);
|
||||
for (let data of notesData) {
|
||||
if (data.note_type == 'Notes') {
|
||||
let dataObject = {}
|
||||
dataObject.note = data.note
|
||||
dataObject.doctor = props.orderData.appointment_details.provider_name;
|
||||
dataObject.date = formatDateDate(data.created_at)
|
||||
dataObject.id = data.id
|
||||
notes.value.push(dataObject)
|
||||
}
|
||||
}
|
||||
notes.value.sort((a, b) => {
|
||||
return b.id - a.id;
|
||||
});
|
||||
console.log("getNotes", notes.value);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<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 title="Notes" v-if="notes.length > 0">
|
||||
<VCardText>
|
||||
<VTimeline truncate-line="both" align="start" side="end" line-inset="10" line-color="primary"
|
||||
density="compact" class="v-timeline-density-compact">
|
||||
|
||||
<template v-if="historyNotes">
|
||||
<VTimelineItem dot-color="yellow" size="x-small" v-for="(p_note, index) of notes" :key="index">
|
||||
<div class="d-flex justify-space-between align-center mb-3">
|
||||
<span class="app-timeline-title">{{ p_note.note }}</span>
|
||||
<span class="app-timeline-meta">{{ p_note.date }}</span>
|
||||
</div>
|
||||
<p class="app-timeline-text mb-0">
|
||||
{{ p_note.doctor }}
|
||||
</p>
|
||||
</VTimelineItem>
|
||||
</template>
|
||||
|
||||
</VTimeline>
|
||||
</VCardText>
|
||||
|
||||
</VCard>
|
||||
|
||||
<VCard v-else>
|
||||
<VAlert border="start" color="rgb(var(--v-theme-yellow-theme-button))" variant="tonal">
|
||||
<div class="text-center">No data found</div>
|
||||
</VAlert>
|
||||
</VCard>
|
||||
|
||||
<!-- <VList class="pb-0" lines="two" v-if="historyNotes">
|
||||
<template v-if="notes.length > 0" v-for="(p_note, index) of notes" :key="index">
|
||||
<VListItem class="pb-0" border>
|
||||
<VListItemTitle>
|
||||
<span class="pb-0">{{ p_note.note }}</span>
|
||||
<p class="text-start fs-5 mb-0 pb-0 text-grey">
|
||||
<small> {{ p_note.doctor }}</small>
|
||||
</p>
|
||||
<p class="text-end fs-5 mb-0 pb-0 text-grey">
|
||||
<small> {{ p_note.date }}</small>
|
||||
</p>
|
||||
</VListItemTitle>
|
||||
</VListItem>
|
||||
<VDivider v-if="index !== notes.length - 1" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<VCard>
|
||||
<VAlert border="start" color="rgb(var(--v-theme-yellow))" variant="tonal">
|
||||
<div class="text-center">No data found</div>
|
||||
</VAlert>
|
||||
|
||||
</VCard>
|
||||
</template>
|
||||
</VList> -->
|
||||
</template>
|
362
resources/js/pages/patient/OrderDetailPrecrption.vue
Normal file
362
resources/js/pages/patient/OrderDetailPrecrption.vue
Normal file
@@ -0,0 +1,362 @@
|
||||
<script setup>
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useStore } from "vuex";
|
||||
const store = useStore();
|
||||
const props = defineProps({
|
||||
orderData: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const itemsPrescriptions = ref([]);
|
||||
// const patientId = route.params.patient_id;
|
||||
const prescriptionLoaded = ref(false)
|
||||
const doctorName = ref('');
|
||||
const prescription = computed(async () => {
|
||||
await fetchPrescriptions()
|
||||
return prescriptionLoaded.value ? itemsPrescriptions.value : null
|
||||
})
|
||||
const fetchPrescriptions = async () => {
|
||||
store.dispatch('updateIsLoading', true)
|
||||
await getprescriptionList()
|
||||
doctorName.value = props.orderData.appointment_details.provider_name
|
||||
store.dispatch('updateIsLoading', false)
|
||||
prescriptionLoaded.value = true
|
||||
}
|
||||
const getprescriptionList = async () => {
|
||||
|
||||
let prescriptions = props.orderData.prescription;
|
||||
// itemsPrescriptions.value = store.getters.getPrescriptionList
|
||||
for (let data of prescriptions) {
|
||||
let dataObject = {}
|
||||
dataObject.brand = data.brand
|
||||
dataObject.direction_one = data.direction_one
|
||||
dataObject.direction_quantity = data.direction_quantity
|
||||
dataObject.direction_two = data.direction_two
|
||||
dataObject.date = formatDateDate(data.created_at)
|
||||
dataObject.dosage = data.dosage
|
||||
dataObject.from = data.from
|
||||
dataObject.name = data.name
|
||||
dataObject.quantity = data.quantity
|
||||
dataObject.refill_quantity = data.refill_quantity
|
||||
dataObject.status = data.status
|
||||
dataObject.comments = data.comments
|
||||
itemsPrescriptions.value.push(dataObject)
|
||||
}
|
||||
itemsPrescriptions.value.sort((a, b) => {
|
||||
return b.id - a.id;
|
||||
});
|
||||
console.log("itemsPrescriptions", itemsPrescriptions.value);
|
||||
|
||||
};
|
||||
const formatDate = (dateString) => {
|
||||
return new Date(dateString).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric'
|
||||
})
|
||||
}
|
||||
|
||||
const formatTime = (dateString) => {
|
||||
return new Date(dateString).toLocaleTimeString('en-US', {
|
||||
hour: 'numeric',
|
||||
minute: 'numeric'
|
||||
})
|
||||
}
|
||||
onMounted(async () => {
|
||||
let prescriptions = props.orderData.prescription;
|
||||
console.log('props.orderData', props.orderData.prescription)
|
||||
itemsPrescriptions.value = prescriptions
|
||||
});
|
||||
const getStatusColor = (status) => {
|
||||
switch (status) {
|
||||
case 'pending':
|
||||
return 'warning'; // Use Vuetify's warning color (typically yellow)
|
||||
case 'shipped':
|
||||
return '#45B8AC'; // Use Vuetify's primary color (typically blue)
|
||||
case 'delivered':
|
||||
return 'green';
|
||||
case 'returned':
|
||||
return 'red';
|
||||
case 'results':
|
||||
return 'blue';
|
||||
default:
|
||||
return 'grey'; // Use Vuetify's grey color for any other status
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<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>
|
||||
|
||||
<template v-if="itemsPrescriptions.length > 0">
|
||||
<v-row>
|
||||
<v-col v-for="prescription in itemsPrescriptions" :key="prescription.prescription_id" cols="12" md="4">
|
||||
<v-card class="mx-auto mb-4" elevation="4" hover>
|
||||
<v-img height="200" src="https://cdn.pixabay.com/photo/2016/11/23/15/03/medication-1853400_1280.jpg"
|
||||
class="white--text align-end" gradient="to bottom, rgba(0,0,0,.1), rgba(0,0,0,.5)">
|
||||
<v-card-title class="text-h5" style="color: #fff;">{{ prescription.prescription_name
|
||||
}}</v-card-title>
|
||||
</v-img>
|
||||
|
||||
<v-card-text>
|
||||
|
||||
|
||||
<div class="my-4 text-subtitle-1">
|
||||
|
||||
{{ prescription.prescription_price }}
|
||||
</div>
|
||||
|
||||
<v-chip :color="getStatusColor(prescription.status)" text-color="white" small class="mr-2">
|
||||
{{ prescription.status }}
|
||||
</v-chip>
|
||||
|
||||
|
||||
</v-card-text>
|
||||
|
||||
<v-divider class="mx-4"></v-divider>
|
||||
|
||||
<v-card-text>
|
||||
<v-row dense>
|
||||
<v-col cols="6">
|
||||
<v-icon small color="rgb(var(--v-theme-yellow))">mdi-pill</v-icon>
|
||||
<span class="ml-1">Dosage:{{ prescription.dosage }}</span>
|
||||
</v-col>
|
||||
<v-col cols="6">
|
||||
<v-icon small color="rgb(var(--v-theme-yellow))">mdi-bottle-tonic</v-icon>
|
||||
<span class="ml-1">Quantity:{{ prescription.quantity }}</span>
|
||||
</v-col>
|
||||
<v-col cols="6">
|
||||
<v-icon small color="rgb(var(--v-theme-yellow))">mdi-calendar</v-icon>
|
||||
<span class="ml-1">{{ formatDate(prescription.prescription_date) }}</span>
|
||||
</v-col>
|
||||
<v-col cols="6">
|
||||
<v-icon small color="rgb(var(--v-theme-yellow))">mdi-clock-outline</v-icon>
|
||||
<span class="ml-1">{{ formatTime(prescription.prescription_date) }}</span>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions>
|
||||
<v-btn color="rgb(var(--v-theme-yellow-theme-button))" text>
|
||||
More Details
|
||||
</v-btn>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn icon @click="prescription.show = !prescription.show">
|
||||
<v-icon color="rgb(var(--v-theme-yellow-theme-button))">{{ prescription.show ?
|
||||
'mdi-chevron-up' :
|
||||
'mdi-chevron-down'
|
||||
}}</v-icon>
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
|
||||
<v-expand-transition>
|
||||
<div v-show="prescription.show">
|
||||
<v-divider></v-divider>
|
||||
<v-card-text>
|
||||
<v-row dense>
|
||||
<v-col cols="12">
|
||||
<strong>Provider:</strong> {{ props.orderData.appointment_details.provider_name
|
||||
}}
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<strong>Brand:</strong> {{ prescription.brand }}
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<strong>From:</strong> {{ prescription.from }}
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<strong>Direction One:</strong> {{ prescription.direction_one }}
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<strong>Direction Two:</strong> {{ prescription.direction_two }}
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<strong>Refill Quantity:</strong> {{ prescription.refill_quantity }}
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<strong>Direction Quantity:</strong> {{ prescription.direction_quantity }}
|
||||
</v-col>
|
||||
<v-col cols="12" v-if="prescription.comments">
|
||||
<strong>Comments:</strong> {{ prescription.comments }}
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
</div>
|
||||
</v-expand-transition>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<VExpansionPanels variant="accordion" style="display: none;">
|
||||
<VExpansionPanel v-for="(item, index) in itemsPrescriptions" :key="index">
|
||||
<div>
|
||||
|
||||
<VExpansionPanelTitle collapse-icon="mdi-chevron-down" expand-icon="mdi-chevron-right"
|
||||
style="margin-left: 0px !important;">
|
||||
<p class=""><b> {{ item.name }}</b>
|
||||
<br />
|
||||
<div class=" pt-2"> {{ doctorName }}</div>
|
||||
<div class=" pt-2">{{ item.date }}</div>
|
||||
</p>
|
||||
|
||||
|
||||
<v-row>
|
||||
|
||||
</v-row>
|
||||
|
||||
<span class="v-expansion-panel-title__icon badge text-warning"
|
||||
v-if="item.status == null">Pending</span>
|
||||
<span class="v-expansion-panel-title__icon badge" v-else>
|
||||
<v-chip :color="getStatusColor(item.status)" label size="small" variant="text">
|
||||
{{ item.status }}
|
||||
</v-chip></span>
|
||||
</VExpansionPanelTitle>
|
||||
<VExpansionPanelText class="pt-0">
|
||||
|
||||
<v-row class='mt-1'>
|
||||
<v-col cols="12" md="4" sm="6">
|
||||
<p class='heading'><b>Brand:</b></p>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4" sm="6">
|
||||
<p>{{ item.brand }}</p>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row class='mt-1'>
|
||||
<v-col cols="12" md="4" sm="6">
|
||||
<p class='heading'><b>From:</b></p>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4" sm="6">
|
||||
<p>{{ item.from }}</p>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row class='mt-1'>
|
||||
<v-col cols="12" md="4" sm="6">
|
||||
<p class='heading'><b>Dosage:</b></p>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4" sm="6">
|
||||
<p>{{ item.dosage }}</p>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row class='mt-1'>
|
||||
<v-col cols="12" md="4" sm="6">
|
||||
<p class='heading'><b>Quantity:</b></p>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4" sm="6">
|
||||
<p>{{ item.quantity }}</p>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row class='mt-1'>
|
||||
<v-col cols="12" md="4" sm="6">
|
||||
<p class='heading'><b>Direction Quantity:</b></p>
|
||||
</v-col>
|
||||
<v-col cols="12" md="8" sm="6">
|
||||
<p>{{ item.direction_quantity }}</p>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row class='mt-1'>
|
||||
<v-col cols="12" md="4" sm="6">
|
||||
<p class='heading'><b>Direction One:</b></p>
|
||||
</v-col>
|
||||
<v-col cols="12" md="8" sm="6">
|
||||
<p>{{ item.direction_one }} </p>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row class='mt-1'>
|
||||
<v-col cols="12" md="4" sm="6">
|
||||
<p class='heading'><b>Direction Two:</b></p>
|
||||
</v-col>
|
||||
<v-col cols="12" md="8" sm="6">
|
||||
<p>{{ item.direction_two }} </p>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row class='mt-1'>
|
||||
<v-col cols="12" md="4" sm="6">
|
||||
<p class='heading'><b>Refill Quantity:</b></p>
|
||||
</v-col>
|
||||
<v-col cols="12" md="8" sm="6">
|
||||
<p>{{ item.refill_quantity }}</p>
|
||||
</v-col>
|
||||
|
||||
</v-row>
|
||||
<v-row class='mt-1'>
|
||||
<v-col cols="12" md="4" sm="6">
|
||||
<p class='heading'><b>Status:</b></p>
|
||||
</v-col>
|
||||
<v-col cols="12" md="8" sm="6">
|
||||
<p v-if="item.status == null" class="text-warning">Pending</p>
|
||||
<p v-else>{{ item.status }}</p>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row class='mt-1'>
|
||||
<v-col cols="12" md="4" sm="6">
|
||||
<p class='heading'><b>Comments:</b></p>
|
||||
</v-col>
|
||||
<v-col cols="12" md="8" sm="8">
|
||||
<p>{{ item.comments }} </p>
|
||||
</v-col>
|
||||
|
||||
</v-row>
|
||||
|
||||
</VExpansionPanelText>
|
||||
</div>
|
||||
|
||||
|
||||
</VExpansionPanel>
|
||||
<br />
|
||||
</VExpansionPanels>
|
||||
</template>
|
||||
<template v-else="prescriptionLoaded">
|
||||
<VCard>
|
||||
<VCard>
|
||||
<VAlert border="start" color="rgb(var(--v-theme-yellow-theme-button))" variant="tonal">
|
||||
<div class="text-center">No data found</div>
|
||||
</VAlert>
|
||||
|
||||
</VCard>
|
||||
</VCard>
|
||||
|
||||
</template>
|
||||
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
button.v-expansion-panel-title {
|
||||
background-color: rgb(var(--v-theme-yellow)) !important;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
button.v-expansion-panel-title.bg-secondary {
|
||||
background-color: rgb(var(--v-theme-yellow)) !important;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
button.v-expansion-panel-title {
|
||||
background-color: rgb(var(--v-theme-yellow)) !important;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
span.v-expansion-panel-title__icon {
|
||||
margin-left: 0px !important;
|
||||
}
|
||||
|
||||
span.v-expansion-panel-title__icon {
|
||||
color: #fff
|
||||
}
|
||||
|
||||
.v-expansion-panel {
|
||||
background-color: #fff;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1);
|
||||
margin-bottom: 10px;
|
||||
overflow: hidden;
|
||||
transition: box-shadow 0.3s ease;
|
||||
}
|
||||
</style>
|
193
resources/js/pages/patient/UserInfoEditDialog.vue
Normal file
193
resources/js/pages/patient/UserInfoEditDialog.vue
Normal file
@@ -0,0 +1,193 @@
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
userData: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => ({
|
||||
avatar: "",
|
||||
company: "",
|
||||
contact: "",
|
||||
country: null,
|
||||
currentPlan: "",
|
||||
email: "",
|
||||
fullName: "",
|
||||
id: 0,
|
||||
role: "",
|
||||
status: null,
|
||||
username: "",
|
||||
language: [],
|
||||
projectDone: 0,
|
||||
taskDone: 0,
|
||||
taxId: "",
|
||||
}),
|
||||
},
|
||||
isDialogVisible: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(["submit", "update:isDialogVisible"]);
|
||||
|
||||
const userData = ref(structuredClone(toRaw(props.userData)));
|
||||
|
||||
watch(props, () => {
|
||||
userData.value = structuredClone(toRaw(props.userData));
|
||||
});
|
||||
|
||||
const onFormSubmit = () => {
|
||||
emit("update:isDialogVisible", false);
|
||||
emit("submit", userData.value);
|
||||
};
|
||||
|
||||
const onFormReset = () => {
|
||||
userData.value = structuredClone(toRaw(props.userData));
|
||||
emit("update:isDialogVisible", false);
|
||||
};
|
||||
|
||||
const dialogVisibleUpdate = (val) => {
|
||||
emit("update:isDialogVisible", val);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VDialog
|
||||
:width="$vuetify.display.smAndDown ? 'auto' : 900"
|
||||
:model-value="props.isDialogVisible"
|
||||
@update:model-value="dialogVisibleUpdate"
|
||||
>
|
||||
<VCard class="pa-sm-11 pa-3">
|
||||
<!-- 👉 dialog close btn -->
|
||||
|
||||
<VCardText class="pt-5">
|
||||
<div class="text-center pb-6">
|
||||
<h4 class="text-h4 mb-2">Edit User Information</h4>
|
||||
<div class="text-body-1">
|
||||
Updating user details will receive a privacy audit.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 👉 Form -->
|
||||
<VForm class="mt-4" @submit.prevent="onFormSubmit">
|
||||
<VRow>
|
||||
<!-- 👉 First Name -->
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="userData.fullName.split(' ')[0]"
|
||||
label="First Name"
|
||||
placeholder="John"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Last Name -->
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="userData.fullName.split(' ')[1]"
|
||||
label="Last Name"
|
||||
placeholder="doe"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 User Name -->
|
||||
|
||||
<VCol cols="12">
|
||||
<VTextField
|
||||
v-model="userData.username"
|
||||
label="Username"
|
||||
placeholder="John Doe"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Billing Email -->
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="userData.email"
|
||||
label="Billing Email"
|
||||
placeholder="johndoe@email.com"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Status -->
|
||||
<VCol cols="12" md="6">
|
||||
<VSelect
|
||||
v-model="userData.status"
|
||||
:items="['Active', 'Inactive', 'Pending']"
|
||||
label="Status"
|
||||
placeholder="Status"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Tax Id -->
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="userData.taxId"
|
||||
label="Tax Id"
|
||||
placeholder="Tax-3456789"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Contact -->
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
v-model="userData.contact"
|
||||
label="Contact"
|
||||
placeholder="+1 9876543210"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Language -->
|
||||
<VCol cols="12" md="6">
|
||||
<VSelect
|
||||
v-model="userData.language"
|
||||
:items="['English', 'Spanish', 'French']"
|
||||
label="Language"
|
||||
placeholder="English"
|
||||
chips
|
||||
closable-chips
|
||||
multiple
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Country -->
|
||||
<VCol cols="12" md="6">
|
||||
<VSelect
|
||||
v-model="userData.country"
|
||||
:items="[
|
||||
'United States',
|
||||
'United Kingdom',
|
||||
'France',
|
||||
]"
|
||||
label="Country"
|
||||
placeholder="United States"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Switch -->
|
||||
<VCol cols="12">
|
||||
<VSwitch
|
||||
density="compact"
|
||||
label="Use as a billing address?"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Submit and Cancel -->
|
||||
<VCol
|
||||
cols="12"
|
||||
class="d-flex flex-wrap justify-center gap-4"
|
||||
>
|
||||
<VBtn type="submit"> Submit </VBtn>
|
||||
|
||||
<VBtn
|
||||
color="secondary"
|
||||
variant="outlined"
|
||||
@click="onFormReset"
|
||||
>
|
||||
Cancel
|
||||
</VBtn>
|
||||
</VCol>
|
||||
</VRow>
|
||||
</VForm>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
</VDialog>
|
||||
</template>
|
170
resources/js/pages/patient/complete-appintment-detail.vue
Normal file
170
resources/js/pages/patient/complete-appintment-detail.vue
Normal file
@@ -0,0 +1,170 @@
|
||||
<script setup>
|
||||
import Notes from '@/pages/patient/notes.vue';
|
||||
import Prescription from '@/pages/patient/prescription.vue';
|
||||
import store from '@/store';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
// const patientId = route.params.patient_id;
|
||||
const appointmentId = route.params.appiontment_id;
|
||||
const currentTab = ref(0)
|
||||
const appiontmentID = ref();
|
||||
const dcotorName = ref();
|
||||
const starttime = ref();
|
||||
const endtime = ref();
|
||||
const duration = ref();
|
||||
const patientName = ref();
|
||||
const patientEmail = ref();
|
||||
const patientPhone = ref();
|
||||
const patientAddress = ref();
|
||||
const patientDob = ref();
|
||||
const patientData = ref(null);
|
||||
const his = computed(async () => {
|
||||
// store.dispatch('updateIsLoading', true)
|
||||
await store.dispatch('getPatientInfo')
|
||||
// notes.value = store.getters.getPatientNotes;
|
||||
patientData.value = store.getters.getPatient;
|
||||
console.log("appointmentData1", patientData.value);
|
||||
// appiontmentID.value = appointmentId;
|
||||
patientName.value = patientData.value.first_name + ' ' + patientData.value.last_name;
|
||||
patientEmail.value = patientData.value.email;
|
||||
patientDob.value = changeFormat(patientData.value.dob);
|
||||
patientPhone.value = patientData.value.phone_no;
|
||||
patientAddress.value = patientData.value.address + ' ' +
|
||||
patientData.value.city + ' ' +
|
||||
patientData.value.state + ' ' +
|
||||
patientData.value.country;
|
||||
// localStorage.setItem('meetingPatientAppiontmentId', appiontmentID.value)
|
||||
// console.log("notesData", notesData.value[0].appointment.id);
|
||||
// store.dispatch('updateIsLoading', false)
|
||||
});
|
||||
|
||||
function changeFormat(dateFormat) {
|
||||
const dateParts = dateFormat.split('-'); // Assuming date is in yyyy-mm-dd format
|
||||
const year = parseInt(dateParts[0]);
|
||||
const month = parseInt(dateParts[1]); // No need for padding
|
||||
const day = parseInt(dateParts[2]); // No need for padding
|
||||
|
||||
// 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;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<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>
|
||||
|
||||
<div class="">
|
||||
<VCardTitle class="my-1"><b>Appiontments Detail</b></VCardTitle>
|
||||
<VRow>
|
||||
<VCol cols="12" md="8" v-if="his">
|
||||
<Prescription></Prescription>
|
||||
<Notes></Notes>
|
||||
|
||||
</VCol>
|
||||
<VCol cols="12" md="4">
|
||||
<VCard class="mb-4">
|
||||
<VCardText>
|
||||
|
||||
<h5 class="mt-2 mb-2">ABOUT</h5>
|
||||
|
||||
<VList class="card-list text-medium-emphasis mb-2">
|
||||
<VListItem>
|
||||
<template #prepend>
|
||||
<VIcon icon="mdi-account" size="20" class="me-2" />
|
||||
</template>
|
||||
<VListItemTitle>
|
||||
<!-- <span class="font-weight-medium me-1">Full Name:</span> -->
|
||||
<span> {{ patientName }}</span>
|
||||
</VListItemTitle>
|
||||
</VListItem>
|
||||
<VListItem>
|
||||
<template #prepend>
|
||||
<VIcon icon="mdi-email" size="20" class="me-2" />
|
||||
</template>
|
||||
<VListItemTitle>
|
||||
<!-- <span class="font-weight-medium me-1">Email:</span> -->
|
||||
<span>{{ patientEmail }}</span>
|
||||
</VListItemTitle>
|
||||
</VListItem>
|
||||
<VListItem>
|
||||
<template #prepend>
|
||||
<VIcon icon="tabler-calendar" size="20" class="me-2" />
|
||||
</template>
|
||||
<VListItemTitle>
|
||||
<!-- <span class="font-weight-medium me-1">DOB:</span> -->
|
||||
<span>
|
||||
{{ patientDob }}</span>
|
||||
</VListItemTitle>
|
||||
</VListItem>
|
||||
<VListItem>
|
||||
<template #prepend>
|
||||
<VIcon icon="mdi-phone" size="20" class="me-2" />
|
||||
</template>
|
||||
<VListItemTitle>
|
||||
<!-- <span class="font-weight-medium me-1">Phone:</span> -->
|
||||
<span>{{
|
||||
patientPhone }}</span>
|
||||
</VListItemTitle>
|
||||
</VListItem>
|
||||
<VListItem>
|
||||
<template #prepend>
|
||||
<VIcon icon="mdi-address-marker" size="20" class="me-2" />
|
||||
</template>
|
||||
<VListItemTitle>
|
||||
<!-- <span class="font-weight-medium me-1">Address:</span> -->
|
||||
<span>{{ patientAddress }}</span>
|
||||
</VListItemTitle>
|
||||
</VListItem>
|
||||
</VList>
|
||||
<!-- <VDivider></VDivider> -->
|
||||
|
||||
<!-- <h5 class="mt-2 mb-2"></h5> -->
|
||||
|
||||
<VList class="card-list text-medium-emphasis">
|
||||
|
||||
</VList>
|
||||
|
||||
|
||||
</VCardText>
|
||||
</VCard>
|
||||
</VCol>
|
||||
</VRow>
|
||||
|
||||
<!-- <VTabs v-model="currentTab" direction="vertical">
|
||||
<VTab>
|
||||
<VIcon start icon="tabler-edit" />
|
||||
Notes
|
||||
</VTab>
|
||||
|
||||
<VTab>
|
||||
<VIcon start icon="tabler-lock" />
|
||||
Prescriptions
|
||||
</VTab>
|
||||
</VTabs> -->
|
||||
<!-- </div> -->
|
||||
|
||||
<!-- <VCardText> -->
|
||||
<!-- <VWindow v-model="currentTab" class="ms-3"> -->
|
||||
<!-- <VWindowItem> -->
|
||||
<!-- <Notes></Notes> -->
|
||||
<!-- </VWindowItem> -->
|
||||
|
||||
<!-- <VWindowItem> -->
|
||||
<!-- <Prescription></Prescription> -->
|
||||
<!-- </VWindowItem> -->
|
||||
<!-- </VWindow> -->
|
||||
<!-- </VCardText> -->
|
||||
</div>
|
||||
|
||||
</template>
|
1303
resources/js/pages/patient/complete-appiontments.vue
Normal file
1303
resources/js/pages/patient/complete-appiontments.vue
Normal file
File diff suppressed because it is too large
Load Diff
278
resources/js/pages/patient/lab-kits.vue
Normal file
278
resources/js/pages/patient/lab-kits.vue
Normal file
@@ -0,0 +1,278 @@
|
||||
<script setup>
|
||||
import { computed, onMounted, reactive, ref } from "vue";
|
||||
import { useStore } from "vuex";
|
||||
const route = useRoute();
|
||||
const isConfirmDialogVisible = ref(false);
|
||||
const isUserInfoEditDialogVisible = ref(false);
|
||||
const isEditAddressDialogVisible = ref(false);
|
||||
const scheduleDate = ref('');
|
||||
const scheduleTime = ref('');
|
||||
const isLoading = ref(true);
|
||||
const state = reactive({
|
||||
addLabOrder: false,
|
||||
selectedTestKitId: null,
|
||||
valid: false,
|
||||
testKits: [],
|
||||
item_id: null,
|
||||
labKitList: []
|
||||
});
|
||||
const props = defineProps({
|
||||
orderID: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
const getFieldRules = (fieldName, errorMessage) => {
|
||||
if (fieldName) {
|
||||
return [
|
||||
v => !!v || `${errorMessage}`,
|
||||
// Add more validation rules as needed
|
||||
];
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const headersLab = [
|
||||
{
|
||||
title: "Product",
|
||||
key: "item_name",
|
||||
},
|
||||
{
|
||||
title: "Lab Kit",
|
||||
key: "lab_kit_name",
|
||||
},
|
||||
{
|
||||
title: "Status",
|
||||
key: "status",
|
||||
},
|
||||
{
|
||||
title: "Results",
|
||||
key: "result",
|
||||
},
|
||||
|
||||
];
|
||||
const openDialog = (item) => {
|
||||
|
||||
state.item_id = item.id
|
||||
state.addLabOrder = true
|
||||
};
|
||||
const openPdfInNewTab = (url) => {
|
||||
window.open(url, '_blank')
|
||||
}
|
||||
const storeTestKit = async () => {
|
||||
await store.dispatch('saveOrderLabKitBYitems', {
|
||||
lab_kit_id: state.selectedTestKitId,
|
||||
item_id: state.item_id,
|
||||
cart_id: route.params.id
|
||||
})
|
||||
|
||||
console.log('Selected Test Kit:', state.selectedTestKitId);
|
||||
|
||||
state.addLabOrder = false;
|
||||
state.selectedTestKitId = null
|
||||
state.item_id = null
|
||||
|
||||
};
|
||||
const store = useStore();
|
||||
const orderData = ref(null);
|
||||
const pateintDetail = ref({});
|
||||
const productItems = ref([]);
|
||||
const filteredOrders = computed(() => {
|
||||
let filtered = store.getters.getPatientOrderDetail;
|
||||
|
||||
return filtered;
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const testKits = computed(async () => {
|
||||
//await store.dispatch('getLabKitProductList', {})
|
||||
// console.log(store.getters.getLabOrderProductList)
|
||||
|
||||
//state.testKits = store.getters.getLabOrderProductList
|
||||
});
|
||||
onMounted(async () => {
|
||||
await store.dispatch('getOrderLabKitPatient', { cart_id: route.params.id })
|
||||
|
||||
state.labKitList = store.getters.getOrderLabKitPatient
|
||||
console.log('state.testKits', state.labKitList)
|
||||
isLoading.value = false
|
||||
|
||||
});
|
||||
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 getStatusColorLabKit = (status) => {
|
||||
switch (status) {
|
||||
case "Ordered":
|
||||
return "orange";
|
||||
case "Shipped":
|
||||
return "blue";
|
||||
case "Delivered":
|
||||
return "green";
|
||||
case "Cancelled":
|
||||
return "red";
|
||||
case "Waiting For Results":
|
||||
return "red";
|
||||
default:
|
||||
return "gray";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
</script>
|
||||
<template>
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- 👉 Order Details -->
|
||||
|
||||
<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>
|
||||
|
||||
|
||||
<VCard title="Lab Kits" style="margin-bottom: 10px;" v-if="state.labKitList.length > 0">
|
||||
<VCardText>
|
||||
<div class="table-container">
|
||||
<VDataTable :headers="headersLab" :loading="isLoading" :items="state.labKitList" class="text-no-wrap ">
|
||||
<template #item.item_name="{ item }">
|
||||
<div class="d-flex gap-x-3">
|
||||
|
||||
<div class="d-flex flex-column align-center">
|
||||
<h5 style="margin-bottom: 0px; font-size: 0.83em;">
|
||||
{{ item.item_name }}
|
||||
</h5>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #item.lab_kit_name="{ item }">
|
||||
<div class="d-flex gap-x-3">
|
||||
|
||||
<div class="d-flex flex-column align-center">
|
||||
<h5 style="margin-bottom: 0px; font-size: 0.83em;">
|
||||
{{ item.lab_kit_name }}
|
||||
</h5>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<template #item.status="{ item }">
|
||||
<span>
|
||||
<VChip variant="tonal" :color="getStatusColorLabKit(item.status)" size="small">
|
||||
{{ item.status }}
|
||||
</VChip>
|
||||
</span>
|
||||
</template>
|
||||
<template #item.result="{ item }">
|
||||
<span v-if="item.result">
|
||||
<a href="#" @click="openPdfInNewTab(item.result)" target="_blank" class="custom-link">
|
||||
<div class="d-inline-flex align-center">
|
||||
<img :src="pdf" height="20" class="me-2" alt="img">
|
||||
<span class="app-timeline-text font-weight-medium">
|
||||
results.pdf
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
</span>
|
||||
<span v-else>
|
||||
Waiting For Result
|
||||
</span>
|
||||
</template>
|
||||
|
||||
|
||||
<template #bottom />
|
||||
</VDataTable>
|
||||
</div>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</template>
|
||||
<style scoped>
|
||||
.appointment-details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
font-weight: bold;
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
::-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 */
|
||||
}
|
||||
</style>
|
87
resources/js/pages/patient/main-orders-detail-tabs.vue
Normal file
87
resources/js/pages/patient/main-orders-detail-tabs.vue
Normal file
@@ -0,0 +1,87 @@
|
||||
<script setup>
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
|
||||
import Notes from "@/pages/patient/OrderDetailNotes.vue";
|
||||
import Prescription from "@/pages/patient/OrderDetailPrecrption.vue";
|
||||
|
||||
import OrderDetail from "@/pages/patient/orders-detail.vue";
|
||||
|
||||
const route = useRoute();
|
||||
const isConfirmDialogVisible = ref(false);
|
||||
const isUserInfoEditDialogVisible = ref(false);
|
||||
const isEditAddressDialogVisible = ref(false);
|
||||
const currentTab = ref("tab-1");
|
||||
|
||||
import { useStore } from "vuex";
|
||||
const store = useStore();
|
||||
const orderData = ref(null);
|
||||
const pateintDetail = ref({});
|
||||
const productItems = ref([]);
|
||||
const userTab = ref(null);
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
icon: "mdi-clipboard-text-outline",
|
||||
title: "Order Detail",
|
||||
},
|
||||
// {
|
||||
// icon: "mdi-note-text-outline",
|
||||
// title: "Notes",
|
||||
// },
|
||||
// {
|
||||
// icon: "mdi-prescription",
|
||||
// title: "Prescriptions",
|
||||
// },
|
||||
];
|
||||
const filteredOrders = computed(() => {
|
||||
let filtered = store.getters.getPatientOrderDetail;
|
||||
|
||||
return filtered;
|
||||
});
|
||||
onMounted(async () => {
|
||||
store.dispatch("updateIsLoading", true);
|
||||
await store.dispatch("orderDetailPatient", {
|
||||
id: route.params.id,
|
||||
});
|
||||
orderData.value = store.getters.getPatientOrderDetail;
|
||||
console.log(orderData.value);
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<VTabs v-model="userTab" grow>
|
||||
<VTab v-for="tab in tabs" :key="tab.icon">
|
||||
<VIcon start :icon="tab.icon" />
|
||||
<span>{{ tab.title }}</span>
|
||||
</VTab>
|
||||
</VTabs>
|
||||
<VWindow
|
||||
v-model="userTab"
|
||||
class="mt-6 disable-tab-transition"
|
||||
:touch="false"
|
||||
>
|
||||
<VWindowItem>
|
||||
<OrderDetail />
|
||||
</VWindowItem>
|
||||
|
||||
<VWindowItem>
|
||||
<Notes :order-data="orderData" />
|
||||
</VWindowItem>
|
||||
|
||||
<VWindowItem>
|
||||
<Prescription :order-data="orderData" />
|
||||
</VWindowItem>
|
||||
</VWindow>
|
||||
</template>
|
||||
<style scoped>
|
||||
.text-primary {
|
||||
color: rgb(var(--v-theme-yellow)) !important;
|
||||
}
|
||||
|
||||
.text-primary span {
|
||||
color: rgb(var(--v-theme-yellow-theme-button)) !important;
|
||||
}
|
||||
|
||||
.text-primary svg {
|
||||
color: rgb(var(--v-theme-yellow-theme-button)) !important;
|
||||
}
|
||||
</style>
|
104
resources/js/pages/patient/notes.vue
Normal file
104
resources/js/pages/patient/notes.vue
Normal file
@@ -0,0 +1,104 @@
|
||||
<script setup>
|
||||
import store from '@/store';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
// const patientId = route.params.patient_id;
|
||||
const appointmentId = route.params.appiontment_id;
|
||||
const notes = ref([]);
|
||||
const historyNotes = computed(async () => {
|
||||
// console.log('...........', patientId, appointmentId);
|
||||
await store.dispatch('getPatientPrescriptionNotes', {
|
||||
appointment_id: appointmentId,
|
||||
})
|
||||
// notes.value = store.getters.getPatientNotes;
|
||||
let notesData = store.getters.getPatientNotes;
|
||||
console.log("notesData", notesData);
|
||||
for (let data of notesData) {
|
||||
if (data.note_type == 'Notes') {
|
||||
let dataObject = {}
|
||||
dataObject.note = data.note
|
||||
dataObject.doctor = data.telemedPro.name
|
||||
dataObject.date = formatDateDate(data.created_at)
|
||||
dataObject.id = data.id
|
||||
notes.value.push(dataObject)
|
||||
}
|
||||
}
|
||||
notes.value.sort((a, b) => {
|
||||
return b.id - a.id;
|
||||
});
|
||||
console.log("getNotes", notes.value);
|
||||
store.dispatch('updateIsLoading', false)
|
||||
});
|
||||
const formatDateDate = (date) => {
|
||||
const messageDate = new Date(date);
|
||||
const options = {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
};
|
||||
return messageDate.toLocaleDateString('en-US', options).replace(/\//g, '-');
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<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 title="Notes">
|
||||
<VCardText>
|
||||
<VTimeline truncate-line="both" align="start" side="end" line-inset="10" line-color="primary"
|
||||
density="compact" class="v-timeline-density-compact">
|
||||
<template v-if="historyNotes">
|
||||
<VTimelineItem dot-color="primary" size="x-small" v-for="(p_note, index) of notes" :key="index">
|
||||
<div class="d-flex justify-space-between align-center mb-3">
|
||||
<span class="app-timeline-title">{{ p_note.note }}</span>
|
||||
<span class="app-timeline-meta">{{ p_note.date }}</span>
|
||||
</div>
|
||||
<p class="app-timeline-text mb-0">
|
||||
{{ p_note.doctor }}
|
||||
</p>
|
||||
</VTimelineItem>
|
||||
</template>
|
||||
<template v-else>
|
||||
<VCard>
|
||||
<VAlert border="start" color="rgb(var(--v-theme-yellow))" variant="tonal">
|
||||
<div class="text-center">No data found</div>
|
||||
</VAlert>
|
||||
</VCard>
|
||||
|
||||
</template>
|
||||
</VTimeline>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
<!-- <VList class="pb-0" lines="two" v-if="historyNotes">
|
||||
<template v-if="notes.length > 0" v-for="(p_note, index) of notes" :key="index">
|
||||
<VListItem class="pb-0" border>
|
||||
<VListItemTitle>
|
||||
<span class="pb-0">{{ p_note.note }}</span>
|
||||
<p class="text-start fs-5 mb-0 pb-0 text-grey">
|
||||
<small> {{ p_note.doctor }}</small>
|
||||
</p>
|
||||
<p class="text-end fs-5 mb-0 pb-0 text-grey">
|
||||
<small> {{ p_note.date }}</small>
|
||||
</p>
|
||||
</VListItemTitle>
|
||||
</VListItem>
|
||||
<VDivider v-if="index !== notes.length - 1" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<VCard>
|
||||
<VAlert border="start" color="rgb(var(--v-theme-yellow))" variant="tonal">
|
||||
<div class="text-center">No data found</div>
|
||||
</VAlert>
|
||||
|
||||
</VCard>
|
||||
</template>
|
||||
</VList> -->
|
||||
</template>
|
926
resources/js/pages/patient/orders-detail.vue
Normal file
926
resources/js/pages/patient/orders-detail.vue
Normal file
@@ -0,0 +1,926 @@
|
||||
<script setup>
|
||||
import LabKits from "@/pages/patient/lab-kits.vue";
|
||||
import avatar1 from "@images/avatars/avatar-1.png";
|
||||
import moment from "moment-timezone";
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import { useStore } from "vuex";
|
||||
const route = useRoute();
|
||||
const isConfirmDialogVisible = ref(false);
|
||||
const isUserInfoEditDialogVisible = ref(false);
|
||||
const isEditAddressDialogVisible = ref(false);
|
||||
const scheduleDate = ref("");
|
||||
const scheduleTime = ref("");
|
||||
const headers = [
|
||||
{
|
||||
title: "Product",
|
||||
key: "title",
|
||||
},
|
||||
{
|
||||
title: "Price",
|
||||
key: "price",
|
||||
},
|
||||
{
|
||||
title: "Quantity",
|
||||
key: "quantity",
|
||||
},
|
||||
{
|
||||
title: "status",
|
||||
key: "status",
|
||||
},
|
||||
{
|
||||
title: "Total",
|
||||
key: "total",
|
||||
sortable: false,
|
||||
},
|
||||
];
|
||||
const store = useStore();
|
||||
const orderData = ref(null);
|
||||
const isMeeting = ref(null);
|
||||
|
||||
const pateintDetail = ref({});
|
||||
const productItems = ref([]);
|
||||
const filteredOrders = computed(() => {
|
||||
let filtered = store.getters.getPatientOrderDetail;
|
||||
|
||||
return filtered;
|
||||
});
|
||||
const formatDateActviy1 = (date) => {
|
||||
const messageDate = new Date(date);
|
||||
const dayFormatter = new Intl.DateTimeFormat("en-US", { weekday: "long" });
|
||||
const timeFormatter = new Intl.DateTimeFormat("en-US", {
|
||||
hour: "numeric",
|
||||
minute: "2-digit",
|
||||
hour12: true,
|
||||
});
|
||||
return `${dayFormatter.format(messageDate)} ${timeFormatter.format(
|
||||
messageDate
|
||||
)}`;
|
||||
};
|
||||
const formatDateActviy = (date) => {
|
||||
const messageDate = new Date(date);
|
||||
const options = {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
};
|
||||
return messageDate.toLocaleDateString("en-US", options).replace(/\//g, "-");
|
||||
};
|
||||
const formatDate = (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, "-");
|
||||
return `${formattedDate}`;
|
||||
};
|
||||
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'.");
|
||||
}
|
||||
};
|
||||
function changeDateFormat(dateFormat) {
|
||||
console.log("startTimeFormat", dateFormat);
|
||||
if (dateFormat) {
|
||||
const [datePart, timePart] = dateFormat.split(" "); // Split date and time parts
|
||||
const [year, month, day] = datePart.split("-"); // Split date into year, month, and day
|
||||
const formattedMonth = parseInt(month).toString(); // Convert month to integer and then string to remove leading zeros
|
||||
const formattedDay = parseInt(day).toString(); // Convert day to integer and then string to remove leading zeros
|
||||
const formattedDate = `${formattedMonth}-${formattedDay}-${year}`; // Format date as mm-dd-yyyy
|
||||
return `${formattedDate} ${timePart}`; // Combine formatted date with original time part
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
onMounted(async () => {
|
||||
store.dispatch("updateIsLoading", true);
|
||||
await store.dispatch("orderDetailPatient", {
|
||||
id: route.params.id,
|
||||
});
|
||||
orderData.value = store.getters.getPatientOrderDetail;
|
||||
console.log(orderData.value);
|
||||
if (orderData.value.appointment_details) {
|
||||
scheduleDate.value = getConvertedDate(
|
||||
convertUtcTime(
|
||||
orderData.value.appointment_details.appointment_time,
|
||||
orderData.value.appointment_details.appointment_date,
|
||||
orderData.value.appointment_details.timezone
|
||||
)
|
||||
);
|
||||
scheduleTime.value = getConvertedTime(
|
||||
convertUtcTime(
|
||||
orderData.value.appointment_details.appointment_time,
|
||||
orderData.value.appointment_details.appointment_date,
|
||||
orderData.value.appointment_details.timezone
|
||||
)
|
||||
);
|
||||
isMeeting.value = convertAndCheckTime(
|
||||
convertAndGetTimeDifference(
|
||||
orderData.value.appointment_details.appointment_time,
|
||||
orderData.value.appointment_details.appointment_date,
|
||||
orderData.value.appointment_details.timezone
|
||||
)
|
||||
);
|
||||
}
|
||||
// let appointmentDate = convertUtcDateTimeToLocal(orderData.value.appointment_details.appointment_date, orderData.value.appointment_details.appointment_time, 'date')
|
||||
// let appointmentTime = convertUtcDateTimeToLocal(orderData.value.appointment_details.appointment_date, orderData.value.appointment_details.appointment_time, 'time')
|
||||
// scheduleDate.value = moment(appointmentDate, "YYYY-MM-DD").format("MMMM DD, YYYY")
|
||||
// scheduleTime.value = moment(appointmentTime, "HH:mm:ss").format("hh:mm A");
|
||||
});
|
||||
const convertAndCheckTime = (str) => {
|
||||
return str.includes("ago");
|
||||
};
|
||||
const convertAndGetTimeDifference = (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);
|
||||
|
||||
// Get the current date and time
|
||||
const now = new Date();
|
||||
|
||||
// Calculate the time difference in milliseconds
|
||||
const timeDifference = dateObj - now;
|
||||
|
||||
// Convert the time difference to a human-readable format
|
||||
const diffInSeconds = Math.abs(timeDifference) / 1000;
|
||||
const diffInMinutes = Math.floor(diffInSeconds / 60) % 60;
|
||||
const diffInHours = Math.floor(diffInSeconds / 3600);
|
||||
|
||||
let timeLeftOrAgo;
|
||||
if (timeDifference > 0) {
|
||||
if (diffInHours > 0) {
|
||||
timeLeftOrAgo = `${diffInHours} hour${
|
||||
diffInHours > 1 ? "s" : ""
|
||||
} ${diffInMinutes} minute${diffInMinutes > 1 ? "s" : ""} left`;
|
||||
} else {
|
||||
timeLeftOrAgo = `${diffInMinutes} minute${
|
||||
diffInMinutes > 1 ? "s" : ""
|
||||
} left`;
|
||||
}
|
||||
} else {
|
||||
if (diffInHours > 0) {
|
||||
timeLeftOrAgo = `${diffInHours} hour${
|
||||
diffInHours > 1 ? "s" : ""
|
||||
} ${diffInMinutes} minute${diffInMinutes > 1 ? "s" : ""} ago`;
|
||||
} else {
|
||||
timeLeftOrAgo = `${diffInMinutes} minute${
|
||||
diffInMinutes > 1 ? "s" : ""
|
||||
} ago`;
|
||||
}
|
||||
}
|
||||
|
||||
return timeLeftOrAgo;
|
||||
};
|
||||
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 getStatusColor = (status) => {
|
||||
switch (status) {
|
||||
case "pending":
|
||||
return "orange";
|
||||
case "Shipped":
|
||||
return "blue";
|
||||
case "Delivered":
|
||||
return "green";
|
||||
case "Cancelled":
|
||||
return "red";
|
||||
default:
|
||||
return "gray";
|
||||
}
|
||||
};
|
||||
const formatCurrency = (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(".");
|
||||
};
|
||||
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="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>
|
||||
<div>
|
||||
<div
|
||||
class="d-flex justify-space-between align-center flex-wrap gap-y-4 mb-6"
|
||||
>
|
||||
<div>
|
||||
<div class="d-flex gap-2 align-center mb-2 flex-wrap">
|
||||
<h5 class="text-h5">Order #{{ route.params.id }}</h5>
|
||||
<div class="d-flex gap-x-2">
|
||||
<!-- <VChip variant="tonal" color="success" size="small">
|
||||
Paid
|
||||
</VChip>
|
||||
<VChip variant="tonal" color="info" size="small">
|
||||
Ready to Pickup
|
||||
</VChip> -->
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-body-1"> </span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<VRow v-if="filteredOrders">
|
||||
<VCol cols="12" md="8">
|
||||
<!-- 👉 Order Details -->
|
||||
<VCard class="mb-6">
|
||||
<VCardItem>
|
||||
<template #title>
|
||||
<h5>Order Details</h5>
|
||||
</template>
|
||||
</VCardItem>
|
||||
<div class="table-container">
|
||||
<VDataTable
|
||||
:headers="headers"
|
||||
:items="filteredOrders.order_items.items"
|
||||
item-value="productName"
|
||||
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 align-left text-left"
|
||||
>
|
||||
<h5
|
||||
style="
|
||||
margin-bottom: 0px;
|
||||
font-size: 0.83em;
|
||||
"
|
||||
>
|
||||
{{ item.title }}
|
||||
</h5>
|
||||
|
||||
<span
|
||||
class="text-sm text-start align-self-start"
|
||||
>
|
||||
{{ item.list_sub_title }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #item.price="{ item }">
|
||||
<span
|
||||
>${{
|
||||
parseFloat(item.price).toLocaleString(
|
||||
"en-US",
|
||||
{
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
}
|
||||
)
|
||||
}}</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>
|
||||
${{
|
||||
parseFloat(
|
||||
item.price * item.quantity
|
||||
).toLocaleString("en-US", {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})
|
||||
}}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template #bottom />
|
||||
</VDataTable>
|
||||
</div>
|
||||
<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>
|
||||
</VCard>
|
||||
<!-- <LabKits :order-id="route.params.id" /> -->
|
||||
<!-- 👉 Shipping Activity -->
|
||||
<VCard title="Shipping Activity">
|
||||
<VCardText>
|
||||
<VTimeline
|
||||
truncate-line="both"
|
||||
align="start"
|
||||
side="end"
|
||||
line-inset="10"
|
||||
line-color="primary"
|
||||
density="compact"
|
||||
class="v-timeline-density-compact"
|
||||
>
|
||||
<VTimelineItem dot-color="yellow" size="x-small">
|
||||
<div
|
||||
class="d-flex justify-space-between align-center mb-3"
|
||||
>
|
||||
<span class="app-timeline-title">
|
||||
Order was placed (Order ID: #{{
|
||||
filteredOrders.order_details.id
|
||||
}})</span
|
||||
>
|
||||
<span class="app-timeline-meta">{{
|
||||
formatDateActviy(
|
||||
filteredOrders.order_details
|
||||
.created_at
|
||||
)
|
||||
}}</span>
|
||||
</div>
|
||||
<p class="app-timeline-text mb-0">
|
||||
{{
|
||||
filteredOrders.order_details
|
||||
.short_description
|
||||
}}
|
||||
</p>
|
||||
</VTimelineItem>
|
||||
<VTimelineItem
|
||||
dot-color="yellow"
|
||||
size="x-small"
|
||||
v-for="item in filteredOrders.items_activity"
|
||||
:key="item.id"
|
||||
>
|
||||
<div
|
||||
class="d-flex justify-space-between align-center mb-3"
|
||||
>
|
||||
<span class="app-timeline-title">
|
||||
{{ item.note }}</span
|
||||
>
|
||||
<span class="app-timeline-meta">{{
|
||||
formatDateActviy(item.created_at)
|
||||
}}</span>
|
||||
</div>
|
||||
<p class="app-timeline-text mb-0">
|
||||
{{ item.short_description }}
|
||||
</p>
|
||||
</VTimelineItem>
|
||||
</VTimeline>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
</VCol>
|
||||
|
||||
<VCol cols="12" md="4">
|
||||
<VCard class="mb-6" v-if="filteredOrders.appointment_details">
|
||||
<VCardText>
|
||||
<div
|
||||
class="d-flex align-center justify-space-between gap-1 mb-6"
|
||||
>
|
||||
<div
|
||||
class="text-body-1 text-high-emphasis font-weight-medium"
|
||||
>
|
||||
<v-icon
|
||||
class="mr-2"
|
||||
color="rgb(var(--v-theme-yellow))"
|
||||
>mdi-calendar-clock</v-icon
|
||||
>
|
||||
Appointment Details
|
||||
</div>
|
||||
</div>
|
||||
<div class="appointment-details">
|
||||
<div class="detail-item">
|
||||
<span class="detail-label"
|
||||
>Appointment At:</span
|
||||
>
|
||||
<span class="detail-value">{{
|
||||
scheduleDate + " " + scheduleTime
|
||||
}}</span>
|
||||
</div>
|
||||
<div
|
||||
class="detail-item"
|
||||
v-if="
|
||||
filteredOrders.appointment_details
|
||||
.start_time &&
|
||||
filteredOrders.appointment_details.end_time
|
||||
"
|
||||
>
|
||||
<span class="detail-label">Start Time:</span>
|
||||
<span class="detail-value">{{
|
||||
formatDate(
|
||||
filteredOrders.appointment_details
|
||||
.start_time
|
||||
)
|
||||
}}</span>
|
||||
</div>
|
||||
<div
|
||||
class="detail-item"
|
||||
v-if="
|
||||
filteredOrders.appointment_details
|
||||
.start_time &&
|
||||
filteredOrders.appointment_details.end_time
|
||||
"
|
||||
>
|
||||
<span class="detail-label">End Time:</span>
|
||||
<span class="detail-value">{{
|
||||
formatDate(
|
||||
filteredOrders.appointment_details
|
||||
.end_time
|
||||
)
|
||||
}}</span>
|
||||
</div>
|
||||
<div
|
||||
class="detail-item"
|
||||
v-if="
|
||||
filteredOrders.appointment_details
|
||||
.start_time &&
|
||||
filteredOrders.appointment_details.end_time
|
||||
"
|
||||
>
|
||||
<span class="detail-label">Duration:</span>
|
||||
<span class="detail-value">{{
|
||||
totalCallDuration(
|
||||
filteredOrders.appointment_details
|
||||
.start_time,
|
||||
filteredOrders.appointment_details
|
||||
.end_time
|
||||
)
|
||||
}}</span>
|
||||
</div>
|
||||
<span v-if="isMeeting">
|
||||
<RouterLink to="/queue" target="_blank">
|
||||
<VBtn
|
||||
style="border-radius: 20px; color: #fff"
|
||||
block
|
||||
class="mt-3"
|
||||
color="rgb(var(--v-theme-yellow-theme-button))"
|
||||
>
|
||||
Go to Meeting
|
||||
</VBtn>
|
||||
</RouterLink>
|
||||
</span>
|
||||
<span v-else>
|
||||
<VBtn
|
||||
block
|
||||
style="border-radius: 20px; color: #fff"
|
||||
class="mt-3"
|
||||
color="rgb(var(--v-theme-yellow-theme-button))"
|
||||
disabled
|
||||
>Go to Meeting
|
||||
</VBtn>
|
||||
</span>
|
||||
</div>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
<!-- 👉 Customer Details -->
|
||||
<VCard class="mb-6" v-if="filteredOrders.appointment_details">
|
||||
<VCardText class="d-flex flex-column gap-y-6">
|
||||
<h3>Provider Details</h3>
|
||||
|
||||
<div class="d-flex align-center">
|
||||
<VAvatar
|
||||
:image="avatar1"
|
||||
class="me-3"
|
||||
style="display: none"
|
||||
/>
|
||||
<VAvatar color="yellow" class="me-3" size="30">
|
||||
<VIcon
|
||||
icon="mdi-account"
|
||||
size="25"
|
||||
color="white"
|
||||
/>
|
||||
</VAvatar>
|
||||
<div>
|
||||
<div
|
||||
class="text-body-1 text-high-emphasis font-weight-medium"
|
||||
>
|
||||
{{
|
||||
filteredOrders.appointment_details
|
||||
.provider_name
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-center" style="display: none">
|
||||
<VAvatar
|
||||
variant="tonal"
|
||||
color="success"
|
||||
class="me-3"
|
||||
style="display: none"
|
||||
>
|
||||
<VIcon icon="ri-shopping-cart-line" />
|
||||
</VAvatar>
|
||||
|
||||
<h4 style="display: none">
|
||||
{{ filteredOrders.order_items.total_products }}
|
||||
Products
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-column gap-y-1">
|
||||
<div
|
||||
class="d-flex justify-space-between gap-1 text-body-2"
|
||||
>
|
||||
<h5>Contact Info</h5>
|
||||
</div>
|
||||
<span
|
||||
>Email:
|
||||
{{
|
||||
filteredOrders.appointment_details
|
||||
.provider_email
|
||||
}}</span
|
||||
>
|
||||
<span
|
||||
>Mobile:
|
||||
{{
|
||||
filteredOrders.appointment_details
|
||||
.provider_phone
|
||||
}}</span
|
||||
>
|
||||
</div>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
|
||||
<!-- 👉 Shipping Address -->
|
||||
<VCard class="mb-6">
|
||||
<VCardText>
|
||||
<div
|
||||
class="d-flex align-center justify-space-between gap-1 mb-6"
|
||||
>
|
||||
<div
|
||||
class="text-body-1 text-high-emphasis font-weight-medium"
|
||||
>
|
||||
<v-icon
|
||||
class="mr-2"
|
||||
color="rgb(var(--v-theme-yellow))"
|
||||
>mdi-truck-delivery</v-icon
|
||||
>
|
||||
Shipping Address
|
||||
</div>
|
||||
<!-- <span
|
||||
class="text-base text-primary font-weight-medium cursor-pointer"
|
||||
@click="
|
||||
isEditAddressDialogVisible =
|
||||
!isEditAddressDialogVisible
|
||||
"
|
||||
>Edit</span
|
||||
> -->
|
||||
</div>
|
||||
<div>
|
||||
{{ filteredOrders.order_details.shipping_address1 }}
|
||||
<br />
|
||||
{{ filteredOrders.order_details.shipping_city }}
|
||||
<br />
|
||||
{{ filteredOrders.order_details.shipping_state }},
|
||||
{{ filteredOrders.order_details.shipping_zipcode }}
|
||||
<br />
|
||||
{{ filteredOrders.order_details.shipping_country }}
|
||||
</div>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
|
||||
<!-- 👉 Billing Address -->
|
||||
<VCard style="display: none">
|
||||
<VCardText>
|
||||
<div
|
||||
class="d-flex align-center justify-space-between gap-1 mb-3"
|
||||
>
|
||||
<div
|
||||
class="text-body-1 text-high-emphasis font-weight-medium"
|
||||
>
|
||||
Billing Address
|
||||
</div>
|
||||
<!-- <span
|
||||
class="text-base text-primary font-weight-medium cursor-pointer"
|
||||
@click="
|
||||
isEditAddressDialogVisible =
|
||||
!isEditAddressDialogVisible
|
||||
"
|
||||
>Edit</span
|
||||
> -->
|
||||
</div>
|
||||
<div>
|
||||
{{ filteredOrders.order_details.billing_address1 }}
|
||||
<br />
|
||||
{{ filteredOrders.order_details.billing_city }}
|
||||
<br />
|
||||
{{ filteredOrders.order_details.billing_state }},
|
||||
{{ filteredOrders.order_details.billing_zipcode }}
|
||||
<br />
|
||||
{{ filteredOrders.order_details.billing_country }}
|
||||
</div>
|
||||
|
||||
<!-- <div class="mt-6">
|
||||
<h6 class="text-h6 mb-1">Mastercard</h6>
|
||||
<div class="text-base">Card Number: ******4291</div>
|
||||
</div> -->
|
||||
</VCardText>
|
||||
</VCard>
|
||||
</VCol>
|
||||
</VRow>
|
||||
|
||||
<!-- <ConfirmDialog
|
||||
v-model:isDialogVisible="isConfirmDialogVisible"
|
||||
confirmation-question="Are you sure to cancel your Order?"
|
||||
cancel-msg="Order cancelled!!"
|
||||
cancel-title="Cancelled"
|
||||
confirm-msg="Your order cancelled successfully."
|
||||
confirm-title="Cancelled!"
|
||||
/> -->
|
||||
|
||||
<!-- <UserInfoEditDialog
|
||||
v-model:isDialogVisible="isUserInfoEditDialogVisible"
|
||||
/>
|
||||
|
||||
<AddEditAddressDialog
|
||||
v-model:isDialogVisible="isEditAddressDialogVisible"
|
||||
/> -->
|
||||
</div>
|
||||
</template>
|
||||
<style scoped>
|
||||
.appointment-details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
font-weight: bold;
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
::-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 */
|
||||
}
|
||||
</style>
|
557
resources/js/pages/patient/orders-list.vue
Normal file
557
resources/js/pages/patient/orders-list.vue
Normal file
@@ -0,0 +1,557 @@
|
||||
<script setup>
|
||||
import { computed, onMounted, reactive, ref } from "vue";
|
||||
import { useStore } from "vuex";
|
||||
const router = useRouter();
|
||||
const store = useStore();
|
||||
const orders = ref([]);
|
||||
const selectedDate = ref();
|
||||
const headers = [
|
||||
{ title: "Order Number", key: "id" },
|
||||
{ title: "Customer", key: "customer" },
|
||||
{ title: "Total", key: "total" },
|
||||
{ title: "Status", key: "status" },
|
||||
{ title: "Actions", key: "actions" },
|
||||
];
|
||||
|
||||
const loading = ref(false);
|
||||
const search = ref("");
|
||||
const filterDialog = ref(false);
|
||||
const isShown = ref(false);
|
||||
const startDateMenu = ref(null)
|
||||
const endDateMenu = ref(null)
|
||||
const showCustomRangePicker = ref(false);
|
||||
const dateRange = ref([]);
|
||||
|
||||
const filters = reactive({
|
||||
startDate: null,
|
||||
endDate: null,
|
||||
dateRangeText: computed(() => {
|
||||
if (filters.startDate && filters.endDate) {
|
||||
return `${formatDateDate(filters.startDate)} - ${formatDateDate(filters.endDate)}`;
|
||||
}
|
||||
return 'Select Date';
|
||||
}),
|
||||
});
|
||||
|
||||
const statusOptions = ["Pending", "Shipped", "Delivered", "Cancelled"];
|
||||
|
||||
|
||||
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 openFilterDialog = () => {
|
||||
isShown.value = true;
|
||||
};
|
||||
|
||||
const resetFilters = async () => {
|
||||
filters.search = "";
|
||||
filters.status = [];
|
||||
filters.startDate = null
|
||||
filters.endDate = null
|
||||
startDateMenu.value = null
|
||||
endDateMenu.value = null
|
||||
store.dispatch("updateIsLoading", true);
|
||||
await store.dispatch("orderPtaientList");
|
||||
orders.value = store.getters.getPatientOrderList;
|
||||
console.log(orders.value);
|
||||
store.dispatch("updateIsLoading", false);
|
||||
};
|
||||
|
||||
const applyFilters = async () => {
|
||||
search.value = filters.search;
|
||||
filterDialog.value = false;
|
||||
await getFilter()
|
||||
};
|
||||
|
||||
const filteredOrders = computed(() => {
|
||||
let filtered = store.getters.getPatientOrderList;
|
||||
|
||||
if (filters.search) {
|
||||
filtered = filtered.filter((order) =>
|
||||
order.orderNumber
|
||||
.toLowerCase()
|
||||
.includes(filters.search.toLowerCase())
|
||||
);
|
||||
}
|
||||
|
||||
// if (filters.status.length > 0) {
|
||||
// filtered = filtered.filter((order) =>
|
||||
// filters.status.includes(order.status)
|
||||
// );
|
||||
// }
|
||||
// if (filters.startDate) {
|
||||
// const startDate = new Date(filters.startDate);
|
||||
// filtered = filtered.filter((order) => {
|
||||
// const orderDate = new Date(order.created_at);
|
||||
// return orderDate >= startDate;
|
||||
// });
|
||||
// }
|
||||
|
||||
// if (filters.endDate) {
|
||||
// const endDate = new Date(filters.endDate);
|
||||
// filtered = filtered.filter((order) => {
|
||||
// const orderDate = new Date(order.created_at);
|
||||
// return orderDate <= endDate;
|
||||
// });
|
||||
// }
|
||||
filtered.sort((a, b) => {
|
||||
return b.id - a.id;
|
||||
});
|
||||
return filtered;
|
||||
});
|
||||
const datepickStart = async () => {
|
||||
console.log("ppicker", startDateMenu.value);
|
||||
|
||||
|
||||
if (startDateMenu.value) {
|
||||
const selectedDate = new Date(startDateMenu.value);
|
||||
const dateWithoutTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate());
|
||||
// Format the date as needed
|
||||
console.log("formattedDate",);
|
||||
// const formattedDate = selectedDate.getFullYear() + '-' + selectedDate.getMonth() + '-' + selectedDate.getDate() //dateWithoutTime.toISOString().slice(0, 10);
|
||||
const formattedDate = formatDateDate(selectedDate)
|
||||
console.log("formattedDate", formattedDate);
|
||||
filters.startDate = formattedDate
|
||||
showStartDatePicker.value = false;
|
||||
// await getFilter()
|
||||
}
|
||||
}
|
||||
const formatDateDate = (date) => {
|
||||
const messageDate = new Date(date);
|
||||
const options = {
|
||||
year: 'numeric',
|
||||
month: 'numeric',
|
||||
day: 'numeric',
|
||||
};
|
||||
return messageDate.toLocaleDateString('en-US', options).replace(/\//g, '-');
|
||||
};
|
||||
|
||||
const datepickendDate = async () => {
|
||||
console.log("ppicker", filters.endDate);
|
||||
|
||||
|
||||
if (endDateMenu.value) {
|
||||
const selectedDate = new Date(endDateMenu.value);
|
||||
const dateWithoutTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate());
|
||||
// Format the date as needed
|
||||
console.log("formattedDate", dateWithoutTime);
|
||||
const formattedDate = formatDateDate(selectedDate)//dateWithoutTime.toISOString().slice(0, 10);
|
||||
console.log("formattedDate", formattedDate);
|
||||
filters.endDate = formattedDate
|
||||
showEndDatePicker.value = false;
|
||||
//await getFilter()
|
||||
|
||||
}
|
||||
}
|
||||
const viewOrder = (orderId) => {
|
||||
router.push({ name: "order-detail", params: { id: orderId } });
|
||||
};
|
||||
|
||||
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 getFilter = async () => {
|
||||
console.log("filter", filters.startDate, filters.endDate);
|
||||
|
||||
|
||||
await store.dispatch('orderPtaientListFilter', {
|
||||
from_date: formatDateDate(filters.startDate),
|
||||
to_date: formatDateDate(filters.endDate),
|
||||
})
|
||||
orders.value = store.getters.getPatientOrderList;
|
||||
store.dispatch('updateIsLoading', false)
|
||||
}
|
||||
onMounted(async () => {
|
||||
store.dispatch("updateIsLoading", true);
|
||||
await store.dispatch("orderPtaientList");
|
||||
orders.value = store.getters.getPatientOrderList;
|
||||
orders.value.sort((a, b) => {
|
||||
return b.id - a.id;
|
||||
});
|
||||
console.log(orders.value);
|
||||
});
|
||||
|
||||
const selectToday = () => {
|
||||
const today = new Date().toISOString().split('T')[0];
|
||||
filters.startDate = today;
|
||||
filters.endDate = today;
|
||||
showCustomRangePicker.value = false;
|
||||
};
|
||||
|
||||
const selectYesterday = () => {
|
||||
const today = new Date();
|
||||
const yesterday = new Date(today);
|
||||
yesterday.setDate(yesterday.getDate() - 1);
|
||||
const formattedYesterday = yesterday.toISOString().split('T')[0];
|
||||
filters.startDate = formattedYesterday;
|
||||
filters.endDate = formattedYesterday;
|
||||
showCustomRangePicker.value = false;
|
||||
};
|
||||
|
||||
const selectLast7Days = () => {
|
||||
const today = new Date();
|
||||
const sevenDaysAgo = new Date(today);
|
||||
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 6);
|
||||
filters.startDate = sevenDaysAgo.toISOString().split('T')[0];
|
||||
filters.endDate = today.toISOString().split('T')[0];
|
||||
showCustomRangePicker.value = false;
|
||||
};
|
||||
const minDate = computed(() => {
|
||||
const date = new Date();
|
||||
date.setFullYear(date.getFullYear() - 1);
|
||||
return date.toISOString().substr(0, 10);
|
||||
});
|
||||
|
||||
const maxDate = computed(() => {
|
||||
const date = new Date();
|
||||
return date.toISOString().substr(0, 10);
|
||||
});
|
||||
const applyCustomRange = (dates) => {
|
||||
console.log(dateRange.value)
|
||||
dateRange.value.sort();
|
||||
if (dates.length === 2) {
|
||||
[filters.startDate, filters.endDate] = dates;
|
||||
showCustomRangePicker.value = false;
|
||||
}
|
||||
};
|
||||
const showCustomRangePickerFunction = (state) => {
|
||||
if (state) {
|
||||
dateRange.value = []
|
||||
showCustomRangePicker.value = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<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>
|
||||
<v-container class="pt-0">
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
|
||||
<VCardTitle class="pt-0"><b>ORDERS </b></VCardTitle>
|
||||
<v-card>
|
||||
<v-card-title>
|
||||
<v-row class="px-0 py-4">
|
||||
|
||||
|
||||
<v-col cols="12" md="4">
|
||||
<VMenu location="bottom" :close-on-content-click="false" :nudge-right="40"
|
||||
transition="scale-transition" offset-y min-width="auto" density="compact">
|
||||
<template #activator="{ props }">
|
||||
<v-text-field v-model="filters.dateRangeText" label="Select Date Range"
|
||||
v-bind="props" outlined density="compact" readonly></v-text-field>
|
||||
</template>
|
||||
<v-card>
|
||||
<v-list>
|
||||
<v-list-item @click="selectToday">Today</v-list-item>
|
||||
<v-list-item @click="selectYesterday">Yesterday</v-list-item>
|
||||
<v-list-item @click="selectLast7Days">Last 7 Days</v-list-item>
|
||||
<v-list-item @click="showCustomRangePickerFunction(true)">Custom
|
||||
Range</v-list-item>
|
||||
</v-list>
|
||||
|
||||
<v-date-picker v-if="showCustomRangePicker" v-model="dateRange" :max="maxDate"
|
||||
:min="minDate" multiple class="custom-date-picker" mode="range"
|
||||
@update:model-value="applyCustomRange" hide-header>
|
||||
|
||||
</v-date-picker>
|
||||
</v-card>
|
||||
</VMenu>
|
||||
</v-col>
|
||||
|
||||
|
||||
|
||||
<v-col cols="4">
|
||||
<v-btn color="primary" class="text-capitalize mr-1" text @click="applyFilters">
|
||||
Filter
|
||||
</v-btn>
|
||||
<v-btn color="primary" class="text-capitalize" text @click="resetFilters">
|
||||
Reset
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-title>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row v-if="filteredOrders.length > 0">
|
||||
<v-col v-for="order in filteredOrders" :key="order.id" cols="12" md="6">
|
||||
<v-card class="order-card mb-6 rounded-lg elevation-3">
|
||||
<div class="order-header pa-4">
|
||||
<v-row no-gutters align="center">
|
||||
<v-col>
|
||||
<div class="d-flex align-center">
|
||||
<v-avatar color="rgb(var(--v-theme-yellow))" size="56"
|
||||
class="text-grey-800 text-h6 font-weight-bold mr-4">
|
||||
#{{ order.id }}
|
||||
</v-avatar>
|
||||
<div>
|
||||
|
||||
<div class="text-subtitle-1 font-weight-medium">{{ formatDate(order.created_at)
|
||||
}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col cols="auto" class="ml-auto">
|
||||
<v-chip color="primary" label x-large class="font-weight-bold">
|
||||
Total: ${{ parseFloat(order.order_total_amount +
|
||||
order.order_total_shipping).toLocaleString('en-US', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
}) }}
|
||||
</v-chip>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
|
||||
<v-divider></v-divider>
|
||||
|
||||
<v-card-text class="pa-4">
|
||||
<h3 class="text-h6 font-weight-bold mb-4">Order Items</h3>
|
||||
<div class="order-items-container">
|
||||
<v-list class="order-items-list">
|
||||
<v-list-item v-for="item in order.order_items" :key="item.id" class="mb-2 rounded-lg"
|
||||
two-line>
|
||||
<v-list-item-avatar tile size="80" class="rounded-lg">
|
||||
<v-img :src="item.image_url" cover></v-img>
|
||||
</v-list-item-avatar>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title class="text-subtitle-1 font-weight-medium">{{
|
||||
item.title
|
||||
}}</v-list-item-title>
|
||||
<v-list-item-subtitle>
|
||||
<v-chip x-small class="mr-2" outlined>Qty: {{ item.qty }} </v-chip>
|
||||
<v-chip x-small outlined>${{ parseFloat(item.price).toLocaleString('en-US',
|
||||
{
|
||||
minimumFractionDigits: 2, maximumFractionDigits: 2
|
||||
}) }}
|
||||
each</v-chip>
|
||||
<v-chip color="primary" x-small>$ {{ parseFloat(item.qty *
|
||||
item.price).toLocaleString('en-US', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
})
|
||||
}}</v-chip>
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
<v-list-item-action>
|
||||
|
||||
</v-list-item-action>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</div>
|
||||
</v-card-text>
|
||||
|
||||
<v-divider></v-divider>
|
||||
|
||||
<v-card-actions class="pa-4">
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn @click="viewOrder(order.id)" color="primary" outlined rounded>
|
||||
<v-icon left>mdi-eye</v-icon>
|
||||
View Details
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-else>
|
||||
<v-col cols="12" md="12">
|
||||
<v-card class="mb-4 rounded">
|
||||
<v-card-title class="d-flex justify-space-between align-center">
|
||||
|
||||
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<v-row>
|
||||
<v-col cols="12" class="text-center">no data found </v-col>
|
||||
|
||||
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
|
||||
</v-row>
|
||||
|
||||
<v-dialog v-model="filterDialog" max-width="500">
|
||||
<v-card>
|
||||
<v-card-title>
|
||||
<span class="text-h5">Filter Orders</span>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-text-field v-model="filters.search" label="Search" outlined dense></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<v-select v-model="filters.status" :items="statusOptions" label="Status" outlined dense
|
||||
multiple></v-select>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-btn color="primary" text @click="resetFilters">
|
||||
Reset Filters
|
||||
</v-btn>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn color="primary" text @click="applyFilters">
|
||||
Apply
|
||||
</v-btn>
|
||||
<v-btn text @click="filterDialog = false"> Cancel </v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
|
||||
</v-container>
|
||||
</template>
|
||||
<style scoped>
|
||||
.custom-select {
|
||||
min-height: 44px;
|
||||
/* Adjust the minimum height as needed */
|
||||
padding-top: 8px;
|
||||
/* Adjust top padding as needed */
|
||||
padding-bottom: 8px;
|
||||
/* Adjust bottom padding as needed */
|
||||
}
|
||||
|
||||
.text-primary {
|
||||
color: rgb(var(--v-theme-yellow-theme-button)) !important;
|
||||
}
|
||||
|
||||
.v-date-picker-month__day .v-btn {
|
||||
--v-btn-height: 23px !important;
|
||||
--v-btn-size: 0.85rem;
|
||||
}
|
||||
|
||||
.custom-date-picker {
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
.custom-date-picker :deep(.v-date-picker-month) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.custom-date-picker :deep(.v-date-picker-month__day) {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
|
||||
}
|
||||
|
||||
.custom-date-picker :deep(.v-date-picker-month) {
|
||||
min-width: 300px;
|
||||
|
||||
}
|
||||
|
||||
.custom-date-picker :deep(.v-date-picker-month__day .v-btn) {
|
||||
--v-btn-height: 20px !important;
|
||||
|
||||
}
|
||||
|
||||
.order-card {
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.order-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 12px 20px -10px rgba(0, 0, 0, 0.1), 0 4px 20px 0px rgba(0, 0, 0, 0.1), 0 7px 8px -5px rgba(0, 0, 0, 0.1) !important;
|
||||
}
|
||||
|
||||
.order-header {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.order-items-container {
|
||||
height: 155px;
|
||||
/* Set a fixed height */
|
||||
overflow-y: auto;
|
||||
/* Enable vertical scrolling */
|
||||
}
|
||||
|
||||
.order-items-list {
|
||||
padding-right: 16px;
|
||||
/* Add some padding for the scrollbar */
|
||||
}
|
||||
|
||||
.order-card .v-list-item {
|
||||
border: 1px solid rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
/* Custom scrollbar styles */
|
||||
.order-items-container::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.order-items-container::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.order-items-container::-webkit-scrollbar-thumb {
|
||||
background: #888;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.order-items-container::-webkit-scrollbar-thumb:hover {
|
||||
background: #555;
|
||||
}
|
||||
|
||||
/* Mobile styles */
|
||||
@media (max-width: 600px) {
|
||||
.order-header {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.order-header .v-avatar {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.order-header .v-col {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.order-header .ml-auto {
|
||||
margin: 16px auto 0 auto;
|
||||
}
|
||||
|
||||
.order-items-container {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
105
resources/js/pages/patient/prescription-detail.vue
Normal file
105
resources/js/pages/patient/prescription-detail.vue
Normal file
@@ -0,0 +1,105 @@
|
||||
<script setup>
|
||||
import Notes from '@/pages/patient/notes.vue';
|
||||
import Prescription from '@/pages/patient/prescription.vue';
|
||||
import store from '@/store';
|
||||
import moment from 'moment';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
// const patientId = route.params.patient_id;
|
||||
const appointmentId = route.params.appiontment_id;
|
||||
const currentTab = ref(0)
|
||||
const appiontmentID = ref();
|
||||
const dcotorName = ref();
|
||||
const starttime = ref();
|
||||
const endtime = ref();
|
||||
const duration = ref();
|
||||
const appointmentData = ref(null);
|
||||
const his = computed(async () => {
|
||||
store.dispatch('updateIsLoading', true)
|
||||
await store.dispatch('getAppointmentByIdPatient', {
|
||||
appointment_id: appointmentId,
|
||||
})
|
||||
// notes.value = store.getters.getPatientNotes;
|
||||
appointmentData.value = store.getters.getSinglePatientAppointment;
|
||||
// console.log("appointmentData", appointmentData.value);
|
||||
appiontmentID.value = appointmentId;
|
||||
dcotorName.value = appointmentData.value.telemedPro.name;
|
||||
starttime.value = appointmentData.value.appointment.start_time;
|
||||
endtime.value = appointmentData.value.appointment.end_time;
|
||||
duration.value = totalCallDuration(starttime.value, endtime.value);
|
||||
localStorage.setItem('meetingPatientAppiontmentId', appiontmentID.value)
|
||||
// console.log("notesData", notesData.value[0].appointment.id);
|
||||
store.dispatch('updateIsLoading', false)
|
||||
});
|
||||
const 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
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<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 cols="6">
|
||||
<VCardText>
|
||||
<h3 v-if="his"> #{{ appiontmentID }} By {{ dcotorName }} </h3>
|
||||
<span> Meeting duration: <b> {{ duration }}</b></span>
|
||||
</VCardText>
|
||||
<div class="d-flex">
|
||||
<div>
|
||||
<VTabs v-model="currentTab" direction="vertical">
|
||||
<VTab>
|
||||
<VIcon start icon="tabler-edit" />
|
||||
Notes
|
||||
</VTab>
|
||||
|
||||
<VTab>
|
||||
<VIcon start icon="tabler-lock" />
|
||||
Prescriptions
|
||||
</VTab>
|
||||
</VTabs>
|
||||
</div>
|
||||
|
||||
<VCardText>
|
||||
<VWindow v-model="currentTab" class="ms-3">
|
||||
<VWindowItem>
|
||||
<Notes></Notes>
|
||||
</VWindowItem>
|
||||
|
||||
<VWindowItem>
|
||||
<Prescription></Prescription>
|
||||
</VWindowItem>
|
||||
</VWindow>
|
||||
</VCardText>
|
||||
</div>
|
||||
</VCard>
|
||||
</template>
|
247
resources/js/pages/patient/prescription.vue
Normal file
247
resources/js/pages/patient/prescription.vue
Normal file
@@ -0,0 +1,247 @@
|
||||
<script setup>
|
||||
import store from '@/store';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const itemsPrescriptions = ref([]);
|
||||
// const patientId = route.params.patient_id;
|
||||
const appointmentId = route.params.appiontment_id;
|
||||
const doctorName = ref('');
|
||||
const prescription = computed(async () => {
|
||||
store.dispatch('updateIsLoading', true)
|
||||
await store.dispatch('getPatientAppointment')
|
||||
await getprescriptionList()
|
||||
doctorName.value = store.getters.getBookedAppointment.agent_name;
|
||||
store.dispatch('updateIsLoading', false)
|
||||
});
|
||||
|
||||
const getprescriptionList = async () => {
|
||||
await store.dispatch('getPatientPrescriptionsByID', {
|
||||
appointment_id: appointmentId,
|
||||
})
|
||||
let prescriptions = store.getters.getPrescriptionList
|
||||
// itemsPrescriptions.value = store.getters.getPrescriptionList
|
||||
for (let data of prescriptions) {
|
||||
let dataObject = {}
|
||||
dataObject.brand = data.brand
|
||||
dataObject.direction_one = data.direction_one
|
||||
dataObject.direction_quantity = data.direction_quantity
|
||||
dataObject.direction_two = data.direction_two
|
||||
dataObject.date = formatDateDate(data.created_at)
|
||||
dataObject.dosage = data.dosage
|
||||
dataObject.from = data.from
|
||||
dataObject.name = data.name
|
||||
dataObject.quantity = data.quantity
|
||||
dataObject.refill_quantity = data.refill_quantity
|
||||
dataObject.status = data.status
|
||||
dataObject.comments = data.comments
|
||||
itemsPrescriptions.value.push(dataObject)
|
||||
}
|
||||
itemsPrescriptions.value.sort((a, b) => {
|
||||
return b.id - a.id;
|
||||
});
|
||||
console.log("itemsPrescriptions", itemsPrescriptions.value);
|
||||
|
||||
};
|
||||
const formatDateDate = (date) => {
|
||||
const messageDate = new Date(date);
|
||||
const options = {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
};
|
||||
return messageDate.toLocaleDateString('en-US', options).replace(/\//g, '-');
|
||||
};
|
||||
const getStatusColor = (status) => {
|
||||
switch (status) {
|
||||
case 'pending':
|
||||
return 'warning'; // Use Vuetify's warning color (typically yellow)
|
||||
case 'shipped':
|
||||
return '#45B8AC'; // Use Vuetify's primary color (typically blue)
|
||||
case 'delivered':
|
||||
return 'green';
|
||||
case 'returned':
|
||||
return 'red';
|
||||
case 'results':
|
||||
return 'blue';
|
||||
default:
|
||||
return 'grey'; // Use Vuetify's grey color for any other status
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<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>
|
||||
<template v-if="prescription">
|
||||
<VExpansionPanels variant="accordion">
|
||||
<VExpansionPanel v-for="(item, index) in itemsPrescriptions" :key="index">
|
||||
<div>
|
||||
|
||||
<VExpansionPanelTitle collapse-icon="mdi-chevron-down" expand-icon="mdi-chevron-right"
|
||||
style="margin-left: 0px !important;">
|
||||
<p class=""><b> {{ item.name }}</b>
|
||||
<br />
|
||||
<div class=" pt-2">#{{ appointmentId }} By {{ doctorName }}</div>
|
||||
<div class=" pt-2">{{ item.date }}</div>
|
||||
</p>
|
||||
|
||||
|
||||
<v-row>
|
||||
|
||||
</v-row>
|
||||
|
||||
<span class="v-expansion-panel-title__icon badge text-warning"
|
||||
v-if="item.status == null">Pending</span>
|
||||
<span class="v-expansion-panel-title__icon badge" v-else>
|
||||
<v-chip :color="getStatusColor(item.status)" label size="small" variant="text">
|
||||
{{ item.status }}
|
||||
</v-chip></span>
|
||||
</VExpansionPanelTitle>
|
||||
<VExpansionPanelText class="pt-0">
|
||||
|
||||
<v-row class='mt-1'>
|
||||
<v-col cols="12" md="4" sm="6">
|
||||
<p class='heading'><b>Brand:</b></p>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4" sm="6">
|
||||
<p>{{ item.brand }}</p>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row class='mt-1'>
|
||||
<v-col cols="12" md="4" sm="6">
|
||||
<p class='heading'><b>From:</b></p>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4" sm="6">
|
||||
<p>{{ item.from }}</p>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row class='mt-1'>
|
||||
<v-col cols="12" md="4" sm="6">
|
||||
<p class='heading'><b>Dosage:</b></p>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4" sm="6">
|
||||
<p>{{ item.dosage }}</p>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row class='mt-1'>
|
||||
<v-col cols="12" md="4" sm="6">
|
||||
<p class='heading'><b>Quantity:</b></p>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4" sm="6">
|
||||
<p>{{ item.quantity }}</p>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row class='mt-1'>
|
||||
<v-col cols="12" md="4" sm="6">
|
||||
<p class='heading'><b>Direction Quantity:</b></p>
|
||||
</v-col>
|
||||
<v-col cols="12" md="8" sm="6">
|
||||
<p>{{ item.direction_quantity }}</p>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row class='mt-1'>
|
||||
<v-col cols="12" md="4" sm="6">
|
||||
<p class='heading'><b>Direction One:</b></p>
|
||||
</v-col>
|
||||
<v-col cols="12" md="8" sm="6">
|
||||
<p>{{ item.direction_one }} </p>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row class='mt-1'>
|
||||
<v-col cols="12" md="4" sm="6">
|
||||
<p class='heading'><b>Direction Two:</b></p>
|
||||
</v-col>
|
||||
<v-col cols="12" md="8" sm="6">
|
||||
<p>{{ item.direction_two }} </p>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row class='mt-1'>
|
||||
<v-col cols="12" md="4" sm="6">
|
||||
<p class='heading'><b>Refill Quantity:</b></p>
|
||||
</v-col>
|
||||
<v-col cols="12" md="8" sm="6">
|
||||
<p>{{ item.refill_quantity }}</p>
|
||||
</v-col>
|
||||
|
||||
</v-row>
|
||||
<v-row class='mt-1'>
|
||||
<v-col cols="12" md="4" sm="6">
|
||||
<p class='heading'><b>Status:</b></p>
|
||||
</v-col>
|
||||
<v-col cols="12" md="8" sm="6">
|
||||
<p v-if="item.status == null" class="text-warning">Pending</p>
|
||||
<p v-else>{{ item.status }}</p>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row class='mt-1'>
|
||||
<v-col cols="12" md="4" sm="6">
|
||||
<p class='heading'><b>Comments:</b></p>
|
||||
</v-col>
|
||||
<v-col cols="12" md="8" sm="8">
|
||||
<p>{{ item.comments }} </p>
|
||||
</v-col>
|
||||
|
||||
</v-row>
|
||||
|
||||
</VExpansionPanelText>
|
||||
</div>
|
||||
|
||||
|
||||
</VExpansionPanel>
|
||||
<br />
|
||||
</VExpansionPanels>
|
||||
</template>
|
||||
<template v-else>
|
||||
<VCard>
|
||||
<VCard>
|
||||
<VAlert border="start" color="rgb(var(--v-theme-yellow))" variant="tonal">
|
||||
<div class="text-center">No data found</div>
|
||||
</VAlert>
|
||||
|
||||
</VCard>
|
||||
</VCard>
|
||||
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
button.v-expansion-panel-title {
|
||||
background-color: rgb(var(--v-theme-yellow)) !important;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
button.v-expansion-panel-title.bg-secondary {
|
||||
background-color: rgb(var(--v-theme-yellow)) !important;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
button.v-expansion-panel-title {
|
||||
background-color: rgb(var(--v-theme-yellow)) !important;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
span.v-expansion-panel-title__icon {
|
||||
margin-left: 0px !important;
|
||||
}
|
||||
|
||||
span.v-expansion-panel-title__icon {
|
||||
color: #fff
|
||||
}
|
||||
|
||||
.v-expansion-panel {
|
||||
background-color: #fff;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1);
|
||||
margin-bottom: 10px;
|
||||
overflow: hidden;
|
||||
transition: box-shadow 0.3s ease;
|
||||
}
|
||||
</style>
|
208
resources/js/pages/patient/prescriptions-history.vue
Normal file
208
resources/js/pages/patient/prescriptions-history.vue
Normal file
@@ -0,0 +1,208 @@
|
||||
<script setup>
|
||||
import store from "@/store";
|
||||
import moment from "moment";
|
||||
import { ref } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const getPrescriptionHistory = ref([]);
|
||||
const doctorAppiontments = ref([]);
|
||||
const selectedFilter = ref(null);
|
||||
const currentMonth = ref(null);
|
||||
|
||||
onMounted(async () => {
|
||||
await store.dispatch("getPrescriptionHistory");
|
||||
getPrescriptionHistory.value = store.getters.getPrescriptionHistory.history;
|
||||
console.log("getPrescriptionHistory", getPrescriptionHistory.value);
|
||||
store.dispatch("updateIsLoading", false);
|
||||
});
|
||||
|
||||
const filter = [
|
||||
{
|
||||
value: "This Month",
|
||||
key: "current_month",
|
||||
},
|
||||
{
|
||||
value: "Past Month",
|
||||
key: "1_month",
|
||||
},
|
||||
{
|
||||
value: "Past 2 Month from today",
|
||||
key: "2_months",
|
||||
},
|
||||
{
|
||||
value: "Past 3 Month from today",
|
||||
key: "3_months",
|
||||
},
|
||||
{
|
||||
value: "Past 6 Month from today",
|
||||
key: "6_months",
|
||||
},
|
||||
{
|
||||
value: "Past 12 Month from today",
|
||||
key: "12_months",
|
||||
},
|
||||
|
||||
{
|
||||
value: "All Time",
|
||||
key: "all_time",
|
||||
},
|
||||
];
|
||||
|
||||
const handleDateInput = async () => {
|
||||
getHistory.value = [];
|
||||
// await store.dispatch('getHistoryFilter', {
|
||||
// filter: selectedFilter.value,
|
||||
// })
|
||||
// getHistory.value = store.getters.getHistoryFilter.patients;
|
||||
// console.log("getHistoryFilter", getHistory.value);
|
||||
// store.dispatch('updateIsLoading', false)
|
||||
};
|
||||
|
||||
const search = ref("");
|
||||
const headers = [
|
||||
// { align: "start", key: "id", title: "Appiontment Id" },
|
||||
// { key: "patient_name", title: "Patient" },
|
||||
{ key: "appointment_date", sortable: false, title: "Date" },
|
||||
{ key: "start_time", title: "Start Time" },
|
||||
{ key: "end_time", title: "End Time" },
|
||||
{ key: "duration", title: "Duration" },
|
||||
{ key: "action", title: "Action" },
|
||||
];
|
||||
|
||||
const formattedHistory = computed(() => {
|
||||
return getPrescriptionHistory.value.map((history) => ({
|
||||
...history,
|
||||
appointment_date: changeFormat(history.appointment_date),
|
||||
start_time: changeDateFormat(history.start_time),
|
||||
end_time: changeDateFormat(history.end_time),
|
||||
duration: totalCallDuration(history.start_time, history.end_time),
|
||||
}));
|
||||
});
|
||||
function changeDateFormat(dateFormat) {
|
||||
console.log("startTimeFormat", dateFormat);
|
||||
if (dateFormat) {
|
||||
const [datePart, timePart] = dateFormat.split(" "); // Split date and time parts
|
||||
const [year, month, day] = datePart.split("-"); // Split date into year, month, and day
|
||||
const formattedMonth = parseInt(month).toString(); // Convert month to integer and then string to remove leading zeros
|
||||
const formattedDay = parseInt(day).toString(); // Convert day to integer and then string to remove leading zeros
|
||||
const formattedDate = `${formattedMonth}-${formattedDay}-${year}`; // Format date as mm-dd-yyyy
|
||||
return `${formattedDate} ${timePart}`; // Combine formatted date with original time part
|
||||
}
|
||||
}
|
||||
|
||||
function changeFormat(dateFormat) {
|
||||
const dateParts = dateFormat.split("-"); // Assuming date is in yyyy-mm-dd format
|
||||
const year = parseInt(dateParts[0]);
|
||||
const month = parseInt(dateParts[1]); // No need for padding
|
||||
const day = parseInt(dateParts[2]); // No need for padding
|
||||
|
||||
// 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;
|
||||
}
|
||||
// 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;
|
||||
// }
|
||||
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 historyDetail = (item) => {
|
||||
console.log("item", item);
|
||||
router.push("/patient/prescription-history/" + item.id);
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<VRow>
|
||||
<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>
|
||||
<VCol cols="12">
|
||||
<VCard title="Prescriptions">
|
||||
<v-card flat>
|
||||
<v-card-title class="d-flex align-center pe-2">
|
||||
<v-select
|
||||
label="April(Month to date)"
|
||||
v-model="selectedFilter"
|
||||
:items="filter"
|
||||
item-title="value"
|
||||
item-value="key"
|
||||
@update:modelValue="handleDateInput"
|
||||
></v-select>
|
||||
<!-- <v-text-field v-model="search" prepend-inner-icon="mdi-magnify" density="compact" label="Search"
|
||||
single-line flat hide-details variant="solo-filled"></v-text-field> -->
|
||||
<v-spacer></v-spacer>
|
||||
<v-spacer></v-spacer>
|
||||
<v-spacer></v-spacer>
|
||||
</v-card-title>
|
||||
|
||||
<v-data-table :headers="headers" :items="formattedHistory">
|
||||
<template v-slot:item.action="{ item }">
|
||||
<VBtn
|
||||
class="text-capitalize text-white"
|
||||
@click="historyDetail(item)"
|
||||
>Detail
|
||||
</VBtn>
|
||||
</template>
|
||||
</v-data-table>
|
||||
</v-card>
|
||||
</VCard>
|
||||
</VCol>
|
||||
</VRow>
|
||||
</template>
|
297
resources/js/pages/patient/schedules.vue
Normal file
297
resources/js/pages/patient/schedules.vue
Normal file
@@ -0,0 +1,297 @@
|
||||
<script setup>
|
||||
import moment from 'moment-timezone';
|
||||
import { ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useStore } from 'vuex';
|
||||
const store = useStore()
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const isMeetingEnable = ref(false);
|
||||
const isMeetingEnd = ref(true);
|
||||
const meetingInFuture = ref(true);
|
||||
const scheduleTime = ref();
|
||||
const scheduleDate = ref();
|
||||
const timeDifference = ref();
|
||||
const callEnd = ref('');
|
||||
const timeZone = ref();
|
||||
const scheduleData = ref([]);
|
||||
const expanded = ref([]);
|
||||
const panel = ref(0)
|
||||
const appId = ref();
|
||||
const dessertHeaders = ref(
|
||||
[
|
||||
// { title: '', key: 'data-table-expand' },
|
||||
{
|
||||
title: 'Order ID',
|
||||
align: 'start',
|
||||
sortable: false,
|
||||
key: 'name',
|
||||
},
|
||||
{ title: 'Date', key: 'calories' },
|
||||
{ title: 'Product', key: 'fat' },
|
||||
|
||||
]
|
||||
);
|
||||
const desserts = ref([
|
||||
{
|
||||
name: '24',
|
||||
calories: 'July 04, 2024 02:00 AM',
|
||||
fat: 2
|
||||
|
||||
},
|
||||
|
||||
]);
|
||||
onMounted(async () => {
|
||||
await fetchApiData();
|
||||
})
|
||||
const fetchApiData = async () => {
|
||||
store.dispatch('updateIsLoading', true)
|
||||
await store.dispatch('getPatientAppointment')
|
||||
appId.value = store.getters.getBookedAppointment.appiontmentId;
|
||||
let appointmentDate = convertUtcDateTimeToLocal(store.getters.getBookedAppointment.appointment_date, store.getters.getBookedAppointment.appointment_time, 'date')
|
||||
let appointmentTime = convertUtcDateTimeToLocal(store.getters.getBookedAppointment.appointment_date, store.getters.getBookedAppointment.appointment_time, 'time')
|
||||
// timeDifference.value = relativeTimeFromDate(appointmentDate, appointmentTime)
|
||||
scheduleDate.value = moment(appointmentDate, "YYYY-MM-DD").format("MMMM DD, YYYY")
|
||||
scheduleTime.value = moment(appointmentTime, "HH:mm:ss").format("hh:mm A");
|
||||
timeZone.value = store.getters.getBookedAppointment.timezone;
|
||||
|
||||
console.log("sch", scheduleDate.value, scheduleTime.value);
|
||||
// scheduleDate.value = formatDateToISO(store.getters.getBookedAppointment.appointment_date);
|
||||
// scheduleTime.value = store.getters.getBookedAppointment.appointment_time;
|
||||
callEnd.value = store.getters.getBookedAppointment.end_time;
|
||||
meetingInFuture.value = isDateTimeInFuture(scheduleDate.value, scheduleTime.value);
|
||||
iscallEnd();
|
||||
store.dispatch('updateIsLoading', false)
|
||||
}
|
||||
|
||||
const iscallEnd = async () => {
|
||||
if (callEnd && !meetingInFuture.value) {
|
||||
isMeetingEnable.value = false;
|
||||
console.log('Bth Conditin');
|
||||
}
|
||||
else if (callEnd) { // Call has been end
|
||||
isMeetingEnable.value = false;
|
||||
|
||||
console.log('callEnd');
|
||||
} else if (!meetingInFuture.value) { // Patient can Join Meeting
|
||||
isMeetingEnable.value = true;
|
||||
console.log('timeDiff');
|
||||
} else { // Call has been end
|
||||
console.log('else');
|
||||
isMeetingEnable.value = false;
|
||||
|
||||
}
|
||||
await nextTick();
|
||||
// isAgentCall();
|
||||
};
|
||||
|
||||
const isDateTimeInFuture = (dateString, timeString) => {
|
||||
// Combine the date and time strings into a datetime string
|
||||
const dateTimeString = dateString + " " + timeString;
|
||||
|
||||
// Convert the datetime string into a Date object
|
||||
const eventDateTime = new Date(dateTimeString);
|
||||
|
||||
// Get the current date and time
|
||||
const now = new Date();
|
||||
|
||||
// Compare the eventDateTime with the current date and time
|
||||
return eventDateTime > now;
|
||||
};
|
||||
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'.");
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<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>
|
||||
<VCol cols="12" class="px-0">
|
||||
|
||||
<VCardTitle class="my-1">Upcoming Appiontments</VCardTitle>
|
||||
|
||||
<VExpansionPanels multiple v-model="panel">
|
||||
<VExpansionPanel class="p-4">
|
||||
<VExpansionPanelTitle class="pb-5">
|
||||
<span>
|
||||
<p><b>#Order:</b> {{ appId }} </p>
|
||||
|
||||
<div class="pl-5">
|
||||
<p><b> Appiontment Date:</b> July 04, 2024 02:00 AM </p>
|
||||
</div>
|
||||
<div class="pl-5">
|
||||
<p><b> Timezone:</b> Asia Karachi</p>
|
||||
</div>
|
||||
<div class="pl-5">
|
||||
<b>Product:</b> 2
|
||||
</div>
|
||||
</span>
|
||||
|
||||
</VExpansionPanelTitle>
|
||||
|
||||
<VExpansionPanelText>
|
||||
<VRow>
|
||||
<VDivider />
|
||||
<VCol cols="12" md="8">
|
||||
<VCard class="rounder-4">
|
||||
<div class="d-flex align-center justify-space-between gap-1 mb-6">
|
||||
<div class="text-body-1 text-high-emphasis font-weight-medium">
|
||||
<v-icon class="mr-2" color="rgb(var(--v-theme-yellow))">mdi-shopping-cart</v-icon>
|
||||
Order Details
|
||||
</div>
|
||||
</div>
|
||||
<v-data-table :headers="dessertHeaders" :items="desserts">
|
||||
</v-data-table>
|
||||
</VCard>
|
||||
</VCol>
|
||||
|
||||
<VCol cols="12" md="4">
|
||||
<VCard class="mb-6" border>
|
||||
<VCardText>
|
||||
<div class="d-flex align-center justify-space-between gap-1 mb-6">
|
||||
<div class="text-body-1 text-high-emphasis font-weight-medium">
|
||||
<v-icon class="mr-2" color="rgb(var(--v-theme-yellow))">mdi-calendar-clock</v-icon>
|
||||
Appointment Details
|
||||
</div>
|
||||
</div>
|
||||
<div class="appointment-details">
|
||||
<div class="detail-item">
|
||||
<span class="detail-label">Appointment At:</span>
|
||||
<span class="detail-value">July 04, 2024 02:00 AM</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="appointment-details">
|
||||
<div class="detail-item pt-1">
|
||||
<span class="detail-label">Timezone:</span>
|
||||
<span class="detail-value pl-4">Asia/Karachi</span>
|
||||
</div>
|
||||
</div>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
</VCol>
|
||||
</VRow>
|
||||
</VExpansionPanelText>
|
||||
</VExpansionPanel>
|
||||
</VExpansionPanels>
|
||||
|
||||
|
||||
<!-- <VCardText class="d-flex flex-column gap-y-4 p-0 rounded-3"> -->
|
||||
<VCard v-if="scheduleDate && scheduleTime && callEnd == null" border class="rounded-3" flat>
|
||||
<VCardText class="d-flex flex-sm-row flex-column pa-5 py-2">
|
||||
<div class="text-no-wrap text-base m-0 pr-9 mr-9" color="secondary">
|
||||
<!-- <VImg
|
||||
:src="card.image"
|
||||
:width="60"
|
||||
:height="25"
|
||||
/> -->
|
||||
<p class="text-secondary mb-2 my-0">DateTime</p>
|
||||
<p class="text-base text-bold">
|
||||
<b>{{ scheduleDate }} {{ scheduleTime }}</b>
|
||||
|
||||
</p>
|
||||
<!-- <span class="text-body-1">**** **** **** {{ card.number.substring(card.number.length - 4) }}</span> -->
|
||||
</div>
|
||||
<div class="text-no-wrap text-base m-0" color="secondary">
|
||||
<!-- <VImg
|
||||
:src="card.image"
|
||||
:width="60"
|
||||
:height="25"
|
||||
/> -->
|
||||
<p class="text-secondary mb-2 my-0">Timezone</p>
|
||||
<p class="text-base">
|
||||
<b>{{ timeZone }} </b>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<VSpacer />
|
||||
|
||||
<div class="d-flex flex-column text-sm-end gap-2">
|
||||
<div class="order-sm-0 order-1 pt-0 mt-3">
|
||||
<RouterLink v-if="isMeetingEnable" to="/queue" target="_blank">
|
||||
<VBtn color="primary" class="me-2">
|
||||
Go to Meeting
|
||||
</VBtn>
|
||||
</RouterLink>
|
||||
<span v-else>
|
||||
<VBtn class="" color="primary" disabled>Go to Meeting
|
||||
</VBtn>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- <span class="mt-auto order-sm-1 order-0">Card expires at {{ card.expiry }}</span> -->
|
||||
</div>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
<VCard border flat v-else>
|
||||
|
||||
<VAlert border="start" color="rgb(var(--v-theme-yellow))" variant="tonal">
|
||||
<div class="text-center">No data found</div>
|
||||
</VAlert>
|
||||
|
||||
</VCard>
|
||||
|
||||
<!-- </VCardText> -->
|
||||
|
||||
</VCol>
|
||||
|
||||
|
||||
</template>
|
||||
<style scoped>
|
||||
.v-card-title {
|
||||
font-weight: 700 !important;
|
||||
}
|
||||
|
||||
.v-table {
|
||||
border-radius: 10px !important;
|
||||
line-height: 4.5;
|
||||
max-width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #fff !important;
|
||||
}
|
||||
|
||||
.v-table__wrapper {
|
||||
background-color: #fff !important;
|
||||
}
|
||||
|
||||
th.v-data-table__td.v-data-table-column--align-start.v-data-table__th {
|
||||
background-color: #fff !important;
|
||||
font-weight: 800px;
|
||||
}
|
||||
|
||||
th.v-data-table__td.v-data-table-column--align-start.v-data-table__th {
|
||||
background-color: #fff !important;
|
||||
font-weight: 800px !important;
|
||||
}
|
||||
|
||||
.v-data-table-header__content>span {
|
||||
font-weight: 700 !important;
|
||||
}
|
||||
|
||||
button.v-btn.v-btn--icon.v-theme--light.text-primary.v-btn--density-default.v-btn--size-small.v-btn--variant-text {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.v-data-table-header__content {
|
||||
font-weight: 700 !important;
|
||||
background-color: white !important;
|
||||
}
|
||||
|
||||
.v-table.v-table--has-top.v-table--has-bottom.v-theme--light.v-table--density-default.v-data-table {
|
||||
border-radius: 10px !important;
|
||||
}
|
||||
</style>
|
389
resources/js/pages/patient/subscription.vue
Normal file
389
resources/js/pages/patient/subscription.vue
Normal file
@@ -0,0 +1,389 @@
|
||||
<script setup>
|
||||
import { computed, onMounted, reactive, ref } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { useStore } from "vuex";
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const store = useStore();
|
||||
const statusOptions = ["Active", "Cancelled"];
|
||||
let filters = reactive({
|
||||
status: [],
|
||||
})
|
||||
let foundText = ref('')
|
||||
const subscriptions = ref([]);
|
||||
const search = ref("");
|
||||
const filteredSubscriptions = computed(() => {
|
||||
let subscriptionList = store.getters.getsubscriptionList;
|
||||
|
||||
// Check if subscriptionList is null or undefined
|
||||
if (!subscriptionList) {
|
||||
return [];
|
||||
}
|
||||
|
||||
subscriptions.value = subscriptionList;
|
||||
|
||||
if (filters.status.length > 0) {
|
||||
foundText.value = filters.status.join(', ')
|
||||
subscriptionList = subscriptionList.filter((order) =>
|
||||
filters.status.includes(order.status)
|
||||
);
|
||||
}
|
||||
|
||||
console.log('subscriptionList', subscriptionList)
|
||||
const searchLower = search.value.toLowerCase();
|
||||
console.log(searchLower)
|
||||
|
||||
return subscriptionList.filter(
|
||||
(sub) =>
|
||||
(sub.order_number && String(sub.order_number).toLowerCase().includes(searchLower)) ||
|
||||
(sub.product_title && String(sub.product_title).toLowerCase().includes(searchLower))
|
||||
);
|
||||
});
|
||||
const formatDate = (date) => {
|
||||
const messageDate = new Date(date);
|
||||
const options = {
|
||||
year: "numeric",
|
||||
month: "numeric",
|
||||
day: "numeric",
|
||||
|
||||
};
|
||||
const formattedDate = messageDate
|
||||
.toLocaleString("en-US", options)
|
||||
.replace(/\//g, "-");
|
||||
return `${formattedDate} `;
|
||||
};
|
||||
const getStatusColor = (status) => {
|
||||
if (status === "Active") return "green";
|
||||
if (status === "Cancelled") return "orange";
|
||||
return "red";
|
||||
};
|
||||
const getStatusIcon = (status) => {
|
||||
switch (status.toLowerCase()) {
|
||||
case 'active':
|
||||
return 'mdi-check-circle';
|
||||
case 'pending':
|
||||
return 'mdi-clock-outline';
|
||||
case 'cancelled':
|
||||
return 'mdi-cancel';
|
||||
case 'expired':
|
||||
return 'mdi-alert-circle-outline';
|
||||
default:
|
||||
return 'mdi-help-circle-outline';
|
||||
}
|
||||
}
|
||||
const viewDetails = (subscription) => {
|
||||
// Implement view details logic here
|
||||
console.log("View details for subscription:", subscription.id);
|
||||
router.push("/subscriptions-detail/" + subscription.id);
|
||||
};
|
||||
const subscriptionList = computed(() => {
|
||||
let filtered = store.getters.getsubscriptionList;
|
||||
subscriptions.value = store.getters.getsubscriptionList;
|
||||
|
||||
return subscriptions.value;
|
||||
});
|
||||
onMounted(async () => {
|
||||
store.dispatch("updateIsLoading", true);
|
||||
await store.dispatch("subscriptionList");
|
||||
});
|
||||
const getPlanIcon = (plan) => {
|
||||
switch (plan.toLowerCase()) {
|
||||
case "basic":
|
||||
return "mdi-star-outline";
|
||||
case "premium":
|
||||
return "mdi-star-half";
|
||||
case "pro":
|
||||
return "mdi-star";
|
||||
case "enterprise":
|
||||
return "mdi-medal";
|
||||
default:
|
||||
return "mdi-help-circle";
|
||||
}
|
||||
};
|
||||
const cancelSubscription = async (subscriptionData) => {
|
||||
// Implement cancellation logic here
|
||||
store.dispatch("updateIsLoading", true);
|
||||
await store.dispatch("subscriptionCancel", { id: subscriptionData.subscription_id });
|
||||
console.log("Cancelling subscription:", subscriptionData.subscription_id);
|
||||
await store.dispatch("subscriptionList");
|
||||
await store.getters.getsubscriptionList;
|
||||
store.dispatch("updateIsLoading", false);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<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>
|
||||
<v-container fluid class="subscription-container pa-6">
|
||||
<v-row justify="center" align="center" class="mb-8" style="display: none;">
|
||||
<v-col cols="12" md="8">
|
||||
<h4 class="text-h4 text-center gradient-text mb-4">
|
||||
Active Subscriptions
|
||||
</h4>
|
||||
<v-text-field v-model="search" prepend-inner-icon="mdi-magnify" label="Search subscriptions" outlined
|
||||
rounded hide-details dense class="search-field"></v-text-field>
|
||||
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
|
||||
<VCardTitle class="pt-0"><b>Subscriptions </b></VCardTitle>
|
||||
<v-card>
|
||||
<v-card-title>
|
||||
<v-row class="px-0 py-4">
|
||||
|
||||
|
||||
<v-col cols="12" md="4">
|
||||
<v-text-field v-model="search" prepend-inner-icon="mdi-magnify"
|
||||
label="Search subscriptions" outlined density="compact" hide-details
|
||||
class="search-field"></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4">
|
||||
<v-select v-model="filters.status" :items="statusOptions" label="Status" outlined
|
||||
density="compact" multiple></v-select>
|
||||
</v-col>
|
||||
|
||||
|
||||
</v-row>
|
||||
</v-card-title>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="filteredSubscriptions.length > 0">
|
||||
|
||||
<VCol sm="6" cols="12" v-for="subscription in filteredSubscriptions" :key="subscription.order_number"
|
||||
style="display: none;">
|
||||
<VCard>
|
||||
<div class="d-flex justify-space-between flex-wrap flex-md-nowrap flex-column flex-md-row">
|
||||
<div class="ma-auto pa-5">
|
||||
<VImg width="137" height="176" :src="subscription.image_url" />
|
||||
</div>
|
||||
|
||||
<VDivider :vertical="$vuetify.display.mdAndUp" />
|
||||
|
||||
<div>
|
||||
<VCardItem>
|
||||
|
||||
</VCardItem>
|
||||
|
||||
<VCardText>
|
||||
<h3>{{ subscription }}</h3>
|
||||
</VCardText>
|
||||
|
||||
<VCardText class="text-subtitle-1">
|
||||
<span>Price :</span> <span class="font-weight-medium">${{
|
||||
subscription.product_price.amount }}</span>
|
||||
<span v-if="subscription.subscription_start_date">Start :</span> <span
|
||||
class="font-weight-medium" v-if="subscription.subscription_start_date">{{
|
||||
formatDate(subscription.subscription_start_date)
|
||||
}}</span>
|
||||
<span v-if="subscription.subscription_renewal_date">Renewal Date :</span> <span
|
||||
class="font-weight-medium" v-if="subscription.subscription_renewal_date">{{
|
||||
formatDate(subscription.subscription_renewal_date)
|
||||
}}</span>
|
||||
<span>Status :</span> <span class="font-weight-medium">{{
|
||||
subscription.status }}</span>
|
||||
</VCardText>
|
||||
|
||||
<VCardActions class="justify-space-between">
|
||||
<VBtn>
|
||||
<VIcon icon="bx-cart-add" />
|
||||
<span class="ms-2">Add to cart</span>
|
||||
</VBtn>
|
||||
|
||||
<VBtn color="secondary" icon="bx-share-alt" />
|
||||
</VCardActions>
|
||||
</div>
|
||||
</div>
|
||||
</VCard>
|
||||
</VCol>
|
||||
|
||||
<v-col v-for="subscription in filteredSubscriptions" :key="subscription.order_number" cols="12" md="4">
|
||||
<v-hover v-slot:default="{ isHovering }">
|
||||
<v-card class="subscription-card mx-auto" :elevation="isHovering ? 8 : 2"
|
||||
:class="{ 'on-hover': isHovering }">
|
||||
<v-card-title class="text-h5 d-flex flex-column align-center">
|
||||
<v-avatar size="80" class="mb-3" color="rgb(var(--v-theme-yellow))"
|
||||
v-if="subscription.image_url">
|
||||
<v-img :src="subscription.image_url" :alt="subscription.title"></v-img>
|
||||
|
||||
</v-avatar>
|
||||
<v-avatar color="rgb(var(--v-theme-yellow))" size="56" v-if="!subscription.image_url"
|
||||
class="text-grey-800 text-h6 font-weight-bold mr-4">
|
||||
#{{ subscription.order_number }}
|
||||
</v-avatar>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<h4 class="text-center title">{{ subscription.product_title }}</h4>
|
||||
<v-list dense class="details-list">
|
||||
<v-list-item>
|
||||
<v-row align="center" no-gutters>
|
||||
<v-col cols="12" md="2">
|
||||
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" md="8">
|
||||
<v-icon color="primary">mdi-cash</v-icon> <span>Price: ${{
|
||||
subscription.product_price.amount }}</span>
|
||||
</v-col>
|
||||
<v-col cols="12" md="2">
|
||||
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-list-item>
|
||||
<v-list-item v-if="subscription.subscription_start_date">
|
||||
<v-row align="center" no-gutters>
|
||||
<v-col cols="12" md="2">
|
||||
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" md="8">
|
||||
<v-icon color="primary">mdi-calendar-start</v-icon><span>Start: {{
|
||||
formatDate(subscription.subscription_start_date) }}</span>
|
||||
</v-col>
|
||||
<v-col cols="12" md="2">
|
||||
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-list-item>
|
||||
<v-list-item v-if="subscription.subscription_renewal_date">
|
||||
<v-row align="center" no-gutters>
|
||||
<v-col cols="12" md="2">
|
||||
|
||||
</v-col>
|
||||
<v-col cols="12" md="8">
|
||||
<v-icon color="primary">mdi-calendar-clock</v-icon> <span>Next Billing: {{
|
||||
formatDate(subscription.subscription_renewal_date) }}</span>
|
||||
</v-col>
|
||||
<v-col cols="12" md="2">
|
||||
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<v-row align="center" no-gutters>
|
||||
<v-col cols="12" md="2">
|
||||
|
||||
</v-col>
|
||||
<v-col cols="12" md="8">
|
||||
<v-icon :color="getStatusColor(subscription.status)">
|
||||
{{ getStatusIcon(subscription.status) }}
|
||||
</v-icon>
|
||||
<span>Status: {{ subscription.status }}</span>
|
||||
</v-col>
|
||||
<v-col cols="12" md="2">
|
||||
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-btn color="rgb(var(--v-theme-yellow))" @click="cancelSubscription(subscription)"
|
||||
prepend-icon="mdi-cancel" block>Cancel
|
||||
Subscription</v-btn>
|
||||
<v-btn color="primary" block @click="viewDetails(subscription)">
|
||||
View Details
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-hover>
|
||||
</v-col>
|
||||
|
||||
</v-row>
|
||||
<v-row v-else justify="center" align="center">
|
||||
<v-col cols="12" class="text-center">
|
||||
<v-icon size="64" color="grey">mdi-playlist-remove</v-icon>
|
||||
<h3 class="mt-4">No {{ foundText }} Subscriptions</h3>
|
||||
<p class="text-subtitle-1">You don't have any {{foundText}} subscriptions at the moment.</p>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</template>
|
||||
<style scoped>
|
||||
.subscription-container {
|
||||
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.subscription-card {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.subscription-card.on-hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
.v-avatar {
|
||||
background-color: var(--v-primary-base);
|
||||
}
|
||||
|
||||
.subscription-container .v-card .v-card-text {
|
||||
line-height: 1.25rem;
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.gradient-text {
|
||||
background: linear-gradient(45deg, rgb(var(--v-theme-yellow)), #173320);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.search-field {
|
||||
max-width: 500px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.subscription-card {
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.subscription-card.on-hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
.v-list-item {
|
||||
min-height: 40px;
|
||||
}
|
||||
|
||||
.details-list .v-list-item {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.details-list .v-row {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.details-list .v-col {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.details-list .v-icon {
|
||||
margin-right: 0.5rem;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.details-list .v-col span {
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
color: var(--v-grey-darken1);
|
||||
}
|
||||
</style>
|
250
resources/js/pages/patient/subscriptionDetail.vue
Normal file
250
resources/js/pages/patient/subscriptionDetail.vue
Normal file
@@ -0,0 +1,250 @@
|
||||
<script setup>
|
||||
import { computed } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { useStore } from "vuex";
|
||||
|
||||
const store = useStore();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const subscription = computed(async () => {
|
||||
const id = route.params.id;
|
||||
await store.dispatch("subscriptionByID", { id: id });
|
||||
return store.getters.getSubscriptionById(id);
|
||||
});
|
||||
|
||||
const getPlanIcon = (title) => {
|
||||
if (!title) return "mdi-help-circle";
|
||||
switch (title.toLowerCase()) {
|
||||
case "basic":
|
||||
return "mdi-star-outline";
|
||||
case "premium":
|
||||
return "mdi-star-half";
|
||||
case "pro":
|
||||
return "mdi-star";
|
||||
case "enterprise":
|
||||
return "mdi-medal";
|
||||
default:
|
||||
return "mdi-help-circle";
|
||||
}
|
||||
};
|
||||
|
||||
const getPlanColor = (title) => {
|
||||
if (!title) return "grey";
|
||||
switch (title.toLowerCase()) {
|
||||
case "basic":
|
||||
return "blue";
|
||||
case "premium":
|
||||
return "purple";
|
||||
case "pro":
|
||||
return "green";
|
||||
case "enterprise":
|
||||
return "orange";
|
||||
default:
|
||||
return "grey";
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusColor = (status) => {
|
||||
if (!status) return "grey";
|
||||
switch (status.toLowerCase()) {
|
||||
case "active":
|
||||
return "success";
|
||||
case "paused":
|
||||
return "warning";
|
||||
case "cancelled":
|
||||
return "error";
|
||||
default:
|
||||
return "grey";
|
||||
}
|
||||
};
|
||||
|
||||
const formatDate = (dateString) => {
|
||||
return new Date(dateString).toLocaleDateString("en-US", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
});
|
||||
};
|
||||
|
||||
const formatPrice = (price) => {
|
||||
return new Intl.NumberFormat("en-US", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
}).format(price);
|
||||
};
|
||||
|
||||
const goBack = () => {
|
||||
router.go(-1);
|
||||
};
|
||||
|
||||
const cancelSubscription = () => {
|
||||
// Implement cancellation logic here
|
||||
console.log("Cancelling subscription:", subscription.value.id);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-container v-if="subscription" class="subscription-detail-container">
|
||||
<v-row justify="center">
|
||||
<v-col cols="12" md="10" lg="8">
|
||||
<v-card class="subscription-detail-card">
|
||||
<v-card-title
|
||||
class="text-h3 d-flex justify-center pa-6 gradient-background"
|
||||
>
|
||||
<v-avatar size="80" color="white" class="elevation-3">
|
||||
<v-icon
|
||||
:color="getPlanColor(subscription.title)"
|
||||
size="50"
|
||||
>
|
||||
{{ getPlanIcon(subscription.title) }}
|
||||
</v-icon>
|
||||
</v-avatar>
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text class="pa-6">
|
||||
<h2 class="text-h4 text-center mb-6">
|
||||
{{ subscription.title }} Plan
|
||||
</h2>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" md="6">
|
||||
<v-list>
|
||||
<v-list-item>
|
||||
<template v-slot:prepend>
|
||||
<v-icon color="primary"
|
||||
>mdi-identifier</v-icon
|
||||
>
|
||||
</template>
|
||||
<v-list-item-title
|
||||
>ID:
|
||||
{{
|
||||
subscription.id
|
||||
}}</v-list-item-title
|
||||
>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item>
|
||||
<template v-slot:prepend>
|
||||
<v-icon color="primary"
|
||||
>mdi-calendar-start</v-icon
|
||||
>
|
||||
</template>
|
||||
<v-list-item-title
|
||||
>Start Date:
|
||||
{{
|
||||
formatDate(
|
||||
subscription.startDate
|
||||
)
|
||||
}}</v-list-item-title
|
||||
>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item>
|
||||
<template v-slot:prepend>
|
||||
<v-icon color="primary"
|
||||
>mdi-calendar-clock</v-icon
|
||||
>
|
||||
</template>
|
||||
<v-list-item-title
|
||||
>Next Billing:
|
||||
{{
|
||||
formatDate(
|
||||
subscription.nextBillingDate
|
||||
)
|
||||
}}</v-list-item-title
|
||||
>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" md="6">
|
||||
<v-list>
|
||||
<v-list-item>
|
||||
<template v-slot:prepend>
|
||||
<v-icon color="primary"
|
||||
>mdi-currency-usd</v-icon
|
||||
>
|
||||
</template>
|
||||
<v-list-item-title
|
||||
>Price:
|
||||
{{
|
||||
formatPrice(subscription.price)
|
||||
}}</v-list-item-title
|
||||
>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item>
|
||||
<template v-slot:prepend>
|
||||
<v-icon color="primary"
|
||||
>mdi-information</v-icon
|
||||
>
|
||||
</template>
|
||||
<v-list-item-title>
|
||||
Status:
|
||||
<v-chip
|
||||
:color="
|
||||
getStatusColor(
|
||||
subscription.status
|
||||
)
|
||||
"
|
||||
small
|
||||
class="ml-2"
|
||||
>
|
||||
{{ subscription.status }}
|
||||
</v-chip>
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item>
|
||||
<template v-slot:prepend>
|
||||
<v-icon color="primary"
|
||||
>mdi-refresh</v-icon
|
||||
>
|
||||
</template>
|
||||
<v-list-item-title
|
||||
>Billing Cycle:
|
||||
{{
|
||||
subscription.billingCycle
|
||||
}}</v-list-item-title
|
||||
>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-divider class="my-6"></v-divider>
|
||||
|
||||
<h3 class="text-h5 mb-4">Plan Features</h3>
|
||||
<v-chip-group column>
|
||||
<v-chip
|
||||
v-for="feature in subscription.features"
|
||||
:key="feature"
|
||||
color="primary"
|
||||
outlined
|
||||
>
|
||||
{{ feature }}
|
||||
</v-chip>
|
||||
</v-chip-group>
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions class="pa-6">
|
||||
<v-btn
|
||||
color="primary"
|
||||
@click="goBack"
|
||||
prepend-icon="mdi-arrow-left"
|
||||
>Go Back</v-btn
|
||||
>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
color="error"
|
||||
@click="cancelSubscription"
|
||||
prepend-icon="mdi-cancel"
|
||||
>Cancel Subscription</v-btn
|
||||
>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</template>
|
1084
resources/js/pages/patient/upcomingAppiontment.vue
Normal file
1084
resources/js/pages/patient/upcomingAppiontment.vue
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user