553 lines
18 KiB
Vue
553 lines
18 KiB
Vue
<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("orderAgentList");
|
|
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;
|
|
// });
|
|
// }
|
|
|
|
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: "provider-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('orderAgentListFilter', {
|
|
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("orderAgentList");
|
|
orders.value = store.getters.getPatientOrderList;
|
|
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="primary" size="56"
|
|
class="white--text text-h5 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)) !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>
|