Prepracovana prace s datem, lepsi input na datetime
This commit is contained in:
@@ -8,7 +8,7 @@ import {isRequired} from "src/composables/form/rules";
|
||||
|
||||
const formData = reactive({
|
||||
id: null,
|
||||
name: "",
|
||||
callSign: "",
|
||||
model: "",
|
||||
manufacturer: "",
|
||||
description: "",
|
||||
@@ -22,7 +22,7 @@ const props = defineProps({
|
||||
});
|
||||
|
||||
const rules = {
|
||||
name: [
|
||||
callSign: [
|
||||
v => isRequired(v)
|
||||
],
|
||||
};
|
||||
@@ -35,11 +35,10 @@ onMounted(() => {
|
||||
|
||||
<template>
|
||||
<FormWrapper v-model="formData">
|
||||
<q-input type="text" :rules="rules.name" v-model="formData.name" label="Volací značka"/>
|
||||
<q-input type="text" :rules="rules.callSign" v-model="formData.callSign" label="Volací značka"/>
|
||||
<q-input type="text" v-model="formData.model" label="Model"/>
|
||||
<q-input type="text" v-model="formData.manufacturer" label="Výrobce"/>
|
||||
<q-input type="textarea" v-model="formData.description" label="Popis" />
|
||||
<q-file v-model="formData.photo" label="Avatar" />
|
||||
|
||||
</FormWrapper>
|
||||
</template>
|
||||
|
||||
@@ -24,7 +24,7 @@ const props = defineProps({
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label label>
|
||||
{{ aircraft.name }}
|
||||
{{ aircraft.callSign }}
|
||||
</q-item-label>
|
||||
<q-item-label caption>
|
||||
{{ aircraft.model }}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import {formatDate, minutesToHumanReadable} from "src/composables/datetimeUtils";
|
||||
import {formatDatetime, minutesToHumanReadable} from "src/composables/datetimeUtils";
|
||||
import {computed} from "vue";
|
||||
import {useQuasar} from "quasar";
|
||||
|
||||
@@ -37,24 +37,30 @@ const totalFlightTime = computed(() => {
|
||||
<q-img :src="flight.titlePhotoUrl" v-if="flight.titlePhotoUrl">
|
||||
<div class="absolute-bottom text-center text-h6">
|
||||
<router-link :to="{name: 'flightDetail', params:{id: flight.id}}" class="text-white">
|
||||
{{ flight.takeoffAirport.name }} - {{ flight.landingAirport.name }}
|
||||
{{ flight.name }}
|
||||
<!-- {{ flight.takeoffAirport.name }} - {{ flight.landingAirport.name }}-->
|
||||
</router-link>
|
||||
</div>
|
||||
</q-img>
|
||||
<q-card-section>
|
||||
<div class="text-h6" v-if="!flight.titlePhotoUrl">
|
||||
<router-link :to="{name: 'flightDetail', params:{id: flight.id}}">
|
||||
{{ flight.takeoffAirport.name }} - {{ flight.landingAirport.name }}
|
||||
<template v-if="flight.name">
|
||||
{{ flight.name }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ flight.takeoffAirport.icaoCode }} - {{ flight.landingAirport.icaoCode }}
|
||||
</template>
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="text-subtitle2">{{ minutesToHumanReadable(flight.durationMinCalculated) }}</div>
|
||||
<div class="text-secondary">
|
||||
<q-icon name="today"/>
|
||||
{{ formatDate(flight.takeoffDatetime) }}
|
||||
{{ formatDatetime(flight.takeoffDatetime) }}
|
||||
</div>
|
||||
<div class="text-secondary">
|
||||
<q-icon name="plane"/>
|
||||
{{ flight.aircraft.name }}
|
||||
{{ flight.aircraft.callSign }}
|
||||
</div>
|
||||
</q-card-section>
|
||||
|
||||
|
||||
@@ -1,88 +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";
|
||||
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>
|
||||
@@ -3,7 +3,9 @@ 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";
|
||||
import {formatDatetime, parseDate} from "src/composables/datetimeUtils";
|
||||
import AnotherDatetimePicker from "components/inputs/AnotherDatetimePicker.vue";
|
||||
import AirportLocationInput from "components/inputs/AirportLocationInput.vue";
|
||||
|
||||
const props = defineProps({
|
||||
aircrafts: {
|
||||
@@ -63,16 +65,16 @@ onMounted(() => {
|
||||
<template>
|
||||
<FormWrapper v-model="formData" v-if="isMounted">
|
||||
|
||||
{{ formData }}
|
||||
<hr>
|
||||
{{ flight }}
|
||||
<hr>
|
||||
<!-- {{ formData }}-->
|
||||
<!-- <hr>-->
|
||||
<!-- {{ flight }}-->
|
||||
<!-- <hr>-->
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<q-select
|
||||
v-model="formData.aircraftId"
|
||||
option-label="name"
|
||||
option-label="callSign"
|
||||
option-value="id"
|
||||
emit-value
|
||||
map-options
|
||||
@@ -89,46 +91,22 @@ onMounted(() => {
|
||||
|
||||
<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 class="col col-12 col-md-6">
|
||||
<!-- <h3>Vzlet</h3>-->
|
||||
<AnotherDatetimePicker v-model="formData.takeoffDatetime" label="Datum a čas vzletu: "/>
|
||||
<AirportLocationInput v-model="formData.takeoffAirportId" :airports="airports" label="Letiště vzletu" takeoff/>
|
||||
</div>
|
||||
<div class="col" data-if="!!formData.takeoffDatetime && !!formData.takeoffAirportId">
|
||||
<div class="col col-12 col-md-6" v-if="!!formData.takeoffDatetime && !!formData.takeoffAirportId">
|
||||
|
||||
<h3>Přistání</h3>
|
||||
<!-- <h3>Přistání</h3>-->
|
||||
<AnotherDatetimePicker v-model="formData.landingDatetime" label="Datum a čas přistání"/>
|
||||
<AirportLocationInput v-model="formData.landingAirportId" :airports="airports" label="Letiště přistání" landing/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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 class="row">
|
||||
<div class="col">
|
||||
<q-checkbox v-model="formData.solo" label="Solo?"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup>
|
||||
import {reactive} from "vue";
|
||||
import {onMounted, reactive} from "vue";
|
||||
import FormWrapper from "components/FormWrapper.vue";
|
||||
import {setDefaultData} from "src/composables/form/formHandling";
|
||||
|
||||
const props = defineProps({
|
||||
flight: {
|
||||
@@ -18,6 +19,12 @@ const formData = reactive({
|
||||
withInstructor: false,
|
||||
copilotId: null,
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
if (props.flight) {
|
||||
setDefaultData(formData, props.flight);
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script setup>
|
||||
import {formatDate, parseDate} from "src/composables/datetimeUtils";
|
||||
import FormWrapper from "components/FormWrapper.vue";
|
||||
import {reactive} from "vue";
|
||||
|
||||
@@ -10,17 +9,42 @@ const props = defineProps({
|
||||
}
|
||||
}
|
||||
)
|
||||
const formData = reactive({});
|
||||
const testDate = "2023-06-09T22:00:00.000+02:00";
|
||||
const formData = reactive({
|
||||
photos: []
|
||||
});
|
||||
|
||||
const deletePhoto = (id) => {
|
||||
console.log(id);
|
||||
};
|
||||
|
||||
const openEdit = (photo) => {
|
||||
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FormWrapper v-model="formData">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-4 col-lg-3" v-for="photo in flight.photos" :key="photo.id">
|
||||
<q-card class="q-my-sm q-mx-sm">
|
||||
<q-img src="https://cdn.quasar.dev/img/parallax2.jpg">
|
||||
<div class="absolute-bottom">
|
||||
<div class="text-h6">{{ photo.name }} jmeno</div>
|
||||
<div class="text-subtitle2">{{ photo.description }}nejaky popis</div>
|
||||
</div>
|
||||
</q-img>
|
||||
|
||||
<q-banner>
|
||||
Test date: {{testDate}} => {{ parseDate(testDate) }}
|
||||
<q-card-actions>
|
||||
<q-btn flat icon="edit" color="blue" @click="openEdit(photo)"></q-btn>
|
||||
<q-btn flat icon="delete" color="red" @click="deletePhoto(photo.id)"></q-btn>
|
||||
|
||||
</q-banner>
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<q-file multiple v-model="formData.photos" label="Fotky"/>
|
||||
</FormWrapper>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
<script setup>
|
||||
import {onMounted, ref} from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: [Number]
|
||||
},
|
||||
airports: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
takeoff: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
landing: {
|
||||
type: Boolean,
|
||||
required: false
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
const model = ref(null);
|
||||
const emitModelValue = (val) => {
|
||||
emit("update:modelValue", val);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
model.value = props.modelValue;
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<q-select
|
||||
v-model="model"
|
||||
emit-value
|
||||
map-options
|
||||
option-label="name"
|
||||
option-value="id"
|
||||
clearable
|
||||
:options="airports"
|
||||
@input="emitModelValue"
|
||||
>
|
||||
<template #prepend v-if="takeoff">
|
||||
<q-icon name="flight_takeoff"/>
|
||||
</template>
|
||||
|
||||
<template #append v-if="landing">
|
||||
<q-icon name="flight_land"/>
|
||||
</template>
|
||||
</q-select>
|
||||
</template>
|
||||
@@ -0,0 +1,111 @@
|
||||
<script setup>
|
||||
import {computed, onMounted, ref} from "vue";
|
||||
import {FORMAT_DATETIME_LONG, FORMAT_ISO, formatDatetime, parseDate} from "src/composables/datetimeUtils";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: [String, Date]
|
||||
},
|
||||
mask: {
|
||||
type: [String, Boolean],
|
||||
required: false,
|
||||
default: FORMAT_DATETIME_LONG
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
let dateModel = ref("");
|
||||
let timeModel = ref("");
|
||||
|
||||
let originalTime, originalDate;
|
||||
onMounted(() => {
|
||||
let emitDefaultValue = false;
|
||||
if (props.modelValue) {
|
||||
let modelVal = props.modelValue;
|
||||
if (typeof modelVal === 'string') {
|
||||
modelVal = parseDate(props.modelValue);
|
||||
emitDefaultValue = true;
|
||||
}
|
||||
|
||||
dateModel.value = formatDatetime(modelVal, "YYYY-MM-DD");
|
||||
timeModel.value = formatDatetime(modelVal, "HH:mm:ss");
|
||||
}
|
||||
|
||||
setOriginalValues();
|
||||
if (emitDefaultValue) {
|
||||
emitModelUpdate();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
const publicModel = computed(() => {
|
||||
if (!dateModel.value) {
|
||||
return "";
|
||||
}
|
||||
const dateObject = parseDate(model.value, FORMAT_ISO);
|
||||
return formatDatetime(dateObject, props.mask);
|
||||
});
|
||||
|
||||
const popupProxy = ref(null);
|
||||
|
||||
const model = computed(() => dateModel.value + "T" + timeModel.value);
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
const emitModelUpdate = () => {
|
||||
const val = parseDate(model.value);
|
||||
emit("update:modelValue", val);
|
||||
popupProxy.value.hide();
|
||||
}
|
||||
|
||||
const containsAnyOf = (needles, haystack) => {
|
||||
for (const i in needles) {
|
||||
if (haystack.includes(needles[i])) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const showDatePicker = computed(() => containsAnyOf(["YY", "M", "D"], props.mask));
|
||||
const showTimePicker = computed(() => containsAnyOf(["H", "h", "m"], props.mask));
|
||||
|
||||
const setOriginalValues = () => {
|
||||
originalDate = dateModel.value;
|
||||
originalTime = timeModel.value;
|
||||
}
|
||||
|
||||
const cancelSelection = () => {
|
||||
dateModel.value = originalDate;
|
||||
timeModel.value = originalTime;
|
||||
popupProxy.value.hide();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<q-input readonly v-model="publicModel">
|
||||
<template #append>
|
||||
<q-icon name="event"/>
|
||||
</template>
|
||||
|
||||
<template #default>
|
||||
<q-popup-proxy ref="popupProxy" @hide="emitModelUpdate" @show="setOriginalValues">
|
||||
<div>
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-6" v-if="showDatePicker">
|
||||
<q-date square v-model="dateModel" :today-btn="true" flat mask="YYYY-MM-DD"/>
|
||||
</div>
|
||||
<div class="col-12 col-md-6" v-if="showTimePicker">
|
||||
<q-time square v-model="timeModel" format24h flat mask="HH:mm"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row q-mb-sm">
|
||||
<div class="col">
|
||||
<q-btn color="secondary" class="q-ml-sm" @click="cancelSelection">Zrušit</q-btn>
|
||||
</div>
|
||||
<div class="col text-right">
|
||||
<q-btn color="primary" class="q-mr-sm" @click="emitModelUpdate">Nastavit</q-btn>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</q-popup-proxy>
|
||||
</template>
|
||||
</q-input>
|
||||
</template>
|
||||
@@ -1,10 +1,17 @@
|
||||
import { date } from "quasar";
|
||||
|
||||
export const FORMAT_ISO = "YYYY-MM-DDTHH:mm:ss";
|
||||
export const FORMAT_DATETIME_LONG = "DD. MM. YYYY, HH:mm";
|
||||
export const FORMAT_DATE = "DD. MM. YYYY";
|
||||
export const FORMAT_TIME = "HH:mm"
|
||||
|
||||
export const minutesToHumanReadable = (totalMinutes) => {
|
||||
const hours = Math.round(totalMinutes / 60);
|
||||
const minutes = totalMinutes % 60;
|
||||
return `${hours}h ${minutes}min`
|
||||
}
|
||||
|
||||
export const formatDate = (date, format = "DD. MM. YYYY") => {
|
||||
export const formatDatetimeNative = (date, format = "DD. MM. YYYY") => {
|
||||
if (typeof date === 'string') {
|
||||
date = parseDate(date);
|
||||
}
|
||||
@@ -12,27 +19,37 @@ export const formatDate = (date, format = "DD. MM. YYYY") => {
|
||||
return format
|
||||
.replace("DD", date.getDate().toString())
|
||||
.replace("MM", (date.getMonth() + 1).toString())
|
||||
.replace("YYYY", date.getFullYear().toString());
|
||||
.replace("YYYY", date.getFullYear().toString())
|
||||
|
||||
// todo: doplnit pocatecni nuly
|
||||
.replace("hh", date.getHours().toString())
|
||||
.replace("mm", date.getMinutes().toString())
|
||||
.replace("ss", date.getSeconds().toString());
|
||||
}
|
||||
|
||||
|
||||
export const parseDate = (datetime) => {
|
||||
export const parseDateNative = (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;
|
||||
return new Date(timestamp);
|
||||
}
|
||||
|
||||
|
||||
export const parseDate = (datetimeStr, format = FORMAT_ISO) => {
|
||||
return date.extractDate(datetimeStr, format)
|
||||
}
|
||||
|
||||
export const formatDatetime = (datetime, format = FORMAT_DATETIME_LONG) => {
|
||||
console.log("orignal ", datetime);
|
||||
if (typeof datetime === 'string') {
|
||||
datetime = parseDate(datetime);
|
||||
}
|
||||
|
||||
console.log(datetime);
|
||||
|
||||
return date.formatDate(datetime, format);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {FORMAT_DATE, formatDatetime} from "src/composables/datetimeUtils";
|
||||
|
||||
export const flightName = (flight) => {
|
||||
const date = flight.takeoffDatetime;//.getDay() + ". " + flight.takeoffDatetime.getMonth() + ". " + flight.takeoffDatetime.getFullYear();
|
||||
const date = formatDatetime(flight.takeoffDatetime, FORMAT_DATE);
|
||||
if (flight.name !== '') {
|
||||
return `${flight.name} (${date}})`
|
||||
return `${flight.name} (${date})`
|
||||
} else {
|
||||
return `${flight.takeoffAirport.icaoCode} - ${flight.takeoffAirport.icaoCode} (${date})`;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import gql from "graphql-tag";
|
||||
export const FRAGMENT_AIRCRAFT_FULL_INFO = gql`
|
||||
fragment aircraftFullInfo on Aircraft {
|
||||
id
|
||||
name
|
||||
callSign
|
||||
model
|
||||
description
|
||||
manufacturer
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import gql from "graphql-tag";
|
||||
|
||||
export const FRAGMENT_AIRPORT_INFO = gql`
|
||||
fragment airportInfo on Airport {
|
||||
name
|
||||
icaoCode
|
||||
}
|
||||
`;
|
||||
@@ -1,4 +1,6 @@
|
||||
import gql from "graphql-tag";
|
||||
import {FRAGMENT_AIRCRAFT_FULL_INFO} from "src/graphql/fragments/aircraft";
|
||||
import {FRAGMENT_AIRPORT_INFO} from "src/graphql/fragments/airport";
|
||||
|
||||
export const FRAGMENT_FLIGHT_FULL_INFO = gql`
|
||||
fragment flightFullInfo on Flight {
|
||||
@@ -9,24 +11,61 @@ fragment flightFullInfo on Flight {
|
||||
landingDatetime
|
||||
durationMinCalculated
|
||||
solo
|
||||
withInstructor
|
||||
|
||||
takeoffAirport {
|
||||
name
|
||||
icaoCode
|
||||
...airportInfo
|
||||
}
|
||||
|
||||
landingAirport {
|
||||
name
|
||||
icaoCode
|
||||
...airportInfo
|
||||
}
|
||||
|
||||
aircraft {
|
||||
...aircraftFullInfo
|
||||
}
|
||||
|
||||
|
||||
photos {
|
||||
url
|
||||
thumbnailUrl
|
||||
name
|
||||
description
|
||||
}
|
||||
}
|
||||
|
||||
${FRAGMENT_AIRCRAFT_FULL_INFO}
|
||||
${FRAGMENT_AIRPORT_INFO}
|
||||
`;
|
||||
|
||||
export const FRAGMENT_FLIGHT_LIST_INFO = gql`
|
||||
fragment flightListInfo on Flight {
|
||||
id
|
||||
name
|
||||
photoUrl
|
||||
takeoffDatetime
|
||||
landingDatetime
|
||||
durationMinCalculated
|
||||
|
||||
coverPhoto {
|
||||
thumbnailUrl
|
||||
}
|
||||
|
||||
takeoffAirport {
|
||||
...airportInfo
|
||||
}
|
||||
|
||||
landingAirport {
|
||||
...airportInfo
|
||||
}
|
||||
|
||||
aircraft {
|
||||
...aircraftFullInfo
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
${FRAGMENT_AIRCRAFT_FULL_INFO}
|
||||
${FRAGMENT_AIRPORT_INFO}
|
||||
`
|
||||
|
||||
export const FRAGMENT_FLIGHT_EDIT_IDS = gql`
|
||||
fragment flightEditIds on Flight {
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import gql from "graphql-tag";
|
||||
import {FRAGMENT_FLIGHT_FULL_INFO} from "src/graphql/fragments/flight";
|
||||
|
||||
export const UPLOAD_PHOTO = gql`
|
||||
mutation uploadPhoto($input: UploadPhotoInput!) {
|
||||
uploadPhoto(input: $input) {
|
||||
url
|
||||
thumbnailUrl
|
||||
filename
|
||||
name
|
||||
description
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const EDIT_FLIGHT = gql`
|
||||
mutation editFlight($id: Int!, $input: EditFlightInput!) {
|
||||
editFlight(id: $id, input: $input) {
|
||||
...flightFullInfo
|
||||
}
|
||||
}
|
||||
|
||||
${FRAGMENT_FLIGHT_FULL_INFO}
|
||||
`;
|
||||
@@ -1,13 +1,13 @@
|
||||
import gql from "graphql-tag";
|
||||
import {FRAGMENT_FLIGHT_EDIT_IDS, FRAGMENT_FLIGHT_FULL_INFO} from "src/graphql/fragments/flight";
|
||||
import {FRAGMENT_FLIGHT_EDIT_IDS, FRAGMENT_FLIGHT_FULL_INFO, FRAGMENT_FLIGHT_LIST_INFO} from "src/graphql/fragments/flight";
|
||||
|
||||
export const FLIGHTS_LIST = gql`
|
||||
query FlightsList {
|
||||
flights {
|
||||
...flightFullInfo
|
||||
...flightListInfo
|
||||
}
|
||||
}
|
||||
${FRAGMENT_FLIGHT_FULL_INFO}
|
||||
${FRAGMENT_FLIGHT_LIST_INFO}
|
||||
`;
|
||||
|
||||
export const FLIGHT_DETAIL = gql`
|
||||
|
||||
@@ -42,10 +42,10 @@ const confirmDelete = (id) => {
|
||||
<template v-else>
|
||||
<q-breadcrumbs>
|
||||
<q-breadcrumbs-el :to="{name: 'aircrafts'}">Letadla</q-breadcrumbs-el>
|
||||
<q-breadcrumbs-el>{{ aircraft.name }}</q-breadcrumbs-el>
|
||||
<q-breadcrumbs-el>{{ aircraft.callSign }}</q-breadcrumbs-el>
|
||||
</q-breadcrumbs>
|
||||
|
||||
<h1>{{ aircraft.name }}</h1>
|
||||
<h1>{{ aircraft.callSign }}</h1>
|
||||
|
||||
{{ aircraft.photoUrl }}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import {useFlightsStore} from "stores/Flights";
|
||||
import {useRoute} from "vue-router";
|
||||
import {onMounted, reactive, ref} from "vue";
|
||||
import {formatDate, minutesToHumanReadable} from "src/composables/datetimeUtils";
|
||||
import {formatDatetime, minutesToHumanReadable} from "src/composables/datetimeUtils";
|
||||
import FlightDetailSkeleton from "components/flights/FlightDetailSkeleton.vue";
|
||||
import AddFAB from "components/AddFAB.vue";
|
||||
|
||||
@@ -27,7 +27,7 @@ onMounted(() => {
|
||||
<article v-html="flight.description" />
|
||||
|
||||
<q-icon name="calendar"/>
|
||||
{{ formatDate(flight.takeoffDatetime) }}
|
||||
{{ formatDatetime(flight.takeoffDatetime) }}
|
||||
{{ minutesToHumanReadable(flight.durationMinCalculated) }}
|
||||
|
||||
<!-- <q-icon name="weather"/>-->
|
||||
|
||||
@@ -38,7 +38,7 @@ onMounted(() => {
|
||||
<q-skeleton v-if="!loadedAircraft"/>
|
||||
|
||||
<template v-if="loadedAircraft">
|
||||
<h1>Editace letadla {{ loadedAircraft.name }}</h1>
|
||||
<h1>Editace letadla {{ loadedAircraft.callSign }}</h1>
|
||||
|
||||
<AircraftForm @save="handleSave" :default-values="loadedAircraft" :is-request-pending="isRequestPending"/>
|
||||
</template>
|
||||
|
||||
@@ -14,6 +14,7 @@ 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";
|
||||
import {usePhotosStore} from "stores/Photo";
|
||||
|
||||
useMeta({title: "Editace letu"});
|
||||
|
||||
@@ -45,7 +46,6 @@ onMounted(() => {
|
||||
|
||||
useAircraftsStore().fetch();
|
||||
usePointsOfInterestStore().fetch();
|
||||
useFlightsStore().fetch();
|
||||
useAirportsStore().fetch();
|
||||
useSecondPilotsStore().fetch();
|
||||
|
||||
@@ -59,6 +59,22 @@ const handleFlightEdit = (data) => {
|
||||
fetchFlight(flightId);
|
||||
})
|
||||
}
|
||||
|
||||
const handlePhotosUpload = (data) => {
|
||||
|
||||
console.log(data);
|
||||
|
||||
const promises = [];
|
||||
for (const photo of data.photos) {
|
||||
promises.push(usePhotosStore().upload(flightId, {photo}));
|
||||
}
|
||||
|
||||
Promise.all(promises).then(() => {
|
||||
$q.notify({message: "Uspesne nahrano", position: "top", color: "positive"});
|
||||
|
||||
fetchFlight(flightId);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -90,8 +106,9 @@ const handleFlightEdit = (data) => {
|
||||
|
||||
<q-tab-panel name="photos">
|
||||
<FlightFormPhotos
|
||||
@save="handleFlightEdit"
|
||||
:flight="flight" />
|
||||
:flight="flight"
|
||||
@save="handlePhotosUpload"
|
||||
/>
|
||||
</q-tab-panel>
|
||||
|
||||
<q-tab-panel name="notes">
|
||||
|
||||
@@ -6,7 +6,6 @@ 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 {useAirportsStore} from "stores/Airports";
|
||||
import FlightFormSteps from "components/flights/form/FlightFormSteps.vue";
|
||||
import FlightFormBasic from "components/flights/form/FlightFormBasic.vue";
|
||||
|
||||
@@ -2,7 +2,6 @@ import {defineStore} from 'pinia'
|
||||
import {apolloWrapper} from "src/graphql/apollo";
|
||||
import {AIRCRAFT_DETAIL, AIRCRAFTS_LIST} from "src/graphql/queries/aircrafts";
|
||||
import {CREATE_AIRCRAFT, DELETE_AIRCRAFT, EDIT_AIRCRAFT} from "src/graphql/mutations/aircrafts";
|
||||
import {reactive} from "vue";
|
||||
|
||||
export const useAircraftsStore = defineStore('Aircrafts', {
|
||||
state: () => ({
|
||||
|
||||
@@ -36,6 +36,7 @@ export const useFlightsStore = defineStore('Flights', {
|
||||
return apolloWrapper.query(FLIGHT_DETAIL, {id}).then(result => result.data.flight);
|
||||
},
|
||||
fetch() {
|
||||
console.log("flights fetch");
|
||||
return apolloWrapper
|
||||
.query(FLIGHTS_LIST)
|
||||
.then((result) => {
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import {defineStore} from "pinia";
|
||||
import {apolloWrapper} from "src/graphql/apollo";
|
||||
import {UPLOAD_PHOTO} from "src/graphql/mutations/photo";
|
||||
|
||||
export const usePhotosStore = defineStore('photos', {
|
||||
|
||||
actions: {
|
||||
upload(flightId, formData) {
|
||||
if (!flightId) {
|
||||
console.error("No flightId specified for photo upload! Aborting");
|
||||
return;
|
||||
}
|
||||
|
||||
formData.flightId = flightId;
|
||||
|
||||
if (!formData.name) {
|
||||
formData.name = "";
|
||||
}
|
||||
|
||||
if (!formData.description) {
|
||||
formData.description = "";
|
||||
}
|
||||
return apolloWrapper.mutate(UPLOAD_PHOTO, {input: formData}).then(({data}) => {
|
||||
return data.uploadPhoto;
|
||||
})
|
||||
},
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user