Jiny pristup k vytvareni letu, polofunkcni editace letu
This commit is contained in:
@@ -1,213 +0,0 @@
|
||||
<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, watch} 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: 43,
|
||||
takeoffAirportId: 3,
|
||||
landingAirportId: 3,
|
||||
takeoffDatetime: new Date("2023-06-05T09:55:01"),
|
||||
landingDatetime: new Date("2023-06-05T10:41:51"),
|
||||
|
||||
photos: [],
|
||||
|
||||
// turnPoints: [],
|
||||
copilotId: null,
|
||||
solo: true,
|
||||
withInstructor: false,
|
||||
durationPic: 0,
|
||||
durationTotal: null,
|
||||
landings: 1,
|
||||
|
||||
name: "",
|
||||
description: ""
|
||||
})
|
||||
const rules = {
|
||||
aircraft: [isRequired],
|
||||
takeoffAirport: [],
|
||||
landingAirport: [],
|
||||
}
|
||||
|
||||
const recalculateDuration = () => {
|
||||
const diff = formData.landingDatetime - formData.takeoffDatetime;
|
||||
|
||||
formData.durationTotal = diff / 1000 / 60;
|
||||
}
|
||||
|
||||
watch(() => formData.takeoffDatetime, () => recalculateDuration())
|
||||
watch(() => formData.landingDatetime, () => recalculateDuration())
|
||||
|
||||
const formSteps = ["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">
|
||||
<template #default>
|
||||
|
||||
<pre>{{ formData }}</pre>
|
||||
|
||||
<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="basic" icon="flight_takeoff" label="Základní informace"/>
|
||||
<q-tab name="copilot" icon="person_add" label="Spolucestující"/>
|
||||
<q-tab name="track" icon="route" label="Trasa"/>
|
||||
<q-tab name="photos" icon="add_a_photo" label="Fotky"/>
|
||||
<q-tab name="notes" icon="edit_note" label="Poznámky"/>
|
||||
</q-tabs>
|
||||
|
||||
<q-tab-panels v-model="formStep" animated>
|
||||
|
||||
<q-tab-panel name="basic">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template v-if="formData.aircraftId">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h3>Vzlet</h3>
|
||||
|
||||
<DatetimePicker v-model="formData.takeoffDatetime" label="Datum a čas vzletu"/>
|
||||
<LocationInput label="Letiště vzletu" :rules="rules.takeoffAirport" :locations="airports" v-model="formData.takeoffAirportId" prepend-icon="flight_takeoff"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" v-if="!!formData.takeoffDatetime && !!formData.takeoffAirportId">
|
||||
<div class="col">
|
||||
|
||||
<h3>Přistání</h3>
|
||||
|
||||
<LocationInput label="Letiště přistání" :rules="rules.landingAirport" :locations="airports" v-model="formData.landingAirportId" prepend-icon="flight_land"/>
|
||||
<DatetimePicker v-model="formData.landingDatetime" label="Datum a čas přistání"/>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<q-input v-model="formData.landings" label="Počet přistání" type="number" min="1"/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<q-input v-model="formData.durationTotal" readonly min=0 label="Délka letu (v minutách)" type="number"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TabsStepper v-model="formStep" next-step="track" v-if="formData.landingAirportId"/>
|
||||
</template>
|
||||
</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="basic" 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="copilot" next-step="photos"/>
|
||||
</q-tab-panel>
|
||||
|
||||
|
||||
|
||||
<q-tab-panel name="photos">
|
||||
<q-file
|
||||
label="Upload photos"
|
||||
multiple
|
||||
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>
|
||||
</template>
|
||||
<template #saveButton>
|
||||
<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>
|
||||
</template>
|
||||
</FormWrapper>
|
||||
</template>
|
||||
@@ -0,0 +1,88 @@
|
||||
<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, watch} from "vue";
|
||||
import {isRequired} from "src/composables/form/rules";
|
||||
import DatetimePicker from "components/inputs/DatetimePicker.vue";
|
||||
import FlightFormBasic from "components/flights/form/FlightFormBasic.vue";
|
||||
|
||||
|
||||
|
||||
|
||||
const rules = {
|
||||
aircraft: [isRequired],
|
||||
takeoffAirport: [],
|
||||
landingAirport: [],
|
||||
}
|
||||
|
||||
|
||||
|
||||
const formSteps = ["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">-->
|
||||
<!-- <template #default>-->
|
||||
|
||||
<pre>{{ formData }}</pre>
|
||||
|
||||
<q-tab-panels v-model="formStep" animated>
|
||||
|
||||
|
||||
<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-file
|
||||
label="Upload GPX track"
|
||||
class="q-mx-sm"
|
||||
style="width: 100%"
|
||||
color="secondary"
|
||||
/>
|
||||
|
||||
<TabsStepper v-model="formStep" previous-step="copilot" next-step="photos"/>
|
||||
</q-tab-panel>
|
||||
|
||||
|
||||
<q-tab-panel name="photos">
|
||||
<q-file
|
||||
label="Upload photos"
|
||||
v-model="formData.photos"
|
||||
multiple
|
||||
style="width: 100%;"
|
||||
color="secondary"
|
||||
/>
|
||||
|
||||
<TabsStepper v-model="formStep" previous-step="copilot" next-step="notes"/>
|
||||
</q-tab-panel>
|
||||
|
||||
</q-tab-panels>
|
||||
|
||||
|
||||
<!-- </template>-->
|
||||
<!-- <template #saveButton>-->
|
||||
<!-- <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>-->
|
||||
<!-- </template>-->
|
||||
<!-- </FormWrapper>-->
|
||||
</template>
|
||||
@@ -0,0 +1,148 @@
|
||||
<script setup>
|
||||
import DatetimePicker from "components/inputs/DatetimePicker.vue";
|
||||
import {onMounted, reactive, ref, watch} from "vue";
|
||||
import FormWrapper from "components/FormWrapper.vue";
|
||||
import {setDefaultData} from "src/composables/form/formHandling";
|
||||
import {formatDate, parseDate} from "src/composables/datetimeUtils";
|
||||
|
||||
const props = defineProps({
|
||||
aircrafts: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
airports: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
locations: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
flight: {
|
||||
type: Object,
|
||||
required: false
|
||||
}
|
||||
}
|
||||
)
|
||||
const formData = reactive({
|
||||
aircraftId: 43,
|
||||
takeoffAirportId: null,
|
||||
landingAirportId: null,
|
||||
takeoffDatetime: null,
|
||||
landingDatetime: null,
|
||||
durationTotal: null,
|
||||
landings: 1,
|
||||
solo: true,
|
||||
|
||||
durationPic: 0,
|
||||
withInstructor: false,
|
||||
copilotId: null,
|
||||
name: "",
|
||||
description: ""
|
||||
})
|
||||
|
||||
const recalculateDuration = (data) => {
|
||||
const diff = data.landingDatetime - data.takeoffDatetime;
|
||||
data.durationTotal = diff / 1000 / 60;
|
||||
}
|
||||
|
||||
watch(() => [formData.takeoffDatetime, formData.landingDatetime], () => recalculateDuration(formData))
|
||||
|
||||
const rules = {};
|
||||
const isMounted = ref(false);
|
||||
// tohle je blby ohack kvuli tomu, ze DatetimeInput se renderuje jeste pred mounted a do props nedostane hodnotu
|
||||
// modelu. A neumi se s tim smirit...
|
||||
onMounted(() => {
|
||||
if (props.flight) {
|
||||
setDefaultData(formData, props.flight);
|
||||
}
|
||||
isMounted.value = true;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FormWrapper v-model="formData" v-if="isMounted">
|
||||
|
||||
{{ formData }}
|
||||
<hr>
|
||||
{{ flight }}
|
||||
<hr>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template v-if="formData.aircraftId">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h3>Vzlet</h3>
|
||||
|
||||
<DatetimePicker v-model="formData.takeoffDatetime" label="Datum a čas vzletu"/>
|
||||
|
||||
<q-select
|
||||
v-model="formData.takeoffAirportId"
|
||||
emit-value
|
||||
map-options
|
||||
option-label="name"
|
||||
option-value="id"
|
||||
clearable
|
||||
:options="airports"
|
||||
label="Letiště vzletu">
|
||||
<template #prepend>
|
||||
<q-icon name="flight_takeoff"/>
|
||||
</template>
|
||||
</q-select>
|
||||
|
||||
<q-checkbox v-model="formData.solo" label="Solo?"/>
|
||||
</div>
|
||||
<div class="col" data-if="!!formData.takeoffDatetime && !!formData.takeoffAirportId">
|
||||
|
||||
<h3>Přistání</h3>
|
||||
|
||||
<DatetimePicker v-model="formData.landingDatetime" label="Datum a čas přistání"/>
|
||||
|
||||
<q-select
|
||||
v-model="formData.landingAirportId"
|
||||
emit-value
|
||||
map-options
|
||||
option-label="name"
|
||||
option-value="id"
|
||||
clearable
|
||||
:options="airports"
|
||||
label="Letiště přistání">
|
||||
<template #append>
|
||||
<q-icon name="flight_land"/>
|
||||
</template>
|
||||
</q-select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" v-if="formData.landingAirportId">
|
||||
<div class="col">
|
||||
<q-input v-model="formData.landings" label="Počet přistání" type="number" min="1"/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<q-input v-model="formData.durationTotal" readonly min=0 label="Délka letu (v minutách)" type="number"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- <TabsStepper v-model="formStep" next-step="track" v-if="formData.landingAirportId"/>-->
|
||||
</template>
|
||||
</FormWrapper>
|
||||
</template>
|
||||
@@ -0,0 +1,35 @@
|
||||
<script setup>
|
||||
import {reactive} from "vue";
|
||||
import FormWrapper from "components/FormWrapper.vue";
|
||||
|
||||
const props = defineProps({
|
||||
flight: {
|
||||
type: Object,
|
||||
required: false
|
||||
},
|
||||
copilots: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const formData = reactive({
|
||||
solo: true,
|
||||
withInstructor: false,
|
||||
copilotId: null,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FormWrapper v-model="formData">
|
||||
|
||||
<q-btn-toggle spread no-caps v-model="formData.solo" toggle-color="primary" :options="[{label: 'Solo', value: true}, {label: 'S někým', value: false}]"/>
|
||||
|
||||
<div v-if="!formData.solo">
|
||||
<q-input type="text" v-model="formData.secondPilot" label="Druhý pilot"/>
|
||||
<q-checkbox v-model="formData.withInstructor" v-if="formData.secondPilot !== ''" label="Instruktor"/>
|
||||
<!-- <q-input v-model="formData.picTime" label="PIC time"/>-->
|
||||
</div>
|
||||
|
||||
</FormWrapper>
|
||||
</template>
|
||||
@@ -0,0 +1,34 @@
|
||||
<script setup>
|
||||
import {onMounted, ref} from "vue";
|
||||
import FormWrapper from "components/FormWrapper.vue";
|
||||
import {setDefaultData} from "src/composables/form/formHandling";
|
||||
import RichTextEditor from "components/inputs/RichTextEditor.vue";
|
||||
|
||||
const props = defineProps({
|
||||
flight: {
|
||||
type: Object,
|
||||
required: false
|
||||
},
|
||||
});
|
||||
|
||||
const formData = ref({
|
||||
name: "",
|
||||
description: ""
|
||||
})
|
||||
|
||||
const isMounted = ref(false);
|
||||
onMounted(() => {
|
||||
if (props.flight)
|
||||
setDefaultData(formData.value, props.flight);
|
||||
|
||||
isMounted.value = true;
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FormWrapper v-model="formData" v-if="isMounted">
|
||||
<q-input type="text" v-model="formData.name" label="Název"/>
|
||||
<RichTextEditor v-model="formData.description" label="Popis letu, poznámky, ..." />
|
||||
</FormWrapper>
|
||||
</template>
|
||||
@@ -0,0 +1,26 @@
|
||||
<script setup>
|
||||
import {formatDate, parseDate} from "src/composables/datetimeUtils";
|
||||
import FormWrapper from "components/FormWrapper.vue";
|
||||
import {reactive} from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
flight: {
|
||||
type: Object,
|
||||
required: false
|
||||
}
|
||||
}
|
||||
)
|
||||
const formData = reactive({});
|
||||
const testDate = "2023-06-09T22:00:00.000+02:00";
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FormWrapper v-model="formData">
|
||||
|
||||
<q-banner>
|
||||
Test date: {{testDate}} => {{ parseDate(testDate) }}
|
||||
|
||||
</q-banner>
|
||||
</FormWrapper>
|
||||
</template>
|
||||
@@ -0,0 +1,66 @@
|
||||
<script setup>
|
||||
import {onMounted, ref, watch} from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: String,
|
||||
disabled: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: () => []
|
||||
},
|
||||
routerEditMode: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
flightId: {
|
||||
type: Number,
|
||||
required: false,
|
||||
}
|
||||
})
|
||||
|
||||
const currentStep = ref("");
|
||||
onMounted(() => {
|
||||
currentStep.value = props.modelValue;
|
||||
})
|
||||
|
||||
const steps = [
|
||||
{name: "basic", icon: "flight_takeoff", label: "Základní informace"},
|
||||
{name: "copilot", icon: "person_add", label: "Spolucestující"},
|
||||
{name: "track", icon: "route", label: "Trasa"},
|
||||
{name: "photos", icon: "add_a_photo", label: "Fotky"},
|
||||
{name: "notes", icon: "edit_note", label: "Poznámky"},
|
||||
];
|
||||
|
||||
const emit = defineEmits(["update:modelValue"])
|
||||
watch(currentStep, (val) => emit("update:modelValue", val));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<q-tabs v-if="routerEditMode" active-color="primary" class="q-mb-lg">
|
||||
<q-route-tab
|
||||
v-for="step in steps"
|
||||
:key="step.name"
|
||||
:icon="step.icon"
|
||||
:label="step.label"
|
||||
replace
|
||||
:to="{name: 'flightEdit', params: {id: flightId, step: step.name}}"
|
||||
:disable="disabled.includes(step.name)"
|
||||
exact
|
||||
/>
|
||||
</q-tabs>
|
||||
|
||||
<q-tabs
|
||||
v-else
|
||||
v-model="currentStep"
|
||||
outside-arrows
|
||||
mobile-arrows
|
||||
left-icon="chevron_left"
|
||||
right-icon="chevron_right"
|
||||
active-color="primary"
|
||||
class="q-mb-lg"
|
||||
>
|
||||
|
||||
<q-tab v-for="step in steps" :key="step.name" v-bind="step" :disable="disabled.includes(step.name)"/>
|
||||
</q-tabs>
|
||||
</template>
|
||||
@@ -1,17 +1,18 @@
|
||||
<script setup>
|
||||
import {computed, onMounted, ref, watch} from "vue";
|
||||
import {parseDate} from "src/composables/datetimeUtils";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Date,
|
||||
type: [String, Date]
|
||||
},
|
||||
dateMask: {
|
||||
type: String, Boolean,
|
||||
type: [String, Boolean],
|
||||
required: false,
|
||||
default: "YYYY-MM-DD"
|
||||
},
|
||||
timeMask: {
|
||||
type: String, Boolean,
|
||||
type: [String, Boolean],
|
||||
required: false,
|
||||
default: "HH:mm"
|
||||
}
|
||||
@@ -19,35 +20,49 @@ const props = defineProps({
|
||||
|
||||
let dateModel = ref("");
|
||||
let timeModel = ref("");
|
||||
|
||||
onMounted(() => {
|
||||
let date = "";
|
||||
let time = "";
|
||||
let emitDefaultValue = false;
|
||||
|
||||
if (props.modelValue) {
|
||||
|
||||
date = props.dateMask.replace("YYYY", props.modelValue.getFullYear())
|
||||
.replace("MM", ('0' + props.modelValue.getMonth()).slice(-2))
|
||||
.replace("DD", ('0' + props.modelValue.getDate()).slice(-2));
|
||||
|
||||
time = props.timeMask.replace("HH", ('0' + props.modelValue.getHours()).slice(-2))
|
||||
.replace("mm", ('0' + props.modelValue.getMinutes()).slice(-2))
|
||||
let modelVal = props.modelValue;
|
||||
if (typeof modelVal === 'string') {
|
||||
modelVal = parseDate(props.modelValue);
|
||||
emitDefaultValue = true;
|
||||
}
|
||||
|
||||
if (time === '') {
|
||||
time = "00:00";
|
||||
date = props.dateMask
|
||||
.replace("YYYY", modelVal.getFullYear().toString())
|
||||
.replace("MM", ('0' + modelVal.getMonth()).slice(-2))
|
||||
.replace("DD", ('0' + modelVal.getDate()).slice(-2));
|
||||
|
||||
time = props.timeMask
|
||||
.replace("HH", ('0' + modelVal.getHours()).slice(-2))
|
||||
.replace("mm", ('0' + modelVal.getMinutes()).slice(-2))
|
||||
.replace("ss", ('0' + modelVal.getSeconds()).slice(-2))
|
||||
|
||||
// if (time === '') {
|
||||
// time = "00:00";
|
||||
// }
|
||||
}
|
||||
|
||||
dateModel.value = date;
|
||||
timeModel.value = time;
|
||||
|
||||
if (emitDefaultValue) {
|
||||
emitModelUpdate();
|
||||
}
|
||||
});
|
||||
|
||||
const model = computed(() => dateModel.value + " " + timeModel.value);
|
||||
|
||||
const model = computed(() => dateModel.value + " " + timeModel.value);
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
const emitModelUpdate = () => {
|
||||
const now = new Date();
|
||||
const val = new Date(model.value);
|
||||
val.setHours(val.getHours() + now.getTimezoneOffset());
|
||||
const val = parseDate(model.value);
|
||||
// val.setHours(val.getHours() + now.getTimezoneOffset());
|
||||
emit("update:modelValue", val);
|
||||
}
|
||||
watch([dateModel, timeModel], emitModelUpdate)
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
<script setup>
|
||||
import {onMounted, ref, watch} from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "60vh"
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: ""
|
||||
},
|
||||
rules: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: () => []
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
|
||||
const content = ref("");
|
||||
onMounted(() => {
|
||||
content.value = props.modelValue;
|
||||
})
|
||||
|
||||
watch(content, () => emit("update:modelValue", content.value));
|
||||
|
||||
const definitions = {
|
||||
left: {icon: "format_align_left"},
|
||||
center: {icon: "format_align_center"},
|
||||
right: {icon: "format_align_right"},
|
||||
justify: {icon: "format_align_justify"},
|
||||
bold: {icon: 'format_bold'},
|
||||
italic: {icon: 'format_italic'},
|
||||
strike: {icon: 'strikethrough_s'},
|
||||
underline: {icon: 'format_underline'},
|
||||
unordered: {icon: 'format_list_bulleted'},
|
||||
ordered: {icon: 'format_list_numbered'},
|
||||
undo: {icon: 'undo'},
|
||||
redo: {icon: 'redo'},
|
||||
}
|
||||
|
||||
const toolbar = [
|
||||
["bold", "italic", "strike"],
|
||||
["left", "center", "justify", "right"],
|
||||
['unordered', 'ordered'],
|
||||
["undo", "redo"]
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<q-editor v-model="content" :definitions="definitions" :toolbar="toolbar" :height="height" :label="label" :rules="rules"/>
|
||||
</template>
|
||||
@@ -4,9 +4,35 @@ export const minutesToHumanReadable = (totalMinutes) => {
|
||||
return `${hours}h ${minutes}min`
|
||||
}
|
||||
|
||||
export const formatDate = (date) => {
|
||||
export const formatDate = (date, format = "DD. MM. YYYY") => {
|
||||
if (typeof date === 'string') {
|
||||
date = new Date(date);
|
||||
date = parseDate(date);
|
||||
}
|
||||
return date.getDate()+". "+date.getMonth()+". "+date.getFullYear();
|
||||
|
||||
return format
|
||||
.replace("DD", date.getDate().toString())
|
||||
.replace("MM", (date.getMonth() + 1).toString())
|
||||
.replace("YYYY", date.getFullYear().toString());
|
||||
}
|
||||
|
||||
|
||||
export const parseDate = (datetime) => {
|
||||
// const [date, time] = datetime.split("T");
|
||||
// const [year, month, day] = date.split("-");
|
||||
// const [hour, minute] = time.split(":");
|
||||
//
|
||||
// const dateObject = new Date(year, month, day, hour, minute, 0);
|
||||
// ^ tohle funguje ok
|
||||
|
||||
|
||||
const timestamp = Date.parse(datetime);
|
||||
const now = new Date();
|
||||
const dateObject = new Date(timestamp);
|
||||
// dateObject.setHours(dateObject.getHours() + now.getTimezoneOffset());
|
||||
// console.log("date", date);
|
||||
// console.log("time", time);
|
||||
// console.log("month", month, dateObject.getMonth(), dateObject.getUTCMonth());
|
||||
// console.log("day", day, dateObject.getDate(), dateObject.getUTCDate());
|
||||
|
||||
return dateObject;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
export const flightName = (flight) => {
|
||||
const date = flight.takeoffDatetime;//.getDay() + ". " + flight.takeoffDatetime.getMonth() + ". " + flight.takeoffDatetime.getFullYear();
|
||||
if (flight.name !== '') {
|
||||
return `${flight.name} (${date}})`
|
||||
} else {
|
||||
return `${flight.takeoffAirport.icaoCode} - ${flight.takeoffAirport.icaoCode} (${date})`;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,12 @@
|
||||
export const setDefaultData = (formData, defaultData) => {
|
||||
console.log("setting default data ", formData, defaultData);
|
||||
|
||||
const ignoredFields = ['__typename'];
|
||||
|
||||
for (const key in defaultData) {
|
||||
for (const key in formData) {
|
||||
if (ignoredFields.includes(key)) continue;
|
||||
if (typeof formData[key] === 'undefined') {
|
||||
console.error(`Key ${key} not in formData, skipping!`)
|
||||
if (typeof defaultData[key] === 'undefined') {
|
||||
console.error(`Key ${key} not in defaultData, skipping!`)
|
||||
continue;
|
||||
}
|
||||
formData[key] = defaultData[key];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,20 +8,18 @@ fragment flightFullInfo on Flight {
|
||||
takeoffDatetime
|
||||
landingDatetime
|
||||
durationMinCalculated
|
||||
|
||||
|
||||
solo
|
||||
|
||||
takeoffAirport {
|
||||
id
|
||||
name
|
||||
icaoCode
|
||||
}
|
||||
|
||||
landingAirport {
|
||||
id
|
||||
name
|
||||
icaoCode
|
||||
}
|
||||
|
||||
|
||||
aircraft {
|
||||
id
|
||||
name
|
||||
@@ -30,6 +28,14 @@ fragment flightFullInfo on Flight {
|
||||
}
|
||||
`;
|
||||
|
||||
export const FRAGMENT_FLIGHT_EDIT_IDS = gql`
|
||||
fragment flightEditIds on Flight {
|
||||
landingAirportId
|
||||
takeoffAirportId
|
||||
aircraftId
|
||||
copilotId
|
||||
}
|
||||
`;
|
||||
|
||||
// export const FRAGMENT_LOCATION = gql`
|
||||
// fragment airportInfo on Airport {
|
||||
|
||||
@@ -10,3 +10,13 @@ mutation createFlight($input: CreateFlightInput!) {
|
||||
|
||||
${FRAGMENT_FLIGHT_FULL_INFO}
|
||||
`
|
||||
|
||||
export const EDIT_FLIGHT = gql`
|
||||
mutation editFlight($id: Int!, $input: EditFlightInput!) {
|
||||
editFlight(id: $id, input: $input) {
|
||||
...flightFullInfo
|
||||
}
|
||||
}
|
||||
|
||||
${FRAGMENT_FLIGHT_FULL_INFO}
|
||||
`;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import gql from "graphql-tag";
|
||||
import {FRAGMENT_FLIGHT_FULL_INFO} from "src/graphql/fragments/flight";
|
||||
import {FRAGMENT_FLIGHT_EDIT_IDS, FRAGMENT_FLIGHT_FULL_INFO} from "src/graphql/fragments/flight";
|
||||
|
||||
export const FLIGHTS_LIST = gql`
|
||||
query FlightsList {
|
||||
@@ -14,9 +14,11 @@ export const FLIGHT_DETAIL = gql`
|
||||
query FlightDetail($id: Int!) {
|
||||
flight(id: $id) {
|
||||
...flightFullInfo
|
||||
...flightEditIds
|
||||
}
|
||||
}
|
||||
|
||||
${FRAGMENT_FLIGHT_FULL_INFO}
|
||||
${FRAGMENT_FLIGHT_EDIT_IDS}
|
||||
`;
|
||||
|
||||
|
||||
@@ -1,7 +1,104 @@
|
||||
<script setup>
|
||||
import FlightFormSteps from "components/flights/form/FlightFormSteps.vue";
|
||||
import {computed, onMounted, reactive, 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 {useMeta, useQuasar} from "quasar";
|
||||
import {storeToRefs} from "pinia";
|
||||
import {flightName} from "src/composables/flight";
|
||||
import FlightFormCopilot from "components/flights/form/FlightFormCopilot.vue";
|
||||
import {useSecondPilotsStore} from "stores/SecondPilots";
|
||||
import FlightFormNotes from "components/flights/form/FlightFormNotes.vue";
|
||||
import FlightFormPhotos from "components/flights/form/FlightFormPhotos.vue";
|
||||
|
||||
useMeta({title: "Editace letu"});
|
||||
|
||||
const $q = useQuasar();
|
||||
const route = useRoute();
|
||||
const flightId = parseInt(route.params.id);
|
||||
|
||||
const aircrafts = storeToRefs(useAircraftsStore()).items;
|
||||
const locations = storeToRefs(usePointsOfInterestStore()).items;
|
||||
const airports = storeToRefs(useAirportsStore()).items;
|
||||
const copilots = storeToRefs(useSecondPilotsStore()).items;
|
||||
|
||||
let flight = ref({});
|
||||
const disabledSteps = computed(() => flight.value.solo ? ["copilot"] : [flight.value.solo]);
|
||||
|
||||
const loading = ref(false);
|
||||
const fetchFlight = (flightId) => {
|
||||
loading.value = true;
|
||||
useFlightsStore().fetchDetail(flightId)
|
||||
.then(f => flight.value = f)
|
||||
.finally(() => loading.value = false);
|
||||
}
|
||||
|
||||
const formStep = ref("");
|
||||
watch(() => route.params.step, (val) => formStep.value = val);
|
||||
|
||||
onMounted(() => {
|
||||
formStep.value = route.params.step || "basic";
|
||||
|
||||
useAircraftsStore().fetch();
|
||||
usePointsOfInterestStore().fetch();
|
||||
useFlightsStore().fetch();
|
||||
useAirportsStore().fetch();
|
||||
useSecondPilotsStore().fetch();
|
||||
|
||||
fetchFlight(flightId);
|
||||
});
|
||||
|
||||
const handleFlightEdit = (data) => {
|
||||
useFlightsStore().update(flightId, data).then(() => {
|
||||
$q.notify({message: "Uspesne upraveno", position: "top", color: "positive"});
|
||||
|
||||
fetchFlight(flightId);
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
flight edit
|
||||
<div v-if="loading">loading</div>
|
||||
|
||||
<template v-else>
|
||||
<q-breadcrumbs :key="loading">
|
||||
<q-breadcrumbs-el :to="{name: 'myFlights'}">Moje lety</q-breadcrumbs-el>
|
||||
<q-breadcrumbs-el :to="{name: 'flightDetail', params: {id: flightId}}">{{ flightName(flight) }}</q-breadcrumbs-el>
|
||||
<q-breadcrumbs-el>Editace</q-breadcrumbs-el>
|
||||
</q-breadcrumbs>
|
||||
|
||||
<FlightFormSteps :router-edit-mode="true" :flightId="flightId" :disabled="disabledSteps"/>
|
||||
|
||||
<q-tab-panels v-model="formStep">
|
||||
<q-tab-panel name="basic">
|
||||
<FlightFormBasic
|
||||
@save="handleFlightEdit"
|
||||
:flight="flight"
|
||||
:locations="locations"
|
||||
:airports="airports"
|
||||
:aircrafts="aircrafts"
|
||||
/>
|
||||
</q-tab-panel>
|
||||
|
||||
<q-tab-panel name="copilot">
|
||||
<FlightFormCopilot @save="handleFlightEdit" :flight="flight" :copilots="copilots"/>
|
||||
</q-tab-panel>
|
||||
|
||||
<q-tab-panel name="photos">
|
||||
<FlightFormPhotos
|
||||
@save="handleFlightEdit"
|
||||
:flight="flight" />
|
||||
</q-tab-panel>
|
||||
|
||||
<q-tab-panel name="notes">
|
||||
<FlightFormNotes @save="handleFlightEdit" :flight="flight"/>
|
||||
</q-tab-panel>
|
||||
</q-tab-panels>
|
||||
|
||||
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -6,8 +6,10 @@ import {useRouter} from "vue-router";
|
||||
import {usePointsOfInterestStore} from "stores/PointsOfInterest";
|
||||
import {storeToRefs} from "pinia";
|
||||
import {useMeta} from "quasar";
|
||||
import FlightForm from "components/flights/FlightForm.vue";
|
||||
import FlightForm from "components/flights/__FlightForm.vue";
|
||||
import {useAirportsStore} from "stores/Airports";
|
||||
import FlightFormSteps from "components/flights/form/FlightFormSteps.vue";
|
||||
import FlightFormBasic from "components/flights/form/FlightFormBasic.vue";
|
||||
|
||||
const aircraftsStore = useAircraftsStore();
|
||||
const poiStore = usePointsOfInterestStore();
|
||||
@@ -27,26 +29,15 @@ const aircrafts = storeToRefs(aircraftsStore).items;
|
||||
const locations = storeToRefs(poiStore).items;
|
||||
const airports = storeToRefs(airportStore).items;
|
||||
|
||||
const turnPointsCount = ref(1);
|
||||
|
||||
// watch(() => formData.takeOffLocation, (newVal) => {
|
||||
// if (!formData.landingLocation) {
|
||||
// formData.landingLocation = locations.value.find(l => l.id === newVal);
|
||||
// }
|
||||
// })
|
||||
|
||||
const router = useRouter();
|
||||
const handleSave = (formData) => {
|
||||
flightsStore.create(formData).then(() => {
|
||||
router.push({name: "myFlights"});
|
||||
flightsStore.create(formData).then((flight) => {
|
||||
router.push({name: "flightEdit", params: {id: flight.id, step: "copilot"}});
|
||||
});
|
||||
}
|
||||
|
||||
const formStep = ref("basic")
|
||||
|
||||
const removeTurnPoint = (idx) => {
|
||||
formData.turnPoints.splice(idx, 1);
|
||||
turnPointsCount.value--;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -58,7 +49,8 @@ const removeTurnPoint = (idx) => {
|
||||
|
||||
<!-- {{ formData }}-->
|
||||
|
||||
<FlightForm @save="handleSave" :aircrafts="aircrafts" :airports="airports" :locations="locations"/>
|
||||
<FlightFormSteps :disabled="['copilot', 'track', 'photos', 'notes']"/>
|
||||
|
||||
<FlightFormBasic @save="handleSave" :aircrafts="aircrafts" :airports="airports" :locations="locations" />
|
||||
|
||||
</template>
|
||||
|
||||
@@ -20,7 +20,7 @@ const routes = [
|
||||
{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/: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/:step?', name: "flightEdit", component: () => import('pages/profile/forms/FlightEditPage.vue')},
|
||||
{path: 'poi', name: "poi", component: () => import('pages/profile/lists/PointsOfInterestPage.vue')},
|
||||
{path: 'poi/:id', name: "poiDetail", component: () => import('pages/profile/details/PointOfInterestDetailPage.vue')},
|
||||
{path: 'pilots', name: "pilots", component: () => import('pages/profile/lists/SecondPilotPage.vue')},
|
||||
|
||||
+10
-1
@@ -1,7 +1,7 @@
|
||||
import {defineStore} from 'pinia'
|
||||
import {apolloWrapper} from "src/graphql/apollo"
|
||||
import {FLIGHT_DETAIL, FLIGHTS_LIST} from "src/graphql/queries/flights";
|
||||
import {CREATE_FLIGHT} from "src/graphql/mutations/flight";
|
||||
import {EDIT_FLIGHT, CREATE_FLIGHT} from "src/graphql/mutations/flight";
|
||||
|
||||
export const useFlightsStore = defineStore('Flights', {
|
||||
state: () => ({
|
||||
@@ -23,7 +23,16 @@ export const useFlightsStore = defineStore('Flights', {
|
||||
return data.createFlight;
|
||||
})
|
||||
},
|
||||
update(id, formData) {
|
||||
return apolloWrapper.mutate(EDIT_FLIGHT, {id: id, input: formData}).then(({data}) => {
|
||||
console.log("Pridat update ve storu")
|
||||
return data.editFlight;
|
||||
});
|
||||
},
|
||||
fetchDetail(id) {
|
||||
if (typeof id !== "number") {
|
||||
id = parseInt(id);
|
||||
}
|
||||
return apolloWrapper.query(FLIGHT_DETAIL, {id}).then(result => result.data.flight);
|
||||
},
|
||||
fetch() {
|
||||
|
||||
@@ -2086,6 +2086,11 @@ minimist@^1.2.6:
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
|
||||
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
|
||||
|
||||
moment@^2.29.4:
|
||||
version "2.29.4"
|
||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108"
|
||||
integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==
|
||||
|
||||
ms@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||
|
||||
Reference in New Issue
Block a user