Prace s mapou, refresh tokenu (tak nejak), uprava vypisu POI, pridani breadcrumbs
This commit is contained in:
+2
-1
@@ -21,7 +21,8 @@
|
|||||||
"pinia": "^2.0.33",
|
"pinia": "^2.0.33",
|
||||||
"quasar": "^2.12.0",
|
"quasar": "^2.12.0",
|
||||||
"vue": "^3.2.47",
|
"vue": "^3.2.47",
|
||||||
"vue-router": "^4.1.6"
|
"vue-router": "^4.1.6",
|
||||||
|
"vue3-google-map": "^0.15.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@quasar/app-vite": "^1.3.0",
|
"@quasar/app-vite": "^1.3.0",
|
||||||
|
|||||||
+7
-1
@@ -29,7 +29,9 @@ module.exports = configure(function (/* ctx */) {
|
|||||||
// app boot file (/src/boot)
|
// app boot file (/src/boot)
|
||||||
// --> boot files are part of "main.js"
|
// --> boot files are part of "main.js"
|
||||||
// https://v2.quasar.dev/quasar-cli/boot-files
|
// https://v2.quasar.dev/quasar-cli/boot-files
|
||||||
boot: [],
|
boot: [
|
||||||
|
"axios",
|
||||||
|
],
|
||||||
|
|
||||||
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css
|
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css
|
||||||
css: [
|
css: [
|
||||||
@@ -57,6 +59,10 @@ module.exports = configure(function (/* ctx */) {
|
|||||||
node: 'node18'
|
node: 'node18'
|
||||||
},
|
},
|
||||||
|
|
||||||
|
env: {
|
||||||
|
GOOGLE_MAPS_API_KEY: "AIzaSyAfTU1VntF9rp2D9TR4CFXIMALF5GLgsio"
|
||||||
|
},
|
||||||
|
|
||||||
vueRouterMode: 'history', // available values: 'hash', 'history'
|
vueRouterMode: 'history', // available values: 'hash', 'history'
|
||||||
// vueRouterBase,
|
// vueRouterBase,
|
||||||
// vueDevtools,
|
// vueDevtools,
|
||||||
|
|||||||
+35
-2
@@ -1,7 +1,8 @@
|
|||||||
import {boot} from 'quasar/wrappers'
|
import {boot} from 'quasar/wrappers'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
import {useUserStore} from "stores/User";
|
||||||
|
import {useRouter} from "vue-router";
|
||||||
|
|
||||||
const accessToken = localStorage.getItem("accessToken")
|
|
||||||
const api = axios.create({
|
const api = axios.create({
|
||||||
baseURL: 'http://localhost:8000',
|
baseURL: 'http://localhost:8000',
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
@@ -12,10 +13,42 @@ const api = axios.create({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
api.interceptors.request.use((config) => {
|
||||||
|
const token = useUserStore().getAccessToken();
|
||||||
|
if (token) {
|
||||||
|
config.headers['Authorization'] = `Bearer ${token}`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.url === '/refresh') {
|
||||||
|
delete config.headers['Authorization'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
})
|
||||||
|
|
||||||
|
// Response interceptor for API calls
|
||||||
|
api.interceptors.response.use((response) => {
|
||||||
|
return response
|
||||||
|
}, async function (error) {
|
||||||
|
const originalRequest = error.config;
|
||||||
|
if (error.response.status === 401 && !originalRequest._retry) {
|
||||||
|
originalRequest._retry = true;
|
||||||
|
const newToken = await useUserStore().refreshAccessToken().catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
useRouter().push({to: "login"});
|
||||||
|
return Promise.reject("Logged out");
|
||||||
|
})
|
||||||
|
api.defaults.headers.common['Authorization'] = 'Bearer ' + newToken;
|
||||||
|
return api(originalRequest);
|
||||||
|
}
|
||||||
|
return Promise.reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
export default boot(({app}) => {
|
export default boot(({app}) => {
|
||||||
// for use inside Vue files (Options API) through this.$axios and this.$api
|
// for use inside Vue files (Options API) through this.$axios and this.$api
|
||||||
|
|
||||||
app.config.globalProperties.$axios = axios
|
// app.config.globalProperties.$axios = axios
|
||||||
// ^ ^ ^ this will allow you to use this.$axios (for Vue Options API form)
|
// ^ ^ ^ this will allow you to use this.$axios (for Vue Options API form)
|
||||||
// so you won't necessarily have to import axios in each vue file
|
// so you won't necessarily have to import axios in each vue file
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ const props = defineProps({
|
|||||||
type: [Object, Boolean],
|
type: [Object, Boolean],
|
||||||
required: false,
|
required: false,
|
||||||
default: () => ({
|
default: () => ({
|
||||||
label: "Save",
|
label: "Uložit",
|
||||||
icon: "save",
|
icon: "save",
|
||||||
align: "left"
|
align: "left"
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
<script setup>
|
||||||
|
import MapWrapper from "components/MapWrapper.vue";
|
||||||
|
import {Marker, Polyline} from "vue3-google-map";
|
||||||
|
import {computed} from "vue";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
gpsLatitude: {
|
||||||
|
type: Number,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
gpsLongitude: {
|
||||||
|
type: Number,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
zoom: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: 10
|
||||||
|
},
|
||||||
|
markers: {
|
||||||
|
type: Array,
|
||||||
|
required: false,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
showAsTrack: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const mapCenter = computed(() => {
|
||||||
|
const north = Math.max(...props.markers.map(m => m.lng));
|
||||||
|
const south = Math.min(...props.markers.map(m => m.lng));
|
||||||
|
const east = Math.max(...props.markers.map(m => m.lat));
|
||||||
|
const west = Math.min(...props.markers.map(m => m.lat));
|
||||||
|
|
||||||
|
const longitudeDiff = north - south;
|
||||||
|
const latitudeDiff = east - west;
|
||||||
|
|
||||||
|
return {
|
||||||
|
lat: west + latitudeDiff / 2,
|
||||||
|
lng: south + longitudeDiff / 2
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<MapWrapper v-bind="$attrs" :center="mapCenter" :zoom="zoom">
|
||||||
|
<template v-if="!showAsTrack">
|
||||||
|
<Marker v-for="(marker, idx) in markers" :key="idx" :options="{position: marker}"/>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<Polyline :options="{path: markers, geodesic: true, strokeColor: '#ff0000', strokeOpacity: 0.3, strokeWeight: 5}"/>
|
||||||
|
</template>
|
||||||
|
</MapWrapper>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<script setup>
|
||||||
|
import {GoogleMap} from "vue3-google-map";
|
||||||
|
|
||||||
|
const key = process.env.GOOGLE_MAPS_API_KEY;
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<GoogleMap :api-key="key" v-bind="$attrs">
|
||||||
|
<slot />
|
||||||
|
</GoogleMap>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<script setup></script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
edit form
|
||||||
|
</template>
|
||||||
@@ -12,13 +12,13 @@ const props = defineProps({
|
|||||||
<template>
|
<template>
|
||||||
<q-list bordered separator>
|
<q-list bordered separator>
|
||||||
<q-item clickable v-ripple v-for="pilot of items" :key="pilot">
|
<q-item clickable v-ripple v-for="pilot of items" :key="pilot">
|
||||||
<q-item-label>
|
<q-item-section avatar>
|
||||||
|
<q-avatar icon="person" color="primary" text-color="white"/>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section>
|
||||||
<router-link :to="{name: 'pilotDetail', params: {id: pilot.id }}">
|
<router-link :to="{name: 'pilotDetail', params: {id: pilot.id }}">
|
||||||
{{ pilot.name }}
|
{{ pilot.name }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</q-item-label>
|
|
||||||
<q-item-section>
|
|
||||||
section
|
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
|
|||||||
@@ -13,21 +13,38 @@ const props = defineProps({
|
|||||||
<template>
|
<template>
|
||||||
<q-list bordered separator>
|
<q-list bordered separator>
|
||||||
<q-item>
|
<q-item>
|
||||||
<q-item-section avatar>
|
|
||||||
<q-icon name="flight_takeoff"/>
|
<q-list>
|
||||||
</q-item-section>
|
<q-item>
|
||||||
<q-item-section>
|
<q-item-section avatar>
|
||||||
{{ flight.takeoffAirport.name }}
|
<q-icon name="flight_takeoff"/>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
<q-item-section>
|
||||||
<q-item>
|
{{ flight.takeoffAirport.name }}
|
||||||
<q-item-section avatar>
|
</q-item-section>
|
||||||
<q-icon name="flight_land"/>
|
</q-item>
|
||||||
</q-item-section>
|
|
||||||
<q-item-section>
|
<q-item v-for="poi in flight.track" :key="poi.id">
|
||||||
{{ flight.landingAirport.name }}
|
<q-item-section avatar>
|
||||||
</q-item-section>
|
<q-icon name="place"/>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section>
|
||||||
|
{{ poi.pointOfInterest.name }}
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
|
||||||
|
<q-item>
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-icon name="flight_land"/>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section>
|
||||||
|
{{ flight.landingAirport.name }}
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</q-list>
|
||||||
</q-item>
|
</q-item>
|
||||||
|
|
||||||
|
|
||||||
<q-item>
|
<q-item>
|
||||||
<q-item-section avatar>
|
<q-item-section avatar>
|
||||||
<q-icon name="event"/>
|
<q-icon name="event"/>
|
||||||
@@ -36,14 +53,6 @@ const props = defineProps({
|
|||||||
{{ formatDatetime(flight.takeoffDatetime) }}
|
{{ formatDatetime(flight.takeoffDatetime) }}
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item>
|
|
||||||
<q-item-section avatar>
|
|
||||||
<q-icon name="thermostat"/>
|
|
||||||
</q-item-section>
|
|
||||||
<q-item-section>
|
|
||||||
Clouds: few, Wind: 220/12kt, Temp: 12°C
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
<q-item>
|
<q-item>
|
||||||
<q-item-section avatar>
|
<q-item-section avatar>
|
||||||
<q-icon name="schedule"/>
|
<q-icon name="schedule"/>
|
||||||
@@ -52,12 +61,22 @@ const props = defineProps({
|
|||||||
{{ minutesToHumanReadable(flight.durationMinCalculated) }}
|
{{ minutesToHumanReadable(flight.durationMinCalculated) }}
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-icon name="thermostat"/>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section>
|
||||||
|
Clouds: few, Wind: 220/12kt, Temp: 12°C
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
<q-item v-if="!flight.solo">
|
<q-item v-if="!flight.solo">
|
||||||
<q-item-section avatar>
|
<q-item-section avatar>
|
||||||
<q-icon name="people"/>
|
<q-icon name="people"/>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
{{ flight.copilot.name }}
|
<router-link :to="{name: 'pilotDetail', params: {id: flight.copilot.id}}">
|
||||||
|
{{ flight.copilot.name }}
|
||||||
|
</router-link>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
|
|||||||
@@ -12,21 +12,13 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const totalFlightTime = computed(() => {
|
|
||||||
return props.items.reduce((acc, flight) => acc + flight.flightDurationMinutes, 0);
|
|
||||||
})
|
|
||||||
|
|
||||||
const emit = defineEmits(["delete"]);
|
|
||||||
const deleteFlight = (flightId) => {
|
|
||||||
emit("delete", flightId);
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-md-4 col-lg-3" v-for="flight of items" :key="flight">
|
<div class="col-12 col-md-6 col-lg-4" 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.coverPhoto.thumbnailUrl" v-if="flight.coverPhoto">
|
<q-img :src="flight.coverPhoto.thumbnailUrl" v-if="flight.coverPhoto">
|
||||||
<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">
|
||||||
@@ -50,16 +42,7 @@ const deleteFlight = (flightId) => {
|
|||||||
{{ flight.aircraft.callSign }}
|
{{ flight.aircraft.callSign }}
|
||||||
</div>
|
</div>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
|
|
||||||
<q-separator/>
|
|
||||||
|
|
||||||
<q-card-actions align="right">
|
|
||||||
<q-btn flat icon="edit" :to="{name: 'flightEdit', params:{id: flight.id}}"></q-btn>
|
|
||||||
<q-btn flat icon="delete" color="red" @click="deleteFlight(flight.id)"></q-btn>
|
|
||||||
</q-card-actions>
|
|
||||||
</q-card>
|
</q-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Total: {{ minutesToHumanReadable(totalFlightTime) }}
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import FormWrapper from "components/FormWrapper.vue";
|
import FormWrapper from "components/FormWrapper.vue";
|
||||||
import {reactive} from "vue";
|
import {reactive} from "vue";
|
||||||
|
import ComboboxInput from "components/inputs/ComboboxInput.vue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
flight: {
|
flight: {
|
||||||
@@ -15,7 +16,18 @@ const formData = reactive({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<FormWrapper v-model="formData">
|
<FormWrapper v-model="formData" :save-button='{
|
||||||
<q-file multiple v-model="formData.photos" label="Fotky"/>
|
label: "Nahrát",
|
||||||
|
icon: "upload",
|
||||||
|
align: "left"
|
||||||
|
}'>
|
||||||
|
<q-file
|
||||||
|
class="q-my-lg"
|
||||||
|
outlined
|
||||||
|
standout
|
||||||
|
multiple
|
||||||
|
v-model="formData.photos"
|
||||||
|
label="Vybert fotky k nahrání"
|
||||||
|
/>
|
||||||
</FormWrapper>
|
</FormWrapper>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,5 +1,67 @@
|
|||||||
<script setup></script>
|
<script setup>
|
||||||
|
import {Marker} from "vue3-google-map";
|
||||||
|
import {onMounted, ref} from "vue";
|
||||||
|
import axios from "axios";
|
||||||
|
import MapWrapper from "components/MapWrapper.vue";
|
||||||
|
|
||||||
|
const center = ref({
|
||||||
|
lat: 50.1314,
|
||||||
|
lng: 14.5256
|
||||||
|
});
|
||||||
|
const zoom = ref(10);
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
gpsLatitude: {
|
||||||
|
type: Number,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
gpsLongitude: {
|
||||||
|
type: Number,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
required: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const emits = defineEmits(["update:gpsLatitude", "update:gpsLongitude", "notFound"]);
|
||||||
|
const emitModels = (lat, lng) => {
|
||||||
|
emits("update:gpsLatitude", lat);
|
||||||
|
emits("update:gpsLongitude", lng);
|
||||||
|
}
|
||||||
|
const markerDropped = (position) => {
|
||||||
|
emitModels(position.latLng.lat(), position.latLng.lng());
|
||||||
|
}
|
||||||
|
|
||||||
|
const setCoordinatesFromGeocode = (address) => {
|
||||||
|
axios.get(`https://maps.googleapis.com/maps/api/geocode/json?address=${encodeURI(address)}&key=${process.env.GOOGLE_MAPS_API_KEY}`).then(response => {
|
||||||
|
if (response.data.status !== 'OK') {
|
||||||
|
emits("notFound", props.label);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
center.value = response.data.results[0].geometry.location;
|
||||||
|
emitModels(center.value.lat, center.value.lng);
|
||||||
|
zoom.value = 13;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (props.gpsLatitude && props.gpsLongitude) {
|
||||||
|
center.value.lng = props.gpsLongitude;
|
||||||
|
center.value.lat = props.gpsLatitude
|
||||||
|
} else if (props.label) {
|
||||||
|
setCoordinatesFromGeocode(props.label);
|
||||||
|
} else {
|
||||||
|
console.error("Set either label or gps coordinates.");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>Tu bude mapa s GPS pointerem</div>
|
<MapWrapper style="width: 100%; height: 500px" :center="center" :zoom="zoom">
|
||||||
|
<Marker :options="{ position: center, title: label, draggable: true }" @dragend="markerDropped"/>
|
||||||
|
</MapWrapper>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import FormWrapper from "components/FormWrapper.vue";
|
import FormWrapper from "components/FormWrapper.vue";
|
||||||
import {onMounted, reactive} from "vue";
|
import {onMounted, reactive, ref} from "vue";
|
||||||
import {setDefaultData} from "src/composables/form/formHandling";
|
import {setDefaultData} from "src/composables/form/formHandling";
|
||||||
import MapGPSInput from "components/inputs/MapGPSInput.vue";
|
import MapGPSInput from "components/inputs/MapGPSInput.vue";
|
||||||
|
import {useQuasar} from "quasar";
|
||||||
|
import {errorObject} from "src/composables/dialog";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
poi: {
|
poi: {
|
||||||
@@ -14,22 +16,63 @@ const props = defineProps({
|
|||||||
|
|
||||||
const formData = reactive({
|
const formData = reactive({
|
||||||
name: null,
|
name: null,
|
||||||
|
gpsLatitude: null,
|
||||||
|
gpsLongitude: null,
|
||||||
isPublic: false,
|
isPublic: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const poiNameForMap = ref("");
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
setDefaultData(formData, props.poi)
|
setDefaultData(formData, props.poi);
|
||||||
|
poiNameForMap.value = props.poi.name;
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const showResetMapButton = ref(false);
|
||||||
|
const resetMap = () => {
|
||||||
|
formData.gpsLongitude = 0;
|
||||||
|
formData.gpsLatitude = 0;
|
||||||
|
poiNameForMap.value = formData.name;
|
||||||
|
showResetMapButton.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const zoom = ref(10);
|
||||||
|
const $q = useQuasar();
|
||||||
|
const handlePositionNotFound = () => {
|
||||||
|
$q.notify(errorObject(`Lokalita ${formData.name} nenalezena. Nastav pozici ručně.`));
|
||||||
|
zoom.value = 7;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<FormWrapper v-model="formData">
|
<FormWrapper v-model="formData">
|
||||||
|
|
||||||
<q-input v-model="formData.name" label="Jméno" />
|
<q-input
|
||||||
<q-checkbox v-model="formData.isPublic" label="Veřejný?" />
|
class="q-my-lg"
|
||||||
|
outlined
|
||||||
|
standout
|
||||||
|
v-model="formData.name"
|
||||||
|
@change="showResetMapButton=true"
|
||||||
|
label="Jméno bodu"
|
||||||
|
>
|
||||||
|
<template #append v-if="showResetMapButton">
|
||||||
|
<q-btn @click="resetMap" icon="gps_fixed" round flat text-color="green" title="Nastav pozici na mapě"/>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
|
||||||
|
|
||||||
|
<MapGPSInput
|
||||||
|
v-if="poiNameForMap"
|
||||||
|
:key="poiNameForMap"
|
||||||
|
:label="poiNameForMap"
|
||||||
|
:zoom="zoom"
|
||||||
|
v-model:gpsLatitude="formData.gpsLatitude"
|
||||||
|
v-model:gpsLongitude="formData.gpsLongitude"
|
||||||
|
@not-found="handlePositionNotFound"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-checkbox v-model="formData.isPublic" label="Veřejný?"/>
|
||||||
|
|
||||||
<MapGPSInput />
|
|
||||||
|
|
||||||
</FormWrapper>
|
</FormWrapper>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import {pointsOfInterest} from "src/composables/objects/poi";
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
items: {
|
items: {
|
||||||
type: Array,
|
type: Array,
|
||||||
@@ -11,6 +9,9 @@ const props = defineProps({
|
|||||||
<template>
|
<template>
|
||||||
<q-list bordered separator>
|
<q-list bordered separator>
|
||||||
<q-item clickable v-ripple v-for="poi of items" :key="poi">
|
<q-item clickable v-ripple v-for="poi of items" :key="poi">
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-avatar icon="place" color="primary" text-color="white"/>
|
||||||
|
</q-item-section>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<router-link :to="{name: 'poiDetail', params: {id: poi.id}}">
|
<router-link :to="{name: 'poiDetail', params: {id: poi.id}}">
|
||||||
{{ poi.name }}
|
{{ poi.name }}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { date } from "quasar";
|
import {date} from "quasar";
|
||||||
|
|
||||||
export const FORMAT_ISO = "YYYY-MM-DDTHH:mm:ss";
|
export const FORMAT_ISO = "YYYY-MM-DDTHH:mm:ss";
|
||||||
export const FORMAT_DATETIME_LONG = "DD. MM. YYYY, HH:mm";
|
export const FORMAT_DATETIME_LONG = "DD. MM. YYYY, HH:mm";
|
||||||
|
|||||||
@@ -10,3 +10,9 @@ export const successObject = (message) => ({
|
|||||||
position: "top",
|
position: "top",
|
||||||
color: "positive"
|
color: "positive"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const errorObject = (message) => ({
|
||||||
|
message,
|
||||||
|
position: "top",
|
||||||
|
color: "negative"
|
||||||
|
})
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
import {useMeta} from "quasar";
|
|
||||||
|
|
||||||
export const setTitle = (title) => {
|
|
||||||
|
|
||||||
// todo: otestovat, nevim proc je tam ta factory
|
|
||||||
const metadata = {
|
|
||||||
titleTemplate: () => `${title} - Polétání.cz`,
|
|
||||||
}
|
|
||||||
useMeta(metadata);
|
|
||||||
}
|
|
||||||
@@ -6,13 +6,16 @@ export const loading = ref(true);
|
|||||||
export const copilot = ref(null);
|
export const copilot = ref(null);
|
||||||
export const fetchCopilot = (copilotId) => {
|
export const fetchCopilot = (copilotId) => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
useCopilotsStore().fetchDetail(parseInt(copilotId))
|
return useCopilotsStore().fetchDetail(parseInt(copilotId))
|
||||||
.then(resp => copilot.value = resp)
|
.then(resp => {
|
||||||
|
copilot.value = resp;
|
||||||
|
return resp;
|
||||||
|
})
|
||||||
.finally(() => loading.value = false);
|
.finally(() => loading.value = false);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const copilots = storeToRefs(useCopilotsStore()).items;
|
export const copilots = storeToRefs(useCopilotsStore()).items;
|
||||||
export const fetchCopilots = () => {
|
export const fetchCopilots = () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
useCopilotsStore().fetch().finally(() => loading.value = false);
|
return useCopilotsStore().fetch().finally(() => loading.value = false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,11 @@ export const loading = ref(true);
|
|||||||
export const poi = ref(null);
|
export const poi = ref(null);
|
||||||
export const fetchPoi = (poiId) => {
|
export const fetchPoi = (poiId) => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
usePointsOfInterestStore().fetchDetail(poiId)
|
return usePointsOfInterestStore().fetchDetail(poiId)
|
||||||
.then(data => poi.value = data)
|
.then(data => {
|
||||||
|
poi.value = data;
|
||||||
|
return data;
|
||||||
|
})
|
||||||
.finally(() => loading.value = false);
|
.finally(() => loading.value = false);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -15,5 +18,5 @@ export const fetchPoi = (poiId) => {
|
|||||||
export const pointsOfInterest = storeToRefs(usePointsOfInterestStore()).items;
|
export const pointsOfInterest = storeToRefs(usePointsOfInterestStore()).items;
|
||||||
export const fetchPOIList = () => {
|
export const fetchPOIList = () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
usePointsOfInterestStore().fetch().finally(() => loading.value = false);
|
return usePointsOfInterestStore().fetch().finally(() => loading.value = false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
export const randomInt = (min, max) => {
|
export const randomInt = (min, max) => {
|
||||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||||
}
|
}
|
||||||
|
|||||||
+44
-9
@@ -1,19 +1,53 @@
|
|||||||
import {ApolloClient, ApolloLink, HttpLink, InMemoryCache} from '@apollo/client/core'
|
import {ApolloClient, ApolloLink, fromPromise, HttpLink, InMemoryCache} from '@apollo/client/core'
|
||||||
import {createApolloProvider} from "@vue/apollo-option";
|
import {createApolloProvider} from "@vue/apollo-option";
|
||||||
import { createUploadLink } from 'apollo-upload-client';
|
import {createUploadLink} from 'apollo-upload-client';
|
||||||
|
import {onError} from "@apollo/client/link/error";
|
||||||
|
import {useUserStore} from "stores/User";
|
||||||
|
import {useRouter} from "vue-router";
|
||||||
|
|
||||||
const httpLink = new HttpLink({
|
|
||||||
uri: 'http://localhost:8000/graphql',
|
|
||||||
credentials: 'include'
|
|
||||||
})
|
|
||||||
|
|
||||||
const uploadLink = createUploadLink({
|
const uploadLink = createUploadLink({
|
||||||
uri: 'http://localhost:8000/graphql',
|
uri: 'http://localhost:8000/graphql',
|
||||||
credentials: 'include'
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
const errorLink = onError(
|
||||||
|
({graphQLErrors, networkError, operation, forward}) => {
|
||||||
|
if (networkError?.statusCode === 401) {
|
||||||
|
return fromPromise(
|
||||||
|
useUserStore().refreshAccessToken().catch((error) => {
|
||||||
|
useRouter().push({to: "login"});
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.filter((value) => Boolean(value))
|
||||||
|
.flatMap((accessToken) => {
|
||||||
|
const oldHeaders = operation.getContext().headers;
|
||||||
|
operation.setContext({
|
||||||
|
headers: {
|
||||||
|
...oldHeaders,
|
||||||
|
authorization: `Bearer ${accessToken}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// retry the request, returning the new observable
|
||||||
|
return forward(operation);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const authLink = new ApolloLink((operation, forward) => {
|
||||||
|
const accessToken = useUserStore().getAccessToken();
|
||||||
|
|
||||||
|
if (accessToken) {
|
||||||
|
operation.setContext({
|
||||||
|
headers: {"Authorization": `Bearer ${accessToken}`},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return forward(operation);
|
||||||
|
});
|
||||||
|
|
||||||
export const apolloClient = new ApolloClient({
|
export const apolloClient = new ApolloClient({
|
||||||
link: ApolloLink.from([uploadLink]),
|
link: ApolloLink.from([authLink, errorLink, uploadLink]),
|
||||||
cache: new InMemoryCache(),
|
cache: new InMemoryCache(),
|
||||||
connectToDevTools: true,
|
connectToDevTools: true,
|
||||||
})
|
})
|
||||||
@@ -31,7 +65,8 @@ export const apolloWrapper = {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
mutate(mutation, variables, onUpdate = () => {}) {
|
mutate(mutation, variables, onUpdate = () => {
|
||||||
|
}) {
|
||||||
return apolloClient
|
return apolloClient
|
||||||
.mutate({
|
.mutate({
|
||||||
mutation: mutation,
|
mutation: mutation,
|
||||||
|
|||||||
@@ -4,5 +4,7 @@ export const FRAGMENT_AIRPORT_INFO = gql`
|
|||||||
fragment airportInfo on Airport {
|
fragment airportInfo on Airport {
|
||||||
name
|
name
|
||||||
icaoCode
|
icaoCode
|
||||||
|
gpsLatitude
|
||||||
|
gpsLongitude
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ fragment flightFullInfo on Flight {
|
|||||||
pointOfInterest {
|
pointOfInterest {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
|
gpsLatitude
|
||||||
|
gpsLongitude
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import gql from "graphql-tag";
|
import gql from "graphql-tag";
|
||||||
|
import {FRAGMENT_FLIGHT_LIST_INFO} from "src/graphql/fragments/flight";
|
||||||
|
|
||||||
export const COPILOTS_LIST = gql`
|
export const COPILOTS_LIST = gql`
|
||||||
query copilotsList {
|
query copilotsList {
|
||||||
@@ -14,6 +15,12 @@ query copilot($id: Int!) {
|
|||||||
copilot(id: $id) {
|
copilot(id: $id) {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
|
|
||||||
|
flights {
|
||||||
|
...flightListInfo
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
${FRAGMENT_FLIGHT_LIST_INFO}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<q-toolbar-title>
|
<q-toolbar-title>
|
||||||
{{ app.pageTitle.value || "Polétání.cz"}}
|
{{ pageTitle || "Polétání.cz" }}
|
||||||
</q-toolbar-title>
|
</q-toolbar-title>
|
||||||
|
|
||||||
</q-toolbar>
|
</q-toolbar>
|
||||||
@@ -27,10 +27,10 @@
|
|||||||
<q-img class="absolute-top" src="https://galerie.kvacek.cz/_data/i/upload/2022/06/22/20220622102027-b104fc0d-me.jpg" style="height: 150px">
|
<q-img class="absolute-top" src="https://galerie.kvacek.cz/_data/i/upload/2022/06/22/20220622102027-b104fc0d-me.jpg" style="height: 150px">
|
||||||
<div class="absolute-bottom bg-transparent">
|
<div class="absolute-bottom bg-transparent">
|
||||||
<q-avatar size="56px" class="q-mb-sm">
|
<q-avatar size="56px" class="q-mb-sm">
|
||||||
<q-img :src="loggedUser.avatarImageUrl" />
|
<q-img :src="loggedUser.avatarImageUrl"/>
|
||||||
</q-avatar>
|
</q-avatar>
|
||||||
|
|
||||||
<div class="text-weight-bold">{{loggedUser.name}}</div>
|
<div class="text-weight-bold">{{ loggedUser.name }}</div>
|
||||||
</div>
|
</div>
|
||||||
</q-img>
|
</q-img>
|
||||||
<q-scroll-area style="height: calc(100% - 150px); margin-top: 150px; border-right: 1px solid #ddd">
|
<q-scroll-area style="height: calc(100% - 150px); margin-top: 150px; border-right: 1px solid #ddd">
|
||||||
@@ -47,24 +47,28 @@
|
|||||||
</q-drawer>
|
</q-drawer>
|
||||||
|
|
||||||
<q-page-container class="q-px-md">
|
<q-page-container class="q-px-md">
|
||||||
<BreadcrumbsNavigation :items="app.breadcrumbs.value" />
|
<BreadcrumbsNavigation class="q-my-md" :items="breadcrumbs"/>
|
||||||
<router-view/>
|
<router-view/>
|
||||||
</q-page-container>
|
</q-page-container>
|
||||||
</q-layout>
|
</q-layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {onMounted, ref} from 'vue'
|
import {onMounted, ref, watch} from 'vue'
|
||||||
import NavigationButton from "../components/navigation/NavigationButton.vue";
|
import NavigationButton from "components/navigation/NavigationButton.vue";
|
||||||
import {useUserStore} from "stores/User";
|
import {useUserStore} from "stores/User";
|
||||||
import {storeToRefs} from "pinia";
|
import {storeToRefs} from "pinia";
|
||||||
import BreadcrumbsNavigation from "components/BreadcrumbsNavigation.vue";
|
import BreadcrumbsNavigation from "components/BreadcrumbsNavigation.vue";
|
||||||
import {useAppStore} from "stores/App";
|
import {useAppStore} from "stores/App";
|
||||||
|
import {useMeta} from "quasar";
|
||||||
|
|
||||||
const app = storeToRefs(useAppStore());
|
const {pageTitle, breadcrumbs} = storeToRefs(useAppStore());
|
||||||
|
useMeta(() => ({
|
||||||
|
title: pageTitle.value !== '' ? `${pageTitle.value} - Polétání.cz` : 'Polétání.cz'
|
||||||
|
}));
|
||||||
|
|
||||||
const linksList = [
|
const linksList = [
|
||||||
{
|
{
|
||||||
title: 'Přidat let',
|
title: 'Přidat let',
|
||||||
icon: 'airplane_ticket',
|
icon: 'airplane_ticket',
|
||||||
to: {name: "addFlight"}
|
to: {name: "addFlight"}
|
||||||
@@ -81,7 +85,6 @@ const linksList = [
|
|||||||
to: {name: "aircrafts"}
|
to: {name: "aircrafts"}
|
||||||
},
|
},
|
||||||
{separator: true},
|
{separator: true},
|
||||||
|
|
||||||
{
|
{
|
||||||
title: 'Zajímavá místa',
|
title: 'Zajímavá místa',
|
||||||
icon: "place",
|
icon: "place",
|
||||||
|
|||||||
@@ -1,7 +1,17 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import {useUserStore} from "stores/User";
|
import {useUserStore} from "stores/User";
|
||||||
|
import {onMounted} from "vue";
|
||||||
|
import {useFlightsStore} from "stores/Flights";
|
||||||
|
|
||||||
const loggedUser = useUserStore().user
|
const loggedUser = useUserStore().user
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
useUserStore().setupCredentials();
|
||||||
|
setInterval(() => {
|
||||||
|
useFlightsStore().fetch();
|
||||||
|
}, 1000);
|
||||||
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -39,11 +39,11 @@ watch(aircraft, (newVal) => {
|
|||||||
<AircraftDetailSkeleton v-if="loading || !aircraft"/>
|
<AircraftDetailSkeleton v-if="loading || !aircraft"/>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
|
|
||||||
<h1>{{ aircraft.callSign }}</h1>
|
<q-card>
|
||||||
|
|
||||||
<q-img :src="aircraft.photoUrl"/>
|
<q-img :src="aircraft.photoUrl"/>
|
||||||
|
|
||||||
<q-list>
|
<q-card-section>
|
||||||
|
<q-list>
|
||||||
<q-item>
|
<q-item>
|
||||||
Výrobce: {{ aircraft.manufacturer }}
|
Výrobce: {{ aircraft.manufacturer }}
|
||||||
</q-item>
|
</q-item>
|
||||||
@@ -53,7 +53,16 @@ watch(aircraft, (newVal) => {
|
|||||||
</q-item>
|
</q-item>
|
||||||
|
|
||||||
</q-list>
|
</q-list>
|
||||||
<q-btn @click="confirmDelete(aircraft.id)">Delete</q-btn>
|
</q-card-section>
|
||||||
|
|
||||||
|
<q-card-actions align="right">
|
||||||
|
<q-btn @click="confirmDelete(aircraft.id)" icon="delete" text-color="red" rounded flat/>
|
||||||
|
|
||||||
|
</q-card-actions>
|
||||||
|
</q-card>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- <h2>Limits</h2>-->
|
<!-- <h2>Limits</h2>-->
|
||||||
<!-- Wind: {{ aircraft.limits.wind.frontKt }}/{{ aircraft.limits.wind.sideKt }} kt-->
|
<!-- Wind: {{ aircraft.limits.wind.frontKt }}/{{ aircraft.limits.wind.sideKt }} kt-->
|
||||||
|
|||||||
@@ -1,22 +1,21 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import {useRoute} from "vue-router";
|
import {useRoute} from "vue-router";
|
||||||
import {computed, onMounted} from "vue";
|
import {onMounted} from "vue";
|
||||||
import {storeToRefs} from "pinia";
|
|
||||||
import {minutesToHumanReadable} from "src/composables/datetimeUtils";
|
|
||||||
import {copilot, fetchCopilot, loading} from "src/composables/objects/copilot";
|
import {copilot, fetchCopilot, loading} from "src/composables/objects/copilot";
|
||||||
import {useFlightsStore} from "stores/Flights";
|
|
||||||
import CopilotDetailSkeleton from "components/copilots/CopilotDetailSkeleton.vue";
|
import CopilotDetailSkeleton from "components/copilots/CopilotDetailSkeleton.vue";
|
||||||
|
import {useAppStore} from "stores/App";
|
||||||
|
import FAB from "components/FAB.vue";
|
||||||
|
import FlightsList from "components/flights/FlightsList.vue";
|
||||||
|
|
||||||
const copilotId = parseInt(useRoute().params.copilotId);
|
const copilotId = parseInt(useRoute().params.id);
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
useFlightsStore().fetch();
|
fetchCopilot(copilotId).then(() => {
|
||||||
fetchCopilot(copilotId);
|
useAppStore().setPageTitle(copilot.value.name);
|
||||||
})
|
useAppStore().setBreadcrumbs([
|
||||||
|
{to: {name: "pilots"}, label: "Kopiloti"},
|
||||||
const flights = storeToRefs(useFlightsStore()).items;
|
{label: copilot.value.name}
|
||||||
const commonFlights = computed(() => flights.value.filter(f => f.secondPilot.id === copilot.id))
|
])
|
||||||
const totalFlightTime = computed(() => {
|
});
|
||||||
return commonFlights.value.reduce((acc, flight) => acc + flight.flightDurationMinutes, 0);
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -24,16 +23,12 @@ const totalFlightTime = computed(() => {
|
|||||||
<CopilotDetailSkeleton v-if="loading || !copilot"/>
|
<CopilotDetailSkeleton v-if="loading || !copilot"/>
|
||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<q-breadcrumbs>
|
<h3>Společné lety</h3>
|
||||||
<q-breadcrumbs-el :to="{name: 'pilots'}">Pilots</q-breadcrumbs-el>
|
<q-banner v-if="copilot.flights.length === 0" class="bg-light-blue text-white">
|
||||||
<q-breadcrumbs-el>{{ copilot.name }}</q-breadcrumbs-el>
|
Zatím žádné společné lety
|
||||||
</q-breadcrumbs>
|
</q-banner>
|
||||||
|
<FlightsList :items="copilot.flights" v-else/>
|
||||||
|
|
||||||
<h1>{{ copilot.name }}</h1>
|
<FAB mode="edit" :router-link="{name: 'pilotEdit', params: {id: copilotId}}"/>
|
||||||
|
|
||||||
Flights:
|
|
||||||
{{ commonFlights }}
|
|
||||||
|
|
||||||
Total time together: {{ minutesToHumanReadable(totalFlightTime) }}
|
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import {useRoute, useRouter} from "vue-router";
|
import {useRoute, useRouter} from "vue-router";
|
||||||
import {onMounted, watch} from "vue";
|
import {computed, onMounted, watch} from "vue";
|
||||||
import FlightDetailSkeleton from "components/flights/FlightDetailSkeleton.vue";
|
import FlightDetailSkeleton from "components/flights/FlightDetailSkeleton.vue";
|
||||||
import {fetchFlight, flight, flightName, handleDeleteFlight, loading} from "src/composables/objects/flight";
|
import {fetchFlight, flight, flightName, handleDeleteFlight, loading} from "src/composables/objects/flight";
|
||||||
import {useAppStore} from "stores/App";
|
import {useAppStore} from "stores/App";
|
||||||
@@ -8,6 +8,8 @@ import PhotosList from "components/photos/PhotosList.vue";
|
|||||||
import FlightBasicInfo from "components/flights/FlightBasicInfo.vue";
|
import FlightBasicInfo from "components/flights/FlightBasicInfo.vue";
|
||||||
import {useQuasar} from "quasar";
|
import {useQuasar} from "quasar";
|
||||||
import {successObject} from "src/composables/dialog";
|
import {successObject} from "src/composables/dialog";
|
||||||
|
import MapWithMarkers from "components/MapWithMarkers.vue";
|
||||||
|
import FAB from "components/FAB.vue";
|
||||||
|
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
const $router = useRouter();
|
const $router = useRouter();
|
||||||
@@ -34,13 +36,30 @@ const handleDeleteAndRedirect = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const flightPath = computed(() => {
|
||||||
|
const path = [{
|
||||||
|
lat: flight.value.takeoffAirport.gpsLatitude,
|
||||||
|
lng: flight.value.takeoffAirport.gpsLongitude
|
||||||
|
}];
|
||||||
|
|
||||||
|
for (const poi of flight.value.track) {
|
||||||
|
path.push({lat: poi.pointOfInterest.gpsLatitude, lng: poi.pointOfInterest.gpsLongitude});
|
||||||
|
}
|
||||||
|
|
||||||
|
path.push({
|
||||||
|
lat: flight.value.landingAirport.gpsLatitude,
|
||||||
|
lng: flight.value.landingAirport.gpsLongitude
|
||||||
|
});
|
||||||
|
|
||||||
|
return path;
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<FlightDetailSkeleton v-if="loading || !flight"/>
|
<FlightDetailSkeleton v-if="loading || !flight"/>
|
||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<q-card class="q-mx-md q-my-md">
|
<q-card class="q-mx-md-lg q-my-md">
|
||||||
<q-parallax
|
<q-parallax
|
||||||
:src="flight.coverPhoto.url"
|
:src="flight.coverPhoto.url"
|
||||||
v-if="flight.coverPhoto"
|
v-if="flight.coverPhoto"
|
||||||
@@ -50,22 +69,23 @@ const handleDeleteAndRedirect = () => {
|
|||||||
<q-card-section>
|
<q-card-section>
|
||||||
<FlightBasicInfo :flight="flight"/>
|
<FlightBasicInfo :flight="flight"/>
|
||||||
|
|
||||||
<article v-html="flight.description"/>
|
<article class="q-my-lg" v-html="flight.description"/>
|
||||||
|
|
||||||
<ul>
|
<MapWithMarkers
|
||||||
<li v-for="poi in flight.track" :key="poi.pointOfInterest.id">
|
style="width: 100%; height: 500px;"
|
||||||
{{ poi.pointOfInterest.name }}
|
:show-as-track="true"
|
||||||
</li>
|
:markers="flightPath"
|
||||||
</ul>
|
/>
|
||||||
|
|
||||||
<PhotosList :photos="flight.photos" :show-actions="false"/>
|
<PhotosList :photos="flight.photos" :show-actions="false"/>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
|
|
||||||
<q-card-actions align="right">
|
<q-card-actions align="right">
|
||||||
<q-btn @click="handleDeleteAndRedirect" icon="delete" rounded flat text-color="red"/>
|
<q-btn @click="handleDeleteAndRedirect" icon="delete" rounded flat text-color="red"/>
|
||||||
<q-btn :to="{name: 'flightEdit', params:{id: flight.id}}" icon="edit" rounded flat text-color="primary"/>
|
|
||||||
</q-card-actions>
|
</q-card-actions>
|
||||||
</q-card>
|
</q-card>
|
||||||
<!-- <FAB mode="edit" :router-link=""/>-->
|
|
||||||
|
<FAB :router-link="{name: 'flightEdit', params:{id: flight.id}}" mode="edit"/>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -4,11 +4,19 @@ import {useRoute} from "vue-router";
|
|||||||
import POIDetailSkeleton from "components/poi/POIDetailSkeleton.vue";
|
import POIDetailSkeleton from "components/poi/POIDetailSkeleton.vue";
|
||||||
import FAB from "components/FAB.vue";
|
import FAB from "components/FAB.vue";
|
||||||
import {fetchPoi, loading, poi} from "src/composables/objects/poi";
|
import {fetchPoi, loading, poi} from "src/composables/objects/poi";
|
||||||
import {flight} from "src/composables/objects/flight";
|
|
||||||
import PhotosList from "components/photos/PhotosList.vue";
|
import PhotosList from "components/photos/PhotosList.vue";
|
||||||
|
import {useAppStore} from "stores/App";
|
||||||
|
import MapWithMarker from "components/MapWithMarkers.vue";
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
fetchPoi(useRoute().params.id);
|
fetchPoi(useRoute().params.id).then(() => {
|
||||||
|
useAppStore().setPageTitle(`${poi.value.name}`);
|
||||||
|
useAppStore().setBreadcrumbs([
|
||||||
|
{to: {name: "poi"}, label: "Zajímavá místa"},
|
||||||
|
{label: poi.value.name},
|
||||||
|
])
|
||||||
|
});
|
||||||
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -16,7 +24,13 @@ onMounted(() => {
|
|||||||
<POIDetailSkeleton v-if="loading || !poi"/>
|
<POIDetailSkeleton v-if="loading || !poi"/>
|
||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<h1>{{ poi.name }}</h1>
|
|
||||||
|
<MapWithMarker
|
||||||
|
v-if="poi.gpsLongitude && poi.gpsLatitude"
|
||||||
|
style="width: 100%; height: 500px;"
|
||||||
|
:zoom="13"
|
||||||
|
:markers="[{lat: poi.gpsLatitude, lng: poi.gpsLongitude}]"
|
||||||
|
/>
|
||||||
|
|
||||||
<PhotosList
|
<PhotosList
|
||||||
:photos="poi.photos"
|
:photos="poi.photos"
|
||||||
|
|||||||
@@ -1,31 +1,30 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import {useCopilotsStore} from "stores/Copilots";
|
import {onMounted} from "vue";
|
||||||
|
import {loading, copilot, fetchCopilot} from "src/composables/objects/copilot";
|
||||||
|
import {useAppStore} from "stores/App";
|
||||||
|
import CopilotEditSkeleton from "components/copilots/CopilotEditSkeleton.vue";
|
||||||
|
import CopilotEditForm from "components/copilots/CopilotEditForm.vue";
|
||||||
import {useRoute} from "vue-router";
|
import {useRoute} from "vue-router";
|
||||||
import {useFlightsStore} from "stores/Flights";
|
|
||||||
import {computed, onMounted} from "vue";
|
|
||||||
import {storeToRefs} from "pinia";
|
|
||||||
import {minutesToHumanReadable} from "src/composables/datetimeUtils";
|
|
||||||
|
|
||||||
const secondPilotStore = useCopilotsStore();
|
const copilotId = parseInt(useRoute().params.id);
|
||||||
const flightsStore = useFlightsStore();
|
|
||||||
const route = useRoute();
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
flightsStore.fetch();
|
fetchCopilot(copilotId).then(() => {
|
||||||
})
|
useAppStore().setPageTitle(`${copilot.value.name} - editace`);
|
||||||
|
useAppStore().setBreadcrumbs([
|
||||||
|
{to: {name: "pilots"}, label: "Kopiloti"},
|
||||||
|
{to: {name: "pilotDetail", params: {id: copilotId}}, label: copilot.value.name},
|
||||||
|
{label: "Editace"}
|
||||||
|
])
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
const secondPilot = {id: 1, name: "Franta Karel"};
|
|
||||||
const flights = storeToRefs(flightsStore).items;
|
|
||||||
|
|
||||||
const commonFlights = computed(() => flights.value.filter(f => f.secondPilot.id === secondPilot.id))
|
|
||||||
|
|
||||||
const totalFlightTime = computed(() => {
|
|
||||||
return commonFlights.value.reduce((acc, flight) => acc + flight.flightDurationMinutes, 0);
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<CopilotEditSkeleton v-if="loading" />
|
||||||
|
|
||||||
|
<CopilotEditForm v-else />
|
||||||
|
|
||||||
Editace copilota
|
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ const openPhotoEdit = (photo) => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<FlightEditSkeleton v-if="loading" />
|
<FlightEditSkeleton v-if="loading"/>
|
||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<q-breadcrumbs :key="loading">
|
<q-breadcrumbs :key="loading">
|
||||||
@@ -133,17 +133,18 @@ const openPhotoEdit = (photo) => {
|
|||||||
</q-tab-panel>
|
</q-tab-panel>
|
||||||
|
|
||||||
<q-tab-panel name="photos">
|
<q-tab-panel name="photos">
|
||||||
|
<FlightFormPhotos
|
||||||
|
:flight="flight"
|
||||||
|
@save="handlePhotosUpload"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<PhotoForm v-model="openEdit" :locations="locations" :photo="editPhoto" @save="handlePhotoUpdate"/>
|
||||||
|
|
||||||
<PhotosList
|
<PhotosList
|
||||||
:photos="flight.photos"
|
:photos="flight.photos"
|
||||||
@edit="openPhotoEdit"
|
@edit="openPhotoEdit"
|
||||||
@delete="confirmPhotoDelete"
|
@delete="confirmPhotoDelete"
|
||||||
/>
|
/>
|
||||||
<PhotoForm v-model="openEdit" :locations="locations" :photo="editPhoto" @save="handlePhotoUpdate"/>
|
|
||||||
|
|
||||||
<FlightFormPhotos
|
|
||||||
:flight="flight"
|
|
||||||
@save="handlePhotosUpload"
|
|
||||||
/>
|
|
||||||
</q-tab-panel>
|
</q-tab-panel>
|
||||||
|
|
||||||
<q-tab-panel name="notes">
|
<q-tab-panel name="notes">
|
||||||
|
|||||||
@@ -5,23 +5,33 @@ import {onMounted} from "vue";
|
|||||||
import {useRoute} from "vue-router";
|
import {useRoute} from "vue-router";
|
||||||
import POIEditForm from "components/poi/POIEditForm.vue";
|
import POIEditForm from "components/poi/POIEditForm.vue";
|
||||||
import {usePointsOfInterestStore} from "stores/PointsOfInterest";
|
import {usePointsOfInterestStore} from "stores/PointsOfInterest";
|
||||||
|
import {useAppStore} from "stores/App";
|
||||||
|
import {useQuasar} from "quasar";
|
||||||
|
import {successObject} from "src/composables/dialog";
|
||||||
|
|
||||||
const poiId = parseInt(useRoute().params.id);
|
const poiId = parseInt(useRoute().params.id);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
fetchPoi(poiId)
|
fetchPoi(poiId).then(() => {
|
||||||
|
useAppStore().setPageTitle(`${poi.value.name} - editace`);
|
||||||
|
useAppStore().setBreadcrumbs([
|
||||||
|
{to: {name: "poi"}, label: "Zajímavá místa"},
|
||||||
|
{to: {name: "poiDetail", params: {id: poiId}}, label: poi.value.name},
|
||||||
|
{label: "Editace"},
|
||||||
|
]);
|
||||||
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const $q = useQuasar();
|
||||||
const handleSave = (data) => {
|
const handleSave = (data) => {
|
||||||
usePointsOfInterestStore().update(poiId, data).then(respData => {
|
usePointsOfInterestStore().update(poiId, data).then(respData => {
|
||||||
console.log(respData);
|
$q.notify(successObject("Úspěšně uloženo"));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<POIEditSkeleton v-if="loading" />
|
<POIEditSkeleton v-if="loading"/>
|
||||||
|
|
||||||
<POIEditForm :poi="poi" v-else @save="handleSave"/>
|
<POIEditForm :poi="poi" v-else @save="handleSave"/>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ onMounted(() => {
|
|||||||
<template>
|
<template>
|
||||||
<AircraftsListSkeleton v-if="loading" />
|
<AircraftsListSkeleton v-if="loading" />
|
||||||
|
|
||||||
<AircraftsList :items="aircrafts" v-else />
|
<AircraftsList class="q-my-lg" :items="aircrafts" v-else />
|
||||||
|
|
||||||
<FAB mode="add" :router-link="{name: 'addAircraft'}"/>
|
<FAB mode="add" :router-link="{name: 'addAircraft'}"/>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
+7
-5
@@ -4,17 +4,19 @@ import CopilotListSkeleton from "components/copilots/CopilotListSkeleton.vue";
|
|||||||
import {fetchCopilots, loading, copilots} from "src/composables/objects/copilot";
|
import {fetchCopilots, loading, copilots} from "src/composables/objects/copilot";
|
||||||
import CopilotsList from "components/copilots/CopilotsList.vue";
|
import CopilotsList from "components/copilots/CopilotsList.vue";
|
||||||
import FAB from "components/FAB.vue";
|
import FAB from "components/FAB.vue";
|
||||||
|
import {useAppStore} from "stores/App";
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
fetchCopilots();
|
fetchCopilots();
|
||||||
|
useAppStore().setPageTitle("Spolucestující");
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<CopilotListSkeleton v-if="loading" />
|
<CopilotListSkeleton v-if="loading"/>
|
||||||
|
|
||||||
<CopilotsList :items="copilots" />
|
|
||||||
|
|
||||||
<FAB :router-link="{name: 'addCopilot'}" mode="add" />
|
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
|
<CopilotsList class="q-my-lg" :items="copilots"/>
|
||||||
|
<FAB :router-link="{name: 'addCopilot'}" mode="add"/>
|
||||||
|
</template>
|
||||||
</template>
|
</template>
|
||||||
@@ -19,7 +19,7 @@ onMounted(() => {
|
|||||||
<FlightsListSkeleton v-if="loading"/>
|
<FlightsListSkeleton v-if="loading"/>
|
||||||
|
|
||||||
<div class="q-pa-md q-gutter-sm" v-else-if="!loading && flights.length === 0">
|
<div class="q-pa-md q-gutter-sm" v-else-if="!loading && flights.length === 0">
|
||||||
<q-banner class="bg-light-blue text-white ">
|
<q-banner class="bg-light-blue text-white">
|
||||||
<template v-slot:avatar>
|
<template v-slot:avatar>
|
||||||
<q-icon name="info"/>
|
<q-icon name="info"/>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ useAppStore().setPageTitle("Zajímavá místa")
|
|||||||
<template>
|
<template>
|
||||||
<POIListSkeleton v-if="loading"/>
|
<POIListSkeleton v-if="loading"/>
|
||||||
|
|
||||||
<POIList v-else :items="pointsOfInterest"/>
|
<POIList v-else class="q-my-lg" :items="pointsOfInterest"/>
|
||||||
|
|
||||||
<FAB :router-link="{name: 'addPOI'}" mode="add" />
|
<FAB :router-link="{name: 'addPOI'}" mode="add" />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
+6
-23
@@ -1,7 +1,6 @@
|
|||||||
import { route } from 'quasar/wrappers'
|
import {route} from 'quasar/wrappers'
|
||||||
import { createRouter, createMemoryHistory, createWebHistory, createWebHashHistory } from 'vue-router'
|
import {createRouter, createMemoryHistory, createWebHistory, createWebHashHistory} from 'vue-router'
|
||||||
import routes from './routes'
|
import routes from './routes'
|
||||||
import {useUserStore} from "stores/User";
|
|
||||||
import {useAppStore} from "stores/App";
|
import {useAppStore} from "stores/App";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -19,7 +18,7 @@ export default route(function (/* { store, ssrContext } */) {
|
|||||||
: (process.env.VUE_ROUTER_MODE === 'history' ? createWebHistory : createWebHashHistory)
|
: (process.env.VUE_ROUTER_MODE === 'history' ? createWebHistory : createWebHashHistory)
|
||||||
|
|
||||||
const Router = createRouter({
|
const Router = createRouter({
|
||||||
scrollBehavior: () => ({ left: 0, top: 0 }),
|
scrollBehavior: () => ({left: 0, top: 0}),
|
||||||
routes,
|
routes,
|
||||||
|
|
||||||
// Leave this as is and make changes in quasar.conf.js instead!
|
// Leave this as is and make changes in quasar.conf.js instead!
|
||||||
@@ -28,25 +27,9 @@ export default route(function (/* { store, ssrContext } */) {
|
|||||||
history: createHistory(process.env.VUE_ROUTER_BASE)
|
history: createHistory(process.env.VUE_ROUTER_BASE)
|
||||||
})
|
})
|
||||||
|
|
||||||
Router.beforeEach(async (to) => {
|
Router.afterEach(async (to) => {
|
||||||
if (to.meta?.protected) {
|
useAppStore().resetAfterRouteChange();
|
||||||
const userStore = useUserStore();
|
});
|
||||||
// const $q = useQuasar();
|
|
||||||
|
|
||||||
useAppStore().resetAfterRouteChange();
|
|
||||||
|
|
||||||
userStore.setupCredentials().then(()=>{
|
|
||||||
if (!userStore.user.id) {
|
|
||||||
// console.log("neni prihlaseny")
|
|
||||||
// $q.notify("No access token, login again!");
|
|
||||||
Router.push({name: "login"});
|
|
||||||
}
|
|
||||||
}).catch(e => {
|
|
||||||
// console.error("Error in router: ", e);
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return Router
|
return Router
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -25,10 +25,10 @@ const routes = [
|
|||||||
{path: 'poi/add', name: "addPOI", component: () => import('pages/profile/forms/NewPointOfInterestPage.vue')},
|
{path: 'poi/add', name: "addPOI", component: () => import('pages/profile/forms/NewPointOfInterestPage.vue')},
|
||||||
{path: 'poi/:id', name: "poiDetail", component: () => import('pages/profile/details/PointOfInterestDetailPage.vue')},
|
{path: 'poi/:id', name: "poiDetail", component: () => import('pages/profile/details/PointOfInterestDetailPage.vue')},
|
||||||
{path: 'poi/:id/edit', name: "poiEdit", component: () => import('pages/profile/forms/EditPointOfInterestPage.vue')},
|
{path: 'poi/:id/edit', name: "poiEdit", component: () => import('pages/profile/forms/EditPointOfInterestPage.vue')},
|
||||||
{path: 'pilots', name: "pilots", component: () => import('pages/profile/lists/SecondPilotPage.vue')},
|
{path: 'pilots', name: "pilots", component: () => import('pages/profile/lists/CopilotsPage.vue')},
|
||||||
{path: 'pilots/add', name: "addPilots", component: () => import('pages/profile/forms/NewCopilotPage.vue')},
|
{path: 'pilots/add', name: "addPilots", component: () => import('pages/profile/forms/NewCopilotPage.vue')},
|
||||||
{path: 'pilots/:id', name: "pilotDetail", component: () => import('pages/profile/details/CopilotDetailPage.vue')},
|
{path: 'pilots/:id', name: "pilotDetail", component: () => import('pages/profile/details/CopilotDetailPage.vue')},
|
||||||
{path: 'pilots/:id/edit', name: "pilotEdit", component: () => import('pages/profile/details/EditCopilotPage.vue')},
|
{path: 'pilots/:id/edit', name: "pilotEdit", component: () => import('pages/profile/forms/EditCopilotPage.vue')},
|
||||||
{path: 'aircrafts', name: "aircrafts", component: () => import('pages/profile/lists/AircraftsPage.vue')},
|
{path: 'aircrafts', name: "aircrafts", component: () => import('pages/profile/lists/AircraftsPage.vue')},
|
||||||
{path: 'aircrafts/new', name: "addAircraft", component: () => import('pages/profile/forms/NewAircraftsPage.vue')},
|
{path: 'aircrafts/new', name: "addAircraft", component: () => import('pages/profile/forms/NewAircraftsPage.vue')},
|
||||||
{path: 'aircrafts/:id', name: "aircraftDetail", component: () => import('pages/profile/details/AircraftDetailPage.vue')},
|
{path: 'aircrafts/:id', name: "aircraftDetail", component: () => import('pages/profile/details/AircraftDetailPage.vue')},
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import {defineStore} from 'pinia'
|
import {defineStore} from 'pinia'
|
||||||
import {useMeta} from "quasar";
|
|
||||||
|
|
||||||
export const useAppStore = defineStore('App', {
|
export const useAppStore = defineStore('App', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
@@ -13,10 +12,6 @@ export const useAppStore = defineStore('App', {
|
|||||||
},
|
},
|
||||||
setPageTitle(pageTitle) {
|
setPageTitle(pageTitle) {
|
||||||
this.pageTitle = pageTitle;
|
this.pageTitle = pageTitle;
|
||||||
useMeta({title: pageTitle !== '' ? `${pageTitle} - Polétání.cz` : 'Polétání.cz'});
|
|
||||||
|
|
||||||
|
|
||||||
console.log("setPageTitle:", pageTitle);
|
|
||||||
},
|
},
|
||||||
setBreadcrumbs(breadcrumbs) {
|
setBreadcrumbs(breadcrumbs) {
|
||||||
this.breadcrumbs = breadcrumbs;
|
this.breadcrumbs = breadcrumbs;
|
||||||
|
|||||||
+31
-30
@@ -4,31 +4,42 @@ import {api} from "boot/axios";
|
|||||||
export const useUserStore = defineStore('LoggedUser', {
|
export const useUserStore = defineStore('LoggedUser', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
user: {},
|
user: {},
|
||||||
accessToken: null
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
|
setTokens({access_token, access_token_validity}) {
|
||||||
setAccessToken(token) {
|
const now = new Date().getTime() / 1000;
|
||||||
console.log(token);
|
localStorage.setItem("accessToken", access_token);
|
||||||
if (token) {
|
localStorage.setItem("accessTokenValidity", now + access_token_validity);
|
||||||
localStorage.setItem("accessToken", token);
|
|
||||||
}
|
|
||||||
this.accessToken = localStorage.getItem("accessToken");
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getAccessToken() {
|
getAccessToken() {
|
||||||
if (!this.accessToken) {
|
const tokenValidity = localStorage.getItem("accessTokenValidity");
|
||||||
this.setAccessToken();
|
const now = new Date().getTime() / 1000;
|
||||||
|
|
||||||
|
if (tokenValidity < now) {
|
||||||
|
localStorage.removeItem("accessToken");
|
||||||
|
localStorage.removeItem("accessTokenValidity");
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.accessToken;
|
return localStorage.getItem(`accessToken`);
|
||||||
},
|
},
|
||||||
|
|
||||||
async setupCredentials() {
|
setupCredentials() {
|
||||||
// TODO: aktualne se vola s kazdym prechodem mezi routami, to je urcite zbytecne!
|
return api.get("/me").then(({data}) => {
|
||||||
await api.get("/me").then(({data}) => {
|
|
||||||
this.user = data;
|
this.user = data;
|
||||||
}).catch(e => {
|
return this.user;
|
||||||
console.log(e);
|
}).catch(async ({response}) => {
|
||||||
|
console.log(response);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
async refreshAccessToken() {
|
||||||
|
return api.post("/refresh").then(({data}) => {
|
||||||
|
this.setTokens(data);
|
||||||
|
return data.access_token;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -40,25 +51,15 @@ export const useUserStore = defineStore('LoggedUser', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async login(formData) {
|
async login(formData) {
|
||||||
|
return api.post("/login", formData).then(({data}) => {
|
||||||
return api.post("/login", formData).then(response => {
|
this.setTokens(data);
|
||||||
const data = response.data;
|
|
||||||
console.log("data", data);
|
|
||||||
this.setAccessToken(data.access_token);
|
|
||||||
this.user = data.user;
|
this.user = data.user;
|
||||||
})
|
})
|
||||||
|
|
||||||
// console.log("login", username, password);
|
|
||||||
// Object.assign(this.user, {
|
|
||||||
// id: 1,
|
|
||||||
// email: "michal@kvacek.cz",
|
|
||||||
// name: "Michal K.",
|
|
||||||
// accessToken: "abcdef",
|
|
||||||
// avatarImageUrl: "https://via.placeholder.com/50x50"
|
|
||||||
// });
|
|
||||||
},
|
},
|
||||||
logout() {
|
logout() {
|
||||||
Object.assign(this.user, {});
|
Object.assign(this.user, {});
|
||||||
|
localStorage.removeItem("accessToken");
|
||||||
|
localStorage.removeItem("accessTokenValidity");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -63,6 +63,21 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.36.0.tgz#9837f768c03a1e4a30bd304a64fb8844f0e72efe"
|
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.36.0.tgz#9837f768c03a1e4a30bd304a64fb8844f0e72efe"
|
||||||
integrity sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg==
|
integrity sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg==
|
||||||
|
|
||||||
|
"@googlemaps/js-api-loader@^1.12.11":
|
||||||
|
version "1.16.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@googlemaps/js-api-loader/-/js-api-loader-1.16.2.tgz#3fe748e21243f8e8322c677a5525c569ae9cdbe9"
|
||||||
|
integrity sha512-psGw5u0QM6humao48Hn4lrChOM2/rA43ZCm3tKK9qQsEj1/VzqkCqnvGfEOshDbBQflydfaRovbKwZMF4AyqbA==
|
||||||
|
dependencies:
|
||||||
|
fast-deep-equal "^3.1.3"
|
||||||
|
|
||||||
|
"@googlemaps/markerclusterer@^2.0.6":
|
||||||
|
version "2.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@googlemaps/markerclusterer/-/markerclusterer-2.4.0.tgz#52ede8a186f264468d7100b4c27d2a866956af6f"
|
||||||
|
integrity sha512-yjHrqaaYMzclbPERoCP/6r2w+wJfTCW/+Wblr7eJ4dyQcRFeOxZ8bsdoqvsyj1UbgNt91ly2aiww8h0uDOD6zw==
|
||||||
|
dependencies:
|
||||||
|
fast-deep-equal "^3.1.3"
|
||||||
|
supercluster "^8.0.1"
|
||||||
|
|
||||||
"@graphql-typed-document-node/core@^3.1.1":
|
"@graphql-typed-document-node/core@^3.1.1":
|
||||||
version "3.2.0"
|
version "3.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861"
|
resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861"
|
||||||
@@ -1911,6 +1926,11 @@ jsonfile@^6.0.1:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
graceful-fs "^4.1.6"
|
graceful-fs "^4.1.6"
|
||||||
|
|
||||||
|
kdbush@^4.0.2:
|
||||||
|
version "4.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/kdbush/-/kdbush-4.0.2.tgz#2f7b7246328b4657dd122b6c7f025fbc2c868e39"
|
||||||
|
integrity sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA==
|
||||||
|
|
||||||
kind-of@^6.0.2:
|
kind-of@^6.0.2:
|
||||||
version "6.0.3"
|
version "6.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
|
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
|
||||||
@@ -2086,11 +2106,6 @@ minimist@^1.2.6:
|
|||||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
|
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
|
||||||
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
|
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:
|
ms@2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||||
@@ -2737,6 +2752,13 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
|
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
|
||||||
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
|
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
|
||||||
|
|
||||||
|
supercluster@^8.0.1:
|
||||||
|
version "8.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/supercluster/-/supercluster-8.0.1.tgz#9946ba123538e9e9ab15de472531f604e7372df5"
|
||||||
|
integrity sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==
|
||||||
|
dependencies:
|
||||||
|
kdbush "^4.0.2"
|
||||||
|
|
||||||
supports-color@^7.1.0:
|
supports-color@^7.1.0:
|
||||||
version "7.2.0"
|
version "7.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
|
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
|
||||||
@@ -2939,6 +2961,15 @@ vue-router@^4.1.6:
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@vue/devtools-api" "^6.4.5"
|
"@vue/devtools-api" "^6.4.5"
|
||||||
|
|
||||||
|
vue3-google-map@^0.15.0:
|
||||||
|
version "0.15.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/vue3-google-map/-/vue3-google-map-0.15.0.tgz#83d996c23934ae06cd6d3e158fc2dd46ca7a18e1"
|
||||||
|
integrity sha512-Y0fyBa9feS3mFIsYnCdqGr2TQWtRAhs0Q2vTmrhD/kzyY9z7lqd5p8ZM+Axw7xsaUXP+NvvIdIP4TianIOubkQ==
|
||||||
|
dependencies:
|
||||||
|
"@googlemaps/js-api-loader" "^1.12.11"
|
||||||
|
"@googlemaps/markerclusterer" "^2.0.6"
|
||||||
|
fast-deep-equal "^3.1.3"
|
||||||
|
|
||||||
vue@^3.2.47:
|
vue@^3.2.47:
|
||||||
version "3.2.47"
|
version "3.2.47"
|
||||||
resolved "https://registry.yarnpkg.com/vue/-/vue-3.2.47.tgz#3eb736cbc606fc87038dbba6a154707c8a34cff0"
|
resolved "https://registry.yarnpkg.com/vue/-/vue-3.2.47.tgz#3eb736cbc606fc87038dbba6a154707c8a34cff0"
|
||||||
|
|||||||
Reference in New Issue
Block a user