Zaklad pro zadavani novych letu
This commit is contained in:
@@ -22,7 +22,7 @@ const setStep = (val) => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<q-btn-group flat spread class="q-mt-xl">
|
<q-btn-group flat spread class="q-mt-xl">
|
||||||
<q-btn v-if="previousStep" label="Previous step" @click=setStep(props.previousStep) icon="chevron_left"/>
|
<q-btn v-if="previousStep" label="Zpět" @click=setStep(props.previousStep) icon="chevron_left"/>
|
||||||
<q-btn v-if="nextStep" label="Next step" @click=setStep(props.nextStep) icon-right="chevron_right"/>
|
<q-btn v-if="nextStep" label="Další krok" @click=setStep(props.nextStep) icon-right="chevron_right"/>
|
||||||
</q-btn-group>
|
</q-btn-group>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -10,19 +10,28 @@ const props = defineProps({
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<q-list bordered separator>
|
<q-list bordered separator>
|
||||||
<q-item clickable v-ripple v-for="plane of props.items" :key="plane">
|
<q-item
|
||||||
|
clickable
|
||||||
|
v-ripple
|
||||||
|
:to="{name: 'aircraftDetail', params: {id: aircraft.id }}"
|
||||||
|
v-for="aircraft of props.items" :key="aircraft"
|
||||||
|
>
|
||||||
<q-item-section avatar>
|
<q-item-section avatar>
|
||||||
<q-avatar v-if="plane.photoUrl">
|
<q-avatar v-if="aircraft.photoUrl">
|
||||||
<img :src="plane.photoUrl">
|
<img :src="aircraft.photoUrl" alt=""/>
|
||||||
</q-avatar>
|
</q-avatar>
|
||||||
<q-avatar icon="airplane" v-else></q-avatar>
|
<q-avatar color="teal" text-color="white" icon="flight" v-else/>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<router-link :to="{name: 'aircraftDetail', params: {id: plane.id }}">
|
<q-item-label label>
|
||||||
{{ plane.name }}
|
{{ aircraft.name }}
|
||||||
</router-link>
|
</q-item-label>
|
||||||
|
<q-item-label caption>
|
||||||
{{ plane.type }}
|
{{ aircraft.model }}
|
||||||
|
<template v-if="aircraft.manufacturer">
|
||||||
|
({{ aircraft.manufacturer }})
|
||||||
|
</template>
|
||||||
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
|
|||||||
@@ -0,0 +1,177 @@
|
|||||||
|
<script setup>
|
||||||
|
import LocationInput from "components/inputs/LocationInput.vue";
|
||||||
|
import TabsStepper from "components/TabsStepper.vue";
|
||||||
|
import FormWrapper from "components/FormWrapper.vue";
|
||||||
|
import {computed, reactive, ref} from "vue";
|
||||||
|
import {isRequired} from "src/composables/form/rules";
|
||||||
|
import DatetimePicker from "components/inputs/DatetimePicker.vue";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
aircrafts: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
airports: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
locations: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const formData = reactive({
|
||||||
|
aircraftId: null,
|
||||||
|
takeoffAirportId: null,
|
||||||
|
landingAirportId: null,
|
||||||
|
takeoffDatetime: null,
|
||||||
|
landingDatetime: null,
|
||||||
|
|
||||||
|
// turnPoints: [],
|
||||||
|
copilotId: null,
|
||||||
|
solo: true,
|
||||||
|
withInstructor: false,
|
||||||
|
durationPic: 0,
|
||||||
|
// flightDurationMinutes: 0,
|
||||||
|
landings: 1,
|
||||||
|
|
||||||
|
name: "",
|
||||||
|
description: ""
|
||||||
|
})
|
||||||
|
const rules = {
|
||||||
|
aircraft: [isRequired],
|
||||||
|
takeoffAirport: [],
|
||||||
|
landingAirport: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
const formSteps = ["aircraft", "basic", "copilot", "track", "photos", "notes"]
|
||||||
|
const formStep = ref(formSteps[0]);
|
||||||
|
const basicInfoFilled = computed(() => {
|
||||||
|
return (
|
||||||
|
formData.aircraftId && formData.takeoffAirportId && formData.landingAirportId && formData.takeoffDatetime
|
||||||
|
&& formData.landingDatetime
|
||||||
|
)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
|
||||||
|
<FormWrapper v-model="formData">
|
||||||
|
{{ formData }}
|
||||||
|
|
||||||
|
<q-tabs
|
||||||
|
v-model="formStep"
|
||||||
|
outside-arrows
|
||||||
|
mobile-arrows
|
||||||
|
left-icon="chevron_left"
|
||||||
|
right-icon="chevron_right"
|
||||||
|
active-color="primary"
|
||||||
|
class="q-mb-lg"
|
||||||
|
>
|
||||||
|
<q-tab name="aircraft" icon="flight" label="Aircraft"/>
|
||||||
|
<q-tab name="basic" icon="flight_takeoff" label="Basic"/>
|
||||||
|
<q-tab name="copilot" icon="person_add" label="Copilot"/>
|
||||||
|
<q-tab name="track" icon="route" label="Route"/>
|
||||||
|
<q-tab name="photos" icon="add_a_photo" label="Photos"/>
|
||||||
|
<q-tab name="notes" icon="edit_note" label="Notes"/>
|
||||||
|
</q-tabs>
|
||||||
|
|
||||||
|
<q-tab-panels v-model="formStep" animated>
|
||||||
|
<q-tab-panel name="aircraft">
|
||||||
|
<q-select
|
||||||
|
v-model="formData.aircraftId"
|
||||||
|
option-label="name"
|
||||||
|
option-value="id"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
:rules="rules.aircraft"
|
||||||
|
:options="aircrafts"
|
||||||
|
label="Letadlo"
|
||||||
|
>
|
||||||
|
<template #prepend>
|
||||||
|
<q-icon name="flight"></q-icon>
|
||||||
|
</template>
|
||||||
|
</q-select>
|
||||||
|
|
||||||
|
<TabsStepper v-model="formStep" next-step="basic"/>
|
||||||
|
</q-tab-panel>
|
||||||
|
<q-tab-panel name="basic">
|
||||||
|
<!-- <q-input type="date" v-model="formData.date" label="Date"/>-->
|
||||||
|
|
||||||
|
<DatetimePicker v-model="formData.takeoffDatetime" label="Datum a čas vzletu" />
|
||||||
|
|
||||||
|
<LocationInput label="Take-off location" :rules="rules.takeoffAirport" :locations="airports" v-model="formData.takeoffAirportId" prepend-icon="flight_takeoff"/>
|
||||||
|
<LocationInput label="Landing location" :rules="rules.landingAirport" :locations="airports" v-model="formData.landingAirportId" prepend-icon="flight_land"/>
|
||||||
|
<q-input type="datetime-local" v-model="formData.landingDatetime" label="Landing date & time"/>
|
||||||
|
<q-input v-model="formData.landings" label="Landings" type="number" min="1"/>
|
||||||
|
<q-input v-model="formData.flightDurationMinutes" min=0 label="Flight duration (minutes)" type="number"/>
|
||||||
|
|
||||||
|
<TabsStepper v-model="formStep" previous-step="aircraft" next-step="track"/>
|
||||||
|
</q-tab-panel>
|
||||||
|
|
||||||
|
<q-tab-panel name="track">
|
||||||
|
<!-- <template v-for="i in turnPointsCount" :key="`${i}-${turnPointsCount}`">-->
|
||||||
|
<!-- <LocationInput label="Turn point" :locations="locations" v-model="formData.turnPoints[i-1]">-->
|
||||||
|
<!-- <template #append v-if="i > 1">-->
|
||||||
|
<!-- <q-btn @click="removeTurnPoint(i - 1)">-</q-btn>-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- </LocationInput>-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- <q-btn @click="turnPointsCount++">-->
|
||||||
|
<!-- Add turn point-->
|
||||||
|
<!-- </q-btn>-->
|
||||||
|
|
||||||
|
<q-uploader
|
||||||
|
label="Upload GPX track"
|
||||||
|
url="http://localhost:4444/upload"
|
||||||
|
class="q-mx-sm"
|
||||||
|
style="width: 100%"
|
||||||
|
auto-upload
|
||||||
|
color="secondary"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TabsStepper v-model="formStep" previous-step="basic" next-step="copilot"/>
|
||||||
|
</q-tab-panel>
|
||||||
|
|
||||||
|
<q-tab-panel name="copilot">
|
||||||
|
<q-btn-toggle spread no-caps v-model="formData.solo" toggle-color="primary" :options="[{label: 'Solo flight', value: true}, {label: 'Flight with someone', value: false}]"/>
|
||||||
|
|
||||||
|
<div v-if="!formData.solo">
|
||||||
|
<q-input type="text" v-model="formData.secondPilot" label="Second pilot"/>
|
||||||
|
<q-checkbox v-model="formData.instructor" v-if="formData.secondPilot !== ''" label="Instructor"/>
|
||||||
|
<q-input v-model="formData.picTime" label="PIC time"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<TabsStepper v-model="formStep" previous-step="track" next-step="photos"/>
|
||||||
|
</q-tab-panel>
|
||||||
|
|
||||||
|
<q-tab-panel name="photos">
|
||||||
|
<q-uploader
|
||||||
|
label="Upload photos"
|
||||||
|
url="http://localhost:4444/upload"
|
||||||
|
multiple
|
||||||
|
auto-upload
|
||||||
|
style="width: 100%;"
|
||||||
|
color="secondary"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TabsStepper v-model="formStep" previous-step="copilot" next-step="notes"/>
|
||||||
|
</q-tab-panel>
|
||||||
|
|
||||||
|
<q-tab-panel name="notes">
|
||||||
|
<q-input type="text" v-model="formData.name" label="Nazev" />
|
||||||
|
<q-editor v-model="formData.description" height="60vh" label="Poznámky"/>
|
||||||
|
|
||||||
|
<TabsStepper v-model="formStep" previous-step="photos"/>
|
||||||
|
</q-tab-panel>
|
||||||
|
</q-tab-panels>
|
||||||
|
|
||||||
|
<q-page-sticky expand position="bottom" :offset="[10, 10]" v-if="basicInfoFilled">
|
||||||
|
<div>
|
||||||
|
<q-btn type="submit" size="lg" class="full-width" color="primary" icon="save" label="Save flight"/>
|
||||||
|
</div>
|
||||||
|
</q-page-sticky>
|
||||||
|
</FormWrapper>
|
||||||
|
</template>
|
||||||
+18
-21
@@ -1,18 +1,15 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import {computed, onMounted} from "vue";
|
|
||||||
import {useFlightsStore} from "stores/Flights";
|
|
||||||
import {minutesToHumanReadable} from "src/composables/timeUtils";
|
import {minutesToHumanReadable} from "src/composables/timeUtils";
|
||||||
import {useMeta, useQuasar} from "quasar";
|
import {computed} from "vue";
|
||||||
|
import {useQuasar} from "quasar";
|
||||||
|
|
||||||
useMeta({title: "Moje lety"});
|
const props = defineProps({
|
||||||
|
items: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const flightsStore = useFlightsStore();
|
|
||||||
onMounted(() => {
|
|
||||||
flightsStore.fetch();
|
|
||||||
})
|
|
||||||
const totalFlightTime = computed(() => {
|
|
||||||
return flightsStore.items.reduce((acc, flight) => acc + flight.flightDurationMinutes, 0);
|
|
||||||
})
|
|
||||||
|
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
const deleteFlight = (flightId) => {
|
const deleteFlight = (flightId) => {
|
||||||
@@ -22,28 +19,32 @@ const deleteFlight = (flightId) => {
|
|||||||
cancel: true,
|
cancel: true,
|
||||||
persistent: true
|
persistent: true
|
||||||
}).onOk(() => {
|
}).onOk(() => {
|
||||||
flightsStore.deleteItem(flightId);
|
// flightsStore.deleteItem(flightId);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const totalFlightTime = computed(() => {
|
||||||
|
return props.items.reduce((acc, flight) => acc + flight.flightDurationMinutes, 0);
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-md-4 col-lg-3" v-for="flight of flightsStore.items" :key="flight">
|
<div class="col-12 col-md-4 col-lg-3" v-for="flight of items" :key="flight">
|
||||||
|
|
||||||
<q-card class="q-my-sm q-mx-sm">
|
<q-card class="q-my-sm q-mx-sm">
|
||||||
<q-img :src="flight.titlePhotoUrl" v-if="flight.titlePhotoUrl">
|
<q-img :src="flight.titlePhotoUrl" v-if="flight.titlePhotoUrl">
|
||||||
<div class="absolute-bottom text-center text-h6">
|
<div class="absolute-bottom text-center text-h6">
|
||||||
<router-link :to="{name: 'flightDetail', params:{id: flight.id}}" class="text-white">
|
<router-link :to="{name: 'flightDetail', params:{id: flight.id}}" class="text-white">
|
||||||
{{ flight.takeOffLocation }} - {{ flight.landingLocation }}
|
{{ flight.takeoffAirport.name }} - {{ flight.landingAirport.name }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
</q-img>
|
</q-img>
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<div class="text-h6" v-if="!flight.titlePhotoUrl">
|
<div class="text-h6" v-if="!flight.titlePhotoUrl">
|
||||||
<router-link :to="{name: 'flightDetail', params:{id: flight.id}}">
|
<router-link :to="{name: 'flightDetail', params:{id: flight.id}}">
|
||||||
{{ flight.takeOffLocation }} - {{ flight.landingLocation }}
|
{{ flight.takeoffAirport.name }} - {{ flight.landingAirport.name }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-subtitle2">{{ minutesToHumanReadable(flight.flightDurationMinutes) }}</div>
|
<div class="text-subtitle2">{{ minutesToHumanReadable(flight.flightDurationMinutes) }}</div>
|
||||||
@@ -65,12 +66,8 @@ const deleteFlight = (flightId) => {
|
|||||||
<q-btn flat icon="delete" color="red" @click="deleteFlight(flight.id)"></q-btn>
|
<q-btn flat icon="delete" color="red" @click="deleteFlight(flight.id)"></q-btn>
|
||||||
</q-card-actions>
|
</q-card-actions>
|
||||||
</q-card>
|
</q-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
Total: {{ minutesToHumanReadable(totalFlightTime) }}
|
Total: {{ minutesToHumanReadable(totalFlightTime) }}
|
||||||
|
</div>
|
||||||
<q-page-sticky location="bottom" :offset="[30, 30]">
|
|
||||||
<q-fab icon="add" color="primary" label="Add flight" @click="$router.push({name: 'addFlight'})"/>
|
|
||||||
</q-page-sticky>
|
|
||||||
</template>
|
</template>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<template>
|
||||||
|
Skeleton
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<script setup>
|
||||||
|
import {onMounted, ref, watch} from "vue";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const emit = defineEmits(['update:modelValue']);
|
||||||
|
|
||||||
|
let model = ref("");
|
||||||
|
onMounted(() => {
|
||||||
|
model.value = props.modelValue;
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(model, (newVal) => {
|
||||||
|
emit("update:modelValue", newVal);
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<q-input type="datetime-local" v-model="model" v-bind="$attrs"/>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import gql from "graphql-tag";
|
||||||
|
|
||||||
|
export const FRAGMENT_FLIGHT_FULL_INFO = gql`
|
||||||
|
fragment flightFullInfo on Flight {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
takeoffDatetime
|
||||||
|
landingDatetime
|
||||||
|
|
||||||
|
takeoffAirport {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
|
||||||
|
landingAirport {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
aircraft {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
photoUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
|
||||||
|
// export const FRAGMENT_LOCATION = gql`
|
||||||
|
// fragment airportInfo on Airport {
|
||||||
|
// id
|
||||||
|
// name
|
||||||
|
// }
|
||||||
|
// `;
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import gql from "graphql-tag";
|
||||||
|
import {FRAGMENT_FLIGHT_FULL_INFO} from "src/graphql/fragments/flight";
|
||||||
|
|
||||||
|
export const CREATE_FLIGHT = gql`
|
||||||
|
mutation createFlight($input: CreateFlightInput!) {
|
||||||
|
createFlight(input: $input) {
|
||||||
|
...flightFullInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
${FRAGMENT_FLIGHT_FULL_INFO}
|
||||||
|
`
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import gql from "graphql-tag";
|
||||||
|
|
||||||
|
export const AIRPORTS_LIST = gql`
|
||||||
|
query AirportsList {
|
||||||
|
airports {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
icaoCode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
import gql from "graphql-tag";
|
import gql from "graphql-tag";
|
||||||
|
import {FRAGMENT_FLIGHT_FULL_INFO} from "src/graphql/fragments/flight";
|
||||||
|
|
||||||
export const FLIGHTS_LIST = gql`
|
export const FLIGHTS_LIST = gql`
|
||||||
query FlightsList {
|
query FlightsList {
|
||||||
flights {
|
flights {
|
||||||
name
|
...flightFullInfo
|
||||||
id
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
${FRAGMENT_FLIGHT_FULL_INFO}
|
||||||
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@@ -1,55 +1,33 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import {computed, onMounted, reactive, ref, watch} from "vue";
|
import {onMounted, ref} from "vue";
|
||||||
import LocationInput from "components/inputs/LocationInput.vue";
|
|
||||||
import {useAircraftsStore} from "stores/Aircrafts";
|
import {useAircraftsStore} from "stores/Aircrafts";
|
||||||
import {useFlightsStore} from "stores/Flights";
|
import {useFlightsStore} from "stores/Flights";
|
||||||
import {useRouter} from "vue-router";
|
import {useRouter} from "vue-router";
|
||||||
import {usePointsOfInterestStore} from "stores/PointsOfInterest";
|
import {usePointsOfInterestStore} from "stores/PointsOfInterest";
|
||||||
import {storeToRefs} from "pinia";
|
import {storeToRefs} from "pinia";
|
||||||
import {isRequired} from "src/composables/form/rules";
|
|
||||||
import {useMeta} from "quasar";
|
import {useMeta} from "quasar";
|
||||||
import TabsStepper from "components/TabsStepper.vue";
|
import FlightForm from "components/flights/FlightForm.vue";
|
||||||
|
import {useAirportsStore} from "stores/Airports";
|
||||||
|
|
||||||
const aircraftsStore = useAircraftsStore();
|
const aircraftsStore = useAircraftsStore();
|
||||||
const poiStore = usePointsOfInterestStore();
|
const poiStore = usePointsOfInterestStore();
|
||||||
const flightsStore = useFlightsStore();
|
const flightsStore = useFlightsStore();
|
||||||
|
const airportStore = useAirportsStore();
|
||||||
|
|
||||||
useMeta({title: "New flight"});
|
useMeta({title: "New flight"});
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
aircraftsStore.fetch();
|
aircraftsStore.fetch();
|
||||||
poiStore.fetch();
|
poiStore.fetch();
|
||||||
flightsStore.fetch();
|
flightsStore.fetch();
|
||||||
|
airportStore.fetch();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const rules = {
|
|
||||||
aircraft: [isRequired],
|
|
||||||
takeOffLocation: [isRequired],
|
|
||||||
LandingLocation: [isRequired],
|
|
||||||
}
|
|
||||||
|
|
||||||
const aircrafts = storeToRefs(aircraftsStore).items;
|
const aircrafts = storeToRefs(aircraftsStore).items;
|
||||||
const locations = storeToRefs(poiStore).items;
|
const locations = storeToRefs(poiStore).items;
|
||||||
|
const airports = storeToRefs(airportStore).items;
|
||||||
|
|
||||||
const turnPointsCount = ref(1);
|
const turnPointsCount = ref(1);
|
||||||
const formData = reactive({
|
|
||||||
aircraftId: null,
|
|
||||||
takeOffLocation: null,
|
|
||||||
landingLocation: null,
|
|
||||||
turnPoints: [],
|
|
||||||
solo: true,
|
|
||||||
secondPilot: "",
|
|
||||||
instructor: false,
|
|
||||||
picTime: 0,
|
|
||||||
date: new Date(),
|
|
||||||
takeOffTime: "",
|
|
||||||
landingTime: "",
|
|
||||||
flightDurationMinutes: 0,
|
|
||||||
landings: 1,
|
|
||||||
note: ""
|
|
||||||
})
|
|
||||||
const formSteps = ["aircraft", "basic", "track", "copilot", "photos", "notes"]
|
|
||||||
const formStep = ref(formSteps[0]);
|
|
||||||
|
|
||||||
// watch(() => formData.takeOffLocation, (newVal) => {
|
// watch(() => formData.takeOffLocation, (newVal) => {
|
||||||
// if (!formData.landingLocation) {
|
// if (!formData.landingLocation) {
|
||||||
@@ -58,15 +36,12 @@ const formStep = ref(formSteps[0]);
|
|||||||
// })
|
// })
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const saveForm = () => {
|
const handleSave = (formData) => {
|
||||||
flightsStore.create(this.formData).then(() => {
|
flightsStore.create(formData).then(() => {
|
||||||
router.push({name: "myFlights"});
|
router.push({name: "myFlights"});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const basicInfoFilled = computed(() => {
|
|
||||||
return formData.aircraftId && formData.takeOffLocation && formData.landingLocation && formData.date && formData.flightDurationMinutes > 0
|
|
||||||
})
|
|
||||||
|
|
||||||
const removeTurnPoint = (idx) => {
|
const removeTurnPoint = (idx) => {
|
||||||
formData.turnPoints.splice(idx, 1);
|
formData.turnPoints.splice(idx, 1);
|
||||||
@@ -77,120 +52,13 @@ const removeTurnPoint = (idx) => {
|
|||||||
<template>
|
<template>
|
||||||
|
|
||||||
<q-breadcrumbs>
|
<q-breadcrumbs>
|
||||||
<q-breadcrumbs-el :to="{name: 'myFlights'}">My Flights</q-breadcrumbs-el>
|
<q-breadcrumbs-el :to="{name: 'myFlights'}">Moje lety</q-breadcrumbs-el>
|
||||||
<q-breadcrumbs-el>Add new</q-breadcrumbs-el>
|
<q-breadcrumbs-el>Nový</q-breadcrumbs-el>
|
||||||
</q-breadcrumbs>
|
</q-breadcrumbs>
|
||||||
|
|
||||||
<!-- {{ formData }}-->
|
<!-- {{ formData }}-->
|
||||||
|
|
||||||
<q-form @submit.prevent="saveForm" class="q-my-lg">
|
<FlightForm @save="handleSave" :aircrafts="aircrafts" :airports="airports" :locations="locations"/>
|
||||||
<q-tabs
|
|
||||||
v-model="formStep"
|
|
||||||
outside-arrows
|
|
||||||
mobile-arrows
|
|
||||||
left-icon="chevron_left"
|
|
||||||
right-icon="chevron_right"
|
|
||||||
active-color="primary"
|
|
||||||
class="q-mb-lg"
|
|
||||||
>
|
|
||||||
<q-tab name="aircraft" icon="flight" label="Aircraft"/>
|
|
||||||
<q-tab name="basic" icon="flight_takeoff" label="Basic"/>
|
|
||||||
<q-tab name="track" icon="route" label="Route"/>
|
|
||||||
<q-tab name="copilot" icon="person_add" label="Copilot"/>
|
|
||||||
<q-tab name="photos" icon="add_a_photo" label="Photos"/>
|
|
||||||
<q-tab name="notes" icon="edit_note" label="Notes"/>
|
|
||||||
</q-tabs>
|
|
||||||
|
|
||||||
<q-tab-panels v-model="formStep" animated>
|
|
||||||
<q-tab-panel name="aircraft">
|
|
||||||
<q-select
|
|
||||||
v-model="formData.aircraftId"
|
|
||||||
option-label="name"
|
|
||||||
option-value="id"
|
|
||||||
emit-value
|
|
||||||
map-options
|
|
||||||
:rules="rules.aircraft"
|
|
||||||
:options="aircrafts"
|
|
||||||
label="Aircraft"
|
|
||||||
>
|
|
||||||
<template #prepend>
|
|
||||||
<q-icon name="flight"></q-icon>
|
|
||||||
</template>
|
|
||||||
</q-select>
|
|
||||||
|
|
||||||
<TabsStepper v-model="formStep" next-step="basic"/>
|
|
||||||
</q-tab-panel>
|
|
||||||
<q-tab-panel name="basic">
|
|
||||||
<q-input type="date" v-model="formData.date" label="Date"/>
|
|
||||||
<q-input type="time" v-model="formData.time" label="Take-off time"/>
|
|
||||||
<LocationInput label="Take-off location" :rules="rules.takeOffLocation" :locations="locations" v-model="formData.takeOffLocation" prepend-icon="flight_takeoff"/>
|
|
||||||
<LocationInput label="Landing location" :rules="rules.landingLocation" :locations="locations" v-model="formData.landingLocation" prepend-icon="flight_land"/>
|
|
||||||
<q-input v-model="formData.landings" label="Landings" type="number" min="1"/>
|
|
||||||
<q-input v-model="formData.flightDurationMinutes" min=0 label="Flight duration (minutes)" type="number"/>
|
|
||||||
|
|
||||||
<TabsStepper v-model="formStep" previous-step="aircraft" next-step="track"/>
|
|
||||||
</q-tab-panel>
|
|
||||||
|
|
||||||
<q-tab-panel name="track">
|
|
||||||
<template v-for="i in turnPointsCount" :key="`${i}-${turnPointsCount}`">
|
|
||||||
<LocationInput label="Turn point" :locations="locations" v-model="formData.turnPoints[i-1]">
|
|
||||||
<template #append v-if="i > 1">
|
|
||||||
<q-btn @click="removeTurnPoint(i - 1)">-</q-btn>
|
|
||||||
</template>
|
|
||||||
</LocationInput>
|
|
||||||
</template>
|
|
||||||
<q-btn @click="turnPointsCount++">
|
|
||||||
Add turn point
|
|
||||||
</q-btn>
|
|
||||||
|
|
||||||
<q-uploader
|
|
||||||
label="Upload GPX track"
|
|
||||||
url="http://localhost:4444/upload"
|
|
||||||
class="q-mx-sm"
|
|
||||||
style="width: 100%"
|
|
||||||
auto-upload
|
|
||||||
color="secondary"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TabsStepper v-model="formStep" previous-step="basic" next-step="copilot"/>
|
|
||||||
</q-tab-panel>
|
|
||||||
|
|
||||||
<q-tab-panel name="copilot">
|
|
||||||
<q-btn-toggle spread no-caps v-model="formData.solo" toggle-color="primary" :options="[{label: 'Solo flight', value: true}, {label: 'Flight with someone', value: false}]"/>
|
|
||||||
|
|
||||||
<div v-if="!formData.solo">
|
|
||||||
<q-input type="text" v-model="formData.secondPilot" label="Second pilot"/>
|
|
||||||
<q-checkbox v-model="formData.instructor" v-if="formData.secondPilot !== ''" label="Instructor"/>
|
|
||||||
<q-input v-model="formData.picTime" label="PIC time"/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<TabsStepper v-model="formStep" previous-step="track" next-step="photos"/>
|
|
||||||
</q-tab-panel>
|
|
||||||
|
|
||||||
<q-tab-panel name="photos">
|
|
||||||
<q-uploader
|
|
||||||
label="Upload photos"
|
|
||||||
url="http://localhost:4444/upload"
|
|
||||||
multiple
|
|
||||||
auto-upload
|
|
||||||
style="width: 100%;"
|
|
||||||
color="secondary"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TabsStepper v-model="formStep" previous-step="copilot" next-step="notes"/>
|
|
||||||
</q-tab-panel>
|
|
||||||
|
|
||||||
<q-tab-panel name="notes">
|
|
||||||
<q-editor v-model="formData.note" height="60vh" label="Notes"/>
|
|
||||||
|
|
||||||
<TabsStepper v-model="formStep" previous-step="photos"/>
|
|
||||||
</q-tab-panel>
|
|
||||||
</q-tab-panels>
|
|
||||||
|
|
||||||
<q-page-sticky expand position="bottom" :offset="[10, 10]" v-if="basicInfoFilled">
|
|
||||||
<div>
|
|
||||||
<q-btn type="submit" size="lg" class="full-width" color="primary" icon="save" label="Save flight"/>
|
|
||||||
</div>
|
|
||||||
</q-page-sticky>
|
|
||||||
</q-form>
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -10,9 +10,6 @@ const $q = useQuasar();
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const handleSave = (formData) => {
|
const handleSave = (formData) => {
|
||||||
isRequestPending.value = true;
|
isRequestPending.value = true;
|
||||||
|
|
||||||
console.log("handlesave", formData);
|
|
||||||
|
|
||||||
useAircraftsStore().create(formData).then(() => {
|
useAircraftsStore().create(formData).then(() => {
|
||||||
$q.notify({message: "Letadlo úspěšně vytvořeno", position: "top", color: "positive"});
|
$q.notify({message: "Letadlo úspěšně vytvořeno", position: "top", color: "positive"});
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
<script setup>
|
||||||
|
import {onMounted, ref} from "vue";
|
||||||
|
import {useFlightsStore} from "stores/Flights";
|
||||||
|
import {useMeta} from "quasar";
|
||||||
|
import FlightsList from "components/flights/FlightsList.vue";
|
||||||
|
import FlightsListSkeleton from "components/flights/FlightsListSkeleton.vue";
|
||||||
|
import {storeToRefs} from "pinia";
|
||||||
|
import AddFAB from "components/AddFAB.vue";
|
||||||
|
|
||||||
|
useMeta({title: "Moje lety"});
|
||||||
|
|
||||||
|
const flightsStore = useFlightsStore();
|
||||||
|
const flights = storeToRefs(flightsStore).items;
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
onMounted(() => {
|
||||||
|
loading.value = true;
|
||||||
|
flightsStore.fetch().finally(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FlightsListSkeleton v-if="loading"/>
|
||||||
|
|
||||||
|
<div class="q-pa-md q-gutter-sm" v-else-if="!loading && flights.length === 0">
|
||||||
|
<q-banner class="bg-light-blue text-white ">
|
||||||
|
<template v-slot:avatar>
|
||||||
|
<q-icon name="info"/>
|
||||||
|
</template>
|
||||||
|
Nemáš zaznamenaný žádný let.
|
||||||
|
<template v-slot:action>
|
||||||
|
<q-btn flat color="white" :to="{name: 'addFlight'}" label="Přidat let"/>
|
||||||
|
</template>
|
||||||
|
</q-banner>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<FlightsList :items="flights" v-else/>
|
||||||
|
|
||||||
|
|
||||||
|
<AddFAB :router-link="{name: 'addFlight'}"/>
|
||||||
|
</template>
|
||||||
@@ -17,7 +17,7 @@ const routes = [
|
|||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{path: '', name: "profile", component: () => import('pages/profile/ProfilePage.vue')},
|
{path: '', name: "profile", component: () => import('pages/profile/ProfilePage.vue')},
|
||||||
{path: 'my-flights', name: "myFlights", component: () => import('pages/profile/lists/MyFlightsPage.vue')},
|
{path: 'my-flights', name: "myFlights", component: () => import('pages/profile/lists/FlightsPage.vue')},
|
||||||
{path: 'my-flights/add', name: "addFlight", component: () => import('pages/profile/forms/FlightLogPage.vue')},
|
{path: 'my-flights/add', name: "addFlight", component: () => import('pages/profile/forms/FlightLogPage.vue')},
|
||||||
{path: 'my-flights/:id', name: "flightDetail", component: () => import('pages/profile/details/FlightDetailPage.vue')},
|
{path: 'my-flights/:id', name: "flightDetail", component: () => import('pages/profile/details/FlightDetailPage.vue')},
|
||||||
{path: 'my-flights/:id/edit', name: "flightEdit", component: () => import('pages/profile/forms/FlightEditPage.vue')},
|
{path: 'my-flights/:id/edit', name: "flightEdit", component: () => import('pages/profile/forms/FlightEditPage.vue')},
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ export const useAircraftsStore = defineStore('Aircrafts', {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
update(id, formData) {
|
update(id, formData) {
|
||||||
console.log(id, formData);
|
|
||||||
return apolloWrapper.mutate(EDIT_AIRCRAFT, {id: id, input: formData}).then(({data}) => {
|
return apolloWrapper.mutate(EDIT_AIRCRAFT, {id: id, input: formData}).then(({data}) => {
|
||||||
console.log("Pridat update ve storu")
|
console.log("Pridat update ve storu")
|
||||||
return data.editAircraft;
|
return data.editAircraft;
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import {defineStore} from 'pinia'
|
||||||
|
import {apolloWrapper} from "src/graphql/apollo";
|
||||||
|
import {AIRPORTS_LIST} from "src/graphql/queries/airports";
|
||||||
|
|
||||||
|
export const useAirportsStore = defineStore('Airports', {
|
||||||
|
state: () => ({
|
||||||
|
items: [],
|
||||||
|
}),
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
fetch() {
|
||||||
|
return apolloWrapper
|
||||||
|
.query(AIRPORTS_LIST)
|
||||||
|
.then((result) => {
|
||||||
|
this.items = result.data.airports;
|
||||||
|
return result.data.airports;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import {defineStore} from 'pinia'
|
||||||
|
|
||||||
|
export const useFlashMessagesStore = defineStore('FlashMessages', {
|
||||||
|
state: () => ({
|
||||||
|
items: []
|
||||||
|
}),
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
add(title, content, type) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
+14
-10
@@ -1,6 +1,7 @@
|
|||||||
import {defineStore} from 'pinia'
|
import {defineStore} from 'pinia'
|
||||||
import {apolloClient} from "src/graphql/apollo"
|
import {apolloWrapper} from "src/graphql/apollo"
|
||||||
import {FLIGHTS_LIST} from "src/graphql/queries/flights";
|
import {FLIGHTS_LIST} from "src/graphql/queries/flights";
|
||||||
|
import {CREATE_FLIGHT} from "src/graphql/mutations/flight";
|
||||||
|
|
||||||
export const useFlightsStore = defineStore('Flights', {
|
export const useFlightsStore = defineStore('Flights', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
@@ -9,27 +10,30 @@ export const useFlightsStore = defineStore('Flights', {
|
|||||||
|
|
||||||
getters: {
|
getters: {
|
||||||
getDetail: (state) => (flightId) => {
|
getDetail: (state) => (flightId) => {
|
||||||
console.log(state.items);
|
|
||||||
return state.items.find(f => f.id === flightId);
|
return state.items.find(f => f.id === flightId);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
create(formData) {
|
create(formData) {
|
||||||
this.items.push(formData);
|
delete formData['id'];
|
||||||
|
return apolloWrapper.mutate(CREATE_FLIGHT, {input: formData}).then(({data}) => {
|
||||||
|
this.items.push(data.createFlight);
|
||||||
|
|
||||||
|
return data.createFlight;
|
||||||
|
})
|
||||||
},
|
},
|
||||||
fetchDetail(id) {
|
fetchDetail(id) {
|
||||||
this.items.push({id: id, takeOffLocation: "LKLT", landingLocation: "LKLT", date: "2023-02-01", aircraft: {id: 1, name: "OK-AUR28"}, flightDurationMinutes: 76})
|
this.items.push({id: id, takeOffLocation: "LKLT", landingLocation: "LKLT", date: "2023-02-01", aircraft: {id: 1, name: "OK-AUR28"}, flightDurationMinutes: 76})
|
||||||
},
|
},
|
||||||
fetch() {
|
fetch() {
|
||||||
apolloClient
|
return apolloWrapper
|
||||||
.query({
|
.query(FLIGHTS_LIST)
|
||||||
|
.then((result) => {
|
||||||
|
this.items = result.data.flights;
|
||||||
|
|
||||||
query: FLIGHTS_LIST,
|
return result.data.flights;
|
||||||
|
});
|
||||||
})
|
|
||||||
|
|
||||||
.then((result) => console.log(result));
|
|
||||||
// if (this.items.length > 0) return;
|
// if (this.items.length > 0) return;
|
||||||
//
|
//
|
||||||
// this.$patch({
|
// this.$patch({
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export const useUserStore = defineStore('LoggedUser', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async setupCredentials() {
|
async setupCredentials() {
|
||||||
|
// TODO: aktualne se vola s kazdym prechodem mezi routami, to je urcite zbytecne!
|
||||||
await api.get("/me").then(({data}) => {
|
await api.get("/me").then(({data}) => {
|
||||||
this.user = data;
|
this.user = data;
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
|
|||||||
Reference in New Issue
Block a user