Napoveda, fix filtru pro nacitani letu u objektu

This commit is contained in:
Michal Kváček
2024-02-13 23:00:37 +01:00
parent 9c9bbc9baa
commit 1a5c8f09bf
17 changed files with 189 additions and 104 deletions
+12 -11
View File
@@ -101,16 +101,17 @@ const chartOptions = {
</script>
<template>
<MapWithMarkers
v-if="displayMap"
style="width: 100%; height: 500px;"
:gpx-track="flight.gpxTrack?.coordinates || []"
:markers="pointsOfInterests"
:highlight-position="currentPositionMarker"
:photos="mapPhotos"
/>
<Line v-if="gpxTrack" :data="chartSpeedData" :options="chartOptions"/>
<Line v-if="gpxTrack" :data="chartAltitudeData" :options="chartOptions"/>
<q-no-ssr>
<MapWithMarkers
v-if="displayMap"
style="width: 100%; height: 500px;"
:gpx-track="flight.gpxTrack?.coordinates || []"
:markers="pointsOfInterests"
:highlight-position="currentPositionMarker"
:photos="mapPhotos"
/>
<Line v-if="gpxTrack" :data="chartSpeedData" :options="chartOptions"/>
<Line v-if="gpxTrack" :data="chartAltitudeData" :options="chartOptions"/>
</q-no-ssr>
</template>
@@ -29,7 +29,14 @@ const slide = ref(0);
class="bg-blue-1 text-white"
>
<q-carousel-slide :name="idx" v-for="(flight, idx) in items" :key="idx" class="column no-wrap flex-center text-center">
<q-img :src="flight.titlePhoto.url" fit="cover" ratio="16/9" height="400px" v-if="flight.titlePhoto">
<q-img
:src="flight.titlePhoto.url"
fit="cover"
ratio="16/9"
style="max-width: 1000px;"
height="400px"
v-if="flight.titlePhoto"
>
<div class="absolute-bottom">
<LatestFlightSliderInfo :flight="flight"/>
</div>
+29 -29
View File
@@ -1,45 +1,45 @@
const stripHtml = (str) => {
if (!str)
return '';
else
str = str.toString();
if (!str)
return '';
else
str = str.toString();
return str.replace(/<[^>]+>/ig, '');
return str.replace(/<[^>]+>/ig, '');
}
export const commonKeywords = [
"zápisník", "létání", "ultralight", "letectví", "fotogalerie", "výlet", "vyhlídkový let", "navigační let"
"zápisník", "létání", "ultralight", "letectví", "fotogalerie", "výlet", "vyhlídkový let", "navigační let"
];
export const aircraftKeywords = (aircraft) => {
return [aircraft.callSign, aircraft.manufacturer, aircraft.model]
return [aircraft.organization?.name, aircraft.callSign, aircraft.manufacturer, aircraft.model]
};
export const metaData = ({title, description, keywords, image, index = true}) => {
const filteredKeywords = (keywords || []).filter(kw => !!kw);
const filteredDescription = stripHtml(description);
const filteredKeywords = (keywords || []).filter(kw => !!kw);
const filteredDescription = stripHtml(description);
return {
title: title,
titleTemplate(title) {
return title !== '' ? `${title} - Polétání.cz: fotogalerie ze vzduchu` : 'Polétání.cz'
},
meta: {
description: {name: "description", content: filteredDescription || ""},
keywords: {name: "keywords", content: (filteredKeywords || commonKeywords).join(", ")},
ogImage: image ? {property: "og:image", content: image} : undefined,
ogTitle: {
property: "og:title",
content: title,
template(ogTitle) {
return (!ogTitle || ogTitle === '') ? "Polétání.cz" : `${ogTitle} - Polétání.cz`
}
},
ogDescription: {
property: "og:description", content: filteredDescription,
},
robots: {name: "robots", content: index ? "index" : "noindex"},
return {
title: title,
titleTemplate(title) {
return title !== '' ? `${title} - Polétání.cz: fotogalerie ze vzduchu` : 'Polétání.cz'
},
meta: {
description: {name: "description", content: filteredDescription || ""},
keywords: {name: "keywords", content: (filteredKeywords || commonKeywords).join(", ")},
ogImage: image ? {property: "og:image", content: image} : undefined,
ogTitle: {
property: "og:title",
content: title,
template(ogTitle) {
return (!ogTitle || ogTitle === '') ? "Polétání.cz" : `${ogTitle} - Polétání.cz`
}
},
ogDescription: {
property: "og:description", content: filteredDescription,
},
robots: {name: "robots", content: index ? "index" : "noindex"},
}
}
}
+1 -1
View File
@@ -31,7 +31,7 @@
:src="user.titleImageUrl"
style="height: 150px"
>
<div class="bg-transparent absolute-bottom-right" v-if="user.publicUsername !== ''">
<div class="bg-transparent absolute-bottom-right" v-if="user.publicUsername && user.publicUsername !== ''">
<router-link
v-if="user.avatarImageUrl"
target="_blank"
@@ -16,7 +16,7 @@ import {useLoggedUserStore} from "stores/LoggedUser";
import {useFlightsStore} from "stores/Flights";
import {toggleVisibility} from "src/composables/form/formHandling";
import ContentCardEditButton from "components/ContentCardEditButton.vue";
import {loadPage, paginationLimit} from "src/composables/pagination";
import {filters, loadPage, paginationLimit} from "src/composables/pagination";
import BasicInfoBanner from "components/banners/BasicInfoBanner.vue";
import FlightsListSkeleton from "components/flights/skeletons/FlightsListSkeleton.vue";
import CopyLinkButton from "components/inputs/CopyLinkButton.vue";
@@ -53,9 +53,9 @@ const togglePublicVisibility = (variables) => toggleVisibility({
$q, store: useAircraftsStore(), object: aircraft.value, variables
});
filters.value = {aircraftId: aircraftId};
const loadNextFlightsPage = (appendItems) => loadPage({
fetchFunc: useFlightsStore().fetch,
filters: {aircraftId: aircraftId},
appendItems
});
const {isQueryRunning: areFlightsLoading, totalItemsCount, flights} = storeToRefs(useFlightsStore());
@@ -88,12 +88,14 @@ const loggedUser = storeToRefs(useLoggedUserStore()).user;
<q-card-section>
<ContentCardEditButton
v-if="aircraft.createdById === loggedUser?.id"
:to='{name: "aircraftEdit", params: {id: aircraft.id, step: "info"}}'
label="Upravit informace"
/>
<AircraftInfo :aircraft="aircraft"/>
<ContentCardEditButton
v-if="aircraft.createdById === loggedUser?.id"
:to='{name: "aircraftEdit", params: {id: aircraft.id, step: "photo"}}'
label="Titulní fotografie"
/>
+1 -2
View File
@@ -14,8 +14,7 @@ import {loadPage, paginationLimit} from "src/composables/pagination";
useAppStore().setPageTitle("Letadla");
const loadNextAircraftsPage = (appendItems) => loadPage({
fetchFunc: useAircraftsStore().fetch,
appendItems
fetchFunc: useAircraftsStore().fetch, appendItems
});
loadNextAircraftsPage(false);
+11 -12
View File
@@ -8,7 +8,7 @@ import {useCopilotsStore} from "stores/Copilots";
import {storeToRefs} from "pinia";
import PhotoGallery from "components/photos/PhotoGallery.vue";
import ContentCard from "components/ContentCard.vue";
import {loadPage, paginationLimit} from "src/composables/pagination";
import {filters, loadPage, paginationLimit} from "src/composables/pagination";
import {useFlightsStore} from "stores/Flights";
import FlightsListSkeleton from "components/flights/skeletons/FlightsListSkeleton.vue";
import {useMeta, useQuasar} from "quasar";
@@ -27,21 +27,20 @@ useCopilotsStore().fetchDetail(copilotId).then(() => {
])
});
const loadNextFlightsPage = (appendItems) => loadPage({
fetchFunc: useFlightsStore().fetch,
filters: {copilotId: copilotId},
appendItems
});
loadNextFlightsPage(false);
const {isQueryRunning: areFlightsLoading, totalItemsCount, flights} = storeToRefs(useFlightsStore());
const {isQueryRunning, copilot} = storeToRefs(useCopilotsStore());
const loggedUser = storeToRefs(useLoggedUserStore()).user;
const $q = useQuasar();
const togglePublicVisibility = (variables) => toggleVisibility({
$q, store: useCopilotsStore(), object: copilot.value, variables
});
filters.value = {copilotId: copilotId};
const loadNextFlightsPage = (appendItems) => loadPage({
fetchFunc: useFlightsStore().fetch, appendItems
});
loadNextFlightsPage(false);
const {isQueryRunning: areFlightsLoading, totalItemsCount, flights} = storeToRefs(useFlightsStore());
const {isQueryRunning, copilot} = storeToRefs(useCopilotsStore());
const loggedUser = storeToRefs(useLoggedUserStore()).user;
</script>
<template>
+11 -11
View File
@@ -8,7 +8,7 @@ import FlightsList from "components/flights/FlightsList.vue";
import {FORMAT_DATE, formatDatetime} from "src/composables/datetimeUtils";
import {useEventsStore} from "stores/Events";
import {storeToRefs} from "pinia";
import {loadPage, paginationLimit} from "src/composables/pagination";
import {filters, loadPage, paginationLimit} from "src/composables/pagination";
import {useFlightsStore} from "stores/Flights";
import PublishButton from "components/inputs/PublishButton.vue";
import {useLoggedUserStore} from "stores/LoggedUser";
@@ -29,22 +29,22 @@ onMounted(() => {
loadNextFlightsPage(false);
})
const {isQueryRunning, event} = storeToRefs(useEventsStore());
const loadNextFlightsPage = (appendItems) => loadPage({
fetchFunc: useFlightsStore().fetch,
filters: {eventId: eventId},
appendItems
});
const {isQueryRunning: areFlightsLoading, totalItemsCount, flights} = storeToRefs(useFlightsStore());
const loggedUser = storeToRefs(useLoggedUserStore()).user;
const $q = useQuasar();
const togglePublicVisibility = (variables) => toggleVisibility({
$q, store: useEventsStore(), object: event.value, variables
});
const {isQueryRunning, event} = storeToRefs(useEventsStore());
filters.value = {eventId: eventId}
const loadNextFlightsPage = (appendItems) => loadPage({
fetchFunc: useFlightsStore().fetch, appendItems
});
const {isQueryRunning: areFlightsLoading, totalItemsCount, flights} = storeToRefs(useFlightsStore());
const loggedUser = storeToRefs(useLoggedUserStore()).user;
</script>
<template>
@@ -16,7 +16,7 @@ import {successObject} from "src/composables/dialog";
import ContentCardEditButton from "components/ContentCardEditButton.vue";
import {useFlightsStore} from "stores/Flights";
import FlightsListSkeleton from "components/flights/skeletons/FlightsListSkeleton.vue";
import {loadPage, paginationLimit} from "src/composables/pagination";
import {filters, loadPage, paginationLimit} from "src/composables/pagination";
import PublishButton from "components/inputs/PublishButton.vue";
import CopyLinkButton from "components/inputs/CopyLinkButton.vue";
@@ -33,19 +33,20 @@ usePointsOfInterestStore().fetchDetail(poiId).then((p) => {
])
});
const loadNextFlightsPage = (appendItems) => loadPage({
fetchFunc: useFlightsStore().fetch,
filters: {pointOfInterestId: poiId},
appendItems
});
loadNextFlightsPage(false);
const {isQueryRunning: areFlightsLoading, totalItemsCount, flights} = storeToRefs(useFlightsStore());
const {isQueryRunning, pointOfInterest} = storeToRefs(usePointsOfInterestStore());
const $q = useQuasar();
const togglePublicVisibility = (variables) => toggleVisibility({
$q, store: usePointsOfInterestStore(), object: pointOfInterest.value, variables
});
filters.value = {pointOfInterestId: poiId};
const loadNextFlightsPage = (appendItems) => loadPage({
fetchFunc: useFlightsStore().fetch,
appendItems
});
loadNextFlightsPage(false);
const {isQueryRunning: areFlightsLoading, totalItemsCount, flights} = storeToRefs(useFlightsStore());
const {isQueryRunning, pointOfInterest} = storeToRefs(usePointsOfInterestStore());
</script>
<template>
@@ -73,6 +74,7 @@ const togglePublicVisibility = (variables) => toggleVisibility({
<q-card-section>
<ContentCardEditButton
v-if="pointOfInterest.createdById === loggedUser?.id"
:to="{name: 'poiEdit', params:{id: pointOfInterest.id, step: 'info'}}"
label="Upravit informace"
/>
@@ -89,6 +91,7 @@ const togglePublicVisibility = (variables) => toggleVisibility({
</BasicInfoBanner>
<ContentCardEditButton
v-if="pointOfInterest.createdById === loggedUser?.id"
:to="{name: 'poiEdit', params:{id: pointOfInterest.id, step: 'photo'}}"
label="Úvodní fotografie"
/>
+83
View File
@@ -0,0 +1,83 @@
<script setup>
import {useRoute} from "vue-router";
import {useAppStore} from "stores/App";
import {onMounted, ref} from "vue";
import {useMeta} from "quasar";
import {metaData} from "src/composables/meta";
const $route = useRoute();
const help = {
"gpx-trasa": {
title: "Co je to GPX?",
content: "<p>GPX je formát umožňující ukládat (a následně zpracovávat) GPS data z nějakého záznamového zařízení. Pokud v letadle vozíš nějaký tracker, nejspíš bude existovat způsob, jak z něj dostat právě GPX.</p>" +
"<p>Pokud nahraješ tento soubor do Polétání.cz, aplikace zobrazí mapu s trasou letu a graf rychlosti a výšky, společně s výškou terénu.</p>" +
"<p>Tento soubor se dá i použít pro načtení základních údajů o letu při jeho zadávání. Ze záznamu trasy Polétání.cz umí vyčíst čas a místo vzletu i přistání.</p>"
},
"organizace": {
title: "Co jsou to organizace?",
content: "Organizace slouží ke sdílení letadel, např. v rámci aeroklubu. "
},
"udalosti": {
title: "Co jsou to události?",
content: ""
},
"uvodni-fotografie": {
title: "K čemu potřebuji úvodní fotografii?",
content: ""
},
"url": {
title: "Proč k veřejným datům musím uvádět URL část?",
content: ""
},
"poi": {
title: "Jak funguje databáze zajímavých míst?",
content: ""
},
"editor-fotek": {
title: "Co umí editor fotek?",
content: ""
},
"debriefing": {
title: "Co jsou poznámky k debriefingu?",
content: "Prostě soukromé poznámky. Vidíš je jen ty."
},
"verejny-profil": {
title: "Musím mít veřejný profil?",
content: "<strong>Rozhodně nemusíš!</strong> Pokud chceš, můžeš si ukládat fotky a trasy jen do svého účtu a s nikým je sdílet nemusíš."
},
};
const opened = ref({});
onMounted(() => {
opened.value[$route.query.h] = true;
})
useAppStore().setPageTitle("Nápověda");
useMeta(() => metaData({
title: "Nápověda",
}));
useAppStore().setBreadcrumbs([
{label: "Polétání.cz", to: {name: "homepage"}},
{label: "Nápověda"},
]);
</script>
<template>
<q-list padding bordered class="rounded-borders">
<q-expansion-item
v-model="opened[index]"
v-for="(helpItem, index) in help"
:key="index"
expand-separator
header-class="text-primary text-bold text-h5"
:label="helpItem.title"
>
<q-card>
<q-card-section>
<div v-html="helpItem.content"/>
</q-card-section>
</q-card>
</q-expansion-item>
</q-list>
</template>
@@ -15,7 +15,7 @@ import {useAppStore} from "stores/App";
import FlightsList from "components/flights/FlightsList.vue";
import {storeToRefs} from "pinia";
import {onMounted} from "vue";
import {metaData} from "src/composables/meta";
import {aircraftKeywords, metaData} from "src/composables/meta";
import PhotoGallery from "components/photos/PhotoGallery.vue";
import ContentCard from "components/ContentCard.vue";
import AircraftInfo from "components/aircrafts/AircraftInfo.vue";
@@ -24,7 +24,7 @@ import {useFlightsStore} from "stores/Flights";
import FlightsListSkeleton from "components/flights/skeletons/FlightsListSkeleton.vue";
import BasicInfoBanner from "components/banners/BasicInfoBanner.vue";
import PhotoGallerySkeleton from "components/photos/skeletons/PhotoGallerySkeleton.vue";
import {loadPage, paginationLimit} from "src/composables/pagination";
import {filters, loadPage, paginationLimit} from "src/composables/pagination";
const $q = useQuasar();
const $router = useRouter();
@@ -38,11 +38,7 @@ useMeta(() => metaData({
title: aircraft.value.callSign,
image: aircraft.value.titleImage?.url,
keywords: [
"letadlo",
aircraft.value.callSign,
aircraft.value.model,
aircraft.value.organization?.name,
aircraft.value.manufacturer
"letadlo", ...aircraftKeywords(aircraft.value)
],
description: aircraft.value.description || `Detail letadla ${aircraft.value.callSign}.`
}));
@@ -53,10 +49,9 @@ useAppStore().setBreadcrumbs([
]);
filters.value = {aircraftId: aircraft.value.id};
const loadNextFlightsPage = (appendItems) => loadPage({
fetchFunc: useFlightsStore().fetchPublic,
filters: {aircraftId: aircraft.value.id},
appendItems
fetchFunc: useFlightsStore().fetchPublic, appendItems
});
onMounted(() => {
@@ -20,7 +20,7 @@ import PhotoGallery from "components/photos/PhotoGallery.vue";
import {useMeta} from "quasar";
import {metaData} from "src/composables/meta";
import PhotoGallerySkeleton from "components/photos/skeletons/PhotoGallerySkeleton.vue";
import {loadPage, paginationLimit} from "src/composables/pagination";
import {filters, loadPage, paginationLimit} from "src/composables/pagination";
import BasicInfoBanner from "components/banners/BasicInfoBanner.vue";
const props = defineProps({
@@ -49,15 +49,13 @@ useMeta(() => metaData({
}));
filters.value = {copilotId: copilot.value.id};
const loadNextFlightsPage = (appendItems) => loadPage({
fetchFunc: useFlightsStore().fetchPublic,
filters: {copilotId: copilot.value.id},
appendItems
fetchFunc: useFlightsStore().fetchPublic, appendItems
});
onMounted(() => {
loadNextFlightsPage(false);
usePhotosStore().fetchPublic({copilotId: copilot.value.id})
})
</script>
@@ -14,7 +14,7 @@ import EventDetailSkeleton from "components/event/skeletons/EventDetailSkeleton.
import {FORMAT_DATE, formatDatetime} from "src/composables/datetimeUtils";
import FlightsList from "components/flights/FlightsList.vue";
import {storeToRefs} from "pinia";
import {loadPage, paginationLimit} from "src/composables/pagination";
import {filters, loadPage, paginationLimit} from "src/composables/pagination";
import {useFlightsStore} from "stores/Flights";
import FlightsListSkeleton from "components/flights/skeletons/FlightsListSkeleton.vue";
import {useMeta} from "quasar";
@@ -41,15 +41,13 @@ useMeta(() => metaData({
description: event.value.description
}))
filters.value = {username: props.pilot.publicUsername, eventId: event.value.id};
const loadNextFlightsPage = (appendItems) => loadPage({
fetchFunc: useFlightsStore().fetchPublic,
filters: {username: props.pilot.publicUsername, eventId: event.value.id},
appendItems
fetchFunc: useFlightsStore().fetchPublic, appendItems
});
loadNextFlightsPage(false);
const {flights, totalItemsCount, isQueryRunning: areFlightsLoading} = storeToRefs(useFlightsStore());
</script>
<template>
@@ -45,7 +45,7 @@ useAppStore().setBreadcrumbs([
useMeta(() => metaData({
title: `${flightNameFormatted} - ${props.pilot.name}`,
keywords: ["let", "výlet", "letadlo", props.pilot.name, aircraftKeywords(flight.value.aircraft)],
keywords: ["let", "výlet", "letadlo", props.pilot.name, ...aircraftKeywords(flight.value.aircraft)],
description: flight.value.description,
image: flight.value.titlePhoto?.url
}
@@ -7,7 +7,7 @@ import {useFlightsStore} from "stores/Flights";
import {storeToRefs} from "pinia";
import {commonKeywords, metaData} from "src/composables/meta";
import {useMeta} from "quasar";
import {loadPage, paginationLimit} from "src/composables/pagination";
import {filters, loadPage, paginationLimit} from "src/composables/pagination";
const props = defineProps({
pilot: {
@@ -29,9 +29,9 @@ useAppStore().setBreadcrumbs([
{label: `@${props.pilot.publicUsername}`},
])
filters.value = {username: props.pilot.publicUsername};
const loadNextFlightsPage = (appendItems) => loadPage({
fetchFunc: useFlightsStore().fetchPublic,
filters: {username: props.pilot.publicUsername},
appendItems
});
loadNextFlightsPage(false);
@@ -23,7 +23,7 @@ import FlightsListSkeleton from "components/flights/skeletons/FlightsListSkeleto
import BasicInfoBanner from "components/banners/BasicInfoBanner.vue";
import {onMounted} from "vue";
import PhotoGallerySkeleton from "components/photos/skeletons/PhotoGallerySkeleton.vue";
import {loadPage, paginationLimit} from "src/composables/pagination";
import {filters, loadPage, paginationLimit} from "src/composables/pagination";
const {pointOfInterest, isQueryRunning} = storeToRefs(usePointsOfInterestStore());
const {photos, isQueryRunning: arePhotosLoading} = storeToRefs(usePhotosStore());
@@ -41,10 +41,9 @@ useAppStore().setBreadcrumbs([
{label: pointOfInterest.value.name}
]);
filters.value = {pointOfInterestId: pointOfInterest.value.id};
const loadNextFlightsPage = (appendItems) => loadPage({
fetchFunc: useFlightsStore().fetchPublic,
filters: {pointOfInterestId: pointOfInterest.value.id},
appendItems
});
+1
View File
@@ -20,6 +20,7 @@ const routes = [
{path: 'letadlo/:callSign', name: "publicAircraftDetail", component: () => import('pages/public/aircraft/AircraftDetailPage.vue')},
{path: 'mista', name: "publicPOIs", component: () => import('pages/public/poi/PointsOfInterestPage.vue')},
{path: 'mista/:slug', name: "publicPOIDetail", component: () => import('pages/public/poi/PointOfInterestDetailPage.vue')},
{path: 'napoveda', name: "help", component: () => import('pages/public/HelpPage.vue')},
{
path: ':username',
component: () => import('pages/public/PilotProfileWrapper.vue'),