Prace s mapu, zobrazeni GPX, editace kopilota, skeletony

This commit is contained in:
Michal Kváček
2023-08-07 23:22:28 +02:00
parent 09d2f25523
commit c4603cf090
55 changed files with 891 additions and 408 deletions
-8
View File
@@ -4,14 +4,6 @@ import {onMounted} from "vue";
import {useFlightsStore} from "stores/Flights";
const loggedUser = useUserStore().user
onMounted(() => {
useUserStore().setupCredentials();
setInterval(() => {
useFlightsStore().fetch();
}, 1000);
})
</script>
<template>
@@ -8,6 +8,8 @@ import {confirmObject, successObject} from "src/composables/dialog";
import {loading, aircraft, fetchAircraft} from "src/composables/objects/aircraft";
import FAB from "components/FAB.vue";
import {useAppStore} from "stores/App";
import FlightsList from "components/flights/FlightsList.vue";
import {copilot} from "src/composables/objects/copilot";
const aircraftId = parseInt(useRoute().params.id);
onMounted(() => {
@@ -40,30 +42,31 @@ watch(aircraft, (newVal) => {
<template v-else>
<q-card>
<q-img :src="aircraft.photoUrl"/>
<q-img :src="aircraft.photoUrl"/>
<q-card-section>
<q-list>
<q-item>
Výrobce: {{ aircraft.manufacturer }}
</q-item>
<q-item>
Výrobce: {{ aircraft.manufacturer }}
</q-item>
<q-item>
Model: {{ aircraft.model }}
</q-item>
</q-list>
<q-item>
Model: {{ aircraft.model }}
</q-item>
</q-list>
<h3>Lety</h3>
<q-banner v-if="aircraft.flights.length === 0" class="bg-light-blue text-white">
Zatím žádné lety.
</q-banner>
<FlightsList :items="aircraft.flights" v-else/>
</q-card-section>
<q-card-actions align="right">
<q-btn @click="confirmDelete(aircraft.id)" icon="delete" text-color="red" rounded flat/>
<q-btn @click="confirmDelete(aircraft.id)" icon="delete" text-color="red" rounded flat/>
</q-card-actions>
</q-card>
<!-- <h2>Limits</h2>-->
<!-- Wind: {{ aircraft.limits.wind.frontKt }}/{{ aircraft.limits.wind.sideKt }} kt-->
<!-- Neprekrocitelna rychlost: {{ aircraft.limits.speed.vmax }}-->
@@ -12,7 +12,7 @@ onMounted(() => {
fetchCopilot(copilotId).then(() => {
useAppStore().setPageTitle(copilot.value.name);
useAppStore().setBreadcrumbs([
{to: {name: "pilots"}, label: "Kopiloti"},
{to: {name: "copilots"}, label: "Kopiloti"},
{label: copilot.value.name}
])
});
@@ -29,6 +29,6 @@ onMounted(() => {
</q-banner>
<FlightsList :items="copilot.flights" v-else/>
<FAB mode="edit" :router-link="{name: 'pilotEdit', params: {id: copilotId}}"/>
<FAB mode="edit" :router-link="{name: 'copilotEdit', params: {id: copilotId}}"/>
</template>
</template>
+70 -21
View File
@@ -1,21 +1,26 @@
<script setup>
import {useRoute, useRouter} from "vue-router";
import {computed, onMounted, watch} from "vue";
import {computed, onMounted, ref, watch} from "vue";
import FlightDetailSkeleton from "components/flights/FlightDetailSkeleton.vue";
import {fetchFlight, flight, flightName, handleDeleteFlight, loading} from "src/composables/objects/flight";
import {useAppStore} from "stores/App";
import PhotosList from "components/photos/PhotosList.vue";
import FlightBasicInfo from "components/flights/FlightBasicInfo.vue";
import {useQuasar} from "quasar";
import {successObject} from "src/composables/dialog";
import MapWithMarkers from "components/MapWithMarkers.vue";
import MapWithMarkers from "components/map/MapWithMarkers.vue";
import FAB from "components/FAB.vue";
import PhotoDetail from "components/photos/PhotoDetail.vue";
const $q = useQuasar();
const $router = useRouter();
const displayedPhoto = ref(null);
onMounted(() => {
fetchFlight(useRoute().params.id)
fetchFlight(useRoute().params.id).then(() => {
if (flight.value.photos.length === 0) return;
displayedPhoto.value = flight.value.coverPhoto.url || flight.value.photos[0].url;
})
})
watch(flight, (newVal) => {
@@ -36,35 +41,77 @@ const handleDeleteAndRedirect = () => {
})
}
const flightPath = computed(() => {
const trackWithGPS = computed(() => {
return flight.value.track.filter(poi => poi.pointOfInterest.gpsLatitude && poi.pointOfInterest.gpsLongitude)
});
const pointsOfInterests = computed(() => {
const path = [{
lat: flight.value.takeoffAirport.gpsLatitude,
lng: flight.value.takeoffAirport.gpsLongitude
label: flight.value.takeoffAirport.name,
position:
{
lat: flight.value.takeoffAirport.gpsLatitude,
lng: flight.value.takeoffAirport.gpsLongitude
}
}];
for (const poi of flight.value.track) {
path.push({lat: poi.pointOfInterest.gpsLatitude, lng: poi.pointOfInterest.gpsLongitude});
for (const poi of trackWithGPS.value) {
path.push({
label: poi.pointOfInterest.name,
position: {lat: poi.pointOfInterest.gpsLatitude, lng: poi.pointOfInterest.gpsLongitude}
});
}
path.push({
lat: flight.value.landingAirport.gpsLatitude,
lng: flight.value.landingAirport.gpsLongitude
label: flight.value.takeoffAirport.name,
position: {
lat: flight.value.landingAirport.gpsLatitude,
lng: flight.value.landingAirport.gpsLongitude
}
});
return path;
});
const openPhotoDetail = ref(false);
const photoData = ref(null);
const openDetail = ({photo, idx, allPhotos}) => {
openPhotoDetail.value = true;
photoData.value = {photo, idx, allPhotos}
}
const mapPhotos = computed(() => {
return flight.value.photos.filter(photo => photo.gpsLatitude && photo.gpsLongitude)
});
const displayMap = computed(() => {
return (
!!flight.value.gpsTrackUrl ||
mapPhotos.value.length > 0 ||
trackWithGPS.value.length > 0 ||
flight.value.landingAirport.id !== flight.value.takeoffAirport.id
)
});
</script>
<template>
<FlightDetailSkeleton v-if="loading || !flight"/>
<template v-else>
<q-card class="q-mx-md-lg q-my-md">
<q-parallax
:src="flight.coverPhoto.url"
v-if="flight.coverPhoto"
:height="450"
/>
<q-card class="q-mx-md q-my-md">
<q-carousel
v-if="flight.photos.length > 0"
animated
v-model="displayedPhoto"
arrows
swipeable
height="auto"
infinite
>
<q-carousel-slide style="padding: 0" v-for="(photo, idx) in flight.photos" :key="photo.url" :name="photo.url">
<q-img :src="photo.url" @click="openDetail({photo, idx, allPhotos: flight.photos})"/>
</q-carousel-slide>
</q-carousel>
<q-card-section>
<FlightBasicInfo :flight="flight"/>
@@ -72,12 +119,12 @@ const flightPath = computed(() => {
<article class="q-my-lg" v-html="flight.description"/>
<MapWithMarkers
v-if="displayMap"
style="width: 100%; height: 500px;"
:show-as-track="true"
:markers="flightPath"
:gpx-track-url="flight.gpxTrackUrl"
:markers="pointsOfInterests"
:photos="mapPhotos"
/>
<PhotosList :photos="flight.photos" :show-actions="false"/>
</q-card-section>
<q-card-actions align="right">
@@ -85,6 +132,8 @@ const flightPath = computed(() => {
</q-card-actions>
</q-card>
<PhotoDetail v-if="photoData" v-model="openPhotoDetail" :photo="photoData.photo" :photos="photoData.allPhotos" :opened-index="photoData.idx"/>
<FAB :router-link="{name: 'flightEdit', params:{id: flight.id}}" mode="edit"/>
</template>
@@ -6,7 +6,7 @@ import FAB from "components/FAB.vue";
import {fetchPoi, loading, poi} from "src/composables/objects/poi";
import PhotosList from "components/photos/PhotosList.vue";
import {useAppStore} from "stores/App";
import MapWithMarker from "components/MapWithMarkers.vue";
import EmbeddedMap from "components/map/EmbeddedMap.vue";
onMounted(() => {
fetchPoi(useRoute().params.id).then(() => {
@@ -25,11 +25,11 @@ onMounted(() => {
<template v-else>
<MapWithMarker
v-if="poi.gpsLongitude && poi.gpsLatitude"
style="width: 100%; height: 500px;"
<EmbeddedMap
v-if="poi.gpsLatitude && poi.gpsLongitude"
:gps-latitude="poi.gpsLatitude"
:gps-longitude="poi.gpsLongitude"
:zoom="13"
:markers="[{lat: poi.gpsLatitude, lng: poi.gpsLongitude}]"
/>
<PhotosList
+18 -13
View File
@@ -7,6 +7,8 @@ import {onMounted, ref} from "vue";
import AircraftEditSkeleton from "components/aircrafts/AircraftEditSkeleton.vue";
import {successObject} from "src/composables/dialog";
import {aircraft, fetchAircraft, loading} from "src/composables/objects/aircraft";
import {useAppStore} from "stores/App";
import {copilot} from "src/composables/objects/copilot";
let isRequestPending = ref(false);
@@ -16,29 +18,32 @@ const aircraftId = parseInt(useRoute().params.id);
const handleSave = (formData) => {
isRequestPending.value = true;
useAircraftsStore().update(aircraftId, formData).then(() => {
$q.notify(successObject("Letadlo uspesne upraveno"));
$q.notify(successObject("Letadlo úspěšně upraveno"));
useRouter().push({name: "aircrafts"});
}).finally(() => {
isRequestPending.value = true;
isRequestPending.value = false;
})
}
onMounted(() => {
fetchAircraft(aircraftId)
fetchAircraft(aircraftId).then(() => {
useAppStore().setPageTitle(`${aircraft.value.callSign} - editace`);
useAppStore().setBreadcrumbs([
{to: {name: "aircraft"}, label: "Letadla"},
{to: {name: "aircraftDetail", params: {id: aircraftId}}, label: aircraft.value.callSign},
{label: "Editace"}
])
})
});
</script>
<template>
<AircraftEditSkeleton v-if="loading"/>
<template v-else>
<h1>Editace letadla {{ aircraft.callSign }}</h1>
<AircraftForm
@save="handleSave"
:default-values="aircraft"
:is-request-pending="isRequestPending"
/>
</template>
<AircraftForm
v-else
@save="handleSave"
:default-values="aircraft"
:is-request-pending="isRequestPending"
/>
</template>
+18 -7
View File
@@ -3,8 +3,11 @@ import {onMounted} from "vue";
import {loading, copilot, fetchCopilot} from "src/composables/objects/copilot";
import {useAppStore} from "stores/App";
import CopilotEditSkeleton from "components/copilots/CopilotEditSkeleton.vue";
import CopilotEditForm from "components/copilots/CopilotEditForm.vue";
import CopilotEditForm from "components/copilots/CopilotForm.vue";
import {useRoute} from "vue-router";
import {useCopilotsStore} from "stores/Copilots";
import {successObject} from "src/composables/dialog";
import {useQuasar} from "quasar";
const copilotId = parseInt(useRoute().params.id);
@@ -12,19 +15,27 @@ onMounted(() => {
fetchCopilot(copilotId).then(() => {
useAppStore().setPageTitle(`${copilot.value.name} - editace`);
useAppStore().setBreadcrumbs([
{to: {name: "pilots"}, label: "Kopiloti"},
{to: {name: "pilotDetail", params: {id: copilotId}}, label: copilot.value.name},
{to: {name: "copilots"}, label: "Kopiloti"},
{to: {name: "copilotDetail", params: {id: copilotId}}, label: copilot.value.name},
{label: "Editace"}
])
})
});
const $q = useQuasar();
const handleSave = (formData) => {
useCopilotsStore().update(copilotId, formData).then(() => {
$q.notify(successObject("Kopilot úspěšně upraven"));
// useRouter().push({name: "copilots"});
// TODO: ^^ hazi error
})
}
</script>
<template>
<CopilotEditSkeleton v-if="loading" />
<CopilotEditForm v-else />
<CopilotEditSkeleton v-if="loading"/>
<CopilotEditForm v-else :copilot="copilot" @save="handleSave"/>
</template>
+1 -1
View File
@@ -17,7 +17,7 @@ import FlightFormPhotos from "components/flights/form/FlightFormPhotos.vue";
import {usePhotosStore} from "stores/Photo";
import PhotosList from "components/photos/PhotosList.vue";
import {confirmObject, successObject} from "src/composables/dialog";
import PhotoForm from "components/flights/PhotoForm.vue";
import PhotoForm from "components/photos/PhotoForm.vue";
import FlightFormTrack from "components/flights/form/FlightFormTrack.vue";
import {fetchFlight, flight, loading} from "src/composables/objects/flight";
import FlightEditSkeleton from "components/flights/FlightEditSkeleton.vue";
+11 -1
View File
@@ -3,7 +3,8 @@ import AircraftForm from "components/aircrafts/AircraftForm.vue";
import {useAircraftsStore} from "stores/Aircrafts";
import {useRouter} from "vue-router";
import {useQuasar} from "quasar";
import {ref} from "vue";
import {onMounted, ref} from "vue";
import {useAppStore} from "stores/App";
let isRequestPending = ref(false);
const $q = useQuasar();
@@ -18,6 +19,15 @@ const handleSave = (formData) => {
isRequestPending.value = true;
})
}
onMounted(() => {
useAppStore().setPageTitle("Nové letadlo");
useAppStore().setBreadcrumbs([
{to: {name: "aircrafts"}, label: "Letadla"},
{label: "Nové"}
]);
});
</script>
<template>
+18 -9
View File
@@ -1,25 +1,34 @@
<script setup>
import AircraftForm from "components/aircrafts/AircraftForm.vue";
import {useAircraftsStore} from "stores/Aircrafts";
import {useRouter} from "vue-router";
import {useQuasar} from "quasar";
import {ref} from "vue";
import {onMounted, ref} from "vue";
import {successObject} from "src/composables/dialog";
import {useCopilotsStore} from "stores/Copilots";
import CopilotForm from "components/copilots/CopilotForm.vue";
import {useAppStore} from "stores/App";
let isRequestPending = ref(false);
const $q = useQuasar();
const router = useRouter();
const handleSave = (formData) => {
isRequestPending.value = true;
useAircraftsStore().create(formData).then(() => {
$q.notify({message: "Letadlo úspěšně vytvořeno", position: "top", color: "positive"});
router.push({name: "aircrafts"});
useCopilotsStore().create(formData).then(() => {
$q.notify(successObject("Kopilot úspěšně vytvořen"));
router.push({name: "copilots"});
}).finally(() => {
isRequestPending.value = true;
isRequestPending.value = false;
})
}
onMounted(() => {
useAppStore().setPageTitle("Nový spolucestující");
useAppStore().setBreadcrumbs([
{to: {name: "copilots"}, label: "Spolucestující"},
{label: "Nový"}
]);
});
</script>
<template>
<AircraftForm @save="handleSave" :is-request-pending="isRequestPending"/>
<CopilotForm @save="handleSave" :is-request-pending="isRequestPending"/>
</template>
+15 -25
View File
@@ -5,49 +5,39 @@ import {useFlightsStore} from "stores/Flights";
import {useRouter} from "vue-router";
import {usePointsOfInterestStore} from "stores/PointsOfInterest";
import {storeToRefs} from "pinia";
import {useMeta} from "quasar";
import {useAirportsStore} from "stores/Airports";
import FlightFormSteps from "components/flights/form/FlightFormSteps.vue";
import FlightFormBasic from "components/flights/form/FlightFormBasic.vue";
import {useAppStore} from "stores/App";
const aircraftsStore = useAircraftsStore();
const poiStore = usePointsOfInterestStore();
const flightsStore = useFlightsStore();
const airportStore = useAirportsStore();
useMeta({title: "New flight"});
onMounted(() => {
aircraftsStore.fetch();
poiStore.fetch();
flightsStore.fetch();
airportStore.fetch();
useAircraftsStore().fetch();
usePointsOfInterestStore().fetch();
useFlightsStore().fetch();
useAirportsStore().fetch();
useAppStore().setPageTitle("Nový let");
useAppStore().setBreadcrumbs([
{to: {name: "myFlights"}, label: "Moje lety"},
{label: "Nový"}
]);
});
const aircrafts = storeToRefs(aircraftsStore).items;
const locations = storeToRefs(poiStore).items;
const airports = storeToRefs(airportStore).items;
const aircrafts = storeToRefs(useAircraftsStore()).items;
const locations = storeToRefs(usePointsOfInterestStore()).items;
const airports = storeToRefs(useAirportsStore()).items;
const router = useRouter();
const handleSave = (formData) => {
flightsStore.create(formData).then((flight) => {
useFlightsStore().create(formData).then((flight) => {
router.push({name: "flightEdit", params: {id: flight.id, step: "copilot"}});
});
}
const formStep = ref("basic")
</script>
<template>
<q-breadcrumbs>
<q-breadcrumbs-el :to="{name: 'myFlights'}">Moje lety</q-breadcrumbs-el>
<q-breadcrumbs-el>Nový</q-breadcrumbs-el>
</q-breadcrumbs>
<!-- {{ formData }}-->
<FlightFormSteps :disabled="['copilot', 'track', 'photos', 'notes']"/>
<FlightFormBasic @save="handleSave" :aircrafts="aircrafts" :airports="airports" :locations="locations" />