This commit is contained in:
nasir@endelospay.com 2024-06-08 03:54:45 +05:00
parent 872735dac3
commit 908fdd43e5
14 changed files with 1588 additions and 20 deletions

View File

@ -48,4 +48,4 @@ export const ADMIN_LAB_KIT_UPDATE_API = MAIN_DOMAIN + "/api/admin/labkit-update/
export const ADMIN_LAB_KIT_ADD_API = MAIN_DOMAIN + "/api/admin/labkit-list-create"
export const ADMIN_LAB_KIT_DELETE_API = MAIN_DOMAIN + "/api/admin/labkit-delete/"
export const ADMIN_PATIENT_DETAIL_API = MAIN_DOMAIN + "/api/admin/patient-full-detail/"

View File

@ -91,10 +91,10 @@ const headers = [
key: 'list_sub_title',
},
{
title: 'ACTIONS',
key: 'actions',
},
// {
// title: 'ACTIONS',
// key: 'actions',
// },
]
const resolveStatusVariant = status => {
@ -377,7 +377,7 @@ onMounted(() => {
<VRow>
<VCol cols="12" md="8" class="d-flex align-center">
<VBtn color="primary" prepend-icon="ri-add-line" @click="addDialog = true">
<VBtn color="primary" prepend-icon="ri-add-line" @click="addDialog = true" style="display: none;">
New Medicine
</VBtn>
</VCol>

View File

@ -0,0 +1,106 @@
<script setup>
import { ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useStore } from 'vuex';
const store = useStore();
const router = useRouter();
const route = useRoute();
const patientId = route.params.patient_id;
const appointmentId = route.params.id;
const notes = ref([]);
const props = defineProps({
notesData: {
type: Object,
required: true,
},
})
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 downloadFile = (fileUrl) => {
const link = document.createElement('a');
link.href = fileUrl;
link.download = 'noteFile.png';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
</script>
<template>
<VCard title="Notes">
<VCardText>
<VTimeline
side="end"
align="start"
line-inset="8"
truncate-line="both"
density="compact"
>
<!-- SECTION Timeline Item: Flight -->
<template v-for="(p_note, index) in props.notesData.patientNotes" :key="index">
<VTimelineItem
dot-color="primary"
size="x-small"
>
<!-- 👉 Header -->
<div class="d-flex justify-space-between align-center gap-2 flex-wrap">
<span class="app-timeline-title">
{{p_note.note}}
</span>
<span class="app-timeline-meta">{{ formatDateDate(p_note.created_at) }}</span>
<!-- <span></span> -->
</div>
<!-- 👉 Content -->
<div class="app-timeline-text mb-1">
<span>{{ p_note.telemedPro.name }}</span>
<VIcon
size="20"
icon="tabler-arrow-right"
class="mx-2 flip-in-rtl"
/>
<!-- <span>Heathrow Airport, London</span> -->
</div>
<!-- <p class="app-timeline-meta mb-2">
6:30 AM
</p> -->
<!-- <div class="app-timeline-text d-flex align-center gap-2">
<div>
<VImg
:src="pdf"
:width="22"
/>
</div>
<span>booking-card.pdf</span>
</div> -->
</VTimelineItem>
</template>
<!-- !SECTION -->
<!-- !SECTION -->
</VTimeline>
</VCardText>
</VCard>
</template>

View File

@ -0,0 +1,202 @@
<script setup>
import moment from 'moment';
import { onBeforeMount, onMounted, onUnmounted, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useStore } from 'vuex';
const store = useStore();
const router = useRouter();
const route = useRoute();
const editDialog = ref(false);
const deleteDialog = ref(false);
const search = ref('');
const appointmentId = ref('');
const defaultItem = ref({
id: -1,
avatar: '',
name: '',
email: '',
dob: '',
phone_no: '',
});
const editedItem = ref({ ...defaultItem.value });
const editedIndex = ref(-1);
const patientMeetingList = ref([]);
const isLoading = ref(false);
// Status options
const selectedOptions = [
{ text: 'Active', value: 1 },
{ text: 'InActive', value: 2 },
];
const refVForm = ref(null);
// Headers
const headers = [
// { title: 'Appointment Id', key: 'id' },
{ title: 'Patient', key: 'patient_name' },
// { key: 'appointment_date', sortable: false, title: 'Date' },
{ key: 'start_time', title: 'Start Time' },
{ key: 'end_time', title: 'End Time' },
{ key: 'duration', title: 'Duration' },
{ title: 'ACTIONS', key: 'actions' },
];
// Utility functions
function changeDateFormat(dateFormat) {
if (dateFormat) {
const [datePart, timePart] = dateFormat.split(' ');
const [year, month, day] = datePart.split('-');
const formattedDate = `${parseInt(month)}-${parseInt(day)}-${year}`;
return `${formattedDate} ${timePart}`;
}
return dateFormat;
}
function changeFormat(dateFormat) {
const [year, month, day] = dateFormat.split('-');
return `${parseInt(month)}-${parseInt(day)}-${year}`;
}
function totalCallDuration(start_time, end_time) {
const startMoment = moment(start_time);
const endMoment = moment(end_time);
const duration = moment.duration(endMoment.diff(startMoment));
const hours = String(duration.hours()).padStart(2, '0');
const minutes = String(duration.minutes()).padStart(2, '0');
const seconds = String(duration.seconds()).padStart(2, '0');
if (hours === '00' && minutes === '00') {
return `00:00:${seconds}`;
} else if (hours === '00') {
return `00:${minutes}:${seconds}`;
} else {
return `${hours}:${minutes}:${seconds}`;
}
}
// Fetch and process the patient meeting list
const getPatientMeetingList = async () => {
store.dispatch('updateIsLoading', true);
await store.dispatch('patientMeetingList', { id: route.params.id });
store.dispatch('updateIsLoading', false);
let list = store.getters.getPatientMeetingList;
patientMeetingList.value = list.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),
}));
console.log(list);
};
// Lifecycle hooks
onBeforeMount(() => {});
onMounted(async () => {
await getPatientMeetingList();
});
onUnmounted(() => {});
const historyDetail = (item, value) => {
console.log('item',item.id ,value)
if(value == 'notes')
router.push('/admin/patient/meeting/notes/' + route.params.id + '/' + item.id);
if(value == 'prescription')
router.push('/admin/patient/meeting/prescription/' + route.params.id + '/' + item.id);
}
const menusVariant = [
'primary'
];
const options = [
{
title: 'Notes',
key: 'notes',
},
{
title: 'Prescription',
key: 'prescription',
},
]
const breadcrums = [
{
title: 'Patient',
disabled: false,
to: '/admin/patients',
},
{
title: 'Meetings',
disabled: false,
}
];
</script>
<template>
<v-row>
<v-col cols="12" md="12">
<v-card title="Meetings">
<v-card-text>
<v-row>
<v-col cols="12" offset-md="8" md="4">
<v-text-field
v-model="search"
label="Search"
placeholder="Search ..."
append-inner-icon="ri-search-line"
single-line
hide-details
dense
outlined
/>
</v-col>
</v-row>
</v-card-text>
<v-data-table
:headers="headers"
:items="patientMeetingList"
:search="search"
:items-per-page="5"
class="text-no-wrap"
>
<template #item.id="{ item }">{{ item.id }}</template>
<template #item.duration="{ item }">{{ item.duration }}</template>
<!-- Actions -->
<template #item.actions="{ item }">
<div class="demo-space-x">
<VMenu
v-for="menu in menusVariant"
:key="menu"
>
<template #activator="{ props }">
<i class="ri-more-2-line cursor-pointer" style="font-size: 32px;" v-bind="props"></i>
</template>
<v-list>
<v-list-item
v-for="opt in options"
:key="opt.value"
@click="historyDetail(item, opt.key)"
>
{{ opt.title }}
</v-list-item>
</v-list>
</VMenu>
</div>
<!-- <div class="d-flex gap-1">
<VBtn class="text-capitalize text-white" @click="historyDetail(item)"> Detail
</VBtn>
</div> -->
</template>
</v-data-table>
</v-card>
</v-col>
</v-row>
</template>

View File

@ -0,0 +1,360 @@
<script setup>
const props = defineProps({
userData: {
type: Object,
required: true,
},
})
const standardPlan = {
plan: 'Standard',
price: 99,
benefits: [
'10 Users',
'Up to 10GB storage',
'Basic Support',
],
}
const isUserInfoEditDialogVisible = ref(false)
const isUpgradePlanDialogVisible = ref(false)
const resolveUserStatusVariant = stat => {
if (stat === 'pending')
return 'warning'
if (stat === 'active')
return 'success'
if (stat === 'inactive')
return 'secondary'
return 'primary'
}
const resolveUserRoleVariant = role => {
if (role === 'subscriber')
return {
color: 'primary',
icon: 'ri-user-line',
}
if (role === 'author')
return {
color: 'warning',
icon: 'ri-settings-2-line',
}
if (role === 'maintainer')
return {
color: 'success',
icon: 'ri-database-2-line',
}
if (role === 'editor')
return {
color: 'info',
icon: 'ri-pencil-line',
}
if (role === 'admin')
return {
color: 'error',
icon: 'ri-server-line',
}
return {
color: 'primary',
icon: 'ri-user-line',
}
}
</script>
<template>
<VRow>
<!-- SECTION User Details -->
<VCol cols="12">
<VCard v-if="props.userData">
<VCardText class="text-center pt-12 pb-6">
<!-- 👉 Avatar -->
<VAvatar
rounded
:size="120"
:color="!props.userData.patient.avatar ? 'primary' : undefined"
:variant="!props.userData.patient.avatar ? 'tonal' : undefined"
>
<VImg
v-if="props.userData.patient.avatar"
:src="props.userData.patient.avatar"
/>
<span
v-else
class="text-5xl font-weight-medium"
>
{{ avatarText(props.userData.patient.first_name) }}
</span>
</VAvatar>
<!-- 👉 User fullName -->
<h5 class="text-h5 mt-4">
{{ props.userData.patient.first_name }} {{ props.userData.patient.last_name }}
</h5>
<!-- 👉 Role chip -->
<VChip
:color="resolveUserRoleVariant('Patient').color"
size="small"
class="text-capitalize mt-4"
>
Patient
</VChip>
</VCardText>
<!-- 👉 Details -->
<VCardText class="pb-6">
<h5 class="text-h5">
Details
</h5>
<VDivider class="my-4" />
<!-- 👉 User Details list -->
<VList class="card-list">
<VListItem>
<VListItemTitle class="text-sm">
<span class="font-weight-medium">
Email:
</span>
<span class="text-body-1">{{ props.userData.patient.email }}</span>
</VListItemTitle>
</VListItem>
<VListItem>
<VListItemTitle class="text-sm">
<span class="font-weight-medium">
Address:
</span>
<span class="text-body-1">{{ props.userData.patient.address }}</span>
</VListItemTitle>
</VListItem>
<VListItem>
<VListItemTitle class="text-sm">
<span class="font-weight-medium">
City:
</span>
<span class="text-body-1">{{ props.userData.patient.city }}</span>
</VListItemTitle>
</VListItem>
<VListItem>
<VListItemTitle class="text-sm">
<span class="font-weight-medium">
State:
</span>
<span class="text-body-1">{{ props.userData.patient.state }}</span>
</VListItemTitle>
</VListItem>
<VListItem>
<VListItemTitle class="text-sm">
<span class="font-weight-medium">
Zip:
</span>
<span class="text-body-1">{{ props.userData.patient.zip_code }}</span>
</VListItemTitle>
</VListItem>
<VListItem>
<VListItemTitle class="text-sm">
<span class="font-weight-medium">
Status:
</span>
<VChip
size="small"
:color="resolveUserStatusVariant('Active')"
class="text-capitalize"
>
Active
</VChip>
</VListItemTitle>
</VListItem>
<VListItem>
<VListItemTitle class="text-sm">
<span class="font-weight-medium">Role: </span>
<span class="text-capitalize text-body-1">Patient</span>
</VListItemTitle>
</VListItem>
<VListItem>
<VListItemTitle class="text-sm">
<span class="font-weight-medium">
Contact:
</span>
<span class="text-body-1">{{ props.userData.patient.phone_no }}</span>
</VListItemTitle>
</VListItem>
<VListItem>
<VListItemTitle class="text-sm">
<span class="font-weight-medium">
Country:
</span>
<span class="text-body-1">{{ props.userData.patient.country }}</span>
</VListItemTitle>
</VListItem>
</VList>
</VCardText>
<!-- 👉 Edit and Suspend button -->
<VCardText class="d-flex justify-center">
<VBtn
variant="elevated"
class="me-4"
@click="isUserInfoEditDialogVisible = true"
>
Edit
</VBtn>
<VBtn
variant="outlined"
color="error"
>
Suspend
</VBtn>
</VCardText>
</VCard>
</VCol>
<!-- !SECTION -->
<!-- SECTION Current Plan -->
<VCol cols="12">
<VCard
flat
class="current-plan"
>
<VCardText class="d-flex">
<!-- 👉 Standard Chip -->
<VChip
color="primary"
size="small"
>
{{ props.userData.plans[0].title }}
</VChip>
<VSpacer />
<!-- 👉 Current Price -->
<div class="d-flex align-center">
<sup class="text-primary text-lg font-weight-medium"> {{ props.userData.plans[0].currency }}</sup>
<h1 class="text-h1 text-primary">
{{ props.userData.plans[0].price }}
</h1>
<sub class="mt-3"><h6 class="text-h6 font-weight-regular">month</h6></sub>
</div>
</VCardText>
<VCardText>
<!-- 👉 Price Benefits -->
<VList class="card-list">
<VListItem
>
<div class="d-flex align-center">
<VIcon
size="10"
color="medium-emphasis"
class="me-2"
icon="ri-circle-fill"
/>
<div class="text-medium-emphasis">
{{ props.userData.plans[0].list_one_title }}
</div>
</div>
</VListItem>
<VListItem
>
<div class="d-flex align-center">
<VIcon
size="10"
color="medium-emphasis"
class="me-2"
icon="ri-circle-fill"
/>
<div class="text-medium-emphasis">
{{ props.userData.plans[0].list_sub_title }}
</div>
</div>
</VListItem>
<VListItem
>
<div class="d-flex align-center">
<VIcon
size="10"
color="medium-emphasis"
class="me-2"
icon="ri-circle-fill"
/>
<div class="text-medium-emphasis">
{{ props.userData.plans[0].list_two_title }}
</div>
</div>
</VListItem>
</VList>
<!-- 👉 Days -->
<div class="my-6">
<div class="d-flex mt-3 mb-2">
<h6 class="text-h6 font-weight-medium">
Days
</h6>
<VSpacer />
<h6 class="text-h6 font-weight-medium">
26 of 30 Days
</h6>
</div>
<!-- 👉 Progress -->
<VProgressLinear
rounded
:model-value="86"
height="8"
color="primary"
/>
<p class="text-sm mt-1">
4 days remaining
</p>
</div>
<!-- 👉 Upgrade Plan -->
<VBtn
block
@click="isUpgradePlanDialogVisible = true"
>
Upgrade Plan
</VBtn>
</VCardText>
</VCard>
</VCol>
<!-- !SECTION -->
</VRow>
<!-- 👉 Edit user info dialog -->
<UserInfoEditDialog
v-model:isDialogVisible="isUserInfoEditDialogVisible"
:user-data="props.userData"
/>
<!-- 👉 Upgrade plan dialog -->
<UserUpgradePlanDialog v-model:isDialogVisible="isUpgradePlanDialogVisible" />
</template>
<style lang="scss" scoped>
.card-list {
--v-card-list-gap: .5rem;
}
.current-plan {
border: 2px solid rgb(var(--v-theme-primary));
}
.text-capitalize {
text-transform: capitalize !important;
}
</style>

View File

@ -0,0 +1,352 @@
<script setup>
import { useRoute, useRouter } from 'vue-router';
import { useStore } from 'vuex';
const store = useStore();
const router = useRouter()
const route = useRoute()
const itemsPrescriptions = ref([]);
const patientId = route.params.patient_id;
const props = defineProps({
prescriptionData: {
type: Object,
required: true,
},
})
const prescription = computed(async () => {
console.log('computed=====')
await getprescriptionList()
});
const getprescriptionList = async () => {
let prescriptions = props.prescriptionData.prescriptionData
console.log("BeforeOverviewItem", prescriptions);
// itemsPrescriptions.value = store.getters.getPrescriptionList
for (let data of prescriptions) {
let dataObject = {}
dataObject.name = data.prescription.name
dataObject.brand = data.prescription.brand
dataObject.from = data.prescription.from
dataObject.direction_quantity = data.prescription.direction_quantity
dataObject.dosage = data.prescription.dosage
dataObject.quantity = data.prescription.quantity
dataObject.refill_quantity = data.prescription.refill_quantity
dataObject.actions = ''
dataObject.id = data.prescription.id
dataObject.comments = data.comments
dataObject.direction_one = data.direction_one
dataObject.direction_two= data.direction_two
dataObject.status = data.status
dataObject.date = formatDateDate(data.created_at)
itemsPrescriptions.value.push(dataObject)
}
console.log(itemsPrescriptions.value)
itemsPrescriptions.value.sort((a, b) => {
return b.id - a.id;
});
console.log("OverviewItem", 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 'success';
case 'returned':
return 'red';
case 'results':
return 'blue';
default:
return 'grey'; // Use Vuetify's grey color for any other status
}
};
const resolveStatusVariant = status => {
if (status === 1)
return {
color: 'primary',
text: 'Current',
}
else if (status === 2)
return {
color: 'success',
text: 'Professional',
}
else if (status === 3)
return {
color: 'error',
text: 'Rejected',
}
else if (status === 4)
return {
color: 'warning',
text: 'Resigned',
}
else
return {
color: 'info',
text: 'Applied',
}
}
const headers = [
{
title: 'Name',
key: 'name',
},
{
title: 'Date',
key: 'date',
},
{
title: 'Status',
key: 'status',
},
]
const prescriptionIndex = ref([]);
const prescriptionItem = ref(-1)
const prescriptionDialog = ref(false)
const selectedItem = ref(null);
const showDetail = item => {
// console.log("id",item);
prescriptionIndex.value = item
selectedItem.value = item;
// console.log("index",prescriptionIndex.value);
// prescriptionItem.value = { ...item }
prescriptionDialog.value = true
}
const close = () => {
prescriptionDialog.value = false
prescriptionIndex.value = -1
prescriptionItem.value = { ...defaultItem.value }
}
</script>
<template>
<v-row>
<v-col cols="12" md="12" v-if="itemsPrescriptions">
<v-card title="Prescriptions" v-if="prescription">
<VCardText >
</VCardText>
<VDataTable
:headers="headers"
:items="itemsPrescriptions"
:items-per-page="5"
class="text-no-wrap"
>
<!-- full name -->
<!-- status -->
<template #item.status="{ item }">
<VChip
:color="getStatusColor(item.status)"
density="comfortable"
>
{{ getStatusColor(item.status) }}
</VChip>
</template>
<!-- Actions -->
<template #item.name="{ item }">
<router-link
:to="{ name: 'admin-patient-profile', params: { id: item.id } }"
class="text-high-emphasis "
@click.prevent=showDetail(item)
>
{{ item.name }}
</router-link>
</template>
</VDataTable>
</v-card>
</v-col>
</v-row>
<VDialog
v-model="prescriptionDialog"
max-width="600px"
>
<VCard :title=prescriptionIndex.name>
<DialogCloseBtn
variant="text"
size="default"
@click="[prescriptionDialog = false,selectedItem = '']"
/>
<VCardText>
<v-row class='mt-0'>
<v-col cols="12" md="4" sm="6">
<p class='heading mb-0 text-right'><b>Brand:</b></p>
</v-col>
<v-col cols="12" md="4" sm="6">
<p>{{ prescriptionIndex.brand }}</p>
</v-col>
</v-row>
<VDivider></VDivider>
<v-row class='mt-1'>
<v-col cols="12" md="4" sm="6">
<p class='heading mb-0 text-right'><b>From:</b></p>
</v-col>
<v-col cols="12" md="4" sm="6">
<p>{{ prescriptionIndex.from }}</p>
</v-col>
</v-row>
<VDivider></VDivider>
<v-row class='mt-1'>
<v-col cols="12" md="4" sm="6">
<p class='heading mb-0 text-right'><b>Dosage:</b></p>
</v-col>
<v-col cols="12" md="4" sm="6">
<p>{{ prescriptionIndex.dosage }}</p>
</v-col>
</v-row>
<VDivider></VDivider>
<v-row class='mt-1'>
<v-col cols="12" md="4" sm="6">
<p class='heading mb-0 text-right'><b>Quantity:</b></p>
</v-col>
<v-col cols="12" md="4" sm="6">
<p>{{ prescriptionIndex.quantity }}</p>
</v-col>
</v-row>
<VDivider></VDivider>
<v-row class='mt-1'>
<v-col cols="12" md="4" sm="6">
<p class='heading mb-0 text-right'><b>Direction Quantity:</b></p>
</v-col>
<v-col cols="12" md="8" sm="6">
<p>{{ prescriptionIndex.direction_quantity }}</p>
</v-col>
</v-row>
<VDivider></VDivider>
<v-row class='mt-1'>
<v-col cols="12" md="4" sm="6">
<p class='heading mb-0 text-right'><b>Direction One:</b></p>
</v-col>
<v-col cols="12" md="8" sm="6">
<p>{{ prescriptionIndex.direction_one }} </p>
</v-col>
</v-row>
<VDivider></VDivider>
<v-row class='mt-1'>
<v-col cols="12" md="4" sm="6">
<p class='heading mb-0 text-right'><b>Direction Two:</b></p>
</v-col>
<v-col cols="12" md="8" sm="6">
<p>{{ prescriptionIndex.direction_two }} </p>
</v-col>
</v-row>
<VDivider></VDivider>
<v-row class='mt-1'>
<v-col cols="12" md="4" sm="6">
<p class='heading mb-0 text-right'><b>Refill Quantity:</b></p>
</v-col>
<v-col cols="12" md="8" sm="6">
<p>{{ prescriptionIndex.refill_quantity }}</p>
</v-col>
</v-row>
<VDivider></VDivider>
<v-row class='mt-1'>
<v-col cols="12" md="4" sm="6">
<p class='heading mb-0 text-right'><b>Status:</b></p>
</v-col>
<v-col cols="12" md="8" sm="6">
<p v-if="prescriptionIndex.status == null" class="text-warning">Pending</p>
<p v-else>{{ prescriptionIndex.status }}</p>
</v-col>
</v-row>
<VDivider></VDivider>
<v-row class='mt-1 pb-0'>
<v-col cols="12" md="4" sm="6">
<p class='heading mb-0 text-right'><b>Comments:</b></p>
</v-col>
<v-col cols="12" md="8" sm="8">
<p>{{ prescriptionIndex.comments }} </p>
</v-col>
</v-row>
</VCardText>
</VCard>
</VDialog>
</template>
<style lang="scss">
button.v-expansion-panel-title {
background-color: #003152 !important;
color: #fff;
}
button.v-expansion-panel-title.bg-secondary {
background-color: #003152 !important;
color: #fff;
}
button.v-expansion-panel-title {
background-color: #003152 !important;
color: #fff;
}
span.v-expansion-panel-title__icon {
color: #fff
}
// button.v-expansion-panel-title.bg-secondary {
// background-color: rgba(var(--v-theme-primary), var(--v-activated-opacity)) !important;
// }
// button.v-expansion-panel-title {
// background-color: rgba(var(--v-theme-primary), var(--v-activated-opacity)) !important;
// }
// button.v-expansion-panel-title.v-expansion-panel-title--active {
// background-color: rgba(var(--v-theme-primary), var(--v-activated-opacity)) !important;
// }
.v-expansion-panel {
overflow: hidden;
border-radius: 16px;
background-color: #fff;
box-shadow: 0 4px 12px rgba(0, 0, 0, 10%);
margin-block-end: 10px;
transition: box-shadow 0.3s ease;
}
.details {
position: relative;
box-sizing: content-box;
border: 2px solid #fff;
border-radius: 1em;
background-color: #696cff;
block-size: 0.85em;
box-shadow: 0 0 3px rgba(67, 89, 113, 80%);
color: #fff;
content: "+";
font-family: "Courier New", Courier, monospace;
font-weight: 500;
inline-size: 0.85em;
inset-block-start: 50%;
inset-block-start: 0.7em;
inset-inline-start: 50%;
line-height: 0.9em;
text-align: center;
transform: translate(-50%, -50%);
}
</style>

View File

@ -0,0 +1,286 @@
<script setup>
import { useTheme } from 'vuetify'
import UserInvoiceTable from './UserInvoiceTable.vue'
import pdf from '@images/icons/project-icons/pdf.png'
import avatar2 from '@images/avatars/avatar-2.png'
import avatar3 from '@images/avatars/avatar-3.png'
import avatar4 from '@images/avatars/avatar-4.png'
import avatar5 from '@images/avatars/avatar-5.png'
import figma from '@images/icons/project-icons/figma.png'
import html5 from '@images/icons/project-icons/html5.png'
import python from '@images/icons/project-icons/python.png'
import react from '@images/icons/project-icons/react.png'
import sketch from '@images/icons/project-icons/sketch.png'
import vue from '@images/icons/project-icons/vue.png'
import xamarin from '@images/icons/project-icons/xamarin.png'
const projectTableHeaders = [
{
title: 'PROJECT',
key: 'name',
},
{
title: 'TOTAL TASK',
key: 'totalTask',
},
{
title: 'PROGRESS',
key: 'progress',
},
{
title: 'HOURS',
key: 'hours',
},
]
const { name } = useTheme()
const projects = [
{
logo: react,
name: 'BGC eCommerce App',
project: 'React Project',
totalTask: '122/240',
progress: 78,
hours: '18:42',
},
{
logo: figma,
name: 'Falcon Logo Design',
project: 'Figma Project',
totalTask: '09/56',
progress: 18,
hours: '20:42',
},
{
logo: vue,
name: 'Dashboard Design',
project: 'Vuejs Project',
totalTask: '290/320',
progress: 62,
hours: '120:87',
},
{
logo: xamarin,
name: 'Foodista mobile app',
project: 'Xamarin Project',
totalTask: '290/320',
progress: 8,
hours: '120:87',
},
{
logo: python,
name: 'Dojo Email App',
project: 'Python Project',
totalTask: '120/186',
progress: 49,
hours: '230:10',
},
{
logo: sketch,
name: 'Blockchain Website',
project: 'Sketch Project',
totalTask: '99/109',
progress: 92,
hours: '342:41',
},
{
logo: html5,
name: 'Hoffman Website',
project: 'HTML Project',
totalTask: '98/110',
progress: 88,
hours: '12:45',
},
]
const resolveUserProgressVariant = progress => {
if (progress <= 25)
return 'error'
if (progress > 25 && progress <= 50)
return 'warning'
if (progress > 50 && progress <= 75)
return 'primary'
if (progress > 75 && progress <= 100)
return 'success'
return 'secondary'
}
const search = ref('')
</script>
<template>
<VRow>
<VCol cols="12">
<VCard title="Project List">
<template #append>
<VTextField
v-model="search"
placeholder="Search Project"
density="compact"
style="inline-size: 10rem;"
/>
</template>
<!-- 👉 User Project List Table -->
<!-- SECTION Datatable -->
<VDataTable
:search="search"
:headers="projectTableHeaders"
:items="projects"
item-value="name"
class="text-no-wrap rounded-0"
>
<!-- projects -->
<template #item.name="{ item }">
<div class="d-flex align-center">
<VAvatar
:size="34"
class="me-3"
:image="item.logo"
/>
<div>
<h6 class="text-h6 mb-0">
{{ item.name }}
</h6>
<p class="text-sm text-medium-emphasis mb-0">
{{ item.project }}
</p>
</div>
</div>
</template>
<!-- total task -->
<template #item.totalTask="{ item }">
<div class="text-high-emphasis">
{{ item.totalTask }}
</div>
</template>
<!-- Progress -->
<template #item.progress="{ item }">
<div class="text-high-emphasis">
{{ item.progress }}%
</div>
<VProgressLinear
:height="6"
:model-value="item.progress"
rounded
:color="resolveUserProgressVariant(item.progress)"
/>
</template>
<!-- remove footer -->
<!-- TODO refactor this after vuetify community gives answer -->
<template #bottom />
</VDataTable>
<!-- !SECTION -->
</VCard>
</VCol>
<VCol cols="12">
<!-- 👉 Activity timeline -->
<VCard title="User Activity Timeline">
<VCardText>
<VTimeline
density="compact"
align="start"
truncate-line="both"
:line-inset="8"
class="v-timeline-density-compact"
>
<VTimelineItem
dot-color="error"
size="x-small"
>
<div class="d-flex justify-space-between align-center flex-wrap gap-2 mb-3">
<span class="app-timeline-title">
12 Invoices have been paid
</span>
<span class="app-timeline-meta">12 min ago</span>
</div>
<p class="app-timeline-text mb-2">
Invoices have been paid to the company
</p>
<div class="d-inline-flex align-center timeline-chip">
<img
:src="pdf"
height="20"
class="me-2"
alt="img"
>
<span class="app-timeline-text font-weight-medium">
invoice.pdf
</span>
</div>
</VTimelineItem>
<VTimelineItem
dot-color="primary"
size="x-small"
>
<div class="d-flex justify-space-between align-center flex-wrap gap-2 mb-3">
<span class="app-timeline-title">
Client Meeting
</span>
<span class="app-timeline-meta">45 min ago</span>
</div>
<p class="app-timeline-text mb-2">
React Project meeting with john @10:15am
</p>
<div class="d-flex align-center mt-3">
<VAvatar
size="32"
class="me-2"
:image="avatar2"
/>
<div>
<p class="text-sm font-weight-medium mb-0">
Lester McCarthy (Client)
</p>
<span class="text-sm">CEO of Kelly Group</span>
</div>
</div>
</VTimelineItem>
<VTimelineItem
dot-color="info"
size="x-small"
>
<div class="d-flex justify-space-between align-center flex-wrap gap-2 mb-3">
<span class="app-timeline-title">
Create a new project for client
</span>
<span class="app-timeline-meta">2 day ago</span>
</div>
<p class="app-timeline-text mb-2">
6 team members in a project
</p>
<div class="v-avatar-group">
<VAvatar
v-for="avatar in [avatar2, avatar3, avatar4, avatar5]"
:key="avatar"
:image="avatar"
/>
<VAvatar :color="name === 'light' ? '#F0EFF0' : '#3F3B59'">
<span class="text-high-emphasis">+3</span>
</VAvatar>
</div>
</VTimelineItem>
</VTimeline>
</VCardText>
</VCard>
</VCol>
<VCol cols="12">
<UserInvoiceTable />
</VCol>
</VRow>
</template>

View File

@ -0,0 +1,114 @@
<script setup>
import NotesPanel from '@/pages/patients/NotesPanel.vue'
import PatienTabOverview from '@/pages/patients/PatienTabOverview.vue'
import PatientBioPanel from '@/pages/patients/PatientBioPanel.vue'
import PrescriptionPanel from '@/pages/patients/PrescriptionPanel.vue'
import UserTabNotifications from '@/views/apps/user/view/UserTabNotifications.vue'
import { useStore } from 'vuex'
const patientDtail = ref(null);
const store = useStore();
const route = useRoute('apps-user-view-id')
const userTab = ref(null)
const tabs = [
{
icon: 'ri-group-line',
title: 'Overview',
},
{
icon: 'ri-lock-2-line',
title: 'Notes',
},
{
icon: 'ri-bookmark-line',
title: 'Prescriptions',
},
// {
// icon: 'ri-notification-4-line',
// title: 'Notifications',
// },
// {
// icon: 'ri-link-m',
// title: 'Connections',
// },
]
const getPatientDeatail = async () => {
store.dispatch('updateIsLoading', true);
await store.dispatch('patientDetial', { id: route.params.id });
store.dispatch('updateIsLoading', false);
let list = store.getters.getPatientDetail;
patientDtail.value=list
console.log(list.patient);
};
onMounted(async () => {
await getPatientDeatail();
});
//const { data: userData } = await useApi(`/apps/users/${ route.params.id }`)
</script>
<template>
<VRow v-if="patientDtail">
<VCol
cols="12"
md="5"
lg="4"
>
<PatientBioPanel :user-data="patientDtail" />
</VCol>
<VCol
cols="12"
md="7"
lg="8"
>
<VTabs
v-model="userTab"
class="v-tabs-pill"
>
<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>
<PatienTabOverview :notes-data="patientDtail"/>
</VWindowItem>
<VWindowItem>
<NotesPanel :notes-data="patientDtail"/>
</VWindowItem>
<VWindowItem>
<PrescriptionPanel :prescription-data="patientDtail"/>
</VWindowItem>
<VWindowItem>
<UserTabNotifications />
</VWindowItem>
</VWindow>
</VCol>
</VRow>
<VCard v-else>
<VCardTitle class="text-center">
No User Found
</VCardTitle>
</VCard>
</template>

View File

@ -94,17 +94,17 @@ const headers = [
key: 'phone_no',
},
{
title: 'Lab Kit',
key: 'labkit',
},
// {
// title: 'Lab Kit',
// key: 'labkit',
// },
{
title: 'ACTIONS',
key: 'actions',
},
// {
// title: 'ACTIONS',
// key: 'actions',
// },
]
const resolveStatusVariant = status => {
@ -272,9 +272,14 @@ const LabKit = (item)=> {
class="text-sm"
>{{ avatarText(item.name) }}</span>
</VAvatar>
<div class="d-flex flex-column ms-3">
<router-link
:to="{ name: 'admin-patient-profile', params: { id: item.id } }"
class="text-high-emphasis "
>
<span class="d-block font-weight-medium text-high-emphasis text-truncate">{{ item.name }}</span>
</router-link>
<small>{{ item.post }}</small>
</div>
</div>

View File

@ -0,0 +1,101 @@
<script setup>
import UserBioPanel from '@/views/apps/user/view/UserBioPanel.vue'
import UserTabBillingsPlans from '@/views/apps/user/view/UserTabBillingsPlans.vue'
import UserTabConnections from '@/views/apps/user/view/UserTabConnections.vue'
import UserTabNotifications from '@/views/apps/user/view/UserTabNotifications.vue'
import UserTabOverview from '@/views/apps/user/view/UserTabOverview.vue'
import UserTabSecurity from '@/views/apps/user/view/UserTabSecurity.vue'
const route = useRoute('apps-user-view-id')
const userTab = ref(null)
const tabs = [
{
icon: 'ri-group-line',
title: 'Overview',
},
{
icon: 'ri-lock-2-line',
title: 'Security',
},
{
icon: 'ri-bookmark-line',
title: 'Billing & Plan',
},
{
icon: 'ri-notification-4-line',
title: 'Notifications',
},
{
icon: 'ri-link-m',
title: 'Connections',
},
]
const { data: userData } = await useApi(`/apps/users/${ route.params.id }`)
</script>
<template>
<VRow v-if="userData">
<VCol
cols="12"
md="5"
lg="4"
>
<UserBioPanel :user-data="userData" />
</VCol>
<VCol
cols="12"
md="7"
lg="8"
>
<VTabs
v-model="userTab"
class="v-tabs-pill"
>
<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>
<UserTabOverview />
</VWindowItem>
<VWindowItem>
<UserTabSecurity />
</VWindowItem>
<VWindowItem>
<UserTabBillingsPlans />
</VWindowItem>
<VWindowItem>
<UserTabNotifications />
</VWindowItem>
<VWindowItem>
<UserTabConnections />
</VWindowItem>
</VWindow>
</VCol>
</VRow>
<VCard v-else>
<VCardTitle class="text-center">
No User Found
</VCardTitle>
</VCard>
</template>

View File

@ -74,10 +74,10 @@ const headers = [
key: 'phone_no',
},
{
title: 'ACTIONS',
key: 'actions',
},
// {
// title: 'ACTIONS',
// key: 'actions',
// },
]
const resolveStatusVariant = status => {
@ -253,12 +253,14 @@ onMounted(() => {
<IconBtn
size="small"
@click="editItem(item)"
style="display: none;"
>
<VIcon icon="ri-pencil-line" />
</IconBtn>
<IconBtn
size="small"
@click="deleteItem(item)"
style="display: none;"
>
<VIcon icon="ri-delete-bin-line" />
</IconBtn>

View File

@ -112,6 +112,11 @@ export const routes = [
path: '/admin/site-setting',
name: 'admin-site-setting',
component: () => import('@/views/pages/account-settings/WebsiteSettings.vue'),
},
{
path: '/admin/patients/patient-profile/:id',
name: 'admin-patient-profile',
component: () => import('@/pages/patients/patient-profile.vue'),
},
{
path: '/apps/email/filter/:filter',

View File

@ -7,6 +7,7 @@ import {
ADMIN_LAB_KIT_LIST_API,
ADMIN_LAB_KIT_UPDATE_API,
ADMIN_LOGIN_DETAIL,
ADMIN_PATIENT_DETAIL_API,
ADMIN_UPDATE_PASSWORD,
ADMIN_UPDATE_SITE_SETTING,
APPOINTMENT_DETAILS_API,
@ -56,7 +57,8 @@ export default createStore({
showMessage: null,
timeout: null,
checkLoginExpire: false,
labKitList:[]
labKitList: [],
patientDetail:null
},
mutations: {
setLoading(state, payload) {
@ -87,6 +89,10 @@ export default createStore({
console.log('payload');
state.showMessage = payload
},
setPatientDetail(state, payload) {
console.log('payload');
state.patientDetail = payload
},
setPtientList(state, payload) {
state.patientList = payload
@ -874,6 +880,24 @@ export default createStore({
console.error('Error:', error);
});
},
async patientDetial({ commit }, payload) {
commit('setLoading', true)
await axios.post(ADMIN_PATIENT_DETAIL_API+payload.id, {}, {
headers: {
'Authorization': `Bearer ${localStorage.getItem('admin_access_token')}`,
}
}) .then(response => {
commit('setLoading', false)
console.log('Response:', response.data);
commit('setPatientDetail',response.data)
})
.catch(error => {
commit('setLoading', false)
console.error('Error:', error);
});
},
},
getters: {
getIsLoading(state){
@ -938,5 +962,8 @@ export default createStore({
getLabKitList(state) {
return state.labKitList
},
getPatientDetail(state) {
return state.patientDetail
},
}
})

8
typed-router.d.ts vendored
View File

@ -148,6 +148,7 @@ declare module 'vue-router/auto/routes' {
'pages-icons': RouteRecordInfo<'pages-icons', '/pages/icons', Record<never, never>, Record<never, never>>,
'pages-misc-coming-soon': RouteRecordInfo<'pages-misc-coming-soon', '/pages/misc/coming-soon', Record<never, never>, Record<never, never>>,
'pages-misc-under-maintenance': RouteRecordInfo<'pages-misc-under-maintenance', '/pages/misc/under-maintenance', Record<never, never>, Record<never, never>>,
'pages-patient-labkits-labkit': RouteRecordInfo<'pages-patient-labkits-labkit', '/pages/patient-labkits/labkit', Record<never, never>, Record<never, never>>,
'pages-patient-meetings-notes': RouteRecordInfo<'pages-patient-meetings-notes', '/pages/patient-meetings/notes', Record<never, never>, Record<never, never>>,
'pages-patient-meetings-prescription': RouteRecordInfo<'pages-patient-meetings-prescription', '/pages/patient-meetings/prescription', Record<never, never>, Record<never, never>>,
'pages-pricing': RouteRecordInfo<'pages-pricing', '/pages/pricing', Record<never, never>, Record<never, never>>,
@ -155,9 +156,16 @@ declare module 'vue-router/auto/routes' {
'pages-user-profile-tab': RouteRecordInfo<'pages-user-profile-tab', '/pages/user-profile/:tab', { tab: ParamValue<true> }, { tab: ParamValue<false> }>,
'patients-meeting-details': RouteRecordInfo<'patients-meeting-details', '/patients/meeting-details', Record<never, never>, Record<never, never>>,
'patients-meetings': RouteRecordInfo<'patients-meetings', '/patients/meetings', Record<never, never>, Record<never, never>>,
'patients-notes-panel': RouteRecordInfo<'patients-notes-panel', '/patients/NotesPanel', Record<never, never>, Record<never, never>>,
'patients-patient-profile': RouteRecordInfo<'patients-patient-profile', '/patients/patient-profile', Record<never, never>, Record<never, never>>,
'patients-patien-tab-overview': RouteRecordInfo<'patients-patien-tab-overview', '/patients/PatienTabOverview', Record<never, never>, Record<never, never>>,
'patients-patient-bio-panel': RouteRecordInfo<'patients-patient-bio-panel', '/patients/PatientBioPanel', Record<never, never>, Record<never, never>>,
'patients-patients': RouteRecordInfo<'patients-patients', '/patients/patients', Record<never, never>, Record<never, never>>,
'patients-prescription-panel': RouteRecordInfo<'patients-prescription-panel', '/patients/PrescriptionPanel', Record<never, never>, Record<never, never>>,
'patients-user-tab-overview': RouteRecordInfo<'patients-user-tab-overview', '/patients/UserTabOverview', Record<never, never>, Record<never, never>>,
'providers-meeting-details': RouteRecordInfo<'providers-meeting-details', '/providers/meeting-details', Record<never, never>, Record<never, never>>,
'providers-meetings': RouteRecordInfo<'providers-meetings', '/providers/meetings', Record<never, never>, Record<never, never>>,
'providers-provider-profile': RouteRecordInfo<'providers-provider-profile', '/providers/provider-profile', Record<never, never>, Record<never, never>>,
'providers-providers': RouteRecordInfo<'providers-providers', '/providers/providers', Record<never, never>, Record<never, never>>,
'register': RouteRecordInfo<'register', '/register', Record<never, never>, Record<never, never>>,
'tables-data-table': RouteRecordInfo<'tables-data-table', '/tables/data-table', Record<never, never>, Record<never, never>>,