279 lines
9.2 KiB
Vue
279 lines
9.2 KiB
Vue
<script setup>
|
|
import FlightFormSteps from "components/flights/form/FlightFormSteps.vue";
|
|
import {computed, onMounted, ref, watch} from "vue";
|
|
import {useRoute} from "vue-router";
|
|
import {useFlightsStore} from "stores/Flights";
|
|
import FlightFormBasic from "components/flights/form/FlightFormBasic.vue";
|
|
import {useAircraftsStore} from "stores/Aircrafts";
|
|
import {usePointsOfInterestStore} from "stores/PointsOfInterest";
|
|
import {useAirportsStore} from "stores/Airports";
|
|
import {useQuasar} from "quasar";
|
|
import {storeToRefs} from "pinia";
|
|
import {flightName} from "src/composables/flight";
|
|
import FlightFormCopilot from "components/flights/form/FlightFormCopilot.vue";
|
|
import {useCopilotsStore} from "stores/Copilots";
|
|
import FlightFormNotes from "components/flights/form/FlightFormNotes.vue";
|
|
import FlightFormPhotos from "components/flights/form/FlightFormPhotos.vue";
|
|
import {usePhotosStore} from "stores/Photos";
|
|
import PhotosEditorList from "components/photos/PhotosEditorList.vue";
|
|
import {errorObject, successObject, warningObject} from "src/composables/dialog";
|
|
import PhotoFormDialog from "components/photos/PhotoFormDialog.vue";
|
|
import FlightFormTrack from "components/flights/form/FlightFormTrack.vue";
|
|
import FlightEditSkeleton from "components/flights/skeletons/FlightEditSkeleton.vue";
|
|
import BatchPhotoFormDialog from "components/photos/BatchPhotoFormDialog.vue";
|
|
import {useAppStore} from "stores/App";
|
|
import {useEventsStore} from "stores/Events";
|
|
import FlightEditCopilotSkeleton from "components/flights/skeletons/FlightEditCopilotSkeleton.vue";
|
|
|
|
const $q = useQuasar();
|
|
const $route = useRoute();
|
|
const flightId = parseInt($route.params.id);
|
|
|
|
const {aircrafts} = storeToRefs(useAircraftsStore());
|
|
const {pointsOfInterest} = storeToRefs(usePointsOfInterestStore());
|
|
const {airports} = storeToRefs(useAirportsStore());
|
|
const {copilots} = storeToRefs(useCopilotsStore());
|
|
const {events} = storeToRefs(useEventsStore());
|
|
const {flight, isQueryRunning, isMutationRunning} = storeToRefs(useFlightsStore());
|
|
|
|
const formStep = ref("basic");
|
|
watch(() => $route.params.step, (val) => formStep.value = val);
|
|
|
|
onMounted(() => {
|
|
formStep.value = $route.params.step || "basic";
|
|
|
|
useAircraftsStore().fetch();
|
|
usePointsOfInterestStore().fetch();
|
|
useAirportsStore().fetch();
|
|
useCopilotsStore().fetch();
|
|
useEventsStore().fetch();
|
|
|
|
useFlightsStore().fetchDetail(flightId).then((f) => {
|
|
const flightNameFormatted = flightName(f);
|
|
useAppStore().setPageTitle(`Editace - ${flightNameFormatted}`);
|
|
useAppStore().setBreadcrumbs([
|
|
{to: {name: "myFlights"}, label: "Moje lety"},
|
|
{to: {name: "flightDetail", params: {id: flightId}}, label: flightNameFormatted},
|
|
{label: "Editace"}
|
|
]);
|
|
})
|
|
});
|
|
|
|
const handleFlightEdit = (data) => {
|
|
useFlightsStore().update(flightId, data).then((data) => {
|
|
$q.notify(successObject("Úspěšně upraveno"));
|
|
})
|
|
}
|
|
|
|
const isRequestPending = storeToRefs(useFlightsStore()).isMutationRunning;
|
|
const uploadInProgress = ref(false);
|
|
|
|
const handlePhotosUpload = (data) => {
|
|
const promises = [];
|
|
uploadInProgress.value = true;
|
|
for (const photo of data.photos) {
|
|
promises.push(usePhotosStore().upload(flightId, {photo}).then((data) => {
|
|
flight.value.photos.push(data);
|
|
photo._uploaded = true;
|
|
}));
|
|
}
|
|
|
|
Promise.allSettled(promises).then((values) => {
|
|
const success = values.filter(v => v.status === 'fulfilled');
|
|
const failed = values.filter(v => v.status === 'rejected'); // TODO: nebo vse - success?
|
|
|
|
if (success.length > 0)
|
|
$q.notify(successObject(`Úspěšně nahráno ${success.length} fotek`));
|
|
|
|
if (failed.length > 0)
|
|
$q.notify(warningObject(`Nepovedlo se nahrát ${failed.length} fotek. Zde jsou důvody: ${failed.map(f => f.reason)}`));
|
|
|
|
data.photos = [];
|
|
}).finally(() => uploadInProgress.value = false);
|
|
}
|
|
|
|
const _findPhotoIndex = (photoId) => flight.value.photos.findIndex((object) => object.id === photoId);
|
|
const deletePhoto = (id) => {
|
|
usePhotosStore().destroy(id).then((data) => {
|
|
flight.value.photos.splice(_findPhotoIndex(data.id), 1);
|
|
});
|
|
}
|
|
|
|
const setTitlePhoto = (photo) => {
|
|
useFlightsStore().update(flight.value.id, {titlePhotoId: photo.id}).then(f => {
|
|
$q.notify(successObject("Úvodní fotografie úspěšně nastavena"));
|
|
});
|
|
}
|
|
|
|
const rotatePhotos = ({photos, direction}) => {
|
|
const promises = [];
|
|
for (const photo of photos) {
|
|
promises.push(usePhotosStore().changeOrientation(photo.id, direction).then(data => {
|
|
const idx = _findPhotoIndex(data.id);
|
|
data.thumbnailUrl += "?t=" + (new Date()).getTime();
|
|
flight.value.photos[idx] = data;
|
|
}));
|
|
}
|
|
|
|
let success = 0;
|
|
Promise.allSettled(promises).then(result => {
|
|
for (const item of result) {
|
|
if (item.status === 'fulfilled') success++;
|
|
}
|
|
const error = result.length - success;
|
|
|
|
if (success > 0) $q.notify(successObject("Otočeno " + success + " fotografií"));
|
|
if (error > 0) $q.notify(errorObject(`Nepovedlo se otočit ${error} fotografi`))
|
|
});
|
|
}
|
|
|
|
const updateSinglePhoto = (id, formData) => {
|
|
formData.copilots = formData.copilots.map(c => ({id: c.id, name: c.name}));
|
|
|
|
return usePhotosStore().update(id, formData).then((data) => {
|
|
const idx = _findPhotoIndex(data.id);
|
|
flight.value.photos[idx] = data;
|
|
usePointsOfInterestStore().fetch();
|
|
});
|
|
}
|
|
|
|
const handlePhotoUpdate = (flightId, formData) => {
|
|
const photoId = formData.id;
|
|
delete formData.id;
|
|
|
|
return updateSinglePhoto(photoId, formData);
|
|
};
|
|
|
|
const openEdit = ref(false);
|
|
const editPhoto = ref(null);
|
|
|
|
const openPhotoEdit = (photo) => {
|
|
openEdit.value = true;
|
|
editPhoto.value = photo;
|
|
};
|
|
|
|
|
|
// batch edit
|
|
const openBatchEdit = ref(false);
|
|
const editBatchPhotos = ref([]);
|
|
const openBatchPhotoForm = (photos) => {
|
|
openBatchEdit.value = true;
|
|
editBatchPhotos.value = photos;
|
|
}
|
|
|
|
const handleBatchPhotoEdit = async (flightId, data) => {
|
|
const photoIds = data.photoIds;
|
|
delete data.photoIds;
|
|
delete data.id;
|
|
|
|
if (data.pointOfInterest && !data.pointOfInterest.id) {
|
|
const poiData = {
|
|
...data.pointOfInterest,
|
|
isPublic: false,
|
|
description: "",
|
|
}
|
|
await usePointsOfInterestStore().create(poiData).then(poi => data.pointOfInterest = {id: poi.id, name: poi.name});
|
|
}
|
|
|
|
const promises = [];
|
|
for (const id of photoIds) {
|
|
const updateData = {
|
|
isAircraft: data.isAircraft,
|
|
copilots: data.copilots.map(c => ({id: c.id, name: c.name}))
|
|
};
|
|
if (data.name !== '') updateData.name = data.name;
|
|
if (data.description !== '') updateData.description = data.description;
|
|
if (data.pointOfInterest) updateData.pointOfInterest = data.pointOfInterest;
|
|
|
|
promises.push(updateSinglePhoto(id, data));
|
|
}
|
|
|
|
let success = 0;
|
|
Promise.allSettled(promises).then(result => {
|
|
for (const item of result) {
|
|
if (item.status === 'fulfilled') success++;
|
|
}
|
|
const error = result.length - success;
|
|
|
|
if (success > 0) $q.notify(successObject("Upraveno " + success + " fotografií"));
|
|
if (error > 0) $q.notify(errorObject(`Nepovedlo se uložit ${error} fotografií`))
|
|
});
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<FlightFormSteps :router-edit-mode="true" :flightId="flightId"/>
|
|
|
|
<q-tab-panels v-model="formStep">
|
|
<q-tab-panel name="basic">
|
|
<FlightEditSkeleton :step="formStep" v-if="isQueryRunning || !flight"/>
|
|
<FlightFormBasic
|
|
@save="handleFlightEdit"
|
|
:flight="flight"
|
|
:locations="pointsOfInterest"
|
|
:airports="airports"
|
|
:aircrafts="aircrafts"
|
|
/>
|
|
</q-tab-panel>
|
|
|
|
<q-tab-panel name="copilot">
|
|
<FlightEditCopilotSkeleton :step="formStep" v-if="isQueryRunning || !flight"/>
|
|
<FlightFormCopilot @save="handleFlightEdit" :flight="flight" :copilots="copilots"/>
|
|
</q-tab-panel>
|
|
|
|
<q-tab-panel name="track">
|
|
<FlightEditSkeleton :step="formStep" v-if="isQueryRunning || !flight"/>
|
|
<FlightFormTrack
|
|
v-if="pointsOfInterest"
|
|
:flight="flight"
|
|
:locations="pointsOfInterest"
|
|
:airports="airports"
|
|
@save="handleFlightEdit"
|
|
/>
|
|
</q-tab-panel>
|
|
|
|
<q-tab-panel name="photos">
|
|
<FlightFormPhotos
|
|
:flight="flight"
|
|
:is-request-pending="isRequestPending || uploadInProgress"
|
|
@save="handlePhotosUpload"
|
|
/>
|
|
|
|
<PhotosEditorList
|
|
:title-photo="flight.titlePhoto"
|
|
:photos="flight.photos"
|
|
:show-actions="true"
|
|
@edit="openPhotoEdit"
|
|
@batch-edit="openBatchPhotoForm"
|
|
@batch-rotate="rotatePhotos"
|
|
@delete="deletePhoto"
|
|
@set-title-photo="setTitlePhoto"
|
|
/>
|
|
|
|
<!-- Dialogs for photo edit -->
|
|
<PhotoFormDialog
|
|
v-model="openEdit"
|
|
:copilots="flight.copilots"
|
|
:aircrafts="aircrafts"
|
|
:locations="pointsOfInterest"
|
|
:photo="editPhoto"
|
|
@save="handlePhotoUpdate(flightId, $event)"
|
|
/>
|
|
|
|
<BatchPhotoFormDialog
|
|
v-model="openBatchEdit"
|
|
:locations="pointsOfInterest"
|
|
:photos="editBatchPhotos"
|
|
:copilots="flight.copilots"
|
|
:aircrafts="aircrafts"
|
|
@save="handleBatchPhotoEdit(flightId, $event)"
|
|
@delete="deletePhoto"
|
|
/>
|
|
</q-tab-panel>
|
|
|
|
<q-tab-panel name="notes">
|
|
<FlightFormNotes @save="handleFlightEdit" :events="events" :flight="flight"/>
|
|
</q-tab-panel>
|
|
</q-tab-panels>
|
|
</template>
|