first commit
This commit is contained in:
55
resources/js/pages/apps/logistics/dashboard.vue
Normal file
55
resources/js/pages/apps/logistics/dashboard.vue
Normal file
@@ -0,0 +1,55 @@
|
||||
<script setup>
|
||||
import LogisticsCardStatistics from '@/views/apps/logistics/LogisticsCardStatistics.vue'
|
||||
import LogisticsDeliveryExpectations from '@/views/apps/logistics/LogisticsDeliveryExpectations.vue'
|
||||
import LogisticsDeliveryPerformance from '@/views/apps/logistics/LogisticsDeliveryPerformance.vue'
|
||||
import LogisticsOrderByCountries from '@/views/apps/logistics/LogisticsOrderByCountries.vue'
|
||||
import LogisticsOverviewTable from '@/views/apps/logistics/LogisticsOverviewTable.vue'
|
||||
import LogisticsShipmentStatistics from '@/views/apps/logistics/LogisticsShipmentStatistics.vue'
|
||||
import LogisticsVehicleOverview from '@/views/apps/logistics/LogisticsVehicleOverview.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VRow class="match-height">
|
||||
<VCol cols="12">
|
||||
<LogisticsCardStatistics />
|
||||
</VCol>
|
||||
<VCol
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<LogisticsVehicleOverview />
|
||||
</VCol>
|
||||
|
||||
<VCol
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<LogisticsShipmentStatistics />
|
||||
</VCol>
|
||||
|
||||
<VCol
|
||||
cols="12"
|
||||
md="4"
|
||||
>
|
||||
<LogisticsDeliveryPerformance />
|
||||
</VCol>
|
||||
|
||||
<VCol
|
||||
cols="12"
|
||||
md="4"
|
||||
>
|
||||
<LogisticsDeliveryExpectations />
|
||||
</VCol>
|
||||
|
||||
<VCol
|
||||
cols="12"
|
||||
md="4"
|
||||
>
|
||||
<LogisticsOrderByCountries />
|
||||
</VCol>
|
||||
|
||||
<VCol cols="12">
|
||||
<LogisticsOverviewTable />
|
||||
</VCol>
|
||||
</VRow>
|
||||
</template>
|
378
resources/js/pages/apps/logistics/fleet.vue
Normal file
378
resources/js/pages/apps/logistics/fleet.vue
Normal file
@@ -0,0 +1,378 @@
|
||||
// ❗ WARNING please use your access token from mapbox.com
|
||||
<script setup>
|
||||
import mapboxgl from 'mapbox-gl'
|
||||
import {
|
||||
onMounted,
|
||||
ref,
|
||||
} from 'vue'
|
||||
import { PerfectScrollbar } from 'vue3-perfect-scrollbar'
|
||||
import { useDisplay } from 'vuetify'
|
||||
import fleetImg from '@images/misc/fleet-car.png'
|
||||
|
||||
const { isLeftSidebarOpen } = useResponsiveLeftSidebar()
|
||||
const accessToken = 'pk.eyJ1Ijoic29jaWFsZXhwbG9yZXIiLCJhIjoiREFQbXBISSJ9.dwFTwfSaWsHvktHrRtpydQ'
|
||||
const map = ref()
|
||||
const vuetifyDisplay = useDisplay()
|
||||
|
||||
definePage({ meta: { layoutWrapperClasses: 'layout-content-height-fixed' } })
|
||||
|
||||
const carImgs = ref([
|
||||
fleetImg,
|
||||
fleetImg,
|
||||
fleetImg,
|
||||
fleetImg,
|
||||
])
|
||||
|
||||
const refCars = ref([])
|
||||
|
||||
const showPanel = ref([
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
])
|
||||
|
||||
const geojson = {
|
||||
type: 'FeatureCollection',
|
||||
features: [
|
||||
{
|
||||
type: 'Feature',
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [
|
||||
-73.999024,
|
||||
40.75249842,
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'Feature',
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [
|
||||
-74.03,
|
||||
40.75699842,
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'Feature',
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [
|
||||
-73.967524,
|
||||
40.7599842,
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'Feature',
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [
|
||||
-74.0325,
|
||||
40.742992,
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const activeIndex = ref(0)
|
||||
|
||||
onMounted(() => {
|
||||
mapboxgl.accessToken = accessToken
|
||||
map.value = new mapboxgl.Map({
|
||||
container: 'mapContainer',
|
||||
style: 'mapbox://styles/mapbox/light-v9',
|
||||
center: [
|
||||
-73.999024,
|
||||
40.75249842,
|
||||
],
|
||||
zoom: 12.25,
|
||||
})
|
||||
for (let index = 0; index < geojson.features.length; index++)
|
||||
new mapboxgl.Marker({ element: refCars.value[index] }).setLngLat(geojson.features[index].geometry.coordinates).addTo(map.value)
|
||||
refCars.value[activeIndex.value].classList.add('marker-focus')
|
||||
})
|
||||
|
||||
const vehicleTrackingData = [
|
||||
{
|
||||
name: 'VOL-342808',
|
||||
location: 'Chelsea, NY, USA',
|
||||
progress: 88,
|
||||
driverName: 'Veronica Herman',
|
||||
},
|
||||
{
|
||||
name: 'VOL-954784',
|
||||
location: 'Lincoln Harbor, NY, USA',
|
||||
progress: 90,
|
||||
driverName: 'Myrtle Ullrich',
|
||||
},
|
||||
{
|
||||
name: 'VOL-342808',
|
||||
location: 'Midtown East, NY, USA',
|
||||
progress: 60,
|
||||
driverName: 'Barry Schowalter',
|
||||
},
|
||||
{
|
||||
name: 'VOL-343908',
|
||||
location: 'Hoboken, NY, USA',
|
||||
progress: 28,
|
||||
driverName: 'Helen Jacobs',
|
||||
},
|
||||
]
|
||||
|
||||
const flyToLocation = (geolocation, index) => {
|
||||
activeIndex.value = index
|
||||
showPanel.value.fill(false)
|
||||
showPanel.value[index] = !showPanel.value[index]
|
||||
if (vuetifyDisplay.mdAndDown.value)
|
||||
isLeftSidebarOpen.value = false
|
||||
map.value.flyTo({
|
||||
center: geolocation,
|
||||
zoom: 16,
|
||||
})
|
||||
}
|
||||
|
||||
watch(activeIndex, () => {
|
||||
refCars.value.forEach((car, index) => {
|
||||
if (index === activeIndex.value)
|
||||
car.classList.add('marker-focus')
|
||||
else
|
||||
car.classList.remove('marker-focus')
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VLayout class="fleet-app-layout">
|
||||
<VNavigationDrawer
|
||||
v-model="isLeftSidebarOpen"
|
||||
width="320"
|
||||
absolute
|
||||
touchless
|
||||
:border="0"
|
||||
location="start"
|
||||
>
|
||||
<VCard
|
||||
class="h-100"
|
||||
flat
|
||||
>
|
||||
<VCardItem>
|
||||
<VCardTitle>
|
||||
Fleet
|
||||
</VCardTitle>
|
||||
|
||||
<template #append>
|
||||
<IconBtn
|
||||
class="d-lg-none"
|
||||
@click="isLeftSidebarOpen = !isLeftSidebarOpen"
|
||||
>
|
||||
<VIcon icon="ri-close-line" />
|
||||
</IconBtn>
|
||||
</template>
|
||||
</VCardItem>
|
||||
|
||||
<!-- 👉 Perfect Scrollbar -->
|
||||
<PerfectScrollbar
|
||||
:options="{ wheelPropagation: false, suppressScrollX: true }"
|
||||
style="block-size: calc(100% - 60px);"
|
||||
>
|
||||
<VCardText class="pt-0">
|
||||
<div
|
||||
v-for="(vehicle, index) in vehicleTrackingData"
|
||||
:key="index"
|
||||
class="mb-6"
|
||||
>
|
||||
<div
|
||||
class="d-flex align-center justify-space-between cursor-pointer"
|
||||
@click="flyToLocation(geojson.features[index].geometry.coordinates, index)"
|
||||
>
|
||||
<div class="d-flex gap-x-4">
|
||||
<VAvatar
|
||||
icon="ri-car-line"
|
||||
variant="tonal"
|
||||
color="secondary"
|
||||
/>
|
||||
<div>
|
||||
<h6 class="text-h6 font-weight-regular">
|
||||
{{ vehicle.name }}
|
||||
</h6>
|
||||
<div class="text-body-1">
|
||||
{{ vehicle.location }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<IconBtn density="comfortable">
|
||||
<VIcon :icon="showPanel[index] ? 'ri-arrow-down-s-line' : $vuetify.locale.isRtl ? 'ri-arrow-left-s-line' : 'ri-arrow-right-s-line'" />
|
||||
</IconBtn>
|
||||
</div>
|
||||
<VExpandTransition mode="out-in">
|
||||
<div v-show="showPanel[index]">
|
||||
<div class="py-4 mb-4">
|
||||
<div class="d-flex justify-space-between text-body-1 mb-1">
|
||||
<span class="text-high-emphasis ">Delivery Process</span>
|
||||
<span>{{ vehicle.progress }}%</span>
|
||||
</div>
|
||||
<VProgressLinear
|
||||
:model-value="vehicle.progress"
|
||||
color="primary"
|
||||
rounded
|
||||
height="6"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<VTimeline
|
||||
side="end"
|
||||
align="start"
|
||||
truncate-line="both"
|
||||
density="compact"
|
||||
class="v-timeline--variant-outlined"
|
||||
>
|
||||
<VTimelineItem
|
||||
icon="ri-checkbox-circle-line"
|
||||
dot-color="rgb(var(--v-theme-surface))"
|
||||
icon-color="success"
|
||||
fill-dot
|
||||
size="20"
|
||||
:elevation="0"
|
||||
>
|
||||
<div class="text-caption text-uppercase text-success">
|
||||
TRACKING NUMBER CREATED
|
||||
</div>
|
||||
<div class="app-timeline-title">
|
||||
{{ vehicle.driverName }}
|
||||
</div>
|
||||
<div class="text-body-2 mb-1">
|
||||
Sep 01, 7:53 AM
|
||||
</div>
|
||||
</VTimelineItem>
|
||||
<VTimelineItem
|
||||
icon="ri-checkbox-circle-line"
|
||||
dot-color="rgb(var(--v-theme-surface))"
|
||||
icon-color="success"
|
||||
fill-dot
|
||||
size="20"
|
||||
:elevation="0"
|
||||
>
|
||||
<div class="text-caption text-uppercase text-success">
|
||||
OUT FOR DELIVERY
|
||||
</div>
|
||||
<div class="app-timeline-title">
|
||||
Veronica Herman
|
||||
</div>
|
||||
<div class="app-timeline-text mb-1">
|
||||
Sep 03, 8:02 AM
|
||||
</div>
|
||||
</VTimelineItem>
|
||||
<VTimelineItem
|
||||
icon="ri-map-pin-line"
|
||||
dot-color="rgb(var(--v-theme-surface))"
|
||||
icon-color="primary"
|
||||
fill-dot
|
||||
size="20"
|
||||
:elevation="0"
|
||||
>
|
||||
<div class="text-caption text-uppercase text-primary">
|
||||
ARRIVED
|
||||
</div>
|
||||
<div class="app-timeline-title">
|
||||
Veronica Herman
|
||||
</div>
|
||||
<div class="app-timeline-text">
|
||||
Sep 04, 8:18 AM
|
||||
</div>
|
||||
</VTimelineItem>
|
||||
</VTimeline>
|
||||
</div>
|
||||
</div>
|
||||
</VExpandTransition>
|
||||
</div>
|
||||
</VCardText>
|
||||
</PerfectScrollbar>
|
||||
</VCard>
|
||||
</VNavigationDrawer>
|
||||
|
||||
<VMain>
|
||||
<div class="h-100">
|
||||
<IconBtn
|
||||
class="d-lg-none navigation-toggle-btn rounded-sm"
|
||||
variant="elevated"
|
||||
@click="isLeftSidebarOpen = true"
|
||||
>
|
||||
<VIcon icon="ri-menu-line" />
|
||||
</IconBtn>
|
||||
|
||||
<!-- 👉 Fleet map -->
|
||||
<div
|
||||
id="mapContainer"
|
||||
class="basemap"
|
||||
/>
|
||||
|
||||
<img
|
||||
v-for="(car, index) in carImgs"
|
||||
:key="index"
|
||||
ref="refCars"
|
||||
:src="car"
|
||||
alt="car Img marker"
|
||||
height="42"
|
||||
width="20"
|
||||
>
|
||||
</div>
|
||||
</VMain>
|
||||
</VLayout>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@use "@styles/variables/vuetify.scss";
|
||||
@use "@core-scss/base/mixins.scss";
|
||||
@import "mapbox-gl/dist/mapbox-gl.css";
|
||||
|
||||
.fleet-app-layout {
|
||||
border-radius: vuetify.$card-border-radius;
|
||||
|
||||
@include mixins.elevation(vuetify.$card-elevation);
|
||||
|
||||
$sel-fleet-app-layout: &;
|
||||
|
||||
@at-root {
|
||||
.skin--bordered {
|
||||
@include mixins.bordered-skin($sel-fleet-app-layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.navigation-toggle-btn{
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
inset-block-start: 1rem;
|
||||
inset-inline-start: 1rem;
|
||||
}
|
||||
|
||||
.navigation-close-btn{
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
inset-block-start: 1rem;
|
||||
inset-inline-end: 1rem;
|
||||
}
|
||||
|
||||
.basemap {
|
||||
block-size: 100%;
|
||||
inline-size: 100%;
|
||||
}
|
||||
|
||||
.marker-focus {
|
||||
filter: drop-shadow(0 0 7px rgb(var(--v-theme-primary)));
|
||||
}
|
||||
|
||||
.mapboxgl-ctrl-bottom-left,
|
||||
.mapboxgl-ctrl-bottom-right {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* stylelint-disable-next-line selector-id-pattern */
|
||||
#mapContainer {
|
||||
block-size: 100vh !important;
|
||||
}
|
||||
</style>
|
Reference in New Issue
Block a user